Java基础深度总结:String

今天是如此珍贵,不值得为了苦涩的烦恼及悔恨而糟蹋它,抬高你的下巴,保持思想开朗,如同春阳下闪耀的水涧。紧紧把握住今天,因为它一去不复返。

1.String概述

String被声明为final,因此它不可被继承。

  • Java8中,String内部使用char数组存储数据
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];
}
  • Java9中,String类的实现改为byte数组存储字符串,并用coder标识使用的编码
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final byte[] value;

    /** The identifier of the encoding used to encode the bytes in {@code value}. */
    private final byte coder;
}
2.String的不可变性

String的不可变性由以下两点保证:

  1. final修饰存放字符的成员变量(Java8中的char数组、Java9中的byte数组和coder都被声明为final),意味着它们都只能被赋值一次,并且String内部的方法都没有去改变存放字符的成员变量的内容,而是每次创建一个新的String对象,如 split()、subString等方法。
  2. final修饰String类,表明String类不可被继承,子类无法继承父类,就不能通过重写String类中的方法改变String不可变性的特点。

针对第一点,需要注意的是,String通过char数组或byte数组存放字符串,数组是引用类型,final修饰引用类型仅仅表示改引用的值(地址)只能被赋值一次,因此不能保证该引用所指向对象的内容不发生改变。对于引用类型,只有final修饰还不足以保证不可变性,此时还需要增加额外的逻辑去保证。String类在每次要改变数组内容时,都返回了一个新的String对象来保证不可变。

不可变的好处

  • 可以缓存hash值(hash码的唯一)
    String经常作为hash值被使用,如String作为hashMap的key,String的不可变性保证了hash值的不可变。
  • String Pool的需要
    如果一个String对象已经被创建过了,那么就会从String Pool中取得引用,只有String是不可变的,才能使用/实现String Pool。
  • 安全性
    String经常作为参数,如果字符串是可变的,就会引起各种严重错误和安全漏洞。
  • 线程安全
    String的不可变性天生具备线程安全,可在多线程环境下安全的使用。
3.String,StringBuffer,StringBuilder
类别可变性线程安全性效率
String不可变线程安全
StringBuffer可变线程安全
StringBuilder可变线程不安全

(1)可变性

  • String的不可变性
  • StringBuffer和StringBuilder都继承自AbstractStringBuilder类,AbstractStringBuilder使用char数组来保存字符串,是可变的。

(2)线程安全性

  • String的不可变性保证了线程安全
  • StringBuilder线程不安全
  • StringBuffer对方法加入了synchronized同步锁进行同步,因此线程安全

(3)效率

       StringBuilder > StringBuffer > String

  • 由于String的不可变性,导致每次对String的改变都会生成新的对象,因此在大量的字符串操作下,会造成效率低下,且大量消耗内存
  • StringBuffer和StringBuilder会通过append方法,将新的字符串加入value数组末尾(默认value数组的容量为16)
  • StringBuffer对方法加入了同步锁,因此效率低于StringBuilder,但在多线程环境下,不应该使用StringBuilder
4.String Pool 字符串常量池(重点)

在JVM中存放着一个字符串池,其中保存着很多String对象,这些对象可以被共享使用。当以字符串直接创建String对象时,会首先在字符串池中查找是否存在该常量。如果不存在,则在String Pool中创建一个,然后将其地址返回。如果在String Pool中查询到已经存在该常量,则不创建对象,直接返回这个对象地址。

字符串常量池存在于运行时常量池中

常量池常分为两大类:
(1)静态常量池:用于存放编译后生成的各种字面量和符号引用。
(2)运行时常量池:再类加载后产生的新常量放入运行时常量池中。运行时常量池主要包含:

  • 类、接口、方法、类字段表述信息
  • 字符串常量池
  • 被final修饰的类变量
  • 包装类缓存池

Tips:

  • JDK1.7之前,运行时常量池逻辑包含字符串常量池,放在方法区,此时hotspot虚拟机对方法区的实现为永久代。
  • JDK1.7字符串常量池从方法区拿到了堆中,原因是方法区空间有限,在大量使用字符串的场景下,会导致内存溢出(OutOfMemory)
  • JDK1.8 hotspot用元空间替换了永久代,字符串常量池还在堆中,方法区的实现从永久代变为元空间。

