JAVA | 聚焦 String 的常见用法与底层内存原理

发布于:2025-03-18 ⋅ 阅读:(18) ⋅ 点赞:(0)

*个人主页

文章专栏

《赛博算命之梅花易数的JAVA实现》*

在这里插入图片描述

#前言:API

1.定义

API(Application Programming Interface)应用程序编程接口

简单理解:API就是别人已经写好的东西,我们不需要自己编写,直接使用即可

public static void main (String []args){
    Random r = new Random ();
    int number = r.nextInt (100);
}

Java API : 指的就是JDK提供的各种功能的Java类

这些类将底层的实现封装了起来,我们不需要关心这些类是如何实现的,只需要学习这些类如何使用即可。

2.已经学习过的API

Scanner 键盘录入

Random 随机数

API和API帮助文档

API:目前是JDK中提供的各种功能的Java类

这些类将底层的实现封装了起来,我们不需要关心这些类是如何实现的,只需要学习这些类如何使用即可。

API帮助文档:帮助开发人员更好的使用API和查询API的一个工具

3.如何使用帮助文档:

  • 打开API帮助文档
  • 点击显示,并找到索引下面的输入框、
  • 在输入框中输入类名并点击显示
  • 查看类所在的包
  • 查看类的描述(一般情况下,只需要看第一行)
  • 查看构造方法
  • 查看成员方法

一、String

1、String概述

java.lang.String类代表字符串,java程序中的所有字符串文字(例如:”abc“)都为此类的对象。

String 是java定义好的一个类。定义在java.lang包中,所以使用的时候不需要导包。

String name = "尼古拉斯";
String schoolName = "黑马程序员";

2.String的注意点

字符串的内容是不会发生改变的,它的对象在创建后不能被更改

String name = "爱因斯晨";
String schoolName = "黑马程序员";
System.out.println(name+schoolName);

3.创建String对象的两种方式

  1. 直接赋值

    String name = "爱因斯晨";
    
  2. new

    构造方法 说明
    public String () 创建空白字符串,不含任何内容
    public String (String original ) 根据传入的字符串,创建字符串对象
    public String(char [] chs) 根据字符数组,创建字符串对象
    public String (byte [] chs) 根据字节数组,创建字符串对象
    public class zifuu {
        public static void main(String[] args) {
            //直接赋值
            String s1 = "abc";
            System.out.println(s1);
    
            //使用new的方式创建字符串
            //空参构造
            String s2 = new String();
            System.out.println("@"+s2+"#");
    
            //传递一个字符串,根据传递的字符串内容再创建一个新的字符串对象
            String s3 = new String(original:"abc");
            System.out.println(s3);
    
            //传递一个字符数组,根据字符数组的内容,再创建一个新的字符串对象
            char[] ch = {'a','b','c'};
            String s4 = new String(ch);
            System.out.println(s4);
            
            //传递一个字节数组,根据字节数组的内容再创建一个新的字符串对象。
            byte [] bytes ={97,98,99};
            String s5 = new String (bytes);
            System.out.println(s5);
    
        }
    }
    
    

3.区别与用法:

Java的内存模型
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

注意:StringTable(串池)在JDK7版本开始,从方法区中挪到了堆内存

举例1:

public class Zicun {
    public static void main(String[] args) {
        String s1 = "abc";
        String s2 = "abc";
        System.out.println(s1);
        System.out.println(s2);
    }
}

在这里插入图片描述

当使用双引号直接赋值的时候,系统会检查该字符串在串池中是否存在。

不存在:创建新的

存在:复用

举例2:

public class fif {
    public static void main(String[] args) {
        char[] ch = {'a','b','c'};
        String str = new String(ch);
        String str1 = new String(ch);
    }
}

在这里插入图片描述

地址值不会复用

综上所述:我们推荐使用直接赋值,不仅因为书写简单而且内存方便

4.java的常用方法(比较)

