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

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

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

null是Java中的一个关键字,用于表示一个空对象引用,但其本身并不是任何类型也不是属于任何对象。因此,下面的做法是错误的:

int a = null;

但:

Object obj = null;

是可以的,因为null表示Object类型的一个空对象引用,符合其用意。

【注1】引用类型使用null声明对象变量后,无法使用该变量访问对象的成员。例如上述obj对象如果使用obj.toString()便会报NullPointerException。

【注2】需要注意的是,上边已经说过,null并不属于任何对象,它代表的是一个不确定的对象引用。所以此时使用instanceof进行实例判断的时候均会返回false,例:

1 String a = null;
2 Object b = null;
3 System.out.println(a instanceof String);// false
4 System.out.println(b instanceof Object);// false

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

null的作用有两个,即在声明对象的时候延缓内存的申请开销和告知JVM可以回收的内存。

例1(不考虑常量池):

1 // 在栈内存中创建一个引用变量b;
2 // 在堆内存中申请空间存储“Use Memory”字符串;
3 // 把字符串的堆内存地址赋给变量b
4 String b = new String("Use Memory");
5
6 // 只在栈内存中创建一个引用变量a
7 String a = null;

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

例2:

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

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

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

看下边这段代码:

 1 /*
 2  **********************************************************
 3  ***                                                    ***
 4  *** Copyright(C) 2014 Miao-Nodin. All rights reserved. ***
 5  ***                                                    ***
 6  ***                 Author: Miao-Nodin                 ***
 7  ***                                                    ***
 8  *** This is a source file of a part of a huge project. ***
 9  *** Firstly, you must thank a pretty princess named    ***
10  *** Lady喵~喵~ for bestowing on you this honour that ge ***
11  *** -ts this file. Who is she? She is my student.      ***
12  ***                                                    ***
13  **********************************************************
14  */
15 package com.miao.base;
16
17 /**
18 * @Created: 2014年5月28日 by Miao-Nodin
19 * @Description: 演示使用null访问对象方法
20 *
21 * @Version:1.0
22 * @Update:
23 *
24 */
25 public class Null {
26     private static final int LEVEL_ONE = 1;
27     private static final int LEVEL_TWO = 2;
28     private static final int LEVEL_THREE = 3;
29
30     private static void say(int level) {
31         switch (level) {
32         case LEVEL_ONE:
33             System.out.println("请你放尊重点,不要碰我!");
34             break;
35         case LEVEL_TWO:
36             System.out.println("你再这样我要生气了!!!");
37             break;
38         case LEVEL_THREE:
39             System.out.println("讨厌了啦~~~臭流氓~~~");
40             break;
41         }
42     }
43
44     public static void main(String[] args) {
45         Null x = null;
46         x.say(x.LEVEL_ONE);                 //正常输出
47         ((Null) x).say(x.LEVEL_TWO);        //正常输出
48         ((Null) null).say(x.LEVEL_THREE);   //正常输出
49     }
50 }

输出结果:

请你放尊重点,不要碰我!
你再这样我要生气了!!!
讨厌了啦~~~臭流氓~~~

  困惑:【注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的字符串相加困惑

看下面这段代码:

 1 /*
 2  **********************************************************
 3  ***                                                    ***
 4  *** Copyright(C) 2014 Miao-Nodin. All rights reserved. ***
 5  ***                                                    ***
 6  ***                 Author: Miao-Nodin                 ***
 7  ***                                                    ***
 8  *** This is a source file of a part of a huge project. ***
 9  *** Firstly, you must thank a pretty princess named    ***
10  *** Lady喵~喵~ for bestowing on you this honour that ge ***
11  *** -ts this file. Who is she? She is my student.      ***
12  ***                                                    ***
13  **********************************************************
14  */
15 package com.miao.base;
16
17 /**
18 * @Created: 2014年6月4日 by Miao-Nodin
19 * @Description:演示null作为String类型空对象时的‘+‘操作
20 *
21 * @Version:1.0
22 * @Update:
23 *
24 */
25 public class NullPlus {
26
27     public static void plus() {
28         String a = null;
29         String b = null;
30         String c = a + b;
31         if("nullnull".equals(c)){
32             System.out.println("This String-Object is not null.");
33         }
34     }
35
36     /**
37      * @param args
38      */
39     public static void main(String[] args) {
40         plus();
41     }
42 }

输出结果:

This String-Object is not null.

困惑:null 加上 null为什么会是"nullnull"?

这个问题是String对‘+‘运算符的实现逻辑导致的。在Java中,String的‘+‘操作在被JVM编译后会使用StringBuilder的append(Object)方法代替,而StringBuilder的append(Object)方法源码如下:

 1 /**
 2  *  @see      java.lang.String#valueOf(java.lang.Object)
 3  *  @see      #append(java.lang.String)
 4  */
 5 public  StringBuilder append(Object obj) {
 6   return  append(String. valueOf (obj));
 7 }
 8 public  StringBuilder append(String str) {
 9   super.append (str);
10   return   this ;
11 }

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

附:plus()方法被JVM编译后的字节码

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

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

本文系Nodin原创,转载请注明出处!http://www.cnblogs.com/monodin/p/3841212.html

