String 类基础学习

本文详细解析了Java中的String类,包括构造方法、不可变性带来的好处、字符串常量池的工作机制以及相关方法。重点阐述了String对象的创建、内存分配以及如何通过intern()方法利用字符串池。此外,还探讨了字符串比较和操作的方法,如equals()、equalsIgnoreCase()、substring()等。
摘要由CSDN通过智能技术生成

1.String 类常见的构造方法:

方法名说明
public String()创建一个空白字符串对象,不含有任何内容
public String(char[] char)根据字符数组的内容,来创建字符串对象
public String(String str)根据传入的字符串内容,来创建字符串对象
String s = “小雨”直接赋值的方法创建字符串对象,内容就是 小雨

String被声明为final,因此它不可被继承,(Integer等包装类也不能被继承).
在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来标识使用了哪种编码. value被声明为final,这意味着value数组初始化之后就不能引用其它数组,并且String内部没有改变value数组的方法,因此可以保证String不可变.

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字符串不可变的好处
(1), 可以缓存hash值, 因为String的hash值经常被使用,例如 String 用做 HashMap的 key,不可变的特性可以使得hash值也不可变,因此只需要进行以此计算.
(2),String Pool 的需要, 如果一个String 对象已经被创建过了,那么就会从String Pool 中取得引用. 只有String Pool是不可变的, 才可能使用String Pool.
(3), 安全性, 字符串被广泛用作许多java类的参数,例如网络连接、打开文件等。如果字符串不是不变的,则会更改连接或文件,这会导致严重的安全威胁。该方法认为它是连接到一台机器,但不是。可变字符串也可能导致反射中的安全问题,因为参数是字符串。
(4), 线程安全, String 不可变性天生具备线程安全, 可以在多个线程中安全使用,

3.构造方法能创建对象, " " 也能创建对象,有什么区别?
(1), 以 " " 方式给出的字符串,只要字符序列相同(顺序和大小写),无论在代码中出现几次,JVM都只会建立一个String对象,并在字符串常量池中维护.

String s = "小雨";
String s1 = "小雨";
***s 与 s1地址相同:***

字符串常量: 当使用双引号创建字符串对象的时候,系统会检查该字符是否在字符串常量池中存在;

(2), 通过 new 创建的字符串对象, 每一次 new 都会申请一个内存空间,虽然内容相同,但是地址值不同.

Stirng s = new String("小方");
String s1 = new Stirng("小方");
***s 与 s1地址不相同:***

4. Stirng字符串的特点

  • java 程序中所有的双引号字符串,都是Stirng 类的对象,字符串效果相当于char[]字符数组,但是底层原理都是byte[]字节数组。
  • 字符串不可变, 它们的值在创建后不能被更改.
  • 虽然 String 的值是不可变的, 但是它们可以被共享.
@Test
    void test(){

        String a = "小雨";
        String b = "小雨";
        if (a == b){                                  //true
            System.out.println("a,b地址相同!");
        }

        String a1 = "小方";
        String b1 = new String("小方");
        if (a1 == b1){                               //false
            System.out.println("a1,b1 地址相同!");
        }else {
            System.out.println("a1,b1 地址不相同!");
        }

        String a2 = "小方";
        String b2 = "小";
        String c2 = b2 + "方";
        if (a2 == c2){                                //false    
                                    当字符串之间使用 + 拼接(字符串常量和变量拼接) 的时候, 
                                    系统底层会自动创建一个StringBuilder对象,然后
                                    再调用append方法完成拼接, 拼接后,
                                    再调用其 toStirng 方法转换为String类型
            System.out.println("a2,c2 地址相同!");
        }else{
            System.out.println("a2,c2 地址不相同!");
        }

        String a3 = "方雨欣";
        String b3 = "方" + "雨" + "欣";
        if (a3 == b3) {                              //true    
                                     java存在常量优化机制,在编译的时候,
                                     就会将 "方" + "雨" + "欣" 拼接为 "方雨欣";
            System.out.println("a3,b3 地址相同!");
        }else{
            System.out.println("a3,b3 地址不相同!");
        }

5.字符串常用方法
str.equals(str); 比较字符串内容是否相同;
str.equalsIgnoreCase(str); 忽略大小写比较字符串内容是否相同;
charAt(); 返回指定索引处的char的值,字符串的索引也是从0开始.
length(); 获取字符串的长度.
toCharArray(); 将当前字符串拆分为字符数组并返回;
substring(int beginIndex); 从传入的索引位置处截取到末尾,得到新的字符串并返回;
substring(int beginIndex, int endIndex); 从beginIndex索引位置开始截取,截取到endIndex索引位置,得到新字符串并返回 (包括头,不包括尾)
replace(旧值, 新值); 字符串替换;
split(String str); 将传入的字符串作为规则进行切割,将切割后的内容存入字符串数组中返回

6.为什么打印String 的对象是字符串值而不是地址?
不论是哪种引用类型,直接打印对象的时候,默认调用toString方法, 如果此类没有重写Object中的toString方法,那就调用Object中的toString 方法,即打印出来的是地址:全类名+@+16进制的hashCode, 如果此类重写了toString方法,那么就打印重写的toString方法.
String重写toString方法:

public String toString() {
        return this;
    }

Object中的toString方法:

public String toString() {
     return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

7. String Pool (字符串常量池)
String Pool 字符串常量池 保存着所有字符串字面量, 这些字面量在编译时期就确定,不仅如此,还可以使用 String 的 intern() 方法在运行过程将字符串添加到String Pool 中,.

当一个字符串调用 intern() 方法时, 如果String Pool 中已经存在一个字符串和该字符串值相等(使用equals() 方法进行确定), 那么就会返回String Pool 中字符串的引用; 否则,就会在String Pool 中添加一个新的字符串, 并返回这个新字符串的引用.

String s1 = new String("aaa");
String s2 = new String("aaa");
System.out.println(s1 == s2); // false
String s3 = s1.intern();
String s4 = s2.intern();
System.out.println(s3 == s4); // true
String s5 = "bbb";
String s6 = "bbb";
System.out.println(s5 == s6); // true

在上面示例中, s1 和 s2 采用 new 的方式创建了两个不同字符串,而 s3 和 s4 是通过s1.intern() 和 s2.intern() 方法取得同一个字符串引用. intern() 首先把"aaa" 放到String Pool中, 然后返回这个字符串引用, 因此s3 和 s4 引用的是同一个字符串.
如果采用 “bbb” 这种字面量的形式创建字符串,会自动地将字符串放入 String Pool 中.

在java7之前, String Pool 被放在运行时常量池中, 它属于永久代, 而在 java7, String Pool 被移到堆中, 这是因为永久代的空间有限, 在大量使用字符串的场景下会导致 OutOfMemoryError(堆区内存溢出) 错误.

new String(“abc”);
使用这种方式一共会创建两个字符串对象( 前提是String Pool 中还没有"abc" 字符串对象).
(1), “abc” 属于字符串字面量,因此编译时期会在 String Pool 中创建一个字符串对象, 指向这个 “abc” 字符串字面量;
(2), 而使用new 的方式会在堆中创建一个字符串对象.

public String(String original) {
 this.value = original.value;
 this.hash = original.hash; }

以上是String 构造函数的源码, 可以看到, 在将一个字符串对象作为另一个字符串对象的构造函数参照时,并不会完全复制value数组内容, 而是都会指向同一个value数组.
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值