【JAVA】String中两种初始化方式比较

方法1:String s1 = "abc";

  • 这一句话作了什么操做:

    • 首先在常量池中查找"abc",若是没有则在常量池建立该对象
    • 在栈中建立s1的引用,将s1直接指向对象"abc"
    • 所以在这里"abc"是常量池中的对象,若是声明另外一个String类型的对象引用,并将它指向对象"abc",则这两个引用指向的是同一个常量池中的对象。
  • 详述

    • 建立过程是,"abc"是字符串,是匿名的String对象。
    • 当"abc"被声明时,会先去字符串常量池中(位于堆内存中)查询是够存在该字符串了,若是存在将会返回常量池中的字符串引用地址,将此赋值给s。
    • 若是没有,将会把字符串"abc"存入到常量池中,而后进行再返回地址。
    • 下次再使用该字符串时,直接返回该字符串再常量池中的地址便可。
    • 这种建立对象的方式叫作享元模式,在Interger类中对[-128~127)之间的数值也是使用了该模式。
  • 代码示例:

String s1 = "abc";//"abc"是一个对象,将对象赋予类变量s1
String s2 = new String("abc");//这里是两个对象,在内存中存在两个,包括对象abc 和 new 出来的对象
String s3 = "abc";  //由于String类型数据是不可变的,因此‘abc’被放在了常量池中,这里的‘abc’ַ和s1的‘abc’是
							//同一个常量abc对象,所以两者的内存地址是同样的。
							
System.out.println(s1==s2);//false
System.out.println(s1==s3);//true 这是这号i
  • 图示辨析:

public class StringDemo {
    public static void main(String[] args) {
        String str = "Hello";
        str = str + "World";
        str += "!!!";
        System.out.println(str);
 
    }
}

public static void main(String[] args) {
    String stra = "hello" ;
    String strb = "hello" ;
    String strc = "hello" ;
    System.out.println(stra == strb);//true
    System.out.println(stra == strc);//true
    System.out.println(strb == strc);//true
    }
}

方法2:String s = new String(“abc”);

  • 凡是通过 new 建立出来的对象,都会在堆内存中分配新的空间,建立新的对象,因此s是String类新建立的对象

两种初始化方法对比

String s = "aa";
s =s + "bb";
String s2 = "aabb";
s == s2;???

这个的结果是false,这时候s 和s2已经不是同样的了,首先看 s2,s2指向的是常量池中的对象,这是肯定的。因此尽管s的值和s2是同样的,可是s指向的不是常量池的中的对象,而是一个新的new出来的对象。 解释以前,先了解一下 + 这个符号,在字符串拼接里面,至关于+ 源码大意为: (new StringBuffer()).append(s3).append(“bbb”).toString; 因此,这里的s指向的是一个新的对象。缓存

总结: 在String的两种声明方式,直接赋予字符值的是,String对象引用获取常量池中对象的地址,因此String声明出来是不能够改变的。new String()出来的是在堆内存建立对象。若是要给每一个对象中的String属性赋予一个初始值,采用String s = ‘abc’方式,这样建立的是常量池中的一个对象,其余对象是获取这个常量的地址。要是new 则每次都要建立,加大内存消耗。还要注意,字符串拼接不要用+ ,会建立对象。安全

  • 代码示例

public class Demo1 {

	@Test
	public void test1() {
		String s1 = "abc";//"abc"是一个对象,将对象赋予类变量s1
		String s2 = new String("abc");//这里是两个对象,在内存中存在两个,包括对象abc 和 new 出来的对象
		String s3 = "abc";  //由于String类型数据是不可变的,因此‘abc’被放在了常量池中,这里的‘abc’ַ和s1的‘abc’是
							//同一个常量abc对象,所以两者的内存地址是同样的。
		
		System.out.println(s1==s2);//false
		System.out.println(s1==s3);//true 这是这号i
		
		//+ 源码大意为: (new StringBuffer()).append(s3).append("bbb").toString;
		//是新new出一个新的StringBuffer对象,
		s3 = s3+"bbb";//这时候s3已经不指向"abc",源对象依旧存在,s3是新的string类型的对象
		String s4 = "abcbbb";
		String s5 = new String("abcbbb");
		System.out.println(s3);
		System.out.println(s3==s4);//false  s3是一个新的String对象
		System.out.println(s4=="abcbbb");//true  这个“abcbbb”属于同一个常量池中的对象
		System.out.println(s4==s5);//false 一个在常量池,一个是new的对象
	}
}

intern()方法

  • 概述

    • intern()方法是能使一个位于堆中的字符串在运行期间动态地加入到字符串常量池中(字符串常量池的内容是程序启动的时候就已经加载好了),若是字符串常量池中有该对象对应的字面量,则返回该字面量在字符串常量池中的引用,不然,建立复制一份该字面量到字符串常量池并返回它的引用。
  • 代码示例:

String s1 = "abc";
        String s2 = new String("abc");
        String s3 = new String("abc").intern();//s3实际上是字符串常量"abc"
    	
		/*
		s1是常量池中的对象,s2是堆中对象,是不一样对象
		*/
        System.out.println(s1 == s2);//false
		
		//二者都是表示字符串常量abc,因此是true
        System.out.println(s1 == s3);//true
		
		//s3是常量池中的对象abc,s2是堆中对象,是不一样对象
        System.out.println(s2 == s3);
		
		//都表示一个值abc
        System.out.println(s1 == "abc");  //true
        System.out.println(s3 == "abc");  //true

字符串内容不可变属性分析

  • 弊端:

    • 字符串内容的更改,实际上改变的是字符串对象的引用过程,而且会伴随有大量的垃圾出现,在实际开发中应该避免。
  • 好处:

    • 能够缓存hash值:
      • String的值是不容许改变的,所以hash值就能够计算出来,缓存起来,不要要每次使用都计算。
    • String pool的须要
      • 若是一个String对象已经建立过了,那么将会从String pool中取得引用,只有String不可变,才能保证其余使用该对象是安全的。
    • 线程安全
      • String 不可变性天生具有线程安全,能够在多个线程中安全地使用。
    • 安全性
      • String常常做为参数,String的不可变特性能够保证参数不可变。列如在做为网络链接参数的状况下,若是String可变,那么在网络链接过程当中,String被改变,改变String对象的哪一方觉得如今链接的是其余主机,而实际状况却不是。

参考连接

END

相关文章
相关标签/搜索