Java String 对 null 对象的容错处理

  《Thinking in Java》中有这样一段话:

  Primitives that are fields in a class are automatically initialized to zero, as noted in the Everything Is an Object chapter. But the object references are initialized to null, and if you try to call methods for any of them, you’ll get an exception-a runtime error. Conveniently, you can still print a null reference without throwing an exception.

  大意是:原生类型会被自动初始化为 0,但是对象引用会被初始化为 null,如果你尝试调用该对象的方法,就会抛出空指针异常。通常,你可以打印一个 null 对象而不会抛出异常。

  第一句相信大家都会容易理解,这是类型初始化的基础知识,但是第二句就让我很疑惑:为什么打印一个Java中 null 对象不会抛出异常?带着这个疑问,下面我将详细阐述我解决这个问题的思路,并且深入 JDK 源码找到问题的答案。

  决问题的过程

  可以发现,其实这个问题有几种情况,所以我们分类讨论各种情况,看最后能不能得到答案。

  首先,我们把这个问题分解为三个小问题,逐一解决。

  第一个问题

  直接打印 null 的 String 对象,会得到什么结果?

  String s = null;

  System.out.print(s);

  运行的结果是

  null

  果然如书上说的没有抛出异常,而是打印了null。显然问题的线索在于print函数的源码中。我们找到print的源码:

  public void print(String s) {

  if (s == null) {

  s = "null";

  }

  write(s);

  }

  看到源码才发现原来就只是加了一句判断而已,简单粗暴,可能你对 JDK 的简单实现有点失望了。放心,第一个问题只是开胃菜而已,大餐还在后面。

  第二个问题

  打印一个 null 的非 String 对象,例如说 Integer:

  Integer i = null;

  System.out.print(i);

  运行的结果不出意料:

  null

  我们再去看看print的源码:

  public void print(Object obj) {

  write(String.valueOf(obj));

  }

  有点不一样的了,看来秘密藏在valueOf里面。

  public static String valueOf(Object obj) {

  return (obj == null) ? "null" : obj.toString();

  }

  看到这里,我们终于发现了打印 null 对象不会抛出异常的秘密。print方法对 String 对象和非 String 对象分开进行处理。

  String 对象:直接判断是否为 null,如果为 null 给 null 对象赋值为"null"。

  非 String 对象:通过调用String.valueOf方法,如果是 null 对象,就返回"null",否则调用对象的toString方法。

  通过上面的处理,可以保证打印 null 对象不会出错。

  到这里,本文就应该结束了。

  什么?说好的大餐呢?上面还不够塞牙缝呢。

  开玩笑啦。下面我们来探讨第三个问题。

  第三个问题(隐藏的大餐)

  null 对象与字符串拼接会得到什么结果?

  String s = null;

  s = s + "!";

  System.out.print(s);

  结果可能你也猜到了:

  null!

  为什么呢?跟踪代码运行可以发现,这回跟print没有什么关系。但是上面的代码就调用了print函数,不是它会是谁呢?+的嫌疑最大,但是+又不是函数,我们怎么看到它的源代码?这种情况,唯一的解释就是编译器动了手脚,天网恢恢,疏而不漏,找不到源代码,我们可以去看看编译器生成的字节码。

  L0

  LINENUMBER 27 L0

  ACONST_NULL

  ASTORE 1

  L1

  LINENUMBER 28 L1

  NEW java/lang/StringBuilder

  DUP

  INVOKESPECIAL java/lang/StringBuilder. ()V

  ALOAD 1

  INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;

  LDC "!"

  INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;

  INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;

  ASTORE 1

  L2

  LINENUMBER 29 L2

  GETSTATIC java/lang/System.out : Ljava/io/PrintStream;

  ALOAD 1

  INVOKEVIRTUAL java/io/PrintStream.print (Ljava/lang/String;)V

  看了上面的字节码是不是一头雾水?这里我们就要扯开话题,来侃侃+字符串拼接的原理了。

  编译器对字符串相加会进行优化,首先实例化一个StringBuilder,然后把相加的字符串按顺序append,最后调用toString返回一个String对象。不信你们看看上面的字节码是不是出现了StringBuilder。

  String s = "a" + "b";

  //等价于

  StringBuilder sb = new StringBuilder();

  sb.append("a");

  sb.append("b");

  String s = sb.toString();

  再回到我们的问题,现在我们知道秘密在StringBuilder.append函数的源码中。

  //针对 String 对象

  public AbstractStringBuilder append(String str) {

  if (str == null)

  return appendNull();

  int len = str.length();

  ensureCapacityInternal(count + len);

  str.getChars(0, len, value, count);

  count += len;

  return this;

  }

  //针对非 String 对象

  public AbstractStringBuilder append(Object obj) {

  return append(String.valueOf(obj));

  }

  private AbstractStringBuilder appendNull() {

  int c = count;

  ensureCapacityInternal(c + 4);

  final char[] value = this.value;

  value[c++] = ‘n‘;

  value[c++] = ‘u‘;

  value[c++] = ‘l‘;

  value[c++] = ‘l‘;

  count = c;

  return this;

  }

  现在我们恍然大悟,append函数如果判断对象为 null,就会调用appendNull,填充"null"。

