Java--深入理解字符串的String#intern()方法奥妙之处_intern java string 解释

学习路线:

这个方向初期比较容易入门一些,掌握一些基本技术,拿起各种现成的工具就可以开黑了。不过,要想从脚本小子变成黑客大神,这个方向越往后,需要学习和掌握的东西就会越来越多以下是网络渗透需要学习的内容:
在这里插入图片描述

需要体系化学习资料的朋友,可以加我V获取:vip204888 (备注网络安全)

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以点击这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!


**JDK1.7 执行输出结果:**



false
true


**大家可以先思考一下为什么结果是这样的?往下会具体介绍!**


### String##intern()源码




---


**先来看一下intern()方法的JDK源码如下:**



/\*\*

* 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();


**很显然通过源码可以看到intern()是一个native本地方法,但是native具体实现源码已经被隐藏了,这是一个历史故事了,SUN公司在JDK7开发期间,由于技术竞争和商业竞争陷入泥潭,无力再投入精力继续研发JDK,Oracle半路杀出直接收购Sun公司,Oracle接管JDK的研发后,发版了自己的Oracle JDK,Oracle的native底层等很多源码就被隐藏了,不过Oracle官方也声明OpenJDK和Oracle JDK7及以后版本,源码几乎是一模一样的,想要了解native底层源码具体实现过程,可以下载开源的OpenJDK的源码进行查看。**


OpenJDK官网:<https://hg.openjdk.java.net/>  
 GitHub也开源啦:<https://github.com/openjdk/jdk>


**例如String对应的OpenJDK底层源码主入口:jdk7\jdk\src\share\native\java\lang\String.c**



Java_java_lang_String_intern(JNIEnv *env, jobject this)
{
return JVM_InternString(env, this);
}


**native底层方法的实现,需要掌握C和C++的语法,学习门槛要求比较高,这里不是我们要学习的重点,不做具体介绍。**


## String#intern()方法作用




---


**前面JDK源码intern()方法的英文注释已经说明了intern()方法的有具体用途了,网上也有很多说明,不过这里我以个人的理解以及话术简单概括下intern()方法的作用如下:**



> 
> **(1)只要调用String对象的intern(),都会去找到字符串常量池,然后判断String对象的字符串内容是否已经存在常量池中,`不存在,则往字符串常量池中创建该字符串内容的对象(JDK6及之前)或创建新的引用并指向堆区已有对象地址(JDK7之后)`,存在则直接返回。**
> 
> 
> **(2)JDK7时,字符串常量池从永久代脱离,迁移到堆区中,相比于JDK6,变化不只是字符串常量池迁移到堆区而已,另一个变化就是调用字符串对象的intern()方法,如果字符串常量池中不存在该字符串内容的对象,则不会再像JDK6直接往字符串常量池中创建该字符串内容的对象,而是创建一个新的引用并指向堆区已有对象地址,实现字符串常量池和堆区字符串共用的目的,效率更高。**
> 
> 
> 


## JDK6 String#intern()执行说明


**一张图介绍前面示例代码JDK6执行过程如下:**  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/0b176d67fee043aba2ca2195b9dc3399.jpg)



/**
* JDK6 String#intern()执行说明
*/
public class StringInternTest {
public static void main(String[] args) {
//Step6.1
//创建了2个对象,分别是堆区的String对象和字符串常量池中的"a"对象,reference1引用指向在堆区中的对象地址
String reference1 = new String(“a”);
//Step6.2
//判断字符串常量池,是否该字符串"a",此前,池中已经有该对象了,因此会返回池中的对象地址的引用
reference1.intern();
//Step6.3
//字符串常量池中已存在字符串"a",因此reference2引用直接指向对象在字符串常量池中的地址
String reference2 = “a”;
//reference1指向对象地址是在堆区,reference2指向对象地址是在永久代的常量池,显然不可能一样
System.out.println(reference1 == reference2);

    //Step6.4
    //创建了2个对象,分别是在堆区的String对象(内容是"aa")和字符串常量池中的"a"对象
    //reference3引用指向对象在堆区中的地址,这过程还会在堆区创建了两个无引用的"a"对象,这里不做讨论
    String reference3 = new String("a") + new String("a");
    //Step6.5
    //判断永久代中的字符串常量池,是否存在该字符串"aa",这里是首次出现,因此直接将字符串拷贝并放到池中
    reference3.intern();
    //Step6.6
    //池中已存在该字符串,reference2引用直接指向对象在永久代字符串常量池中的地址
    String reference4 = "aa";
    //同样,reference3指向堆区地址,reference4指向永久代常量池中的地址,显然不可能一样
    System.out.println(reference3 == reference4);
}

}


