JAVASE语法零基础——String类2

Java系列文章目录


在这里插入图片描述

Write once,Runanywhere.

如果你觉得我的文章有帮助到你,还请【关注➕点赞➕收藏】,得到你们支持就是我最大的动力!!!

版权声明:本文由【马上回来了】原创、在CSDN首发、需要转载请联系博主。
版权声明:本文为CSDN博主「马上回来了」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

在这里插入图片描述

新的知识开始喽
在这里插入图片描述

文章目录

  • Java系列文章目录
  • 1.String常量池
    • 1.1 创建对象的思考
    • 1.2 字符串常量池(Stringable)
    • 1.3 再谈对创建对象的思考
    • 1.4 intern方法
    • 1.5 面试题:请解释String类中两种对象实例化的区别
  • 2.字符串的不可变性
  • 3.字符串修改
  • 4. StringBuilder和StringBuffer


1.String常量池

1.1 创建对象的思考

下面创建String对象的方式相同吗?

  public static void main(String[] args) {
        String s1 = "hello";
        String s2 = "hello";
        String s3 = new String("hello");
        String s4 = new String("hello");
        System.out.println(s1==s2);//true
        System.out.println(s1==s3);//false
        System.out.println(s3==s4);//false
    }

为什么s1和s2引用的是同一个对象,而s3和s4引用的不是同一个对象呢?
对于1,3.14,"hello"这种被经常使用的字面常量,为了使程序运行跟块,跟节省空间,Java为这8中数据类型和String类型提供了常量池。

1.2 字符串常量池(Stringable)

字符串常量池在JVM中是StringTable类,实际是一个固定大小HashTable(一种高效用来进行查找的数据结构,后序给大家详细介绍),不同JDK版本下字符串常量池的位置以及默认大小是不同的,其中Java8 在堆中。

1.3 再谈对创建对象的思考

此处在java8上分析
对于直接使用常量创建String类:

 public static void main(String[] args) {
        String s1 = "hello";
        String s2 = "hello";
        System.out.println(s1==s2);//true
    }

详细讲解:
JAVASE语法零基础——String类2_第1张图片

直接new常量池一开始没有的对象:

public static void main(String[] args) {
        String s1 = new String("world");
        
    }

JAVASE语法零基础——String类2_第2张图片
对于直接new对象:

 public static void main(String[] args) {
        String s3 = new String("hello");
        String s4 = new String("hello");
        System.out.println(s3==s4);//false
    }

JAVASE语法零基础——String类2_第3张图片

1.4 intern方法

intern方法是一个native方法(native方法是指底层用C++实现的,看不到它的源代码)该方法的作用是手动将创建的String对象添加到常量池中

 public static void main(String[] args) {
        char[] chars = {'a','b','c'};
        String s1 = new String(chars);
        //s1.intern();
        String s2 = "abc";
        System.out.println(s1==s2);//未使用intern方法 false
    }

先看这两行代码:

//字符数组转字符串
char[] chars = {'a','b','c'};
String s1 = new String(chars);

在这里插入图片描述
这两行代码执行的结果:
JAVASE语法零基础——String类2_第4张图片
这两行代码执行的过程:
JAVASE语法零基础——String类2_第5张图片
一直到到这两行代码执行结束,常量池都没有添加双引号"“引起的字符串常量,因为s1里没有”"引起的字符串。
整个代码执行完毕:
JAVASE语法零基础——String类2_第6张图片
当执行intern方法时:

 public static void main(String[] args) {
        char[] chars = {'a','b','c'};
        String s1 = new String(chars);
        s1.intern();
        String s2 = "abc";
        System.out.println(s1==s2);// true
    }

JAVASE语法零基础——String类2_第7张图片

 public static void main(String[] args) {
        char[] chars = {'a','b','c'};
        String s1 = new String(chars);
      //运行到这里s1不在常量池中
      //s2在常量池中
        String s2 = "abc";
        //intern检查常量池,发现常量池里有"abc",不会再将s1引用的对象放入到常量池里,因此这里结果跟没有使用intern方法的情况类似。
          s1.intern();
        System.out.println(s1==s2);// false
    }

1.5 面试题:请解释String类中两种对象实例化的区别

JDK1.8中

  1. String str = “hello”
    只会开辟一块堆内存空间,保存在字符串常量池中,然后str共享常量池中的String对象。常量池里的对象。
    JAVASE语法零基础——String类2_第8张图片
  1. String str = new String(“world”)
    会开辟两块堆内存空间,字符串"world"保存在字符串常量池中,然后用常量池中的String对象给新开辟的String对象赋值。new一个对象、常量池里new一个对象。
    JAVASE语法零基础——String类2_第9张图片
  1. String str = new String(new char[]{‘h’, ‘e’, ‘l’, ‘l’, ‘o’})
    先new一个char类型的数组对象,然后在堆上创建一个String对象,然后利用copyof将重新开辟数组空间,将参数字符串数组中内容拷贝到String对象中。
    JAVASE语法零基础——String类2_第10张图片

