Java中的多态、抽象类和接口详解
作者:长歌_w
1.多态
一个特定类型的变量,可以引用多个不同类型的对象,并且能自动调用引用对象的方法,也就是根据引用对象的不同,响应不同的操作
方法重写是多态的基础,在继承中,子类拥有和父类相同的方法(方法名称、参数、返回值)称为重写
package com.itlaobing.demo; public class Pet { public void toHospital() { System.out.println("宠物看病"); } } class Dog extends Pet{ public void toHospital() { System.out.println("狗狗看病"); } } class Cat extends Pet{ public void toHospital() { System.out.println("猫猫看病"); } }
public static void main(String[] args) { Dog dog = new Dog() ; dog.toHospital();//狗狗看病 Cat cat = new Cat(); cat.toHospital(); //猫猫看病 System.out.println("================="); //多态 Pet pet; pet = new Dog(); pet.toHospital();//狗狗看病 pet = new Cat(); pet.toHospital(); //猫猫看病 }
多态中,变量引用的是哪个对象,就执行的是哪个对象中相对应的方法。
多态意味着在一次方法调用中根据包含的对象的实际类型(即实际子类的对象)来决定应该调用哪个子类的方法,而不是由用来存储对象引用变量的类型决定的。当调用一个方法时,为了实现多态操作,这个方法即是在父类中声明过的,也必须是在子类中重写过的方法
1.1 向上转型
由子类类型转换成父类类型,称为向上转型。父类引用指向子类对象
//父类类型 变量 = 子类类型实例; Pet pet = new Dog();//向上转型
多态就是说一个父类可能有多个子类,每个子类都重写了父类的方法(每个子类都有不同方法实现),当父类调用方法时,父类引用指向的是哪个子类,就执行哪个子类的方法。形成了父类引用调用相同的方法时,有不同的实现。
父类引用只能调用父类中有的方法(子类继承自父类的方法/重写的方法)
父类引用不能调用子类扩展的方法(独有的方法)
1.2 向下转型
有父类类型转换成子类类型,称为向下转型。必须要进行强制类型转换。
注意:首先要判断是否属于要强转的类型(instanceof),如果不属于会报错java.lang.ClassCastException
public static void main(String[] args) { // Dog dog = (Dog) new Pet();//向下转型 java.lang.ClassCastException // System.out.println(new Pet() instanceof Dog); // false Pet pet = new Dog(); //向上转型 Dog dog = null; if(pet instanceof Dog){ dog = (Dog) pet; //向下转型 } System.out.println(pet.getClass()); //class com.itlaobing.demo.Dog System.out.println(pet instanceof Dog); //true }
1.3 实现多态的条件
①类之间存在继承关系
②父类引用指向子类对象(向上转型)
③子类要重写父类的方法
1.4多态的特点与使用
特点:①可替换性②可扩充性③接口性④灵活性⑤简化性
使用:①接口②重写③抽象类方法
1.5多态的应用
以父类类型作为方法的参数
父类类型出现的地方,子类都可以出现(使用)
public void toHost(Pet pet) { System.out.print("主人带"); pet.toHospital(); } public static void main(String[] args) { Person person = new Person(); Dog dog = new Dog(); person.toHost(dog); Cat cat = new Cat(); person.toHost(cat); Pet pet = new Pet(); person.toHost(pet); }
使用父类型作为方法的返回值
public Pet getPet(int type) { if(1 == type) { return new Dog(); }else if(2 == type) { return new Cat(); }else { return new Pet(); } }
1.6 多态的注意点
public class Pet { public Pet getInstance() { System.out.println("pet getInstance()"); return new Pet(); } public static void staticMethod() { System.out.println("Pet staticMethod()"); } } class Dog extends Pet{ @Override public Dog getInstance() { System.out.println("Dog getInstance()"); return new Dog(); } public static void staticMethod() { System.out.println("Dog staticMethod()"); } }
public static void main(String[] args) { Pet p = new Dog(); p.getInstance(); // Dog getInstance() p.staticMethod();// Pet staticMethod() }
p.getInstance()
调用的是 Dog 类中的方法,因为在 Dog 类中重写了 Pet 中的 getInstance()
方法,且调用实例方法看的是哪个对象调用就执行哪个对象中的方法。也就是说 by Class
。
p.staticMethod()
调用的是 Pet 类中的方法,因为 staticMethod()
是 static 方法,属于 类。虽然 Dog 类中隐藏了父类的方法,但是调用 static 方法看的是类型,也就是说 by type
,并且这和多态无关。
可以简单的说,调用 static 方法看左边。调用 实例方法 看右边。
2.抽象类
public class Pet { public void toHospital() { System.out.println("宠物看病"); } } public class Dog extends Pet { public void porter() { System.out.println("看门狗"); } public void toHospital() { System.out.println("狗狗看病"); } } public class Cat extends Pet{ public void catchMouse() { System.out.println("猫捉老鼠"); } public void toHospital() { System.out.println("猫猫看病"); } }
从上面的代码可以 发现,父类中的的 toHospital()
方法并没有实际意义,只是定义了一个规范,但是删除之后,调用会报错。失去了多态的特性。抽象类/抽象方法作用就是定义规范
2.1 abstract关键字
在 java 中,被 abstract 关键字修饰的类叫做抽象类,被 abstract 关键字修饰的方法叫做抽象方法。 抽象方法是没有具体实现(没有方法体)的。 abstract 不能和 final 一起使用。
2.2 抽象方法和普通方法的区别
[修饰符] 返回值类型 方法名([参数列表]){ //普通方法 //方法体 } [修饰符] abstract 返回值类型 方法名([参数列表]); //抽象方法
抽象方法没有具体的实现(没有方法体),所以,抽象不能执行。
抽象方法是由继承了抽象类的子类重写后调用子类重写的方法来执行。
区别:
①抽象方法有abstract
修饰
②抽象方法没有方法体
③抽象方法无法执行
④抽象方法不能用private
修饰,因为被private
修饰的成员无法被继承
2.3 抽象类和普通类的区别
[修饰符] abstract class 类名{} //抽象类 [修饰符] class 类名{} //普通类
抽象类中可以有普通方法
如果一个类继承了抽象类,那么这个类必须重写它的抽象方法,或者将类声明为抽象类
抽象类是有构造方法的,但是不能被实例化。因为抽象类中的抽象方法是没有方法体的,导致抽象类不是一个完整的类,因此不允许实例化。
构造方法、类方法( static )不能声明为抽象( abstract )方法
抽象类除了不能被实例化以外,和普通了类没有区别。定义抽象类是为了强迫子类实现抽象方 法,是定义规范的
区别:
①抽象类有abstract
修饰
②抽象类中可以有抽象方法,普通类不能有抽象方法,一个类中只要含有抽象方法,这个类就必须是抽象类,但是抽象类不一定含有抽象方法
③抽象类不能被实例化,需要抽象类变量引用其子类的对象
2.4 本质
上层代码定义规范,不用实现。具体业务实现由子类完成,调用者不用关心。
2.5 抽象类局限
子类重写抽象类的抽象方法可能会出现代码重复的情况,不符合代码复用的要求
3.接口
约定好规范,然后按照规范来做。接口就是定义规范。
java 中的接口作用和生活中类似,它提供一种约定,使实现接口的类在形式上保持一致。
抽象类中可以有普通方法而接口中的方法都是抽象的,如果抽象类诶中的方法都是抽象方法,可以使用java提供的接口表示,因此也可以将接口看做是一个特殊的 抽象类 ,但是采用与抽象类完全不同的语法表示,并且两者的设计理念也不同。
接口不能被 实例化,而且没有构造方法。
3.1 定义接口
[修饰符] interface 接口名{ //接口成员 [public] [static] [final] 数据类型 成员变量名 = 常量; public] [abstract] 返回值类型 方法名称([参数列表]); }
接口的访问权限是public
或package-acces
,与类的访问权限类似
和抽象类不同,定义接口使用interface
关键字
接口中的方法默认是抽象方法,所以可以省略 abstract 修饰符
接口中的方法默认都是 public 的,所以可以省略 public
接口中的变量只能是静态常量( static final ),所以可以省略 static final ,静态常量在定义时就要 赋值,且不可变。
一个接口可以继承其他接口,被继承的接口称为父接口。子接口将继承父接口中声明的常量和抽象方法
3.2 使用接口
使用接口和使用抽象类一样,都是通过子类。子类通过 implements 关键字实现接口,实现接口就必须实现 接口中的抽象方法
public 类名 implements 接口名{ 实现方法 普通方法 属性 }
一个类可以实现多个接口,接口之间使用,
隔开
实现接口的类必须实现接口中定义的所有抽象方法,即使不使用也必须实现它,通常用空方法体实现子类不需要的抽象方法,如果抽象方法有返回值,可返回默认值
接口的实现类中,可以有普通方法
实现的方法必须是 public
的,因为重写方法时,权限不能缩小,只能大于等于被继承的方法的访问权限。
接口与接口之间是继承关系,使用 extends 关键字。多个接口使用 ,
隔开,但是接口不能继承类
3.3 实现多个接口
java 中继承是单继承,使用 extends 关键字;但是一个类可以实现多个接口,使用 implements ,多个 接口之间用 , 隔开。
一个类可以同时继承和实现接口, extends 要在 implements 之前
3.4 jdk8接口新特性
在 jdk8.0 中 default 关键字可用于在接口中修饰方法(默认方法), default 修饰的方法可以有具体 实现,也只能在接口中出现。 default 修饰的方法可以被重写。如果一个类实现了两个接口,这两个接口又同时都包含了一个同名的default
方法,这种情况下编译器会报错。
接口中还可以有 static
修饰的方法,称为静态方法(类方法)。类方法可以直接使用接口名.方法名调用。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。