一 继承(Inheritance)
1.概述
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。多个类可以称为子类(subclass),单独这个类称为父类或者超类(superclass)。继承其实就是“is a”的关系。
子类继承父类:可以修改父类的状态或重载父类的行为;可以添加新的状态和行为。
好处:可以提高程序的抽象程度;实现代码重用,提高开发效率和可维护性。
2.特点
Java只支持单继承(一个类只能有一个直接父类)和多层继承,不支持多继承。
Java中的继承是通过extends关键字来实现的。如果没有extends子句,则该类默认为java.lang.Object的子类。
Student类继承Person类:
class Student extends Person {
String school;
int score;
boolean isGood(){ return score>80; }
//…
}
继承关系UML如图:
3.super关键字
super代表父类的内存空间的标识。
用法:
a.使用super访问父类的域和方法。正是由于继承,使用this可以访问父类的域和方法。但有时为了明确地指明父类的域和方法,就要用关键字super。
b.使用父类的构造方法。使用时,super()必须放在第一句。
4.覆盖(Override)
子类重新定义与父类同名的方法,实现对父类方法的覆盖(Override)。通过方法的覆盖,能够修改对象的同名方法的具体实现方法。
总结一下重载(overload)和覆盖(or重写,override)的区别:
重载(overload):在同一个类中,两个方法的方法名相同而参数类型、参数个数或者顺序不同,即为重载方法。不能重载只有返回值不同的方法名。重载是一个类中多态性的体现。
覆盖(override):子类在继承父类的时候子类中定义与父类中方法名和参数完全相同的方法,子类在调用这一方法时自动调用子类的该方法,父类方法即被覆盖了。覆盖是父类与子类之间多态性的体现。
什么时候使用覆盖呢?
当子类需要父类的功能,而功能主体子类有自己特有内容时,可以复写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容。
注意:a.父类中的私有方法不可以被覆盖
b.父类为static的方法无法被覆盖。
c.覆盖时,子类方法权限一定要不小于父类方法权限。
5.构造函数
子类构造函数执行时,父类构造函数也运行了。子类中所有的构造函数默认都会访问父类中空参数的构造函数,因为子类的每一个构造函数默认第一行有一条隐式的语句super();
为什么子类实例化的时候要访问父类中的构造函数呢?
因为子类继承了父类,获取到了父类中内容(属性),所以在使用父类内容之前,要先看父类是如何对自己的内容进行初始化的。
注意:a.当父类中没有空参数的构造函数时,子类的构造函数必须通过this或者super语句指定要访问的构造函数。
b.子类构造函数中如果使用this调用了本类构造函数,那么默认的super();就没有了,因为super和this都只能定义在第一行,所以只能有一个。但是可以保证的是,子类中肯定会有其他的构造函数访问父类的构造函数。
class Parent{
int num;
Parent(){
num = 10;
System.out.println("A parent run");
}
Parent(int x){
System.out.println("B parent run "+x);
}
public void show(){
System.out.println("Parent show run");
}
}
class Son extends Parent{
Son(){
//super();//默认调用父类中的空参数的构造函数
System.out.println("C son run "+num);
}
Son(int x){
super(4);
System.out.println("D son run "+x);
}
//子类Son重写父类Parent的show方法,若父类的show方法为private或者此处show方法为private,程序都会报错。
public void show(){
System.out.println("Son show run");
}
}
public class ExtendDemo{
public static void main(String[] args){
Son son = new Son();
son.show();
System.out.println("------------------");
new Son();
System.out.println("------------------");
new Son(6);
}
}
6.final关键字
在程序中,用final修饰固定的类、方法或变量,以增强程序的阅读性。
final类:不可以被继承
final方法:不可以被覆盖
final变量:值一旦给定就不能更改,如Integer. MAX_VALUE(表示最大整数)、Math.PI(表示圆周率)就是这种常量。变量名所有字母都大写,单词间用_连接。
在定义static final域时,若不给定初始值,则按默认值进行初始化(数值为0,boolean型为false,引用型为null)。
在定义final字段时,若不是static的域,则必须且只能赋值一次,不能缺省。这种域的赋值的方式有两种:一是在定义变量时赋初始值,二是在每一个构造函数中进行赋值。
在定义final局部变量时,也必须且只能赋值一次。它的值可能不是常量,但它的取值在变量存在期间不会改变。
二、 抽象类(Abstract Type)
1.概述
抽象就是从多个事物中将共性的、本质的内容抽取出来。
抽象类:用abstract修饰、没有方法体的方法称为抽象方法,包含抽象方法的类就是抽象类,抽象类也用abstract修饰。抽象方法的具体实现由子类完成。抽象方法的作用在为所有子类定义一个统一的接口。
2.特点
抽象方法的作用在为所有子类定义一个统一的接口。对抽象方法只需声明,而不需实现,即用分号(;)而不是用{},格式:abstract returnTypeabstractMethod( [paramlist] );
抽象类不可以被实例化,也就是不可以用new创建对象。因为:
a.抽象类是具体事物抽取出来的,本身是不具体的,没有对应的实例。
b.抽象类即使创建了对象,其抽象方法也没有意义。
抽象类通过子类实例化,而子类需要覆盖抽象类中所有的抽象方法后才可以创建对象,否则该子类也是抽象类。
注意:1)抽象类中可以有构造函数,构造函数将子类对象初始化。
2)抽象关键字不可以和private、static、final关键字共存。
3)抽象类中可以没有抽象方法。目的是不让该类创建对象,如AWT的适配器对象就是这种类。
4)抽象类一定是父类。
/*
公司中程序员有姓名,工号,薪水,工作内容;项目经理除了姓名,工号,薪水,工作内容外还有奖金;
程序员和经理的共同部分进行抽取。
*/
//Employee抽象类
abstract class Employee{
private String name;
private String id;
private double pay;
Employee(String name, String id, double pay){
this.name = name;
this.id = id;
this.pay = pay;
}
//省略setter和getter方法
public abstract void work();
}
//Programmer
class Programmer extends Employee{
Programmer (String name, String id, double pay){
super(name, id, pay);
}
public void work(){
System.out.println("code");
}
}
//Manager
class Manager extends Employee{
private int bonus;
Manager(String name, String id, double pay, int bonus){
super(name, id, pay);
this.bonus = bonus;
}
public void work(){
System.out.println("manage");
}
}
public class EmployeeDemo {
public static void main(String[] args){
Programmer programmer = new Programmer("Andy", "123", 12000);
programmer.work();
Manager manager = new Manager("Hans", "345", 30000, 6000);
manager.work();
}
}
三、 接口(Interface)
1.概述
接口是某种特征的约定。
“In object-oriented programming, a protocol or interface is a common means for unrelated objects to communicate with each other. These are definitions of methods and values which the objects agree upon in order to co-operate.” ——From Wikipedia
如上图,Flyable即是一个接口,它实现了takeoff()、fly()和land()三个功能,实现它没有类的限制,Airplane,Bird 和 Superman 都可以实现接口的这三个功能。因此通过接口可以实现不相关类的相同行为,而不需要考虑这些类之间的层次关系。从而从一定意义上实现了多重继承。
定义接口用interface,实现接口用implements。
2.特点
完整的接口声明如下:
[public] interface interfaceName[extends listOfSuperInterface]{
…
}
其中public指明任意类均可以使用这个接口,缺省情况下,只有与该接口定义在同一个包中的类才可以访问这个接口。
接口中的成员权限都是public,方法都是抽象方法。定义时public和abstract这两个关键字可以省略。在接口中定义的常量具有public, static, final的属性。
一个类可以实现多个接口。
通过接口可以指明多个类需要实现的方法。可以了解对象的交互界面,而不需了解对象所对应的类。
3.控制符总结
成员的访问控制符:
非访问控制符:
/*
笔记本电脑使用
为了扩展笔记本的功能,需定义一个规则,只要后接设备符合这一规则即可。在这个例子中,规则就是接口。
*/
interface USB{
public void open();
public void close();
}
//设备实现这一规则,降低了耦合性
class UsbDisk implements USB{
public void open(){
System.out.println("UsbDisk open");
}
public void close(){
System.out.println("UsbDisk close");
}
}
class UsbMouse implements USB{
public void open(){
System.out.println("UsbMouse open");
}
public void close(){
System.out.println("UsbMouse close");
}
}
public class BookPC {
public static void main(String[] args){
useUSB(new UsbDisk()); //扩展功能
}
//接口类型的引用
public static void useUSB(USB u){
if (u != null){
u.open();
u.close();
}
}
}
四、多态(Polymorphism)
1.概述
多态(Polymorphism)是指一个程序中相同的名字表示不同的含义的情况。
多态有两种情形:
编译时多态:重载(overload)
运行时多态:覆盖(override);多态绑定(dynamic binding)——虚方法调用(virtual method invoking);在调用方法时,程序会正确地调用子类对象的方法。
多态大大提高了程序的抽象程度和简洁性。
2.特点
子类和父类的多态可以用上溯类型转换(upcasting),即把派生类型当作基本类型处理。此时用虚方法调用,可以实现运行时的多态。
3.虚方法
虚方法调用的情形:
a.子类重载了父类方法时,运行时
b.运行时系统根据调用该方法的实例的类型来决定选择哪个方法调用。
c.所有的非final方法都会自动地进行动态绑定!
Java中,普通的方法是虚方法,但static,private方法不是虚方法调用,因为static,private与虚方法编译后用的指令是不同的。
Java中三种非虚的方法:
1)static的方法,以声明的类型为准,与实例类型无关
2)private方法子类看不见,也不会被虚化
3)final方法子类不能覆盖,不存在虚化问题
abstract class Animal{
abstract void eat();
}
class Dog extends Animal{
void eat(){
System.out.println("Gnaw a bone");
}
void lookAfterHome(){
System.out.println("Look after the home");
}
}
class Cat extends Animal{
void eat(){
System.out.println("Eat a fish");
}
void catchMouse(){
System.out.println("Catch the mouse");
}
}
class PolymophismDemo{
public static void main(String[] args){
//upcasting,不能再使用子类特有的方法了。
Animal a = new Cat();
a.eat();
//a.catchMouse(); //报错
//若想用子类特有的方法,需将对象向下转型
//instanceof:用于判断对象的具体类型,只能用于引用数据类型判断,通常在向下转型前用于健壮性的判断。
if (a instanceof Cat){
Cat c = (Cat)a;
c.catchMouse();
}
else if (a instanceof Dog){
Dog d = (Dog)a;
d.lookAfterHome();
}
//注意:只能对子类对象进行转型,否则抛出ClassCastException.
//Cat c = (Cat)a;
}
public static void method(Animal a){
a.eat();
}
}
4.多态时成员的特点
参考量 | 编译时 | 运行时 |
---|---|---|
成员变量 | 引用型变量(左边) | 引用型变量(左边) |
非静态成员函数 | 引用型变量(左边) | 对象所属类(右边) |
静态成员函数 | 引用型变量(左边) | 引用型变量(左边) |
简而言之,只有非静态成员函数在运行时参考对象所属类中是否有调用的方法,看的是等号右边,其他情况都是看引用型变量(等号左边)。