1.抽象类
1)定义:
对象(实例)是现实世界中存在的,每个对象的属性与行为有所不同;类是对具有共性特征的对象抽象的结果,其保留了对象的共性特点,是实例创建的通用模板;抽象类是基于类与类之间的共性特征,将这些具有共性特征的类再进一步抽象形成了抽象类。
抽象类的下层是类,类是不存在的所以抽象类不可以创建对象;类的下层是对象,对象是现实世界存在的,所以类可以创建对象。
2)语法格式(abstract关键字修饰):
[修饰符列表] abstract class 类名{
类体;
}
注意:抽象类是无法实例化的,无法创建对象的,所以抽象类就是用来被子类继承的,继承抽象类的非抽象类可以实例化对象;抽象类无法实例化,但抽象类中也有构造方法,抽象类中的构造方法是供子类使用的:super()。
3)抽象方法:
· 定义:抽象方法表示没有实现的方法,没有方法体的方法。例如:public abstract void dosome();
· 特点:没有方法体,以";"结尾;通过关键字 abstract 修饰。
注意:抽象类中不一定有抽象方法(允许静态/实例方法);有抽象方法的类都必须是抽象类;非抽象类继承抽象类时必须将抽象方法实现,通过重写抽象方法的形式。
2. 接口
1)定义:接口是完全抽象的,抽象类是半抽象的,或者说接口是特殊的抽象类。
2)语法:
[修饰符列表] interface 接口名{}
注意:接口之间可以继承,并且接口支持多继承(一个接口可以继承多个接口);当一个非抽象类实现接口时需要重写接口中的所有抽象方法。
3)组成(两部分):
· 常量;
· 抽象方法,不能有方法体;
注意:接口中的所有元素都是public修饰的;接口中的常量可以省略public static final三个关键字,接口中的方法可以省略public与abstract两个关键字。
4)作用:类似多态,降低代码的耦合度,由于接口是抽象的,面向接口编程就是面向抽象编程,接口是可插拔的;
5)抽象类与接口之间的区别:
· 抽象类是半抽象的,接口是完全抽象的;
· 抽象类中有构造方法,接口中没有构造方法;
· 接口之间支持多继承,类和类之间只能单继承;
· 一个类可以同时实现多个接口,一个抽象类只能继承一个类(单继承);
3. 继承
1)Java继承(目的在于实现代码的复用):
在Java中使用 extends 关键字表示类之间的继承关系(类之间只允许单继承),使用 implement 关键字表示接口与类/接口之间的继承关系(允许实现多个接口,变相多继承)。注意:一个实现类继承抽象类或者实现接口都需要重写其中的 抽象方法。
如果两个类存在继承关系,则子类会自动继承父类的方法和属性,在子类中可以直接调用父类的方法和变量。
当创建一个类时,总是在继承,如果没有明确指出要继承的类,就总是隐式地从根类 Object 进行继承。
2)继承的特点:
· 成员变量的继承:子类可以继承父类 public 和 protected 访问符修饰的成员变量,但不能够继承父类 private 访问符修饰的成员变量(可以通过父类的set/get方法访问);对于 default 访问符修饰的成员变量,如果子类与父类在用一个包下,可以继承,否则不可以;对于子类继承的父类成员变量,如果在子类中出现了同名的成员变量,会发生隐藏现象,即屏蔽父类中的同名成员变量,需要通过 super 关键字访问。
· 实例方法的继承:子类可以继承父类 public 和 protected 访问符修饰的成员方法,但不能够继承父类 private 访问符修饰的成员方法;对于 default 访问符修饰的成员方法,如果子类与父类在用一个包下,可以继承,否则不可以;对于子类继承的父类成员方法,如果在子类中出现了同名的成员方法,会发生覆盖现象,即子类的成员方法会覆盖掉父类的同名成员方法。
· 子类会继承父类的静态方法,但隐藏是针对成员变量与静态方法的(不能在子类中重写),覆盖是针对实例方法(可以在子类中重写)。
· 构造器:子类的继承不仅仅是复制父类的方法与属性,当创建一个子类对象时,该对象必定含有一个父类对象(只不过该父类对象被包含在了子类对象的内部)。同时,为了保证对子类对象的正确初始化,子类必然会在其构造方法中调用父类的构造方法对继承自父类的属性进行初始化(无参构造为隐式调用,有参构造为 super 显示调用)。
4. 多态
1)多态的概念:
现实事物经常会体现出多种形态,如猫,猫是动物的一种,则一只具体的猫图图既是猫也是动物,即出现两种形态。
Java作为面向对象的语言,同样可以描述一个事物的多种形态。如Cat类继承了Animal类,一个Cat的对象便既是Cat,又是Animal。一个Cat对象既可以赋值给一个Cat类型的引用,也可以赋值给一个Animal类型的引用。
最终多态体现为父类的引用指向子类的对象(向上转型): 父类类型 引用名 = new 子类类型()。多态的前提是必须有子父类继承关系或者类实现接口关系,同时,父类的引用在调用方法时,会调用子类重写的方法。
2)向上转型
定义:子 ——> 父 (自动类型转换),通过子类去实例化父类对象;向上转型时,虽然是一个子类对象,但不能访问子类中特有的方法,只能访问父类的方法,或父类子类共有的方法。
//父类Animal,子类Cat
public static void main(String[] args){
Animal a1 = new Cat(); //底层对象是一只猫;向上转型
a1.move();
}
实例:父类型引用指向子类型对象,编译阶段静态绑定父类型方法,运行阶段动态绑定子类型方法。
注意:java程序的分为编译阶段和运行阶段,
· 在编译阶段编译器负责检查语法,并没有通过new创建对象,所以编译器默认为a1的类型为Animal而非Cat,其会在Animal.class 字节码文件中查找 move() 方法,并将该方法静态绑定到a1对象上;
· 在运行阶段创建对象,new关键字在堆内存中创建的java对象是Cat对象,所以运行阶段会动态执行Cat对象的move()方法,属于动态绑定。
*分析程序一定要分析编译阶段的静态绑定和运行阶段的动态绑定,只有编译通过的.class字节码文件才能运行
3)向下转型:
定义:父 ——> 子(强制类型转换),通过父类去实例化子类对象;向下转型时,为了能使子类对象使用子类中特有的方法(向下转型的目的),需要进行强制转换。
实例:
//父类Animal,子类Cat
public static void main(String[] args){
Animal a1 = new Cat(); //底层对象是一只猫;向上转型
Cat x = (Cat)a1; //向下转型
x.move();
}
注意:进行向下转型时:使用instanceof运算符进行判断以避免java.lang.ClassCastException。
instanceof运算符:(用于避免java.lang.ClassCastException,即两个无法强制类型转换的类发生了强制类型转换,通常向下转型要求转型的对象是发生了向上转型的子类实例,即指向子类实例的父类引用)
Java中允许向上转型,也允许向下转型,但无论是向上转型还是向下转型,两种类型之间必须要有继承关系。
4)多态在开发中的用处:
面向抽象编程(父类型),能提高在减少代码修改的前提下提高代码的拓展性,因为父类型可以接纳所有的子类型。
即编译时静态绑定父类型,运行时底层实际的对象是什么就调用该对象的实例方法(仅限于共有方法),对于子类独有的方法,需要通过向下转型为子类的引用后才可以调用。
public class Master{
// 父类Pet, 子类Dog, Cat
public void feed(Dog dog);
public void feed(Cat cat);
}
// 以上代码表示:Master、Dog、Cat的关系很紧密,耦合度高导致拓展能力差
public class Master{
public void feed(Pet pet);
}
// 以上代码表示Master与Dog、Cat的关系脱离了
// Master关注的是Pet类,而Dog和Cat可以源自Pet类,
// 即使用Dog、Cat实例作为参数传入Pet pet中