《Java架构筑基》从Java基础讲起——String类深入理解

一. String问题答疑

  • String字符串是如何设计与实现考量的?
  • String字符串缓存 intern()方法,由永久代移到堆中。
  • String 的演化,Java 9 中底层把 char 数组换成了 byte 数组,占用更少的空间

二. String的创建机理

由于String在Java世界中使用过于频繁,Java为了避免在一个系统中产生大量的String对象,引入了字符串常量池。其运行机制是:创建一个字符串时,首先检查池中是否有值相同的字符串对象,如果有则不需要创建直接从池中刚查找到的对象引用;如果没有则新建字符串对象,返回对象引用,并且将新创建的对象放入池中。但是,通过new方法创建的String对象是不检查字符串池的,而是直接在堆区或栈区创建一个新的对象,也不会把对象放入池中。上述原则只适用于通过直接量给String对象引用赋值的情况。博客

举例:String str1 = "123"; //通过直接量赋值方式,放入字符串常量池
String str2 = new String(“123”);//通过new方式赋值方式,不放入字符串常量池

注意:String提供了inter()方法。调用该方法时,如果常量池中包括了一个等于此String对象的字符串(由equals方法确定),则返回池中的字符串。否则,将此String对象添加到池中,并且返回此池中对象的引用。

三. StringBuffer/StringBuilder

StringBuffer和StringBuilder都实现了AbstractStringBuilder抽象类,拥有几乎一致对外提供的调用接口;其底层在内存中的存储方式与String相同,都是以一个有序的字符序列(char类型的数组)进行存储,不同点是StringBuffer/StringBuilder对象的值是可以改变的,并且值改变以后,对象引用不会发生改变;两者对象在构造过程中,首先按照默认大小申请一个字符数组,由于会不断加入新数据,当超过默认大小后,会创建一个更大的数组,并将原先的数组内容复制过来,再丢弃旧的数组。因此,对于较大对象的扩容会涉及大量的内存复制操作,如果能够预先评估大小,可提升性能。

四. String类的考点分析

  • 通过 String 和相关类,考察基本的线程安全设计与实现,各种基础编程实践。
  • 考察 JVM 对象缓存机制的理解以及如何良好地使用。
  • 考察 JVM 优化 Java 代码的一些技巧。
  • String 相关类的演进,比如 Java 9 中实现的巨大...

五. String技术点深入分析

5.1 String类是典型的Immutable类

是典型的 Immutable 类,被声明成为 final class,所有属性也都是 final 的。也由于它的不可变,类似拼接、裁剪字符串等动作,都会产生新的 String 对象。

5.2 字符串设计和实现考量

  • String 是 Immutable 类的典型实现,原生的保证了基础线程安全,因为你无法对它内部数据进行任何修改,这种便利甚至体现在拷贝构造函数中,由于不可变,Immutable 对象在拷贝时不需要额外复制数据。
  • 为了实现修改字符序列的目的,StringBuffer 和 StringBuilder 底层都是利用可修改的(char,JDK 9 以后是 byte)数组,二者都继承了 AbstractStringBuilder,里面包含了基本操作,区别仅在于最终的方法是否加了 synchronized。
  • 这个内部数组应该创建成多大的呢?如果太小,拼接的时候可能要重新创建足够大的数组;如果太大,又会浪费空间。目前的实现是,构建时初始字符串长度加 16(这意味着,如果没有构建对象时输入最初的字符串,那么初始值就是 16)。我们如果确定拼接会发生非常多次,而且大概是可预计的,那么就可以指定合适的大小,避免很多次扩容的开销。扩容会产生多重开销,因为要抛弃原有数组,创建新的(可以简单认为是倍数)数组,还要进行arraycopy。

5.3 字符串缓存

  • String 在 Java 6 以后提供了 intern()方法,目的是提示 JVM 把相应字符串缓存起来,以备重复使用。在我们创建字符串对象并调用 intern() 方法的时候,如果已经有缓存的字符串,就会返回缓存里的实例,否则将其缓存起来。
  • 在后续版本中,这个缓存被放置在堆中,这样就极大避免了永久代占满的问题,甚至永久代在 JDK 8 中被 MetaSpace(元数据区)替代了。而且,默认缓存大小也在不断地扩大中,从最初的 1009,到 7u40 以后被修改为 60013。

六. String不可变的好处

6.1 可以缓存 hash 值 博客

因为 String 的 hash 值经常被使用,例如 String 用做 HashMap 的 key。不可变的特性可以使得 hash 值也不可变,因此只需要进行一次计算。

6.2 String Pool 的需要

如果一个String对象已经被创建过了,那么就会从 String Pool 中取得引用。只有 String 是不可变的,才可能使用 String Pool。

6.3 安全性

String 经常作为参数,String 不可变性可以保证参数不可变。例如在作为网络连接参数的情况下如果 String 是可变的,那么在网络连接过程中,String 被改变,改变 String 对象的那一方以为现在连接的是其它主机,而实际情况却不一定是。

6.4 线程安全

