【重走Android之路】【番外篇】有关于null的一些知识点
 

1、首先,到底什么是null?

null是Java中的一个关键字,用于表示一个空对象引用,但其本身并不是任何类型也不是属于任何对象。因此,下面的做法是错误的:
int a = null;
但:
Object obj = null;
是可以的,因为null表示Object类型的一个空对象引用,符合其用意。
 
【注1】引用类型使用null声明对象变量后,无法使用该变量访问对象的成员。例如上述obj对象如果使用obj.toString()便会报NullPointerException。
 
【注2】需要注意的是,上边已经说过,null并不属于任何对象,它代表的是一个不确定的对象引用。所以此时使用instanceof进行实例判断的时候均会返回false,例:
 String a = null;
Object b = null;
System.out.println(a instanceof String);// false
System.out.println(b instanceof Object);// false

2、那么,为什么要使用null?

null的作用有两个,即在声明对象的时候延缓内存的申请开销和告知JVM可以回收的内存。
例1(不考虑常量池):
 // 在栈内存中创建一个引用变量b;
// 在堆内存中申请空间存储“Use Memory”字符串;
// 把字符串的堆内存地址赋给变量b
String b = new String("Use Memory"); // 只在栈内存中创建一个引用变量a
String a = null;

【注3】在不必立马指定对象实例的前提下,使用null可以尽可能节省空间。

 
例2:
 // 声明对象并开辟空间保存User对象的值
User user = new User(1, "张三", 23); // ... 经过一番处理后不再使用 // 此时把user对象置为null,会告知JVM该对象不再被使用,可以进行回收
// JVM在必要的时机会把user对象对应的空间回收
user = null;

【注4】在确定对象不再使用的时候,使用null可以及时告知JVM等待回收的资源

 

3、一个关于null的调用困惑

看下边这段代码:
 /*
**********************************************************
*** ***
*** Copyright(C) 2014 Miao-Nodin. All rights reserved. ***
*** ***
*** Author: Miao-Nodin ***
*** ***
*** This is a source file of a part of a huge project. ***
*** Firstly, you must thank a pretty princess named ***
*** Lady喵~喵~ for bestowing on you this honour that ge ***
*** -ts this file. Who is she? She is my student. ***
*** ***
**********************************************************
*/
package com.miao.base; /**
* @Created: 2014年5月28日 by Miao-Nodin
* @Description: 演示使用null访问对象方法
*
* @Version:1.0
* @Update:
*
*/
public class Null {
private static final int LEVEL_ONE = 1;
private static final int LEVEL_TWO = 2;
private static final int LEVEL_THREE = 3; private static void say(int level) {
switch (level) {
case LEVEL_ONE:
System.out.println("请你放尊重点,不要碰我!");
break;
case LEVEL_TWO:
System.out.println("你再这样我要生气了!!!");
break;
case LEVEL_THREE:
System.out.println("讨厌了啦~~~臭流氓~~~");
break;
}
} public static void main(String[] args) {
Null x = 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)相当于Null x = (Null) null; 如此便和第一种一致。
 

4、null的字符串相加困惑

看下面这段代码:
 /*
**********************************************************
*** ***
*** Copyright(C) 2014 Miao-Nodin. All rights reserved. ***
*** ***
*** Author: Miao-Nodin ***
*** ***
*** This is a source file of a part of a huge project. ***
*** Firstly, you must thank a pretty princess named ***
*** Lady喵~喵~ for bestowing on you this honour that ge ***
*** -ts this file. Who is she? She is my student. ***
*** ***
**********************************************************
*/
package com.miao.base; /**
* @Created: 2014年6月4日 by Miao-Nodin
* @Description:演示null作为String类型空对象时的'+'操作
*
* @Version:1.0
* @Update:
*
*/
public class NullPlus { public static void plus() {
String a = null;
String b = null;
String c = a + b;
if("nullnull".equals(c)){
System.out.println("This String-Object is not null.");
}
} /**
* @param args
*/
public static void main(String[] args) {
plus();
}
}
输出结果:
This String-Object is not null.
 
困惑:null 加上 null为什么会是"nullnull"?
 
这个问题是String对'+'运算符的实现逻辑导致的。在Java中,String的'+'操作在被JVM编译后会使用StringBuilder的append(Object)方法代替,而StringBuilder的append(Object)方法源码如下:

 /**
* @see java.lang.String#valueOf(java.lang.Object)
* @see #append(java.lang.String)
*/
public StringBuilder append(Object obj) {
  return append(String. valueOf (obj));
}
public StringBuilder append(String str) {
  super.append (str);
  return this ;
}

看得出,问题在String.valueOf(obj),null通过这个方法被转化为了"null",然后才有"nullnull"这种奇怪的问题。

附:plus()方法被JVM编译后的字节码
 public static void plus();
Code:
Stack=3, Locals=3, Args_size=0
0: aconst_null
1: astore_0
2: aconst_null
3: astore_1
4: new #15; //class java/lang/StringBuilder
7: dup
8: aload_0
9: invokestatic #17; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
12: invokespecial #23; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
15: aload_1
16: invokevirtual #26; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: invokevirtual #30; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22: astore_2
23: ldc #34; //String nullnull
25: aload_2
26: invokevirtual #36; //Method java/lang/String.equals:(Ljava/lang/Object;)Z
29: ifeq 40
32: getstatic #40; //Field java/lang/System.out:Ljava/io/PrintStream;
35: ldc #46; //String This String-Object is not null.
37: invokevirtual #48; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
40: return
LineNumberTable:
line 28: 0
line 29: 2
line 30: 4
line 31: 23
line 32: 32
line 34: 40 LocalVariableTable:
Start Length Slot Name Signature
2 39 0 a Ljava/lang/String;
4 37 1 b Ljava/lang/String;
23 18 2 c Ljava/lang/String; StackMapTable: number_of_entries = 1
frame_type = 254 /* append */
offset_delta = 40
locals = [ class java/lang/String, class java/lang/String, class java/lang/String ]

另外,从上述字节码中也可以看出:源码中三行声明语句在Stack中一共创建了三个引用。至于创建了几个实例,请自行分析。

 

【重走Android之路】【番外篇】有关于null的一些知识点的更多相关文章