public class bijiao {
    public static void main(String[] args) {
        String str1 = "abc";//串池里面有地址
        String str2 = "abc";//复用串池里面的地址
        System.out.println(str1==str2);
    }
}
//run:true
public class bijiao {
    public static void main(String[] args) {
        String str1 = "aaa";
        String str2 = "bbb";
        System.out.println(str1==str2);
    }
}
//run:false
public class bijiao {
    public static void main(String[] args) {
        String str1 = new String("abc");//记录的是堆里面的地址
        String str2 = "abc";//记录的是串池里面的地址
        System.out.println(str1==str2);
    }
}
//run:false

==号比的到底是什么?

  1. 基本数据类型:比较的是数据值

    int a = 10;
    int b = 20;
    System.out.println(a==b);//false
    
  2. 引用数据类型:比较的是地址值

    String s1 = new String ("abc");
    String s2 = new String ("abc");
    System.out.println(s1==s2);//false
    

字符串比较的方法:

boolean equals 方法(要比较的字符串)
完全一样结果才是true,否则为false

boolean equalslgnoreCase(要比较的字符串)
忽略大小写的比较
public class Stringd {
    public static void main(String[] args) {
        //1.创建两个字符串对象
        String s1 = new String("abc");
        String s2 = "ABC";
        //2.==号比较
        //基本数据类型:比较的是数据值是否相同
        //引用数据类型:比较的是地址值是否相同
        System.out.println(s1 == s2);
        //3.比较字符串内容是否相同
        boolean result =s1.equals(s2);
        System.out.println(result);
        //4.忽略大小写比较字符串内容是否相同
        //忽略大小写只能是英文
        boolean result2 = s1.equalsIgnoreCase(s2);
        System.out.println(result2);
    }
}

//run:
//false
//false
//true

//键盘录入:
import java.util.Scanner;
public class llu {
    public static void main(String[] args) {
        //1.假设键盘录入一个abc
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入一个字符串:");
        String s = sc.next();
        //2.代码中再定义一个字符串abc
        String s2 = "abc";
        //3.比较两个字符串内容是否相同
        boolean result = s.equals(s2);
        System.out.println(result);
    }

}

练习:

//练习:已知正确的用户名和密码,请用程序实现模拟用户登录。总共给三次机会,登录之后,给出相应的提示。

import java.util.Scanner;
public class yanzheng {
    public static void main(String[] args) {
        //定义正确的用户名和密码
        String rightUserName = "aiyinsichen";
        String rightPassword = "123456";
        Scanner sc = new Scanner(System.in);
        //模拟用户输入名字与密码,键盘录入
        for (int i = 1; i <= 3; i++) {
            System.out.println("请输入用户名:");
            String userName = sc.next();
            System.out.println("请输入密码:");
            String password = sc.next();
            //比较用户名和密码
            if (rightUserName.equals(userName) && rightPassword.equals(password)) {
                System.out.println("登录成功");
                break;
            }else {
                System.out.println("密码或用户名错误,登录失败,请重新输入");
            }
        }
    }
}

遍历字符串

需求:键盘录入一个字符串,使用程序实现在控制台遍历该字符串

public char charAt(int index):根据索引返回字符
public int length():返回此字符串的长度
//遍历字符串
import java.util.Scanner;
public class luru {
    public static void main(String[] args) {
       Scanner sc = new Scanner(System.in);
       System.out.println("请输入一个字符串:");
       String a = sc.nextLine();
       //a.length().fori 回车
       for (int i = 0; i < a.length(); i++) {
            //i依次表示字符串的每一个索引
           char c = a.charAt(i);
           System.out.println(c);
        }
    }
}

//键盘录入一个字符串,统计该字符串中大写字母字符,小写字母字符,数字字符出现的次数
import java.util.Scanner;
public class shai {
    public static void main(String[] args) {
        //键盘录入一个字符串
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入一个字符串:");
        String str = sc.next();
        //定义三个统计变量,初始值都为0
        int bigCount = 0;
        int smallCount = 0;
        int numberCount = 0;
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            if (c >= 'A' && c <= 'Z') {
                //char类型的变量在参与计算的时候自动类型提升为int 查询ascii码表
                bigCount++;
            } else if (c >= 'a' && c <= 'z') {
                smallCount++;
            } else if (c >= '0' && c <= '9') {
                numberCount++;
            }
        }
        System.out.println("大写字母字符:" + bigCount);
        System.out.println("小写字母字符:" + smallCount);
        System.out.println("数字字符:" + numberCount);

    }
}

