java的多态
引入的原因
public class Test {
public static void main(String[] args) {
Master master = new Master("tim");
Dg dog = new Dog("金毛");
Cat cat = new Cat("蓝猫");
Fish fish = new Fish("小鱼干");
Bone bone = new Bone("鸡锁骨");
master.feed(dog,bone);
master.feed(cat,fish);
}
}
public void feed(Dog dog,Bone bone) {
System.out println(name+"给"+dog.getName()+"喂"+bone.getName());
}
public void feed(Cat cat,Fish fish) {
System.out.println(name+"给"+cat.getName()+"喂"+fish.getName());
}
随着多个动物,食物的增加,对于代码整体而言,代码复用性不高,不利于代码维护,所以引入多态
多态(羊皮狼)
方法或对象具有多种形态,是面对对象的第三特征,多态是建立在封装和继承基础之上的
(1)方法的多态
重载和重写就体现多态
(2)对象的多态(核心)
- 一个对象的编译类型和运行类型可以不一致
Animal animal = new Dog();
2. 编译类型在定义对象时,就确定了不能改变
3. 运行类型是可以变化的
4. 编译类型看定义时 等号的最左边, 运行类型看 等号的最右边
Animal animal = new Dog();
animal
只是对象引用, new Dog() 才是对象对象的多态: 可以让一个父类的引用 指向 一个子类的对象
animal
的编译类型是Animal
,运动类型是Dog
animal= new Cat();
animal
的编译类型是Animal
,运动类型变成了Cat
练习
package com.hspedu.Poly_.objectpoly_;
public class Dog extends Animal{
public void cry() {
System.out.println("狗:你在狗叫什么...");
}
}
package com.hspedu.Poly_.objectpoly_;
public class Cat extends Animal{
public void cry() {
System.out.println("猫:你在猫叫什么");
}
}
package com.hspedu.Poly_.objectpoly_;
public class Animal {
public static void main(String[] args) {
Animal animal_1 = new Animal();//编译类型是Animal,运行类型是Animal
Animal animal_2 = new Dog();//编译类型是Animal,运行类型是Dog
animal_2.cry();//此时的编译类型是Animal,运行类型是Dog
animal_2 = new Cat();//此时的编译类型仍然是Animal,但运行类型变成了Cat
animal_2.cry();//此时的编译类型是Animal,运行类型是Cat
}
public void cry() {
System.out.println("Animal动物在叫....");
}
}
2.
//animal 编译类型是 Animal,可以指向Animal子类的对象
//food 编译类型是 Food,可以指向Food子类的对象
public void feed(Animal animal,Food food) { System.out.println(this.name+"给"+animal.getName()+"喂"+food.getName());
}
public class Test {
public static void main(String[] args) {
Master master = new Master("Jack");
Dog dog = new Dog("金毛");
Cat cat = new Cat("蓝猫");
Fish fish = new Fish("小鱼干");
Bone bone = new Bone("鸡锁骨");
master.feed(dog ,bone);
master.feed(cat,fish);
}
}
多态的向上转型
-
本质:父类的引用指向了子类的对象
-
语法:
父类类型 引用名 = new 子类类型();
-
特点:编译类型看左边,运行类型看右边
可以调用父类中的所有成员(需要看访问权限),不能调用子类中特有成员
最终运行看子类的具体实现效果
package com.hspedu.hhh; public class ss { public void sss() { System.out.println("sss"); } }
package com.hspedu.hhh; public class Animal extends ss { String name = "动物"; int age = 10; public void sleep() { System.out.println("睡"); } public void run() { System.out.println("跑"); } public void eat() { System.out.println("吃"); } public void show() { System.out.println("hello"); } }
package com.hspedu.hhh; public class cat extends Animal{ public void eat() { //方法重写 System.out.println("猫吃鱼"); } public void catMouse() { System.out.println("猫抓老鼠"); } }
package com.hspedu.hhh; public class Test { public static void main(String[] args) { Animal animal = new cat(); //animal.catMouse() 不能调用,编译后animal只能调用自己成员 // 因为在编译阶段,能调用哪些成员,是由编译类型来决定的 animal.eat();//猫吃鱼 //编译类型是父类,执行父类的成员,但运行时按照特指的子类执行重写父类的方法,所以子类重写是关键, // 而且执行过程中遵循就近原则,先找特指运行的子类,再依次往上找,直到找到为止 animal.sleep(); animal.run(); animal.show(); //特指的运行子类找不到该方法,就会向编译环境父类Animal找仍未果,再从上级父类直到找到为止 animal.sss(); //sss } }
向下转型
为了让父类指向子类,并且父类引用也可以访问子类的所有成员(除特殊权限)
- 语法
子类类型
引用名 = (子类类型) 父类引用`; - 只能强转父类的引用,不能强转父类的对象
- 要求父类的引用必须指向是当前目标类型的对象
- 可以调用子类类型中的所有成员
package com.hspedu.hhh; public class Test { public static void main(String[] args) { Animal animal = new cat(); //animal.catMouse() 不能调用,编译后animal只能调用自己成员 //如果想调用只能使用向下转型 // 向上转型,访问范围看父类的成员,执行看子类;而向下转型,访问范围看子类的成员,执行看子类 cat c_at = (cat) animal;//和new的区别是,没有创建新的对象 c_at.catMouse();//猫抓老鼠 c_at.eat();//猫吃鱼 } }
注意
-
属性没有重写之说,属性的值看编译类型
-
instanceOf
比较操作符,用于判断对象的运行类型是否为xx类型或者xx类型的子类型 -
动态绑定机制
1.当调用对象方法时,该方法会和该对象的内存地址和运行类型绑定
2.当调用属性时,没有动态绑定机制,哪里声明哪里使用
- 语法