String与StringBuffer、StringBuilder

一、String类的理解和创建对象

  1. String类的理解

      1. String对象用于保存字符串,也就是一组字符序列
      1. 字符串常量对象是用双引号括起的字符序列
      1. 字符串的字符使用Unicode字符编码,一个字符(不区分字母还是汉字)占两个字节
      1. String类实现了Serializable接口,说明字符串可以【串行化:数据可以在网络上传输】
      1. String类实现了Comparable接口,说明String对象可以进行比较
      1. String类是一个final类,不能被其他类继承
      1. String 有一个属性:private final char value[]:用于存储字符串
        value数组是一个final类型,不可以修改(就是说value的地址不能改变,value不能指向新的地址)
  2. String对象的两种创建方式

    • 1)直接赋值:
    • 2)调用构造器:(注意String类有很多的构造器,这里只是选取其中一个)
// 直接赋值:
String s="hsp";
  1. 直接赋值的方式:先从常量池看是否有"hsp"的数据空间,如果有就直接指向这个地址,如果没有,则重新创建,然后指向。s最终指向的是常量池的空间地址
//2)调用构造器:(注意String类有很多的构造器,这里只是选取其中一个)
String s2= new String("hsp");
  1. 调用构造器的方法:先在中创建空间,里面维护了value(数组)属性,指向常量池的"hsp"的空间,如果常量池没有"hsp",则重新创建,如果有,则直接通过value指向。s2最终指向的是堆中的空间地址。

在这里插入图片描述

例题1

public class Exercise_ {
    public static void main(String[] args) {
        String a="abc";
        String b="abc";
        System.out.println(a.equals(b));//true
//        a和b都指向常量池的同一个空间
        System.out.println(a==b);//true


        String a2="daDao";//指向常量池的"daDao"
        String  b2=new String("daDao");//指向堆中的
//      String类的equals重写了的,是比较字符串的内容
        System.out.println(a2.equals(b2)); //true
//        a2是指向常量池的,b2是指向堆中的空间
        System.out.println(a2==b2);//false
//        当调用intern方法时,如果常量池已经包含一个等于此String对象的字符串(用equals(Object)方法确定)
//        则返回池中的字符串,否则,将此String对象添加到池中,并返回此String对象的引用
//        解读:b.intern()方法最终返回的是常量池的地址
        System.out.println(a2==b2.intern());//true
        System.out.println(b2==b2.intern());//false


    }
}

  • 注意::当调用intern方法时,如果常量池已经包含一个等于此String对象的字符串(用equals(Object)方法确定)则返回池中的字符串,否则,将此String对象添加到池中,并返回此String对象的引用
    解读:b.intern()方法最终返回的是常量池的地址

例题2

public class Exercise02 {
    public static void main(String[] args) {
        Person person1 = new Person();
        person1.name="daDao";
        Person person2 = new Person();
        person2.name="daDao";
//        equals比较内容
        System.out.println(person1.name.equals(person2.name));//true
//        person1.name和person2.name都是指向常量池的"daDao"空间,所以true
        System.out.println(person1.name== person2.name);//true
//        "daDao"是字符串常量,不是new出来的,所以本身就是在常量池里面的
        System.out.println(person1.name=="daDao");//true

        String s1 = new String("bcde");
        String s2 = new String("bcde");
//        这里的s1和s2是指向堆中的两个不同的空间,但是它们的values数组是指向常量池的同一个空间"bcde"
        System.out.println(s1==s2); //false
    }
}

class Person{
    String name;
}

  1. 字符串的特性
    • 1)String是final类,代表不可变的字符序列
    • 2)字符串是不可变的。一个字符串对象一旦被 分配,其内容是不可变的。

例题一:

String s1="hello";
s1="No way";

这里总共创建了两个对象:
1. 首先是s1在常量池里查找hello的内存空间,如果没有找到,则创建新的空间(new),然后指向这个空间,找到了就直接指向该空间
2. 然后s1又在常量池里面找No way的内存空间,如果找到了,就直接指向No way的内存空间,没找到,则创建新的空间,然后指向这个空间

例题二:

String a="hello"+"abc";

String a="hello"+"abc";      ==》编译器会优化,其实等价于String a="helloabc"
所以这里只创建了一个对象。