## JDK7 String#intern()执行说明


**一张图介绍前面示例代码JDK7执行过程如下:**  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/696d7071018d4cb38ca223efd408759c.jpg)



/**
* JDK1.7 String#intern()执行说明
**/
public class StringInternTest {
public static void main(String[] args) {
//Step7.1
//创建了2个对象,分别是堆区的String对象和字符串常量池中的"a"对象,reference1引用指向在堆区中的对象地址
String reference1 = new String(“a”);
//Step7.2
//判断字符串常量池,是否该字符串"a",此前,池中已经有该对象了,因此会返回池中的对象地址的引用
reference1.intern();
//Step7.3
//字符串常量池中已存在字符串"a",因此reference2引用直接指向对象在字符串常量池中的地址
String reference2 = “a”;
//reference1指向对象地址是在堆区,reference2指向对象地址是在堆区的字符串常量池,引用指向的对象地址不一样
System.out.println( reference1 == reference2);

    //Step7.4
    //创建了2个对象,分别是在堆区的String对象(内容是"aa")和字符串常量池中的"a"对象(注意并不会创建"aa"对象)
    //reference3引用指向对象在堆区中的地址,这过程还会在堆区创建了两个无引用的"a"对象,这里不做讨论
    String reference3 = new String("a") + new String("a");
    //Step7.5
    //判断堆区的字符串常量池中,是否存在该字符串"aa",显然这里是首次出现
    //但并不像JDK6会新建对象"aa"存储,而是存储指向堆区已有对象地址的一个新引用
    reference3.intern();
    //Step7.6
    //指向池中已有该字符串的新引用,reference4引用直接指向字符串常量池中的这个新引用,新引用则指向堆区已有对象地址
    String reference4 = "aa";
    //reference4指向新引用,而新引用则指向堆区已有对象地址,跟reference3引用直接指向的对象地址是同一个
    System.out.println(reference3 == reference4);
}

## 经典面试问题之创建了几个对象?


**在实际的Java面试当中,经常会被问到字符串创建了几个对象的问题,主要是考察学习者对于对象的实例化以及字符串常量池在JVM结构体系中是如何运行的,个人觉得比较常见问题,无法就是如下几个:**


**1、最简单的比如:String s1 = “a” + “b”;创建了几个对象?**



> 
> 答:最多1个,多个字符串常量相加会被编译器优化为一个字符串常量即"ab",如果字符串常量池不存在,则创建该对象。
> 
> 
> 


**2、相对简单的比如:String s1 = new String(“ab”);创建了几个对象?**



> 
> 答:1个或2个,使用new实例化对象,必然会在堆区创建一个对象,另外一个就是如果在字符串常量池中不存在"ab"这个对象,则会创建这个"ab"常量对象。
> 
> 
> 


**3、稍微难一点的比如:String s2 = new String(“a”) + new String(“b”);创建了几个对象?**



> 
> 答:至少4个,最多6个  
>  堆区的1个new StringBuilder()和2个new String()  
>  还有1个是StringBuilder()的toString()方法底层实现是`new String(value, 0, count)`  
>  另外2个即"a"、"b"可能会在常量池新建对象  
>  有的同学可能会有疑问,那这个toString过程"ab"字符串不会在常量池中也创建吗?  
>  答案是,不会,最后StringBuilder的toString() 的调用,底层`new String(value, 0, count)` 并不会在字符串常量池中去创建"ab"对象。  
>  两个new String相加会被优化为StringBuilder,可以通过javac和javap查看汇编指令如下:  
>  `javac InternTest.java`  
>  `javap -c InternTest`
> 
> 
> 



public class com.justin.java.lang.InternTest {
public com.justin.java.lang.InternTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object.“”😦)V
4: return

public static void main(java.lang.String[]);
Code:

一、网安学习成长路线图

网安所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。
在这里插入图片描述

二、网安视频合集

观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。
在这里插入图片描述

三、精品网安学习书籍

当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。
在这里插入图片描述

四、网络安全源码合集+工具包

光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。
需要体系化学习资料的朋友,可以加我V获取:vip204888 (备注网络安全)

在这里插入图片描述

五、网络安全面试题

最后就是大家最关心的网络安全面试题板块
在这里插入图片描述在这里插入图片描述

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以点击这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值