String类的构造方法

 字符串常量池

在Java程序中,类似于:1, 2, 3,3.14,“hello”等字面类型的常量经常频繁使用,为了使程序的运行速度更快、更节省内存,Java为8种基本数据类型和String类都提供了常量池。

三级常量池架构

为了节省存储空间以及程序的运行效率,Java中引入了:
1. Class文件常量池:每个.Java源文件编译后生成.Class文件中会保存当前类中的字面常量以及符号信息

  • 存储内容:字面量(Literal)和符号引用(Symbolic References)

  • 生成时机:编译阶段

  • 特点:包含类、方法、字段等元数据信息

  • 示例:CONSTANT_Utf8_infoCONSTANT_Integer_info等结构

2. 运行时常量池:在.Class文件被加载时,.Class文件中的常量池被加载到内存中称为运行时常量池,运行时常量池每个类都有一份

  • 存储位置:方法区(元空间)

  • 加载过程:类加载时从Class文件常量池转换而来

  • 特点:动态性(支持运行期间添加常量)

  • 重要方法:String#intern()

3. 字符串常量池:字符串常量池在JVM中是StringTable类,实际是一个固定大小的HashTable(一种高效用来进行查找的数据结构),存储内容是所有字符串字面量和显式intern的字符串

不同JDK版本下字符串常量池的位置以及默认大小是不同的:

字符串常量池是一个特殊的内存区域,用来存储被多个对象共享的字符串。存储双引号引起来的字符串,存的是字符串的常量值。

  • 看常量池中是否存在当前字符串
  • 不存在,Java 会将其放入池中并返回引用。
  • 存在,直接返回池中的引用。
        String s1 = "hello";
        String s2 = "hello";
        System.out.println(s1==s2);//true

优点:提高内存效率,避免重复分配内存

String 对象创建

1. 直接使用字符串常量进行赋值

  • 首次创建

    1. JVM检查字符串常量池是否存在"hello"

    2. 若不存在,在池中创建新字符串对象

    3. 返回常量池对象的引用

  • 再次创建

    1. 直接复用常量池中的现有对象

    2. 返回相同引用

s1s2 引用的是同一个 String 对象,所以它们指向相同的内存地址。

