抽象
概念
抽象即没有任何具体的实际内容,在 Java 中抽象的关键字是 abstract
如:
// 抽象类
public abstract class Human{
// 抽象方法
public abstract void run();
}
public class Worker extends Human{
public void run(){
System.out.println("自行车出行");
}
}
public class Boss extends Human{
public void run(){
System.out.println("小汽车出行");
}
}
// 入口测试
Human worker = new Worker();
workder.run(); // 自行车出行
Human boss = new Boss();
boss.run(); // 小汽车出行
使用abstract
修饰的类无法被实例化调用,只能实例化子类
抽象方法
有些父类的方法没有具体的实现方式,所以可以定义成abstract
方法
public abstract void 方法名();
注意抽象方法不能是private
私有方法,不然无法被子类继承,重写
抽象类
包含抽象方法的类必须为抽象类,而抽象类可以不需要有抽象方法,当我们创建一个父类时,需要避免父类被随意实例化,这就要用到abstract
例:
public abstract class A{}
public abstract class B{
public abstract void b();
}
// 不能实例化
A a = new A(); // 编译失败
B b = new B(); // 编译失败
对于子类,可以实现抽象方法,也可以继续保持抽象而不进行具体的实现
public class A1 extends A{}
public abstract class A2 extends A{}
public abstract class B1 extends B{}
public class B2 extends B{
public void b(){
System.out.println("子类不是抽象类必须实现父类的抽象方法");
}
}
应用场景
- 对方法进行抽象:当某个方法无法描述或是不方便描述的时候,可以不定义具体的实现并要求由子类去定义,使用 abstract 抽象修饰为未实现方法
- 当类不能直接被实例化,或者类不是完整的,应该由子类去扩展并具体实现,那么可以将类修饰为 abstract class 抽象类
- 子类继承抽象类以后,子类也会间接获得抽象类中的抽象方法,如果子类不实现抽象方法那么依然必须保持为抽象类,也可以将抽象方法重写从而变为具体类
- 将多个子类抽象为一个父类,使用抽象类来实现多态,即抽象类为父类引用
接口
接口定义
接口是一种规范,没有具体实现,符合这些规范的对象都可以看做是接口的实现
比如说一个U盘和一根数据线,他们没有明显的继承关系,但是我们可以把他们都具有的某些特征定义为接口
如:U盘的数据线都具有传输数据功能
public interface USB{
public void transfer();
}
创建接口
使用interface
关键字来创建接口
public class U盘 implements USB {
@Override public void service() {
System.out.println("通过U盘传送数据");
}
}
public class 数据线 implements USB {
public void service() {
System.out.println("通过数据线传送数据");
}
}
// 这里可以使用【USB接口】作为参数类型
public class 电脑 {
public void 传送数据(USB usb) {
usb.service();
}
}
使用接口
实现接口的类使用 implements
关键字,并且必须实现接口中定义的方法:
类最多只能继承一个父类,但是可以实现多个接口,接口之间用,
隔开:
public class B implements 接口1, 接口2{
...
}
接口与接口之间也具备继承的概念,同样使用extends
public interface B extends A{
...
}
接口只能继承接口,继承每个接口里面的方法,可以实现多个接口方法整合的作用,所以使得代码的灵活性,扩展性增强
// 定义接口
public interface Program {
public void programming();
}
public interface busy {
public void busyness();
}
// 符合接口
public class Programmer implements Program, busy {
public void programming() {
System.out.println("使用Java编程");
}
public void busyness() {
System.out.println("仔细分析业务");
}
}
面向接口
面向接口编程也就是面向抽象。面向接口编程指的是程序设计时,面向接口的约定而不考虑具体的实现。
另外,合理地设计 interface
接口和 abstract class
抽象类之间的继承关系,可以充分地复用代码。一般来说,公共逻辑适合放在抽象中,具体逻辑放到各个子类,而接口的层次则代表了抽象的程度。
总结接口的一些特点
- 接口定义的方法不能有具体实现(可以使用 default 默认实现),仅仅作为规范和标准
- 接口类似抽象类不能实例化,但是可以作为引用类型,实现多态
- 接口中的方法默认为 public abstract ,任何实现了接口的类必须重写接口的所有方法
- 如果类实现了接口,但是依然没有把所有方法具体实现,那么类必须修饰为抽象类
- 一个类可以实现多个接口,这与单继承不一样,同时可以通过接口类型实现多态
- 接口之间可以继承,而且可以是多继承,与类的单继承不一样
内部接口
有些类可能依赖一个简单的接口,而这个接口在外部定义意义不大,那么可以把接口直接定义在类中,通过类名.接口名
使用该接口:
public class A{
interface B{
void f();
}
public void doSomething(B obj){
obj.f();
}
}
// 入口测试
A a = new A();
a.doSomething(a.B(){
@Override
public void f(){ /*具体方法内容*/ }
});
接口中也可以定义接口
public interface I1{
void f1();
interface I2{
void f2();
}
}
class MyClass implements I1.I2{
@Override
public void f2() {}
}
接口属性
接口中的属性默认为 public,static,final
即使省略也无法修改,也就是静态常量,所以一般都是使用大写加下划线的方式命名。
interface MyInterface{
int CONSTANT_A = 100;
double CONSTANT_B = Math.random();
public static final String CONSTANT_C = "hello";
}
public class MyClass implements MyInterface{}
// 入口测试
System.out.println(MyInterface.CONSTANT_A);
System.out.println(MyClass.CONSTANT_C);
接口默认值
接口允许使用默认实现,通过 default
关键字实现:
interface MyInterface{
default void result() {
System.out.println();
}
}
class A implements MyInterface{
@Override
public int count(){
}
}
class B implements MyInterface{
@Override
public void result() {
System.out.println("重写方法");
}
}
// 入口方法
new A().result();
new B().result();// 重写方法
可以看到,默认方法可以在接口中写方法体,而实现类可以不必重写 default 修饰的默认方法。这样做的好处是:当我们需要给接口新增一个方法时,会涉及到所有实现该接口的类,如果新增的是 default 默认方法,那么实现类就不需要全部修改也可以编译成功,而需要具体实现的仍然可以按需重写新增方法即可。