判断对象是否为null,小伙竟然用StringUtils.isEmpty(obj+"")
我在代码走查时,发现下面的代码。其中Line133行的StringUtils.isEmpty(levyId+"")
引起了我的注意。levyId是Long,你这样判断Long是否为null,靠谱吗?
答案是:不靠谱!
当levyId是null时,levyId+""
的值是什么? 是字符串null哟~~ 显然,org.apache.commons.lang.StringUtils.isEmpty("null")
是false。所以,还是老老实实地用 levyId==null
来判断Long是否为null吧。
那么,为什么levyId+""
在levyId是null时的值是字符串null?
在Java中,当你对一个对象使用加号 "+" 进行字符串连接时,实际上是先调用了String#valueOf方法将对象转换为字符串。
走,带你去司空见惯的java.lang.String小星球,看看它的静态成员valueOf方法。可以看到,valueOf有许多重载,除了valueOf(Object)以外,其他valueOf重载都是基于int/long/char/boolean等基本类型参数的。
对于非基本类型,即引用类型对象,就要用valueOf(Object)
了。来看看这个重载方法的实现。此刻,你应该知道levyId+""
当levyId为null时的值是字符串null的原因了吧!
// in java.lang.String /** * Returns the string representation of the {@code Object} argument. * * @param obj an {@code Object}. * @return if the argument is {@code null}, then a string equal to * {@code "null"}; otherwise, the value of * {@code obj.toString()} is returned. * @see java.lang.Object#toString() */ public static String valueOf(Object obj) { return (obj == null) ? "null" : obj.toString(); }
使用“+”拼接对象与字符串,首先调用了String#value将对象转换为字符串。怎么得出的这个结论?
下面代码:
TestObjectStringJoin.java源码 |
compile生成的二进制文件TestObjectStringJoin.class |
package jstudy; public class TestObjectStringJoin { public static void main(String[] args) { Long l = null; System.out.println(l + "abc"); } }
|
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package jstudy; public class TestObjectStringJoin { public TestObjectStringJoin() { } public static void main(String[] args) { Long l = null; System.out.println(l + "abc"); } }
|
使用javap -v来解析生成的TestObjectStringJoin.class文件里的字节码,重点看main方法(如下)。注意到其中有定义StringBuilder对象,并调用了StringBuilder#append(Object)方法。
D:\workspace\sboot\jstudy\target\test-classes\jstudy>javap -v TestObjectStringJoin.class Classfile /D:/workspace/sboot/jstudy/target/test-classes/jstudy/TestObjectStringJoin.class Last modified 2023-6-5; size 858 bytes MD5 checksum 02a2129f4d9044c32a956d668b575a20 Compiled from "TestObjectStringJoin.java" public class jstudy.TestObjectStringJoin ... { ... public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=2, args_size=1 0: aconst_null 1: astore_1 2: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream; 5: new #3 // class java/lang/StringBuilder 8: dup 9: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V 12: aload_1 13: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; 16: ldc #6 // String abc 18: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 21: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 24: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 27: return LineNumberTable: line 5: 0 line 6: 2 line 7: 27 LocalVariableTable: Start Length Slot Name Signature 0 28 0 args [Ljava/lang/String; 2 26 1 l Ljava/lang/Long; MethodParameters: Name Flags args } SourceFile: "TestObjectStringJoin.java"
总结一下就是,在使用加号 "+" 进行字符串连接时, Java 编译器在编译期间会自动插入代码。在编译时,Java 编译器会将加号 "+" 操作符转换为使用 `StringBuilder` 或 `StringBuffer` 类的 `append()` 方法来连接字符串。类似于String#valueOf,StringBuilder#append也有许多重载,不同重载的参数包括int/long/char/boolean等基本类型/String/Object。重点来了,重载的append(Object)会调用另一个重载append(String),这期间会调用String.valueOf(Object)将对象转换成String。关于String#valueOf(Object)方法,上面已经解释了,它会在对象为null时返回null字符串。见下图所示源码。
下面是与ChatGPT的对话,它对这个知识点的回答并不能令我满意。anyway,我为全世界的程序员出了一把力,训练了一下超强的AI大脑。
当看到一些不好的代码时,会发现我还算优秀;当看到优秀的代码时,也才意识到持续学习的重要!--buguge
本文来自博客园,转载请注明原文链接:https://www.cnblogs.com/buguge/p/17457777.html