【重走Android之路】【番外篇】有关于null的一些知识点
2014-07-13 16:11
351 查看
【重走Android之路】【番外篇】有关于null的一些知识点
inta=null;
但:
Objectobj=null;
是可以的,因为null表示Object类型的一个空对象引用,符合其用意。
【注1】引用类型使用null声明对象变量后,无法使用该变量访问对象的成员。例如上述obj对象如果使用obj.toString()便会报NullPointerException。
【注2】需要注意的是,上边已经说过,null并不属于任何对象,它代表的是一个不确定的对象引用。所以此时使用instanceof进行实例判断的时候均会返回false,例:
例1(不考虑常量池):
【注3】在不必立马指定对象实例的前提下,使用null可以尽可能节省空间。
例2:
【注4】在确定对象不再使用的时候,使用null可以及时告知JVM等待回收的资源
输出结果:
请你放尊重点,不要碰我!
你再这样我要生气了!!!
讨厌了啦~~~臭流氓~~~
困惑:【注1】中不是讲过,对象实例为null的变量不是无法访问对象的成员吗?为什么这里却可以正常调用方法?是不是Java的bug啊?
确实如此,【注1】讲的不错。但是,并不能说明上述代码是Java的bug。待我娓娓道来:
文章开头讲的很清楚,用null声明的对象是不包含对象实体的,此时如果用该对象变量访问对象实体必定会出错。但是,有一种情况例外!观察上述代码,被访问的成员变量和成员方法都有一个共性:被static修饰。对,就是这个static搞的鬼。我们知道,static类型的成员既可以被对象实例访问,也可以被类本身访问,也就是说它们是只与引用类有关而与对象实例无关的成员。
所以,当使用x.say(x.LEVEL_ONE)的时候,JVM检查x是Null的一个引用,便会不再进一步检查x是否被实例化过,而直接调用静态方法say(int)。第二种类似。第三种,直接使用null强制转换为Null对象,然后再调用say(int)方法,此时的((Null)
null)相当于Nullx=(Null)null;如此便和第一种一致。
输出结果:
ThisString-Objectisnotnull.
困惑:null加上null为什么会是"nullnull"?
这个问题是String对'+'运算符的实现逻辑导致的。在Java中,String的'+'操作在被JVM编译后会使用StringBuilder的append(Object)方法代替,而StringBuilder的append(Object)方法源码如下:
看得出,问题在String.valueOf(obj),null通过这个方法被转化为了"null",然后才有"nullnull"这种奇怪的问题。
附:plus()方法被JVM编译后的字节码
另外,从上述字节码中也可以看出:源码中三行声明语句在Stack中一共创建了三个引用。至于创建了几个实例,请自行分析。
1、首先,到底什么是null?
null是Java中的一个关键字,用于表示一个空对象引用,但其本身并不是任何类型也不是属于任何对象。因此,下面的做法是错误的:inta=null;
但:
Objectobj=null;
是可以的,因为null表示Object类型的一个空对象引用,符合其用意。
【注1】引用类型使用null声明对象变量后,无法使用该变量访问对象的成员。例如上述obj对象如果使用obj.toString()便会报NullPointerException。
【注2】需要注意的是,上边已经说过,null并不属于任何对象,它代表的是一个不确定的对象引用。所以此时使用instanceof进行实例判断的时候均会返回false,例:
Stringa=null; Objectb=null; System.out.println(ainstanceofString);//false System.out.println(binstanceofObject);//false
2、那么,为什么要使用null?
null的作用有两个,即在声明对象的时候延缓内存的申请开销和告知JVM可以回收的内存。例1(不考虑常量池):
//在栈内存中创建一个引用变量b; //在堆内存中申请空间存储“UseMemory”字符串; //把字符串的堆内存地址赋给变量b Stringb=newString("UseMemory"); //只在栈内存中创建一个引用变量a Stringa=null;
【注3】在不必立马指定对象实例的前提下,使用null可以尽可能节省空间。
例2:
//声明对象并开辟空间保存User对象的值 Useruser=newUser(1,"张三",23); //...经过一番处理后不再使用 //此时把user对象置为null,会告知JVM该对象不再被使用,可以进行回收 //JVM在必要的时机会把user对象对应的空间回收 user=null;
【注4】在确定对象不再使用的时候,使用null可以及时告知JVM等待回收的资源
3、一个关于null的调用困惑
看下边这段代码:/* ********************************************************** ****** ***Copyright(C)2014Miao-Nodin.Allrightsreserved.*** ****** ***Author:Miao-Nodin*** ****** ***Thisisasourcefileofapartofahugeproject.*** ***Firstly,youmustthankaprettyprincessnamed*** ***Lady喵~喵~forbestowingonyouthishonourthatge*** ***-tsthisfile.Whoisshe?Sheismystudent.*** ****** ********************************************************** */ packagecom.miao.base; /** *@Created:2014年5月28日byMiao-Nodin *@Description:演示使用null访问对象方法 * *@Version:1.0 *@Update: * */ publicclassNull{ privatestaticfinalintLEVEL_ONE=1; privatestaticfinalintLEVEL_TWO=2; privatestaticfinalintLEVEL_THREE=3; privatestaticvoidsay(intlevel){ switch(level){ caseLEVEL_ONE: System.out.println("请你放尊重点,不要碰我!"); break; caseLEVEL_TWO: System.out.println("你再这样我要生气了!!!"); break; caseLEVEL_THREE: System.out.println("讨厌了啦~~~臭流氓~~~"); break; } } publicstaticvoidmain(String[]args){ Nullx=null; x.say(x.LEVEL_ONE);//正常输出 ((Null)x).say(x.LEVEL_TWO);//正常输出 ((Null)null).say(x.LEVEL_THREE);//正常输出 } }
输出结果:
请你放尊重点,不要碰我!
你再这样我要生气了!!!
讨厌了啦~~~臭流氓~~~
困惑:【注1】中不是讲过,对象实例为null的变量不是无法访问对象的成员吗?为什么这里却可以正常调用方法?是不是Java的bug啊?
确实如此,【注1】讲的不错。但是,并不能说明上述代码是Java的bug。待我娓娓道来:
文章开头讲的很清楚,用null声明的对象是不包含对象实体的,此时如果用该对象变量访问对象实体必定会出错。但是,有一种情况例外!观察上述代码,被访问的成员变量和成员方法都有一个共性:被static修饰。对,就是这个static搞的鬼。我们知道,static类型的成员既可以被对象实例访问,也可以被类本身访问,也就是说它们是只与引用类有关而与对象实例无关的成员。
所以,当使用x.say(x.LEVEL_ONE)的时候,JVM检查x是Null的一个引用,便会不再进一步检查x是否被实例化过,而直接调用静态方法say(int)。第二种类似。第三种,直接使用null强制转换为Null对象,然后再调用say(int)方法,此时的((Null)
null)相当于Nullx=(Null)null;如此便和第一种一致。
4、null的字符串相加困惑
看下面这段代码:/* ********************************************************** ****** ***Copyright(C)2014Miao-Nodin.Allrightsreserved.*** ****** ***Author:Miao-Nodin*** ****** ***Thisisasourcefileofapartofahugeproject.*** ***Firstly,youmustthankaprettyprincessnamed*** ***Lady喵~喵~forbestowingonyouthishonourthatge*** ***-tsthisfile.Whoisshe?Sheismystudent.*** ****** ********************************************************** */ packagecom.miao.base; /** *@Created:2014年6月4日byMiao-Nodin *@Description:演示null作为String类型空对象时的'+'操作 * *@Version:1.0 *@Update: * */ publicclassNullPlus{ publicstaticvoidplus(){ Stringa=null; Stringb=null; Stringc=a+b; if("nullnull".equals(c)){ System.out.println("ThisString-Objectisnotnull."); } } /** *@paramargs */ publicstaticvoidmain(String[]args){ plus(); } }
输出结果:
ThisString-Objectisnotnull.
困惑:null加上null为什么会是"nullnull"?
这个问题是String对'+'运算符的实现逻辑导致的。在Java中,String的'+'操作在被JVM编译后会使用StringBuilder的append(Object)方法代替,而StringBuilder的append(Object)方法源码如下:
/**
*@seejava.lang.String#valueOf(java.lang.Object)
*@see#append(java.lang.String)
*/
publicStringBuilderappend(Objectobj){
returnappend(String.valueOf(obj));
}
publicStringBuilderappend(Stringstr){
super.append(str);
returnthis;
}
看得出,问题在String.valueOf(obj),null通过这个方法被转化为了"null",然后才有"nullnull"这种奇怪的问题。
附:plus()方法被JVM编译后的字节码
publicstaticvoidplus();
Code:
Stack=3,Locals=3,Args_size=0
0:aconst_null
1:astore_0
2:aconst_null
3:astore_1
4:new#15;//classjava/lang/StringBuilder
7:dup
8:aload_0
9:invokestatic#17;//Methodjava/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
12:invokespecial#23;//Methodjava/lang/StringBuilder."<init>":(Ljava/lang/String;)V
15:aload_1
16:invokevirtual#26;//Methodjava/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19:invokevirtual#30;//Methodjava/lang/StringBuilder.toString:()Ljava/lang/String;
22:astore_2
23:ldc#34;//Stringnullnull
25:aload_2
26:invokevirtual#36;//Methodjava/lang/String.equals:(Ljava/lang/Object;)Z
29:ifeq40
32:getstatic#40;//Fieldjava/lang/System.out:Ljava/io/PrintStream;
35:ldc#46;//StringThisString-Objectisnotnull.
37:invokevirtual#48;//Methodjava/io/PrintStream.println:(Ljava/lang/String;)V
40:return
LineNumberTable:
line28:0
line29:2
line30:4
line31:23
line32:32
line34:40
LocalVariableTable:
StartLengthSlotNameSignature
2390aLjava/lang/String;
4371bLjava/lang/String;
23182cLjava/lang/String;
StackMapTable:number_of_entries=1
frame_type=254/*append*/
offset_delta=40
locals=[classjava/lang/String,classjava/lang/String,classjava/lang/String]
另外,从上述字节码中也可以看出:源码中三行声明语句在Stack中一共创建了三个引用。至于创建了几个实例,请自行分析。
相关文章推荐
- Android学习之路小结1(加一些小知识点)
- android 积累一些关于rxjava的知识点
- 【重走Android之路】【番外篇】关于==和equals
- 写一些关于Android的知识点记录
- 关于Android中传递数据的一些讨论
- 关于Android中传递数据的一些讨论
- 关于Android 开发的一些资料
- 关于对话框的一些知识点
- 关于Android的一些体验
- 一些关于Android的代码地址
- 关于Android中传递数据的一些讨论
- 一些关于VC的知识点
- 关于Android下WIFI移植的一些指导文章
- 关于Android开发过程中,createImage的一些总结。
- [Android 博客]收集一些不错的,关于android的IT Blog,互相学习!
- 关于Android中传递数据的一些讨论
- java String 类的一些理解 关于==、equals、null
- 关于treeview的一些知识点
- MOSS中关于User Profile的一些知识点
- 关于android开发遇到的一些问题的解决办法---间断的更新