String 不可变性天生具备线程安全,可以在多个线程中安全地使用。

原文地址:https://blog.51cto.com/14637764/2462220

时间: 2024-10-14 04:00:41

《Java架构筑基》从Java基础讲起——String类深入理解的相关文章

《Java架构筑基》从Java基础讲起——常见的API方法

1. Object类 1.1 Object有哪些公用方法? a.方法equals测试的是两个对象是否相等 b.方法clone进行对象拷贝[问题:是浅拷贝还是深拷贝?] c.方法getClass返回和当前对象相关的Class对象 d.方法notify,notifyall,wait都是用来对给定对象进行线程同步的 2. String类 2.1 String类的一些特性 String 类代表字符串.Java 程序中的所有字符串字面值(如 "abc" )都作为此类的实例实现. 字符串是常量:它

【Java 进阶篇】【第一课】String类

引用 String类包含在java.lang包中.这个包会在Java启动的时候自动import,所以可以当做一个内置类(built-in class).我们不需要显式的使用import引入String类. 创建 String类是唯一一个不需要new关键字来创建对象的类.使用的时候需要注意 String s = "Hello World!"; System.out.println(s); 操作 可以用+实现字符串的连接(concatenate),比如: "abc" +

Java 中StringBuffer与StringBuilder区别(转)及String类的一些基本操作代码

String 字符串常量StringBuffer 字符串变量(线程安全)  多个线程访问时,不会产生问题(Synchronized)StringBuilder 字符串变量(非线程安全) 多个线程访问时可能会产生问题 简要的说, String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象, 因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不

《Java架构筑基》从Java基础讲起——基本数据类型

1. 基本类型有哪些 Java定义了八种基本数据类型:byte,short,int,long,char,float,double,boolean. 基本数据类型也称为简单类型,这些类型可以分为四组: 整型.包括byte,short,int,long.用于表示有符号整数. 浮点型.包括float,double.用于表示带小数位的数字. 字符型.包括char.用于表示字符集中的符号. 布尔型.包括boolean.用于表示true/false值. 开发者可以直接使用这些类型,也可以使用它们来构造数组以

《Java架构筑基》从Java基础讲起——深入理解Static

1. static的作用和特点 可以用来修饰:成员变量,成员方法,代码块,内部类等.具体如下所示 修饰成员变量和成员方法 被 static 修饰的成员属于类,不属于单个这个类的某个对象,被类中所有对象共享,可以并且建议通过类名调用. 被static 声明的成员变量属于静态成员变量,静态变量存放在Java内存区域的方法区. 静态代码块 静态代码块定义在类中方法外,静态代码块在非静态代码块之前执行(静态代码块->非静态代码块->构造方法) 该类不管创建多少对象,静态代码块只执行一次. 静态内部类(

《Java架构筑基》从Java基础讲起——泛型基础

一.泛型的概述 1.1 泛型由来 我们的集合可以存储多种数据类型的元素,那么在存储的时候没有任何问题,但是在获取元素,并向下转型的时候,可能会存在一个错误,而这个错误就是ClassCastException . 很显然,集合的这种可以存储多种数据类型的元素的这个特点,不怎么友好 , 程序存在一些安全隐患,那么为了出来这种安全隐患,我们应该限定一个集合存储元素的数据类型,我们只让他存储统一中数据类型的元素,那么在做向下转型的是就不会存在这种安全隐患了. 怎么限定集合只能给我存储同一种数据类型的元素

《Java架构筑基》从Java基础讲起——深入理解Finial

一.final关键字概述 1. 为什么会有final 由于继承中有一个方法重写的现象,而有时候我们不想让子类去重写父类的方法.这对这种情况java就给我们提供了一个关键字: final 2. final概述 final关键字是最终的意思,可以修饰类,变量,成员方法. 3. final修饰特点 修饰类: 被修饰类不能被继承 修饰方法: 被修饰的方法不能被重写 修饰变量: 被修饰的变量不能被重新赋值,因为这个量其实是一个常量 4. final关键字修饰局部变量 基本类型,是值不能被改变 引用类型,是

《Java架构筑基》从Java基础讲起——泛型的使用

一. 泛型的使用 1. 泛型类的概述及使用 A:泛型类概述: 把泛型定义在类上 B:定义格式: public class 类名<泛型类型1,-> C:注意事项: 泛型类型必须是引用类型 2. 泛型方法的概述和使用 A:泛型方法概述: 把泛型定义在方法上 B:定义格式: public <泛型类型> 返回类型 方法名(泛型类型 变量名) public <T> void show(T t) { } 所谓泛型方法,就是在声明方法时定义一个或多个类型形参. 泛型方法的用法格式如下

《Java架构筑基》从Java基础讲起——关键字汇总

1. 常见的关键字 如果还有没有写上的,麻烦小伙伴告知一声-- 用于定义数据类型的关键字 class interface byte short int long float double char boolean void 用于定义数据类型值的关键字 true false null 用于定义流程控制的关键字 if else switch case default while do for break continue return 用于定义访问权限修饰符的关键字 private protecte