JavaLearn # 面向对象编程_4
Posted LRcoding
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaLearn # 面向对象编程_4相关的知识,希望对你有一定的参考价值。
1. final 关键字
- 修饰变量:使用 final 修饰后,变量的值不可改变,就成了常量
- 修饰基本数据类型时:值只能赋值一次,后续不能再赋值
final int NUM = 5;
- 修饰引用类型时:引用变量的值(地址) 不能变,但是对象的属性可以改变
- 修饰基本数据类型时:值只能赋值一次,后续不能再赋值
- 修饰方法:不能被子类重写,但可以重载
- 修饰类:不能被继承
常用的 final 类:System, Math, String
public final class Maths {
public static final double PI = 3.1415926; // 不加 final 的时候还可以修改
// 如果类已经用 final 修改了,方法可以不加 final
public static final double abs(double d) {
if (d >= 0) {
return d;
} else {
return -d;
}
}
public static void main(String[] args) {
final int NUM = 5;
// NUM = 6; 只允许赋值一次,不允许再修改
final Dog dog = new Dog("旺财");
dog.name = "来福"; // 可以修改对象的属性
// dog = new Dog("小强"); 不允许改变
}
}
2. 抽象类
问题1:Animal an = new Animal();
没有一种动物,名称是 Animal,所以 Animal 不能被实例化
解决:抽象类
问题2:子类必须重写父类的某个方法,否则报错
解决:抽象方法
- 有抽象方法的类,必须定义成抽象类
- 抽象类不能实例化,即不能new
- 抽象类必须有构造方法,构造方法不能使用 abstract 修饰
- 一个抽象类可以有 0 或多个抽象方法
- 子类必须重写父类的抽象方法
public abstract class Animal {
private String color;
// 抽象类,虽然不能 new,必须有构造方法
public Animal() {
}
public Animal(String color) {
this.color = color;
}
// 抽象类可以有普通方法
public void shout() {
System.out.println("----发出声音----");
}
// 要求子类必须重写的方法,用 abstract 定义
abstract public void eat();
}
3. 接口
接口是规范,定义的是一组规则,体现了现实世界中 “如果你是…则必须能…” 的思想。
[访问修饰符] interface 接口名 [extends 父接口1, 父接口2.....] {
常量定义;
方法定义;
}
- 访问修饰符:只能是 public 或者 默认
- 接口名:和类名一样的命名规则
- extends:接口可以多继承,弥补了Java单继承的不足(必须先 extends,再 implements)
- 常量:接口中的属性只能是常量,总是用
public static final
修饰 - 方法:接口中的方法,默认总是用
public abstract
修饰(JDK1.8之前)
示例需求 1:飞机、鸟、超人、导弹参加飞行表演
- 思路1:定义一个父类 Fly,让它们都去继承 Fly。 ==》 不可以,继承是 is-a 的关系,显然不是
- 思路2:定义一个接口 Flyable,让它们都去实现 Flyable接口 ==》 可以, 接口是 has-a 的关系
Flyable 接口:
public interface Flyable {
// 变量:默认用 public static final 修饰
double PI = 3.14;
// 接口不能 new,也没有构造方法,不会自动继承某个类
// 成员方法,默认用 public abstract 修饰
void fly();
// JDK1.8之后方法可以有实现体,但必须用 static 修饰
static void testMethod() {
System.out.println("飞飞飞");
}
}
Bird 类:
public class Bird implements Main {
// 必须实现接口中定义的方法(没有方法体的)
@Override
public void fly() {
System.out.println("鸟飞。。。。");
}
public void shout() {
System.out.println("-----鸟叫-------");
}
}
SuperMan类:
public class SuperMan implements Main {
@Override
public void fly() {
System.out.println("超人飞飞飞。。。。");
}
}
Test 类:
public class Test {
public static void main(String[] args) {
Bird bird = new Bird();
fly(bird);
SuperMan superMan = new SuperMan();
fly(superMan);
}
/**
* 多态:形参是一个接口,实参可以是该接口的任意一个实现类的对象
* @param main
*/
public static void fly(Flyable flyable) {
flyable.fly();
// 不能调用实现类中定义的方法 flyable.shout();
}
}
示例需求2:内部比较器 Comparable
图书类、学生类、新闻类、商品类等都是不同的类,但是都需要比较的功能。共同的父类显然不可以,可以定义一个比较接口 Comparable,在其中定义一个实现比较的方法 compareTo(Object obj)。让各个类实现该接口即可(模拟Java的Comparable接口)。
Comparable 接口:
public interface Comparable {
int compareTo(Object obj);
}
Book 类:
public class Book implements Comparable{
private String name;
private int price;
public Book() {
}
public Book(String name, int price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\\'' +
", price=" + price +
'}';
}
/**
* 实现了接口,要实现比较的方法,从而达到比较 对象属性大小 的目的
* @param obj
* @return
*/
@Override
public int compareTo(Object obj) {
Book book = (Book) obj;
int res = this.price - book.getPrice();
// 先比较年龄,如果年龄相同,再比较名称
if (res == 0) {
// 此处调用的是 String 的CompareTo,已经实现了Java的 Comparable 接口
return this.name.compareTo(book.name);
} else {
return res;
}
}
}
Test测试类:
public class TestBook {
public static void main(String[] args) {
Book book1 = new Book("白鹿原", 90);
Book book2 = new Book("平凡的世界", 90);
// 比较两本书的 价格、名称 的大小
System.out.println(book1.compareTo(book2));
}
}
4. JDK1.8的接口新特性
JDK7 及之前:
- 接口的变量都是
public final static
修饰的 - 接口中都是 抽象(abstract) 方法,不能有 static 方法
JDK1.8 及其之后
- 接口中可以添加非抽象方法(static),实现类不能重写,只能通过接口名调用
- 如果实现类中定义了相同名字的静态方法,不是重写,是直接从属于实现类的
- 接口中可以用
default
修饰方法,在实现类中,可以直接使用,也可以重写(不能有default关键字),且只能通过实现类的对象名调用 - 调用实现的接口的上级接口中的 default 方法:
MyInterface.super.method2()
MyInterface类:
public interface MyInterface {
// 常量没有变化,还是用 public static final 修饰
// 抽象方法没有变化 默认用 public abstract 修饰
void method1();
// 变化1:可以有非抽象方法 ==》 static 修饰【实现类不可以重写,只能通过接口名调用】
static void method2() {
System.out.println("JDK1.8新增的特性,实现类不可以重写,只能通过接口名调用");
}
// 变化2:可以有非抽象方法 ==》 default 修饰【实现类可以选择是否重写(重写时要去掉default),只能通过实现类的对象名调用】
default void method3() {
System.out.println("JDK1.8新增的,实现类可以选择是否重写(重写时要去掉default),只能通过实现类的对象名调用");
}
}
MyClass类:
public class MyClass implements MyInterface{
// 实现接口,要实现它的抽象方法
@Override
public void method1() {
}
// 不是重写了接口的方法,是实现类自己定义了一个新的方法
static void method2() {
System.out.println("这是实现类自己定义的方法");
}
@Override
public void method3() {
System.out.println("实现类可以选择是否重写接口中 default 修饰的方法,重写时要【去掉 default】");
}
public static void main(String[] args) {
// 接口中 static 修饰的方法,只能通过【接口名】调用
MyInterface.method2();
// 实现类中,定义了和接口中相同的static修饰的方法,不是重写,是实现类自己的
MyClass.method2();
// 接口中 default 修饰的方法,只能通过【实现类的对象名】调用
MyClass mc = new MyClass();
mc.method3();
}
}
提供非抽象方法的目的:
- 为了解决实现类中代码重复的问题(如果一个接口中有10个抽象方法,20个类实现该接口,那么这20个类中都需要重写10个方法,也就是200次,但是用 default 修饰后,实现类可以选择是否重写)
- 如果一个接口新增了一个功能,不必对那些实现类重新设计。
5. 内部类
内部类是一类特殊的类,是定义在一个类的内部的类。实际开发中,是为了方便的使用外部类的相关属性和方法。
在Java中,内部类分为非静态成员内部类、静态成员内部类、局部内部类、匿名内部类
// 外部类
public class OuterClass {
// 成员变量
private String color = "red";
private int num = 10;
// 构造方法
public OuterClass() {
}
public OuterClass(int num) {
this.num = num;
}
// 静态代码块
static {
}
// 成员方法
public void outerMethod() {
// 局部变量
int num = 20;
// ==================== 局部内部类 ========================
class LocalClass {
}
}
// ==================== (非静态)成员内部类 ========================
class InnerClass {
}
// ==================== 静态成员内部类 ========================
static class StaticInnerClass {
}
}
5.1 非静态成员内部类
- 非静态成员内部类可以直接访问外部类的非静态成员
- 外部类不能直接访问非静态成员内部类的成员,需要先创建非静态成员内部类的对象,再通过对象名访问
- 非静态成员内部类访问外部类同名的成员变量:
OuterClass.this.num
- 创建非静态成员内部类对象:必须先创建外部类对象,再通过外部类对象创建非静态成员内部类的对象
- 内部类是一个编译时的概念,一旦编译成功,就会变成两个完全不同的类。
- 非静态成员内部类中,不能有静态方法、变量、代码块
- 外部类的静态方法、静态代码块,不能访问非静态成员内部类
外部类及非静态成员内部类:
// 外部类
public class OuterClass {
// 成员变量
private String color = "red";
private int num = 10;
// 构造方法
// 静态代码块
// 成员方法
// ======================== 非静态成员内部类(可以使用权限修饰符) =========================
class InnerClass {
// 成员变量
private int num = 30;
// 构造方法
public InnerClass() {
}
// 成员方法
public void innerMethod() {
// 【1. 非静态成员内部类可以直接访问外部类的成员】
System.out.println(color);
outerMethod();
int num = 20;
// 【3. 如果内部类,外部类有同名的变量,如何访问】
System.out.println(num); // 局部变量的
System.out.println(this.num); // 内部类的
System.out.println(OuterClass.this.num); // 外部类的!!!
}
}
public void outerMethod2() {
// 【2. 外部类不能直接访问非静态成员内部类的成员,需要内部类的对象】
InnerClass in = new InnerClass();
in.innerMethod();
}
}
Test类:
public class Test {
public static void main(String[] args) {
// 创建外部类对象
OuterClass out = new OuterClass();
out.outerMethod2();
// 创建 非静态成员内部类对象 【4. 必须使用外部类的对象创建,非静态成员内部类属于外部类的对象】
OuterClass.InnerClass inner = out.new InnerClass(); // = new OutClass().new InnerClass();
}
}
5.2 静态成员内部类
- 静态内部类,只能访问外部类的静态成员
- 静态内部类访问外部类的同名的变量:
OutClass.num
- 静态内部类是属于外部类的。创建静态内部类的对象,使用外部类创建
new OutClass.StaticInnerClass()
- 外部类可以通过静态内部类的类名直接访问内部类的静态成员,访问非静态成员依旧需要先创建内部类对象
外部类及静态成员内部类:
// 外部类
public class OuterClass {
private String color = "red";
// 静态成员变量
static private int num = 10;
// ====================== 静态成员内部类(可以使用权限修饰符) ==========================
static class StaticInnerClass {
// 成员变量
private int num = 30;
// 构造方法
public InnerClass() {
}
// 成员方法
public void innerMethod() {
// 【1. 静态成员内部类,只能访问外部类的静态成员】
// System.out.println(color);
// outerMethod();
int num = 20;
System.out.println(num); // 局部变量的
System.out.println(this.num); // 内部类的
// 【3. 如果内部类,外部类有同名的【【静态】】变量,通过外部类的类名调用】
System.out.println(OuterClass.num); // 外部类的!!!
}
// 静态成员方法
public static void staticInnerMethod() {
}
}
public void outerMethod2() {
// 【2. 外部类不能直接访问静态内部类的非静态成员,需要静态内部类的对象】
StaticInnerClass in = new StaticInnerClass();
in.innerMethod();
// 外部类可以通过静态内部类的类名,直接访问内部类的【【静态方法】】
StaticInnerClass.staticInnerMethod();
}
}
Test类:
public class Test {
public static void main(String[] args) {
// 创建静态成员内部类对象【4. 使用外部类创建,静态成员内部类属于外部类】
OuterClass.StaticInnerClass inner = new OuterClass.StaticInnerClass();
}
}
5.3 局部内部类
- 局部内部类的作用范围是 当前方法
- 要想访问匿名内部类所在方法的变量(JDK1.8默认使用 final 修饰,JDK7及之前,要想访问,需要用 final 修饰)
public class OutClass {
private int num = 10;
public void outMethod() {
int num2 = 20;
// ====================== 局部内部类(不能使用权限修饰符) =======================
class LocalInnerClass {
private int num = 30;
public void localInnerMethod() {
int num = 40;
System.out.println(num); // 局部变量
System.out.println(this.num); // 匿名内部类的
System.out.println(OutClass.this.num); // 外部类的
// 匿名内部类所在方法的变量(JDK1.8默认使用 final 修饰,JDK7及之前,要想访问,需要用 final 修饰)
System.out.println(num2);
}
}
// 【1. 局部内部类的作用范围是 当前方法 】
LocalInnerClass lic = new LocalInnerClass();
lic.localInnerMethod();
}
}
5.4 匿名内部类
- 匿名内部类是一种特殊的局部内部类,在方法中定义
- 匿名内部类可以实现一个接口(只能实现一个),也可以继承一个类
- 必须实现所有的方法,匿名内部类不能是抽象类
- 匿名内部类不可能有构造方法,因为类是匿名的
- 匿名内部类没有访问修饰符
- 如果想实现构造方法形式的一些初始化功能,可以通过代码块实现
- 如果要访问所在方法的局部变量,该变量需要使用 final 修饰(JDK1.8可以省略)
还可以创建外部比较器 Comparator,用于内部比较器定义了比较规则后(比如用 price 比较),想更改比较规则时(不使用 price 了,使用 name 比较)使用。
Comparator接口:
public interface Comparator {
int compare(Object obj1, Object obj2);
}
新增的比较规则类:
public class BookNameComparator implements Comparator {
@Override
public int compare(Object obj1, Object obj2) {
Book book1 = (Book)obj1;
Book book2 = (Book)obj2;
return book1.getName().compareTo(book2.getName());
}
}
使用新增的比较规则类:
public class Test {
public static void main(String[] args) {
Book book1 = new Book("abc", 80);
Book book2 = new Book("abcd", 80);
// 内部比较器
book1.compareTo(book2);
// 以上是关于JavaLearn # 面向对象编程_4的主要内容,如果未能解决你的问题,请参考以下文章