Java之抽象类,多态
-
-
抽象类:被abstract所修饰的抽的类。
抽象类的语法格式
【权限修饰符】 abstract class 类名{ } 【权限修饰符】 abstract class 类名 extends 父类{ }
抽象方法的语法格式
【其他修饰符】 abstract 返回值类型 方法名(【形参列表】);
注意:抽象方法没有方法体
public abstract class Animal { public abstract void run(); } public class Cat extends Animal { public void run (){ System.out.println("小猫在墙头走~~~"); } }
注意事项
-
理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。
-
抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。
理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。
-
抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计。
-
抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类。
1、当解决某个问题,或者完成某个功能时,主体的算法结构(步骤)是确定的,只是其中的一个或者几个小的步骤不确定,要有使用者(子类)来确定时,就可以使用模板设计模式
//模板类 public abstract class CalTime{ public long getTime(){ //1、获取开始时间 long start = System.currentTimeMills(); //2、运行xx代码:这个是不确定的 doWork(); //3、获取结束时间 long end = System.currentTimeMills(); //4、计算时间差 return end - start; } protected abstract void doWork(); } public class MyCalTime extends CalTime{ protected void doWork(){ //....需要计算运行时间的代码 } }
同一行为,通过不同的事物,可以体现出来的不同的形态。多态,描述的就是这样的状态。
前提
-
-
方法的重写【意义体现:不重写,无意义】
-
父类引用指向子类对象【格式体现】
多态的体现
多态体现出来的现象: 编译时,看“父类”,只能调用父类声明的方法,不能调用子类扩展的方法; 运行时,看“子类”,一定是执行子类重写的方法体;
多态体现的格式:
父类类型 变量名 = new 子类对象; 变量名.方法名();//这个方法是父类中声明,子类中重写的方法
父类类型:指子类对象继承的父类类型,或者实现的父接口类型。
public abstract class Animal { public abstract void eat(); } class Cat extends Animal { public void eat() { System.out.println("吃鱼"); } public void catchMouse(){ System.out.println("抓老鼠"); } } class Dog extends Animal { public void eat() { System.out.println("吃骨头"); } } public class Test { public static void main(String[] args) { // 多态形式,创建对象 Animal a1 = new Cat(); // 调用的是 Cat 的 eat a1.eat(); //a1.catchMouse();//错误,catchMouse()是子类扩展的方法,父类中没有 /* 多态引用,编译时,看“父类”,只能调用父类声明的方法; 运行时,看“子类”,一定是执行子类重写的方法体; */ // 多态形式,创建对象 Animal a2 = new Dog(); // 调用的是 Dog 的 eat a2.eat(); } }
多态方法和属性分别重名时候的调用方式
//方法看对象(实例) 属性看类型 public class test { public static void main(String[] args) { // TODO 方法的重写 // 子类重写父类的方法 // 如何区分父类,子类中相同的方法,需要采用TODO 动态绑定机制 // 在调用对象的成员方法过程中,将方法和对象的实际内存进行绑定,然后调用 A1 a1 = new A1(); System.out.println(a1.sum()); // 20 B1 b1 = new B1(); System.out.println(b1.sum()); // 40 A1 a2 = new B1(); System.out.println(a2.sum()); // 40 A1 a3 = new B1(); // 属性是不遵循动态绑定机制,所以在哪里声明,在哪里使用 System.out.println(a3.sum()); // 20 } } class A1 { public int i = 10; public int sum() { return getI() + 10; //父类中使用的属性居然在子类中声明? } public int getI() { return i; } } class B1 extends A1 { public int i = 20; // public int sum() { // return i + 20; // } // public int getI() { // return i; // } }
多态的好处
1.多态参数
public class Test { public static void main(String[] args) { // 多态形式,创建对象 Cat c = new Cat(); Dog d = new Dog(); // 调用showCatEat showCatEat(c); // 调用showDogEat showDogEat(d); /* 以上两个方法, 均可以被showAnimalEat(Animal a)方法所替代 而执行效果一致 */ showAnimalEat(c); showAnimalEat(d); } public static void showCatEat (Cat c){ c.eat(); } public static void showDogEat (Dog d){ d.eat(); } public static void showAnimalEat (Animal a){ a.eat(); } }
由于多态特性的支持,showAnimalEat方法的Animal类型,是Cat和Dog的父类类型,父类类型接收子类对象,当然可以把Cat对象和Dog对象,传递给方法。
当eat方法执行时,多态规定,执行的是子类重写的方法,那么效果自然与showCatEat、showDogEat方法一致,所以showAnimalEat完全可以替代以上两方法。
public class TestAnimal { public static void main(String[] args) { Animal[] all = new Animal[4];//可以存储各种Animal子类的对象 all[0] = new Cat(); all[1] = new Cat(); all[2] = new Dog(); all[3] = new Dog(); for (int i = 0; i < all.length; i++) { all[i].eat();//all[i]编译时是Animal类型,运行时看存储的是什么对象 } } }
向上转型
-
向上转型:多态本身是子类类型向父类类型向上转换的过程,这个过程是默认的。
当父类引用指向一个子类对象时,便是向上转型。
父类类型 变量名 = new 子类类型(); 如:Animal a = new Cat(); (大--小)
向下转型
-
向下转型
一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。
使用格式:
子类类型 变量名 = (子类类型) 父类变量名; 如:Cat c =(Cat) a;
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用
abstract class Animal { abstract void eat(); } class Cat extends Animal { public void eat() { System.out.println("吃鱼"); } public void catchMouse() { System.out.println("抓老鼠"); } } class Dog extends Animal { public void eat() { System.out.println("吃骨头"); } public void watchHouse() { System.out.println("看家"); } } public class Test { public static void main(String[] args) { // 向上转型 Animal a = new Cat(); a.eat(); // 调用的是 Cat 的 eat // 向下转型 Cat c = (Cat)a; c.catchMouse(); // 调用的是 Cat 的 catchMouse } }
public class Test { public static void main(String[] args) { // 向上转型 Animal a = new Cat(); a.eat(); // 调用的是 Cat 的 eat // 向下转型 Dog d = (Dog)a; d.watchHouse(); // 调用的是 Dog 的 watchHouse 【运行报错】 } }
变量名/对象 instanceof 数据类型 如果变量/对象属于该数据类型,返回true。 如果变量/对象不属于该数据类型,返回false。
静态方法不能被重写
调用静态方法最好使用“类名.”
外部类 | 成员变量 | 代码块 | 构造器 | 方法 | 局部变量 | |
---|---|---|---|---|---|---|
public | √ | √ | × | √ | √ | × |
protected | × | √ | × | √ | √ | × |
private | × | √ | × | √ | √ | × |
static | × | √ | √ | × | √ | × |
final | √ | √ | × | × | √ | √ |
abstract | √ | × | × | × | √ | × |
native | × | × | × | × | √ | × |
(1)final:和final不能一起修饰方法和类
(2)static:和static不能一起修饰方法
(3)native:和native不能一起修饰方法
(4)private:和private不能一起修饰方法
static和final一起使用:
(1)修饰方法:可以,因为都不能被重写
(2)修饰成员变量:可以,表示静态常量
(4)修饰代码块:不可以,final不能修改代码块
public class MyClass /*extends Object*/ { // ... }