java基础查漏补缺——String,StringBuffer,StringBulider的区别

String,StringBuffer,StringBulider的区别与联系

字符串常量池

如果想要学习String类相关知识,首先要了解一下什么是字符串常量池

在《深入理解java虚拟机》这本书上是这样写的:对于HotSpot虚拟机,根据官方发布的路线图信息,现在也有放弃永久代并逐步的改为采用Native Memory来实现方法区的规划了,在目前已经发布的JDK1.7的HotSpot中,已经把原来存放在方法区中的字符串常量池移出。根据查阅的资料显示在JDK1.7以后的版本中字符串常量池移到堆内存区域;同时在jdk1.8中移除整个永久代,取而代之的是一个叫元空间(Metaspace)的区域

在 JAVA 语言中有8中基本类型和一种比较特殊的类型String。这些类型为了使他们在运行过程中速度更快,更节省内存,都提供了一种常量池的概念。常量池就类似一个JAVA系统级别提供的缓存。

通俗的去讲,常量池就类似于一个缓存,当创建该字符串的时候总是会先去常量池中查询是否已经存在,存在就可以拿来用,不存在就先创建

  • 直接使用双引号声明出来的String对象会直接存储在常量池中。

  • 如果不是用双引号声明的String对象,可以使用String提供的intern方法。intern 方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中

String

上面已经描述了两种方法,下面我们来做具体的解析

  • 直接双引号声明出来的String对象

        

public class StringPractice {
    public static void main(String[] args) {
        String str1="Hello";
        String str2="Hello";
        System.out.println(str1==str2);
    }
}

运行结果:

 

解释:

采用字面值的方式创建一个字符串时,JVM首先会去字符串池中查找是否存在"Hello"这个对象,如果不存在,则在字符串常量池中创建"Hello"这个对象,然后将池中"Hello"这个对象的引用地址返回给"Hello"对象的引用s1,这样s1会指向字符串常量池中"Hello"这个字符串对象;如果存在,则不创建任何对象,直接将池中"Hello"这个对象的地址返回,赋给引用s2。因为s1、s2都是指向同一个字符串池中的"Hello"对象,所以结果为true。

  • 如果不是用双引号声明的String对象,可以使用String提供的intern方法。intern 方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中

        

public class demo02 {
    public static void main(String[] args) {
        String str1=new String("Hello");
        String str2=new String("Hello");
        System.out.println(str1==str2);
    }
}

运行效果:

 

解释:

采用new关键字新建一个字符串对象时,JVM首先在字符串池中查找有没有"Hello"这个字符串对象,如果有,则不在池中再去创建"Hello"这个对象了,直接在堆中创建一个"Hello"字符串对象,然后将堆中的这个"Hello"对象的地址返回赋给引用s3,这样,s3就指向了堆中创建的这个"Hello"字符串对象;如果没有,则首先在字符串池中创建一个"Hello"字符串对象,然后再在堆中创建一个"Hello"字符串对象,然后将堆中这个"Hello"字符串对象的地址返回赋给s3引用,这样,s3指向了堆中创建的这个"Hello"字符串对象。s4则指向了堆中创建的另一个"Hello"字符串对象。s3 、s4是两个指向不同对象的引用,结果当然是false。

注意:这里我们补充一些关于==和equals()方法的区别

如果比较的为基本数据类型,==比较的为值是否相等,不可以用equals()
如果比较的为引用类型,==比较的依旧为值,但是引用类型是代表的地址,因此也可以说是==比较的是地址
equals()属于Object类的方法,其比较的也为地址,但是大部分类会重写该方法用来比较类的属性,比如String类,重写后比较的就是字符串内容

String一些简单的方法使用

import java.util.Locale;

