• 无语,程序在main方法执行和在junit单元测试结果居然不一致


    问题:

    为什么程序在main方法执行和在junit单元测试结果不一致 ?


    测试步骤:

    1、先抛代码,看结果(JDK8下测试,Junit4.x)
    2、纠正程序再度运行
    3、疑惑
    4、反思

    一、先抛代码,看结果(JDK8下测试)

    事情是这样子的,上个星期有个朋友截图发我一块代码,说他这个代码咋地咋地,最后说在main方法和junit测试结果不同。

    我怀着不相信的态度执行了一次,初次的结果是一致的。所以我就告诉他结果是一致的

    本来事情算是告了一段落,晚上睡觉前突然好像发现什么东西漏了。
    早上起来再测试了一次… 哎,昨晚信誓旦旦的说没问题😂

    果然,结果如他的一样,我失算了!

    Main方法
    	public static void main(String[] args) {
             String s1 = new String("1") + new String("1");
             s1.intern();
             String s2 = "11";
             System.out.println(s1 == s2);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    Junit方法
        @Test
        public void test(){
            String s1 = new String("1") + new String("1");
            s1.intern();
            String s2 = "11";
            System.out.println(s1 == s2);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    第一种结果是 true,第二种结果是 false

    首先,为什么我昨晚执行的结果会一致呢?

    很不好意思,我昨晚漏写了intern(),所以 两者的结果都是 false …

    假如没写 intern(),那么我们来分析一下反编译后的代码
    在这里插入图片描述

    首先看Main方法,先看s1 , 他new 了两个 “1” ,然后通过StringBuilder 的append方法对俩字符串进行添加操作,最后用 StringBuilder 的 toString 方法返回了一个 堆中的字符串对象 “11”。
    其次看s2,他是直接在字符串常量池分配了一个对象 “11”
    所以 堆中的"11" 并不等于 字符串常量池中的"11" , 返回 false
    在这里插入图片描述

    test方法里面也是跟main方法里面是一样的,所以返回 false。

    二、纠正程序再度运行

    那加上intern()呢?
     public static void main(String[] args) {
             String s1 = new String("1") + new String("1");
             s1.intern();
             String s2 = "11";
             System.out.println(s1 == s2);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
     @Test
        public void test2(){
            //10,11,12这三个比较特殊
            String s1 = new String("1") + new String("1");
            s1.intern();
            String s2 = "11";
            System.out.println(s1 == s2);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    首先,前者的执行结果是 true,后者的执行结果是 false。

    先了解 intern() 是干嘛的

    /**
         * Returns a canonical representation for the string object.
         * 

    * A pool of strings, initially empty, is maintained privately by the * class {@code String}. *

    * When the intern method is invoked, if the pool already contains a * string equal to this {@code String} object as determined by * the {@link #equals(Object)} method, then the string from the pool is * returned. Otherwise, this {@code String} object is added to the * pool and a reference to this {@code String} object is returned. *

    * It follows that for any two strings {@code s} and {@code t}, * {@code s.intern() == t.intern()} is {@code true} * if and only if {@code s.equals(t)} is {@code true}. *

    * All literal strings and string-valued constant expressions are * interned. String literals are defined in section 3.10.5 of the * The Java™ Language Specification. * * @return a string that has the same contents as this string, but is * guaranteed to be from a pool of unique strings. */ public native String intern();

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    返回字符串对象的规范表示形式。
    最初为空的字符串池由
    类{@code-String}。
    调用intern方法时,如果池已包含
    等于此{@code string}对象的字符串,由
    使用{@link#equals(Object}方法,则池中的字符串为
    返回。否则,此{@code String}对象将添加到
    并返回对该{@code String}对象的引用。
    因此,对于任何两个字符串{@code s}{@code t}{@code.s.intern()==t.intern()是{@code true}
    当且仅当{@code s.equals(t)}{@code true}。
    所有文字字符串和字符串值常量表达式都是
    实习。字符串文字在的第3.10.5节中定义
    <引用>Java&trade;语言规范</cite>。
    @返回一个与此字符串内容相同的字符串,但
    保证来自唯一字符串池。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    String.intern()是一个Native方法
    在JDK1.6版本时:如果字符常量池中已经包含一个等于此String对象的字符串,则返回常量池中字符串的引用,否则,将新的字符串放入常量池,并返回新字符串的引用
    在JDK1.7以后,如果字符常量池中已经包含一个等于此String对象的字符串,则返回常量池中字符串的引用,否则,将此字符串的引用放入常量池,并返回此引用

    所以,main程序的s1,因为在堆中,常量池里面没有,所以它将指向字符串的引用放入常量池,s2创建时发现常量池已经有了引用(指向的值是堆中的"11"),所以s2指向常量池的引用,比较结果为 true。

    那test程序的s1呢?怎么就故事到这就偏离了呢?

    一开始我还以为难道 junit 里面测试使用的jdk版本是1.6?
    怎么测试结果是1.6的结果,后来发现换成除10,11,12之外的字符串就是同1.8的结果一致
    所以此猜想被推翻…
    任我百般查阅资料,最终也只是找到了一个相关的说明!—— 10,11,12这三个比较特殊
    在这里插入图片描述

    在这里插入图片描述
    搜索了挺久,也没找到其他相关的文章了,目前只能得知在单元测试中,10,11,12 这仨,在程序启动时就已经存在字符串常量池中,不然都没其他理由这样子…

    三、疑惑Junit单元测试 和 main函数区别

    网上搜罗了一下发现还真有挺多这些东西的…
    w
    在这里插入图片描述

    四 、反思

    其实对于这种东西没必要一直钻牛角尖,知道怎么用就行了,而官方的方法也是有返回值的,所以我们还是按照官方规范来编码就行了。即 String s2 = s1.intern();

  • 相关阅读:
    Java8-接口的新增(默认方法和静态方法)
    重定向(dup、dup2、dup3)--Linux
    kubernetes Service详解
    Java网络编程——粘包拆包出现的原因及解决方式
    Selenium4.0+Python3系列(四) - 常见元素操作(含鼠标键盘事件)
    Deformable Convolution 可变形卷积
    Linux搜索查找命令【详细整理】
    智囊AI-基于 ChatGPT 的 AI 工具产品 你的私人AI助手
    洛谷P1064[NOIP2006 提高组] 金明的预算方案题解
    springboot系列(十四):如何实现发送图片、doc文档等附件邮件?你一定得会|超级详细,建议收藏
  • 原文地址:https://blog.csdn.net/csnz123123/article/details/126674043