飞道的博客

java之String、StringBuffer 、 StringBuilder有什么区别?String不可变?

388人阅读  评论(0)

下篇👇
Java比较器之Comparable和Comparator
上篇👇
面向对象编程之继承、多态、封装、抽象类、接口、包-下(实战图书管理系统)

String类

String类:代表字符串,Java 程序中的所有字符串字面值(如 “abc” )都作 为此类的实例实现。

String的定义

/**
 * user:ypc;
 * date:2021-04-24;
 * time: 19:21;
 */
public class StringTest {
   
    public static void main(String[] args) {
   
        String str1 = "abc";//方式一
        String str2 = new String("abc");//方式二
        char [] chars = {
   'a','b','c'};
        String str3 = chars.toString();//也可以这样定义
    }
}

==和equals()方法?

如下语句1、2、3、4、5、6分别会输出什么呢?

/**
* user:ypc;
* date:2021-04-24;
* time: 19:21;
*/
public class StringTest {
   
   public static void main(String[] args) {
   
       String str1 = "abc";//方式一
       String str2 = new String("abc");//方式二
       char [] chars = {
   'a','b','c'};
       String str3 = chars.toString();//也可以这样定义
       String a = "hello";
       String b = "hello";
       System.out.println(a==b);//语句1
       System.out.println(a.equals(b));//语句2
       String c = new String("hello");
       String d = new String("hello");
       System.out.println(c==d);//语句3
       System.out.println(c.equals(d));//语句4
       System.out.println(a==c);//语句5
       System.out.println(a.equals(c));//语句6
   }
}

答案是
true
true
false
true
false
true

原因:


对于 String a = “hello”;
String b = “hello”;
a和b指向同一个对象,所以a= =b是true,而equals()方法String 类中重写了 equals() 方法用于比较两个字符串的内容是否相等,即值相等就为true。

对于
String c = new String(“hello”);
String d = new String(“hello”);如下new在堆中开辟了空间,而字符串在字符串常量池中只有一个相同的。所以c= =d为false;c.equals(d)只会比较值,所以为true
a==c为false因为他们指向不同的对象
a.equals(c )只会比较值,所以为true
注意关键词new,new就会在堆上开辟一块内存。

字符串常量池

🕵️‍♂️ 对象池是面向对象编程中一种普遍使用的技术,因为JVM构造和管理对象是一项费力的事情,如果一个对象满足一定的可重用性,重复利用而不是每次都构建相同功能(或满足equals验证)的新对象,无疑是划算的。字符串常量池就是一个字符串对象池,因为字符串对象的不可变性,所以又称为字符串常量池。JVM设计者是通过统计实践中字符串的重复利用率来设计字符串常量池的,所以说应该是科学的。但是常量池的大小是有限制的(1099),超过这个值就会出现性能下降的问题,JDK1.7之后可以通过-XX:StringTableSize=1099参数设置该值。

🕵️‍♂️ JDK1.6及之前的版本中,字符串常量池存放在Perm(永久代)区中,并且存放的是完整的字符串对象。该区域是一个独立的内存区,还存储了类信息和方法片段等内容,使用不当很容易造成OutOfMemeroyError(PermSize)异常。

🕵️‍♂️ JDK1.7将字符串常量池移到堆内存(Heap)里,并且常量池里存放的是在堆中创建的对象的引用。

🕵️‍♂️ JDK1.8直接移除Perm区,转而使用MetaSpace(元数据区),其实Perm和MetaSapce都是《java虚拟机规范》中method area(方法区)的具体实现。JDK1.8中,字符串常量池依然是在堆中实现,应该和JDK1.8没有区别。

🕵️‍♂️ 将一个字符串添加到字符串常量池中有两种方式:一是直接定义一个类似于"abc"形式的字符串字面量,编译器在编译期就会在class字节码中创建静态常量池,并将这些常量放进去。在虚拟机加载该字节码的时候,如果运行时字符串常量池(动态常量池)中不存在该字符串,就在堆中创建该字符串对象,并将该对象的引用放到池中(JDK1.6及以前是直接在池中创建对象),如果已存在于池中,则直接返回该对象的引用。二是通过调用对象本身的intern()函数,JDK1.7中该操作将对象引用放到常量池中并返回该引用,JDK1.6及以前则是在常量池中创建新的对象,并返回新对象的引用。