时间: 2025-01-01 22:56:51

Java String 对 null 对象的容错处理的相关文章

Java中的null对象也可以访问static成员变量和方法

声明:本博客为原创博客,未经允许,不得转载!小伙伴们如果是在别的地方看到的话,建议还是来csdn上看吧(链接为 http://blog.csdn.net/bettarwang/article/details/26515271),看代码和提问.讨论都更方便. 一般来说,一个类的对象要在实例化之后才可以访问类中的成员变量和方法.如果它还是null,通常意义上我们就认为它不能访问类中的成员.实际上确实不提倡这样,而且null对象确实不能访问实例成员(变量和方法),否则会引发NULLPointerExc

java String不可变对象,但StringBuffer是可变对象

什么是不可变对象? 众所周知, 在Java中, String类是不可变的.那么到底什么是不可变的对象呢? 可以这样认为:如果一个对象,在它创建完成之后,不能再改变它的状态,那么这个对象就是不可变的.不能改变状态的意思是,不能改变对象内的成员变量,包括基本数据类型的值不能改变,引用类型的变量不能指向其他的对象,引用类型指向的对象的状态也不能改变. 区分对象和对象的引用 对于Java初学者, 对于String是不可变对象总是存有疑惑.看下面代码: [java] view plain copy Str

java String 提供的方法

String类的判断功能: 1 * boolean equals(Object obj):比较字符串的内容是否相同,区分大小写 2 * boolean equalsIgnoreCase(String str):比较字符串的内容是否相同,忽略大小写 3 * boolean contains(String str):判断大字符串中是否包含小字符串 4 * boolean startsWith(String str):判断字符串是否以某个指定的字符串开头 5 * boolean endsWith(St

12-02 java String类

String构造方法 package cn.itcast_01; /* * 字符串:就是由多个字符组成的一串数据.也可以看成是一个字符数组. * 通过查看API,我们可以知道 * A:字符串字面值"abc"也可以看成是一个字符串对象. * B:字符串是常量,一旦被赋值,就不能被改变. * * 构造方法: * public String():空构造 * public String(byte[] bytes):把字节数组转成字符串 * public String(byte[] bytes,

(转)Java 中关于String的空对象(null) ,空值(empty),空格

原文出处:Java 中关于String的空对象(null) ,空值(empty),空格 定义 空对象: String s = null; 空对象是指定义一个对象s,但是没有给该对象分配空间,即没有实例化该对象,因此,空对象在调用所有对象方法时候都会抛出异常,如s.length(), s.isEmpty()等方法. 空值: String k = ""; 空值是指一个字符床对象已经实例化,即系统已经给该变量分配了空间,只是对象的内容为空. 空格: String n = " &qu

Java中利用MessageFormat对象实现类似C# string.Format方法格式化

我们在写C#代码的时候常常会使用到string.Format("待格式化字符串{0},{1},....",参数1,参数2,...),来格式化字符串,特别是拼接字符的时候,这种方式使得代码更为直观清楚. 最近使用java时候却java的string.Format与c#重点string.Format用法不一样,java中的string.format更类似于C语言的sprintf()方法 例如: String str=null; str=String.format("Hello,%

关于Java String 类型转换时null的问题

开发中经常遇到从集合类List.Map中取出数据转换为String的问题,这里如果处理不好,经常会遇到空指针异常java.lang.NullPointerException,在此总结一下常用转换为String的方法,以及转换后如何对其进行判null使用的问题. Java中对象转换为String的常用方法: 方法1.String objStr = (String) obj:    强制类型转换,对象obj为null,结果也为null,但是obj必须保证其本质是String类型的值,即可转换的值.例

JAVA中对null进行强制类型转换(null可以强转为任意对象,并执行对象的静态方法)

今天很好奇,对null进行强转会不会抛错.做了如下测试得到的结果是, 如果把null强转给对象,是不会抛异常的,本身对象是可以为null的. 但是如果是基本类型,比如 int i = (Integer)obj的强转,其实内部会调用intvalue方法去赋值给基本类型,所以这时候是会报错的. 代码如下 Object obj = null;Integer s1 = (Integer)obj; 上面能正常执行,即把null赋值给一个对象或者强行类型转换赋值给对象都是没有问题的.如果s1是 int的基本

Java String对象的经典问题(转)

public class StringTest { public static void main(String[] args) { String strA = "abc"; String strB = "abc"; String strC = new String("abc"); System.out.println(strA == strB);//true System.out.println(strA == "abc")