String的Intern方法详解

摘要:整理String的Intern方法

注:原博文地址:https://www.cnblogs.com/wxgblogs/p/5635099.html

前言

  在 JAVA 语言中有8中基本类型和一种比较特殊的类型String。这些类型为了使他们在运行过程中速度更快,更节省内存,都提供了一种常量池的概念。常量池就类似一个JAVA系统级别提供的缓存。8种基本类型的常量池都是系统协调的,String类型的常量池比较特殊。它的主要使用方法有两种:

  • 直接使用双引号声明出来的String对象会直接存储在常量池中。
  • 如果不是用双引号声明的String对象,可以使用String提供的intern方法。intern 方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中

一. intern 的实现原理

java源码

/**
     * Returns a canonical representation for the string object.
     * <p>
     * A pool of strings, initially empty, is maintained privately by the
     * class <code>String</code>.
     * <p>
     * When the intern method is invoked, if the pool already contains a
     * string equal to this <code>String</code> object as determined by
     * the {@link #equals(Object)} method, then the string from the pool is
     * returned. Otherwise, this <code>String</code> object is added to the
     * pool and a reference to this <code>String</code> object is returned.
     * <p>
     * It follows that for any two strings <code>s</code> and <code>t</code>,
     * <code>s.intern()&nbsp;==&nbsp;t.intern()</code> is <code>true</code>
     * if and only if <code>s.equals(t)</code> is <code>true</code>.
     * <p>
     * All literal strings and string-valued constant expressions are
     * interned. String literals are defined in &sect;3.10.5 of the
     * <a href="http://java.sun.com/docs/books/jls/html/">Java Language
     * Specification</a>
     *
     * @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();

  String的intern方法中看到,这个方法是一个 native 的方法,但注释写的非常明了。“如果常量池中存在当前字符串, 就会直接返回当前字符串. 如果常量池中没有此字符串, 会将此字符串放入常量池中后, 再返回”。

2,native 代码

  在 jdk7后,oracle 接管了 JAVA 的源码后就不对外开放了,根据 jdk 的主要开发人员声明 openJdk7 和 jdk7 使用的是同一分主代码,只是分支代码会有些许的变动。所以可以直接跟踪 openJdk7 的源码来探究 intern 的实现。

native实现代码:

\openjdk7\jdk\src\share\native\java\lang\String.c

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

\openjdk7\hotspot\src\share\vm\prims\jvm.h

/*
* java.lang.String
*/
JNIEXPORT jstring JNICALL
JVM_InternString(JNIEnv *env, jstring str);

\openjdk7\hotspot\src\share\vm\prims\jvm.cpp

// String support ///
JVM_ENTRY(jstring, JVM_InternString(JNIEnv *env, jstring str))
  JVMWrapper("JVM_InternString");
  JvmtiVMObjectAllocEventCollector oam;
  if (str == NULL) return NULL;
  oop string = JNIHandles::resolve_non_null(str);
  oop result = StringTable::intern(string, CHECK_NULL);
  return (jstring) JNIHandles::make_local(env, result);
JVM_END

\openjdk7\hotspot\src\share\vm\classfile\symbolTable.cpp

oop StringTable::intern(Handle string_or_null, jchar* name,
                        int len, TRAPS) {
  unsigned int hashValue = java_lang_String::hash_string(name, len);
  int index = the_table()->hash_to_index(hashValue);
  oop string = the_table()->lookup(index, name, len, hashValue);
  // Found
  if (string != NULL) return string;
  // Otherwise, add to symbol to table
  return the_table()->basic_add(index, string_or_null, name, len,
                                hashValue, CHECK_NULL);
}

\openjdk7\hotspot\src\share\vm\classfile\symbolTable.cpp