String字符串不可变

来看👇

结果和我们预想的一样,那么String是可变的吗?不是的,内存变化如下👇

也就是说,不是 String 对象本身发生改变, 而是 str 引用到了其他的对象.“hello"本身还是"hello”。这就是字符串不可变。
String 类的内部实现也是基于 char[] 来实现的, 但是 String 类并没有提供 set 方法之类的来修改内部的字符数组.

如何修改字符串?

🚒借助原字符串, 创建新的字符串如下👇

str是不是变了啊?😁
🚒使用 “反射” 这样的操作可以破坏封装, 访问一个类内部的 private 成员.👇

将hello改为了Wollo!😭。

字符串不可变的优点

1.方便实现字符串对象池. 如果 String 可变, 那么对象池就需要考虑何时深拷贝字符串的问题了.
2.不可变对象是线程安全的.
3.不可变对象更方便缓存 hash code, 作为 key 时可以更高效的保存到 HashMap 中
🎈只有当字符串是不可变的,字符串池才有可能实现。字符串池的实现可以在运行时节约很多heap空间,因为不同的字符串变量都指向池中的同一个字符串。但如果字符串是可变的,那么String interning将不能实现(译者注:String interning是指对不同的字符串仅仅只保存一个,即不会保存多个相同的字符串。),因为这样的话,如果变量改变了它的值,那么其它指向这个值的变量的值也会一起改变。
🎈如果字符串是可变的,那么会引起很严重的安全问题。譬如,数据库的用户名、密码都是以字符串的形式传入来获得数据库的连接,或者在socket编程中,主机名和端口都是以字符串的形式传入。因为字符串是不可变的,所以它的值是不可改变的,否则黑客们可以钻到空子,改变字符串指向的对象的值,造成安全漏洞。
因为字符串是不可变的,所以是多线程安全的,同一个字符串实例可以被多个线程共享。这样便不用因为线程安全问题而使用同步。字符串自己便是线程安全的。
🎈类加载器要用到字符串,不可变性提供了安全性,以便正确的类被加载。譬如你想加载java.sql.Connection类,而这个值被改成了myhacked.Connection,那么会对你的数据库造成不可知的破坏。
因为字符串是不可变的,所以在它创建的时候hashcode就被缓存了,不需要重新计算。这就使得字符串很适合作为Map中的键,字符串的处理速度要快过其它的键对象。这就是HashMap中的键往往都使用字符串。

字符与字符串

字符串内部包含一个字符数组,String 可以和 char[] 相互转换.常用方法👇(想要Java官方文档关注我、然后私我啊,免费分享中文翻译版)

使用👇(前面将hello改为了Wollo,所以输出了Wollo😥)

 char [] chars = {
   'a','b','c','d'};//String(char[]value)方法
        String str2 = new String(chars);
        System.out.println(str2);
        String str3 = new String(chars,1,2);//Str
        System.out.println(str3);// ing(int[] codePoints, int offset, int count) 方法
        //分配一个新的 String ,其中包含字符数组参数的子阵列中的字符
        String str4 = new String("hello");
        char value1= str4.charAt(1);//返回 char指定索引处的值。
        System.out.println(value1);
        char[] chars1 = str4.toCharArray();//将此字符串转换为新的字符数组。
        System.out.println(chars1);

字节与字符串(有坑)

字节常用于数据传输以及编码转换的处理之中,String 也能方便的和 byte[] 相互转换.


        byte [] bytes = {
   1,3,2,4,5};

        String str2 = new  String(bytes);//将字节数组转为字符串
        byte [] bytes1 = str2.getBytes();//字符串转字节数组
        for (int i =0;i<str2.length();i++){
   
            System.out.print(bytes1[i]+" ");
        }
        System.out.println(new String(bytes1));
        String str3 = new String(bytes,1,2);
        System.out.println(str2);
        System.out.println(str3);


打印出来是这样的,全部乱码。这是因为1,2…对应字符找不到,idea 不能显示全。

String常用函数

🎁字符串比较
官方:

🚑boolean equals(Object anObject)
将此字符串与指定对象进行比较。
🚑boolean equalsIgnoreCase(String anotherString)
将此 String与其他 String比较,忽略大小写。

🚑int compareTo(String anotherString)
按字典顺序比较两个字符串。
🚑int compareToIgnoreCase(String str)
按字典顺序比较两个字符串,忽略大小写。
a. compareToIgnoreCase(b)a>b,返回正数,a=b返回0,a<b返回负数

        String a = "abc";
        String b = "bac";
        String c = "ABC";
        System.out.println(a.equals(b));
        System.out.println(a.equalsIgnoreCase(c));
        System.out.println(a.compareTo(b));
        System.out.println(a.compareToIgnoreCase(c));


🎁字符串查找




 String a = "abc";
        String b = "bac";
        String c = "ABC";
        System.out.println(a.equals(b));
        System.out.println(a.equalsIgnoreCase(c));
        System.out.println(a.compareTo(b));
        System.out.println(a.compareToIgnoreCase(c));

        System.out.println(a.contains("f"));
        System.out.println(a.startsWith("e"));
        System.out.println(a.startsWith("c",2));
        System.out.println(a.indexOf("a"));
        System.out.println(b.endsWith(c));


🎁字符串替换

将与字面目标序列匹配的字符串的每个子字符串替换为指定的字面替换序列。
👕String replaceAll(String regex, String replacement)
用给定的替换替换与给定的 regular expression匹配的此字符串的每个子字符串。
👕String replaceFirst(String regex, String replacement)
用给定的替换替换与给定的 regular expression匹配的此字符串的第一个子字符串。

  String a = "abc";
        String b = "bac";
        String c = "ABC";
        System.out.println(a.replaceAll("a","b"));
        System.out.println(a.replaceFirst("b","a"));


🎁字符串拆分

✈String[] split(String regex)
将此字符串分割为给定的 regular expression的匹配。
✈String[] split(String regex, int limit)
将这个字符串拆分为给定的 regular expression的匹配。

String a = "ab,cchichicjic";
        String b = "b,a,c";
//        String c = "ABC";
        String [] c = a.split("c");
        String [] d = b.split(",",3);
        for (String s : c) {
   
            System.out.print(s+" ");
        }
        System.out.println();
        for (String s : d) {
   
            System.out.print(s+" ");
        }


🎁字符串其它操作
字符串截取
🌮String substring(int beginIndex)
返回一个字符串,该字符串是此字符串的子字符串。
🌮String substring(int beginIndex, int endIndex)
返回一个字符串,该字符串是此字符串的子字符串。


删除空格、转为大写、小写

🌮String toUpperCase()
将所有在此字符 String使用默认语言环境的规则大写。
🌮String toUpperCase(Locale locale)
将所有在此字符 String使用给定的规则,大写 Locale 。
🌮String trim()
返回一个字符串,其值为此字符串,并删除任何前导和尾随空格。
字符串入池、连接、获取长度、判空


 String a = "ab,ccAhichAicjCic";
        String b = "b,a,AcDFG,OP     CJ";
        String c ="";
        System.out.println(b.trim());
        System.out.println(c.isEmpty());
        System.out.println(b.length());
        System.out.println(a.toUpperCase());
        System.out.println(b.toLowerCase());

StringBuffer类、StringBuilder类

当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。
和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。
在使用 StringBuffer 类时,每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,所以如果需要对字符串进行修改推荐使用 StringBuffer。
StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。
由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。StringBuffer很多方法都是synchronized 修饰的。

  //StringBuilder
        StringBuilder sb = new StringBuilder(10);
        sb.append("abcd");
        System.out.println(sb);
        sb.append("!");
        System.out.println(sb);
        sb.insert(5, "Java");
        System.out.println(sb);
        sb.delete(5,6);
        System.out.println(sb);
        //StringBuffer
        StringBuffer sBuffer = new StringBuffer("ok");
        sBuffer.append("hello");
        sBuffer.append("you");
        sBuffer.append("ni");
        System.out.println(sBuffer);


StringBuffer 类支持的主要方法(官方文档):



StringBuffer 类支持的主要方法(官方文档):

总结String、StringBuffer 、StringBuilder


String的内容不可修改,StringBuffer与StringBuilder的内容可以修改.
StringBuffer与StringBuilder大部分功能是相似的
StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操作

欢迎指正,相互关注啊😄


转载:https://blog.csdn.net/qq_45859087/article/details/116117958
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场