细读源码之Java String (一)

简介: 细读源码之Java String (一)

Java中的String类绝对是使用最广泛的类,本文就带着大家细读一下JDK8下的String源码,主要包含以下几个方面的内容:

一.String不可变特性

二.String核心字段分析

三.String常见方法分析

四.String在Jdk1.6和1.8版本的差异

image.gif编辑

一.String的不可变特性

1.什么是不可变对象?

如果一个对象,在它创建完成之后,不能再改变它的状态,那么这个对象就是不可变的。

2.String设计成不可变的好处

A.只有当字符串是不可变的,字符串常量池才有可能实现,才能把字面值相同的字符串都指向常量池中的同一地址空间,达到节约堆内存的目的;

B.字符串是不可变的,必然是线程安全的,这样同一个字符串实例就可以被多个线程安全地共享,而且不需要任何同步操作;

C.字符串是不可变的,才能缓存hash的值,避免重复计算,使得String作为HashMap的Key,具有很好的性能。

3.String类如何实现不可变

1.String类定义使用final修饰,不能被继承,这样String的所有方法就不能被重写;

2.存储字符串数据的字段声明为private final char value[],使用private final修饰,value自身的值初始化后不能被修改,也不能被外部访问;

3.String的构造函数,通过拷贝等方式,保证String内部value字段不能被外部访问;

4.任何需要改变String内容的方法,都创建了一个新的String对象,而原对象保持不变,如substring,trim,toUpperCase等;

5.获取value的方法,都返回了value的一个拷贝,如toCharArray。

4.String类不可变性破坏

虽然value字段被private修饰,但是还是能通过反射的方式获取并进行修改,代码如下:

private static void main() throws IllegalAccessException, NoSuchFieldException {    String a = "1";    Field valueField = String.class.getDeclaredField("value");    System.out.println(a);    valueField.setAccessible(true);    char[] value = (char[]) valueField.get(a);    value[0] = '0';    System.out.println(a);}

通过上面代码,就把变量a的值由1变成0。

二.String的核心字段

1.value

value的定义为private final char value[],存储字符串中的每一个字符,并保证不被外部访问。

2.hash

hash的定义为private int hash,存储的是字符串的hashCode的值,与hash关联的有两个问题:

A.为什么要存储hash值?

因为字符串的hashCode方法计算非常耗时,时间复杂度是O(N),如果每一次用到,都需要计算,严重影响性能,所以有必要缓存hash的值;

2.为什么hash字段没有使用final修饰?

如果使用final修饰,hash的值必须在对象初始化完成之前完成赋值,而hash的值的计算又很耗时,对于那些不需要使用hash的字符串,计算好了又不使用,会造成CPU的浪费。所以这里就采用延迟计算的策略,默认值为0,需要使用的时候再计算,要达到这样目的hash字段就不能被final修饰。

三.String常见方法分析

1.String加法

Java中不容许操作符重载,所以String的加法是通过语法糖(编译手段)来实现的,具体分为以下两种情况:

A. 执行相加操作时,结果值在编译期确定,就直接使用相加后的结果进行替换,这样就消除加法操作。举例说明:

例1:

public static void main(String[] args) {    String value = "a" + "b";    System.out.println(value);}

因为字符串"a"和"b"都是字面常量,value的值在编译的时候就已经确定,值为"ab",所以直接把"ab"赋值给了value。我们反编译上面代码生成的class文件,得到的等价代码如下:

public static void main(String[] args) {    String value = "ab";    System.out.println(value);}

 

例2:

public static void main(String[] args) {    final String a = "a";    final String b = "b";    String value = a + b;    System.out.println(value);}

因为变量a和b都使用final修饰,值为字面常量,编译期确定,所有在编译期确定a + b的值就是就是"ab",就直接把"ab"赋值给了value,我们反编译上面代码生成的class文件,等价代码如下:

public static void main(String[] args) {    String a = "a";    String b = "b";    String value = "ab";    System.out.println(value);}

B. 执行相加操作时,结果值在编译期不能确定,则使用StringBuilder进行拼接,举例说明:

例3:

public static void main(String[] args) {    String a = "a";    String b = "b";    String c = "c";    String value = a + b + c;    System.out.println(value);}

因为a,b,c三个变量,没有final修饰,值可能在运行的过程中被修改,所以value的值就是编译期不定,编译的时候转换为使用StringBuilder进行拼接,反编译后等价代码如下:

public static void main(String[] args) {    String a = "a";    String b = "b";    String c = "c";    String value = new StringBuilder().append(a).append(b).append(c).toString();    System.out.println(value);}

例4:

public static void main(String[] args) {    final String a = String.valueOf(System.currentTimeMillis());    final String b = String.valueOf(System.currentTimeMillis());    String value = a + b;    System.out.println(value);}

虽然变量a和b使用了final修饰,但是a和b的值在编译期不确定,所以value的值也是不定的,最后只能使用StringBuilder进行拼接,反编译后等价代码如下:

public static void main6(String[] args) {    final String a = String.valueOf(System.currentTimeMillis());    final String b = String.valueOf(System.currentTimeMillis());    String value = new StringBuilder().append(a).append(b).toString();    System.out.println(value);}

说明一下,String加法在编译的时候,究竟是采用用结果值直接替换还是使用StringBuilder进行拼接,跟进行相加的变量是否使用final修饰没有直接关系,唯一的判断依据就是编译期加法结果的值是否确定,确定就替换,不确定就拼接。