例题三:

String a="hello"; //创建a对象
String b="abc";//创建b对象
String c=a+b;
 1. 先创建一个stringBuilder sb =new StringBuilder();  --->sb是在堆中,并且append是在原来字符串的基础上追加的
 2. 执行 sb.append("hello");  ==>sb.append(a);
 3. 执行sb.append("abc");  ==>sb.append(b);
 4. 执行sb.toString()     ===>   new String(value, 0, count); 
最后其实是c 指向堆中对象(String)value[] 
然后value[]  指向- ->常量池中“helloabc”

重要规则:
String c1=“ab”+“cd”; 常量相加c1指向常量池的
String a=“ab”; String b=“cd”; String c1=a+b; -->变量相加,c1 是指向堆中的

public class String02 {
    public static void main(String[] args) {
        String a="hello"; //创建a对象
        String b="abc";//创建b对象
        String c=a+b;
        String d="helloabc";
        System.out.println(c==d);//false
        String e="hello"+"abc";
        System.out.println(d==e);//true
        String f=(a+b).intern();//指向常量池中的地址
        System.out.println(d==f);//true
        System.out.println(d.equals(f));//true
    }
}

例题四:

public class String03 {
    public static void main(String[] args) { //main栈
        Test1 test1 = new Test1(); // 在栈里面有个main栈,main栈里面有个对象test1指向在堆中产生一个对象空间,然后在对象空间里面创建一个字符串对象str,str里面有个数组value指常量池的dadao的内存空间
//        在对象空间里面还有一个字符数组ch(数组默认情况下是放在堆里面的)
//        然后有个方法change(调用一个方法会在栈里面创建一个新栈)
        test1.change(test1.str,test1.ch);//test1.str是Test1 test1 = new Test1();对象传给它的,所以change里面的str指向的位置也是和对象的str一样
//        change的ch数组也是和对象一样,指向堆中的数组
//        当change方法执行 str="java";语句的时候,change方法里面的str就不再和对象里面的str指向同一个空间了,而是指向常量池的java空间了
//        当change方法执行 ch[0]='h';语句时,(因为修改字符数组里面的值是可以的(final))所以ch[0]就由原来的j变为了h
         System.out.println(test1.str+"and");//当执行到这句话的时候,执行change方法时所创建的栈就销毁了,所以这里的test.str是指对象(main栈)的str指向的位置,对象的str并没有发生变化
        System.out.println(test1.ch);//到这里的时候,上一句创建的方法栈就会销毁了,这里的
//        第一句输出:dadaoand
//        第二局输出:hava
    }
}
class Test1{
    String str =new String("dadao");
    final char[] ch={'j','a','v','a'};
    public void change(String str,char ch[]){
        str="java";
        ch[0]='h';
    }
}

图片解释:
在这里插入图片描述

  1. String类的常用方法
    • 1)说明:String类是保存字符串常量的。每次更新都需要重新开辟空间,效率较低,因此java设计者还提供了String Builder和StringBuffer来增强String的功能,并提高效率。
