在《Java讲课笔记13:类的继承》里,我们定义了Animal类,其中定义了eat()方法用于表示动物吃东西,但是不同的动物,吃的东西也是不同的,因此在eat()方法中无法准确描述动物吃什么东西。
public class Animal {
……
public void eat() {
System.out.println(name + "在吃……");
}
……
}
如何能使Animal类中既包含eat()方法,又无需提供其方法的实现呢?
Java提供了抽象方法来满足这种需求。抽象方法必须使用abstract
关键字来修饰,并且在定义方法时不需要实现方法体。当一个类中包含了抽象方法,那么该类也必须使用abstract
关键字来修饰,这种使用abstract
关键字修饰的类就是抽象类。
// 定义抽象类
[修饰符] abstract class 类名 {
// 定义抽象方法
[修饰符] abstract 方法返回值类型 方法名([参数列表]);
// 其它方法或属性
}
注意: 如果一个类包含了没有实现的方法(抽象方法),那么这个类就必定是抽象类,不能实例化,只能通过继承,在子类中实现其抽象方法,然后就可以实例化子类。我们知道,包含了抽象方法的类一定是抽象类,然而抽象类中可以不包含任何抽象方法。
package net.hw.lesson15;
/**
* 功能:演示抽象类与抽象方法
* 作者:华卫
* 日期:2020年05月08日
*/
// 定义抽象类
abstract class Flower {
// 定义抽象方法
public abstract void bloom();
}
// 继承Flower类创建Jasmine类
class Jasmine extends Flower {
// 实现抽象方法bloom(),编写方法体
@Override
public void bloom() {
System.out.println("茉莉花正在绽放,散发着淡淡幽香。");
}
}
// 测试类
public class Example1501 {
public static void main(String[] args) {
Jasmine jasmine = new Jasmine();
jasmine.bloom();
}
}
package net.hw.lesson15;
/**
* 功能:演示抽象类不包含抽象方法
* 作者:华卫
* 日期:2020年05月08日
*/
abstract class Window {
public static void open() {
System.out.println("窗户打开咯~");
}
public static void close() {
System.out.println("窗户关闭咯~");
}
}
public class TestAbstractClass {
public static void main(String[] args) {
// 调用抽象类的静态方法
Window.open();
Window.close();
}
}
package net.hw.lesson15;
import java.text.SimpleDateFormat;
import java.util.Calendar;
public class TestCalendar {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
System.out.println("当前时间:" + calendar.getTime());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");
System.out.println("当前时间(12小时制):" + sdf.format(calendar.getTime()));
sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
System.out.println("当前时间(24小时制):" + sdf.format(calendar.getTime()));
}
}
接口是一种特殊的抽象类,它不能包含普通方法,其内部的所有方法都是抽象方法,它将抽象进行得更为彻底。接口其实是定义了一种标准或规范,既然如此,接口里不能包含一般的属性,只能包含常量。
接口中除了抽象方法外,还可以有默认方法和静态方法(也叫类方法),默认方法使用default
修饰,静态方法使用static
修饰,并且这两种方法都允许有方法体。
[修饰符] interface 接口名 [extends 父接口1, 父接口2, ...] {
[public] [static] [final] 常量类型 常量名 = 常量值;
[public] [abstract] 方法返回值类型 方法名([参数列表]);
[public] default 方法返回值类型 方法名([参数列表]){
// 默认方法的方法体
}
[public] static 方法返回值类型 方法名([参数列表]){
// 类方法的方法体
}
}
package net.hw.lesson15;
/**
* 功能:Shape接口
* 作者:华卫
* 日期:2020年05月08日
*/
public interface Shape {
// 定义全局常量
double PI = 3.1415926;
// 定义抽象方法
double getCircumference();
double getArea();
// 定义默认方法(可以定义多个默认方法)
default void getType(String type) {
System.out.println("该图形属于" + type);
}
// 定义静态方法(可以定义多个静态方法)
static double getPI() {
return Shape.PI;
}
}
double PI = 3.1415926;
与public static final double PI = 3.1415926;
等效。package net.hw.lesson15;
/**
* 功能:实现Shape接口创建Circle类
* 作者:华卫
* 日期:2020年05月08日
*/
public class Circle implements Shape {
private double r;
public double getR() {
return r;
}
public void setR(double r) {
this.r = r;
}
@Override
public double getCircumference() {
return 2 * PI * r;
}
@Override
public double getArea() {
return PI * r * r;
}
}
package net.hw.lesson15;
/**
* 功能:测试Circle类
* 作者:华卫
* 日期:2020年05月08日
*/
public class TestCircle {
public static void main(String[] args) {
// 通过接口名访问接口全局常量
System.out.println("Shape.PI = " + Shape.PI);
// 通过接口名调用类方法(接口静态方法)
System.out.println("Shape.getPI() = " + Shape.getPI());
// 实例化Circle对象
Circle circle = new Circle();
// 通过对象名访问接口全局常量
System.out.println("circle.PI = " + circle.PI);
// 通过对象名调用接口默认方法
circle.getType("圆");
// 设置对象属性
circle.setR(3);
// 调用对象方法
System.out.println("circumference = " + circle.getCircumference());
System.out.println("area = " + circle.getArea());
}
}
注意:类只能继承一个父类,但是接口可以继承多个父接口。
package net.hw.lesson15;
/**
* 功能:Person接口
* 作者:华卫
* 日期:2020年05月09日
*/
public interface Person {
void eat();
}
package net.hw.lesson15;
/**
* 功能:Writable接口
* 作者:华卫
* 日期:2020年05月09日
*/
public interface Writable {
void write();
}
package net.hw.lesson15;
/**
* 功能:继承Person与Writable接口,创建Man接口
* 作者:华卫
* 日期:2020年05月09日
*/
public interface Man extends Person, Writable {
void fight();
}
Man接口不仅继承了Person与Writable接口,还添加了一个抽象方法fight()。
package net.hw.lesson15;
/**
* 功能:实现Man接口,创建Police类
* 作者:华卫
* 日期:2020年05月09日
*/
public class Police implements Man {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 实现Man接口的抽象方法
@Override
public void fight() {
System.out.println("警察[" + getName() + "]在破案。");
}
// 实现Person接口的抽象方法
@Override
public void eat() {
System.out.println("警察[" + getName() + "]在吃饭。");
}
// 实现Writable接口的抽象方法
@Override
public void write() {
System.out.println("警察[" + getName() + "]在写作。");
}
}
package net.hw.lesson15;
/**
* 功能:测试Police类
* 作者:华卫
* 日期:2020年05月09日
*/
public class TestPolice {
public static void main(String[] args) {
Police police = new Police();
police.setName("李文强");
police.eat(); // 调用Person接口的方法
police.write(); // 调用Writable接口的方法
police.fight(); // 调用Man接口的方法
}
}
Java简化了C++的多重继承,是单根继承,只能继承一个类,但是作为补偿,Java允许实现多个接口,让一个类可以实现更多的特性。
package net.hw.lesson15;
/**
* 功能:网卡接口
* 作者:华卫
* 日期:2020年05月09日
*/
public interface NetCard {
void connectNetwork();
}
package net.hw.lesson15;
/**
* 功能:USB接口
* 作者:华卫
* 日期:2020年05月09日
*/
public interface USB {
void connectUSB();
}
package net.hw.lesson15;
/**
* 功能:抽象类Computer
* 作者:华卫
* 日期:2020年05月09日
*/
public abstract class Computer {
public abstract void compute();
}
package net.hw.lesson15;
/**
* 功能:笔记本电脑类
* 作者:华卫
* 日期:2020年05月09日
*/
public class Laptop extends Computer implements NetCard, USB {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 重写Computer抽象类的抽象方法
public void compute() {
System.out.println("笔记本电脑[" + getName() + "]正在计算。");
}
// 实现NetCard接口的抽象方法
public void connectNetwork() {
System.out.println("笔记本电脑[" + getName() + "]连接网络。");
}
// 实现USB接口的抽象方法
public void connectUSB() {
System.out.println("笔记本电脑[" + getName() + "]连接USB。");
}
}
此时,查看Laptop类的类图:
Laptop有且只有一个爹:Computer
Laptop有两个叔叔:NetCard与USB
package net.hw.lesson15;
/**
* 功能:测试Laptop类
* 作者:华卫
* 日期:2020年05月09日
*/
public class TestLaptop {
public static void main(String[] args) {
Laptop laptop = new Laptop(); // 实例化对象
laptop.setName("Aspire V"); // 设置对象属性
laptop.connectNetwork(); // 调用NetCard接口的方法
laptop.connectUSB(); // 调用USB接口的方法
laptop.compute(); // 调用Computer抽象类的方法
}
}
package net.hw.lesson15;
/**
* 功能:动物类
* 作者:华卫
* 日期:2020年05月09日
*/
public class Animal {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void speak() {
System.out.println(name + "今年" + age + "岁了。");
}
public void move() {
System.out.println(name + "在动……");
}
public void eat() {
System.out.println(name + "在吃……");
}
@Override
public String toString() {
return "Animal{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package net.hw.lesson15;
import net.hw.lesson13.Animal;
/**
* 功能:继承动物类,创建鸟类
* 作者:华卫
* 日期:2020年05月09日
*/
public class Bird extends Animal {
/**
* 子类改写父类的同名同参方法(多态)
*/
@Override
public void move() {
System.out.println(getName() + "在飞翔。");
}
/**
* 子类改写父类的同名同参方法(多态)
*/
@Override
public void eat() {
System.out.println(getName() + "爱吃小虫。");
}
/**
* 子类添加新的方法
*/
public void play() {
System.out.println(getName()+ "在玩虫子。");
}
}
package net.hw.lesson15;
import net.hw.lesson13.Animal;
/**
* 功能:继承动物类,创建猫类
* 作者:华卫
* 日期:2020年05月09日
*/
public class Cat extends Animal {
/**
* 子类改写父类的同名同参方法(多态)
*/
@Override
public void move() {
System.out.println(getName() + "在走路。");
}
/**
* 子类改写父类的同名同参方法(多态)
*/
@Override
public void eat() {
System.out.println(getName() + "爱吃鱼虾。");
}
/**
* 子类添加新的方法
*/
public void play() {
System.out.println(getName()+ "在玩老鼠。");
}
}
package net.hw.lesson15;
import net.hw.lesson13.Animal;
/**
* 功能:继承动物类,创建狗类
* 作者:华卫
* 日期:2020年05月09日
*/
public class Dog extends Animal {
/**
* 子类改写父类的同名同参方法(多态)
*/
@Override
public void move() {
System.out.println(getName() + "在走路。");
}
/**
* 子类改写父类的同名同参方法(多态)
*/
@Override
public void eat() {
System.out.println(getName() + "爱吃骨头。");
}
/**
* 子类添加新的方法
*/
public void play() {
System.out.println(getName() + "在玩飞盘。");
}
}
package net.hw.lesson15;
/**
* 功能:Student类
* 主要为演示多态
* 作者:华卫
* 日期:2020年05月09日
*/
public class Student {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void feed(Animal animal) {
if (animal instanceof Dog) {
System.out.println(name + "喂养狗崽[" + animal.getName() + "]。");
} else if (animal instanceof Cat) {
System.out.println(name + "喂养猫咪[" + animal.getName() + "]。");
} else if (animal instanceof Bird) {
System.out.println(name + "喂养鸟儿[" + animal.getName() + "]。");
} else {
System.out.println(name + "喂养动物[" + animal.getName() + "]。");
}
animal.move();
animal.eat();
}
}
instanceof
,用来判断一个对象是否是某个类(或接口)的实例或者子类实例。package net.hw.lesson15;
/**
* 功能:测试Student类体现的多态
* 作者:华卫
* 日期:2020年05月09日
*/
public class TestStudent {
public static void main(String[] args) {
Student student = new Student();
student.setName("张三丰");
Animal animal1 = new Cat(); // 父类变量指向子类对象
animal1.setName("欢欢");
animal1.setAge(2);
Animal animal2 = new Dog(); // 父类变量指向子类对象
animal2.setName("瑞瑞");
animal2.setAge(3);
Animal animal3 = new Bird(); // 父类变量指向子类对象
animal3.setName("豆豆");
animal3.setAge(1);
// 学生对象调用喂养方法,传入不同的动物对象
student.feed(animal1);
student.feed(animal2);
student.feed(animal3);
}
}
Animal animal1 = new Cat(); // 将Cat类对象当作Animal类型来使用
Animal animal2 = new Dog(); // 将Dog类对象当作Animal类型来使用
Animal animal3 = new Bird(); // 将Bird类对象当作Animal类型来使用
向下转型可能会出现错误:向下转型需要考虑安全性,如果父类引用的对象是父类本身或者其它子类,那么在向下转型的过程中是不安全的,编译不会出错,但是运行时会出现java.lang.ClassCastException
错误。
编写汽车接口Vehicle,有跑方法run()。编写公共汽车类Bus和载重卡车类Truck实现汽车接口,公共汽车类增加属性:最大载客量(maxCapacity
),增加报站方法(report(String station)
),重写跑方法。载重卡车类增加属性:最大载重量(maxLoad
),增加装货(load(String cargo, double weight)
)、卸货方法(unload(String cargo, double weight)
),重写跑方法。编写测试类测试2个类,使用汽车类引用公共汽车对象和载重汽车对象,让汽车跑起来。
编写驾驶员(Driver)类,声明属性:姓名。方法:驾驶(drive)方法,方法的参数是任务1的汽车接口,方法体中调用汽车接口的跑方法。测试类中调用驾驶员drive方法时参数传递汽车接口的子类对象,查看输出效果。
实现思路: