JavaSE学习(六)面向对象-多态、抽象类、接口

作者 Zhendong Ho 日期 2019-02-09
JavaSE学习(六)面向对象-多态、抽象类、接口

多态

多态(polymorphic)概述

事物存在的多种形态。

多态前提

  • 要有继承关系。
  • 要有方法重写。
  • 要有父类引用指向子类对象。
class Demo1_Polymorphic {
public static void main(String[] args) {
Cat c = new Cat();
cat.eat();

Animal a = new Cat(); //父类引用指向子类对象
a.eat(); //猫吃鱼
}
}

class Animal {
public void eat() {
System.out.println("动物吃饭");
}
}

class Cat extends Animal {
public void eat() {
System.out.println("猫吃鱼");
}
}

多态中的成员访问特点

成员变量

编译看左边(父类),运行看左边(父类)。

class Demo2_polymorphic {
public static void main(String[] args) {
Father f = new Son(); //父类引用指向子类对象
System.out.println(f.num); //10

Son s = new Son();
System.out.println(s.num); //20
}
}

class Father {
int num = 10;
}

class Son extends Father {
int num = 20;
}

成员方法

编译看左边(父类),运行看右边(子类)。也称作动态绑定

class Demo2_polymorphic {
public static void main(String[] args) {
Father f = new Son(); //编译时先看Father类有无print方法,若无则编译不通过
f.print(); //son,运行时运行子类的print方法,输出son
}
}

class Father {
int num = 10;
public void print() {
System.out.println("father");
}
}

class Son extends Father {
int num = 20;
public void print() {
System.out.println("son");
}
}

静态方法

编译看左边(父类),运行看左边(父类)。

class Demo2_polymorphic {
public static void main(String[] args) {
Father f = new Son();
f.method(); //输出son static method,相当于Father.method()
}
}

class Father {
int num = 10;
public void print() {
System.out.println("father");
}

public static void method() {
System.out.println("father static method");
}
}

class Son extends Father {
int num = 20;
public void print() {
System.out.println("son");
}

public static void method() {
System.out.println("son static method");
}
}

注意

  • 静态方法和类相关,算不上重写,所以访问的还是左边。
  • 只有非静态的成员方法,编译看左边,运行看右边。

向上转型和向下转型

向上转型:父类引用指向子类对象。

向下转型:将已经向上转型的对象强制转换为子类的类型。

class Demo3_Superman {
public static void main(String[] args) {
Person p = new Superman(); //父类引用指向子类对象,就是向上转型

System.out.println(p.name); //John
p.talkBusiness(); //谈生意
//p.fly(); //报错,Person类没有fly方法

Superman sm = (Superman)p; //向下转型
sm.fly(); //飞出去救人
}
}

class Person {
String name = "John";
public void talkBusiness() {
System.out.println("谈生意");
}
}

class Superman extends Person {
String name = "superman";
public void talkBusiness() {
System.out.println("谈几个亿的大生意");
}

public void fly() {
System.out.println("飞出去救人");
}
}

多态的好处

  • 提高了代码的维护性(继承保证)。
  • 提高了代码的扩展性(由多态保证)。
class Demo4_Animal {
public static void main(String[] args) {
method(new Cat());
method(new Dog());

//Animal a = new Cat(); //开发时很少在创建对象的时候用父类引用指向子类对象
//Cat c = new Cat(); //直接创建子类对象更方便,可以使用子类特有的属性和方法
}

public static void method(Animal a) {//当作参数的时候用多态最好,因为扩展性强
//关键字:instanceof判断前边的引用是否是后边的数据类型
if (a instanceof Cat) {
Cat c = (Cat)a;
c.eat();
c.CatchMouse();
} else if (a instanceof Dog) {
Dog d = (Dog)a;
d.eat();
d.lookHome();
} else {
a.eat();
}
}
}

抽象类

抽象类的特点

  • 抽象类和抽象方法必须用abstract关键字修饰。
  • 抽象类不一定有抽象方法,有抽象方法的类一定是抽象类或者是接口。
  • 抽象类不能被实例化。可以通过多态的方式,由具体的子类实例化(抽象类也是多态的一种)。
  • 抽象类的子类,要么是抽象类,要么重写抽象类中的所有抽象方法。