2.  通过new创建String类对象

  • 执行步骤

    1. 强制创建堆对象:无论常量池是否存在,必定在堆中新建String对象

    2. 字符数组处理

      • 若常量池已有"hello":共享字符数组(this.value = original.value

      • 若不存在:先在池创建,再复制到堆对象

    3. 哈希值继承:复制原字符串的哈希值(this.hash = original.hash

由于 s3 和 s4 引用不同的String 对象,s3 == s4 返回 false

缺点:每次都需要为字符串分配新的内存

 

  • this.value = original.value;:将原始字符串的 value 字段(字符数组)复制到新对象
  • this.hash = original.hash;:这是将 original 字符串的 hash 字段的值复制到新对象的 hash 字段。hash 字段通常是字符串的哈希值,用来优化字符串的比较和查找。
  • 虽然字符数组可能共享,但new操作必定创建新对象

  • 不同String对象的value可能指向同一数组,但对象本身不同

这个构造函数的作用是创建一个新 String 对象,它与 original 对象拥有相同的字符数组和哈希值。因此,调用这个构造函数的结果是生成一个新的字符串对象,它的内容和 original 相同。

        String s1 = "hello"; 
        String s2 = "hello";
        String s3=new String("hello");
        String s4=new String("hello");
        System.out.println(s1==s2);//true
        System.out.println(s3==s4);//false
        System.out.println(s1==s3);//false

3. 使用字符数组构造字符串

        char[] ch={'h','e','l','l','o'};
        String s1 = new String(ch);
        //s1.intern();//调用之后,会将s1对象的引用放入到常量池中
        String s2 = "hello";
        System.out.println(s1==s2);//false

intern 是一个native方法(Native方法指:底层使用C++实现的,看不到其实现的源代码),该方法的作用是手动将创建的String对象添加到常量池中

    • original:需要复制的原始 char 数组。
    • newLength:新数组的长度,决定了复制后的数组大小。
  1. char[] copy = new char[newLength];
    创建一个新的 char 数组,长度为 newLength。这个数组将存放源数组复制后的数据。

  2. System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
    这行代码使用 System.arraycopy 方法将 original 数组的内容复制到 copy 数组中。具体地:

    • original:源数组。
    • 0:源数组复制的起始位置(从 original 数组的第 0 个元素开始)。
    • copy:目标数组。
    • 0:目标数组复制的起始位置(将从 copy 数组的第 0 个元素开始)。
    • Math.min(original.length, newLength):复制的元素数量是原数组长度和新数组长度中较小的一个。这是为了避免超出目标数组的边界。

    System.arraycopy 方法会从原数组 original 中复制数据到新数组 copy,最多复制 Math.min(original.length, newLength) 个元素。

  3. return copy;
    最后,返回新复制的数组 copy

总结:

该方法的作用是将原数组 original 的内容复制到一个长度为 newLength 的新数组中:

  • 如果 newLength 大于或等于原数组的长度,则会将整个原数组的内容复制到新数组。
  • 如果 newLength 小于原数组的长度,则只会复制原数组的前 newLength 个元素。

 字符串的不可变性

String 是一种不可变对象 . 字符串中的内容是不可改变。字符串不可被修改。
1. String 类在设计时就是不可改变的

2. 所有涉及到可能修改字符串内容的操作都是创建一个新对象,改变的是新对象,原字符串内容并没有被修改
注意:尽量避免直接对 String 类型对象进行修改,因为 String 类是不能修改的,所有的修改都会创建新对象,效率
非常低下。
        String s="hello";
        s+=" World";
        System.out.println(s);
底层逻辑
        //底层逻辑
        String s="hello"; //创建s对象
        StringBuilder stringBuilder = new StringBuilder(); //创建一个StringBuild对象,为stringbuilkd
        stringBuilder.append(s);//将s对象append(追加)stringBuild之后
        stringBuilder.append(" world"); //将" world" 字符串append(追加)stringBuild之后
        s = stringBuilder.toString();//调用toString方法构造一个新的String对象并赋值给s
        System.out.println(s);

String 类的构造方法

String 类的构造方法会使用传入的 String 参数来创建一个新的 String 对象。

在 IntelliJ IDEA 中,默认字符集通常是 UTF-8,默认情况下,使用平台的默认字符集进行解码。

控制字符(0 到 31 和 127)通常不可显示,不能有效地转化为可见字符。

可打印字符(32 到 126)是可以有效转化为字符串中的可见字符

  • 空格(32)
  • 数字(48-57):0-9
  • 大写字母(65-90):A-Z
  • 小写字母(97-122):a-z
  • 标点符号和其他符号(33-47, 58-64, 91-96, 123-126)

bytes:字节数组(byte[]),包含了待解码的数据。字节数组的每个元素表示一个字节,通常用于表示字符串的字节编码。

charsetCharset 对象,表示解码时使用的字符集。Charset 是 Java 中表示字符集的类,它可以通过如 StandardCharsets.UTF_8StandardCharsets.ISO_8859_1 等来指定

charsetName:字符集的名称,指定解码时使用的字符集(例如 "UTF-8", "ISO-8859-1", "GBK" 等)。

String 类的构造方法

说明
String()创建一个空字符串
String(byte[] bytes)将字节数组转换为对应的字符串
String(byte[] bytes, Charset charset)将字节数组转换为对应的字符串,并且在转换过程中使用指定的字符集 Charset 进行解码
String(byte[] bytes, int offset, int length)在指定的字节数组 bytes 中,从 offset 开始,读取 length 个字节,并将这些字节解码为对应的字符串
String(byte[] bytes, int offset, int length, Charset charset)在指定的字节数组 bytes 中,从 offset 开始,读取 length 个字节,并将这些字节使用指定的字符集 Charset 解码为对应的字符串
String(byte[] bytes, String charsetName)将字节数组转换为对应的字符串,并且在转换过程中使用指定的字符集 charsetName 进行解码
String(byte[] bytes, int offset, int length, String charsetName)在指定的字节数组 bytes 中,从 offset 开始,读取 length 个字节,并将这些字节使用指定的字符集 charsetName 解码为对应的字符串
String(char[] value)将字符数组转换为字符串
String(char[] value, int offset, int count)在指定的字符数组 value 中,从 offset 开始,读取 count 个字符,并将这些字符转换为字符串
String(int[] codePoints, int offset, int count)在指定的Unicode 码点数组(int[] codePoints) 中,从 offset 开始,读取 count 个Unicode 码点数量,并将这些Unicode 码点数量转换为字符串
String(String original)将original字符串复制一份到新的String 对象中
String(StringBuffer buffer)

StringBuffer 中的内容转换为不可变的 String 对象。即使之后修改 StringBuffer 中的内容,string 对象中的值也不会改变。

String(StringBuilder builder)StringBuilder 中的内容转换为一个不可变的 String 对象。即使之后修改 StringBuild 中的内容,string 对象中的值也不会改变。
String, StringBuilder和StringBuffer 最大的区别在于 String 的内容无法修改,而StringBuilder和StringBuffer的内容可以修改

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值