最近在重读 《Think in Java》的时候,发现了下面这段话:
普通内部类的字段和方法,只能放在类的外部层次上,所以普通的内部类不能有static数据和方法,也不能包含嵌套类(也就是静态内部类)。但是嵌套类能够包含所有这些东西。
错误的答案
自己先想了下为什么普通内部类不能够包含静态属性和方法,想了半天也想不通,就到网上搜了下相关问题,结果大部分答案都是如下所示:
某种解释
但是上面这个答案是不正确的,类在以下几种情况下就会进行加载:
- 遇到
new
、getStatic
、putStatic
或invokeStatic
这四条字节码指令时,类如果没有执行过初始化,需要先触发初始化。生成这4条指令最常见得Java
代码场景是:使用new
关键字实例化对象的时候,读取和设置一个类的静态属性(被final
修饰,已经在编译器放入常量池的除外),调用一个类的静态方法。- 使用
java.lang.reflect
包的方法对类进行反射调用的时候,如果类没有进行过初始化,需要对其进行初始化。- 当初始化一个类的时候,如果其父类还没出进行过初始化,需要对这个父类进行初始化。
- 当虚拟机启动的时候,用户指定的那个要执行的类(包含
main
方法)需要先进行初始化。
也就是说,如果我们主类需要使用到内部类的静态方法的时候,会先初始化这个内部类,上面答案说的调用先于加载的情况就不会发生。
正确的答案
那为什么Java
中不能在内部类中有静态变量和方法呢?
我觉得应该从静态内部类和非静态内部类的定义来说明。
首先我们来说说内部类的作用。
内部类是为了弥补Java
中没有多继承的缺陷而产生的。所以静态内部类用于那些与外部类关系不是特别大,不需要调用外部类中的非静态属性和非静态方法。换句话说,静态内部类就相当于一个可以获取到其他类的静态私有属性和私有方法的类,除此之外和放在外面的类不没有什么区别。
public class Test {
public static void main(String[] args) {
A.a();
}
private static int aa = 1;
static class A{
private static void a(){
System.out.println(aa);
}
}
}
静态内部类与静态方法
而非静态内部类则不一样了,非静态内部类比静态内部类更像多继承一些。其能够访问外部类的所有属性和方法。非静态内部类想要做到这一点需要持有一个外部类对象的引用。那么问题就来了,如果说我们允许非静态内部类中存在静态方法,那在调用重名方法的时候怎么知道调用的是哪个方法呢?
我们先看下外部类是如何处理这种情况的:
public static void main(String[] args) {
Outer.a();
Outer.Inner.a();
}
}
class Outer{
static void a(){
System.out.println("这是外部类的方法");
}
static class Inner{
static void a(){
System.out.println("这是内部类的方法");
}
}
}
输出结果为:
这是外部类的方法
这是内部类的方法
可以看出,我们可以通过Outer.a
和Outer.Inner.a
来确定到底使用的是哪一个方法。
非静态内部类调用自身的非静态方法
但是如果是非静态内部类呢?因为非静态内部类中持有外部类对象的引用,我们如何确定想要使用的是哪个方法呢?
先看下非静态方法:
public class Test {
public static void main(String[] args) {
Outer.Testb test = new Outer().getTestb();
test.b();
}
}
class Outer{
void b(){
System.out.println("外部类的b方法");
}
class Testb{
void b(){
System.out.println("Testb的方法");
}
}
Testb getTestb(){
return new Testb();
}
}
输出为:
Testb的方法
所以对于内部类与外部类存在方法名与参数列表都相同的方法时,我们能够准确地确定执行的是哪一个方法。对于内部类对象来说,执行的一定是自己内部的方法,而不是外部类的方法。
非静态内部类调用外部类的非静态方法
因为我们生成的是Testb
对象,调用的是Testb
类中的b
方法,如果想要调用外部类中的方法,我们不能通过内部类对象直接调用,需要通过内部类方法调用。
public class Test {
public static void main(String[] args) {
Outer.Testb test = new Outer().getTestb();
test.b();
}
}
class Outer{
void b(){
System.out.println("外部类的b方法");
}
void c(){
System.out.println("外部类的c方法");
}
class Testb{
void b(){
System.out.println("Testb的方法");
System.out.println("通过Testb的方法调用外部类的方法");
c();
}
}
Testb getTestb(){
return new Testb();
}
}
输出:
Testb的方法
通过Testb的方法调用外部类的方法
外部类的c方法
真正的答案
到现在就很明显了,如果允许内部类中有静态方法,而外部类中又有一个相同名称和参数类型的静态方法,那我们就不知道如何确认调用的是哪一个方法了。而这才是为什么非静态内部类中不能拥有静态方法或者属性的原因。
网友评论