【参考链接】
Java内部类详解http://www.cnblogs.com/dolphin0520/p/3811445.html
静态内部类和非静态内部类的区别http://www.cnblogs.com/dolphin0520/p/3811445.html
为什么Java内部类要设计成静态和非静态两种?https://www.zhihu.com/question/28197253
内部类分为静态内部类和非静态内部类两类,其中非静态内部类又可分为非匿名内部类和匿名内部类
一共是3种
静态内部类 vs 非静态内部类
换个角度来理解
如果要让我们自己来为一个类设计内部类,并且其可以包含static成员变量成员方法,我们会怎么设计?
1、 内部类的static成员方法,只能访问外部类的static成员变量成员方法
2、 内部类的非static成员方法,可以先创建一个外部类的对象,通过这个对象访问其非static成员变量成员方法,当然也可以访问static的
但是Java并没有采用这种设计,而是做了拆分,分为两种内部类
如果要实现第1种需求,可以为这个类定义一个静态内部类,并且将其增强为可以直接创建静态内部类的对象,其非static方法也可以访问外部类的static成员变量成员方法
如果要实现第2种需求,可以为这个类定义一个非静态内部类。
如果需要同时实现这两个需求,这可以在这个类中定义两个内部类。
为了区分这两种内部类,要求在定义静态内部类的时候类名前加static修饰符。
因此,Java中的静态内部类和非静态内部类的区别如下:
1、 静态内部类可以独立创建对象,非静态内部类必须依托于外部类对象创建
2、 静态内部类中可以有static成员,也可以有非static成员,但是非static成员方法只能访问外部类中的static成员。非静态内部类不能有static成员。
不过非静态内部类可以有staticfinal类型常量哈
因为如前所述,static final在被其他类引用的时候,并不是被引用而是直接在该类中自动生成了一条新的常量项。
静态内部类
Java Code
1 | package com.shadowfaxghh.test; |
Java Code
1 | package com.shadowfaxghh.test; |
上述静态内部类中不能访问外部类的非静态成员
非静态内部类
非静态内部类的对象需要依托于外部类的对象而存在,也就是说
要创建非静态内部类的对象,必须先创建一个外部类的对象。(也说明了创建外部类的对象的时候,并不会自动创建内部类的对象)
所以,在非静态内部类的方法中可以,可以自由访问外部类的成员变量和成员方法,无视访问控制修饰符。可以通过如下方式来访问
外部类.this.成员变量、外部类.this.成员方法
非静态内部类可以用public、protected、private、包访问控制修饰符修饰,这会影响创建内部类对象的地点和方式
如下代码所示,OutterClass内部有InnerClass1、InnerClass2两个非静态内部类
InnerClass1因为是public的所以可以在OutterClass外部创建,InnerClass2因为是private的所以只能在OutterClass内部创建
Java Code
1 | package com.shadowfaxghh.test; |
Java Code
1 | package com.shadowfaxghh.test; |
上述main()方法中的InnerClass1 i2 = o.new InnerClass2();会编译异常
为什么在非静态内部类中可以访问外部类对象的成员呢?
这是因为,在编译的时候,会为外部类、内部类生成独立的.class文件
并且会修改非静态内部类的构造函数,为其添加一个外部类的形参,保存在内部类自动添加的一个成员变量中。内部类的对象通过这个成员变量访问外部类的对象
如上的代码,生成了如下.class文件,其中显然OutterClass$InnerClass1.class就是内部类InnerClass1的。
可以看到其拥有一个外部类类型的名为this$0的成员变量
会在创建对象的时候赋值(从字节码看来还调用了外部类的getClass()方法,但是却并未使用返回的Class对象,不解)
访问外部类对象的成员变量也是通过这个成员变量
所以,在外部类的静态方法中,也不能创建其内部类的对象
因为创建内部类对象时要求传递一个外部类对象,而此时调用静态方法时可能并未创建外部类对象
非匿名内部类 vs 匿名内部类
非静态内部类也可以继承类或实现接口
Java Code
1 | package com.shadowfaxghh.test; |
上述代码还可以简写成如下方式,创建对象时不定义内部类的名称,直接重写其方法,此即为匿名内部类
Java Code
1 | package com.shadowfaxghh.test; |
并且还可以在重写的方法中,访问外部方法的局部变量,但是要求这些局部变量是final的。
(在JDK7.0及之前,只要是访问就会要求该变量声明final,在JDK7.0之后,仅访问不会提示,当修改的时候才会)
(注意这里是访问外部方法的局部变量才限制,访问外部类的成员变量是没有限制的)
Java Code
1 | public void omethod1(float a){ |
Compiler compliance level: 1.7 | Compiler compliance level: 1.8 |
这是因为
虽然是匿名,但是仍然会生成自己的.class文件
以上述代码中的new Thread()为例,生成了一个内部类,继承自Thread,自动添加了一个所在外部类类型的成员变量
以上述代码中的newOnClickListener为例,生成了一个内部类,继承自Object,实现了OnClickListener接口,也自动添加了一个所在外部类类型的成员变量
而对外部方法的局部变量的访问,也是通过为这个匿名类自动添加两个成员变量,在创建对象的时候赋值这两个成员变量
而对如下代码而言,子线程main()方法的执行,有可能在b=200f;之前,也有可能是在b=200f;之后,这样就会导致执行run()方法时,数据的不一致性。
所以要求在匿名内部类中访问外部方法中的局部变量时,其必须为final的。