String、StringBuilder、StringBuffer的简单介绍

  • 本文主要记录
    • String的简单概述、常用构造方法、常用API、方法区和堆内存的补充
    • StringBuffer和StringBuilder的简单介绍、常用API和简单的区别

一、String

1、概述

  • String表示字符串,java程序中所有字符串文字都是String类的实例(对象)
  • 字符串是不可变的,它的值在创建后无法更改
    • 因为String的底层是使用char类型的数组存储字符串的
      • 数组的长度一旦确定,无法再更改
  • 如果多个字符串的内容完全一致,它们在内存中就会共享同一块内存
    • 相同的字符串缓存在同一块内存区域,是通过字符串常量池实现的
    • 这里指的是直接使用String xx = "xxx"的格式定义的字符串,才会放在字符串常量池
    • 如果是使用String的构造器创建出的字符串,并不是放在常量池,而是在堆内存新开辟一块空间进行存储,此对象与字符串常量池中相同内容的对象不是同一个对象
  • String是final修饰的,无法被继承

2、常用构造方法

String()

  • 创建一个String对象,表示的是空字符序列
    • 是在堆内存中新开辟一块空间

String(String str)

  • 根据传入的字符串创建一个String对象

String(byte[] bytes, String charsetName)

  • charsetName表示的是字符集编码
  • 该构造器是根据字符集编码,将字节数组转换成String对象
  • 应用场景
    • 当浏览器在发数据的时候,如果tomcat版本比较老(比如8版本之前),有些参数会乱码。这时候可以将这些字符串变成字节数组,再通过此构造器指定编码,将内容重新组合,以实现消除乱码的效果

3、常用API

char charAt(int index)

  • 返回指定索引处的char值
  • 底层源码
    • 如果index不在数组索引范围内,会跑出异常
    • 否则返回char数组索引对应的值
public char charAt(int index) {
    if ((index < 0) || (index >= value.length)) {
        throw new StringIndexOutOfBoundsException(index);
    }
    return value[index];
}

boolean contains(CharSequence s)

  • 当且仅当此字符串包含指定的char序列时返回true
  • CharSequence是String的父接口
  • 底层源码分析
    • 调用String的indexOf方法,判断传入的字符值序列在字符数组中的索引是否大于-1
      • 即判断是否存在,存在则大于-1,返回true;不存在则等于-1返回false
public boolean contains(CharSequence s) {
    return indexOf(s.toString()) > -1;
}

boolean equals(Object anObject)

  • 将此字符串与指定的对象进行比较
    • 区分大小写
  • 底层源码分析
    • 先使用==判断,如果传入的对象和当前字符串是同一个对象,返回true
      • 假设字符串的内容全部一致,分为下面几种情况
        • "abc" == "abc" --true : 因为都在字符串常量池
        • "abc" == new String("abc") --false:前者在字符串常量池,后者是在堆内存新开辟一块空间
        • new String("abc") == new String("abc") --false: 两者在堆内存的不同空间里
    • 如果使用==判断为false,再判断传入的对象是否是String类(用了instanceof关键字)
      • 如果是,则将传入对象转化为字符串,内容逐个和当前字符串比较
        • 完全一致返回true,否则返回false
      • 如果传入的对象不是String,返回false
public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

int indexOf(int ch)

  • 返回指定字符第一次出现的字符串中的索引
  • 还有几个重载方法,简单介绍一下
    • 传入的参数可以是int类型,也可以是String类型
    • 此外还可以多加一个起始索引参数,从该起始索引位置开始搜索
  • 底层代码判断索引是否越界,然后使用一定的算法遍历进行比较

int lastIndexOf(int ch)

  • 返回指定字符最后一次出现的字符串中的索引

String[] split(String regex)

  • 根据regex划分字符串,划分结果存在字符串数组中
  • 举例
String s1 = "a,b,c,d,e";
String[] arrs = s1.split(",");
for (String arr : arrs) {
    System.out.println(arr);
}

String substring(int beginIndex)和String substring(int beginIndex, int endIndex)

  • 返回一个字符串,该字符串是此字符串的子字符串
    • 一个参数
      • 从该索引位置直到数组末尾的内容作为子字符串
    • 两个参数
      • 包含起始索引,不包含结束索引的内容作为子字符串
  • 举例
String s1 = "123456789";
System.out.println(s1.substring(2)); //3456789
System.out.println(s1.substring(2,4)); //34

