首先要弄清楚非静态内部类的概念。
非静态内部类如下:
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
对为何非静态内部类不能有静态成员的问题就探究到这里,如有错误请指出。
分享到:
相关推荐
在探讨C++中类的存储及其地址的探究实验中,我们首先需要理解几个核心概念:类、对象、静态成员以及存储空间。 在C++中,类是面向对象程序设计的基础,它是一种用户定义的数据类型,可以包含数据成员(变量)和函数...
- 静态内部类不能直接访问外部类的非静态成员,而普通内部类可以直接访问。 4. **位运算符&与逻辑运算符&&**: - `&`执行位运算,逐位进行与操作。 - `&&`执行逻辑与,只有当两边的操作数都为真时,结果才为真。...
当我们使用new关键字创建对象或通过类名访问静态成员时,系统将类名传递给被注册的类加载器函数,该函数根据类名自行找到对应的类文件并include。 YiiBase类的autoload方法提供了两个预先存放的数组:$_coreClasses...
26_静态成员变量和静态成员函数 27_C++面向对象模型初探_传智扫地僧 28_this指针 29_作业 源码及文档 01_上一次课程回顾 02_const修饰的是谁_传智扫地僧 03_this的const修饰课堂答疑 04_全局函数pk成员函数(返回...
- **为什么给软件建模?** - 有助于更好地理解需求。 - 在编码之前构建全面的设计。 - **有效地使用UML** - **人员之间传达**: 通过图表形式更容易理解复杂系统。 - **最后的文档**: 为未来维护提供清晰的指导。 ...
- 只能访问类的静态成员。 - **非静态方法**: - 需要通过对象实例调用。 - 可以访问类的所有成员。 #### 3. 编写和调用重载方法(第433页) - **方法重载**:在同一类中定义多个同名方法,但参数列表不同。 - *...
以及“7 Why”可能指的是“5 Why”方法的一个变体,这是一种问题解决技术,通过连问“为什么”五次(或更多次)来探究问题的根本原因。“解决方案”则指出文章可能提供了针对发现的问题的解决方案。 在具体内容部分...
- **抽象类定义及其使用**:抽象类不能被实例化,通常包含一个或多个抽象方法,这些方法没有具体实现,必须在派生类中实现。 - **接口--概念、理解、本质**:接口定义了一组行为规范,任何实现了该接口的类都必须...
**解析**:合作学习强调的是小组内部成员之间的互动和全班范围内的分享交流。因此,最合适的选项是D,它体现了合作学习的基本要素:首先通过组合阅读的形式获取信息,然后在小组内进行讨论,接着个人可以根据小组...