Java——多态
什么是多态:
class Anamals{String name;String color;public void barks(){System.out.println("动物叫");}
}
class Cat extends Anamals{@Overridepublic void barks(){System.out.println(name+"喵喵~");}
}
public class Test2 {public static void main(String[] args) {Cat cat = new Cat();cat.name = "xiaoxiao";Anamals anamals = cat;anamals.barks();}
}
这就是多态!!
多态的特征:
1、必须是继承关系。(cat与anamals是继承关系)。
2、需要引用父类的类行初始化子类对象。
3、必须重写父类中的方法。
4、当访问父类中的重写的方法时,调用的确实子类中重写的方法!
以上的特征都会一一解答。
重写:
想要做到多态,重写是必要条件,那么什么是重写?
重写的意思就是,同一个方法,返回值相同,方法名相同,参数列表相同。
返回值和形参都不能改变。即外壳不变,核心重写!
当然如下总结了方法重写的规则:1、子类在重写父类的方法时,一般必须与父类方法原型一致: 返回值类型 方法名 ( 参数列表 ) 要完全一致2、被重写的方法返回值类型可以不同,但是必须是具有父子关系的3、访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类方法被 public 修饰,则子类中重写该方法就不能声明为 protected4、父类被 static 、 private 修饰的方法、构造方法都不能被重写。5、重写的方法 , 可以使用 @Override 注解来显式指定 . 有了这个注解能帮我们进行一些合法性校验 . 例如不小心将方法名字拼写错了 ( 比如写成 aet), 那么此时编译器就会发现父类中没有 aet 方法 , 就会编译报错 , 提示无法构成重写.
咋们一个规则一个规则分析:
1、子类在重写父类的方法时,一般必须与父类方法原型一致: 返回值类型 方法名 (参数列表) 要完全一致。
例如:
父类中:
子类中:
此时子类中重写了父类中的方法:是符合规矩的。
如果此时改变返回值类型,会不会报错呢?
报错啦!
如果改变参数列表呢?
同样报错啦!
2、被重写的方法返回值类型可以不同,但是必须是具有父子关系的(但是必须是父类返回值的派生类)。
例如:
class Anamals{String name;String color;public Anamals barks(){Anamals anamals = new Anamals();System.out.println("动物叫");return anamals;}
}
class Cat extends Anamals{@Override
//Cat类是Anamals的派生类public Cat barks(){Cat cat = new Cat();System.out.println(name+"喵喵~");return cat;}
}
此时虽然返回值类型不同,但是还是可以的。
因为Cat类行是Anamals的派生类。
3、访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类方法被public修饰,则子类中重写该方法就不能声明为 protected。
例如:
父类:
子类:
这个是可以的。
但是如果交换两个访问权限,那就会报错!!
4、父类被static、private修饰的方法、构造方法都不能被重写。
例如:
如果是以上两种情况时:
子类重写会报错:
重写与重载的区别:
方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。
重写的设计原则:
对于已经投入使用的类,尽量不要进行修改。最好的方式是:重新定义一个新的类,来重复利用其中共性的内容,并且添加或者改动新的内容。
静态绑定:
动态绑定:
class B {
public B() {
// do nothing
func();
}
public void func() {
System.out.println("B.func()");
}
}
class D extends B {
private int num = 1;
@Override
public void func() {
System.out.println("D.func() " + num);
}
}
public class Test {
public static void main(String[] args) {
D d = new D();
}
}
大家可以看看这段代码的运行结果是什么?
// 执行结果D . func () 0
解释:
构造 D 对象的同时 , 会调用 B 的构造方法 .B 的构造方法中调用了 func 方法 , 此时会触发动态绑定 , 会调用到 D 中的 func此时 D 对象自身还没有构造 , 此时 num 处在未初始化的状态 , 值为 0. 如果具备多态性, num 的值应该是 1.
向上转型:
在进行多态时,有一个特征:
需要引用父类的类行初始化子类对象。
这个其实发生的就是向上转型!
向上转型:实际就是创建一个子类对象,将其当成父类对象来使用。
语法格式:父类类型 对象名 = new 子类类型()
向上转型的方式:
1. 直接赋值2. 方法传参3. 方法返回
直接赋值:
public class Test2 {public static void main(String[] args) {Anamals anamals = new Cat();;//直接赋值法}
}
方法传参:
public static void anamalsBarks(Anamals a){a.barks();
}
public static void main(String[] args) {Cat cat = new Cat();anamalsBarks(cat);
}
方式返回:
============================================================================ public static Cat anamalsBarks( ){Cat cat = new Cat();return cat; } public static void main(String[] args) {Anamals anamals = anamalsBarks( );anamals.barks(); }================================================================
向下转型:
将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法,但有时候可能需要调用子类特有的方法,此时:将父类引用再还原为子类对象即可,即向下转换
// 向上转型Animal animal = cat ;animal . eat ();animal = dog ;animal . eat ();// 编译失败,编译时编译器将 animal 当成 Animal 对象处理// 而 Animal 类中没有 bark 方法,因此编译失败// animal.bark();// 向上转型// 程序可以通过编程,但运行时抛出异常 --- 因为: animal 实际指向的是狗// 现在要强制还原为猫,无法正常还原,运行时抛出: ClassCastExceptioncat = ( Cat ) animal ;cat . mew ();// animal 本来指向的就是狗,因此将 animal 还原为狗也是安全的dog = ( Dog ) animal ;dog . bark ();
大家可以试试这段代码:
class Animals{int age;String name;public void barks(){System.out.println(name+"hahaha");}public Animals(){System.out.println(" public Animals()");}
}
class Cat extends Animals{public void barks(){System.out.println(name+"喵喵~");}public void eat(){System.out.println(name+"正在吃");}public Cat(String name,int age) {super();this.name = name;this.age = age;System.out.println(" public Cat(String name,int age) ");}
}
public class Test1 {public static void main(String[] args) {Animals cat = new Cat("lala",2);System.out.println(cat.name+" "+cat.age);Cat cat1 = (Cat) cat;cat1.barks();}
}
安全的向下转型:
向下转型用的比较少,而且不安全,万一转换失败,运行时就会抛异常。 Java 中为了提高向下转型的安全性,引入了 instanceof ,如果该表达式为 true ,则可以安全转换。
例如:
==================================================================
public class Test1 {public static void main(String[] args) {Animals cat = new Cat("lala",2);System.out.println(cat.name+" "+cat.age);//将Animals类行转换为Cat类型的判断if(cat instanceof Cat) {Cat cat1 = (Cat) cat;cat1.barks();}} }==================================================================
多态的有点(作用):
认识了多态,那多态是干嘛的,有啥用?
任务:利用继承关系打印图形:
class Graph{public void draw(){System.out.println("画图形");}
}
class Triangle extends Graph{@Overridepublic void draw(){System.out.println("▲");}
}
class Circle extends Graph{@Overridepublic void draw(){System.out.println("🔘");}
}
class Square extends Graph{@Overridepublic void draw(){System.out.println("⏹");}
}
public class Test2 {//画图形public static void main(String[] args) {//画出▲⏹⏹🔘🔘图形,不用多态Triangle triangle = new Triangle();Circle circle = new Circle();Square square = new Square();int[] arr = {1,2,2,3,3};for(int x:arr){if(x == 1){triangle.draw();}else if(x == 2){circle.draw();}else{square.draw();}}}
}
用多态打印:
Graph[] arr = {triangle,circle,circle,square,square};
for (Graph graph:arr) {graph.draw(); }