拼接字符串

//拼接
//定义一个方法,把int数组中的数据按照指定的格式拼接成一个字符串返回,调用该方法,并在控制台输出结果。
//例如,数组为int[] arr = {1,2,3}; ,执行方法后的输出结果为:[1, 2, 3]
public class pinjie {
    public static void main(String[] args) {
        int[] arr = {1,2,3};
        String s = arrayToString(arr);
        System.out.println(s);
    }
    public static String arrayToString(int[] arr){
        if (arr == null){
            return "";
        }
        if (arr.length == 0){
            return "[]";
        }
        String s = "[";
        for (int i = 0; i < arr.length; i++) {
            if (i == arr.length - 1){
                s = s + arr[i];
            }
            else{
                s = s + arr[i] + ",";
            }
        }
        s = s + "]";
        return s;
    }
}

反转字符串

//定义一个方法,实现字符串反转
//键盘录入一个字符串,调用该方法,并在控制台输出结果
import java.util.Scanner;
public class fanZhuan {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入一个字符串:");
        String s = sc.next();
        //调用方法
        String s1 = fan(s);
        System.out.println("反转后的字符串为:"+s1);
    }
    public static String fan(String s){
        // 将字符串转换为字符数组便于修改
        char[] arr = s.toCharArray();
        // 修正循环初始化和边界条件
        for (int i = 0, j = arr.length - 1; i < j; i++, j--) {
            char temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
        return new String(arr);
    }
}

方法二:
使用自带的反向遍历的方法:
变量名.length().forr 回车
import java.util.Scanner;
public class fanzhuan {  // 字符串反转工具类
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);  
        // 创建控制台输入扫描器
        String n = sc.next();                
        // 读取用户输入的字符串
        String result = fan(n);              
        // 调用反转方法处理输入
        System.out.println(result);           
        // 输出反转结果
    }

    // 字符串反转核心方法
    public static String fan(String s) {    
        // 参数s: 原始字符串
        String s1 = "";
        //s.length().forr回车,就是反向遍历(保留用户原有注释)
        for (int i = s.length()-1; i >= 0; i--) { 
            // 倒序循环:从末位到首位
            char c = s.charAt(i);            
            // 提取当前位置字符
            s1 = s1 + c;                     
            // 反向拼接字符
        }
        return s1;                          
        // 返回反转后的结果
    }
}

综合练习:

金额转换:如键盘录入234.输出大写方法
import java.util.Scanner;
/**
 * Demoutwo 类用于将用户输入的金额转换为中文大写金额表示。
 */
public class Demoutwo {
    /**
     * 程序的入口点,处理用户输入的金额并将其转换为中文大写金额。
     *
     * @param args 命令行参数,在本程序中未使用。
     */
    public static void main(String[] args) {
        // 1. 键盘录入一个金额
        // 创建一个 Scanner 对象,用于从标准输入读取用户输入
        Scanner sc = new Scanner(System.in);
        // 定义变量 money 用于存储用户输入的金额
        int money;
        // 使用 while 循环确保用户输入的金额在有效范围内(0 到 9999999 之间)
        while (true) {
            // 提示用户输入一个金额
            System.out.println("请输入一个金额:");
            // 从标准输入读取一个整数作为金额
            money = sc.nextInt();
            // 检查金额是否在有效范围内
            if (money >= 0 && money <= 9999999) {
                // 如果金额有效,跳出循环
                break;
            } else {
                // 如果金额无效,提示用户并继续循环等待新的输入
                System.out.println("金额无效");
            }
        }

        // 2. 把金额转换为中文
        // 定义一个空字符串 str 用于存储转换后的中文金额
        String str = "";
        // 使用 while 循环将金额逐位转换为中文数字
        while (true) {
            // 获取当前金额的个位数字
            int ge = money % 10;
            // 调用 getChinaNum 方法将个位数字转换为中文数字
            String chinaNum = getChinaNum(ge);
            // 将中文数字拼接到 str 字符串的前面
            str = chinaNum + str;
            // 去掉刚刚处理的个位数字
            money = money / 10;
            // 检查金额是否已经处理完
            if (money == 0) {
                // 如果金额为 0,跳出循环
                break;
            }
        }

        // 3. 补零,补到 7 位
        // 计算需要补充的零的数量
        int count = 7 - str.length();
        // 使用 for 循环在 str 字符串前面补充零
        for (int i = 0; i < count; i++) {
            str = "零" + str;
        }
        // 输出补零后的中文数字字符串
        System.out.println(str);

        // 4. 插入单位
        // 定义一个字符串数组 arr2,包含中文金额的单位
        String[] arr2 = {"佰", "拾", "万", "仟", "佰", "拾", "元"};
        // 使用 for 循环遍历补零后的中文数字字符串
        for (int i = 0; i < str.length(); i++) {
            // 获取当前位置的中文数字字符
            char c = str.charAt(i);
            // 将中文数字字符和对应的单位拼接成一个新的字符串
            String s = c + arr2[i];
            // 输出拼接后的字符串
            System.out.print(s);
        }
    }

