接口与抽象类的区别
- 抽象类要被子类继承;接口要被类实现。
- 抽象类中可以声明抽象方法和非抽象方法;接口只能声明抽象方法。
- 抽象类中的变量是普通变量;接口里定义的变量只能是公共的静态常量
- 抽象类使用继承(extends)来使用,无法多继承;接口使用实现(implements),可以多实现
- 抽象类中可以包含static方法,但是接口中不允许(静态方法不能被子类重写,因此接口中不能声明静态方法)
- 抽象类中可以构造方法;接口不能有。
抽象类
概念
抽象类必须使用abstract class声明
一个抽象类中可以没有抽象方法,但是抽象方法必须写在抽象类或接口中
// 定义一个抽象类
abstract class 类名{
// 可以有构造方法
类名(){}
// 可以存在非抽象方法
public void print(){
System.out.println("这是一个抽象类");
}
}
抽象方法
抽象方法可以定义在抽象类中,也可以定义在接口中,必须用abstract关键字声明,只定义不实现。抽象方法在子类中实现。
// 定义一个抽象类
abstract class 类名{
// 声明抽象方法
public abstract void 方法名();
}
抽象类与普通类的区别
- 抽象类必须用 public 或protected修饰 ,若用private修饰,那么子类则无法继承,也就无法实现其抽象方法(默认缺省为public)。
- 抽象类不能直接进行实例化操作,即抽象类不能使用new关键字创建对象,但是在子类创建对象时,抽象父类也会被JVM实例化。
- 如果一个子类继承抽象类,那么必须实现抽象类中所有抽象方法,如果有未实现的抽象方法,那么子类也必须定义为抽象类。
抽象类的常见问题
- 抽象类能否使用final声明?(final的解释在下面)
不能。因为final 属性修饰的类不能有子类,而抽象类必须有子类才有意义,所以不能。
- 抽象类能否有构造方法
可以有构造方法,而且子类对象实例化的时候的流程与普通类的继承是一样的,都是要先调用父类中的构造方法,之后再调用子类自己的构造方法。
例子
Fruit.java
// 定义一个Fruit抽象类
abstract class Fruit {
public String name;
// 构造方法
Fruit(){}
// 非抽象方法
public void print(){
System.out.println("This is an "+name);
}
// 抽象方法
public abstract void eat();
}
Apple.java
// Apple类实现Fruit抽象类
public class Apple extends Fruit{
public static void main(String[] args) {
Apple apple = new Apple();
apple.name = "apple";
apple.print();
apple.eat();
}
// 实现抽象方法
@Override
public void eat() {
System.out.println("I am eating an apple");
}
}
运行结果
This is an apple
I am eating an apple
接口
概念
如果一个类中的全部方法都是抽象方法,全部属性都是全局常量(用 static final 修饰),那么此时就可以将这个类定义为一个接口。
// 定义一个接口
interface 接口名{
全局常量;
抽象方法;
}
优点
接口是定义(规范、约束)与实现(名实分离的原则)的分离。
- 降低程序的耦合性
- 已与程序的扩展
- 有利于程序的维护
全局变量与抽象方法的简写
- 全局常量编写是,可以省略public static final关键字,例如:
// 全局常量
public static final String INFO = "内容";
// 简写后:
String INFO = "内容";
- 抽象方法编写时,可以省略public abstract关键字,例如:
// 接口中的抽象方法
public abstract void print();
// 简写后:
void print();
接口的实现
接口可以多实现
如果一个接口想要使用,必须依靠子类,子类(如果不是抽象类)要实现接口中的所有抽象方法。
class 子类 implements 父接口1,父接口2...{
}
如果一个类既要实现接口,又要继承抽象类,则可按照如下格式编写:
class 子类 extends 父类 implements 父接口1,父接口2...{
}
接口的继承
接口因为都是抽象部分,不存在具体的实现,所以允许多继承,例如:
interface C extends A,B{
}
final
final 的意思为最终的。用final关键字修饰的类、变量和方法都是不能改变的。
final修饰类
表示这个类不能被继承
final 类中的成员方法都会被隐式的指定为final 方法
final 修饰方法
一个类的 private 方法会隐式的被指定为 final 方法
如果父类中有 final 修饰的方法,那么子类不能去重写
final 修饰成员变量
必须初始化值
赋值方式有两种:直接赋值和在构造方法中赋值
修饰的成员变量是基本类型,则表示这个变量的值不能改变
修饰的成员变量是引用类型,则这个引用的地址不能修改,但是这个引用所指向的对象里的内容可以改变
static
静态资源是类初始化时加载的,而非静态资源是类new的时候加载的
被static修饰的变量属于类变量,可以通过类名.变量名直接引用,不需要new一个类
被static修饰的方法属于类方法,可以通过类名.方法名直接引用,不需要new一个类
类的静态资源是类实例间共享的,一处变,处处变。
// Test.java
public class Test {
public static int i=1;
public static void print(){
System.out.println("此方法属于Test类");
}
}
public class TestMain {
public static void main(String[] args) {
// 通过类名.变量名直接引用
System.out.println(Test.i);
// 通过类名.方法名直接引用
Test.print();
}
}
运行结果
1
此方法属于Test类
public class Test {
private int i=1;
public static void geti(){
System.out.println(i);
}
}
// 定义geti()方法错误,giti()是静态方法,在类初始化时加载,而变量i在Test类new时加载,类初始化早于new,
// 可以将i 也定义为静态变量,或者将geti()定义为非静态方法
由此,我们可以知道:
- 静态方法不可以使用非静态资源,因为非静态资源是new的时候才会产生的,静态方法初始化是,非静态资源还没有产生。
- 静态方法里可以使用静态资源,因为它们都是在类初始化时产生的。
- 非静态方法可以使用静态资源,因为在非静态方法产生时,静态方法已经存在。