字符串和字符串常量池

字符串和字符串常量池

字符串常量池的设计思想?

  1. 字符串的分配和其他对象的分配一样,耗费高昂的时间和空间的代价,作为最基础的数据类型,大量频繁的创建字符串,极大程度的影响程序的性能。
  2. JVM为了提升性能和减小开销,在实例化字符串常量的时候做了一些优化,为字符串开辟一个字符串常量池,类似与缓存区。
    • 创建字符串常量时,首相查询字符串常量池是否存在该字符串
      • 存在:返回引用实例
      • 不存在:实例化该字符串放入池中

什么是字符串常量池?

​ Java内部为了避免每次都创建相同的字符串对象和内存分配,在内部做了优化,将字符串字面量存储到一个缓存区,这个缓存区被称为字符串常量池

字符串常量池在哪?

请添加图片描述

​ 对于JDK1.7及其以上来说,字符串常量池在堆中。

​ 对于JDK1.6来说,字符串常量池在方法区。

经典问题:

String s1 = "aaa";
String s2 = "aaa";
System.out.println(s1 == s2);//true
//因为"aaa"为字面量,在编译时期就会直接在字符串常量池生成,而 s1和s2是引用的 "aaa"在字符串常量池中的引用,所以s1=s2
String s = new String("aaa");//创建了几个对象
//两个,第一个对象"aaa"会存储在字符串常量池中,因为有new 操作,所以还会在堆中创建一个对象,此时s指向的是堆中的字符串对象
String s1 = new String("aaa");
String s2 = new String("aaa");//一共创建了几个对象
System.out.println(s1 == s2);//打印true还是false
//第一个问题,一共创建了几个对象?
//3个,第一个对象因为"aaa"是字面量在编译时期就在字符串常量池中创建出来,在执行new String("aaa")时,首先回去字符串常量池equals比较是否存在"aaa"字符串,此时存在,则不再在字符串常量池中创建对象,但是有new 操作,所以又会在heap堆中创建一个对象。因为new了两次,所以在heap中创建了两个对象,加上字符串常量池中的对象一个是3个对象
//第二个问题,s1==s2是true还是false
//false,因为s1和s2分别指向的是heap中各自对象的地址值,一定是不相等的,所以为false
String s1 = "he";
String s2 = s1+new String("llo");//创建了几个对象
//4个
//在编译时期,字面量"he"和"llo"会先进字符串常量池,进行了一次new String("llo") 操作,此时会在heap中创建一个对象,运行到 "he"+new String("llo")时,还会进行拼接操作,此时还会在heap堆中再创建一个对象,所以一个会创建4对象
//下面代码创建了几个对象?
String s1 = new String("he") + new String("llo");
String s2 = s1.intern();  

	//对于jdk1.7来说,创建了5个对象,“he” "llo"字面量会先去常量池中查看是否存在,不存在,则会在常量池中创建这两个对象,然后他们都进行了new String()操作,所以还会在堆中非常量池区域再各自创建一个对象,最后 把new 出来的对象拼接起来,此时还会在堆中非常量区创建一个对象,所以一共是5个对象。
	//对于jdk1.6来说,s1和jdk1.7一样,会创建5个对象
	//s1.intern();对于jdk1.7来说,会先去字符串常量池进行eq,如果不存在相同的字符串,则会直接指向堆中s1的实例,也就是说s1.intern()返回的是s1堆中对象的指针,此时s2和s1指向的都是同一个对象,所以s1==s2 为true
	//对于jdk1.6来说,intern()操作会先去常量池进行eq,发现不存在,会在常量池创建一个“hello”对象,然后把这个“hello”返回给s2,所以对于jdk1.6来说,s1==s2 为 false
//这行代码会创建对象吗
String s1 = "java";
//不一定,因为"java"字符串大概率会在jdk源码中被先创建出来,此时s1直接会指向字符串常量池中的"java"的地址值

关于字符串使用 “+”号拼接

String s1 = "he"+"llo";
//在编译时期,对于确定的常量类型,编译器进行优化,直接编译成String s1 = "hello",所以在运行时不会再发生拼接操作,直接会在字符串常量池创建一个对象
String s1 = "he";
String s2 = s1+"llo";
//对于编译器老说,在执行s2 = s1+"llo"时,编译器实际上会创建一个StringBuilder对象进行append进行拼接,然后再进行toString()方法返回,通过查看toString()方法的源码发现,实际上是进行了一次new String()操作,所以还会在deap堆中创建一个拼接后的对象,所以一共会创建3个对象

new StringBuilder().toString()源码

关于String对象是不可变对象

​ 关于String对象是不可变对象可以通过String的拼接方法看出,因为所有String对象的拼接都是在内部使用了StringBuilder对象进行拼接,而StringBuilder对象内部源码最终是进行了一个new String()操作

java中类字符串常量技术的应用

Integer s1 = -128;
Integer s2 = -128;
System.out.println(s1 == s2);//true
Integer s3 = -129;
Integer s4 = -129;
System.out.println(s3 == s4);//false

我们都知道Integer -128 到127之间会用到常量,可以直接进行等等于比较,但是为什么呢?
实际上在执行时,Integer s1 = -128;会编译成 Integer s1 = Integer.valueOf(-128) ,通过查看源码发现,Integer在内部初始化的时候会先初始化一个对象池,这个对象池的范围是 -128 到127,而进行valueOf()操作时,会先去对象池找,如果找到,就直接返回,没找到就new 一个,所以,如果在-128到127之间的值之间直接==比较,他们是同一个对象进行比较,会返回true,而超出范围的进行比较,因为是不同的对象进行比较,所以为false.

Integer通过静态内部类的方式进行初始化

请添加图片描述

valueOf的源码
请添加图片描述
注:Byte、Short、Integer、Long、Character 这几种对象都使用了类似的对象池技术

猜你喜欢

转载自blog.csdn.net/XX777666/article/details/120560147