多态
同一个引用类型,使用不同的实例而执行不同操作,即父类引用指向子类对象
文章目录
1.多态的必要条件
- 一定要有继承关系
- 一定要有重写
- 父类引用指向子类对象
2.访问多态成员的特点
-
成员变量:编译时期看左边类型,运行时期看左边类型。(多态运用于行为,不是属性,所以和变量无关)
-
成员方法:编译时期看左边类型,运行时期看右边类型。(虽然运行时期看右边类型,但因为子类方法通过父类方法重写的,所以父类方法不能删除【一定要有重写】)
-
静态成员:编译时期看左边类型,运行时期看左边类型
//static是不能继承的 //所以Fu和Zi的method()是两个同名的方法,并不是重写 public class DuoTaiDemo02 { public static void main(String[] args) { Fu fu = new Zi(); fu.methed(); //The static method methed() from the type Fu should be accessed in a static way //系统也不建议用对象调用静态方法,应该用类直接调用 } } class Fu { public Fu() {} public static void methed() { System.out.println("Fu staticMethod"); } } class Zi extends Fu { public Zi() {} public static void methed() { System.out.println("Zi staticMethod"); } }
-
构造方法:先执行父类构造方法,加载父类静态成员,在执行子类构造方法
- 可以加载父类的静态成员方法区
- 帮助子类初始化从父类继承下来的成员
注意
私有方法 静态方法 final方法不能被继承,所以不能重写,不能构成多态,所以都是编译时期看左,运行时期看左,只有成员方法运行时期是看右的
3.多态的好处
- 简化代码
- 提高了代码维护性
- 提高了代码的扩展性
4.多态的缺点
多态的缺点:利用多态[父类引用]无法访问子类所有的方法
public class DuoTaiDemo04 {
public static void main(String[] args) {
Car c = new Bmw();
c.run();
c.showBrand();
//The method showBrand() is undefined for the type Car
//父类Car没办法识别方法showBrand()
//因为父类没有这方法,遵循 2.访问多态成员的特点
//成员方法:编译时期看左边类型,运行时期看右边类型
//左边在编译时期并没有知道showBrand()方法,所报错
}
}
class Car {
public void run() {
System.out.println("我也不知道怎么跑,想怎么跑就怎么跑!!");
}
}
class Bmw extends Car {
public void run() {
System.out.println("嗖嗖搜地跑");
}
public void showBrand() {
System.out.println("别摸我!");
}
}
解决方法: 向下转型 + i**nstanceof **关键字
4.1向上转型
//格式
[父类类型] 引用变量名 = new [子类类型]( ) ;
4.1.1向上转型的特点
- 子类转化为父类,父类引用指向子类对象,类似于自动进行数据类型转换
- 通过父类引用变量调用的方法是子类覆盖或者继承父类的方法(多态的体现)
- 通过父类引用变量无法调用子类特有的属性和方法(多态的缺点)
4.2向下转型
//格式
[子类类型] 引用变量名 = ( [子类类型] ) 父类引用变量名;
4.2.1向下转型的特点
-
父类转化为子类,父类引用转为子类对象,类似于强制类型转换
-
向下转换的过程中,子类类型与父类创建对象指向的子类类型不同时,会类型转换异常
Car c = new Bmw(); Benz benz = (Benz) c;//java.lang.ClassCastException //原因是向下转型的子类类型并不是创建对象时,父类引用指向的子类对象 //即:c 指的是Bmw,而向下转型的子类类型是Benz
4.3instanceof 关键字
用于解决向下转型发生的类型转换异常
判断左边对象是否属于右边实例
//格式
[对象] instanceof [类];
//返回值类型:boolean
4.4解决方式的缺陷
instanceof 解决类型转异常是有缺陷的
if (c instanceof Bmw) {
Bmw bmw = (Bmw) c;
// 利用向下转型访问子类所特有的方法和属性
bmw.showBrand();
} else if (c instanceof Benz) {
Benz benz = (Benz) c;
benz.showSpeed();
} else if (c instanceof Maslati) {
Maslati maslati = (Maslati) c;
maslati.showMoney();
}
//每当新建一个类,就需要增加一个else if
//问题一:每次修改原码都必须进行测试
//问题而:就算这可修改没什么问题不测试,对于有很多类的情况,就得大量加入else if,这工作量是不合理的
5.多态的表现形式(重载)
//一个方法参数列表不同,在Test类中使用的时候,就可以根据多态调用同一个方法,不同的参数列表
interface IPerson {
void use(Student s);
void use(Student s, Teacher t);
void use(Teacher t, Student s);
void use(Student s, Teacher t, Worker w);
}
6.多态的应用
多态应用大致有三类:直接使用多态;将父类引用作为形参;将父类引用作为返回值
参数类型可以分为:基本数据类型;引用数据类型:包括(普通类 抽象类 接口)
6.1直接使用多态
//普通类:
//当一个形参希望我们传入的是一个普通类时,
//我们实际上传入的是该类的对象/匿名对象/子类匿名对象[匿名内部类]
public class Test {
public static void main(String[] args) {
//访问学生类中的方法study()
Student s = new Student;
s.study();
//用StudentTest访问method方法
StudentTest st = new StudentTest();
Student s = new Student;
st.method(s);
//改进1
StudentTest st = new StudentTest();
st.method(new Student);
//改进2
new StudentTest().method(new Student);
//子类匿名对象
Student s = new Student() {
@Override
public void study() {
System.out.println("very good!!!");
}
};
new StudentTest().method(s);
}
}
class StudentTest {
//假如method方法参数列表使用null会出现空指针异常
//所以当一个方法的形参是引用数据类型的时候,建议做非空判断!
public void method(Student s) {
if (s != null) {
s.study();
}
}
// public void method(Student s) {
// s.study
// }
}
class Student{
public void study() {
System.out.println("好好学习")
}
}
6.2将父类引用作为形参
//抽象类:
//当一个形参希望我们传入的是一个抽象类时,
//我们实际上传入的是该类的子类对象/子类匿名对象 [匿名内部类]
public class Test {
public static void main(String[] args) {
//父类引用作为形参
new StudentTest().method(new Student);
}
}
class StudentTest {
//AbsPerson sp = new Student
public void method(AbsPerson ap) {
s.study
}
}
//抽象类(父类)
class AbsPerson {
public void study();
}
//子类
class Student extends AbsPerson{
public void study() {
System.out.println("好好学习")
}
}
//接口
//当一个形参希望我们传入的是一个接口时,
//我们实际上传入的是该类的实现类对象/实现类匿名对象
public class Test {
public static void main(String[] args) {
//父类引用作为形参
new StudentTest().method(new Student);
}
}
class StudentTest {
//AbsPerson sp = new Student
public void method(IPerson ip) {
s.study
}
}
//抽象类(父类)
class Iperson {
public void study();
}
//子类
class Student implements{
public void study() {
System.out.println("好好学习")
}
}
6.3父类引用作为返回值
//普通类
//当一个方法的返回值是一个普通的类时,
//实际上返回的是该类的对象/子类对象,我们可以使用该类的对象接收,或者该类的父类接受
public class Test {
public static void main(String[] args) {
//TeacherDemo().method的返回值类型是Teacher
Teacher t = new TeacherDemo().method();
t.teach();
// 多态
//Object是所有类的父类
Object obj = new TeacherDemo().method();
//多态防止数据类型转换异常,所以需要向下转型 + instanceof 关键字
if (obj instanceof Teacher) {
Teacher t = (Teacher) obj;
t.teach();
}
//匿名内部类
Teacher t = new Teacher() {
@Override
public void teach() {
System.out.println("my teach");
}
};
//当方法的返回值类型是引用类型的时候,可以使用链式调用
new TeacherDemo().method().teach();
}
}
class TeacherDemo {
public Teacher method() {
// Teacher t = new Teacher();
return t;
}
}
class Teacher {
public void teach() {
System.out.println("教书");
}
}
6.4对多态应用的理解题
//输出结果为:返回 swimming
//因为最终return时创建了新的对象,所以之前的都被覆盖了
public class Test {
public static void main(String[] args) {
new SwimmingTest().method(new ISwimming() {
@Override
public void swimming() {
System.out.println("外部 swimming");
}
}).swimming();
}
}
class SwimmingTest {
public ISwimming method(ISwimming swimming) {
swimming = new ISwimming() {
@Override
public void swimming() {
System.out.println("内部 swimming");
}
};
return new ISwimming() {
@Override
public void swimming() {
System.out.println("返回 swimming");
}
};
}
}
interface ISwimming {
void swimming();
}