String类的使用总结

本文深入探讨 Java 中 String 类的两种实例化方式及其性能差异,并分析了字符串比较的两种方法('==' 和 equals())。此外,还讨论了字符串不变性的概念及如何高效处理字符串内容的变化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

String对象有两种实例化方式:1,直接赋值,2,既然String是一个类,它的对象当然可以通过关键字new来实例化,但是这两种实例化方式,性能上存在很大差异

这里写图片描述

public class StringDemo04{
    public static void main(String args[]){
        String str1 = "hello" ;                 // 直接赋值
        String str2 = new String("hello") ;     // 通过new赋值
        String str3 = str2 ;                    // 传递引用
        System.out.println("str1 == str2 --> " + (str1==str2)) ;    // 结果为false
        System.out.println("str1 == str3 --> " + (str1==str3)) ;    // 结果为false
        System.out.println("str2 == str3 --> " + (str2==str3)) ;    // 结果为true
    }
};

为何是这样的结果呢,通过内存分析就可以明白:
这里写图片描述
这里写图片描述

这里“==”判断的是,地址值,创建了两个对象,当然地址不会相同,所以就是以上的结果。
如果要判断,内容是否相同,而不考虑地址,不考虑是不是同一个对象,就可以使用equals()方法,进行判断。

public class StringDemo05{
    public static void main(String args[]){
        String str1 = "hello" ;                 // 直接赋值
        String str2 = new String("hello") ;     // 通过new赋值
        String str3 = str2 ;                    // 传递引用
        System.out.println("str1 equals str2 --> " + (str1.equals(str2))) ; // true
        System.out.println("str1 equals str3 --> " + (str1.equals(str3))) ; // true
        System.out.println("str2 equals str3 --> " + (str2.equals(str3))) ; // true
    }
};

总结:String的两种比较方式
1,“==”,比较的是地址值,比较的是数值,基本数据类型使用这个判断是否相等。
2,equals(),比较的是内容。

String类对象的两种实例化方式的比较:

一:一个字符串,其实就是String类的一个匿名对象

一个字符串,其实就是String类的一个匿名对象,为什么呢,如果说,一个字符串,可以直接调用String类的方法,比如说equals()方法,那么,就证明,这个字符串是一个String类对象,它又没有栈内存的声明,那肯定就是一个匿名对象了。

public class StringDemo06{
    public static void main(String args[]){
        System.out.println("hello".equals("hello")) ;
    }
};

结果输出了true。
如此说来,经常使用的String str=“hello”,其实就是将一个堆内存空间的指向给了栈内存。

1,直接赋值

看下面代码,

public class StringDemo07{
    public static void main(String args[]){
        String str1 = "hello" ;                 // 直接赋值
        String str2 = "hello" ;                 // 直接赋值
        String str3 = "hello" ;                 // 直接赋值
        System.out.println("str1 == str2 --> " + (str1==str2)) ;    // true
        System.out.println("str1 == str3 --> " + (str1==str3)) ;    // true
        System.out.println("str2 == str3 --> " + (str2==str3)) ;    // true
    }
};

返回结果都是true,说明,三个对象的地址都是相同的。
内存分析下:
这里写图片描述

分析:

        String str1 = "hello" ;                 
        String str2 = "hello" ;             
        String str3 = "hello" ;

这里三条语句,其实是创建三个对象,但是,三个对象的内容都是相同的,在堆中创建对象的时候,如果如果内容相同,那么系统就不会重新创建新的堆内存,而是,直接将现在的栈内存直接指向已经存在的堆内存的那一个地址,从而达到了节省内存的效果,这是Java的机制。,所以,使用“==”对这三个String对象进行比较的时候,得到了三个true。

2,使用关键字new

String str1 = new String("hello") ;

分析下,这一句代码中,首先,双引号中的字符串是一个匿名String对象,那么肯定开辟了堆内存空间用于保存这个匿名对象,然后,看到new关键字,肯定也是开辟堆内存空间,那么,就是说,这里开辟了两个堆内存空间了。必然存在一个是垃圾,浪费了内存空间。
这里写图片描述

总结来说,String的两种实例化方式的区别:采用直接赋值的方式,就只是开辟了一个内存空间,而使用new String(“”)的方式,开辟了两个内存空间,所以,开发中,一般都是使用直接赋值的方式来完成的。

二:String一经创建,其内容不可改变,

这怎么理解呢?字符串的内容不是可以通过加号,可以随意后缀,随意改变么?比如:

        String str = "hello" ;      // 声明字符串
        str = str + " world!!!" ;   // 修改字符串
        System.out.println("str = " + str) ;

此时,其实是,字符串对象改变了,但是字符串的内容的改变是通过字符串对象的改变来实现的,换句话说,str改变了堆内存的指向,就是说改变了内存地址的引用。所以,得到了字符串内容改变的效果。

所以,考虑到效率问题,就尽可能地少使用这种直接改变内容的方式使用String,因为这样的方式需要堆栈内存之间的引用关系,断开,重新指向,次数太多的话,需要较大的系统消耗,会严重影响性能,也产生了很多内存垃圾。但是不可避免的所,这样的需求,肯定是存在的啊,那该怎么办呢,当然有办法,使用StringBuffer或者StringBuilder。

内存分析:
这里写图片描述

那么不禁要问,为什么是这样子呢?为什么不是擦除了,上一个内容,而写入新的内容呢?
比如,创建一个Person类,有 int age 和string name两个属性,通过setter方法修改这个类的对象的属性的时候,就是在这个对象开辟的内存中擦除了上一次的结果,写入setter中设置的新结果啊,
还有,比如int temp=10;这样的代码,其实也是开辟堆栈,当再来一条语句,temp=20;,就会在堆内存中擦除10,重新写入20,
为什么String就是重新创建新对象,开辟新的对内存,然后改变了堆内存指向,指向这个新的堆内存才实现了内容的改变呢?

要回答这样的问题,其实就要从最底层去看,去看String的底层存储和这些基本数据类型,和这Person类是如何保存的。

参考这个帖子,看看底层是如何实现的String StringBuilder StringBuffer底层分析

参考链接2[String StringBuilder StringBuffer底层分析2
参考链接3String StringBuilder StringBuffer效率分析3

说到这里,就不得不说说StringBuilder和StringBuffer了。

1,SringBuffer、StringBuilder和String一样,也用来代表字符串。String类是不可变类,任何对String的改变都 会引发新的String对象的生成;
StringBuffer则是可变类,任何对它所指代的字符串的改变都不会产生新的对象。既然可变和不可变都有了,为何还有一个StringBuilder呢?
先说一下集合的故事,HashTable是线程安全的,很多方法都是synchronized方法,而HashMap不是线程安全的,但其在单线程程序中的性能比HashTable要高。
StringBuffer和StringBuilder类的区别也是如此,他们的原理和操作基本相同,区别在于StringBufferd支持并发操作,线性安全的,适合多线程中使用。StringBuilder不支持并发操作,线性不安全的,不适合多线程中使用,适合在单线程中使用。新引入的StringBuilder类不是线程安全的,但其在单线程中的性能比StringBuffer高。

3.StringBuilder与 StringBuffer

    StringBuilder:线程非安全的,但是速度最快

    StringBuffer:线程安全的,支持并发操作

    当我们在字符串缓冲去被多个线程使用是,JVM不能保证StringBuilder的操作是安全的,虽然他的速度最快,但是可以保证StringBuffer是可以正确操作的。当然大多数情况下就是我们是在单线程下进行的操作,所以大多数情况下是建议用StringBuilder而不用StringBuffer的,就是速度的原因。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值