oop StringTable::lookup(int index, jchar* name,
                        int len, unsigned int hash) {
  for (HashtableEntry<oop>* l = bucket(index); l != NULL; l = l->next()) {
    if (l->hash() == hash) {
      if (java_lang_String::equals(l->literal(), name, len)) {
        return l->literal();
      }
    }
  }
  return NULL;
}

  它的大体实现结构就是:JAVA 使用 jni 调用c++实现的StringTableintern方法, StringTableintern方法跟Java中的HashMap的实现是差不多的, 只是不能自动扩容。默认大小是1009。要注意的是,String的String Pool是一个固定大小的Hashtable,默认值大小长度是1009,如果放进String Pool的String非常多,就会造成Hash冲突严重,从而导致链表会很长,而链表长了后直接会造成的影响就是当调用String.intern时性能会大幅下降。在 jdk6中StringTable是固定的,就是1009的长度,所以如果常量池中的字符串过多就会导致效率下降很快。在jdk7中,StringTable的长度可以通过一个参数指定:

  • -XX:StringTableSize=99991

二.jdk6 和 jdk7 下 intern 的区别

  JAVA 程序员都做做类似 String s = new String("abc")这个语句创建了几个对象的题目。 这种题目主要就是为了考察程序员对字符串对象的常量池掌握与否。上述的语句中是创建了2个对象,第一个对象是”abc”字符串存储在常量池中,第二个对象在JAVA Heap中的 String 对象。

public static void main(String[] args) {
    String s = new String("1");
    s.intern();
    String s2 = "1";
    System.out.println(s == s2);
 
    String s3 = new String("1") + new String("1");
    s3.intern();
    String s4 = "11";
    System.out.println(s3 == s4);
}

  打印结果是

  • jdk6 下false false
  • jdk7 下false true

  具体为什么稍后再解释,然后将s3.intern();语句下调一行,放到String s4 = "11";后面。将s.intern(); 放到String s2 = "1";后面。是什么结果呢

public static void main(String[] args) {
    String s = new String("1");
    String s2 = "1";
    s.intern();
    System.out.println(s == s2);
 
    String s3 = new String("1") + new String("1");
    String s4 = "11";
    s3.intern();
    System.out.println(s3 == s4);
}

  打印结果为:

  • jdk6 下false false
  • jdk7 下false false
1.jdk6中的解释

  注:图中绿色线条代表 string 对象的内容指向。 黑色线条代表地址指向。

  如上图所示。首先说一下 jdk6中的情况,在 jdk6中上述的所有打印都是 false 的,因为 jdk6中的常量池是放在 Perm 区中的,Perm区和正常的 JAVA Heap 区域是完全分开的。上面说过如果是使用引号声明的字符串都是会直接在字符串常量池中生成,而 new 出来的 String 对象是放在 JAVA Heap 区域。所以拿一个 JAVA Heap 区域的对象地址和字符串常量池的对象地址进行比较肯定是不相同的,即使调用String.intern方法也是没有任何关系的。

 

