内部类总结
内部类总览
内部类的知识点较多,我看了好几篇文章,想系统的总结一下。
内部类分为:静态内部类,成员内部类,方法内部类,匿名内部类
静态内部类
静态内部类是定义在另一个类里面用 static 修饰 class 的类,静态内部类不需要依赖于外部类(与类的静态成员属性类似)且无法使用其外部类的非 static 属性或方法(因为在没有外部类对象的情况下可以直接创建静态内部类的对象,如果允许访问外部类的非 static 属性或者方法就会产生矛盾)。
静态内部类和非静态内部类的区别
- 非静态内部类默认持有外部类的引用,静态内部类不存在该特性
- 非静态内部类中不能定义静态成员或者方法,静态内部类中可以随便定义
- 非静态内部类可以直接访问外部类的成员变量或者方法,静态内部类只能直接访问外部类的静态成员或者方法(实质是持有外部类名)
- 非静态内部类可以定义在外部类的任何位置(方法里外均可,在方法外面定义的内部类的 class 访问类型可以是 public、protected 等,方法里的只能是默认 class,类似局部变量),静态内部类只能定义在外部类中最外层,class 修饰符可以是 public、protected 等
- 非静态内部类创建实例时必须先创建外部类实例,静态内部类不依赖外部类实例
- 静态方法中定义的内部类是静态内部类(这时不能在类前面加 static 关键字),静态方法中的静态内部类与普通方法中的内部类使用类似,除了可以直接访问外部类的 static 成员变量或者方法外还可以访问静态方法中的局部变量(java 1.8 以前局部变量前必须加 final 修饰符)
成员内部类
成员内部类是没有用 static 修饰且定义在在外部类类体中的类,是最普通的内部类,可以看做是外部类的成员,可以无条件访问外部类的所有成员属性和成员方法(包括 private 成员和静态成员),而外部类无法直接访问成员内部类的成员和属性,要想访问必须得先创建一个成员内部类的对象然后通过指向这个对象的引用来访问;当成员内部类拥有和外部类同名的成员变量或者方法时会发生隐藏现象(即默认情况下访问的是成员内部类的成员,如果要访问外部类的同名成员需要通过 OutClass.this.XXX 形式访问);成员内部类的 class 前面可以有 private 等修饰符存在。
成员内部类可以直接访问外部成员
1 |
|
编译OutClass.java
会得到两个class文件,分别是OutClass.class
和OutClass$InnerClass.class
,通过javap OutClass$InnerClass.java
可以看到编译后:
1 |
|
可以看到有一个指向外部类的引用,并且构造函数对其赋值,所以在成员内部类中可以随意访问外部类成员或方法
成员内部类的子类
1 |
|
- 成员内部类继承语法格式: OutClass.InnerClass
- 继承类的构造器必须有指向外部类对象的引用,并且通过这个引用调用supper,因为成员内部类默认持有外部类的引用,不实例化外部类无法实例化自己
方法内部类
方法内部类(局部内部类)是定义在一个方法里面的类,和成员内部类的区别在于方法内部类的访问仅限于方法内;方法内部类就像是方法里面的一个局部变量一样,所以其类 class 前面是不能有 public、protected、private、static 修饰符的,也不可以在此方法外对其实例化使用。
匿名内部类
匿名内部类是一种没有构造器的类(实质是继承类或实现接口的子类匿名对象),由于没有构造器所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调,匿名内部类在编译的时候由系统自动起名为 OutClass$1.class,一般匿名内部类用于继承其他类或实现接口且不需要增加额外方法的场景(只是对继承方法的实现或是重写);匿名内部类的 class 前面不能有 pravite 等修饰符和 static 修饰符;匿名内部类访问外部类的成员属性时外部类的成员属性需要添加 final 修饰(1.8 开始可以不用)
使用总结
- 使用匿名内部类时必须是继承一个类或实现一个接口(二者不可兼得且只能继承一个类或者实现一个接口)
- 匿名内部类中是不能定义构造函数的,如需初始化可以通过构造代码块处理
- 匿名内部类中不能存在任何的静态成员变量和静态方法
- 匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效
- 匿名内部类不能是抽象类,必须要实现继承的类或者实现接口的所有抽象方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public class InterfaceT {
public static void main(String[] args) {
Person1 person1 = new Person1() {
@Override
public void eat() {
System.out.println("eat interface");
}
};
person1.eat();
}
}
interface Person1 {
public void eat();
}