Java Polymorphism

编译时多态

  • 设计时多态方法重载

运行时多态

  • 程序运行时动态决定调用那个方法

必要条件

  1. 满足继承关系
  2. 父类引用指向子类对象

向上转型

  • 父类引用指向子类实例,可以调用子类重写父类的方法以及父类派生的方法,无法调用子类独有的方法
  • 父类中的静态方法无法被子类重写,所以向上转型之后,只能调用到父类原有的静态方法

向下转型

  • 子类引用指向父类对象,此处可以使用instanceof进行检查,避免类型转换时的安全性问题
  • 可以调用子类独有的方法

抽象类 abstract class

  • 限制实例化
  • 只能被继承
  • 应用场景: 某个父类只是知道其子类应该包含怎样的方法,但无法准确知道这些子类如何实现这些方法

抽象方法 abstract method

  • 不能有方法体
  • 必须由子类实现
  • 子类如果没有重写父类的所有抽象方法,则也要定义为抽象类

接口 Interface

  • 当多个类具有相同能力的时候,可以使用接口抽象出相同的能力
  • 接口定义了某一批类所需要遵守的规范
  • 接口不关心这些类的内部数据,也不关心这些类里的方法的实现细节,它只规定这些类里必须提供某些方法
  • 接口方法可以不写abstract关键字,并且默认为public的访问权限
  • 当类实现接口时,需要去实现接口中的所有抽象方法,否则需要将该类设置为抽象类
  • 接口中可以定义常量,默认为public static final

默认方法

  • 自JDK1.8之后,接口中可以存在默认方法,使用default关键字定义
  • 默认方法可以带方法体,子类实现接口时可以不用实现默认方法
  • 子类可以重写默认方法,并可以通过接口的引用调用

静态方法

  • 自JDK1.8之后,接口中可以存在静态方法,使用static关键字定义
  • 静态方法可以带方法体,子类可以通过使用接口名访问接口的静态方法

多重实现

  • 子类可以继承一个父类,但是可以实现多个接口
  • 当多个接口中具有相同签名的方法时,子类需要重写方法
  • 当父类和接口具有相同签名的方法时,父类方法具有优先权
  • 当父类和接口具有相同名字的变量时,子类需要重新定义该变量,父类中的变量不具有优先权

接口的继承

  • 接口也可以实现继承关系
  • 接口可以继承多个父接口
1
2
3
4
5
6
7
8
9
10
11
public interface ParentOne {

}

public interface ParentTwo {

}

public interface Child extends ParentOne, ParentTwo {

}

内部类

  • 内部类提供了更好的封装,不允许其他外部类访问内部类的信息

成员内部类

  • 最常见的内部类,也称为普通内部类
  • 内部类在外部使用时,无法直接实例化,需要借由外部类信息才能完成实例化
  • 内部类的访问修饰符,可以是任意的,但是访问权限会受到修饰符的影响
  • 内部类可以直接访问外部类的成员(包括成员属性和成员方法),如果出现同名属性,优先访问内部类中定义的
  • 外部类访问内部类的信息需要通过内部类的实例,无法直接访问
  • 内部类编译后得class文件名:外部类$内部类.class

获取内部类对象实例

  1. new 外部类.new 内部类
1
Person.Heart myHeart = new Person().new Heart();
  1. 外部类对象.new 内部类
1
myHeart = myPerson.new Heart();
  1. 外部类对象.获取方法
1
myHeart = myPerson.getHeart();

静态内部类

  • 静态内部类中,只能直接访问外部类的静态成员
  • 需要使用外部类的实例对象来访问非静态成员
  • 访问静态内部类对象实例时,可以不依赖于外部类对象

获取静态内部类实例

1
Person.Heart myHeart = new Person.Heart();

方法内部类

  • 定义在外部类方法中的内部类,也成为局部内部类
  • 方法内部类中无法定义静态成员
  • 类中可以使用final,abstract成员
  • 和方法内部成员使用规则一样,class前面不可以添加public,private,protected,static等关键字

匿名内部类

  • 将类的定义和类的创建放在一起完成,程序只会用到一次类的实例,所以类名无关紧要
  • 对于抽象类Person来说,如果我们想调用其中的抽象方法,一种做法是创建一个实现read方法的子类
  • 但是如果这个子类只会被用到一次,那这个子类的名字就不重要,就可以使用匿名内部类来解决
  • 无法使用private,public,protected,static修饰
  • 无法编写构造方法,但是可以添加初始化代码块
  • 不能出现静态成员
  • 可以实现接口也可以继承父类,但是不能同时
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public abstract class Person {
public abstract void read();
}

public class PersonTest {
public void getRead(Person person) {
person.read();
}

public static void main(String[] args) {
PersonTest personTest = new PersonTest();

personTest.getRead(new Person() {

@Override
public void read() {
System.out.println("implement read method in Person parent abstract class");
}
})
}
}

匿名类的例子

  • 在我们使用comparator对Collections进行排序的时候可以使用匿名类来省去创建子类的过程

  • 不使用匿名类对List排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// create a child class implements parent Comparator
public class CustomComparator implements Comparator<String> {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
}

public class testComparator() {
List<String> list = new ArrayList<>();

// use CustomComparator to sort list
Collections.sort(list, new CustomComparator());
}
  • 使用匿名类对list排序
1
2
3
4
5
6
7
8
9
public class testComparator() {
List<String> list = new ArrayList<>();

Collections.sort(list, new Comparator<String>() {
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
})
}
  • 自Java1.8以后,可以使用lambda expression来省去方法名,匿名方法
1
2
3
4
5
public class testComparator() {
List<String> list = new ArrayList<>();

Collections.sort(list, (x, y) -> x.compareTo(y));
}
  • 因为上面的匿名方法和String里面定义的compareTo方法一样,我们可以使用method reference来更加简化代码
1
2
3
4
5
public class testComparator() {
List<String> list = new ArrayList<>();

Collections.sort(list, String::compareTo);
}