博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
String intern驻足
阅读量:6291 次
发布时间:2019-06-22

本文共 3791 字,大约阅读时间需要 12 分钟。

之前在出题考察实验室大一的时候,无意间才第一次了解到String的intern方法,那么这个intern方法到底做了什么,看一下源码中对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();

首先能看到这个方法是一个native方法,我们先不去看这个方法是如何实现的,先来根据这一段注释来了解一下intern方法的作用以及一些知识

简单来说就是intern用来返回常量池中的某字符串,如果常量池中已经存在该字符串,则直接返回常量池中该对象的引用。否则,在常量池中加入该对象,然后返回引用

举一个例子

String name1 = "Ram"; String name2 = "Ram";

比如上面的两个字符串name1和name2,在声明第一个name1的时候,首先去查找常量区中是否有"Ram"这个常量,如果没有的话就在常量区开辟空间并存储这个"Ram",当name2声明的时候同样也是去常量区查找,第二次查找的结果当然是已经存在,并且将常量区的同一个字符串的引用返回给name2

Java中的String被设计成不可变的,出于以下几点考虑:

  1. 字符串常量池的需要。字符串常量池的诞生是为了提升效率和减少内存分配。可以说我们编程有百分之八十的时间在处理字符串,而处理的字符串中有很大概率会出现重复的情况。正因为String的不可变性,常量池很容易被管理和优化。

  2. 安全性考虑。正因为使用字符串的场景如此之多,所以设计成不可变可以有效的防止字符串被有意或者无意的篡改。从java源码中String的设计中我们不难发现,该类被final修饰,同时所有的属性都被final修饰,在源码中也未暴露任何成员变量的修改方法。(当然如果我们想,通过反射或者Unsafe直接操作内存的手段也可以实现对所谓不可变String的修改)。

  3. 作为HashMap、HashTable等hash型数据key的必要。因为不可变的设计,jvm底层很容易在缓存String对象的时候缓存其hashcode,这样在执行效率上会大大提升。

下面来一些例子体会一下Java String类型的奥秘,或许这些结果你能预测到,但是理解更好

public static void main(String[] args) {    String s1 = new String("aaa");    String s2 = "aaa";    System.out.println(s1 == s2);    // false    s1 = new String("bbb").intern();    s2 = "bbb";    System.out.println(s1 == s2);    // true    s1 = "ccc";    s2 = "ccc";    System.out.println(s1 == s2);    // true    s1 = new String("ddd").intern();    s2 = new String("ddd").intern();    System.out.println(s1 == s2);    // true    s1 = "ab" + "cd";    s2 = "abcd";    System.out.println(s1 == s2);    // true    String temp = "hh";    s1 = "a" + temp;    // 如果调用s1.intern 则最终返回true    s2 = "ahh";    System.out.println(s1 == s2);    // false    temp = "hh";    s1 = "a" + temp;    s2 = "ahh";    System.out.println(s1 == s2);    // false    temp = "hh";    s1 = ("a" + temp).intern();    s2 = "ahh";    System.out.println(s1 == s2);    // true    s1 = new String("1");    // 同时会生成堆中的对象 以及常量池中1的对象,但是此时s1是指向堆中的对象的    s1.intern();            // 常量池中的已经存在    s2 = "1";    System.out.println(s1 == s2);    // false    String s3 = new String("1") + new String("1");    // 此时生成了四个对象 常量池中的"1" + 2个堆中的"1" + s3指向的堆中的对象(注此时常量池不会生成"11")    s3.intern();    // jdk1.7之后,常量池不仅仅可以存储对象,还可以存储对象的引用,会直接将s3的地址存储在常量池    String s4 = "11";    // jdk1.7之后,常量池中的地址其实就是s3的地址    System.out.println(s3 == s4); // jdk1.7之前false, jdk1.7之后true    s3 = new String("2") + new String("2");    s4 = "22";        // 常量池中不存在22,所以会新开辟一个存储22对象的常量池地址    s3.intern();    // 常量池22的地址和s3的地址不同    System.out.println(s3 == s4); // false// 对于什么时候会在常量池存储字符串对象,我想我们可以基本得出结论: 1. 显示调用String的intern方法的时候; 2. 直接声明字符串字面常量的时候,例如: String a = "aaa";// 3. 字符串直接常量相加的时候,例如: String c = "aa" + "bb";  其中的aa/bb只要有任何一个不是字符串字面常量形式,都不会在常量池生成"aabb". 且此时jvm做了优化,不//   会同时生成"aa"和"bb"在字符串常量池中    System.out.println("!!!!!");    temp = "123";    s1 = ("1" + temp);    s2 = "1123";    System.out.println(s1 == s2);}

到了这里想必大家也都能理解,为什么String会有intern这个方法,也为什么我给这篇笔记起名为驻足了,驻足常量区,提高效率吧

转载地址:http://arcta.baihongyu.com/

你可能感兴趣的文章
MacOS High Sierra 12 13系统转dmg格式
查看>>
关于再次查看已做的多选题状态逻辑问题
查看>>
动态下拉菜单,非hover
查看>>
政府安全资讯精选 2017年第十六期 工信部发布关于规范互联网信息服务使用域名的通知;俄罗斯拟建立备用DNS;Google打击安卓应用在未经同意情况下收集个人信...
查看>>
简单易懂的谈谈 javascript 中的继承
查看>>
iOS汇编基础(四)指针和macho文件
查看>>
Laravel 技巧锦集
查看>>
Android 使用 ViewPager+RecyclerView+SmartRefreshLayout 实现顶部图片下拉视差效果
查看>>
Flutter之基础Widget
查看>>
写给0-3岁产品经理的12封信(第08篇)——产品运营能力
查看>>
ArcGIS Engine 符号自动化配置工具实现
查看>>
小程序 · 跳转带参数写法,兼容url的出错
查看>>
flutter error
查看>>
Flask框架从入门到精通之模型数据库配置(十一)
查看>>
10年重新出发
查看>>
2019年-年终总结
查看>>
聊聊elasticsearch的RoutingService
查看>>
让人抓头的Java并发(一) 轻松认识多线程
查看>>
从源码剖析useState的执行过程
查看>>
地包天如何矫正?
查看>>