zoukankan      html  css  js  c++  java
  • 【重走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
    个人博客网站:http://www.coderself.com
    关于作者:爱摄影,爱旅行,爱自由,爱生活,更要爱自己。活在当下,也在为未来打拼!
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。

  • 相关阅读:
    Python shutil模块
    configparser模块来生成和修改配置文件
    用random模块实现验证码
    python 正则re模块
    python 装饰器
    python迭代器和生成器
    python替换一个文件里面的特定内容
    广告资源收集
    Java 语言中 Enum 类型的使用介绍
    FreeMarker + xml 导出word
  • 原文地址:https://www.cnblogs.com/monodin/p/3841212.html
Copyright © 2011-2022 走看看