无语,程序在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);
    }
Junit方法
    @Test
    public void test(){
        String s1 = new String("1") + new String("1");
        s1.intern();
        String s2 = "11";
        System.out.println(s1 == s2);
    }

第一种结果是 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);
    }
 @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);
    }
首先,前者的执行结果是 true,后者的执行结果是 false。

先了解 intern() 是干嘛的

/**
     * Returns a canonical representation for the string object.
     * <p>
     * A pool of strings, initially empty, is maintained privately by the
     * class {@code String}.
     * <p>
     * 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.
     * <p>
     * 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}.
     * <p>
     * All literal strings and string-valued constant expressions are
     * interned. String literals are defined in section 3.10.5 of the
     * <cite>The Java&trade; Language Specification</cite>.
     *
     * @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();
返回字符串对象的规范表示形式。
最初为空的字符串池由
类{@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>。
@返回一个与此字符串内容相同的字符串,但
保证来自唯一字符串池。
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();

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值