    /**
     * 将阿拉伯数字转换为中文大写数字。
     *
     * @param number 要转换的阿拉伯数字(0 到 9 之间)
     * @return 对应的中文大写数字字符串
     */
    public static String getChinaNum(int number) {
        // 定义一个字符串数组 arr,包含中文大写数字
        String[] arr = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};
        // 返回数组中对应索引的中文大写数字
        return arr[number];
    }
}

字符串截取

截取:
String substring (int beginlandex , int endlndex )
注意点:包头不包尾,包左不包右
    只有返回值才是截取的小串
String substring (int beginlndex) 截取到末尾
//加密电话号码:
//15552534681
//加密成:155****4681
import java.util.Scanner;
public class demout {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入你的手机号码");
        String s = sc.nextLine();
        String s1 = s.substring(0,3);
        String s2 = s.substring(7);
        System.out.println(s1+"****"+s2);
    }
}

//练习:
//读取身份证中的信息:出生年月日还有性别
import java.util.Scanner;
public class demofour {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入你的身份证号码");
        String id = sc.next();
        String year = id.substring(6,10);
        String month = id.substring(10,12);
        String day = id.substring(12,14);
        int xing = id.charAt(16);
        String gender;
        if (xing % 2 == 0) {
           gender = "女";
        }else{
            gender = "男";
        }
        System.out.println("人物信息为:");
        System.out.println("出生年月日为:"+year+"年"+month+"月"+day+"日");
        System.out.println("性别为:"+gender);
    }
}

字符串的替换

String replace (旧值,新值)替换
注意点:只有返回值才是替换之后的结果
//练习:敏感词屏蔽
import java.util.Scanner;
public class mingan {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入一句话");
        String said = sc.next();
        String [] cyuyan ={"cnm","sb","tmd"};
        for (int i = 0; i < cyuyan.length; i++) {
            said =said.replace(cyuyan[i],"***");

        }
        System.out.println(said);
    }
}

注意:字符串是常量,他们的值在创建之后不能被改变的。

二、StringBuilder

1.StringBuilder概述

StringBuilder可以看成是一个容器,创建之后里面的内容是可变

  • 作用:提高字符串的操作效率

2。StringBuilder构造方法

方法名 说明
public StringBuilder () 创建一个空白可变字符串对象,不含有任何内容
public StringBuilder (String str ) 根据字符串的内容,来创建可变字符串对象

3.StringBuilder常用方法

方法名 说明
public StringBuilder append (任意类型) 添加数据,并返回对象本身
public StringBuilder reverse () 反转容器中的内容
public int length() 返回长度(字符出现的个数)
public String toString 通过toString()就可以实现把StringBuilder 转换成String
public class builder {
    public static void main(String[] args) {
        //1.创建对象
        StringBuilder sb = new StringBuilder("abc");
        
        //2.添加功能
        sb.append("def");
        sb.append("ghi");
        sb.append("jkl");
        sb.append("mno");
        
        //3.反转功能
        sb.reverse();
        
        //4.获取长度
        int length = sb.length();
    }
}

