多态&强制类型转换&抽象类和接口
1.多态
- Java引用变量有俩个类型,一个是编译时类型,一个是运行时类型。编译时类型由声明该变量使用类型决定,运行时类型由实际赋给该变量的对象决定。如果编译时类型和运行时类型不一致,就可能出现多态。
- 多态存在的三个必要条件:
一,要有继承;
二,要有重写(重写是指子类对父类同一方法的执行代码块重写,而重载是指同一个类下,同一方法名,不同形参的方法);
三,父类引用指向子类对象(左边为父类,右边为子类)。
接下来我们用一个小题目彻底理解多态:
(一)相关类:
class A ...{
public String show(D obj)...{
return ("A and D");
}
public String show(A obj)...{
return ("A and A");
}
}
class B extends A...{
public String show(B obj)...{
return ("B and B");
}
public String show(A obj)...{
return ("B and A");
}
}
class C extends B...{}
class D extends B...{}
- (二)问题:一下输出结果是什么?
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
System.out.println(a1.show(b)); ①
System.out.println(a1.show(c)); ②
System.out.println(a1.show(d)); ③
System.out.println(a2.show(b)); ④
System.out.println(a2.show(c)); ⑤
System.out.println(a2.show(d)); ⑥
System.out.println(b.show(b)); ⑦
System.out.println(b.show(c)); ⑧
System.out.println(b.show(d)); ⑨
- (三)答案
① A and A
② A and A
③ A and D
④ B and A
⑤ B and A
⑥ A and D
⑦ B and B
⑧ B and B
⑨ A and D
在解析答案之前我希望能先讲解一下强制类型转换:
基本数据类型之间强制转换只能在数值类型之间转换,但是数值类型和布尔类型无法进行类型转换。
List《String》mlist = new ArrayList<>(); 因为子类是一种特殊的父类,因此Java允许把一个子类对象直接赋值给一个父类引用变量,无须任何类型转换,这就是向上转型,向上转型由系统自动完成。 向上转型,运行时会遗忘子类中与父类对象中不同的方法,也会覆盖与父类中相同的方法-重写(方法名,参数都相同)。
子类转父类是向上转型,系统自动完成。父类转子类是向下转型,属于不安全行为,试想,子类中有些方法和成员变量父类没有,如何把没有的东西变为有呢?
对上面答案解析:
- 第四个输出:子类B实例 new B() 赋给 父类A实例 a2,这是向上转型的结果;这时候a2可以调用:show(D obj),show(A obj),向上转型时子类里不同的方法被遗忘,所以show(B obj)被遗忘;子类会对父类的方法进行重写覆盖。
2.抽象类
-抽象方法:在了解抽象类之前,先来了解一下抽象方法。抽象方法是一种特殊的方法:它只有声明,而没有具体的实现,即没有方法体{ }。抽象方法的声明格式为:
abstract void fun();
抽象方法必须用abstract关键字进行修饰。如果一个类含有抽象方法,则称这个类为抽象类,抽象类必须在类前用abstract关键字修饰。
抽象类不能被实例化,无法使用new关键字来调用抽象类的构造器创建抽象类的实例。抽象类的构造器不能用于创建实例,主要用于被其子类调用—->我的理解是子类构造器super() 调用父类构造器,为抽象类里面的成员变量赋值。
包含抽象方法的类(1.包括直接定义一个抽象方法;2.或继承一个抽象父类,但没有完全实现其父类的抽象方法;3.或实现了一个借口当没有完全实现接口的抽象方法)只能被定义为抽象类。
如果一个类继承于一个抽象类,则子类必须实现父类的抽象方法。如果子类没有实现父类的抽象方法,则必须将子类也定义为为abstract类。
包含抽象方法的类称为抽象类,但并不意味着抽象类中只能有抽象方法,它和普通类一样,同样可以拥有成员变量和普通的成员方法。
下面介绍一个例子:
建立一个Shape抽象类,代表形状,求面积和周长两个方法
建立一个正方形类, 长方形 ,圆类 作为抽象类的子类,
通过抽象类来求面积和周长
class Test4 {
Shape s;
Test4(Shape s){
this.s = s;
}
public double getTwo(Shape h){
double area = h.getArea();
return 2*area;
}
public static void main(String [] args){
System.out.print("tthellonn");
Shape s1 =new Square(1);
System.out.println("正方形的面积"+s1.getArea());
System.out.println("正方形的周长"+s1.getPan());
Shape s2 = new Rectangle(1,2);
System.out.println("长方形的面积"+s2.getArea());
System.out.println("长方形的周长"+s2.getPan());
Shape s3 = new Circle(1);
System.out.println("圆的面积"+s3.getArea());
System.out.println("圆的周长"+s3.getPan());
Test4 t = new Test4(s1);
System.out.println("正方形面积的两倍"+t.getTwo(s1));
}
}
abstract class Shape{//抽象类
public abstract double getArea();//抽象方法
public abstract double getPan();
}
class Square extends Shape{
protected double len;
Square(double len){
this.len = len;
}
public double getArea(){
return len*len;
}
public double getPan(){
return 4*len;
}
}
class Rectangle extends Square{
private double wid;
Rectangle(double len,double wid){
super(len);
this.wid = wid;
}
public double getArea(){
return wid*len;
}
public double getPan(){
return (wid+len)*2;
}
}
class Circle extends Shape{
private double r;
Circle (double r){
this.r = r;
}
public double getArea(){
return Math.PI*r*r;
}
public double getPan(){
return Math.PI*r*2;
}
}
3.接口
- 接口,英文称作interface,在软件工程中,接口泛指供别人调用的方法或者函数。它是对行为的抽象。在Java中,定一个接口的形式如下:
(public) interface interfaceName{
}
接口中可以含有 变量和方法。但是要注意,接口中的变量会被隐式地指定为public static final变量(并且只能是public static final变量,用private修饰会报编译错误),而方法会被隐式地指定为public abstract方法且只能是public abstract方法(用其他关键字,比如private、protected、static、 final等修饰会报编译错误),并且接口中所有的方法不能有具体的实现,也就是说,接口中的方法必须都是抽象方法。从这里可以隐约看出接口和抽象类的区别,接口是一种极度抽象的类型,它比抽象类更加“抽象”,并且一般情况下不在接口中定义变量。
要让一个类遵循某组特地的接口需要使用implements关键字,具体格式如下:
class ClassName implements interface1,interface2...{
}
可以看出,允许一个类遵循多个特定的接口。如果一个非抽象类遵循了某个接口,就必须实现该接口中的所有方法。对于遵循某个接口的抽象类,可以不实现该接口中的抽象方法。