String.valueOf()传入null值的问题
今天在项目中遇到了一个String.valueOf()中传入null值的问题,刚开始颇为不解,后来经过百度和查看源码才得以解惑,记录下来,以防以后再次踩坑。上代码:
public class MyTest {
@Test
public void test(){
String str = String.valueOf(null);
System.out.println(str);
}
@Test
public void test1(){
Map<String, Object> map = new HashMap<String, Object>();
map.put("id", null);
String str = String.valueOf(map.get("id"));
System.out.println(str);
}
}
测试输出:
test()方法:
java.lang.NullPointerException
at java.lang.String.<init>(String.java:166)
at java.lang.String.valueOf(String.java:3008)
at cloudboy.MyTest.test(MyTest.java:11)
test1()方法:
null
Process finished with exit code 0
因此很困惑啊,明明两个方法中传入的都是null值,为何一个报空指针异常,一个却是正常的。点击方法进入源码后才发现,它们进入了不同的方法:
String str = String.valueOf(null);
public static String valueOf(char data[]) {
return new String(data);
}
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
另一个:
String str = String.valueOf(map.get("id"));
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
原来,valueof方法被重载多次,其中String.valueof(null)进入了valueof(char[] data)方法,valueof(char[] data)方法直接进入String构造器,内部会获取char[]的length,因此返回空指针异常。String.valueOf(map.get(“id”))却进入了valueOf(Object obj)方法,参数为null时,返回了一个字符串“null”,所以出现了不同的结果。
那重载方法是如何被选择的呢?
多个重载方法均能匹配的条件下,优先会选择精度高的那个,或者说范围小的那个,那上面的问题就明朗了,char[]和object均能匹配null值,而char[]是继承自object的(java中数组也是一种特殊的object),因此string。valueOf(null)优先选择精度高的char[],而String.valueOf(map.get(“id”))中map的v值已经声明为了object,所以它只能进入valueOf(Object obj)方法。
总结:java编译器在选择重载方法时,如果重载方法参数个数一致,且具备多个重载方法可以匹配到所传递的参数,此时,会优先选择精度相对较高的,即java继承树种树的深度较深的那个。
补充一下常见的一些考题:
例子1:
public class TestNull {
public static void main(String[] args) {
test(null); //此处将返回arraylist,因为list同arraylist存在继承或实现的上下级关系
}
static void test(List list) {
System.out.println("list");
}
static void test(ArrayList list) {
System.out.println("arraylist");
}
}
结果:输出arraylist
例子2:
public class TestNull {
public static void main(String[] args) {
test(null); //本行会编译报错
}
static void test(String str) {
System.out.println("string");
}
static void test(Integer num) { //如果修改为int,则编译通过
System.out.println("integer");
}
}
结果:编译失败
因为构造函数是编译时期确定的,代码将无法通过编译,因为String同Integer不存在任何关系,他们都继承自Object,他们的公共父类是object(String和String[]也是一样的道理),此时null值不知道该传入那个方法,因此无法通过编译,将报错。