多态(polymorphism)
- 概念:同一个对象(事物),在不同时刻体现出来的不同状态。(编译时期状态和运行时期状态不一致的现象)
- 前提:要有继承或者实现关系、要有方法重写、要有父类/父接口的引用指向子类对象。
- 格式:
1、父类 变量名 = new 子类(); //普通类多态定义的格式
//class Fu{}
//class Zi extends Fu{}
//Fu f = new Zi();
2、抽象类 变量名 = new 抽象类子类(); //抽象类多态定义的格式
//abstract class Fu {}
//class Zi extends Fu {}
//Fu f = new Zi ();
3、接口 变量名 = new 接口实现类(); //接口多态定义的格式
//interface Inter ()
//class AinterImpl implements Inter{}
//Inter i = new AinterImpl ();
4、方法的参数、返回类型、数组的类型都可以多态!!!!
- 成员访问特点:(多态只适用于成员方法,只有在运行期间才能体现)
A:成员变量:
编译看左边,运行看左边。(所有的成员变量取决于编译时类型!!!!)
//编译时期:参考的父类中是否有被调用的成员变量。没有,编译失败。
//运行时期:也是调用父类中的成员变量。
B:成员方法:
编译看左边,运行看右边。(所有的成员方法取决于运行时类型!!!!)
//编译时期:参考父类,如果父类中没有调用的方法,编译失败。
//运行时期:参考父类,优先运行子类中重写后的成员方法。子类若没有,则往父类上找。
C:构造方法:
创建子类对象的时候,访问父类的构造方法,对父类的数据进行初始化。
D:静态方法:
编译看左边,运行看左边。(所有的静态方法取决于编译时类型!!!!)
//静态和类相关,算不上重写,所以,访问还是左边的。
- 优缺点:提高程序的可维护性和可扩展性,但父类/接口声明的引用变量不能访问子类特有功能。(解决办法:向下转型)
- 类型转换格式:
向上转型:
父类 引用变量名 = new 子类(); //Fu f = new Zi ();
向下转型:
子类 变量名 = (子类)父类引用变量; //Zi z = (Zi) f;
//注意:必须保证对象原来创建的是"猫",向下转型的时候也是"猫"!!!!
- 使用 instanceof 关键字来判断对象是否是类的实例:
格式: boolean result = obj instanceof Class
例子: if (adog instanceof Dog){
Dog d = (Dog) adog;
}
抽象(abstract)
- 抽象类:就是不能使用new方法进行实例化的类,即没有具体实例对象的类。抽象类有点类似“模板”的作用,目的是根据其格式来创建和修改新的类。对象不能由抽象类直接创建,只可以通过抽象类派生出新的子类,再由其子类来创建对象。
格式: 在类的声明前面加上关键词abstract
public abstract class Animal {...}
- 抽象方法:只声明返回的数据类型、方法名称和所需的参数,没有方法体,也就是说抽象方法只需要声明而不需要实现。
格式: 在方法的声明前面加上关键词abstract,然后去掉方法体(即“{}”),直接分号结束
public abstract void eat();
- 抽象类一般包含抽象方法和普通方法。(但抽象类也可以没有抽象方法)
- 含有抽象方法的类(包括直接定义了一个抽象方法;或继承了一个抽象父类,但没有完全实现父类包含的所有抽象方法;或实现了一个接口,但没有完全实现接口包含的抽象方法三种情况)只能被定义成抽象类。
- 抽象类的子类必须实现父类的抽象方法。(如果一个子类没有实现父类中的抽象方法,则该子类也成为了一个抽象类!)
- 抽象类中可以包含构造方法, 但是构造方法不能被声明为抽象。
- 抽象类的作用:在不同的类中需要不同的实现、那么这个时候就需要用到抽象类的抽象出这个抽象方法,当继承了这个抽象类的子类,就可以不同实现(这点和接口很像)。抽象类的另一个作用是各个子类都有相同的方法行为的时候,就可以在抽象类中统一实现这个方法。这个就是上面说的抽象类中的普通方法的作用了。这样在各个子类中就不需要再实现了,起到了抽象的作用。
- abstract不能与private、static、final、native并列修饰同一个方法。
1.abstract与private一起使用,相互矛盾
abstract修饰的方法是要给子类重写,private修饰的方法只能本类访问。
2.abstract与static一起使用,无意义
abstact修饰的方法是抽象的,没有实体。而static修饰的方法,类是可以直接调用,调用抽象方法是没有意义的。
3.abstract与final一起使用,相互矛盾
final修饰方法不让子类重写,而abstract修饰的方法就是为了让子类重写。
接口(interface)
- 概念:(官方解释)Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。(自己理解)接口可以理解为一种特殊的类,里面全部是由全局常量和公共的抽象方法所组成。接口是解决Java无法使用多继承的一种手段,但是接口在实际中更多的作用是制定标准的。或者我们可以直接把接口理解为100%的抽象类,既接口中的方法必须全部是抽象方法。(JDK1.8之前可以这样理解)
- 格式:
接口声明格式: public interface 接口名 {
[public static final] 常量1;
[public static final] 常量2;
...
[public abstract] 方法1;
[public abstract] 方法2;
...
//接口中的属性是常量,即使定义的时候不添加public static final修饰符,系统也会自动添加
//接口中的方法只能是抽象方法,即使定义的时候不添加public abstract修饰符,系统也会自动加上
//注意在声明的时候要给变量赋予初值
}
实现声明格式: public class Dog extends Canine implements 接口1,接口2,... {
//在这里重写接口1,接口2,...的所有方法
...
}
- 一个接口也能够拥有方法和属性,但是在接口中声明的所有方法默认是抽象的。
- 接口的子类(如果不是抽象类)必须实现接口之中的全部抽象方法。
- 一个类可以实现一个或多个接口(支持多继承),不同继承树的类也可以实现相同的接口。
- 直接向接口中扩展方法可能带来问题:所有实现原来接口的类将因为接口的改变而不能正常工作。(解决办法:需要通过继承扩展接口,即一个接口可以 extends 另一个接口,或者另一些接口,接口也可以继承,并且可以多继承)
扩展接口格式: public interface 接口名 extends 父接口1,父接口2,... {...}
- 一个接口可以有多个父接口,子接口继承父接口的所有常量和方法。
- 可以用接口作为参数或返回类型,这样就可以传入任何有实现该接口的子类。
- 在类的继承中,只能做单重继承,而实现接口时,一次则可以实现多个接口,每个接口间使用逗号“,”分隔。这时就可能出现常量或方法名冲突的情况,解决该问题时,如果常量冲突,则需要明确指定常量的接口,这可以通过“接口名.常量”实现。如果出现方法冲突时,则只要实现一个方法就可以了。
- 与Java的类文件一样,接口文件的文件名必须与接口名相同。
- 如果一个类实现了一个接口中要求的所有的方法,然而没有提供方法体而仅仅只有方法标识,那么这个类一定是一个抽象类。
- 我们不能直接去实例化一个接口,因为接口中的方法都是抽象的,是没有方法体的。但是,可以使用接口类型的引用指向一个实现了该接口的对象,并且可以调用这个接口中的方法。
抽象类与接口的区别:
- 接口是对动作的抽象,抽象类是对本质的抽象。
抽象类表示的是,这个对象是什么。接口表示的是,这个对象能做什么。
比如,男人和女人,他们的抽象类是人类,而猫和狗的抽象类是宠物类。
人类可以吃东西,宠物类也可以吃东西,但是两者不能混为一谈,因为有本质的区别。
这个“吃东西”是一个动作,你可以把“吃东西”定义成一个接口,然后让两个类去实现它的方法。
一个类只能继承一个类或抽象类,正如人不可能同时是动物类又是植物类,但是可以实现多个接口,例如,吃饭接口、呼吸接口等。
- 抽象类可以有具体的方法和属性, 接口只能有抽象方法和不可变常量(final)。
- 抽象类主要用来抽象类别,接口主要用来抽象功能。
- 抽象类实现接口时,接口的方法在抽象类中可以被实现也可以不被实现,而普通实现接口必须实现所有接口方法。
- 当你关注一个事物的本质的时候,用抽象类;当你关注一个操作的时候,用接口。