public class demoo {
    public static void main(String[] args) {
        //1.创建对象
        StringBuilder sb = new StringBuilder("abc");
        //2.添加功能
        sb.append("def");
        sb.append("ghi");
        sb.append("jkl");
        sb.append("mno");
        System.out.println(sb);
        //3.再把StringBuilder变回String
        //为什么要把StringBuilder变回String呢?
        //因为StringBuilder是一个可变的字符串,而String是一个不可变的字符串,所以我们需要把StringBuilder变回String,才能使用String的方法
        String str = sb.toString();
        System.out.println(str);
    }
}

链式编程

当我们在调用一个方法的时候,不需要用变量接受他的结果,可以继续调用其他方法

import java.util.Scanner;

public class lianshi {
    public static void main(String[] args) {
        //链式编程:
        //当我们在调用一个方法的时候,这个方法的返回值是一个对象,我们可以继续调用这个对象的方法。
        //这种方式叫做链式编程。
        int len = getString().substring(1).length();
        System.out.println(len);
    }
    public static String getString() {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入一个字符串:");
        String s = sc.next();
        return s;
    }
}
//链式编程练习
public class lioain {
    public static void main(String[] args) {
        //1.创建对象
        StringBuilder sb = new StringBuilder("abc");

        //2.添加功能
        sb.append("def").append("ghi").append("jkl").append("mno");
        System.out.println(sb);
        String s = sb.toString();
        System.out.println(s);

    }
}

//判断是不是回文串
import java.util.Scanner;
public class xio {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入一个字符串:");
        //键盘录入一个字符串
        String str = sc.next();
        //将字符串反转
        String sb = new StringBuilder().append(str).reverse().toString();
        //判断是否是回文串
        if (sb.equals(str)) {
            System.out.println("是回文串");
        }else {
            System.out.println("不是回文串");
        }
    }
}
//需求:定义方法,把int数组中的数据类型按照指定的格式拼接换成一个字符串返回,调用方法,并在控制台输出结果
public class ding {
    public static void main(String[] args) {
        int[] arr = {1,2,3,4,5,6,7,8,9,10};
        System.out.println(getArray(arr));
    }
    public static String getArray(int[] arr) {
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        for (int i = 0; i < arr.length; i++) {
            if (i == arr.length - 1) {
                sb.append(arr[i]);
            }else {
                sb.append(arr[i]).append(",");
            }
        }
        sb.append("]");

        return sb.toString();
    }
}

三、StringJoiner

我们为什么要学StringJoiner?

我们原来在学习String时拼接元素太耗费时间,于是有了StringBuilder

但是我们在上面练习中,在打印字符串格式时,要做诸多限制,不是很方便于是

就有了StringJoiner。
在这里插入图片描述

1.StringJoiner概述

  • StringJoiner跟StringBuilder一样,也可以看成是一个容器,创建之后里面的内容是可变的
  • 作用:提高字符串的操作效率,而且代码编写的特别简洁,但是市场上很少有人看
  • JDK8出现的

2.StringJoiner的构造方法

没有空参构造

方法名 说明
pubilc StringJoiner(间隔符号) 创建一个StringJoiner对象,指定拼接时的间隔符号
pubilc StringJoiner(间隔符号,开始符号,结束符号 ) 创建一个StringJoiner对象,指定拼接时的间隔符号、开始符号、结束符号
StringJoiner sj = new StringJoiner("---");
1--2--3
StringJoiner sj = new StringJoiner(",","[","]");
[1,2,3]

3.StringJoiner的成员方法

方法名 说明
public StringJoiner add (添加的内容) 添加数据,并返回对象本身
public int length() 返回长度(字符出现的个数)
public String toString() 返回一个字符串(该字符串就是拼接之后的结果)
import java.util.StringJoiner;

public class joner {
    public static void main(String[] args) {
        //1.创建对象
        StringJoiner sj = new StringJoiner("---");
        //2.添加功能
        sj.add("adc").add("def").add("ghi");
        System.out.println(sj);
        
        
        //3.创建对象2
        //delimiter: 分隔符
        //prefix: 前缀
        //suffix: 后缀
        StringJoiner sj2 = new StringJoiner(" ", "[", "]");
        //4.添加功能
        sj2.add("adc").add("def").add("ghi");
        System.out.println(sj2);

    }

}

