Java中的String些许理解

对于程序员来讲,字符串是最常见的数据类型,我们每天都在和它打交道,今天简单聊下对String的理解。

对于Java来讲,我们编写的代码都会交由JVM执行,所以在讲String之前,先简单介绍下一段Java代码是如何运行的。如图所示,从Class装载到加载到内存中运行过程。


image.png

对于我们来讲,运行时内存空间一般涉及到的就是方法区,堆,栈和常量池。
关于常量池也简单说明下,常量池主要分为:Class文件常量池、运行时常量池,全局字符串常量池。

Class文件常量池是在编译期间生成,由static和final修饰的会存放在Class文件中(典型的就是String,查看String源代码可发现,String由final修饰),这些统称为字面量。另外还包括符号引用,包括类和接口,以及方法的描述。

运行时常量池存放在方法区,它会加载Class文件常量池中的内容,同时该常量池可以动态修改,例如可以使用String的String.intern()方法动态添加到常量池中。

全局字符串常量池,JDK1.7以前该常量池存放在方法区,以后的版本存放在堆中(因为方法区的大小固定,不太适合存放太多内容,而堆可以根据根据物理内存做调整,适合存放大量数据)。

下面来讲下String的几种创建方式

  • String str1= "abc";
  • String str2= new String("abc");
  • String str3= new String("abc").intern();

第一种,首先检查要创建的字符串是否在常量池中存在,如果存在直接引用常量池中的String对象,如果不存在,在常量池中创建新的String对象,然后引用新创建的对象。

说到这里,简单勘误下初学者常见的理解偏差,关于对象和对象引用。对于Java来讲,除了基本数据类型外,形如String str = "abc",str变量即为内存中的对象引用,"abc"为内存中的对象,简单分解上述过程,String str;会在内存(栈内存)中创建一个String类型的对象引用,"abc"代表内存(堆内存)中的对象,对象生成后通过=符号,把该对象的内存地址赋值给str,str在内存中保存的仅仅是对象存放的地址,而不是对象本身,下图描述了内存中的对象和对象引用的存储情况。


image.png

可以看到对象引用是指向堆内存中的对象的,对象对于我们是不可见的,我们操作的都是对象引用。

另简单介绍下堆栈

  • 栈为连续空间,大小和操作系统设置有关,速度快,用来存放临时变量,对象引用,函数调用使用栈,数据结构类似数组,栈为线程私有,外部不能访问
  • 堆为非连续空间,大小可以根据物理内存调整,速度较慢,用来存放对象,空间不连续,数据结构类似链表,堆为线程共享

第二种,会检查其中的字符串本身(abc)是否在常量池中,如果在常量池中即引用常量池中的abc对象在堆内存中生成String对象,str引用的是堆中创建的对象,而不是常量池中的String对象。

第三种,intern()方法会检查字符串是否在常量池中,在的话引用常量池中String对象,不在的话在常量池中创建新的String对象并引用。

对于第一种和第三种intern可以动态的缓存String对象,可以减少存储大量重复的String对象所需的内存空间。但需要注意的是常量池采用hashtable实现,如果对象太多,会增加遍历时间。

关于intern(),引用一张图片说明实际过程

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);
}
image.png

引用自美团技术团队博客,引用地址 https://tech.meituan.com/2014/03/06/in-depth-understanding-string-intern.html

你可能感兴趣的:(Java中的String些许理解)