public class Method {
    public static void main(String[] args) {
        String s1="hello";
        String s2="Hello";
        //equals(区分大小写),判断内容是否相等
        System.out.println(s1.equals(s2)); //false
        // equalsIgnoreCase(不区分大小写),判断内容是否相等
        System.out.println(s1.equalsIgnoreCase(s2));//true
        //length,获取字符串的个数,即字符串的长度
        System.out.println(s1.length());//5
        //存在则返回对应的第一次出现下标,不存在就返回-1(除了单个字符,也可以是字符串)
        System.out.println(s1.indexOf("llo")); //2
        //存在则返回对应最后一次出现的下标,不存在就返回-1(除了单个字符,也可以是字符串)
        System.out.println(s1.lastIndexOf("h"));//0
        //截取输入的起始位置,到末尾的字符串
        System.out.println(s1.substring(2));//llo
        //截取指定位置之间的字符串(这里是从索引1开始截取到索引(4-1)的位置
        System.out.println(s1.substring(1,4));  //ell
        //获取某索引处的字符,不能用数组s[index];
        System.out.println(s1.charAt(2)); //l


        String s3="daDao";
        //toUpperCase,是把字符串转换为大写的
        System.out.println(s3.toUpperCase());//DADAO
        //toLowerCase把字符串转换为小写的
        System.out.println(s1.toLowerCase());//hello
        //concat,拼接字符串,可以多个
        System.out.println(s3.concat("喜欢打游戏").concat("和听歌"));//daDao喜欢打游戏和听歌
        //replace,是把前面的字符串参数,转换成后面的字符串参数
        System.out.println(s3.replace("da","Big"));//BigDao
//        注意,replace方法执行后,返回的结果才是替换过的,但对s3本身没有影响

        String poem="sakdj,aekfj,akjfkl,akjfk";
        String[] poem2=poem.split(",");
//       上面那句的意思是把poem,按,为标准进行分割
//        要注意:在进行字符串分割时,如果有特殊字符,需要加入转义字符"\"
        for (int i = 0; i <poem2.length ; i++) {
            System.out.println(poem2[i]);
        }
        char[] chs=s3.toCharArray();//toCharArray,把字符串转换为字符数组

        String a="jack";
        String b="ha";
        System.out.println(a.compareTo(b));//2(就是第一个字符的差(h和j的比较j-h))
        System.out.println("abc".compareTo("abcd")); //-1(返回的是长度差)
        System.out.println("abc".compareTo("abc"));//0
        //compareTo,比较两个字符串的大小,
        // 如果前者大,则返回正数,后者大则返回负数,如果相等则返回0
//         如果长度相同,并且每个字符也相同,就返回0;
//        如果前面部分都相同,就返回长度差,
//        如果前面部分不同就比较第一个不同字符的比较的差值


        String name="john";
        int age=10;
        double score=93;
        String info="我是"+name+"年龄是"+age+"成绩是"+score;
        System.out.println(info);
        String formatStr="我的名字是%s  年龄是%d  成绩是%.2f";
        String info2=String.format("我的名字是%s  年龄是%d  成绩是%.2f",name,age,score);
        String info3=String.format(formatStr,name,age,score); //这里可以用formatStr代替,%s,%d,%.2f是占位符
//        %s:表示后面由字符串代替,%d为整数,%.2f;表示使用小数来替换,替换后保留小数两位
        System.out.println(info2);//我的名字是john  年龄是10  成绩是93.00
        System.out.println(info3);//我的名字是john  年龄是10  成绩是93.00



    }
}

一、1、StringBuffer类

  1. 基本介绍:
    • Java.lang.StringBuffer代表可变的字符序列,可以对字符串内容进行增删。
    • 很多方法与String相同,但StringBuffer是可变长度的
    • StringBuffer是一个容器
    • 线程安全,一般用在多线程
    • 继承父类AbstractStringBuilder
    • StringBuffer实现Serializable接口,使得StringBuffer可以实现串行化
    • StringBuffer是一个final类,不能被继承
  2. String VS StringBuffer
      1. String保存的是字符串常量,里面的值不能更改,每次String类的更新,实际上就是更改地址,效率较低(private final char value[])
      1. StringBuffer保存的是字符串变量,里面的值可以更改,每次StringBuffer的更新实际上是可以更新内容的,不用每次更新地址 (即不是每次都创建新对象),效率更高(char value[] ===>这个放在堆中)
    • 当char value[] 数组的空间不够的时候,才会更新地址,把原来的内容复制到新的地址(缓冲机制)
