写在前面
博客写到这里,我们可以说对Java语法有了初步的了解了,多态是三大特点里面最抽象的形式.我们需要一点点来了解.
向上转型
我们不是要学习多态吗?,向上转型是什么玩意儿,这是我们学习多态的基础.向上转型就是父类引用子类的对象.
在谈这之前,我们需要知道什么是上,什么是下.
用代码来表示就是
class Animal { } class Dog extends Animal{ } public class TestDemo { public static void main(String[] args) { Animal animal = new Dog(); // 这就是向上转型 } }
## 发生向上转型的方式
发生向上转型的方式有以下三种方式
- 直接赋值
- 函数传参
- 作为函数的返回值
下面我们一一举例
直接赋值
class Animal { } class Dog extends Animal{ } public class TestDemo { public static void main(String[] args) { Animal animal = new Dog(); //父类 引用 子类 } }
函数传参
class Animal { } class Dog extends Animal{ } public class TestDemo { public static void func(Animal ani) { } public static void main(String[] args) { Dog dog = new Dog(); func(dog); } }
返回值
public static Animal func(Animal ani) { Dog dog = new Dog(); return dog; }
向上转型的注意事项
父类引用子类的对象后,只能够使用父类里面的字段和方法,我们要注意这一点
class Animal { public String name; public int age; } class Dog extends Animal{ public int a = 10; } public class TestDemo { public static void main(String[] args) { Animal animal = new Dog(); animal.a = 100; } }
要是父类和子类都存在一个相同的字段,通过父类的引用这个字段出现的父类的字段
class A { public int n = 0; } class B extends A { public int n = 1; } public class TestDemo { public static void main(String[] args) { A a = new A(); System.out.println(a.n); } }
向上转型的作用
出现一个事物肯定是这个有用,我们来说说向上转型的作用,我们看看下面的代码
我们可以很容易的得出结果,很简单
class Animal { public String name; public int age; public Animal(String name,int age) { this.name = name; this.age = age; } public void eat() { System.out.println("我叫" + this.name + ",今年"+this.age + "岁了"); } } class Dog extends Animal{ public Dog(String name,int age) { super(name,age); } } public class TestDemo { public static void main(String[] args) { Animal animal = new Dog("hehe",18); animal.eat(); } }
那要是下面的代码呢?我们就很疑惑了
class Animal { public String name; public int age; public Animal(String name,int age) { this.name = name; this.age = age; } public void eat() { System.out.println("我叫" + this.name + ",今年"+this.age + "岁了"); } } class Dog extends Animal{ public Dog(String name,int age) { super(name,age); } public void eat() { System.out.println("我是子类中的eat方法"); } } public class TestDemo { public static void main(String[] args) { Animal animal = new Dog("hehe",18); animal.eat(); } }
这就是动态绑定,这是我们多态的核心,下面我会说一下为何会先这种情况
动态绑定
说到动态绑定,我还是更喜欢叫他运行时绑定,我们对于上面现象可能会感觉到新奇,多态的出现大大优化了我们使用代码的能力.
使用动态绑定需要满足一下条件
- 父类引用子类的对象
- 子类中存在重写的方法
重写
我记得不久前我们看过函数的重载,这里的重写的形式一样,但是内含完全不一样,所谓的重写就是我们重新在子类中再次些父类中的方法,上面的eat方法就是重写
重写也要满足一定的要求
- 父类和子类都有
- 方法名相同
- 参数类型和数量一样
- 返回类型也相同,但是要是返回类型构成协变类型,也可以有那么稍稍不同,注意一下就可以了
- 子类中的访问修饰限定符 的权限>= 父类的
- private 修饰方法不能重写,final修饰的也不能重写
为何叫动态绑定
我们为何取这个名字,为啥不叫阿猫阿狗,这是有原因的,我们反编译一下(如何编译不用了解)
我们很疑惑,调用的明明是父类里面的eat方法,为什么打印出的结果和我们想的不一样,这就是动态绑定,也就是说,动态绑定是在运行代码的时候出在的,也叫运行时绑定
存在静态绑定吗
存在的,我们之前学的函数重载就是静态绑定
向下转型
抱歉,有向上转型,那就有向下转型,就是子类对父类的引用,不过我们并不建议这么做,会出现一定的风险
class Animal { public String name; public int age; public Animal(String name,int age) { this.name = name; this.age = age; } } class Bird extends Animal { public int a = 10; public Bird(String name, int age) { super(name, age); } public void fly() { System.out.println("我叫" + this.name + ",正在飞"); } } public class TestDemo { public static void main(String[] args) { Animal animal = new Bird("feie",18); Bird bird = (Bird) animal; bird.fly(); } }
理解多态
到这里,我们就可以很容易理解多态了,我们使用多态就要依赖动态绑定和重写方法
class Shape {
public void draw() {
}
}
class Triangle extends Shape {
@Override
public void draw() {
System.out.println("打印一个🔺");
}
}
class Round extends Shape {
@Override
public void draw() {
System.out.println("打印一个⚪");
}
}
public class TestDemo {
public static void main(String[] args) {
Shape shape1 = new Triangle();
shape1.draw();
Shape shape2 = new Round();
shape2.draw();
}
}
下面的代码就可以很好的表示多态的优点
public class TestDemo {
public static void drawMap(Shape shape) {
shape.draw();
}
public static void main(String[] args) {
drawMap(new Triangle());
drawMap(new Round());
}
}
多态的好处
- 封装使得代码的安全性提高
- 多态的核心都是让调用者不必关注对象的具体类型 ,这是降低用户使用成本的一种重要方式
378910375)]
下面的代码就可以很好的表示多态的优点
public class TestDemo {
public static void drawMap(Shape shape) {
shape.draw();
}
public static void main(String[] args) {
drawMap(new Triangle());
drawMap(new Round());
}
}
[外链图片转存中…(img-3Kd0YqXw-1648378910375)]
多态的好处
- 封装使得代码的安全性提高
- 多态的核心都是让调用者不必关注对象的具体类型 ,这是降低用户使用成本的一种重要方式