JAVA_String

一.String的基本特性

两种声明方式:

 String s1 = "abc";  //字面量的方式,存储在堆中的字符串常量池中,常量池不允许出现相同的字符串常量
 String s2 = new String("abc");   //对象的声明方式,存储在堆中。并且在字符串常量池中复制一份

 不可变性(原因):对String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新的对象

  • String 声明为final的,不可被继承,不可被重写
  • 因为底层是数组,数组在一开始就把空间大小确定下来了。所以不可变

为什么String类被设计用final修饰?

  • 为了安全
  • String 类中有很多调用底层的本地方法,调用了操作系统的 API, 如果方法可以重写,可能被植入恶意代码,破坏程序。Java 的安全性也体现在这里。

底层的改变

  • jdk8:底层使用char[]    jdk9以后:底层使用 byte[]
  • 原因:
    • 我们大多数情况下,string中的都是一些"字母或者数字",使用"一个字节"存储就行。 而使用char存储就需要占2个字节(B),浪费资源。所以改为byte[],对于一些汉字那就采用 2个字节存储,这样最大化节省空间 
二.字符串的拼接操作
  • 常量+常量:结果在常量池 编译期优化原理
  • 只要有一个变量: 结果在堆中 new StingBulider()

执行细节

开发中使用StringBuilder拼接字符串,效率高 

三.intern()的使用
1.入门
new String("123")    //这种声明方式,默认创建2个对象.一个放在堆中,一个放在字符串常量池中。(常量池中有就使用现成的,没有就创建)
而对于String s ="123"; //这种直接放在字符串常量池中。

intern()方法的作用是:字符串常量池有就直接返回对象地址,没有就创建
(因为普通的new String("123")也会在字符串常量池中创建对象,所以该方法的作用就没那么明显。
但是对于String s =new String("1")+new String("2");这种拼接操作,因为它不会在量池中创建对象,
效果明显)

总结:
    new String("123")和使用intern都是常量池中"有就直接使用,没有就创建"。
    但是intern的创建大有乾坤,下面解释
面试题(小试牛刀)

(1)new String("ab")创建几个对象?
答:创建2个对象.一个放在堆中,一个放在字符串常量池中。(常量池中有就使用现成的,没有就创建)

(2)String s="a"+"b"+"c"+"d";创建了几个对象?
    1个,编译期优化

(3)new String("a")+new String("b")创建几个对象
答:
对象(1):new StringBuilder()
对象(2): new String("a")
对象(3): 常量池中的"a"
对象(4): new String("b")
对象(5): 常量池中的"b"
对象(6):StringBuilder 的toString(): new String("ab");
"注意":以上的new String("ab");和普通的new String("ab")调用的构造器就不一样,
所以不会在量池中生成"ab"对象。(解释如下)

 2.intern的创建

关于intern()的创建说明

  • jdk6:是真实的在字符串常量池中创建一个对象(复制一份)。
  • jdk7之后:也可以泛泛的说在字符串常量池中创建一个对象,但是不是真实的创建,而是引用堆空间中对象的地址
    • 造成这个原因也是因为字符串常量池在jdk7从方法区放到堆空间中

面试题(升级)

public class Test {
    public static void main(String[] args) {
        String s = new String("1");
        s.intern();//调用此方法之前,字符串常量池中就有“1”
        String s1 ="1";
        System.out.println(s == s1);  //jdk6:false  jdk7/8:false

        String s2 =new String("1")+new String("2");
        s2.intern(); //调用该方法之前,字符串常量池中没有"12".使用该方法就强制在字符串常量池中生成"12"
     //因为上面的拼接最后在堆中new String("12").生成这个对象的字符串常量对象时,生成的方式随着版本的不同有所改变
     //jdk6是真实的在字符串常量池中创建一个对象"12"。jdk7之后就是引用堆中的new String("12")的地址
     //造成这个原因也是因为字符串常量池在jdk7之后从方法区放到堆空间中的原因
        String s3 = "12";
        System.out.println(s2 == s3);//jdk6:false  jdk7/8:true
        
    }
}

 3.使用Intern()的好处

  • 主要是为了对象的复用,节省内存空间
  • 这个特点对于创建多个相同的String就有用了。因为可以让堆中只生成一个对象,字符串常量池生成 不同的引用,指向同一个堆中的对象
  • 使用intern()就是把对象放在字符串常量池中,字符串常量池最大的特点就是字符串常量不重复, 这样也就说可以保证一些字符串对象不重复,可以复用。让之前堆中的对象就可以被垃圾回收
  • 所以在大型网站,使用大量重复的字符串时的业务时,都使用intern()。比如:存储个人地址,大量的人都在 北京市海淀区。那这个存储这个信息就可以使用String.intern()来节省内存

 四.如何将字符串反转?

  • 使用 StringBuilder 或 StringBuffer 的 reverse 方法
    • String str = new String("123456789"); System.out.println(new StringBuilder(str).reverse().toString());
  • 自己进行倒序输出
  • 自己使用递归输出

 五.String和StringBuilder,StringBuffer的区别

  • String 不可变性 底层(这三个底层都一样) 拼接 intern 
  • StringBuffer 可变的字符序列 线程安全(所有方法都加synchronized) 效率低
  • StringBuilder 可变的字符序列 线程不安全 效率高
  • 性能比较:StringBuilder > StringBuffer > String

为什么StringBuilder,StringBuffer就是可变的?(源码分析)

说明:StringBuilder,StringBuffer内部实现是一样的,只是StringBuffer的方法都加了synchronized。String直接创建一个固定长度的数组,StringBuilder和StringBuffer创建一个足够大的数组,还支持扩容。(具体解释如下)

  • 具体代码:
    • new String("123"):内部直接创建一个长度为3的数组,不能扩容
    • new StringBuilder(): 内部创建一个默认长度为16的数组:new char[16];
    • new StringBuilder("123"):内部创建一个长度为3+16的数组:new char["abc".length()+16];
  • 每次添加元素时,进行判断,数组空间不够就进行扩容机制
  • 扩容机制:空间不够就进行扩容,扩展为 原来容量*2倍+2,同时将原有的数组元素复制到新的数组中

 

寄语:每个人真正强大起来都要度过一段没人帮忙,没人支持的日子。所有事情都是自己一个人撑,所有情绪都是只有自己知道。但只要咬牙撑过去,一切都不一样了。

posted @ 2021-12-03 21:41  小猴子_X  阅读(71)  评论(0编辑  收藏  举报