2.字符串的不可变性

String是一种不可变对象. 字符串中的内容是不可改变。字符串不可被修改,是因为:

  1. String类在设计时就是不可改变的,String类实现描述中已经说明了
    JAVASE语法零基础——String类2_第11张图片
    String类中的字符实际保存在内部维护的value字符数组中,该图还可以看出:
  2. String类被final修饰,表明该类不能被继承。
  3. value被修饰被final修饰,表明value自身的值不能改变,即不能引用其它字符数组,但是其引用空间中的内容可以修改。
    JAVASE语法零基础——String类2_第12张图片
    【纠正】网上有些人说:字符串不可变是因为其内部保存字符的数组被final修饰了,因此不能改变。
    这种说法是错误的,不是因为String类自身,或者其内部value被final修饰而不能被修改。
    【正确】关键在于value被private修饰类外拿不到value的值,因此无法对string进行修改。

为什么 String 要涉及成不可变的?(不可变对象的好处是什么?) (选学)

  1. 方便实现字符串对象池. 如果 String 可变, 那么对象池就需要考虑写时拷贝的问题了.
  2. 不可变对象是线程安全的.
  3. 不可变对象更方便缓存 hash code, 作为 key 时可以更高效的保存到 HashMap 中

3.字符串修改

  1. 所有涉及到可能修改字符串内容的操作都是创建一个新对象,改变的是新对象。比如 replace 方法:
    JAVASE语法零基础——String类2_第13张图片
    看下面这个代码:
public static void main(String[] args) {
String s = "hello";
s += " world";
System.out.println(s); //并不是在原有的hello空间上接了一个world,而是产生了一个新对象输出:hello world, 
}

2.注意:尽量避免直接对String类型对象进行修改,因为String类是不能修改的,所有的修改都会创建新对象,效率非常低下。效率低下的原因是因为每次对字符串拼接都会产生新的对象。
下面是String类、StringBuilder类和StringBuffer类分别执行一段代码,StringBuilder类和StringBuffer类的效率是String类的282倍。

public static void main(String[] args) {
        //String
            long start = System.currentTimeMillis();
            String s = "";
            for(int i = 0; i < 10000; ++i){
                s += i;
            }
            long end = System.currentTimeMillis();
            System.out.println(end - start);//282
            //StringBuffer
            start = System.currentTimeMillis();
            StringBuffer sbf = new StringBuffer("");
            for(int i = 0; i < 10000; ++i){
                sbf.append(i);
            }
            end = System.currentTimeMillis();
            System.out.println(end - start);//1
            //StringBuilder
            start = System.currentTimeMillis();
            StringBuilder sbd = new StringBuilder();
            for(int i = 0; i < 10000; ++i){
                sbd.append(i);
            }
            end = System.currentTimeMillis();
            System.out.println(end - start);//1
    }

4. StringBuilder和StringBuffer

再看这个代码:

public static void main(String[] args) {
String s = "hello";
s += " world";
System.out.println(s); //并不是在原有的hello空间上接了一个world,而是产生了一个新对象输出:hello world, 
}

我们看看这段代码的汇编:
JAVASE语法零基础——String类2_第14张图片
我们已经知道了:尽量避免直接对String类型对象进行修改,因为String类是不能修改的,所有的修改都会创建新对象,效率非常低下。
现在用StringBuilder来还原这段汇编码:
JAVASE语法零基础——String类2_第15张图片
append方法:
JAVASE语法零基础——String类2_第16张图片

toString方法:
JAVASE语法零基础——String类2_第17张图片

 StringBuilder s1 = new StringBuilder();
        s1.append("hello");
        s1.append("hello");
      String s = s1.toString();
        System.out.println(s);//hellohello

代码执行过程看动图:


当然也可以使用StringBuffer来实现:

  StringBuffer s1 = new StringBuffer();
        s1.append("hello");
        s1.append("world");
        String s = s1.toString();
        System.out.println(s);

使用StringBuilder与StringBuffer的区别:
JAVASE语法零基础——String类2_第18张图片
面试题:

  1. String、StringBuffer、StringBuilder的区别
  • String的内容不可修改,StringBuffer与StringBuilder的内容可以修改.
  • StringBuffer与StringBuilder大部分功能是相似的
  • StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操作
  1. 以下总共创建了多少个String对象【前提不考虑常量池之前是否存在】
String str = new String("ab"); // 会创建多少个对象 2 
String str = new String("a") + new String("b"); // 会创建多少个对象 2 + 2 + 2(new StringBuilde)、(new toString)


今天的你看懂这里又学到了很多东西吧

在这里插入图片描述

下次见喽
在这里插入图片描述

你可能感兴趣的:(JAVASE,0基础入门,jvm,java,算法)