2.jdk7中的解释

  在 Jdk6 以及以前的版本中,字符串的常量池是放在堆的Perm区的,Perm区是一个类静态的区域,主要存储一些加载类的信息,常量池,方法片段等内容,默认大小只有4m,一旦常量池中大量使用 intern 是会直接产生java.lang.OutOfMemoryError:PermGen space错误的。在 jdk7 的版本中,字符串常量池已经从Perm区移到正常的Java Heap区域了。为什么要移动,Perm 区域太小是一个主要原因,当然据消息称jdk8已经直接取消了Perm区域,而新建立了一个元区域。应该是jdk开发者认为Perm区域已经不适合现在 JAVA 的发展了。正式因为字符串常量池移动到JAVA Heap区域后,再来解释为什么会有上述的打印结果。

  • 在第一段代码中,先看 s3和s4字符串。String s3 = new String("1") + new String("1");,这句代码中现在生成了2最终个对象,是字符串常量池中的“1” 和 JAVA Heap中的 s3引用指向的对象。中间还有2个匿名的new String("1")我们不去讨论它们。此时s3引用对象内容是”11″,但此时常量池中是没有 “11”对象的。
  • 接下来s3.intern();这一句代码,是将 s3中的"11"字符串放入String 常量池中,因为此时常量池中不存在"11"字符串,因此常规做法是跟 jdk6 图中表示的那样,在常量池中生成一个"11"的对象,关键点是 jdk7 中常量池不在Perm区域了,这块做了调整。常量池中不需要再存储一份对象了,可以直接存储堆中的引用。这份引用指向s3引用的对象。 也就是说引用地址是相同的。
  • 最后String s4 = "11"; 这句代码中”11″是显示声明的,因此会直接去常量池中创建,创建的时候发现已经有这个对象了,此时也就是指向s3引用对象的一个引用。所以s4引用就指向和s3一样了。因此最后的比较 s3 == s4 是 true。
  • 再看s和 s2 对象。String s = new String("1"); 第一句代码,生成了2个对象。常量池中的“1” 和 JAVA Heap 中的字符串对象。s.intern(); 这一句是 s 对象去常量池中寻找后发现 “1” 已经在常量池里了。
  • 接下来String s2 = "1"; 这句代码是生成一个 s2的引用指向常量池中的“1”对象。 结果就是 s 和 s2 的引用地址明显不同。图中画的很清晰

 

  • 来看第二段代码,从上边第二幅图中观察。第一段代码和第二段代码的改变就是 s3.intern(); 的顺序是放在String s4 = "11";后了。这样,首先执行String s4 = "11";声明 s4 的时候常量池中是不存在“11”对象的,执行完毕后,“11“对象是 s4 声明产生的新对象。然后再执行s3.intern();时,常量池中“11”对象已经存在了,因此 s3 和 s4 的引用是不同的。
  • 第二段代码中的 s 和 s2 代码中,s.intern();,这一句往后放也不会有什么影响了,因为对象池中在执行第一句代码String s = new String("1");的时候已经生成“1”对象了。下边的s2声明都是直接从常量池中取地址引用的。 s 和 s2 的引用地址是不会相等的。
小结

  从上述的例子代码可以看出 jdk7 版本对 intern 操作和常量池都做了一定的修改。主要包括2点:

  • 将String常量池从Perm区移动到了Java Heap区
  • String#intern 方法时,如果存在堆中的对象,会直接保存对象的引用,而不会重新创建对象

三.使用 intern

1.intern 正确使用例子

  接下来我们来看一下一个比较常见的使用String#intern方法的例子。

static final int MAX = 1000 * 10000;
static final String[] arr = new String[MAX];
 
public static void main(String[] args) throws Exception {
    Integer[] DB_DATA = new Integer[10];
    Random random = new Random(10 * 10000);
    for (int i = 0; i < DB_DATA.length; i++) {
        DB_DATA[i] = random.nextInt();
    }
    long t = System.currentTimeMillis();
    for (int i = 0; i < MAX; i++) {
        //arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length]));
         arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length])).intern();
    }
 
    System.out.println((System.currentTimeMillis() - t) + "ms");
    System.gc();
}

  运行的参数是:-Xmx2g -Xms2g -Xmn1500M 上述代码是一个演示代码,其中有两条语句不一样,一条是使用 intern,一条是未使用 intern。结果如下图

2160ms

826ms

 

  通过上述结果,我们发现不使用 intern 的代码生成了1000w 个字符串,占用了大约640m 空间。 使用了 intern 的代码生成了1345个字符串,占用总空间 133k 左右。其实通过观察程序中只是用到了10个字符串,所以准确计算后应该是正好相差100w 倍。虽然例子有些极端,但确实能准确反应出 intern 使用后产生的巨大空间节省。

  细心的同学会发现使用了 intern 方法后时间上有了一些增长。这是因为程序中每次都是用了 new String 后, 然后又进行 intern 操作的耗时时间,这一点如果在内存空间充足的情况下确实是无法避免的,但我们平时使用时,内存空间肯定不是无限大的,不使用 intern 占用空间导致 jvm 垃圾回收的时间是要远远大于这点时间的。 毕竟这里使用了1000w次intern 才多出来1秒钟多的时间。

下面是我测试的数据:

未使用intern,3463ms

使用intern,1772ms

 

