一、内部类的定义和作用
将一个类定义在另一个类里面或者方法里面的类成为内部类。
作用和优点:每个内部类都能独立的继承一个接口的实现,所以无论外部类是否继承了某个接口的实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整。弥补了单继承的缺点
二、内部类的用法
1.成员内部类
成员内部类即为外部类的成员,非静态的。可以直接访问外部类的所有成员和方法,当有和内部类重名时需要用外部类.this.属性或方法。外部类访问内部类需要创建一个内部类的对象才可以访问。PS:成员内部类不能含有static的修饰符。因为内部类属于非静态的,先要创建外部类,才能创建它自己。成员内部类的修饰符可以是private,默认的,protected,public或者是静态的。和外部类(public和default)不同
public class Outer {
public static void main(String[] args){
Outer outer = new Outer();
Outer.Inner inner = outer.getInner();
inner.print("成员内部类第一次打印");
inner = outer.new Inner();
inner.print("这是一成员内部类");
}
public Inner getInner(){
return new Inner();
}
public static void printOuter(){
System.out.println("这是一个外部类");
}
public class Inner{
public void print(String str){
getInner();
Outer.this.printOuter();
System.out.println(str);
}
}
}
2.局部内部类
局部内部类定义在方法或作用域内部。
public class Part {
public Destination print(String str){
class a implements Destination{
private String label;
private a(String label){
this.label = label;
}
public String read() {
return label;
}
}
return new a(str);
}
public static void main(String[] args){
System.out.println(new Part().print("这是一局部内部类的事例").read());
}
}
3.静态内部类
静态内部类可以让外部类随意访问,无需创建内部类的对象。静态内部类不能访问外部类的非静态成员,因为非静态成员必须是对象存在的时候,所以有矛盾。
public class Singleton2 {
// 提供一私有构造函数
private Singleton2(){};
public Integer i;
public static Singleton2 getInstance(){
return SingletonHolder.instance;
}
private static class SingletonHolder{
private static final Singleton2 instance = new Singleton2();
}
}
4.匿名内部类
匿名内部类也可以进行实例初始化,虽然没有构造函数
public class Outer {
public static void main(String[] args){
Outer outer = new Outer();
Outer.Inner inner = outer.getInner("Inner", "gz");
System.out.println(inner.getName());
System.out.println(inner.getProvince());
}
public Inner getInner(final String name, final String city){
// 匿名内部类中使用
return new Inner() {
private String namString = name;
private String province;
// 实例初始化
{
if(city.equals("gz")){
province = "gd";
}else {
province = "";
}
}
public String getName() {
return namString;
}
public String getProvince() {
return province;
}
};
}
interface Inner{
String getName();
String getProvince();
}
}
5.内部类的继承
注意的是继承内部类的类的构造函数默认传递外部类参数,并调用super方法,不然会有编译错误。
class InheritInner extends Outer.Inner {
public InheritInner(Outer outer){
outer.super();
}
public static void main(String[] args){
new InheritInner(new Outer()).print("这是内部类继承");
}
}
三、注意的问题
1.为什么成员内部类可以无条件的访问外部类成员:
首先要明确一点的是内部类和外部类是两份字节码,即Outer.class, Outer$Inner.class.
通过反编译字节码可以看出,内部类的构造器是无参构造函数,编译器会默认添加一个参数,改参数的类型为指向外部类对象的一个引用,所以成员内部类中的Outter this&0 指针便指向了外部类对象,因此可以在成员内部类中随意访问外部类的成员。
2.为什么匿名内部类和局部内部类只能访问局部的final。
public class Test {
public static void main(String[] args) {
}
public void test(final int b) {
final int a = 10;
new Thread(){
public void run() {
System.out.println(a);
System.out.println(b);
};
}.start();
}
}
如上面的代码,因为是当test方法执行完毕之后,变量a的生命周期就结束了,而此时Thread对象的生命周期很可能还没有结束,那么在Thread的run方法中继续访问变量a就变成不可能了,但是又要实现这样的效果,怎么办呢?Java采用了 复制,也就说如果局部变量的值在编译期间就可以确定,则直接在匿名内部里面创建一个拷贝。如果局部变量的值无法在编译期间确定,则通过构造器传参的方式来对拷贝进行初始化赋值。 的手段来解决这个问题。复制的手段会带来一个问题,会导致数据不一致,所以采用final为不可变