例5:

public static void main(String[] args) {    String a = "";    for (int i = 0; i < 26; i++) {        a += (char) ('A' + i);    }    System.out.println(a);}

这个例子用来说明低效使用String加法运算,此过程会频繁的创建String和StringBuilder对象,所以一定避免在循环中使用加号对字符串进行拼接,上面代码反编译后等价代码如下:

public static void main(String[] args) {    String a = "";    for (int i = 0; i < 26; i++) {        a = new StringBuilder().append(a).append((char) ('A' + i)).toString();    }    System.out.println(a);}

效果更高的写法弃用加法,直接使用StringBuilder进行字符串拼接,只创建一个StringBuilder对象,只调用一次builder.toString()方法,代码如下:

public static void main(String[] args) {    StringBuilder builder = new StringBuilder();    for (int i = 0; i < 26; i++) {        builder.append((char) ('A' + i));    }    String value = builder.toString();    System.out.println(value);}
相关文章
|
5月前
|
前端开发 Java 关系型数据库
基于Java+Springboot+Vue开发的鲜花商城管理系统源码+运行
基于Java+Springboot+Vue开发的鲜花商城管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的鲜花商城管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。技术学习共同进步
378 7
|
5月前
|
消息中间件 算法 安全
JUC并发—1.Java集合包底层源码剖析
本文主要对JDK中的集合包源码进行了剖析。
|
5月前
|
人工智能 安全 Java
智慧工地源码,Java语言开发,微服务架构,支持分布式和集群部署,多端覆盖
智慧工地是“互联网+建筑工地”的创新模式,基于物联网、移动互联网、BIM、大数据、人工智能等技术,实现对施工现场人员、设备、材料、安全等环节的智能化管理。其解决方案涵盖数据大屏、移动APP和PC管理端,采用高性能Java微服务架构,支持分布式与集群部署,结合Redis、消息队列等技术确保系统稳定高效。通过大数据驱动决策、物联网实时监测预警及AI智能视频监控,消除数据孤岛,提升项目可控性与安全性。智慧工地提供专家级远程管理服务,助力施工质量和安全管理升级,同时依托可扩展平台、多端应用和丰富设备接口,满足多样化需求,推动建筑行业数字化转型。
179 5
|
2月前
|
自然语言处理 Java Apache
在Java中将String字符串转换为算术表达式并计算
具体的实现逻辑需要填写在 `Tokenizer`和 `ExpressionParser`类中,这里只提供了大概的框架。在实际实现时 `Tokenizer`应该提供分词逻辑,把输入的字符串转换成Token序列。而 `ExpressionParser`应当通过递归下降的方式依次解析
171 14
|
4月前
|
JavaScript Java 关系型数据库
家政系统源码,java版本
这是一款基于SpringBoot后端框架、MySQL数据库及Uniapp移动端开发的家政预约上门服务系统。
140 6
家政系统源码,java版本
|
3月前
|
存储 安全 Java
Java 集合面试题从数据结构到 HashMap 源码剖析详解及长尾考点梳理
本文深入解析Java集合框架,涵盖基础概念、常见集合类型及HashMap的底层数据结构与源码实现。从Collection、Map到Iterator接口,逐一剖析其特性与应用场景。重点解读HashMap在JDK1.7与1.8中的数据结构演变,包括数组+链表+红黑树优化,以及put方法和扩容机制的实现细节。结合订单管理与用户权限管理等实际案例,展示集合框架的应用价值,助你全面掌握相关知识,轻松应对面试与开发需求。
185 3
|
4月前
|
供应链 JavaScript 前端开发
Java基于SaaS模式多租户ERP系统源码
ERP,全称 Enterprise Resource Planning 即企业资源计划。是一种集成化的管理软件系统,它通过信息技术手段,将企业的各个业务流程和资源管理进行整合,以提高企业的运营效率和管理水平,它是一种先进的企业管理理念和信息化管理系统。 适用于小微企业的 SaaS模式多租户ERP管理系统, 采用最新的技术栈开发, 让企业简单上云。专注于小微企业的应用需求,如企业基本的进销存、询价,报价, 采购、销售、MRP生产制造、品质管理、仓库库存管理、财务应收付款, OA办公单据、CRM等。
254 23
|
5月前
|
Java
【源码】【Java并发】【ConcurrentHashMap】适合中学体质的ConcurrentHashMap
本文深入解析了ConcurrentHashMap的实现原理,涵盖JDK 7与JDK 8的区别、静态代码块、构造方法、put/get/remove核心方法等。JDK 8通过Node数组+链表/红黑树结构优化并发性能,采用CAS和synchronized实现高效锁机制。文章还详细讲解了hash计算、表初始化、扩容协助及计数更新等关键环节,帮助读者全面掌握ConcurrentHashMap的工作机制。
128 6
【源码】【Java并发】【ConcurrentHashMap】适合中学体质的ConcurrentHashMap
|
5月前
|
Java
【源码】【Java并发】【LinkedBlockingQueue】适合中学体质的LinkedBlockingQueue入门
前言 有了前文对简单实用的学习 【Java并发】【LinkedBlockingQueue】适合初学体质的LinkedBlockingQueue入门 聪明的你,一定会想知道更多。哈哈哈哈哈,下面主播就...
97 6
【源码】【Java并发】【LinkedBlockingQueue】适合中学体质的LinkedBlockingQueue入门

热门文章

最新文章