一、概述
1.定义
同类型的对象,表现出的不同形态。(对象的多种形态)
2.表现形式
父类类型 对象名称 = 子类对象;
Fu f = new Zi();
3.前提
①有继承/实现关系
②有父类引用指向子类对象
③有方法重写
//父类
public class Person {
String name;
int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void show(){
System.out.println("person信息为:"+name+age+"岁");
}
}
//子类
public class Teacher extends Person{
public Teacher() {
}
public Teacher(String name, int age) {
super(name, age);
}
@Override
public void show() {
System.out.println("teacher信息为:"+getName()+getAge()+"岁");
}
}
//子类
public class Student extends Person {
public Student() {
}
public Student(String name, int age) {
super(name, age);
}
@Override
public void show() {
System.out.println("student信息为:" + getName() + getAge() + "岁");
}
}
测试类:
public class Test {
public static void main(String[] args) {
Student s=new Student("张三",23);
Teacher t=new Teacher("李四",24);
register(s);
register(t);
}
public static void register(Person p){
p.show();
}
}
运行结果:
根据register方法中实参传递的对象不同,调用不同show方法
二、多态中调用成员的特点
1.变量调用:
编译看左边,运行也看左边
编译看左边:javac编译代码时,会看左边的父类中有没有该变量。如果有,编译成功,反之报错
运行也看左边:java运行代码时,实际获取的就是左边父类中成员变量的值
class Animal {
String name = "动物";
}
class Dog extends Animal {
String name = "小狗";
}
class Cat extends Animal {
String name = "小猫";
}
public class Test {
public static void main(String[] args) {
//创建方式(多态方式)-->Fu f = new Zi();
Animal a = new Dog();
//调用成员变量:编译看左边(父类:Animal),运行也看左边(父类:Animal)
System.out.println(a.name);//动物
}
}
在子类的对象中,会把父类的成员变量也继承下来。
用多态方式创建对象,变量是父类Animal类型的,编译和运行时都会去对象的父类空间中找该属性。
如果不用多态方式,正常创建对象(Dog d = new Dog(); ),变量是子类Dog类型的,就会先去该对象本类的空间里面找,找不到再去父类空间里面找。
2.方法调用:
编译看左边,运行看右边
编译看左边:javac编译代码时,会看左边的父类中有没有该方法。如果有,编译成功,反之报错
运行看右边: java运行代码时,实际上运行的是右边子类中的方法。
class Animal {
public void show(){
System.out.println("Animal------show方法");
}
}
class Dog extends Animal {
@Override
public void show(){
System.out.println("Dog------show方法");
}
}
class Cat extends Animal {
@Override
public void show(){
System.out.println("Cat------show方法");
}
}
public class Test {
public static void main(String[] args) {
//创建方式(多态方式)-->Fu f = new Zi();
Animal a = new Dog();
//调用成员方法:编译看左边(父类:Animal),运行看右边(子类:Dog)
a.show();//Dog------show方法
}
}
如果子类对方法进行了重写,那么在虚方法表中,是会把父类的方法进行覆盖的。
采用多态方式创建对象,编译时先看父类的虚方法表里面有没有该方法。
实际运行时,执行的是子类的虚方法表中的show方法,由于该方法已经重写过,被覆盖了,所以真正运行的还是子类中的show方法。
三、多态的优缺点
1.优点:
①在多态形式下,右边对象可以实现解耦合,便于扩展和维护
②定义方法时,使用父类类型作为参数,可以接收所有子类对象,体现多态的扩展性与便利。
2.缺点:
不能使用子类的特有功能
原因:当调用成员方法时,编译看左边,也就是看左边的父类中有没有这个方法,如果没有直接报错。因为子类的特有方法父类中并没有,所以不能使用。
解决方法:强制转换成子类类型
class Animal {
public void eat() {
System.out.println("动物在吃东西");
}
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃骨头");
}
//特有方法
public void lookHome() {
System.out.println("狗看家");
}
}
public class Test {
public static void main(String[] args) {
Animal a = new Dog();
//a.lookHome(); 报错
Dog d = (Dog) a;
d.lookHome();//狗看家
}
}
注:
①可以转换成真正的子类类型,从而调用子类独有功能
②转换类型必须与真实对象类型一致,不一致会报错
class Animal {
public void eat() {
System.out.println("动物在吃东西");
}
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃骨头");
}
//特有方法
public void lookHome() {
System.out.println("狗看家");
}
}
class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃鱼");
}
//特有方法
public void catchMouse(){
System.out.println("猫捉老鼠");
}
}
public class Test {
public static void main(String[] args) {
Animal a = new Dog();
//转换对象类型:Cat 真实对象类型:Dog --> 报错
Cat c=(Cat) a;
c.catchMouse();
}
}
运行结果:
③转换的时候用instanceof关键字进行类型判断
class Animal {
public void eat() {
System.out.println("动物在吃东西");
}
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃骨头");
}
//特有方法
public void lookHome() {
System.out.println("狗看家");
}
}
class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃鱼");
}
//特有方法
public void catchMouse(){
System.out.println("猫捉老鼠");
}
}
public class Test {
public static void main(String[] args) {
Animal a = new Dog();
if(a instanceof Dog){
Dog d = (Dog) a;
d.lookHome();//狗看家
} else if (a instanceof Cat) {
Cat c = (Cat) a;
c.catchMouse();
}else{
System.out.println("没有这个类型,无法转换");
}
}
}
JDK14新特性: 判断和强转合并
if(a instanceof Dog d){}
先判断a是否是Dog类型,如果是,则强转成Dog类型,转换之后变量名为d
如果不是,则不强转,结果直接是false
class Animal {
public void eat() {
System.out.println("动物在吃东西");
}
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃骨头");
}
//特有方法
public void lookHome() {
System.out.println("狗看家");
}
}
class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃鱼");
}
//特有方法
public void catchMouse(){
System.out.println("猫捉老鼠");
}
}
public class Test {
public static void main(String[] args) {
Animal a = new Dog();
if(a instanceof Dog d){
d.lookHome();//狗看家
}else if(a instanceof Cat c){
c.catchMouse();
}else{
System.out.println("没有这个类型,无法转换");
}
}
}
3.引用数据类型的类型转换
①自动类型转换
子类类型自动转换为父类类型
②强制类型转换
父类类型强制转换为子类类型