目录
一、抽象类
1、抽象类的概念
在面向对象的过程中,通过类的描述来实例化对象,但是有时会出现一些类,这些类没有足够的信息具体地描述某一个对象。这样的类就被称为“抽象类”。
比如,在一个分析图形的过程中,图形类中存在着圆、三角形、正方形等图形,这样一些具体概念,它们是不同的,但是它们又都属于形状这样一个概念,形状这个概念在问题领域并不是直接存在的,它就是一个抽象概念。
图形Shape和圆、三角形、正方形之间存在继承关系。
图形Shape类不是具体的图形,所以即使其内部存在draw()方法,也没办法实现该方法。
如果我们对这些图形都进行draw()绘画的动作,那么根据对象的不同,所绘制出来的也是不一样的,可图形类(Shape)作为父类,没法具体绘画出具体的图形,导致draw()方法无法具体地实现,因此把图形类(Shape)称为“抽象类”。
在打印图形例子中, 父类 Shape 中的 draw()方法并没有什么实际工作, 主要的绘制图形都是由 Shape 的各种子类(三角形、圆形、正方形)的 draw()方法来完成的.
像这种没有实际工作的方法, 我们可以把它设计成一个 抽象方法(abstract method), 包含抽象方法的类我们称为 抽象类(abstract class).
打印图形示例:
abstract class Shape{
abstract public void draw();
}
class Circle extends Shape{
@Override
public void draw() {
System.out.println("⚪");
}
}
class triangle extends Shape{
@Override
public void draw() {
System.out.println("▲");
}
}
class square extends Shape{
@Override
public void draw() {
System.out.println("□");
}
}
public class TestDemo1 {
public static void main(String[] args) {
//注意:不能实例化抽象类,此处实例化继承抽象类的子类
Shape shape=new Circle();
Shape shape1=new triangle();
Shape shape2=new square();
shape.draw();
shape1.draw();
shape2.draw();
}
}
2、抽象类的语法
一般情况下,我们把被抽象化的类称为“抽象类”,而在抽象类中被抽象化的方法被称为“抽象方法”。抽象方法不需要有具体的实现语句.
而抽象类和抽象方法都是由abstract修饰的。
【注意】:
- 当一个方法的具体实现不想写,加个abstract就可以让它抽象化,类也要加上abstract,让其变成抽象类。
- 类中多了一个抽象方法,这个类也得变成抽象类。
- 抽象类和普通类的差不多,内部也可以具有普通成员变量、普通方法和构造方法。
- 抽象类中可以没有抽象方法,但如果一个类中有抽象方法,那该类一定得是抽象类。
3、抽象类的注意事项
在Java中,抽象类的使用有许多要注意的地方。稍不注意,就容易导致程序运行时报错,或出现意想不到的错误结果。
4、抽象类的作用
一般情况下,我们都是用普通的父类和子类进行向上转型,之后再通过调用它们之间重写的方法,发生动态绑定,来具体实现某一个对象的功能。
但还是建议重写抽象类中的抽象方法。
而抽象类本身是不能被实例化的,要想使用,只能先创建一个继承该抽象类的子类,然后让该子类重写抽象类的抽象方法。
二、接口
1、接口的概念
对于接口,在日常生活中,我们首先可以联想到的:我们平常充电USB口,电源插头等等。
根据接口的不同,可以插不同的设备。
比如:电脑的USB口上,可以插:U盘、鼠标、键盘...所有符合USB协议的设备
电源插座插孔上,可以插:电脑、电视机、电饭煲...所有符合规范的设备
2、接口的语法
接口其实和类差不多,但还是有所差别。接口的定义格式和类的定义格式基本相同。
接口是用interface关键字修饰的。
将平时定义类的class关键字,更换成interface关键词,就定义了一个接口。
接口的定义示例:
【注意】:
接口的语法规则:
- 接口是由interface关键字修饰的。
- 接口不是类,而是对类的一组需求描述,这些类要遵从接口描述的统一形式进行定义。
- 接口中的方法都是抽象方法,默认为public abstract。
- 接口中存在成员变量,但都默认为public static final静态常量,都得进行初始化。
- 创建接口时,接口的命名一般都是以大写字母I开头。
- 阿里编码规范中约定, 接口中的方法和属性不要加任何修饰符号, 保持代码的简洁性.
3、接口的使用
接口不能直接使用,需要有一个类来实现该接口,而该类还需重写接口中所有的抽象方法。
换句话说,一个类如果遵循了特定接口的规范,那么该类需要履行接口中的功能服务。
而将类声明为实现某一接口,需要用implements关键字。
语法格式:
class 类名词 implements 接口名称{
...
}
【注意】:子类和父类之间是extends 继承关系,类与接口之间是 implements 实现关系。
接口使用的代码示例:
以Animal接口,Dog类和Cat类实现该接口为例子
4、接口的特性
接口具有必须注意的特性。
(1)接口虽然是一种引用数据类型,但是不可以实例化new接口的对象,需要通过类实现该接口,之后实例化这个类对象。
(2)接口内的成员变量默认为静态常量,必须初始化,接口当中的成员变量即使不写public static final也会默认是public static final!
(3)接口中每一个方法都是public的抽象方法, 即接口中的方法会被隐式的默认为 public abstract,即使没有修饰符,编译器也会自动默认为public abstract(而且只能是 public abstract,其他修饰符都会报错)
(4)接口中的方法是不能在接口中实现的,但可以定义静态方法来实现该方法。
(5) 在继承抽象类或实现接口时所有不想重写抽象方法的类,都可以使它变成抽象类
(6) 重写接口中方法时,不能使用default访问权限修饰
(7)接口当中的方法如果要实现,需要用default来修饰。(这个默认的方法可以被重写)
(8)接口中不能有静态代码块和构造方法
(9)接口虽然不是类,但是接口编译完成后字节码文件的后缀格式也是.class
以此处代码为例
5、实现多个接口
接口不像类的继承一样(在Java中,类和类之间是单继承的),子类在继承父类的时候,一次只能继承一个父类,没法继承多个。(Java中不支持多继承)
而类实现接口,可以同时实现多个。
实现的多个接口之间是通过逗号分隔开的。
对于我们前面学习到的继承关系,子类继承父类的这种关系,比如Animal和Dog类之间表达的是一种is-a 关系(狗是动物)。
而在实现接口这个地方,表达的含义是该Dog类具有Swim,Move等特性.
狗是一种动物,具有跑,游泳的特性。
6、接口之间的继承
在Java中,类和类之间是单继承的,一个类可以实现多个接口,
而接口与接口之间可以多继承。
即:用接口可以达到多继承的目的。 接口可以继承一个接口, 达到复用的效果. 和类继承一样使用 extends 关键字.
也就是说,一个接口继承其他接口,就具备了其他接口的功能。
比如:
三、接口的使用实例
1、Comparable接口
使用类来实现接口,可以帮助我们很方便地进行许多有意思地操作。
(具体内部源码暂不作分析)
(1)首先我们先定义一个学生类
(2)定义一个Student学生类数组,里面存放实例化的学生对象.
(3)如果要对学生对象进行比较,我们得先确定要比较的属性是什么,类和普通的整型是不一样,整型是可以直接比较的, 大小关系明确,可不能说直接用学生的引用变量进行比较。
(4)比如,以学生的年龄作为比较条件,我们可以让Student学生类,实现一个Comparable的接口,该接口是Java中自带的。
需要注意的是,在Comparable接口后面有一个尖括号,里面应该存放要比较的类类型。
之后要在该Student类中重写ComparTo()方法
如果没有重写该方法,会报编译错误。
(5)如果我们想比较的是学生的年龄,那么我们可以在compareTo()方法中,返回比较的两个学生对象年龄之差。(如果比较的是成绩,把age替换成score即可)
如果返回的是正数,说明前者的学生年龄大;
如果返回的是负数,说明后者的学生年龄大;
如果返回的是0,说明两者的年龄一样大。
(6)在前面我们定义了一个学生类数组,我们可能通过Arrays类调用内部的sort()排序来对学生类进行排序。
在 sort 方法中会自动调用 compareTo 方法. compareTo 的参数是 Object , 其实传入的就是 Student 类型的对象.
之后我们再通过Arrays.toString ()方法将该数组转换为字符串输出,即可输出学生对象之间的年龄大小关系.
【注意】:使用Arrays时,记得要导包.
(7)如果比较的是学生对象的名字(字符串)该怎么比较呢?
只需再调用一个compareTo()方法比较, (具体内部源码暂不作分析)
2、Comparator接口(比较器)
下面引出Comparator(比较器)
通过实现Comparator接口,我们后续可以很方便地对类对象进行各种比较。
如果我们想比较学生的成绩,我们可以定义一个实现Comparator接口的成绩比较类ScoreIgnore.
Comparator接口和Comparable接口一样,后面的尖括号内为要比较的类类型。
通过重写compare方法,来返回比较的结果
之后再通过实例化ScoreIgnore对象,通过Arrays中的sort方法,传入该对象和要比较的学生数组students,进行比较。Arrays类中的sort()方法会进行一系列相关操作.
如果要比较学生对象的名字大小,可以在重写的compare方法内,再调用compareTo方法进行比较.