什么是多态:
class Anamals{
String name;
String color;
public void barks(){
System.out.println("动物叫");
}
}
class Cat extends Anamals{
@Override
public 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{
@Override
public void draw(){
System.out.println("▲");
}
}
class Circle extends Graph{
@Override
public void draw(){
System.out.println("🔘");
}
}
class Square extends Graph{
@Override
public 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(); }