JAVA | 聚焦 String 的常见用法与底层内存原理
*个人主页
文章专栏
《赛博算命之梅花易数的JAVA实现》*
文章目录
- *[个人主页](https://blog.csdn.net/2401_87533975?spm=1011.2124.3001.5343)
- 文章专栏
- 《赛博算命之梅花易数的JAVA实现》*
- #前言:API
- 1.定义
- 2.已经学习过的API
- 3.如何使用帮助文档:
- 一、String
- 1、String概述
- 2.String的注意点
- 3.创建String对象的两种方式
- 4.java的常用方法(比较)
- 字符串比较的方法:
- 遍历字符串
- 拼接字符串
- 反转字符串
- 字符串截取
- 字符串的替换
- 二、StringBuilder
- 1.StringBuilder概述
- 2。StringBuilder构造方法
- 3.StringBuilder常用方法
- 三、StringJoiner
- 1.StringJoiner概述
- 2.StringJoiner的构造方法
- 3.StringJoiner的成员方法
- 总结:String与StringBuilder与StringJoiner
- 四、字符串原理
- 扩展底层原理1:字符串存储的内存原理
- 扩展底层原理2:==号比较的到底是什么?
- 扩展底层原理3:字符串拼接的底层原理
- 等号的右边没有变量
- 等号的右边有变量
- 总结:
- 扩展底层原理4:StringBuilder提高效率原理图
- 总结:
- 扩展底层原理5:StringBuilder的源码分析
- 总结:
- 扩展底层原理5:StringBuilder的源码分析
#前言: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对象的两种方式
-
直接赋值
String name = "爱因斯晨";
-
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
==号
比的到底是什么?
-
基本数据类型:比较的是数据值
int a = 10; int b = 20; System.out.println(a==b);//false
-
引用数据类型:比较的是地址值
String s1 = new String ("abc"); String s2 = new String ("abc"); System.out.println(s1==s2);//false
字符串比较的方法:
boolean equals 方法(要比较的字符串)
完全一样结果才是true,否则为falseboolean 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.假设键盘录入一个abcScanner sc = new Scanner(System.in);System.out.println("请输入一个字符串:");String s = sc.next();//2.代码中再定义一个字符串abcString 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();//定义三个统计变量,初始值都为0int 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)
- 如果扩容之后还不够,以实际长度为准
相关文章:
JAVA | 聚焦 String 的常见用法与底层内存原理
*个人主页 文章专栏 《赛博算命之梅花易数的JAVA实现》* 文章目录 *[个人主页](https://blog.csdn.net/2401_87533975?spm1011.2124.3001.5343)文章专栏《赛博算命之梅花易数的JAVA实现》* #前言:API1.定义2.已经学习过的API3.如何使用帮助文档: 一、…...
CAN总线的CC帧和FD帧之间如何仲裁
为满足CAN总线日益提高的带宽需求,博世公司于2012年推出CAN FD(具有灵活数据速率的CAN)标准,国际标准化组织(ISO)2015年通过ISO 11898-1:2015标准,正式将CAN FD纳入国际标准,以示区别…...
提升 React 应用性能:使用 React Profiler 进行性能调优
前言 在现代前端开发中,性能优化是一个不可忽视的重要环节。在 React 生态系统中,React Profiler 是一个强大的工具,它可以帮助我们检测和优化应用的性能。 本文将通过通俗易懂的语言介绍 React Profiler 的作用,并展示如何使用它…...
【QT】-toUtf8() 和 toBase64()的区别
toUtf8() 和 toBase64() 在 Qt 中是两个不同的函数,它们用于不同的目的: toUtf8():将 QString 转换为 UTF-8 编码的字节数组(QByteArray)。 toBase64():将字节数组(通常是二进制数据࿰…...
Git 面试问题,解决冲突
1.问题描述 在多人协作开发中,当多个开发者在同一文件的同一部分进行修改并提交时,Git 无法自动合并这些更改,从而产生代码冲突(Conflict)。冲突的代码会被 Git 标记出来,需要开发者手动解决。 冲突原因 多…...
天梯赛训练L1-031——L1-040
天梯赛训练L1-031——L1-040 L1-031 到底是不是太胖了 import math n int(input()) for i in range(n):h,w map(int,input().split())w / 2biaozhun (h - 100)* 0.9if math.fabs(biaozhun - w) < biaozhun * 0.1:print("You are wan mei!")elif w < biaoz…...
C语言 —— 此去经年梦浪荡魂音 - 深入理解指针(卷二)
目录 1. 数组名与地址 2. 指针访问数组 3.一维数组传参本质 4.二级指针 5. 指针数组 6. 指针数组模拟二维数组 1. 数组名与地址 我们先看下面这个代码: int arr[10] { 1,2,3,4,5,6,7,8,9,10 };int* p &arr[0]; 这里我们使用 &arr[0] 的方式拿到了数…...
面试中文版示例
各位老师好,我是*** ,2010 年毕业于****大学,信息管理与信息系统专 业,获得管理学学士学位,同时学习了*****,取得国家中级物流师认证。 在校期间多次获得一、二等奖学金。作为文艺部部长,经常…...
C++多线程编程 4.condition_variable 条件变量
概念: std::condition_variable 是 C 标准库中用于实现线程间同步的类。它提供了等待和通知的机制,使得线程可以等待某个条件成立时被唤醒,或者在满足某个条件时通知其他等待的线程。 语法: #include <condition_variable&g…...
基于51单片机的12864模拟示波器proteus仿真
地址: https://pan.baidu.com/s/12SGtyqAYKOAjx6rjtTz5Nw 提取码:1234 仿真图: 芯片/模块的特点: AT89C52/AT89C51简介: AT89C51 是一款常用的 8 位单片机,由 Atmel 公司(现已被 Microchip 收…...
C++数据结构哈希表的实现(开散列实现、闭散列实现)
C哈希 1. 哈希概念 哈希作为数据结构时,是一种通过某种哈希函数使元素的存储位置与它的关键码之间建立一一映射的关系,在查找时通过该函数就能快速找到该元素,平均时间复杂度为 O ( 1 ) \rm O(1) O(1) ,且遍历结果是无序的。 …...
显著性检测分类(数据集和评估指标总结)
一:RGB显著性检测 常用数据集 其中有DUTS,ECSSD,DUT-OMRON,PASCAL-S,HKU-IS,SOD,SOC,MSRA-B (1)DUTS:DUTS-TR(训练集):10553张,DUT…...
【R语言】使用DESeq2对微生物组进行差异分析
代码展示: asv <- read.delim(paste0(input,_0.5wen.10050.asv_table.txt), row.names 1, sep \t, stringsAsFactors FALSE, check.names FALSE) group <- read.delim(paste0(group2_,input,.txt),row.names 1,sep \t) asv <- asv1 #将变量转换为因…...
什么是广播系统语言传输指数 STIPA
广播系统语言传输指数(STIPA) 是用于评估公共广播系统中语音信号传输质量的国际标准指标,主要用于衡量语音清晰度和可懂度。以下是其关键信息: 1. 定义与作用 STIPA(Speech Transmission Index for Public…...
【Json—RPC框架】:宏定义不受命名空间限制,续行符的错误使用造成的bug
为什么不受命名空间的限制? 宏处理在预处理阶段, 预处理在编译之前,编译才进行语法分析,语义分析。命名空间也只能限制这部分。 在Json-RPC框架的实现中,遇到如下问题。一开始以为是在实现日志宏的时候,有…...
解决前端文字超高度有滚动条的情况下padding失效(el-scrollbar)使用
<div class"detailsBlocksContent"><div>测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试…...
失败的面试经历(ʘ̥∧ʘ̥)
一.面向对象的三大特性 1.封装:将对象内部的属性私有化,外部对象不能够直接访问,但是可以提供一些可以使外部对象操作内部属性的方法。 2.继承:类与类之间会有一些相似之处,但也会有一些异处,使得他们与众…...
大数据学习(70)-大数据调度工具对比
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言📝支持一…...
Description of a Poisson Imagery Super Resolution Algorithm 论文阅读
Description of a Poisson Imagery Super Resolution Algorithm 1. 研究目标与意义1.1 研究目标1.2 实际意义2. 创新方法与模型2.1 核心思路2.2 关键公式与推导2.2.1 贝叶斯框架与概率模型2.2.2 MAP估计的优化目标2.2.3 超分辨率参数α2.3 对比传统方法的优势3. 实验验证与结果…...
PIP离线安装包
获得离线包 pip freeze >requirements.txt pip download -rrequirements.txt 可以看到pip开始下载依赖包列表中的所有依赖包 安装离线包 如果你希望完全从本地 .whl 文件安装依赖,而不从 PyPI 或其他外网源下载任何包,可以使用 --no-index 参数来…...
动静态库区别
目录 示例 动静态库区别 定义和链接方式 文件大小 内存使用 更新和维护 加载时间 依赖关系 适用场景 动静态库总结 示例 Linux系统中ls也是一个进程,它运行也得依赖动态库,那么学习动静态库区别是有必要的!!!…...
剑指 Offer II 076. 数组中的第 k 大的数字
comments: true edit_url: https://github.com/doocs/leetcode/edit/main/lcof2/%E5%89%91%E6%8C%87%20Offer%20II%20076.%20%E6%95%B0%E7%BB%84%E4%B8%AD%E7%9A%84%E7%AC%AC%20k%20%E5%A4%A7%E7%9A%84%E6%95%B0%E5%AD%97/README.md 剑指 Offer II 076. 数组中的第 k 大的数字 …...
容联云大模型应用入选甲子光年AI Agent产业图谱
近日,甲子光年发布《2025中国AI Agent行业研究报告》,旨在系统梳理AI Agent技术演进与产业重构路径,展示行业标杆厂商及先锋实践。 容联云凭借卓越的Copilot & Agent产品和解决方案,以及在银行、保险等领域的成熟应用验证&…...
机器学习——深入浅出理解朴素贝叶斯算法
文章目录 引言一、朴素贝叶斯定理概述1.从贝叶斯定理说起2.朴素贝叶斯的“朴素”之处3.朴素贝叶斯算法的应用 二、朴素贝叶斯算法的优缺点三、python代码实现案例1.导入库2.数据预处理3.模型训练4.模型评估5.完整代码 四、总结 引言 朴素贝叶斯算法,一个听起来充满…...
TCP/IP协议中三次握手(Three-way Handshake)与四次挥手(Four-way Wave)
TCP/IP协议中三次握手(Three-way Handshake)与四次挥手(Four-way Wave) 一、TCP三次握手(Three-way Handshake)二、TCP四次挥手(Four-way Wave)三、常见问题解答总结为什么三次握手不…...
【CF】Day9——Codeforces Round 953 (Div. 2) BCD
B. New Bakery 题目: 思路: 被标签害了,用什么二分( 很简单的思维题,首先如果a > b,那么全选a就行了,还搞啥活动 否则就选 b - a 天来搞活动,为什么? 首先如果我…...
【AI知识管理系统】(一)AI知识库工具测评
嘿,朋友们!🧐你们有没有想过,咱们平日里那些一闪而过的知识笔记、各种碎片化的idea,记录下来之后都是怎么管理的呀? 还有啊,咱们读过的那些书,大家会不会随手写点东西记录一下呢?📝要知道,如果不写的话,很可能过不了多久就全忘得一干二净啦。 😭那多年前记下的…...
Model Context Protocol 的生命周期
生命周期阶段 生命周期分为三个主要阶段: 初始化阶段 (Initialization) 客户端与服务器建立协议版本兼容性。交换并协商能力。分享实现细节。客户端必须发送 initialize 请求,包含支持的协议版本、客户端能力和客户端实现信息。服务器必须响应其自身能力…...
hot100_part_堆
不该要求事情一开始就是完美。 堆排序 【从堆的定义到优先队列、堆排序】 10分钟看懂必考的数据结构——堆_哔哩哔哩_bilibili 排序算法:堆排序【图解代码】_哔哩哔哩_bilibili 堆定义 堆必须是完全二叉树,从上到下,从左到右不能用空缺。…...
CoreData 调试警告:多个 NSEntityDescriptions 声明冲突的解决
概述 目前在苹果生态 App 的开发中,CoreData 数据库仍然是大部分中小应用的优先之选。不过,运行时 CoreData 常常产生各种“絮絮叨叨”的警告不禁让初学的秃头小码农们云里雾里。 这不,对于下面这一大段 CoreData 警告,大家是否一…...
【白话神经网络(二)】矩阵、CNN、RNN
全连接层 回顾前面学过的知识: 一个最简单的神经网络,就是ywxb 套上一个激活函数。 如果有多个输入,那就是多个w和x 如果有多个输出,那就再来一行公式,多一组w和b 要是神经元多了的话,公式密密麻麻的&…...
map容器练习:使用map容器识别统计单词个数
题目链接:单词识别_牛客题霸_牛客网 对map的使用不太熟悉的同学可以参考:超详细介绍map(multimap)的使用-CSDN博客 题目解析 输入一个英文句子,把句子中的单词(不区分大小写)按出现次数按从多到少把单词和次数在屏幕…...
DeepSeek 是否被过度吹捧了?
DeepSeek 作为中国人工智能领域的后起之秀,其技术进展引发了广泛关注和讨论。然而,DeepSeek 是否被过度吹捧仍然值得客观分析。 DeepSeek 的确取得了不错的成果,不过可能没有媒体宣传和人们想象中那么重大。它的轰动性主要在于以低廉的成本达…...
前端大文件上传(分片上传)与下载
文章目录 一、问题二、思路1、选择文件2、校验文件是否符合规范3、文件切片上传4、分片上传注意点5、大文件下载 一、问题 日常业务中难免出现前端需要向后端传输大型文件的情况,这时单次的请求不能满足传输大文件的需求,就需要用到分片上传 业务需求为…...
【最佳实践】Go 状态模式
设计思路 状态模式的核心在于将对象的行为封装在特定的状态类中,使得对象在不同的状态下表现出不同的行为。每个状态实现同一个接口,允许对象在运行时通过改变其内部状态对象来改变其行为。状态模式使得状态转换更加明确,并且易于扩展新的状…...
如何用Python批量将CSV文件编码转换为UTF-8并转为Excel格式?
在处理数据时,CSV文件格式常常用作数据的交换格式。不过,很多情况下我们会遇到编码问题,特别是当文件不是UTF-8编码时。为了更好地处理这些文件,可能需要将它们转换为UTF-8编码,并且将其转换为Excel格式,这…...
回顾Transformer,并深入讲解替代方案Mamba原理(图解)
一种语言建模中 Transformer 的替代方案 Transformer 架构是大语言模型(LLMs)成功的关键组成部分。几乎所有今天使用的大语言模型都采用了该架构,从开源模型如 Mistral 到闭源模型如 ChatGPT。 为了进一步改进大语言模型,新的架构…...
2025开源风险治理最佳实践︱新能源汽车车企开源风险治理案例
案例来源:悬镜安全 案例背景 当前我国新能源汽车产业蓬勃发展,智能网联趋势持续深化。汽车技术与工程核心逐渐从传统硬件层面转移到软件层面,踏上软件定义汽车(SDV)的变革之路。引用开源组件成为车企、Tier1、Tier2在软件开发过程中的常规操…...
Spring中Bean的自动装配
1.自动装配的核心概念 定义: Bean的自动装配是Spring框架中用于自动满足Bean依赖的一种机制。通过自动装配,Spring容器会在应用上下文中为某个Bean寻找其依赖的Bean,从而减少手动配置的工作量。其核心目标是减少配置代码,通过类型…...
一文掌握 PostgreSQL 的各种指令(PostgreSQL指令备忘)
引言 PostgreSQL 作为一款功能强大、开源的关系型数据库管理系统(RDBMS),以其高扩展性、SQL 标准兼容性以及丰富的功能特性,成为企业级应用的首选数据库之一。无论是开发、运维还是数据分析,掌握 PostgreSQL 的核心指…...
C#入门学习记录(三)C#中的隐式和显示转换
C#类型转换:隐式与显式转换的机制与应用 在C#的强类型体系中,数据类型转换是实现数据交互和算法逻辑的基础操作。当数值类型范围存在包含关系,或对象类型存在继承层次时,系统通过预定义的转换规则实现类型兼容处理。隐式转换&…...
【Linux网络-网络层】TCP与IP的关系+IP协议基本概念+网段划分+路由+IP分片与组装
网络层 在复杂的网络环境中确定一个合适的路径 一、TCP与IP的关系 TCP(传输控制协议)和IP(互联网协议)是互联网协议栈中的两个核心协议,属于不同的层级,分别在传输层和网络层,共同实现数据的可…...
【第K小数——可持久化权值线段树】
题目 代码 #include <bits/stdc.h> using namespace std;const int N 1e5 10;int a[N], b[N]; int n, m, len; int rt[N], idx; // idx 是点分配器struct node {int l, r;int s; } tr[N * 22];int getw(int x) {return lower_bound(b 1, b len 1, x) - b; }int bui…...
需要使用新应用以打开此ms-gamingoverlay链接怎么解决
要解决Windows系统提示“需要使用新应用以打开此ms-gamingoverlay链接”的问题,通常与系统自带的游戏工具栏(Game Bar)或Xbox相关应用缺失或配置错误有关。以下是综合多个来源的详细解决方法: 方法1:关闭游戏栏功能 这…...
五子棋小游戏-简单开发版
一、需求分析 开发一个基于 Pygame 库的五子棋小游戏,允许两名玩家在棋盘上轮流落子,当有一方达成五子连珠时游戏结束,显示获胜信息,并提供退出游戏和重新开始游戏的操作选项。 1.棋盘显示 : 显示一个 15x15 的五子棋…...
C语言内存函数讲解
(一)memcpy函数 这是memcpy函数的说明。它的头文件是string.h。函数原型是 void* memcpy(void* destination, const void* source, size_t num) 第一个参数是一个指向一个字符串的指针,第二个也是一样的。而第三个参数是复制的字节个数。这…...
2018年全国职业院校技能大赛高职组-计算机网络应用竞赛竞赛样题C卷
目录 总体规划 模块二:设备基础信息配置 模块三:网络搭建与网络冗余备份方案部署 模块四:移动互联网搭建与网优 模块五:出口安全防护与远程接入 总体规划 CII教育公司在进行企业大学信息化建设的过程中,为了保证北京校区、广州校区与本部校区的日常OA办公通信等关键业务,…...
《解锁Flutter:跨平台开发的未来之光》:此文为AI自动生成
《解锁Flutter:跨平台开发的未来之光》:此文为AI自动生成 Flutter:崭新时代的跨平台框架 在当今数字化浪潮中,移动应用已成为人们生活中不可或缺的一部分。无论是购物、社交、娱乐还是办公,我们都离不开各种手机应用…...
基于大数据的酒类商品数据可视化分析系统
【大数据】基于大数据的酒类商品数据可视化分析系统 (完整系统源码开发笔记详细部署教程)✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 该系统充分利用Python与Flask的强大后端处理能力,结合前端Layui框架࿰…...
【数学建模】一致矩阵的应用及其在层次分析法(AHP)中的性质
一致矩阵在层次分析法(AHP)中的应用与性质 在层次分析法(AHP)中,一致矩阵是判断矩阵的一种理想状态,它反映了决策者判断的完全合理性和一致性,也就是为了避免决策者认为“A比B重要,B比C重要,但是C又比A重要”的矛盾。…...