String类总结

一、String类介绍:
String是一个引用数据类型默认为null;
String 为final类型不可更改,不能被继承;
基本类型与String结合都会转换为String类型;
String类的基底是一个数组,和数组一样在初始化之后它的长度是不变的,字符串内容也是不变的,所以如果要改变它的长度或者值,就会产生一个新的字符串,
二、String两种赋值方式:
1)直接赋值:String str = " ";
String str = “” 原理:
在 JVM(虚拟机)中有一个字符串池,专门用来存储字符串。如果遇到 String a=”hello”时(注意没有 NEW,不是创建新串),系统在字符串池中寻找是否有 ”hello”,
此时字符串池中没有”hello”,那么系统将此字符串存到字符串池中,然后将 ”hello”在字符串池中的地址返回 a。
如果系统再遇到String b=”hello”,此时系统可以在字符串池中找到 “hello”。则会把地址返回b,此时 a与b 为相同。

2)使用new进行赋值:String str = new String (" ");
每new一次就会在堆区开辟一个新的地址
3) 两种赋值方式的区别:
1、直接定义的String "a"是储存在常量存储区中的字符串常量池中;new String(“a”)是存储在堆中;
2、常量池中相同的字符串只有一个,但是new String(),每new一个对象就会在堆中新建一个对象;

3、String a = “a”在编译阶段就会在内存中创建;String a = new String(“a”);是在运行时才会在堆中创建对象;

    String a = "hello";
    String b = "hello";
    System.out.println(a == b );//true  a b 指向同一个值,地址相同
    System.out.println(a.equals(b));//true a b 是同一个值,当然相等

    String a1 = new String ("hello");
    String b1 = new String ("hello");
    System.out.println(a1 == b1 );//false  每次new都会创建一个新对象,a1 b1 地址不同 
    System.out.println(a1 .equals(b1) );//true 

由此可以得出两种实例化方式的区别
1)直接赋值(String str = “hello”):只开辟一块堆内存空间,并且会自动入池,不会产生垃圾。
2)构造方法(String str= new String(“hello”);):会开辟两块堆内存空间,其中一块堆内存会变成垃圾被系统回收
在开发的过程中不会采用构造方法进行字符串的实例化。
4)常量池
字符串常量池在jdk1.6及之前在方法区中,但是在jdk1.7及以后就放在了堆中

编译阶段确定的字符串才会被放到字符串常量池中

分析下面创建几个对象:

            String s1 = "java"; 
	String s2 = "从0到1"; 
	String s3 = "java从0到1"; 
	String s4 = s1+s2;
	System.out.println(s3==s4);

s1和s2还有s3都会在字符串常量池中,这已经有三个对象了 。关键在于这行代码上String s4= s1 + s2;

对于s3无法在编译阶段确定下来,所以无论最后的字符串是什么都不会存在字符串常量池中,那么存在哪呢?因为对象创建在堆中,而字符串常量池也在堆中,既然不在字符串常量池中那就是在字符串常量池之外的堆中了,而s3最终的字符串对象是“Java从0到1”,那么也就是说这个字符串在常量池之外的堆中,这是一个对象了,加上之前的三个,这里一共四个了,但是我们还要分析这个s3是怎么来的,是通过s1和s2相加得到的,因为这个s3最终是在常量池之外的堆中形成的,而s1和s2都是在常量池中,因此会将s1和s2拷贝一份到字符串常量池之外的堆中来形成s3,我们看下面的一张图来加深理解在这里插入图片描述
堆内存复制s1,s2,这样的话要再加上两个对象,那一共就是创建6个对象了!

三、String源码解析:
Jdk1.7 的String源码如下:

public final class String
implements java.io.Serializable, Comparable, CharSequence {
/** The value is used for character storage. */
private final char value[];

/** Cache the hash code for the string */
private int hash; // Default to 0

由以上的代码可以看出, 在Java中String类其实就是对字符数组的封装,除此之外还有一个hash成员变量,是该String对象的哈希值的缓存。
value这个变量是final的, 也就是说在String类内部,一旦这个值初始化了, 也不能被改变。所以可以认为String对象是不可变的了。

String s = new (“ABCabc”); 对应的内存模型为
在这里插入图片描述

当我们调用s.replace(‘A’, ‘a’)时, 方法内部创建了一个新的String对象,并把这个新的对象重新赋给了引用s;

因此结果为 aBCabc

Spring是否真的是不可变?
我们知道被final修饰的引用类型变量是引用的地址不可变,而不是引用的值不可变,因此我们能否改变value数组的值而使String改变???答案当然是可以的,反射可以搞定。

反射可以访问私有成员,因此我们可以反射出String对象中的value属性, 进而改变通过获得的value引用改变数组的结构。

public static void testReflection() throws Exception {
	
	//创建字符串"Hello World", 并赋给引用s
	String s = "Hello World"; 
	
	System.out.println("s = " + s);	//Hello World
	
	//获取String类中的value字段
	Field valueFieldOfString = String.class.getDeclaredField("value");
	
	//改变value属性的访问权限
	valueFieldOfString.setAccessible(true);
	
	//获取s对象上的value属性的值
	char[] value = (char[]) valueFieldOfString.get(s);
	
	//改变value所引用的数组中的第5个字符
	value[5] = '_';
	
	System.out.println("s = " + s);  //Hello_World
}

在这个过程中,s始终引用的同一个String对象,但是再反射前后,这个String对象发生了变化, 也就是说,通过反射是可以修改所谓的“不可变”对象的。但是一般我们不这么做。这个反射的实例还可以说明一个问题:如果一个对象,他组合的其他对象的状态是可以改变的,那么这个对象很可能不是不可变对象。例如一个Car对象,它组合了一个Wheel对象,虽然这个Wheel对象声明成了private final 的,但是这个Wheel对象内部的状态可以改变, 那么就不能很好的保证Car对象不可变。

四、String进行字符串拼接:
拼接原理:String是字符串常量的引用,String += String的本质是new了新的临时对象StringBuild,拼接后再把StringBuild.toString赋给原String。(简单点来说是先创建对象再进行拼接)

    String s="a"+"b"+"c"+"d"; 实际只创建了一个对象  ,过程是new 了 一个临时的StringBuild,然后把"a"+"b"+"c"+"d"进行拼接,结果"abcd".toString 返回gei s;
    
    所有大量字符串拼接不要直接使用String,否则会生成大量临时对象,严重影响性能。 

五、String,StringBuffer, StringBuilder比较:

    1、String是字符串常量,StringBuffer和StringBuilder都是字符串变量。后两者的字符内容可变,而前者创建后内容不可变(String进行字符串拼接时创建新对象)。

    2、StringBuffer是线程安全的,而StringBuilder是非线程安全的。

    3.线程安全会带来额外的系统开销,所以StringBuilder的效率比StringBuffer高。如果对系统中的线程是否安全很掌握,
    可用StringBuffer,在线程不安全处加上关键字Synchronize。

    4.  StringBuffer 和 StringBuilder 要调用.toString方法在进行比较

原文:https://blog.youkuaiyun.com/Bruce_Up/article/details/82731563

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值