2,intern 使用不当

  看过了 intern 的使用和 intern 的原理等,我们来看一个使用 intern 操作不当导致的问题。

  在使用 fastjson 进行接口读取的时候,我们发现在读取了近70w条数据后,我们的日志打印变的非常缓慢,每打印一次日志用时30ms左右,如果在一个请求中打印2到3条日 志以上会发现请求有一倍以上的耗时。在重新启动 jvm 后问题消失。继续读取接口后,问题又重现。接下来我们看一下出现问题的过程。

1,根据 log4j 打印日志查找问题原因

在使用log4j#info打印日志的时候时间非常长。所以使用 housemd 软件跟踪 info 方法的耗时堆栈。

  • trace SLF4JLogger.
  • trace AbstractLoggerWrapper:
  • trace AsyncLogger
org/apache/logging/log4j/core/async/AsyncLogger.actualAsyncLog(RingBufferLogEvent)                
sun.misc.Launcher$AppClassLoader@109aca82            1            1ms    
org.apache.logging.log4j.core.async.AsyncLogger@19de86bb 
org/apache/logging/log4j/core/async/AsyncLogger.location(String)                                  
sun.misc.Launcher$AppClassLoader@109aca82            1           30ms    
org.apache.logging.log4j.core.async.AsyncLogger@19de86bb 
org/apache/logging/log4j/core/async/AsyncLogger.log(Marker, String, Level, Message, Throwable)    
sun.misc.Launcher$AppClassLoader@109aca82            1           61ms    
org.apache.logging.log4j.core.async.AsyncLogger@19de86bb

  代码出在 AsyncLogger.location 这个方法上. 里边主要是调用了 return Log4jLogEvent.calcLocation(fqcnOfLogger);Log4jLogEvent.calcLocation()

  Log4jLogEvent.calcLocation()的代码如下:

public static StackTraceElement calcLocation(final String fqcnOfLogger) {  
    if (fqcnOfLogger == null) {  
        return null;  
    }  
    final StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();  
    boolean next = false;  
    for (final StackTraceElement element : stackTrace) {  
        final String className = element.getClassName();  
        if (next) {  
            if (fqcnOfLogger.equals(className)) {  
                continue;  
            }  
            return element;  
        }  
        if (fqcnOfLogger.equals(className)) {  
            next = true;  
        } else if (NOT_AVAIL.equals(className)) {  
            break;  
        }  
    }  
    return null;  
}

  经过跟踪发现是 Thread.currentThread().getStackTrace(); 的问题。

2, 跟踪Thread.currentThread().getStackTrace()的 native 代码,验证String#intern

  Thread.currentThread().getStackTrace();native的方法:

public StackTraceElement[] getStackTrace() {  
    if (this != Thread.currentThread()) {  
        // check for getStackTrace permission  
        SecurityManager security = System.getSecurityManager();  
        if (security != null) {  
            security.checkPermission(  
                SecurityConstants.GET_STACK_TRACE_PERMISSION);  
        }  
        // optimization so we do not call into the vm for threads that  
        // have not yet started or have terminated  
        if (!isAlive()) {  
            return EMPTY_STACK_TRACE;  
        }        StackTraceElement[][] stackTraceArray = dumpThreads(new Thread[] {this});  
        StackTraceElement[] stackTrace = stackTraceArray[0];  
        // a thread that was alive during the previous isAlive call may have  
        // since terminated, therefore not having a stacktrace.  
        if (stackTrace == null) {  
            stackTrace = EMPTY_STACK_TRACE;  
        }  
        return stackTrace;  
    } else {  
        // Don't need JVM help for current thread  
        return (new Exception()).getStackTrace();  
    }  
}
<code>private native static StackTraceElement[][] dumpThreads(Thread[] threads);</code>

下载 openJdk7的源码查询 jdk 的 native 实现代码,列表如下【这里因为篇幅问题,不详细罗列涉及到的代码,有兴趣的可以根据文件名称和行号查找相关代码】:

\openjdk7\jdk\src\share\native\java\lang\Thread.c
\openjdk7\hotspot\src\share\vm\prims\jvm.h line:294:
\openjdk7\hotspot\src\share\vm\prims\jvm.cpp line:4382-4414:
\openjdk7\hotspot\src\share\vm\services\threadService.cpp line:235-267:
\openjdk7\hotspot\src\share\vm\services\threadService.cpp line:566-577:
\openjdk7\hotspot\src\share\vm\classfile\javaClasses.cpp line:1635-[1651,1654,1658]:

