Java——String类(二)

字符串常量池: 字符串常量池在JVM中是StringTable类,实际是一个固定大小的HashTable。为了使程序的运行速度更快、更节省内存,Java为8种基本数据类型和String类都提供了常量池。
StringBuilder和StringBuffer: 当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。

一、字符串常量池


字符串常量池是一种池化技术的体现。常见的池化技术有:数据库连接池、线程池、内存池等。
为了节省存储空间,提高程序的运行效率,Java中引入了:

  1. Class文件常量池: 每个.Java源文件编译后生成.Class文件中会保存当前类中的字面常量以及符号信息
  2. 运行时常量池: 在.Class文件被加载时,.Class文件中的常量池被加载到内存中称为运行时常量池,运行时常量池每个类都有一份。
  3. 字符串常量池

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

JDK版本字符串常量池位置大小
Java6方法区固定大小:1009
Java7可设置,没有大小限制,默认大小:60013
Java8可设置,有范围限制,最小是1009
  1. 字符串常量池只有一份,是全局共享。
  2. 字符串常量池中的元素会随着程序运行不断增多。
  3. 当类加载时,字节码文件中的常量池也被加载到JVM中,称为运行时常量池,同时会将其中的字符串常量保存在字符串常量池中。
  4. 字符串常量池中的内容,一部分来自运行时常量池,一部分来自程序动态添加。

1、字符串的创建

1.1 字符串常量进行赋值

    public static void main(String[] args) {
        String s1 = "CSDN";
        String s2 = "CSDN";
        System.out.println(s1 == s2); //运行结果:true
    }

在这里插入图片描述
在这里插入图片描述
** 注:** 在字节码文件加载时,“CSDN”的常量串就已经创建好了,并保存在字符串常量池中。当使用String s1 = “CSDN”;创建对象时,在字符串常量池中找,找到需要的字符串便将字符串的引用赋给s1。

1.2 通过new创建String类对象。

    public static void main(String[] args) {
        String s1 = "CSDN";
        String s2 = "CSDN";
        String s3 = new String("Hello");
        String s4 = new String("CSDN");
    }

在这里插入图片描述
new: 在堆上开辟String对象大小的空间,并将对象中成员初始化为0,然后将空间首地址压栈。
dup: 将栈顶元素,String对象空间的首地址拷贝一份到栈顶备用。
ldc: 将常量池中的“Hello”对象引用拷贝到栈顶。
invokespecial: 调用String类构造方法,取走栈顶2个元素。
astore2: 用栈顶元素给s2赋值。
在这里插入图片描述
注: 使用new创建的对象都是唯一的。并且通过常量串创建String对象效率更高,更节省空间。

1.3 intern方法

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

//未使用intern方法
    public static void main(String[] args) {
        char[] ch = new char[]{'a', 'b', 'c'};
         // s1对象并不在常量池中
        String s1 = new String(ch);
        String s2 = "abc";
        System.out.println(s1 == s2);
    }

在这里插入图片描述

//使用intern方法
    public static void main(String[] args) {
        char[] ch = new char[]{'a', 'b', 'c'};
        String s1 = new String(ch);
        // s1.intern();调用之后,会将s1对象的引用放入到常量池中
        s1.intern(); 
        String s2 = "abc"; 
        System.out.println(s1 == s2);
    }

在这里插入图片描述

二、String类不可修改


String是一种不可变对象. 字符串中的内容是不可改变。

  1. String类在设计的时候就设计为不可修改的。
    在这里插入图片描述
    ① String类被final修饰,表明不能被继承。
    ② value被final修饰,表明自身的值不能改变,即不能引用其它字符数组,但是其引用空间中的内容可以修改。
  2. 所有涉及到可能修改字符串内容的操作都是创建一个新对象,操作的都是新对象。

注:String设计成不可变的原因

  • 方便字符串对象池。如果 String 可变, 那么对象池就需要考虑何时深拷贝字符串的问题。
  • 不可变对象是线程安全的。
  • 不可变对象更方便缓存 hash code, 作为 key 时可以更高效的保存到 HashMap 中。

1、修改字符串

