04 JAVA接口与内部类
4.1 接口
4.1.1 接口与抽象类
抽象类:声明方法的存在但不去实现它的类被叫做抽象类(abstract class),它用于要创建一个体现某些基本行为的类,并为该类声明方法,但不在该类中实现这些方法。
-
抽象类中可以没有抽象方法;接口中的方法必须是抽象方法(1.8中的增强接口除外)
-
抽象类中可以有普通的成员变量;接口中的变量必须被初始化
-
抽象类只能被单继承,一个类可以实现多个接口
-
抽象类不可被实例化,即不可直接new一个抽象类。但是,可以声明一个变量为抽象类类型,让改变量指向一个继承了该抽象类的具体子类实例
-
Java 8 中接口中会有 default 方法,即方法可以被实现(函数式接口)
-
Java 不支持多继承的原因是多继承会导致语言本身变得非常复杂,效率也会降低
(1)抽象类
public abstract class AbstractClassDemo {
private static final int cont = 1;
// 成员变量
private int id;
private String name;
// 抽象类可以有构造器
public AbstractClassDemo(int id, String name) {
this.id = id;
this.name = name;
}
// 非抽象方法
public int getId(){
return this.id;
}
// 即使抽象类中没有抽象方法也是可以的
// 抽象方法
public abstract String getName();
}
(2)接口
public interface InterfaceDemo {
// 不允许有变量
//int id;
// 变量必须被实例化
int cont = 1;
// 抽象方法
int get();
// 默认方法可以被实现类重写,也可以不被重写,直接使用
// JDK8中的default方法,可以在接口中定义多个默认方法
default void defaultMethod() {
System.out.println("这是默认方法");
}
default void defaultMethod1() {
System.out.println("这是默认方法1");
}
}
4.1.2 标识接口
由于Java不支持多重继承,即一个类只能有一个父类,为了克服单继承的缺点,引入了接口的概念。接口是抽象方法的集合,是一个特殊的抽象类。接口中只包含方法的定义,没有方法的实现。但是从jdk8开始,接口可以实现default方法和静态方法。接口中所有的方法都是抽象方法,接口中的成员方法的作用域修饰符是public。接口中不允许有变量的存在,只允许有常量,其修饰符是public static final。由于一个类可以实现多个接口,因此可以采用实现多个接口来间接达到多重继承的目的。
在Java语言中,有些接口内部没有声明任何方法,也就是说实现这些接口的类不需要重写任何方法,这些没有任何方法声明的接口被称为标识接口。标识接口对实现它的类没有任何语义上的要求,仅仅是充当一个标识,用来表明实现它的类属于一个特定的类型。因为程序中往往会使用instanceof来判断这个实例对象的类型是不是实现了某一个标识接口。
4.2 内部类
**内部类:**一个类被定义在另一个类的内部。(关联-聚合关系)
4.2.1 作用
1.私有变量访问:我们可以把内部类当成外部类的一个成员,内部类对于外部类的其它私有成员变量来说是不受访问控制权限所影响的。那么内部类就实现了跨域访问的特性。
2.隐藏式的多继承:这个多继承不同于接口的"多继承",而是实实在在的多继承。内部类也是一个单独类,既然是类就有面向对象的特性,它就可以进行继承操作,并且内部类的继承操作对于外部类是否发生过继承是没有影响的。这样内部类和外部类就共同实现了多继承操作(如果内部类中还有内部类,继承的个数会被扩大下去)。
3.可以作为抽象类、接口的匿名实现:一些接口和抽象类中的抽象方法非常少,单独的创建实现子类非常的麻烦,我们就可以通过匿名内部类的方式直接实现接口或者抽象类,这样能够简化类的设计过程。例如Swing中的事件处理、函数式接口都可以直接通过内部类来实现。
4.2.2 分类
内部类大体上可以分为四种类型:成员内部类,静态内部类,局部内部类、匿名内部类。
- 其中成员内部类和静态内部类都是直接定义在外部类中,它们可以作为单独的类被外部其它类所访问(受限于访问修饰符)——聚合关系
- 局域内部类和匿名内部类定义在方法中,它们并不会被外部的类所访问,只是一种临时类——依赖关系
(1)成员内部类
成员内部类为一个外部类一个成员的方式存在。
特点:
- 成员内部类可以无条件的访问外部类的所有的成员属性和方法不受 private 和 static影响;
- 当内部类的成员属性或者方法与外部类的同名时,会发生隐藏现象。直接在内部类中调用的是内部类的成员属性和方法。需要用 “外部类名”.this.成员变量/成员方法;
- 如果外部类需要访问内部类,需要生成内部对象,才能访问内部类对象;
- 成员内部类是依附外部类存在的,如果需要访问成员内部类,那么一定需要先创建外部类;
- 由于成员内部类的以为外部类的成员的方式存在,所以类修饰可以用 private public protect 无权限的修饰符修饰来表示内部的访问权限的问题。而外部类不可以。
(2)静态内部类
使用 static 修饰的成员内部类,也叫做嵌套内部类
特点
- 静态内部类不需要依赖于外部类的存在而被实例化,通常的内部类需要在其所依赖的外部类实例化之后才能被实例化
- 静态内部类不能使用外部内非 static 修饰的成员变量或方法,静态内部类只能访问外部类的静态变量。而且在非静态的内部类中,是不能创建静态的类变量。
(3)局部内部类
局部内部类主要是根据内部类定义的作用域来区分的,定义在一个方法或者某个作用域内的类。
特点
- 不能用访问权限修饰符关键字public、private、protected。因为其处于内部方法中,所以其与内部方法的局部变量一样,都不可被访问控制修饰符修饰
- 作用区间只在申明的局部内。局部内部类不能被外部类和外围类访问,它只能在定义的局部位置使用
(4)匿名内部类
匿名内部类属于局部内部类,是没有命名的局部内部类,匿名内部类只有类的实现体,而没有类的方法。
特点:
-
匿名内部类不能使用 static 和权限修饰符
-
匿名内部类没有构造器。通过new XXX 的生成一个对象引用
-
匿名内部类在使用同级别的局部变量时,此局部变量必须为final
public static void main(String[] args) { // 同级别局部变量,为final final int num = 0; Runnable runnable = new Runnable() { @Override public void run() { // 匿名内部类引用同级别局部变量 System.out.println(num); } }; }
-
匿名内部类属于局部内部类,所有局部内部类限制的条件在匿名内部中依然有效
匿名内部类相当于我们在使用接口(抽象类)时,临时创造了一个子类,但是这个子类我们没有给它命名,我们称之为匿名类。通常这样的类是在方法调用过程中创建并使用的。