成员内部类:
- 内部类的默认持有外部类的引用(可以访问任何修饰符的成员变量、成员方法)
- 创建内部类对象,需要先持有一个外部类的实例(依附于外部类)
- 外部类访问内部类,需要先创建一个内部类的实例(也可创建多个内部类的实例)
- 内部类可以拥有private访问权限、protected访问权限、public访问权限及包访问权限
- 当内部类持有和外部类同名的变量/方法时,默认访问内部类的变量/方法(除非显示指定:外部类的类名.this.变量)
- 内部类不能定义静态属性和静态方法
**为什么内部类默认持有外部类的引用?**
javac 编译后,javap反编译后,就可以看到内部类的构造。(编译器为我们做了很多隐藏的工作)
编译器会为内部类的构造方法添加一个参数, 参数的类型就是外部类的类型;
也就是说内部类实例化时,将外部类的实例也传了进来,所以内部类默认持有外部类的引用,创建内部类也需要外部类的实例。
局部内部类:
- 定义在一个方法或者一个作用域里面的类,与成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。
- 局部内部类和局部变量一样,不能被public、protected、private和static修饰。
匿名内部类:
- 没有类名的内部类,不使用关键字class、extends、implements,没有构造函数,必须继承其他类或实现其他接口。
- 匿名内部类也是不能有访问修饰符和static修饰符的。
静态内部类:
- 只能访问外部类的静态成员、静态方法,包括private类型(与外部类是独立的)
- 可以通过外部类的类名,来创建内部类
内部类的优点:
- 内部类可以有多个实例,每个实例都有自己的状态信息,并且与其外围类对象那个的信息相互独立
- 隐藏部分实现细节
因为Java不支持多继承,支持实现多个接口。但有时候会存在一些使用接口很难解决的问题,这个时候我们可以利用内部类提供的、可以继承多个具体的或者抽象的类的能力来解决这些程序设计问题。可以这样说,接口只是解决了部分问题,而内部类使得多重继承的解决方案变得更加完整。
局部内部类和匿名内部类引用方法内的局部变量时,方法内的局部变量需要是final类型的。因为方法内的局部变量在方法运行结束后便不在存在,导致局部内部类/匿名内部类去访问一个不存在的数据。如果将局部变量定义为final类型,会保存一份局部变量的副本,作为内部类的一个成员数据。
而java8的lambda 表达式之所以不用写final,是因为Java8这里加了一个语法糖:在lambda表达式以及匿名类内部,如果引用某局部变量,则直接将其视为final。本质并没有改变。