字面量创建字符串常量:String s = "abc"

使用字面量创建字符串常量时(String s = “abc”),JVM首先会检查 String Pool 字符串常量池中是否存在"abc"字符串对象,若该字符串对象已存在于 String Pool 中,则会直接将常量池中该对象的内存地址赋值给 s。

String s5 = "bbb";
String s6 = "bbb";
System.out.println(s5 == s6);  // true

使用关键字new创建字符串常量:String s = new String(“abc”)

使用这种方式一共会创建两个字符串对象(前提是 String Pool 中还没有 “abc” 字符串对象)。

  • 由于"abc"作为构造参数,本身就是一个字面量字符串,所以若String Pool中没有 "abc"字符串对象,那么就会在在 String Pool 中创建一个字符串对象。
  • 使用new关键字会在堆中创建一个字符串对象。
String s1 = new String("aaa");
String s2 = new String("aaa");
System.out.println(s1 == s2);           // false

String.intern()解析

intern方法: 如果String Pool 中已经存在该字符串对象,则直接返回String Pool中该对象的引用。否则,在String Pool中加入该对象,然后返回引用。(这里的加入该对象,在Java7之前和Java7及以后的处理方式不同)

String s1 = new String("1"); // String Pool中和堆中分别创建了一个对象,s1指向堆中的对象
s1.intern();	 //String Pool中已经创建过该字符串对象了
String s2 = "1"; //s2指向String Pool中的字符串对象
System.out.println(s1 == s2);  //false

JDK1.6与JDK1.7 intern方法的区别

String s3 = new String("1") + new String("1"); // 堆中有 "1"和"11"两个对象,String Pool中有"1"一个对象,s3指向堆中的'11"对象
s3.intern();  //将“11”对象加入String Pool中,JDK1.6 和 JDK1.7不同
String s4 = "11"; 
System.out.println(s3 == s4); // JDK1.6:false   JDK1.7:true

JDK1.6:调用s3.intern();若字符串常量池中不存在该字符串对象,则在String Pool中创建一个对象,然后返回它在String Pool的内存地址;若存在则直接返回String Pool中该对象的内存地址。

JDK1.7:调用s3.intern();若字符串常量池中不存在该字符串对象,则会将堆中此对象的引用添加到String Pool中,并返回该引用;若存在则直接返回String Pool中该对象的内存地址。

Tip:也就是说JDK1.7开始,String Pool中即可以存放字符串对象,也可以存放字符串对象的引用。因此JDK1.6中 String Pool存放的是"11"字符串对象,s4就指向String Pool中的"11"对象,因此是false,而JDK1.7 String Pool 存放的是"11"字符串对象在堆内存的引用,所以s4和s3是同一个对象,结果为true。

String里的"+"操作

  • 字面量常量字符串之间的"+"操作会在编译阶段优化,直接合成一个字符串。
    如:String str = “JA”+“VA”; 编译后等同于String str = “JAVA”;因此String Pool中只会有"JAVA"这一个对象。
  • 字面量常量字符串与变量的"+“操作,会调用StringBuilder.append()在堆中创建新的对象。
    如:String str2 = str + “01”; 等同于 String str2 = new StringBuilder(str).append(“01”);会将str转化成一个StringBuilder对象,然后调用append方法,添加"01”,因此str2不会进入常量池,但由于每次拼接字符串时都会new StringBuilder,所以在循环拼接字符串时,不建议用 + 号。
  • 被final修饰的字段,编译期直接进行常量替换
fianl Sring s1 = "Ja";
fianl Sring s2 = "va";
String s3 = s1 + s2;

在编译时,直接替换成:String s3 = “Ja” + “va”; 又由于 “Ja” 和 “va” 都是字面量常量字符串,于是又被优化成:String s3 = “Java”;

5.String 作为形参

Java中只有值传递,没有引用传递。

值传递引用传递
根本区别创建副本不创建副本
所以函数中无法改变原对象函数中可以改变原对象
public class Test{
	public static void main(String[] args) {
		String s = "aaa";
		System.out.println(s.hashcode()); //96321
		change(s);
		System.out.println(s);// aaa
	}

