String类介绍
一、String类介绍:
String是一个引用数据类型默认为null;
String 为final类型不可更改,不能被继承;
基本类型与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 (" ");
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还有s4都会在字符串常量池中,这已经有三个对象了 。关键在于这行代码上String s3 = 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<
String>,
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"); 对应的内存模型为:
当我们调用a.replace('A', 'a')时, 方法内部创建了一个新的String对象,并把这个新的对象重新赋给了引用a;
因此结果为 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方法在进行比较
<li class="tool-item tool-active is-like "><a href="javascript:;"><svg class="icon" aria-hidden="true"> <use xlink:href="#csdnc-thumbsup"></use> </svg><span class="name">点赞</span> <span class="count">10</span> </a></li> <li class="tool-item tool-active is-collection "><a href="javascript:;" data-report-click="{"mod":"popu_824"}"><svg class="icon" aria-hidden="true"> <use xlink:href="#icon-csdnc-Collection-G"></use> </svg><span class="name">收藏</span></a></li> <li class="tool-item tool-active is-share"><a href="javascript:;" data-report-click="{"mod":"1582594662_002"}"><svg class="icon" aria-hidden="true"> <use xlink:href="#icon-csdnc-fenxiang"></use> </svg>分享</a></li> <!--打赏开始--> <!--打赏结束--> <li class="tool-item tool-more"> <a> <svg t="1575545411852" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5717" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M179.176 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5718"></path><path d="M509.684 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5719"></path><path d="M846.175 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5720"></path></svg> </a> <ul class="more-box"> <li class="item"><a class="article-report">文章举报</a></li> </ul> </li> </ul> </div> </div> <div class="person-messagebox"> <div class="left-message"><a href="https://blog.csdn.net/Bruce_Up"> <img src="https://profile.csdnimg.cn/D/2/1/3_bruce_up" class="avatar_pic" username="Bruce_Up"> <img src="https://g.csdnimg.cn/static/user-reg-year/2x/2.png" class="user-years"> </a></div> <div class="middle-message"> <div class="title"><span class="tit"><a href="https://blog.csdn.net/Bruce_Up" data-report-click="{"mod":"popu_379"}" target="_blank">Bruce_Up</a></span> </div> <div class="text"><span>发布了51 篇原创文章</span> · <span>获赞 27</span> · <span>访问量 2万+</span></div> </div> <div class="right-message"> <a href="https://im.csdn.net/im/main.html?userName=Bruce_Up" target="_blank" class="btn btn-sm btn-red-hollow bt-button personal-letter">私信 </a> <a class="btn btn-sm bt-button personal-watch" data-report-click="{"mod":"popu_379"}">关注</a> </div> </div> </div> </article>