String toUpperCase()和String toLowerCase()

  • toUpperCase()将此字符串的所有字符转化为大写
  • toLowerCase()将此字符串的所有字符转化为小写
  • 举例
String s = "Abc123AABBccdd";
System.out.println(s.toUpperCase()); //ABC123AABBCCDD
System.out.println(s.toLowerCase()); //abc123aabbccdd

String trim()

  • 删除字符串前面和后面所有的空格
    • 中间的空格不会删除
  • 举例
String s = "   abc d  ";
System.out.println(s.trim()); //abc d

static String valueOf(不同基本数据类型的数据)

  • 将传入的基本数据类型的数据变成字符串
  • 举例
System.out.println(String.valueOf(123)); //123
System.out.println(String.valueOf(0)); //0
char[] a = {'a','b','c','d'};
System.out.println(String.valueOf(a,0,2)); //ab

boolean endsWith(String suffix)

  • 判断字符串是否以指定的后缀结尾

4、方法区和堆内存补充

a、方法区

  • 就是加载代码的内存区域,存储的是可以重复使用的资源,包括
    • 类信息(构造方法/接口定义)
    • 静态资源
    • 常量
    • 成员方法
    • 运行时常量池
  • 方法区是被所有线程共享的
    • 所有定义的方法都保存在该区域,此区域属于共享空间
  • jdk1.7之前
    • hotspot虚拟机对方法区的实现为永久代
      • hotspot是JVM虚拟机的概念
      • 永久代
        • 永久在内存中村相互的内容
        • 加载一次之后,除非程序结束,否则可以一直复用
        • 不会被GC回收
    • 运行时常量池(包括字符串常量池)存放在方法区
  • jdk1.7
    • 字符串常量池从方法区移到了堆内存
    • 运行时常量池
      • 除了字符串常量池,剩下的都还在方法区,也就是hotspot的永久代
      • 这里的永久代区别于后面堆中的永久代,方法区不是在堆中的,这里只是表明方法区在这个时候是用永久代的方式实现的
  • jdk1.8及之后
    • hotspot移除了永久代,用元空间代替
      • 元空间和永久代其实是一个概念,只是处理机制有一点不同
        • 元空间使用的是本地内存,而不是JVM内存
    • 字符串常量池还在堆内存中
    • 运行时常量池还在方法区,只不过方法区的实现从永久代变成了元空间

b、堆内存

  • 一个JVM实例只存在一个堆内存,堆内存的大小是可以调节的
  • 堆内存存储对象和数组数据
  • 堆的分类
    • 新生代
      • 刚创建的对象都存储在新生代里
      • 新生代的GC机制(java的垃圾回收处理机制)不一样,GC机制运转的很快
        • 因为刚创建的对象很可能马上就不会再使用了(没有被引用),所以GC需要经常进行内存回收处理
      • 如果新生代的对象连续15次回收都没被回收成功,说明该对象使用的时间可能会比较久,就把该对象从新生代转移到老年代
    • 老年代
      • 老年代里的对象,GC很久才访问一次,节省内存
    • 永久代
      • 永久代里面的内容不会被GC访问,即不会被回收

二、StringBuilder和StringBuffer

a、概述

  • 在进行大量的字符串拼接时,需要避免使用String直接拼接
    • 使用String拼接的本质是在堆内存中不断创建新的char数组进行数据拼接,而不用的字符串因为存在于常量池,不会被GC回收,会造成大量的内存空间浪费,甚至导致内存溢出
  • 由此引入了StringBuffer和StringBuilder,用于字符串的拼接操作
    • 两者都是可变字符序列
    • 字符内容存储在数组中,调用这两个类的构造器产生数组的初始容量都是16个字符,存满了会动态扩容
      • 它们在拼接字符串的时候,在内存中没有进行缓存,每一次产生的垃圾都会被及时回收

b、特点

  • StringBuilder的拼接效率比StringBuffer高
  • StringBuffer是线程安全,StringBuilder是线程不安全的 

c、常用API

  • append()
    • 用于拼接字符串
  • toString()
    • 用于将StringBuilder或StringBuffer对象转换成字符串
  • reserve()
    • 用于将字符串反转

# 本文写得没有特别详细,其他的知识点日后完善...

Guess you like

Origin blog.csdn.net/future_god_qr/article/details/121140002