第十三章:抽象类和接口
一.抽象类
父类中定义了子类的共同行为
接口定义了类的共同行为(包括非相关类的)
注意:
- 抽象类不可以创建对象,可以包含抽象方法,这些方法在具体的子类中实现
abstract class GeometircObject {
//成员变量
private String color;
private boolean filled;
//构造方法只能被子类访问,所以用protected修饰
protected GeometircObject (){}
//访问器getters&修改器setters
public boolean isFilled() {
return filled;
}
public void setFilled(boolean filled) {
this.filled = filled;
}
public void setColor(String color) {
this.color = color;
}
public String getColor() {
return color;
}
// 抽象方法,只有定义没有实现,需要被子类实现
abstract double getArea();
// 普通方法
double devideArea() {
return getArea() / 2;
}
}
- 抽象方法只有定义没有实现,在具体的子类中实现,一个包含抽象方法的类必须声明为抽象类
- 抽象类的构造方法定义为protected,因为它只能被子类调用
1.抽象类与子类总结
- 在抽象类扩展的非抽象子类中,所有的抽象方法都要被实现,抽象方法都是非静态的
- 抽象类不能使用new操作符来初始化
- 包含抽象方法的类必须是抽象的,但是可以定义一个不包含抽象方法的抽象类
abstract class GeometircObject {
//不包含抽象方法的抽象类
double devideArea() {
return 2;
}
}
- 子类可以覆盖父类的方法,并将它定义为abstract,这种情况下子类也必须是abstract
abstract class GeometircObject {
// 抽象方法,需要被子类实现
abstract double getArea();
abstract double getPrimeter();
}
abstract class Rectangle extends GeometircObject {
private double width;
private double height;
public Rectangle(double width,double height) {
this.width=width;
this.height=height;
}
//只实现了getArea方法,但是没有实现getPrimeter方法,所以用abstract修饰
public double getArea() {
return width*height;
}
}
class RectangleChild extends Rectangle {
//如果父类只有含参构造方法,那子类必须要实现,否则构造不出父类,也就构造不出自己了,除非父类有一个或默认为无参构造
private double width;
private double height;
public RectangleChild(double width,double height) {
super(width,height);
}
//实现父类没有实现的getPrimeter方法
public double getPrimeter() {
return (width+height)*2;
}
}
- 即使父类是具体的,子类也可以是抽象的
- 不能使用new操作符创建一个对象,但是可以将抽象类当做一种数据类型
- abstract修饰符就是要求子类(覆盖)这个方法,调用可以以多态的方式调用子类覆盖后的方法
public static void main(String[] args) {
//abstract修饰的getArea()由Circle子类实现,可以使用多态动态调用子类覆盖后的方法
GeometircObject circle = new Circle(3);
System.out.println(circle.getArea());
}
二.抽象的Numer类
Number是抽象包装类,是BigInteger和BigDecimal的抽象父类
//byteValue和shortValue由inValue()方法得来,所以它不是抽象方法
byte byteValue(){
return (byte)intValue();
}
short shortValue(){
return (short)intValue();
}
//抽象方法
abstract int intValue();
abstract double doubleValue();
……
三.Calendar和GregorianCalendar
GregorianCalendar是抽象类Calendar的抽象子类
GregorianCalendar和Calendar有很多方法,这里就不一一介绍了
值得一提的是Calendar类中有很多常量
例:YEAR 年
MONTH 月(0代表一月)
DAY_OF_WEEK (一周的天数,1是星期日)
……
public static void main(String[] args) {
// 创建GregorianCalendar实例
Calendar calendar = new GregorianCalendar();
// 打印当前年-月-日 备注:月份中0代表1月
System.out.println(calendar.get(Calendar.YEAR) + "-" + calendar.get(Calendar.MONTH) + "-"
+ calendar.get(Calendar.DAY_OF_MONTH));
}
//结果:2020-1-27 (实际是2020年2月27日)
四.接口
接口是一种与类相似的结构,只包含常量和抽象方法
接口的目的是指明多个相关或不相关的多个对象的共同行为
语法
修饰符 interface 接口名{
//常量声明 默认使用public static final 修饰
public static final int NUMBER=1; //等价于int NUMBER=1;
//方法签名 默认使用public abstract 修饰
public abstract void function();//等价于void function();
}
- 不能使用new操作符创建接口实例
- 实现接口使用的关键字是inplements
public static void main(String[] args) {
//使用接口CouldCall作为引用变量的类型
CouldCall animal = new Tiger();
System.out.println(animal.callFunction());
}
}
//可食用接口
interface CouldEat {
// 烹饪方法
public abstract String cookingFunction();
}
//叫声接口
interface CouldCall {
// 叫声
public abstract String callFunction();
}
//动物类
class Animal {
}
//老虎继承自动物类并且实现叫声接口,切记:老虎不能吃,禁食野味,爱自己也爱护他人!
class Tiger extends Animal implements CouldCall {
public String callFunction() {
return "ao";
}
}
//鸡继承自动物类,实现叫声接口和可以食用接口
class Chicken extends Animal implements CouldCall, CouldEat {
public String cookingFunction() {
return "Broil";
}
public String callFunction() {
return "gogogo";
}
可以使用接口作为引用变量的数据类型或类型转换的结果
五.Comparable接口
//Comparable是一个泛型接口
interface Comparable<E>{
//比较方法返回一个int类型值
public int compare(E object);
}
instanceof 关键字用于判断是否是此类的实例
//判断animal是否是Animal的实例
System.out.println(animal instanceof Animal);//true
【补充 20220218】
12.Comparable接口和Compartor接口
Comparable是一个排序接口,实现这个接口代表这个类支持排序 传递一个对象和自己比,内比较器
Compartor是一个比较器,创建一个比较器比较进行排序 外比较器
六.Cloneable接口
Cloneable给出了一个可克隆的对象
正常接口应该包含常量和抽象方法,但是Cloneable接口是空的
public interface Cloneable{}
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] array = { 3, 5, 4, 6 };
int[] array2 = array.clone();
// 克隆实际上是创建了一个新的对象
System.out.println(array == array2);
array2[2] = 2;
printArray(array);
printArray(array2);
}
public static void printArray(int[] list) {
for (int i : list) {
System.out.print(i + " ");
}
System.out.println();
}
//结果
//false
//3 5 4 6
//3 5 2 6
实现Cloneable接口
//定义Cloneable 接口
interface Cloneable {
}
//Circle类实现Cloneable 接口
class Circle extends GeometircObject implements Cloneable {
//……
@Override
public Object clone() throws CloneNotSupportedException {
// 调用父类的Clone方法,会抛出一个CloneNotSupportedException的异常
return super.clone();
}
}
下图是clone方法在Object类中的实现,可以看到使用native修饰的,由c语言编写的,并且只能由包内类和子类可以访问
七.接口与抽象类的区别
一个类只能继承一个父类,但是可以实现多个接口
抽象类 | 接口 | |
---|---|---|
变量 | 无限制 | public static final 修饰 |
构造方法 | 通过构造方法链继承构造方法,不能通过new操作符创建对象 | 不能通过new操作符创建对象 |
方法 | 无限制 | public abstract修饰 |
接口可以继承其他接口,这样的接口称为子接口
所有的类共享一个根类Object,但是接口没有共同的根,接口可以定义一种类型,一个接口变量可以引用任何实现改接口的类的实例
类名可以是名词,接口名可以是形容词和名次
强的“是一种”关系用类来描述 例如:公历是日历的一种
弱的“是一种”关系用接口来描述 例如:所有的字符串都是可比较的
注意
接口可以扩展其他的接口而不是类,一个类可以扩展它父类的同时实现多个接口
【补充 20220218】
共同点:
都不能new对象
继承抽象类或 实现接口的类必须实现他们的抽象方法,否则需要定义为抽象类
抽象方法都没有方法体,只能由子类来实现
都描述是一种的关系,强的是一种用抽象类,若的是一种用接口
不同点:
抽象类定义了子类的共同行为
接口定义了类的共同行为,包括非相关类的
抽象类中可以有抽象方法和其它方法,接口中只能有抽象方法,静态方法和默认方法
抽象类中可以有变量和常量,接口中只能有静态常量
抽象类可以实现接口,接口不能实现接口
抽象类不可以多继承,可以多实现接口,但是接口可以多继承接口
八.Rational类
Rational类用于处理有理数,具体的方法就不一一描述了
Rational类没有提供分子、分母的set方法,也就是说值不可改变,像String和基本数值类型的包装类一样
Rational类有严格的限制,容易溢出,可以使用BigInteger表示分子和分母
九.类的设计原则
- 内聚性 各司其职,内聚到一个
- 一致性 名字和方法一致性
- 封装性 保护和维护私有数据
- 清晰性 方法设计,数据导入
- 完整性 提供多种方案实现不同的需求
- 实例和静态 设为静态,类名直接调用
- 继承和聚合 “是一种”和“具有”的关系
- 接口和抽象类 弱“是一种”和强“是一种”的关系
每个接口都被编译成独立的字节码文件
深复制和浅复制
class Course implements Cloneable{
public Object clone() {
//深复制
Course c=(Course)super.clone();
c.student=student.clone();
//浅复制
c.student=super.clone();
}
}
十.总结
通过对本章的学习,我大致学会了使用抽象类和接口,以及他们使用的场景和规则,还学会了 几个具有代表性的类,掌握了接口和抽象类的区别,最后,懂得了类的设计原则。
加油!第十四章待更……