完成跟踪了底层的 jvm 源码后发现,是下边的三条代码引发了整个程序的变慢问题。

oop classname = StringTable::intern((char*) str, CHECK_0);  
oop methodname = StringTable::intern(method->name(), CHECK_0);  
oop filename = StringTable::intern(source, CHECK_0);

  这三段代码是获取类名、方法名、和文件名。因为类名、方法名、文件名都是存储在字符串常量池中的,所以每次获取它们都是通过String#intern方法。但没有考虑到的是默认的 StringPool 的长度是1009且不可变的。因此一旦常量池中的字符串达到的一定的规模后,性能会急剧下降。

3,fastjson 不当使用 String#intern

导致这个 intern 变慢的原因是因为 fastjson 对String#intern方法的使用不当造成的。跟踪 fastjson 中的实现代码发现,

com.alibaba.fastjson.parser.JSONScanner#scanFieldSymbol():
if (ch == '\"') {
    bp = index;
    this.ch = ch = buf[bp];
    strVal = symbolTable.addSymbol(buf, start, index - start - 1, hash);
    break;
}
com.alibaba.fastjson.parser.SymbolTable#addSymbol():
/**
 * Constructs a new entry from the specified symbol information and next entry reference.
 */
public Entry(char[] ch, int offset, int length, int hash, Entry next){
    characters = new char[length];
    System.arraycopy(ch, offset, characters, 0, length);
    symbol = new String(characters).intern();
    this.next = next;
    this.hashCode = hash;
    this.bytes = null;
}

fastjson 中对所有的 json 的 key 使用了 intern 方法,缓存到了字符串常量池中,这样每次读取的时候就会非常快,大大减少时间和空间。而且 json 的 key 通常都是不变的。这个地方没有考虑到大量的 json key 如果是变化的,那就会给字符串常量池带来很大的负担。

这个问题 fastjson 在1.1.24版本中已经将这个漏洞修复了。程序加入了一个最大的缓存大小,超过这个大小后就不会再往字符串常量池中放了。