class Demo1_Abstract {
public static void main(String[] args) {
//Animal a = new Animal; //错误:抽象类不能被实例化
Animal a = new Cat(); //父类引用指向子类对象
a.eat();
}
}

abstract class Animal { //抽象类
public abstract void eat(); //抽象方法
}

class Cat extends Animal {
public void eat() {
System.out.println("猫吃鱼");
}
}

抽象类的成员特点

成员变量:既可以是变量,也可以是常量。abstract不能修饰成员变量。

构造方法:有,用于子类访问父类数据的初始化。

成员方法:既可以是抽象的,也可以是非抽象的。

class Demo2_Abstract {
public static void main(String[] args) {

}
}

abstract class Demo {
int num1 = 10;
final int num2 = 20;

public Demo() { }

public void print() {
System.out.println("111");
}

public abstract void method();
}

class Test extends Demo {
public void method() {
System.out.println("222");
}
}

抽象类中的面试题

问题一:一个抽象类如果没有抽象方法,可不可以定义为抽象类?如果可以,有什么意义?

可以,这么做的目的只有一个。就是不让其他类创建本类对象,交给子类完成。

问题二:abstract不能和那些关键字共存?

  • abstract和static。被abstract修饰的方法没有方法体,被static修饰的可以用类名.调用。但是类名.调用抽象方法是没有意义的。
  • abstract和final。被abstract修饰的方法强制子类重写,被final修饰的不让子类重写。因此相互矛盾。
  • abstract和private。被abstract修饰的是为了让子类看到并强制重写,被private修饰不让子类访问。因此相互矛盾。
abstract class Demo {
//public static abstract void print(); //错误:非法的修饰符组合:abstract和static
//public final abstract void print(); //错误:非法的修饰符组合:abstract和final
private static abstract void print(); //错误:非法的修饰符组合:abstract和private
}

接口

接口概述

从狭义的角度讲,就是指Java中的interface。

从广义的角度讲,对外提供规则的都是接口。

接口的特点

  • 接口用关键字interface表示,interface 接口名 { }。
  • 类实现接口用关键字implements表示,class 类名 implements 接口名 { }。
  • 接口不能实例化,必须按照按照多态的形式实例化。
  • 接口的子类可以是抽象类(意义不大),也可以是具体类(要重写接口中的所有抽象方法)。
class Demo1_Interface {
public static void main(String[] args) {
//Inter i = new Inter; //出错,接口不能实例化
Inter i = new Demo(); //父类引用指向子类对象
i.print(); //print
}
}

interface Inter {
public abstract void print(); //接口中的方法都是抽象的
}

class Demo implements Inter {
public void print() {
System.out.println("print");
}
}

接口的成员特点

成员变量

只能是常量,并且是静态的、公共的。

默认修饰符:public static final,建议自己手动给出。

构造方法

接口没有构造方法。

成员方法

只能是抽象方法(没有方法体)。

默认修饰符:public abstract,建议自己手动给出。

interface Inter {
public static final int num = 10; //三个关键字可交换位置,可省略
//public Inter() { } //接口中没有构造方法

/*public void print() { //接口中不能定义非抽象方法

}*/

public abstract void print(); //public和abstract为默认修饰符
}

class Demo implements Inter {
public void print() { //因为接口中的print为public,所以这里必须加public
//num = 20; //报错,接口中的num为常量
System.out.println(num);
}
}

类和接口相互之间的关系

类与类之间:继承关系,只能单继承,可以多层继承。

类与接口:实现关系,可以单实现,也可以多实现。并且还可以在继承一个类的同时实现多个接口。

接口与接口:继承关系,可以单继承,也可以多继承。

抽象类和接口的区别

抽象类:被继承体现的是“is a”的关系,抽象类中定义的是该继承体系的共性功能

接口:被实现体现的是“like a”关系,接口中定义的是该继承体系的扩展功能