为何非静态内部类不能有静态成员

来源:互联网 发布:最好的电子书软件 编辑:程序博客网 时间:2024/04/29 05:18

首先要弄清楚非静态内部类的概念。         非静态内部类如下:

public class Outer {class Inner {//public static int si = 1;错误,不允许存在静态的内部成员public static final int i = 1;}public static void main(String[] args) {Outer outer = new Outer();//错误非静态内部类的创建必须要有一个外部类的实例//Outer.Inner inner = new Outer.Inner(); Outer.Inner inner = outer.new Inner();//正确System.out.println(Outer.Inner.i);//正确}}

非静态内部类的创建、访问都需要有一个外部类的实例,通过外部类的实例才能访问到内部类。从底层的角度来说,外部类的实例持有持有指向内部类的指针,只有通过外部类实例才能访问到内部类的数据。 那么,为什么非静态内部类不能有静态的成员呢!?         首先从内存分配角度来说,众所周知,静态成员是在类加载时候分配内存空间的;但对于内部类来说,要访问它的成员,就要有一个外部类实例,但是在加载类的时候不可能实例化一个外部类给内部类的,因此,没有任何外部类的实例持有这个静态成员的指针,内部类的静态成员是无法访问到的,所以Java不允许有非静态内部类的静态成员。         有人会问,为什么不能用Outer.Inner.si来访问。很简单,如果这样就和静态内部类的静态成员的访问方式重复了,并且违反Java定义的非静态内部类要依赖外部类实例的原则。         有一种情况,就是

public static final int i = 1;
         为什么加了final就可以了呢,并且是通过Outer.Inner.i的方式访问,是不是违规了。其实这里Outer.Inner.i并不是通过变量来访问的,他在编译期已经确定了值,因为编译器会对static  final 声明的变量做优化,会用常量替换static  final声明的变量,因此,这里用常量1去代替了这里的Outer.Inner.i。虽然final可以在static块里面初始化值,但是非静态内部类已经不允许静态成员,同时由于编译器的优化的需要和非静态内部类依赖于外部类实例的特殊性,非静态内部类的static  final 只能用在基本类型(int,short,long byte,double,float, char,boolean)和字符串(String)类型上(数组也不行)。          Outer.Inner.i被替换的具体证据看class字节码22、25、26行部分
 // 堆栈:4,局部:3 public static void main(java.lang.String[] args); 0 new Outer [1] 3 dup 4 invokespecial Outer() [16] 7 astore_1 [outer] 8 new Outer$Inner [17] 11 dup 12 aload_1 [outer] 13 dup 14 invokevirtual java.lang.Object.getClass() : java.lang.Class [19] 17 pop//这里也说明了,Inner初始化时候需要一个Outer的实例, // 隐含的需要Outer参数的构造函数 18 invokespecial Outer$Inner(Outer) [23]  21 astore_2 [inner]//获取输出流(控制台) 22 getstatic java.lang.System.out :java.io.PrintStream [26]  25 iconst_1//明显被编译器替换成常量了,这里意思是将常量1传入寄存器//以寄存器中的int值作为参数,调用println方法 26 invokevirtual java.io.PrintStream.println(int) : void [32] 29 return
        对为何非静态内部类不能有静态成员的问题就探究到这里,如有错误请指出。

1 0