1 public static String valueOf(Object obj) {
2 return (obj == null) ? "null" : obj.toString();
3 }
以上会报空指针异常;而下面这样就可以打印 null。
1 Object obj = null;
2 System.out.println(String.valueOf(obj));
这种方式能正常运行,原因如下:debug 代码会发现,两种方式执行了不同的重载方法,第一种执行了以下方法
1 public static String valueOf(char data[]) {
2 return new String(data);
3 }
第二种执行了以下方法,
1 public static String valueOf(Object obj) {
2 return (obj == null) ? "null" : obj.toString();
3 }
如果对重载不熟悉,很难解释其中原因;当然null
是另一个让人头疼的问题,
重载
Java 的重载解析过程是以两阶段运行的:
- 第一阶段:选取所有可获得并且可应用的方法或构造器。
- 第二阶段:在第一阶段选取的方法或构造器中选取最精确的一个,如果一个方法或构造器可以接受传递给另一个方法或构造器的任何参数,那么我们就说第一个方法比第二个方法缺乏精确性。
java.lang.String 中 valueOf() 所有重载方法
1 static String valueOf(boolean b)
2 static String valueOf(char c)
3 static String valueOf(char[] data)
4 static String valueOf(char[] data, int offset, int count)
5 static String valueOf(double d)
6 static String valueOf(float f)
7 static String valueOf(int i)
8 static String valueOf(long l)
9 static String valueOf(Object obj)
因为基本类型不能赋值 null,String.valueOf(null) 只能匹配 valueOf(char[] data)
和 valueOf(Object obj)
;而 valueOf(char data[])
更精确,所以选择执行 valueOf(char data[])
。
再看一个例子:
1 public static void hah(Integer i) {
2 System.out.println(i);
3 }
4
5 public static void hah(Long l) {
6 System.out.println(l);
7 }
8
9 private static void hah(Object o){
10 System.out.println(o);
11 }
null
null 有类型吗?null 是一个值还是一个对象?
我们知道若instanceof
左边为null
,那么不论右边是什么类,直接返回 false;至少可以知道 null 不是对象;其实执行valueOf(char[] data)
方法也证明了这点。
null 有类型吗?null 是一个值还是一个对象?
我们知道若instanceof
左边为null
,那么不论右边是什么类,直接返回 false;至少可以知道 null 不是对象;其实执行valueOf(char[] data)
方法也证明了这点。
1 System.out.println(String.valueOf((Integer)null));
null 造成的NullPointerException
大概是最常见的异常,不论是 JDK 还是第三方类库都做了很多工作尽可能的避免空指针异常;比如 Apache Commons 的 collections、lang 判空,Guava 的 Optional 等;甚至 Optional 类已经成为 Java 8 类库的一部分。
或许,大家认为这两段代码都会抛出空指针异常;其实,第二段代码会正常执行。
hah() 方法是上面例子中定义的方法,它用 static 修饰是静态方法;对于静态方法和静态变量,使用了静态绑定,并不会抛出空指针异常;但是像这种对象访问类成员的写法最好不要使用,很容易造成误解。
1 String s = null;
2 System.out.println(s.length());
1 Demo demo = null;
2 demo.hah("hello");
静态绑定就是在程序执行前方法已经被绑定(在编译期中已经确定);比如 demo.hah("hello") 这个代码,反编译 class 文件得到:
1 Demo demo = null;
2 hah((Object)"hello");
你会发现编译后方法调用已经和 demo 没有关系,必然不会抛出空指针异常。