  1. 【重走Android之路】【番外篇】关于==和equals

    [重走Android之路][番外篇]关于==和equals   在实际的编程当中,经常会使用==和equals来判断变量是否相同.但是这两种比较方式也常常让人搞得云里雾里摸不着头脑.下面是我个人做的总 ...

  2. 【重走Android之路】【路线篇(二)】知识点归纳

    [重走Android之路][路线篇(二)]知识点归纳   参考:http://blog.csdn.net/xujing81/article/details/7313507   第一阶段:Java面向对 ...

  3. 【重走Android之路】【Java面向对象基础(二)】细说String、StringBuffer和StringBuilder

    [重走Android之路][基础篇(二)][Java面向对象基础]细说String.StringBuffer和StringBuilder   1.String String是Java中的一个final ...

  4. 【重走Android之路】【Java面向对象基础(三)】面向对象思想

    [重走Android之路][基础篇(三)][Java面向对象基础]面向对象思想   1 面向对象的WWH   1.1 What--什么是面向对象         首先,要理解“对象”.在Thinkin ...

  5. 【重走Android之路】【Java面向对象基础(一)】数据类型与运算符

    [重走Android之路][基础篇(一)][Java面向对象基础]数据类型与运算符   1.数据类型介绍 在Java中,数据类型分为两种:基本数据类型和引用类型. 基本数据类型共8种,见下表: 基本数 ...

  6. 【重走Android之路】【开篇】序

    [重走Android之路][开篇]   [序]         本人Nodin,偶尔也叫MoNodin,朋友们都喜欢叫我丁,还有个笔名叫陌上幽人,文艺时叫恋风,发奋时叫不肯腐烂的土壤...也许你觉得我 ...

  7. Python之路番外:PYTHON基本数据类型和小知识点

    Python之路番外:PYTHON基本数据类型和小知识点 一.基础小知识点 1.如果一行代码过长,可以用续行符 \换行书写 例子 if (signal == "red") and ...

  8. 【重走Android之路】【路线篇(一)】路线图

    总结归纳了J2SE和Android的知识点,自己制订了一套详细的路线图,其中肯定有考虑不全和不合适的地方,欢迎各位大牛批评指正.   详细路线图如下:  

  9. Apache Cordova开发Android应用程序——番外篇

    很多天之前就安装了visual studio community 2015,今天闲着么事想试一下Apache Cordova,用它来开发跨平台App.在这之前需要配置N多东西,这里找到了一篇MS官方文 ...

随机推荐

  1. android Service Activity交互之传递复杂数据类型的远程服务

    远程服务往往不只是传递java基本数据类型.这时需要注意android的一些限制和规定: android支持String和CharSequence 如果需要在aidl中使用其他aidl接口类型,需要i ...

  2. haproxy实现mysql从库负载均衡

    本文主要讲述通过haproxy实现mysql从库间的负载均衡,至于mysql主从的搭建,本文不再重述,可以参考我之前写的博客. 1.首先下载haproxy包 wget http://haproxy.1 ...

  3. 【Path Sum II】cpp

    题目: Given a binary tree and a sum, find all root-to-leaf paths where each path's sum equals the give ...

  4. 第九章 CSS-DOM

    另一个网友整理了很多书中的代码:http://www.cnblogs.com/jingangel/archive/2013/01/03/2843505.html 1. 三位一体的网页 浏览器看到的网页 ...

  5. Hibernate中启用日志

    Problem How do you determine what SQL query is being executed by Hibernate? How can you see the Hibe ...

  6. C语音的调查

    1.你对自己的未来有什么规划?做了哪些准备? 很迷茫吧.尽力做好该做的事情.希望认真学习代码,毕竟是专业课 2.你认为什么是学习?学习有什么用?现在学习动力如何?为什么? 学习有很多啊,不论是课堂上, ...

  7. 【POJ】【1160】Post Office

    DP/四边形不等式 邮局,经典的四边形不等式例题! 关于四边形不等式的学习请看 赵爽论文<动态规划加速原理之四边形不等式> 题目总结&题解:http://blog.csdn.net ...

  8. Java内存模型(JMM)

    参考: 1. http://www.tuicool.com/articles/UVzuQb

  9. 剑指offer--3题

    题目:输入一个整形数组,数组里有正数也有负数.数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和.求所有子数组的和的最大值.要求时间复杂度为O(n). 例如输入的数组为1, -2, 3, ...

  10. RabbitMQ 3.6 安装

    1. 首先安装这个 http://www.erlang.org/downloads 2. 再安装这个 http://www.rabbitmq.com/install-windows.html 3. 添 ...