String

String声明为final,不可被继承
String实现了Serializable接口:表示字符串支持序列化
String实现了Comparable接口:表示String可以比较大小
String在jdk8及以前内部定义private final char[] value 用于存储字符串数据,jdk9改为private final byte[] value

String s1 = "s1";       //字面量定义方式
String se = new String("s2");

  

String的不可变性
当对字符串重新赋值时,需要重新指定内存区域赋值
当对字符串进行连接操作时,需要重新指定内存区域赋值
当调用String的replace()方法修改指定字符或字符串时,需要重新指定内存区域赋值

 

字符串常量池中是不会存储相同内容的字符串的

 

jdk6中StringTable是固定的,即1009
jdk7中StringTable默认长度为60013
jdk8中StringTable可设置的最小值是1009

 

String的内存分配

jdk6及之前,字符串常量池存放在永久代
jdk7及之后,字符串常量池存放在Java堆
调整原因:
永久代默认比较小
永久代垃圾回收频率低

 

 

class Memory{
	public static void main(String[] args) {
		int i=1;
		Object obj=new Object();
		Memory mem=new Memory();
		mem.foo(obj);
	}
	private void foo(Object param) {
		String str=param.toString();
	}
}

  

 

 

 

字符串拼接操作

常量与常量的拼接结果在常量池,原理是编译期优化
常量与变量、变量与变量的结果在堆中(不在常量池),原理是StringBuilder

 

String s1 = "a"+"b";
String s2 = "ab";
System.out.println(s1== s2);     //true
System.out.println(s1.equals(s2));    //true

编译期优化,编译成 .class 文件后为

String s1 = "ab";
String s2 = "ab";
System.out.println(s1== s2);     //true
System.out.println(s1.equals(s2));    //true

  

s1+s2 的执行细节

  • StringBuilder s=new StringBuilder();
  • s.append("Hello");
  • s.append("World");
  • s.toString();    //约等于 new String("HelloWorld");
String s1 = "Hello";
String s2 = "World";

String s3 = "HelloWorld";
String s4 = "Hello"+"World";
String s5 = s1+"World";
String s6 = "Hello"+s2;
String s7 = s1+s2;

System.out.println(s3 ==s4);	//true
System.out.println(s3 ==s5);	//false
System.out.println(s3 ==s6);	//false
System.out.println(s3 ==s7);	//false
System.out.println(s5 ==s6);	//false
System.out.println(s5 ==s7);	//false
System.out.println(s6 ==s7);	//false

String s8 = s6.intern();
System.out.println(s3 ==s8);	//true

  

 

final String s1="a";
final String s2="b";
String s3="ab";
String s4=s1+s2;
System.out.println(s3 == s4);     //true

编译期优化,编译成 .class 文件后为

String s1="a";
String s2="b";
String s3="ab";
String s4="ab";
System.out.println(s3 == s4);

  

拼接操作和append操作的效率对比

public void method1() {       //执行时间4014ms
	String src = "";
	for(int i=0; i<100000; i++) {
		src = src + "a";       //每次循环都会创建一个StringBuilder、String
	}
}

public void method2() {      //执行时间7ms
	StringBuilder src = new StringBuilder();
	for(int i=0; i<100000; i++) {
		src.append("a");        //只需创建一个StringBuilder
	}
}

  

 

intern()方法

如果不是用双引号声明的String对象,可以使用String提供的intern方法。intern方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中。比如:String str = new String("i love you").intern();

 

保证变量s指向的是字符串常量池中的数据
方式一:

String s = "add";

方式二:

String s = new String("add").intern();
String s = new StringBuilder("add").toString().intern();

  

 

首先,明确一点,字符串常量池中存储的可以是一个字符串对象或者一个堆中对象的引用。
当使用 String s = "abc"; 这种字面量定义方式创建一个字符串对象时,首先会在字符串常量池中检查是否存在 "abc" 这个字符串对象或者指向堆中对象的引用(堆中对象的内容为"abc")。若存在,则返回已存在的对象的引用;若不存在,则将"abc"放入字符串常量池中,并返回引用。
当使用 String s = new String("abc"); 创建时,会创建两次对象,一个是字符串常量池中的字符串对象,一个是堆中new出来的String对象。
当使用 String s = new String("abc") + new String("abc"); 创建时,会在堆中new出来内容为"abcabc"的对象,但在字符串常量池中不会创建该字符串对象。但是,如果新增一条语句 s.intern(); 的话,字符串常量池中会创建一个指向堆中对象(即s指向的对象)的引用的字符串对象。

 

为什么字符串变量拼接不会在字符串常量池中创建字符串对象?

 

posted @ 2021-04-27 11:44  455994206  阅读(106)  评论(0)    收藏  举报