	public static void change(String s1){
		System.out.println(s1.hashcode()); //96321
		s1 = "bbb";
		System.out.println(s1.hashcode()); //97314
	}
}

解析:
在这里插入图片描述
s1是s的副本,因此开始与s同时指向aaa对象,s1=“bbb”,并不是改变了s1原来所指向对象的值,而是在常量池中新创建了一个对象"bbb",然后指向"bbb",而s的指向没有发生变化。

Tip:Java语言中所谓的"引用传递",本质上依然是一种值传递,其修改的内容是指针所指向对象的属性,而不是对象本身。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
绝对想你所想,超乎想象!够详细,够给力! 目录 1. Jvm内存空间结构是什么样的? 1 程序计数器 1 Java栈 1 本地方法栈 2 堆 2 方法区 3 2. Jvm堆内存的划分结构和优化 3 2.1. 原理 6 2.1.1. 年轻代 6 2.1.2. 年老代 6 2.1.3. 持久代 7 2.2. 参数说明 8 2.3. 疑问解答 9 2.4. 垃圾回收器选择 10 2.4.1. 串行收集器 10 2.4.2. 并行收集器(吞吐量优先) 10 2.4.3. 并发收集器(响应时间优先) 11 2.4.4. 其它垃圾回收参数 11 2.5. 辅助信息参数设置 12 2.6. 关于参数名称等 13 3. JVM服务参数调优实战 14 3.1. 大型网站服务器案例 14 3.2. 内部集成构建服务器案例 15 4. 常量池在jvm的哪个空间里边? 17 5. jvm垃圾回收是什么时候触发的? 17 5.1. 那究竟GC为我们做了什么操作呢? 17 5.1.1. Jvm怎么判断对象可以回收了? 18 5.2. 下面我们来看一下几种垃圾收集算法 18 5.2.1. 在JDK1.2之前,使用的是引用计数器算法, 18 5.2.2. 根搜索算法 19 5.2.3. 引用的分类 19 5.3. 方法区也是会被回收的 20 5.4. 下面我们来逐一介绍一下每个垃圾回收器。 22 5.4.1. 1、 Serial收集器 22 5.4.2. 2、 ParNew收集器 23 5.4.3. 3、 ParallelScavenge 23 5.4.4. 4、 ParallelOld 24 5.4.5. 5、 SerialOld 24 5.4.6. 6、CMS 24 5.4.7. 7、 GarbageFirst(G1 ) 26 6. 常量池如何触发的垃圾回收? 28 7. 垃圾回收从哪里开始检测树进行回收?根节点是什么? 28 7.1. 目前java中可作为GC Root的对象有 29 8. Redis怎么判断对象可以清理了? 29 9. Redis怎么提高命中率? 29 10. Finalize中调用垃圾回收方法,再调用方法会回收么? 29 11. Int进行自增操作,如何保证线程安全? 30 12. Int a=1是原子方法,布尔类型进行赋值是原子方法么? 30 12.1. 多线程原子操作的安全性 31 13. Cas怎么实现unsafe? 32 13.1. Unsafe 32 13.2. CAS 32 13.3. 由CAS分析AtomicInteger原理 33 13.4. CAS的缺点 35 14. Map数据结构? 35 14.1. 一、定义 36 14.2. 二、构造函数 36 14.3. 三、数据结构 36 14.4. 四、存储实现:put(key,vlaue) 38 14.5. 五、读取实现:get(key) 41 15. 一百万数据放Arraylist数组,怎么放? 在哪个代? 42 15.1.1. 调整数组容量 42 16. Hashmap和 concurrentHashmap除了线程安全 还有什么区别,put的时候是怎么处理的。 43 17. 数据库组合索引,储存在一个叶子节点还是多个? 44 17.1. 索引的利弊与如何判定,是否需要索引: 44 17.1.1. 索引的好处 44 17.1.2. 索引的弊端 44 17.1.3. 如何判定是否须要创建索引 44 17.2. 复合索引优化 45 17.3. 下面是一些常见的索引限制问题 45 17.3.1. 使用不等于操作符(<>, !=) 45 17.3.2. 使用 is null 或 is not null 45 17.3.3. 使用函数 45 17.3.4. 比较不匹配的数据类型 46 17.4. 关于索引的排序 46 18. 数据库没有orderby默认如何排序。 46 19. 分布式事务如何实现。 46 19.1. 1 事务/分布式事务 47 19.1.1. 1.1 事务 47 19.1.2. 1.2 分布式事务与 XA 规范 47 19.2. 2 两阶段提交协议 47 19.2.1. 2.1 预提交阶段 48 19.2.2. 2.2 提交阶段 48 19.3. 3 分布式事务应用框架 48 19.3.1. 3.1 角色 48 19.3.2. 3.2 交互时序 49 19.3.3. 3.3 关键点 50 19.3.4. 参考 50 20. Jvm底层源码 51 21. 二叉树怎么实现的? 51 22. Executourservice 数据结构。哪种只能执行一个线程。 Cache怎么实现的 fixed线程如果中断,线程回自己销毁么? 51 23. 栅栏的原理和实现。 51 23.1. 1. CyclicBarrier简介 51 23.2. 2. CyclicBarrier数据结构 52 23.3. 3. CyclicBarrier源码分析(基于JDK1.7.0_40) 52 23.3.1. 3.1 构造函数 52 23.3.2. 3.2 等待函数 53 23.4. 4. CyclicBarrier的使用示例 57 23.4.1. 示例1 57 23.4.2. 示例2 59 24. Blockingqueue有几种形式?各自的编码方式。 72 24.1. Queue接口 72 24.2. BlockingQueue接口 73 24.2.1. ArrayBlockingQueue 74 24.2.2. LinkedBlockingQueue 78 24.2.3. LinkedBlockingDeque(双向并发阻塞队列) 84 24.2.4. PriorityBlockingQueue(优先阻塞队列) 85 24.2.5. 总结一下阻塞队列 90 25. Tomcat 如何管理servlet? 90 26. Servlet生命周期 90 27. 没有缓存,如何实现领券并发操作? 92
【完整课程列表】 资源汇总下载地址:https://download.csdn.net/download/qq_27595745/55418618 Java SE编程入门教程 collection与collections(共7页).pptx Java SE编程入门教程 java GC(共6页).pptx Java SE编程入门教程 java instanceof(共3页).pptx Java SE编程入门教程 java IO(共28页).pptx Java SE编程入门教程 java Math(共11页).pptx Java SE编程入门教程 java object(共9页).pptx Java SE编程入门教程 java static final(共24页).pptx Java SE编程入门教程 java this和super(共10页).pptx Java SE编程入门教程 java 常用API(共22页).pptx Java SE编程入门教程 javaGUI编程快速入门(1)(共82页).pptx Java SE编程入门教程 java包装器(共9页).pptx Java SE编程入门教程 java反射机制(共16页).pptx Java SE编程入门教程 java泛型(共11页).pptx Java SE编程入门教程 java封装继承多态(共56页).pptx Java SE编程入门教程 java集合(共38页).pptx Java SE编程入门教程 java接口(共21页).pptx Java SE编程入门教程 java类和对象方法以及包的使用(共56页).pptx Java SE编程入门教程 java类和对象方法以及包的使用(共62页).pptx Java SE编程入门教程 java判断循环(共79页).ppt Java SE编程入门教程 java入门以及变量debug(共63页).pptx Java SE编程入门教程 java设计模式(共17页).pptx Java SE编程入门教程 java数组(共33页).pptx Java SE编程入门教程 java网络编程(共29页).pptx Java SE编程入门教程 java线程(共61页).pptx Java SE编程入门教程 java序列化(共14页).pptx Java SE编程入门教程 java异常(共57页).pptx Java SE编程入门教程 java正则(共8页).pptx Java SE编程入门教程 properties(共3页).pptx Java SE编程入门教程 String字符串(共27页).pptx Java 位运算.docx java正则表达式例子大全 共10页.doc JAVA正则表达式实例教程 共17页.docx JAVA正则表达式实例教程 共17页.pdf 递归算法.docx 堆和栈.wps

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值