【喵"的Android之路】【番外篇】有关于null的一些知识点,布布扣,bubuko.com

时间: 2024-12-09 22:33:48

【喵"的Android之路】【番外篇】有关于null的一些知识点的相关文章

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

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

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

很多天之前就安装了visual studio community 2015,今天闲着么事想试一下Apache Cordova,用它来开发跨平台App.在这之前需要配置N多东西,这里找到了一篇MS官方文章:配置 Visual Studio Tools for Apache Cordova.看着这片文章开始一个个安装,nodejs.chrome.Git.Apache Ant.Oracle Java.Android SDK等,然后又是配置环境变量.最后呢,我们可以使用VS自带的检测工具来检测一下依赖环

云架构在甲方的落地之路-番外篇之企业安全

本来准备直接就云架构定义开启新的篇章.但是在仔细斟酌之后发现,将一整套云基础架构搬出来讲解,从技术角度来说其实并不是难事,但是这样就体现不出甲方IT在设计架构整个过程中的考量.正如在背景篇中介绍的,一个甲方的IT在思考架构或者方案时必定需要和企业自身的组织架构,业务需求结合在一起,并不是看到别人上个新颖的概念或者系统我们就必须依样画葫芦. 在云架构的设计中有至关重要的一项那就是云安全.说到云安全,许多同志们我估计第一反应就是IPS,IDS,FW, DLP, anti-virus,WAF等一整套全

【喵&quot;的Android之路】【番外篇】关于==和equals

[喵"的Android之路][番外篇]关于==和equals 在实际的编程当中,经常会使用==和equals来判断变量是否相同.但是这两种比较方式也常常让人搞得云里雾里摸不着头脑.下面是我个人做的总结,希望能起到拨云见日的作用. [讲前普及] 请阅读 [喵"的Android之路][基础篇(一)][Java面向对象基础]数据类型与运算符 了解Java基本数据类型和引用数据类型 1."=="运算符 通俗点讲,==运算符比较的是两个变量在栈中的内容是否相同. 以a ==

Monkey源码分析番外篇之Android注入事件的三种方法比较

原文:http://www.pocketmagic.net/2012/04/injecting-events-programatically-on-android/#.VEoIoIuUcaV 往下分析monkey事件注入源码之前先了解下在android系统下事件注入的方式,翻译一篇国外文章如下. Method 1: Using internal APIs 方法1:使用内部APIs This approach has its risks, like it is always with intern

Android好奇宝宝_番外篇_看脸的世界_05

上一篇番外篇讲了一个炒鸡炒鸡简单的自定义ProgressBar,这一篇基于上一篇的基础扩展为SeekBar,没看过上一篇的,请先看一遍:传送门 先上效果图(2G内存的机子运行模拟器,所以有点卡): 这个效果之前不知道在哪里看到过,我也忘了. 下面进入正题: 测量大小和绘制部分沿用上一篇ProgressBar的,不清楚的请走上面的传送门. 对比上一篇的扩展: (1)SeekBar能通过触摸改变刻度 (2)SeekBar上方添加一个显示当前刻度的浮动View(后面用FloatView表示) (1)通

编程珠玑番外篇之番外篇-O 中间语言和虚拟机漫谈(ZZ)

http://blog.youxu.info/2014/05/11/language-and-vm/ 导言 编程语言的发展历史,总的来说,是一个从抽象机器操作逐步进化为抽象人的思维的过程.机器操作和人的思维如一枚硬币的两面,而语言编译器就像是个双面胶,将这两面粘在一起,保证编程语言源程序和机器代码在行为上等价.当然,人本身并不是一个完美的编译器,不能无错的将思维表达为高级语言程序,这种偏差,即Bug.因为编译器的帮助,我们可以脱离机器细节,只关心表达思维和程序行为这一面. 编程语言的发展日新月异

iOS冰与火之歌番外篇 - 在非越狱手机上进行App Hook(转载)

作者简介:郑旻(花名蒸米),阿里巴巴移动安全部门资深安全工程师,香港中文大学移动安全(Android & iOS)方向博士,曾在腾讯.百度以及硅谷的FireEye实习.在博士期间发表了多篇移动安全方向的论文(BlackHat.AsiaCCS等),去过10多个不同的国家做论文演讲. 曾帮助Apple公司修复了多处iOS安全问题,并且Apple在官网表示感谢.同时也是蓝莲花战队和Insight-labs的成员,在业余时间多次参加信息安全竞赛(Defcon.AliCTF.GeekPwn等),并取得优异

编程珠玑番外篇

1.Plan 9 的八卦 在 Windows 下喜欢用 FTP 的同学抱怨 Linux 下面没有如 LeapFTP 那样的方便的工具. 在苹果下面用惯了 Cyberduck 的同学可能也会抱怨 Linux 下面使用 FTP 和 SFTP 是一件麻烦的事情. 其实一点都不麻烦, 因为在 LINUX 系统上压根就不需要用 FTP. 为什么呢? 因为一行简单的配置之后, 你就可以像使用本机文件一样使用远程的任何文件. 无论是想编辑, 查看还是删除重命名, 都和本机文件一样的用. 这么神奇的功能到底如何