//adc---def---ghi
//[adc def ghi]
import java.util.StringJoiner;

public class zifu {
    public static void main(String[] args) {
        StringJoiner sj = new StringJoiner(",","[","]");
        int len = sj.add("aaa").add("bbb").add("ccc").length();
        //len不仅是字符串的长度,还包括了[]和,的长度
        System.out.println(len);
    }
}
//13

length不仅是字符串的长度,还包括符号的长度

总结:String与StringBuilder与StringJoiner

String:

表示字符串的类,定义了很多操作字符串的方法

StringBuilder:

一个可变的操作字符串的容器。

可以高效的拼接字符串,还可以将容器里面的内容反转

StringJoiner:

JDK8出现的一个可变的操作字符串的容器,可以高效,方便的拼接字符串。

在拼接的时候,可以指定间隔符号,开始符号,结束符号。

四、字符串原理

扩展底层原理1:字符串存储的内存原理

  • 直接赋值会复用字符串常量池中的
  • new出来不会复用,而是开辟一个新的空间

扩展底层原理2:==号比较的到底是什么?

  • 基本数据类型比较数据值
  • 引用数据类型比较地址值

扩展底层原理3:字符串拼接的底层原理

等号的右边没有变量

public class Test {
    public static void main (String [] args){
        String s = "a"+ "b"+"c";
        System.out.println(s);
    }
}

拼接的时候没有变量,都是字符串。触发字符串的优化机制。在编译的时候就已经是最终的结果了。

在这里插入图片描述

等号的右边有变量

public class Test {
    public static void main (String [] args ){
        String s1 = "a";
        String s2 = s1 + "b";
        String s3 = s2 + "c";
        System.out.println (s3);
        
    }
} 

在拼接的时候有变量,JDK8 以前底层会使用StringBuilder

在这里插入图片描述

内存分析:

1、进入 main 方法

在栈为 main 方法创建帧,用于存局部变量

2、执行变量赋值

String s1 = “a”;:

栈中创建 s1,若字符串常量池无 "a" 则创建,将其引用赋给 s1

String s2 = s1 + “b”;:

栈中创建 s2,堆中创建 StringBuilder 拼接 s1"b"(若常量池无 "b" 则创建),生成新字符串 "ab" 存于堆,引用赋给 s2

String s3 = s2 + “c”;:

栈中创建 s3,堆中新建 StringBuilder 拼接 s2 "c"(若常量池无 "c" 则创建),生成新字符串 "abc" 存于堆,引用赋给 s3

3、输出结果

从栈取s3引用,找到堆中 "abc" 输出。

4、方法结束

main 栈帧销毁,局部变量消失,堆对象等待垃圾回收,常量池字符串保留至程序结束。

**总结:**一个加号,堆内存中俩对象

public class Test {
    public static void main (String [] args ){
        String s1 = "a";
        String s2 = s1 + "b";
        String s3 = s2 + "c";
        System.out.println (s3);
        
    }
} 

字符串拼接的时候有变量参与:

在内存中创建了很多对象,浪费空间,时间也非常的慢

结论:如果很多字符串变量拼接,不要直接+。在底层会创建多个对象,浪费时间,浪费性能

总结:

  • 如果没有变量参与,都是字符串直接相加,编译之后就是拼接之后的结果,会复用串池中的字符串
  • 如果没有变量参与,每一行拼接的代码,都会在内存中创建新的字符串,浪费内存

扩展底层原理4:StringBuilder提高效率原理图

public class Test {
    public static void main (String [] args){
        StringBuilder sb = new StringBuilder();
        sb.append("a");
        sb.append("b");
        sb.append("c");
        System.out.println(sb); 
    }
}

在这里插入图片描述

StringBuilder 是一个可变的容器

内存分析

1、进入 main 方法

JVM 在栈为 main 方法创建帧,用于存储局部变量。

2、执行代码

