一、多态
多态是继封装、继承之后,面向对象的第三大特性。
多态: 是指同一行为,具有多个不同表现形式。
引入
比较两个对象的内容(属性)是否相同:p1.equals(p2) 需要重写equals方法使满足要求。
//用来判断当前对象的每一个属性和另外一个对象的每一个属性是否相同
@Override
public boolean equals(Object obj){
if(this == obj){
return true;
}
//强转
Person p = (Person)obj;
//如果所有属性都相同
if(this.age == p.age && this.gender == p.gender){
return true;
}else {
return false;
}
}
测试:
Person p1 = new Person(18,true);
Person p2 = new Person(18,true);
System.out.println(p1.equals(p2));
打印输出String的内容:p1.toString() 需要重写toString方法使满足要求。
//重写
@Override
public String toString(){
//?:
return "性别" + (this.gender?"男":"女") + ",年龄" + this.age;
}
测试
System.out.println(p1.toString());//性别男,年龄18
多态产生的必要条件
- 继承或者实现【二选一】
- 方法的重写【意义体现:不重写,无意义】
- 父类引用指向子类对象【格式体现】
多态的体现
多态体现的格式
父类类型 变量名 = new 子类对象;
变量名.方法名();
Fu f = new Zi();
f.method();
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,执行的是子类重写后方法。
定义父类:
public class Person {
//吃饭
public void eat(){
System.out.println("人都要吃饭");
}
//睡觉
public void sleep(){
System.out.println("人都要睡觉");
}
}
定义子类:
public class Student extends Person {
public Student(int age, boolean gender) {
super(age, gender);
}
@Override
public void eat(){
System.out.println("学生吃饭要营养均衡");
}
}
public class Teather extends Person {
public Teather(int age, boolean gender) {
super(age, gender);
}
@Override
public void eat(){
System.out.println("老师吃饭无所谓");
}
}
测试类:
public class Test02 {
public static void main(String[] args) {
//父类引用指向子类对象
Person p1 = new Student();
p1.sleep();//没有发生多态
p1.eat(); //多态
Person p2 = new Teather();
p2.sleep();//没有发生多态
p2.eat();
}
}
父类类型作为方法形式参数,传递子类对象给方法,进行方法的调用,更能体现出多态的扩展 性与便利。多态的好处,体现在,可以使程序编写的更简单,并有良好的扩展。
方法调用四大原则
- 继承链
- 编译看类型,运行看对象
- 就近原则,子类有不找父亲,没有才找父亲
- 发生多态,父类对子类新增方法不可见
二、对象转型
- 自动转了的却把对象身上的方法丢了看不见;
- 强制转的编译过了运行却可能报异常。
- 还有强制都转不了的
- 一个父类的引用类型变量可以“指向”其子类的对象。 (自动类型转换)
- 一个父类的引用不可以访问其子类对象新增加的成员(属性和方法)(子类新增内容对父类是不可见)
- 可以使用 引用 变量 instanceof 类名 来判断该引用型变量所“指向”的 对象是否属于该类或该类的子类。
- 子类的对象可以当作父类的对象来使用称作向上转型(upcasting),反之 称为向下转型(downcasting)
但是无论怎么转,都需要在同一条继承链上
instanceof
使用Java中的instance运算符,在强转前进行一次判断
对象 instanceof 类型
含义: 对象是否是类型或其子类型的对象
结论
- 在引用数据类型中,只有有继承关系的类型才能进行类型转换;
- 类型转换只是转换看待对象的引用的类型,对象本身没有也不可能参与转换;
- 父类引用可以自动指向子类对象,但只能访问和调用到来自于父类的属性和行为;
- 子类的引用不能指向父类或其它子类对象,就算强转也会导致运行失败并抛出ClassCastException;
- 把父类引用赋给子类引用,语法上必须使用强制类型转换,要想运行也成功还必须保证父类引用指向的对象 一定是该子类对象(最好使用instance判断后,再强转)。
- instanceof只能在同一条继承链上的类使用
三、抽象类
抽象类的产生
父类中的方法,被它的子类们重写,子类各自的实现都不尽相同。那么父类的方法声明和方法主体,只有声明还有 意义,而方法主体则没有存在的意义了。我们把没有方法主体的方法称为抽象方法。Java语法规定,包含抽象方法 的类就是抽象类。
抽象方法
没有方法体的方法。
使用 abstract 关键字修饰方法,该方法就成了抽象方法,抽象方法只包含一个方法名,而没有方法体。
修饰符 abstract 返回值类型 方法名 (参数列表);
抽象类
包含抽象方法的类,如果一个类包含抽象方法,那么该类必须是抽象类。
abstract class 类名字 { }
抽象的使用
继承抽象类的子类必须重写父类所有的抽象方法。否则,该子类也必须声明为抽象类。最终,必须有子类实现该父 类的抽象方法,否则,从最初的父类到最终的子类都不能创建对象,失去意义。
public abstract class Shape {
//求周长
public abstract void perimeter();
//求面积
public abstract void area();
}
public class Circle extends Shape {
private int radius;
@Override
public void perimeter() {
double c = 2 * Math.PI * radius;
System.out.println("圆形周长为:" + c);
}
@Override
public void area() {
double s = Math.PI * radius * radius;
System.out.println("圆形面积为:" + s);
}
}
注意
- 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。 - 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。
理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。 - 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计。 - 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象 类。
理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。
四、接口
接口(interface)是抽象方法和常量值的定义的集合。
定义格式
public interface 接口名称 {
// 抽象方法
// 默认方法
// 静态方法
// 私有方法
}
含有抽象方法
抽象方法:使用 abstract
关键字修饰,可以省略,没有方法体。该方法供子类实现使用。
含有默认方法和静态方法
默认方法:使用 default 修饰,不可省略,供子类调用或者子类重写。
静态方法:使用 static 修饰,供接口直接调用。
含有私有方法和私有静态方法
私有方法:使用 private 修饰,供接口中的默认方法或者静态方法调用。
基本实现
类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为接口的子类。实现的动作类似继承,格式相仿,只是关键字不同,实现使用 implements
关键字。
非抽象子类实现接口:
- 必须重写接口中所有抽象方法。
- 继承了接口的默认方法,即可以直接调用,也可以重写。
抽象方法的使用 :
必须全部实现
默认方法的使用
可以继承,可以重写,二选一,但是只能通过实现类的对象来调用。
静态方法的使用
静态与.class 文件相关,只能使用接口名调用,不可以通过实现类的类名或者实现类的对象调用。
私有方法的使用
- 私有方法:只有默认方法可以调用。
- 私有静态方法:默认方法和静态方法可以调用。
接口的多实现
在继承体系中,一个类只能继承一个父类。而对于接口而言,一个类是可以实现多个接口的,这叫做接 口的多实现。并且,一个类能继承一个父类,同时实现多个接口。
- 接口中,有多个抽象方法时,实现类必须重写所有抽象方法。如果抽象方法有重名的,只需要重写一次。
- 接口中,有多个默认方法时,实现类都可继承使用。如果默认方法有重名的,必须重写一次。
- 接口中,存在同名的静态方法并不会冲突,原因是只能通过各自接口名访问静态方法。
- 当一个类,既继承一个父类,又实现若干个接口时,父类中的成员方法与接口中的默认方法重名,子类就近选择执 行父类的成员方法。
接口的多继承
一个接口能继承另一个或者多个接口,这和类之间的继承比较相似。接口的继承使用 extends 关键字,子接口继 承父接口的方法。如果父接口中的默认方法有重名的,那么子接口需要重写一次。
其他成员特点
- 接口中,无法定义成员变量,但是可以定义常量,其值不可以改变,默认使用public static final修饰。
- 接口中,没有构造方法,不能创建对象。
- 接口中,没有静态代码块。
五、碎碎念
依赖 类A和类B没有关系,但是类A需要用到类B,产生依赖关系
查看源代码快捷键 Ctrl + 鼠标左键