public class demo3 {
    public static void main(String[] args) {
        String str1="HelloWorld I Love You Love Me";
        //返回字符ch或子字符串str在字符串中第一次出现位置的索引
        int a=str1.indexOf('a');
        System.out.println("a:"+a);
        //返回字符ch或子字符串str在字符串中最后一次出现位置的索引
        int b=str1.lastIndexOf("Love");
        System.out.println("b:"+b);
        //返回字符串中index位置上的字符,其中index的取值范围是0~字符串长度-1
        char c=str1.charAt(6);
        System.out.println("c:"+c);
        //判断此字符串是否以指定的字符串结尾
        boolean d=str1.endsWith("Me");
        System.out.println("d:"+d);
        //返回此字符串的长度
        int e=str1.length();
        System.out.println("e:"+e);
        //将此字符串与指定字符串比较
        boolean f=str1.equals("lalala~");
        System.out.println("f:"+f);
        //判断此字符串是否以指定的字符串开始
        boolean g=str1.startsWith("Hello");
        System.out.println("g:"+g);
        //判断此字符串是否包含指定的字符序列
        boolean h=str1.contains("World");
        System.out.println("h:"+h);
        //将字符串中的字符都转换为小写字母
        String i=str1.toLowerCase(Locale.ROOT);
        System.out.println("i:"+i);
        //将字符串中的字符都转换为大写字母
        String j=str1.toUpperCase(Locale.ROOT);
        System.out.println("j:"+j);
        //将int变量i转换成字符串
        String k=String.valueOf(5);
        System.out.println("k:"+k);
        //将此字符串转换为一个字符数组
        char[] l=str1.toCharArray();
        for(char m:l){
            System.out.print("m:"+m);
        }
        System.out.println();
        //返回一个字符串,用新字符串替换旧字符串
        String n=str1.replace("World","Love");
        System.out.println("n:"+n);
        //根据参数将字符串分割
        String[] o=str1.split(" ");
        for (String p : o) {
            System.out.print("p:"+p);
        }
        System.out.println();
        //返回一个字符串,从指定位置开始到末尾
        String q=str1.substring(1);
        System.out.println("q:"+q);
        //返回一个字符串,从指定位置开始,到指定位置-1结束
        String r=str1.substring(1,6);
        System.out.println("r:"+r);
        //返回一个字符串,去除首尾空格
        String s=str1.trim();
        System.out.println("s:"+s);
    }
}




//输出:
a:-1
b:22
c:o
d:true
e:29
f:false
g:true
h:true
i:helloworld i love you love me
j:HELLOWORLD I LOVE YOU LOVE ME
k:5
m:Hm:em:lm:lm:om:Wm:om:rm:lm:dm: m:Im: m:Lm:om:vm:em: m:Ym:om:um: m:Lm:om:vm:em: m:Mm:e
n:HelloLove I Love You Love Me
p:HelloWorldp:Ip:Lovep:Youp:Lovep:Me
q:elloWorld I Love You Love Me
r:elloW
s:HelloWorld I Love You Love Me

String为什么是不可变的?

 我们可以看到,String类存储字符串是用字符数组存储的,同时也用了final进行了修饰,我们知道,final修饰的方法不能被重写,修饰的变量为基本类型的话不可变,为引用类型的话不可以指向其它对象,修饰的类不可以被继承。而这个字符数组是可以变的,只是不能指向其它对象而已。所以说根本原因是String类是final修饰的,不能被继承,同时该字符数组是被private和final修饰,也没有暴漏给外界可以更改的方法,因此不可变。

StringBuffer

由于字符串是常量,一旦创建就可以变了。若要是对字符串修改就得创建新的字符串,不方便。为此出现了StringBuffer类,StringBuffer的内容和长度都是可以变化的。

StringBuffer是继承AbstractStringBuilder类的

StingBuffer常用的方法

public class demo4 {
    public static void main(String[] args) {
        StringBuffer str=new StringBuffer();
        //在字符串末尾添加新的字符串
        str.append("Hello");
        System.out.println("append之后:"+str);
        //在指定位置插入字符串
        str.insert(5,"World");
        System.out.println("insert之后:"+str);
        //删除指定位置的字符
        str.deleteCharAt(5);
        System.out.println("删除指定位置字符后:"+str);
        //删除指定范围的字符数组
        str.delete(1,3);
        System.out.println("delete之后:"+str);
        //替换指定的字符或字符数组
        str.replace(2,3,"Love");
        System.out.println("replace之后:"+str);
        //返回字符串
        System.out.println(str.toString());
        //反转字符串
        System.out.println("字符串反转"+str.reverse());
    }
}




输出:
    append之后:Hello
    insert之后:HelloWorld
	删除指定位置字符后:Helloorld
	delete之后:Hloorld
	replace之后:HlLoveorld
	HlLoveorld
	字符串反转dlroevoLlH

为什么StringBuffer是线程安全的

因为StringBuffer的方法绝大部分都加了synchronized修饰,同步锁,被synchronized修饰的方法在某一时刻只允许一个线程访问,访问该方法的其他线程会发生阻塞,直到当前线程访问完毕,其他线程才有机会执行该方法。

StringBulider

StringBulider与StringBuffer相似,最大的不同在于是非线程安全的,可以同步访问。

运行效率

StringBulider > StringBuffer >String

字符串拼接 “+”的使用及原理

StringBulider和StingBuffer不可以使用+来进行字符串拼接

String可以使用

 

这个可以看出StringBuffer在字符串拼接的时候出现报错;

“+”实际上是通过StringBulider调用append()方法实现的,拼接完成之后调用toString()得到一个String对象。

不过在循环内使用“+”进行字符串拼接的话,存在比较明显的缺陷,编译器不会创建单个StringBulider以复用,会导致创建过多的StringBulider对象

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

henghengzhao_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值