[1.1.24版本的com.alibaba.fastjson.parser.SymbolTable#addSymbol() Line:113]代码

public static final int MAX_SIZE           = 1024;
 
if (size >= MAX_SIZE) {
    return new String(buffer, offset, len);
}

这个问题是70w 数据量时候的引发的,如果是几百万的数据量的话可能就不只是30ms 的问题了。因此在使用系统级提供的String#intern方式一定要慎重!

五.总结

  本文大体的描述了 String#intern和字符串常量池的日常使用,jdk 版本的变化和String#intern方法的区别,以及不恰当使用导致的危险等内容,让大家对系统级别的 String#intern有一个比较深入的认识。让我们在使用和接触它的时候能避免出现一些 bug,增强系统的健壮性。

 

转载于:https://www.cnblogs.com/cainiaomahua/p/10064839.html

dashabi201412
关注 关注
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
String类中的intern()方法
菩提小猿的博客
06-06 4102
1.前言         其实,促使我写这篇博客的原因说起来就挺可笑的。疫情在家,无所事事,就和室友对于专业方面的知识互相吹牛皮。然后,室友出了好几道题考我。也许结果早就在他的意料之中吧。其中,最让我不解的是这道题: String s1 = new String("a") + new String("bc"); s1.intern(); String s2 = "abc"; System.out.println(s1 == s2
详解Stringintern方法
m0_59181747的博客
11-04 1995
参考链接:黑马程序员JVM完整教程,全网超高评价,全程干货不拖沓_哔哩哔哩_bilibili 知识补充 常量池 1、常量池,就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息 运行时常量池,常量池是 *.class 文件中的,当该类被加载,它的常量池信息就会放入运行时常量 池,并把里面的符号地址变为真实地址 2、程序执行时,常量池中的信息(符号)都会被加载到运行时常量池中,但是加载完之后,这些符号还没有成为java对象,只有执行到具体某一行代码的时候,才会转变为ja
JAVA基础:正则表达式,Stringintern方法StringBuilder可变字符串特点与应用,+连接字符串特点
最新发布
weixin_53755148的博客
09-18 1193
将字符串按照指定的内容进行分割,将分割成的每一个子部分组成一个数组分割内容不会出现在数组中实际上该方法不是按照指定的简单的符号进行分割的,而是按照正则表达式进行分割。
StringIntern方法
m0_37210184的博客
04-28 171
引言 在 JAVA 语言中有8中基本类型和一种比较特殊的类型String。这些类型为了使他们在运行过程中速度更快,更节省内存,都提供了一种常量池的概念。常量池就类似一个JAVA系统级别提供的缓存。8种基本类型的常量池都是系统协调的,String类型的常量池比较特殊。它的主要使用方法有两种:· 直接使用双引号声明出来的String对象会直接存储在常量池中。· 如果不是用双引号声明的String对象,...
几张图轻松理解String.intern()
热门推荐
程序老兵的博客
04-12 5万+
在翻《深入理解Java虚拟机》的书时,又看到了2-7的 String.intern()返回引用的测试。 其实要搞明白String.intern(),我总结了下面几条规则: 一、new String都是在堆上创建字符串对象。当调用 intern() 方法时,编译器会将字符串添加到常量池中(stringTable维护),并返回指向该常量的引用。 二、通过字面量赋值创建字符串(如:String st
详解String intern方法
做最亮的星星
04-15 813
一,弄清楚String对象的特点: 不可变的(final); 二,弄清楚+号和StringBuilder: +号操作分n种情况,但只有一种情况等同于直接字符串,那就是常量值相加,像这样: String str = "abc" + 123; 等同于String str = "abc123"; String str = "abc" + "def"; 等同于String str = "abc...
C#中字符串优化String.Intern、IsInterned详解
08-28
C#中字符串优化String.Intern、IsInterned详解 C#中字符串优化是指在编程过程中对字符串进行优化,以减少内存占用和提高程序运行效率。在C#中,字符串是一种特殊的数据类型,它既是基元类型又是引用类型。在编译和...
关于java Stringintern的深入讲解
08-26
Java String 中的 intern 方法是一个非常重要的概念,它可以将字符串对象存储在字符串常量池中,以便重复使用相同的字符串对象,减少内存的占用。下面我们将深入讲解 Java Stringintern 的相关知识点。 什么是 ...
JVM字符串常量池及Stringintern方法详解
m0_60747279的博客
08-09 474
intern方法的作用 intern()方法的功能定义: (1)如果当前字符串内容存在于字符串常量池(即equals()方法为true,也就是内容一样),那直接返回此字符串在常量池的引用; (2)如果当前字符串不在字符串常量池中,那么在常量池创建一个引用并指向堆中已存在的字符串,然后返回常量池中的引用。 简单说intern方法就是判断并将字符串是否存在于字符串常量池,如果不存在则创建,存在则返回。 字符串常量池 在HotSpot中实现字符串常量池功能的是一个StringTable类,它是一个Hash表,默认
Stringintern()方法详解
qq_48157004的博客
10-16 994
在开发过程中很多朋友,由于不会正确使用intern(),导致开发的程序,执行效率比较差。同时最近发现一道非常有意思的关于intern()的面试题,这道面试题还是有不小的难度,相信很多朋友看到以后也不知道怎么解答,所以今天咱们深入详解intern()。当调用intern()时,如果池子里已经包含了一个与这个String对象相等的字符串,正如equals(Object)方法所确定的,那么池子里的字符串会被返回。否则,这个String对象被添加到池中,并返回这个String对象的引用。
详解String类中的intern()方法
mosterRan的博客
01-25 217
我们用一个经典的例子来理解 package com.jvm.heap; public class MyTest { public static void main(String[] args) { String str1 = new StringBuilder("计算机").append("软件").toString(); System.out.pri...
String.intern() 方法
Hannah097的博客
04-03 331
字符串常量池: jdk6中字符串常量池在永久代,从jdk7开始,在堆中又划分了一块区域,放到了堆空间中。 intern() 方法: 是扩充常量池的一个方法,当一个String的实例调用intern()方法时,Java会首先去查找常量池中是否有相同Unicode的字符串常量,如果有,则返回其引用;如果没有,则在常量池中增加一个Unicode等于该实例的字符串并返回它的引用。 举例: String s6 = new String("abc"); String s8 = s6.intern(); St
Stringintern方法
weixin_38568503的博客
03-03 1020
intern方法作用:简单总结就是:有,直接引用,没有,新建加入。 在我们使用System.out.中的System类的时候,就默认加载了Version类 考点:是否读过这些经典书
String类的intern方法
99Savage
02-03 316
String类的intern方法
String中的intern()方法
weixin_45607513的博客
08-19 617
案例 此案例是《深入理解java虚拟机》中的一个案例。 package javase; public class StringPool58Demo { public static void main(String[] args) { String str1 = new StringBuilder("ha").append("llo").toString(); System.out.println(str1); System.out.println(
String.intern()
迷失在代码的海洋里
04-13 3283
对于String.intern()清楚以下几点便能完全理解其作用: 1,new String都是在堆上创建字符串对象。当调用 intern() 方法时,编译器会将字符串添加到常量池中(stringTable维护),并返回指向该常量的引用(内存地址)。 调用str1.intern(),编译器会将"abc"字符串添加到常量池中,并返回指向该常量的引用。此后再调用str2.intern(),因常量池中已有"abc"存在,因此就直接返回常量池中的"abc"的引用。 2,通过字面量赋值创建字符串(如:S
测试String类的intern方法
单行线的专栏
07-19 560
测试String类的intern方法
说说String类的intern方法
yanghan1222的博客
05-01 2035
声明:此篇内容来源于书籍&lt;深入理解虚拟机&gt;说到String类,大家都不陌生吧。但是对于String类的intern方法呢。先来看一下下面这段代码public class day01_6 { public static void main(String[] args) { String str1 = new StringBuilder("计算机").append(...
string intern
08-16
- *2* *3* [详解Stringintern方法](https://blog.csdn.net/m0_59181747/article/details/121146280)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common...
写文章

热门文章

  • Maven之pom.xml配置文件详解 433
  • java连接Oracle数据库 277
  • Java输入输出流详解 269
  • 学习Acegi应用到实际项目中(8)- 扩展UserDetailsService接口 253
  • String的Intern方法详解 198

最新文章

  • Maven之pom.xml配置文件详解
  • Mybatis MapperScannerConfigurer 自动扫描 将Mapper接口生成代理注入到Spring
  • 学习Acegi应用到实际项目中(12)- Run-As认证服务
2019年1篇
2018年15篇
2017年3篇
2016年3篇

目录

目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值

玻璃钢生产厂家福建商业商场美陈制造南宁欧式玻璃钢雕塑河北户外商场美陈北京多彩玻璃钢雕塑制作玻璃钢雕塑厂家批量河南中庭商场美陈订购珠海玻璃钢公仔雕塑图片枣庄人物玻璃钢雕塑施工安装商场橱窗秋季美陈玻璃钢雕塑口碑龙象玻璃钢雕塑造型贴金人物校园玻璃钢景观雕塑厂家赣州人物玻璃钢雕塑多少钱宣威市玻璃钢雕塑哪里有卖保定直销玻璃钢雕塑源头好货玻璃钢雕塑 广州佛山玻璃钢工艺雕塑厂广州景观玻璃钢雕塑价位山西玻璃钢广场雕塑气球商场美陈装饰哪家值得信赖八路军玻璃钢雕塑珠海精美玻璃钢座椅雕塑宣城景观玻璃钢雕塑定做广东玻璃钢雕塑摆件市场商场中庭吊挂美陈湖北玻璃钢雕塑心形花盆湖南玻璃钢动物雕塑价格实用的玻璃钢花盆普洱玻璃钢雕塑设计定做玻璃钢铸铜雕塑价格香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化