尽量避免对字符串进行修改,因为String类是不可修改的,所有的修改操作都是会创建新的对象,效率低下。
在这里插入图片描述
字节码文件的第19行有StringBuilder.toString方法,方法源码如下:
在这里插入图片描述
可以看出是新建了一个字符串。
下面用一个示例来看下这个效率有多慢:

    public static void main(String[] args) {
        long start = System.currentTimeMillis();

        //对String进行修改
        String s = "";
        for(int i = 0; i < 100000; ++i){
            s += i;
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start);

        //对StringBuffer进行修改
        start = System.currentTimeMillis();
        StringBuffer sbf = new StringBuffer("");
        for(int i = 0; i < 100000; ++i){
            sbf.append(i);
        }
        end = System.currentTimeMillis();
        System.out.println(end - start);

        //对StringBuilder进行修改
        start = System.currentTimeMillis();
        StringBuilder sbd = new StringBuilder();
        for(int i = 0; i < 100000; ++i){
            sbd.append(i);
        }
        end = System.currentTimeMillis();
        System.out.println(end - start);
    }

在这里插入图片描述

三、StringBuffer 和 StringBuilder 类


由于String的不可更改特性,为了方便字符串的修改,Java中又提供StringBuilder和StringBuffer类。这两个类大部分功能是相同的。

  • 在使用 StringBuffer 类时,每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,所以如果需要对字符串进行修改推荐使用 StringBuffer。
  • StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。
  • StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder(10);
        sb.append("CSDN..");
        System.out.println(sb);
        //在sb字符串
        sb.append("!");
        System.out.println(sb);
        //在sb字符串6的位置后添加Java
        sb.insert(6, "Java");
        System.out.println(sb);
        //删除5到8位的字符
        sb.delete(5,8);
        System.out.println(sb);
    }

在这里插入图片描述
介绍一些StringBuilder类的方法

方法说明
StringBuilder append(String str)在字符串尾部追加指定的序列,相当于String的+=
char charAt(int index)获取index位置的字符
int length()获取字符串的长度
int capacity()获取底层保存字符串空间总的大小
void ensureCapacity(int mininmumCapacity)扩容
void setCharAt(int index, char ch)将index位置的字符设置为ch
int indexOf(String str)返回str第一次出现的位置
int indexOf(String str, int fromIndex)从fromIndex位置开始查找str第一次出现的位置
StringBuff insert(int offset, String str)在offset位置插入:八种基类类型 & String类型 & Object类型数据
StringBuffer deleteCharAt(int index)删除index位置字符
StringBuffer delete(int start, int end)删除[start, end)区间内的字符
StringBuffer replace(int start, int end, String str)将[start, end)位置的字符替换为str
String substring(int start)从start开始一直到末尾的字符以String的方式返回
String substring(int start,int end)将[start, end)范围内的字符以String的方式返回
StringBuffer reverse()反转字符串
String toString()将所有字符按照String的方式返回

示例:

    public static void main(String[] args) {
        StringBuilder sb1 = new StringBuilder("hello");
        StringBuilder sb2 = sb1;
        // 追加:即尾插-->字符、字符串、整形数字
        sb1.append(' '); // hello
        sb1.append("world"); // hello world
        sb1.append(123); // hello world123
        System.out.println(sb1); // hello world123
        System.out.println(sb1 == sb2); // true
        System.out.println(sb1.charAt(0)); // 获取0号位上的字符 h
        System.out.println(sb1.length()); // 获取字符串的有效长度14
        System.out.println(sb1.capacity()); // 获取底层数组的总大小
        sb1.setCharAt(0, 'H'); // 设置任意位置的字符 Hello world123
        sb1.insert(0, "Hello world!!!"); // Hello world!!!Hello world123
        System.out.println(sb1);
        System.out.println(sb1.indexOf("Hello")); // 获取Hello第一次出现的位置
        System.out.println(sb1.lastIndexOf("hello")); // 获取hello最后一次出现的位置
        sb1.deleteCharAt(0); // 删除首字符
        sb1.delete(0,5); // 删除[0, 5)范围内的字符
        String str = sb1.substring(0, 5); // 截取[0, 5)区间中的字符以String的方式返回
        System.out.println(str);
        sb1.reverse(); // 字符串逆转
        str = sb1.toString(); // 将StringBuffer以String的方式返回
        System.out.println(str);
    }

在这里插入图片描述
String和StringBuilder最大的区别在于String的内容无法修改,而StringBuilder的内容可以修改。
注: String和StringBuilder类不能直接转换,如需转换,可以使用如下方法:

  • String变为StringBuilder: 利用StringBuilder的构造方法或append()方法。
  • StringBuilder变为String: 调用toString()方法。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值