面向对象高阶
1. 多态
1.1 基本概念
多态就是指同一种事物表现出来的多种形态。
饮料:可乐、雪碧、红牛、美年达、.............
宠物:狗、猫、鸟、..........
整数: byte b = 10; short s = 10; int i = 10;.......
C++多态分为两种:编译多态、运行时多态
Java中的多态只有一种,就是运行时多态。是一种运行期间的行为,而不是编译期的行为。
1.2 语法格式
父类类型 引用 = new 子类类型();
如:
Person p = new Student();
p.show();
解析:
在编译阶段p是Person类型,因此调用Person类自己的show()方法,若没有则编译的时候就报错。
在运行阶段p真正指向的对象是Student类型,因此最终调用的是Student类中自己的show()方法。
当使用多态方式调用方法的时候,首先会检查父类中是否有该方法,如果没有,则编译报错。
如果有,再去调用子类的同名方法。(注意:静态static方法属于特殊情况,静态方法只能继承,不能重写override,如果
子类中定义了同名同形式的静态方法,它对父类方法只能起到隐藏的作用。调用的时候用谁的引用,则调用谁的版本。)
课堂小案例:
自定义Person类实现封装,特征由:姓名和年龄
自定义Student类继承自Person类实现封装,特征有:学号。
自定义TestPersonStudent类,在main()方法中使用Person类的引用指向Student类的对象。
多态存在的三个比较条件:
1.要有继承
2.要有重写
3.父类引用指向子类对象
1.3 多态效果
1.对于指向子类对象的父类来说,在编译期间只能调用父类的方法,不能直接调用子类的方法。
2.对于父子类都有的非静态方法来说,最终调用子类中的重写版本。
3.对于父子类中都有的静态方法来说 ,最终调用父类中的版本,与指向的对象类型无关。
1.4 引用类型之间的转换
基本概述
1.引用类型之间的转换必须发生在父子类之间,分为自动类型转换(向上转换)和强制类型转换(向下转换)
2.向上转换(子类类型向父类类型的转换)
父类引用指向子类对象: Person p = new Student();
把一个子类对象转换为父类对象,向上转型(自动转型、隐式转型),代码中是父类引用指向子类的对象,父类
引用指向类型实例,可以调用子类重写父类的方法以及父类派生的方法,无法调用子类独有的方法。
注意:
父类中的静态方法无法被子类重写,所以向上转型之后只能调用父类原有的静态方法。
3.向下转换(父类类型向子类类型的转换)
子类引用指向父类实例,必须进行强制类型转换,可以调用子类特有的方法。必须满足转型的条件才能。
instanceof 运算符可以进行判断,左边的对象是否是他右边对象的实例,换句话来说就是左侧对象是否满足右侧对象类型
的特征如果是返回true
if(p instanceof Teacher){}
父类中的静态方法(含有static修饰的方法,只能被子类继承使用无法被子类重写)。
4.当没有父子类关系发生强制类型转换则编译报错,当拥有父子类关系发生强制转换是编译通过,
若目标类型并不是该引用真正指向的类型时,在运行阶段发生类型转换异常。
5.为了避免上述错误的发生,通常只要进行强制类型转换就需要进行判断,格式如下:
if(引用变量名 instanceof 数据类型){}
- 判断引用指向的对象是否为指定的数据类型,若是则返回true,否则返回false;
1.5 多态的实际意义
多态的意义在于:可以屏蔽不同子类的差异性编写通用的代码,从而产生不同的效果。
案例:
形状:横坐标、纵坐标
矩形:长度、宽度
圆形:半径
写一个方法要求既能打印矩形又能打印圆形。
2. 抽象类
2.1 抽象方法
抽象方法就是指不能被具体实现的方法,也就是没有方法体,并且使用abstract关键字修饰;
语法格式:
访问修饰符 abstract void show();
2.2 抽象类
抽象类就是指使用abstract关键字修饰的类,抽象类不能被实例化对象
2.3 注意事项
1.抽象类中可以有成员变量、成员方法以及构造方法;
2.抽象类中可以有抽象方法也可以没有;
3.拥有抽象方法的类必须是抽象类,因此通常情况下认为拥有抽象方法并且有abstract
关键字修饰的类才认为是真正的抽象类;
2.4 实际意义
抽象类的意义不再与实例化对象而在于被继承,若一个类继承自抽象类则必须重写抽象方法,否则该类也得变成抽象类。
因此,抽象类对子类具有强制性和规范性,叫做模板设计模式。
经验分享:
在以后的开发中推荐使用父类引用指向子类的对象形式,因为父类引用直接调用的方法一定是
父类拥有的方法,当需要更换指向子类对象的时候,只需要将new后面的改方式类型更改一下就可以了,其他的代码无需改动,因此提高了代码的可维护性和可扩展性。
该方式的缺点在于:父类引用不能直接调用子类独有的方法,若调用则需要强制类型转换。
练习:
自定义Account类实现封装,特征:账户余额,提供计算利息并返回的抽象方法。
自定义FixedAccount类继承Account类,实现抽象方法的重写。
自定义TestFixedAccount类,在main()方法中使用多态的语法创建对象并调用计算利息的方法。
2.5 抽象总结
抽象关键字 abstract ,当abstract修饰类,就是抽象类,抽象类不能被实例化,
当 abstract 修饰方法抽象方法没有方法体,继承抽象类,必然要重写抽象方法。
3. 接口
3.1 基本概述
接口就是一种比抽象类还抽象的类,该类型不能实例化对象。
定义类的关键字是class 而定义接口的关键字是interface
继承类的关键字是extends,而实现接口的关键字是implements
当多个类型之间具有相同的行为能力的时候,java中就可以通过接口来进行类型之间的联系。
通过接口可以解决java中单继承所带来的一些类型无法共享的问题。
接口定义了某一些规范,并且需要去遵守
接口不关心类的内部数据,也不关心这些类里方法的实现细节,它只规定这些类里必须提供的某些方法。
3.2 语法格式
修饰符 interface 接口名称 [extends 父接口1,父接口2,......] {
零个到多个常量的定义.........
零个到多个抽象方法的定义......
零个到多个默认方法定义.......(jdk1.8新特性)
零个到多个静态方法的定义.......(jdk1.8新特性)
}
3.3 注意事项
1.接口可以实现多继承,也就是说一个接口可以同时继承多个父接口。
2.实现接口的类如果不能实现所有接口中待重写的方法,则必须设置为抽象类。
3.一个类可以继承自一个父类,同时实现多个接口。
4.接口中的所有成员变量都必须有public static final共同修饰,也就是常量。
5.接口中所有成员方法必须有public abstract共同修饰,也就是抽象方法。
4. 类和接口
4.1 类和接口之间的关系
类和类之间采用继承的关系 使用extends关键 支持单继承
类与接口之间采用实现的关系 使用implements关键字 支持多实现
接口与接口之间采用继承的关系 使用extends关键字 通常认为支持单继承
4.2 抽象类和接口的主要区别
1.定义抽象类的关键是class,而定义接口的关键字是interface。
2.继承抽象类关键字extends,而实现接口关键字 implements
3.继承抽象类支持单继承,而实现接口支持多实现。
4.抽象类有构造方法,但是接口没有
5.接口中的所有的成员变量都必须是常量,而抽象类中可以是变量。
6.接口中的所有成员方法都必须是抽象方法,而抽象类中可以是普通方法
7.接口中增加方法一定影响子类,而抽象类中可以不影响。
4.3 接口中的常量为什么必须使用public static final修饰
public 作用修饰符公开的,也就是说接口的实现类可以使用这个变量。
static 修饰就是表示类的,随着类的加载而存在的
如果不是static的话,就表示属于对象的,只有创建对象才能有它,而接口是不能创建对象的,所以接口的常量定义必须为static。
final 修饰就是保存接口定义的常量不能被实现类去修改,如果没有final的话,由子类随意修改,接口建立常量将没有意义了。
5. 内部类(了解)
5.1 基本概述
当一个类的定义放在另一个类的实体时,则该类叫做内部类,该类所在的类叫做外部类。
在一个类体中可以出现的内容:成员变量、成员方法、构造方法、构造块、静态语句块、静态变量、方法、内部类。
嵌套类:
内部类(成员内部类、局部内部类、匿名内部类)
静态嵌套类
5.2 语法格式
class 外部类类名{
class 内部类类名{
内部类的类体;
}
}
5.3 成员内部类
成员内部类定义在另一个类或接口中的内部类
注意事项:
1.必须先创建外部类对象才能创建成员内部类对象
2.不能含有静态变量、静态代码块、静态方法(除了静态常量)
3.外部类可通过成员内部类的对象调用内部类私有成员
4.成员内部类是一个独立的类,编译成独立的.class文件
作用:
成员内部类既可以访问外部类信息,又可以访问父类信息,从而使多继承的解决方案变得完整。
5.4 局部内部类
局部内部类是定义在方法或代码块里的内部类
注意事项:
1.不能含有静态变量、静态代码块、静态方法
2.只能在定义该类的方法或代码块中使用,必须在使用前定义
3.防伪码它所有方法的局部变量的时候,局部变量必须是有效的常量。
4.是一个独立的类,编译成独立的.class文件
5.只能使用abstract、final修饰
6.定义静态块或方法时候,只能访问外部类的静态成员。
5.5 匿名内部类
匿名内部类是直接使用接口或父类实例化对象时创建没有名字的内部类。
语法格式:
接口/父类类型 引用名 = new 接口/父类类型(){
进行方法的重写;
};
注意事项:
1.必须且仅能继承一个父类或实现一个接口
2.没有class关键字,没有类名
3.是特殊的局部内部类
4.仅能使用一次
5.不能定义构造方法
6.匿名类不能是抽象类
优点以及作用:
匿名内部类可以使我们的代码更加紧凑,简洁、封装性比较好
匿名内部类可以灵活的创建
匿名内部类使用多继承的解决方案比较完整
5.6 静态嵌套类
静态嵌套类定义在另一个类、接口,使用static关键字修饰的嵌套类
注意事项:
1.不需要通过生成外部类对象来生成静态嵌套类对象
2.只能直接访问外部类的静态成员
3.外部类可通过静态嵌套类的对象调用内部类的成员
4.可以定义静态成员变量或静态成员方法。
5.7 静态嵌套类和非静态嵌套类的区别
名称 内部类(非静态嵌套类) 静态嵌套类
定义位置上 成员位置、方法、代码块 只能在外部类的成员位置
组成 实例成员、静态常量、构造方法 实例成员、静态成员、静态代码块、构造方法
对象创建 必须先有外部类对象 不依赖于外部类实例,可以直接实例化
访问外部类 可以直接访问外部类所有成员 只能直接访问外部类的静态成员