一、理解多态思想
1.多态的产生
学了继承关系,我们知道继承关系是一种”is A”的关系,也就说子类是父类的一种特殊情况;既然子类是一种特殊的父类,那么我们可不可以认为狗对象/猫对象就是动物类型的对象.
Animal d = new Dog(); //创建一只狗对象
Animal c =new Cat(); //创建一只猫对象
-------------------------------------------------------
当我的代码变成以下的样子的时候,多态就产生了:
Animal a = new Dog();对象(a)具有两种类型:
1)编译类型: 声明对象变量的类型,Animal,表示把对象看出什么类型.
2)运行类型: 对象的真实类型,Dog.运行类型--->对象的真实类型.
编译类型必须是运行类型的父类/或相同.
当编译类型和运行类型不同的时候,多态就出现了.
2.多态定义
对象具有多种形态,对象可以存在不同的形式。Animal a = null;a = new Dog(); //a此时表示Dog类型的形态a = new Cat(); //a此时表示Cat类型的形态3.多态的前提
1)继承关系(类和类);2)实现关系(接口和实现类)在开发中一般都是第二种4.多态的特点
把子类对象赋给父类变量,在运行时期会表现出具体的子类特征(调用子类的方法)。Animal a = new Dog();5.多态的好处
需求: 给饲养员提供一个喂养动物的方法, 用于喂养动物.1)没有多态是代码如下//Super类 动物 class Animal { public void eat() { System.out.println("吃一般食物"); } } //狗类 class Dog extends Animal { public void eat() { System.out.println("吃骨头"); } } //猫类 class Cat extends Animal { public void eat() { System.out.println("吃猫粮"); } } //饲养员 class Feeder { //喂食方法--狗 public void feed(Dog d) { System.out.println("feeding"); d.eat(); } //喂食方法--猫-----重载 public void feed(Cat c) { System.out.println("feeding"); c.eat(); } } //演示多态 class PolymorphismDemo { public static void main(String[] args) { //创建饲养员、狗和猫对象 Feeder f = new Feeder(); Dog d = new Dog(); Cat c = new Cat(); //调用饲养员的喂食方法 f.feed(d); f.feed(c); } }
由代码可知,没有多态是针对不同类型的动物需要提供不同的feed方法喂食。
2)存在多态时,代码如下//Super类 动物 class Animal { public void eat() { System.out.println("吃一般食物"); } } //狗类 class Dog extends Animal { public void eat() { System.out.println("吃骨头"); } } //猫类 class Cat extends Animal { public void eat() { System.out.println("吃猫粮"); } } //饲养员 class Feeder { //喂食 对于不同类型的动物,在运行时体现子类独有的特性 public void feed(Animal a) { System.out.println("feeding"); a.eat(); } } //演示多态 class PolymorphismDemo { public static void main(String[] args) { //创建饲养员、狗和猫对象 Feeder f = new Feeder(); Dog d = new Dog(); Cat c = new Cat(); //调用饲养员的喂食方法 f.feed(d); f.feed(c); } }
统一了喂养动物的行为;从上述例子,可以看出多态的好处:当把不同的子类对象都当作父类类型来看待,可以屏蔽不同子类对象之间的实现差异,从而写出通用的代码达到通用代码
二、多态时方法调用问题
前提:必须先存在多态情况:存在父类:SuperClass,子类:SubClass,方法:doWork.
--------------------------------------------------------------------
测试代码:
SuperClass clz = new SubClass();//多态
clz.doWork();//???输出什么结果
1)doWork方法存在于SuperClass中,不存在于SubClass中.
此时执行结果:编译通过,执行SuperClass的doWork方法.
编译时:去编译类型中寻找doWork()方法;存在--->编译通过;不存在--->编译失败
运行时:先去运行类型中寻找doWork()放法,找不到去父类中找
2) doWork方法存在于 SubClass中 ,不存在于 SuperClass 中.
此时执行结果: 编译错误.
编译时期,会去编译类型(SuperClass)中找是否有doWork方法:
找 到:编译通过.
找不到:编译报错.
3)doWork方法存在于SuperClass和SubClass中.
此时执行结果: 编译通过,执行SubClass的doWork方法.
在运行时期,调用运行类型(SubClass)中的方法.
4) doWork方法存在于 SuperClass 和SubClass中 ,但是doWork是静态方法.此时这种情况,我们称之为隐藏,而不叫方法覆盖.
此时执行结果: 编译通过,执行SuperClass的doWork方法.
静态方法的调用只需要类即可.
如果使用对象来调用静态方法,其实使用的是对象的编译类型来调用静态方法.
和对象没有关系.
三、引用类型转换
1.基本数据类型转换:
1) 自动类型转换: 把小类型的数据 赋给 大类型的变量.(此时的大和小表示的容量范围)
byte b = 12; byte是1个字节
int i = b; int是4个字节
2)强制类型转换: 把大类型的数据赋给 小类型的变量.
short s = (short)i ;short是2个字节
2.引用类型的转换:
引用类型的大和小,指的是父类 和子类的关系.
1) 自动类型转换: 把子类对象赋给父类变量(多态).
Animal a = new Dog();
Object是所有类的根类:
Object obj = new Dog();
2)强制类型转换: 把父类类型对象赋给子类类型变量(当时该父类类型变量的真实类型应该是子类类型).
Animal a = new Dog();
instanceof运算符:判断该对象是否是某个类的实例语法格式:booleanb = 对象A instanceof 类B; //判断A对象是否是B类的实例,如果是,返回true.
另:如果对象A是类B的实例,那么他也是类B的父类的实例。
在开发中 ,有时候 ,我们只想判断是真实类型的实例, 而不想判断为编译类型的实例.此时需要用到getClass()方法,返回该对象的运行类型。Object obj = "ABC";//自动装箱:把String对象赋给Object类型 System.out.println(obj instanceof Object);//true System.out.println(obj instanceof String);//true System.out.println(obj.getClass());//获取对象的运行类型 System.out.println(obj.getClass() ==String.class);//true System.out.println(obj.getClass() ==Object.class);//false
四、组合关系
如果A类为了得到B的功能行为:
如果A类是B类的一种特殊情况,我们就应该采用继承来实现;否则使用组合方式.
继承关系: 子类可以继承到父类中部分的成员,那么此时子类是可以修改到父类的信息的.
继承关系破坏封装,为了复用代码可能会让子类具有不该具有的功能.
-----------------------------------------------------------------------------------
为什么引入继承: 为了代码复用问题.
解决代码复用问题,不一定非要使用继承,也可以使用”包含关系”(has A).
我没钱,但是我想开豪车,我想吃火锅:
方式1: 任一个富豪干爹. 继承关系:
方式2: 把一个富豪绑架在我家里,挟天子以令诸侯! 组合关系/包含.
1)组合关系
2)继承关系
class ArrayUtil { //排序 public void sort(int[] arr) { for (int times = 1; times <= arr.length - 1 ; times ++ ) { for (int i = times; i <= arr.length - 1 ; i ++ ) { if(arr[times - 1] > arr[i]) { swap(arr,times - 1, i); } } } } //交换 static void swap(int[] arr, int index1, int index2) { int temp = arr[index1]; arr[index1] = arr[index2]; arr[index2] = temp; } //打印 public void print(int[] arr) { for (int i = 0; i <arr.length ; i ++ ) { System.out.print(arr[i] +" "); } } } //演示组合关系 class CombinationDemo extends ArrayUtil {
/* //继承关系 public void test() { int[] arr = {-2, 9, 1, 0, 5}; sort(arr); print(arr); } public static void main(String[] args) { new CombinationDemo().test(); }
*/ //组合关系 //创建一个ArrayUtil对象,调用ArrayUtil中的方法 private ArrayUtil util = new ArrayUtil(); public void test() { int[] arr = {-2, 9, 1, 8, 15}; util.sort(arr); print(arr); } public static void main(String[] args) { new CombinationDemo().test(); }
}
五、字段不存在多态特征
//父类 class SuperClass { public String name = "Super.name"; public void doWork() { System.out.println("Super.World!"); } } //子类 class SubClass extends SuperClass { public String name = "Sub.name"; public void doWork() { System.out.println("Sub.World!"); } } //演示:字段不存在多态特征 class FieldDemo { public static void main(String[] args) { SuperClass clz = new SubClass(); clz.doWork();//结果:Sub.World System.out.println(clz.name);//结果:Sub.name } }
通过对象调用字段,在编译时期就已经决定了调用那一块内存空间的数据.
--------->字段不存在覆盖的概念,在多态时,不能有多态特征(在运行时期体现子类特征).
只有方法才有覆盖的概念.
当子类和父类存在相同的字段的时候,无论修饰符是什么(即使private),都会在各自的内存空间中存储数据.