我指着一些技巧并且碰到了这个。在以下代码中:
public class TestClass1 {
static int a = 10;
public static void main(String ar[]){
TestClass1 t1 = null ;
System.out.println(t1.a); // At this line
}
}
t1
对象为null
。为什么此代码不会抛出NullPointerException
?
我知道这不是访问static
变量的正确方法,但问题是关于NullPointerException
。
答案 0 :(得分:27)
调用静态成员或方法时不需要实例。
由于静态成员属于类而不是实例。
可以使用空引用来访问类(静态)变量,而不会导致异常。
http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#d5e19846
如果您看到示例(参见规范中的完整示例)
public static void main(String[] args) {
System.out.println(favorite().mountain); //favorite() returns null
}
即使favorite()的结果为null,也不会抛出NullPointerException。打印“Mount”表明主表达式确实在运行时完全评估,尽管事实上只使用其类型而不是其值来确定要访问的字段(因为该字段)山是静止的)。
答案 1 :(得分:11)
如果您使用以下代码反汇编类文件,请在当前答案中添加一些其他信息。
javap -c TestClass1
你会得到:
Compiled from "TestClass1.java"
public class TestClass1 extends java.lang.Object{
static int a;
public TestClass1();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: aconst_null
1: astore_1
2: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
5: aload_1
6: pop
7: getstatic #3; //Field a:I
10: invokevirtual #4; //Method java/io/PrintStream.println:(I)V
13: return
static {};
Code:
0: bipush 10
2: putstatic #3; //Field a:I
5: return
}
在这里,您可以看到,getstatc
指令在第7行完成了对静态字段的访问。每当通过代码访问静态字段时,将在.class
程序文件中生成相应的getstatic
指令。
*static
指令具有这样的特殊性:在调用它们之前,它们不会要求对象实例在堆栈中的引用(例如,在堆栈中需要对象引用的invokevirtual),他们仅使用run time constant pool的索引来解析字段/方法,该索引稍后将用于解决字段引用位置。
这是警告的技术原因“应该以静态方式访问静态字段”当你编写t1.a
时某些IDE会向你抛出,因为对象实例是没有必要解决静态字段。
答案 2 :(得分:2)
代码中NullPointerException
的原因是什么:
TestClass t = null;
t.SomeMethod();
如果SomeMethod是实例方法,它将使用标准执行某些操作 此参考:
public void SomeMethod() {
// Here we'll have a NullPointerException (since "this" is null)
this.SomeField = ... // <- We usually omit "this" in such a code
}
由于此 null ,我们将 NullPointerException 。如果方法,领域等
是静态它的保证了这个参考的缺席,所以会有
没有NullPointerException
public static void SomeStaticMethod() {
// You can't put "this.SomeField = ..." here, because the method is static
// Ans since you can't address "this", there's no reason for NullPointerException
...
}
...
TestClass t = null;
// Equal to "TestClass.SomeStaticMethod();"
t.SomeStaticMethod(); // <- "this" is null, but it's not addressed
答案 3 :(得分:2)
静态成员由类的所有实例共享,而不是单独重新分配给每个实例。这些是第一次遇到类时类加载器加载的。 A related SO question
请记住,这些静态成员不属于特定的类实例。 More info here. 我已经提供了一个小代码片段供参考:
@NotThreadSafe
public class Test {
// For all practical purpuose the following block will be only executed once at class load time.
static {
System.out.println("Loaded by the classloader : " + Test.class.getClassLoader());
}
// Keeps track of created instances.
static int instanceCount;
// A simple constructor that increments instance count.
public Test(){
Test.instanceCount++;
System.out.println("instance number : " + instanceCount);
}
public static void main(String[] args) {
System.out.println("Instaintiating objects");
new Test(); new Test();
}
// Where would you expect this line to get printed?
// i.e last statement on the console or somewhere in the middle :)
static {
System.out.println("It should be printed at the end or would it?");
}
}
答案 4 :(得分:1)
t1.a
相当于TestClass1.a
,因为a
是静态成员(不是实例成员)。编译器查看t1
的声明以查看它的类型,然后将其视为使用类型名称。永远不会检查t1
的值。 (但如果它是方法调用,如method(args).a
,我认为该方法将被调用。但返回值将被丢弃,并且从未检查过。)(编辑:我'已验证调用了method(args)
,但如果函数结果为null,则不会抛出异常。)
答案 5 :(得分:0)
由于a
是静态编译器,因此将其转换为TestClass1.a
。对于非静态变量,它会抛出NullPointerException
。
答案 6 :(得分:-1)
任何静态成员都可以通过Directly类名访问 TestClass1.a 不需要实例
System.out.println(TestClass1 .a);
输出: 10