StringBuilder sb = new StringBuilder();:

栈中创建局部变量 sb,堆中创建 StringBuilder 对象,sb 指向该对象。

sb.append(“a”);:

检查字符串常量池,若没有 "a" 则创建,StringBuilder 对象将 "a" 内容添加到自身存储区域。

sb.append(“b”);:

同理,若常量池无 "b" 则创建,StringBuilder 追加 "b"

sb.append(“c”);:

若常量池无 "c" 则创建,StringBuilder 追加 "c"

System.out.println(sb);:

从栈中获取 sb 引用,找到堆中 StringBuilder 对象,输出其内容。

2、方法结束

main 方法栈帧销毁,局部变量 sb 消失,堆中 StringBuilder 对象等待垃圾回收,字符串常量池中的 "a""b""c" 保留到程序结束。

//面试水题:
//问题:下列代码的运行结果是什么?
public static void main (String [] args ){
    String s1 = "abc";//记录串池中的地址值
    String s2 = "ab";
    String s3 = s2 + "c";//新new出来的对象,地址值不一样
    System.out.println(s1==s3);
    //==比较的是引用局部变量的地址值
}
//flase

字符串拼接的时候,如果有变量:

JDK8:系统底层会自动创建一个StringBuilder对象,然后再调用其append方法完成拼接。拼接后,再调用其toString方法转换为String类型,而toString方法的底层是直接new了一个字符串对象。

JDK8版本:系统会预估要字符串拼接之后的总大小,把要拼接的内容都放在数组中,此时也是产生一个新的字符串。

public class Test{
    public static void main (String [] args ){
        String s1 = "abc";//记录串池中的地址值
        String s2 = "a"+"b"+"c";
        //没有变量,触发优化机制,编译时,就会将“a”+"b"+"c"拼接为"abc"
        //复用串池中的字符串
        System.out.println(s1==s2);
    }
}
//ture

总结:

  • 所有要拼接的内容都会往StringBuilder中放,不会创建很多无用的空间,节约内存

扩展底层原理5:StringBuilder的源码分析

  • 默认创建一个长度为16的字节数组
  • 添加的内容长度小于16,直接存
  • 添加的内容大于16会扩容 (原来的容量 *2 +2)
  • 如果扩容之后还不够,以实际长度为准

System.out.println(sb);:

从栈中获取 sb 引用,找到堆中 StringBuilder 对象,输出其内容。

2、方法结束

main 方法栈帧销毁,局部变量 sb 消失,堆中 StringBuilder 对象等待垃圾回收,字符串常量池中的 "a""b""c" 保留到程序结束。

//面试水题:
//问题:下列代码的运行结果是什么?
public static void main (String [] args ){
    String s1 = "abc";//记录串池中的地址值
    String s2 = "ab";
    String s3 = s2 + "c";//新new出来的对象,地址值不一样
    System.out.println(s1==s3);
    //==比较的是引用局部变量的地址值
}
//flase

字符串拼接的时候,如果有变量:

JDK8:系统底层会自动创建一个StringBuilder对象,然后再调用其append方法完成拼接。拼接后,再调用其toString方法转换为String类型,而toString方法的底层是直接new了一个字符串对象。

JDK8版本:系统会预估要字符串拼接之后的总大小,把要拼接的内容都放在数组中,此时也是产生一个新的字符串。

public class Test{
    public static void main (String [] args ){
        String s1 = "abc";//记录串池中的地址值
        String s2 = "a"+"b"+"c";
        //没有变量,触发优化机制,编译时,就会将“a”+"b"+"c"拼接为"abc"
        //复用串池中的字符串
        System.out.println(s1==s2);
    }
}
//ture

总结:

  • 所有要拼接的内容都会往StringBuilder中放,不会创建很多无用的空间,节约内存

扩展底层原理5:StringBuilder的源码分析

  • 默认创建一个长度为16的字节数组
  • 添加的内容长度小于16,直接存
  • 添加的内容大于16会扩容 (原来的容量 *2 +2)
  • 如果扩容之后还不够,以实际长度为准

网站公告

今日签到

点亮在社区的每一天
去签到