public class StringAndStringBuffer {
    public static void main(String[] args) {
//        1.StringBuffer的直接父类是AbstractStringBuffer
//        2.StringBuffer实现Serializable接口,使得StringBuffer可以实现串行化
//        3.在父类AbstractStringBuilder中,有属性 char[] value,但是不是final类型的
//          该value数组存放 字符串内容是存放在堆中的
//        4.StringBuffer是一个final类,不能被继承
//        5.因为StringBuffer 字符序列是存在 char[] value,所以在每次变化
//        不用每次都更换地址(即不是每次都创建新对象),所以效率高于String



//        1.创建一个 大小为16(super(16))的char[],用于存放字符内容
        StringBuffer stringBuffer = new StringBuffer();
//        2.通过构造器指定char[] 数组的大小
        StringBuffer stringBuffer1 = new StringBuffer(100);
//        3.通过 给一个String创建StringBuffer,char[] 的大小就是(String.length()+16)

        StringBuffer sb1= new StringBuffer("niHao");
        System.out.println(sb1);

//     String----》StringBuffer
        String s1="hello";
        //        方式1: 使用构造器
//        注意:返回的才是StringBuffer对象,对s1本身没影响
        StringBuffer strB1 = new StringBuffer(s1);


//        方法2:使用的是append方法
        StringBuffer sb2 = new StringBuffer();
        sb2= sb2.append(s1);




        //    StringBuffer ------>String
        StringBuffer stringBuffer3 = new StringBuffer("大刀哥");
//        方式一:使用StringBuffer的toString方法
        String s = stringBuffer3.toString();

//        方式二:使用构造器
        String s2 = new String(stringBuffer3);
        
    }

}

2)StringBuffer类的常用方法

public class StringBufferMethod {
    public static void main(String[] args) {
//        1.添加
        StringBuffer s1 = new StringBuffer("hello");
        s1.append(',');
        s1.append("张三丰");
        System.out.println(s1);//hello,张三丰

//        2.删除
        StringBuffer s2= new StringBuffer("hello,NiHao");
        s2.delete(4,9);//删除[4-9)的字符(是数组坐标的4-9)
        System.out.println(s2);//hellao
//        3.修改
        StringBuffer s3= new StringBuffer("hello,NiHao");
        s3.replace(2,5,"Dao");//
        System.out.println(s3);//heDao,NiHao
//        4.查找:
        StringBuffer s4= new StringBuffer("张三丰哎张无敌");
        int index=s4.indexOf("张无敌");//查找指定字符串在s4中第一次出现的索引的位置,没有则返回-1
        System.out.println(index);//4
//         5.插入
        s4.insert(4,"梁无忌");//在索引为4的位置插入字符串,原来在索引4的位置的字符串往后移
        System.out.println(s4);//张三丰哎梁无忌张无敌
//        6.长度
        System.out.println(s4.length());//10
        System.out.println(s4);//张三丰哎梁无忌张无敌

    }
}

一、2、StringBuilder

  1. 基本介绍:

    • StringBuilder是一个可变的字符序列。此类提供一个与StringBuffer兼容的API,但不保证同步==(String Builder不是线程安全的)==,该类被设计用作StringBuffer的一个简易替换,用在字符串缓冲区被单个线程使用的时候,如果可能,建议优先使用该类,因为在大多数实现中,StringBuilder比StringBuffer要快。
    • 在StringBuilder上的只有操作是append和insert方法,可重载这些方法,以接受任意类型的数据
    • 继承了AbstractStringBuilder类
    • 实现了Serializable,说明StringBulider对象是可以串行化(对象可以网络传输,可以保存到文件)
    • StringBuilder对象字符序列仍然是存放在其父类,AbstractStringBuilder的char[] value,因此存放在堆中
    • StringBuilder的方法,没有做互斥的处理,即没有synchronized关键字,因此在单线程的情况下,使用StringBulider
  2. String ,StringBuffer,StringBulider的比较:

      1. StringBuilder和StringBuffer非常类似,均代表可变的字符序列,而且方法也一样。
      1. String:不可变字符序列,效率低,但复用率高。
      1. StringBuffer:可变字符序列,效率较高(增删),线程安全
      1. StringBuilder:可变字符序列,效率最高,线程不安全
      1. String使用说明:
      • string s=“a”;//创建了一个字符串
      • s+=“b”; //实际上原来的“a"字符对象已经丢弃,现在又产生了一个字符串s+“b”(也就是”ab").如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率,如果这样的操作放到循环中,会极大影响程序的性能==》结论:如果我们对String做大量的修改,不要使用String
  3. String,StringBuffer,StringBuilder的使用原则:

    • 如果字符串存在大量的修改操作,一般使用StringBuffer和StringBuilder
    • 如果字符串存在大量的修改操作,并在单线程的情况下,使用StringBuilder
    • 如果字符串存在大量的修改操作,并在多线程的情况,使用StringBuffer
    • 如果我们字符串很少修改,被多个对象引用,使用String,比如配置信息等
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值