首页 / 资讯中心 / 文章详情

【Java】String

【Java】String ★ FEATURED ARTICLE
String 原理字符串构造String是 Java 里代表字符串的引用类型4 种常见写法//1. 使用字符串常量直接赋值 String s1hello 2026; System.out.println(s1); //2. new String 对象在堆创建对象 String s2new String(hello 2026); System.out.println(s2); //3. 使用字符数组构造字符串 char[] array{h,e,l,l,o, ,2,0,2,6}; System.out.println(array); //4. 使用字节数组构造对象 byte[] bytes{97,98,99,100}; String s4new String(bytes); System.out.println(s4);String 源码核心public final class String implements java.io.Serializable, ComparableString, CharSequence, Constable, ConstantDesc { Stable private final byte[] value; // 底层字节数组存储字符串内容 private final byte coder; // 编码标记LATIN1 / UTF16 private int hash; // 缓存字符串的hash值默认0 private boolean hashIsZero; // 标记hash计算后是否真的为0避免重复计算 }重点1String被final修饰 → 类不能被继承2value是private final byte[]数组引用不可变字符串一旦创建内容不可修改不可变性字符串常量池 StringTable字符串常量池本质是 JVM 里一个HashTable哈希表作用就是缓存字符串常量复用字符串避免重复创建相同字符串节省内存、提升查找效率重点1JDK6常量池在永久代永久代内存容易 OOM2JDK7 重大改动StringTable 从永久代移到堆JDK8 继续保留在堆3底层是 HashTableHashTable 就是 key-value 结构查找速度快什么是 “池化技术”1池提前准备好一批资源放在一处需要的时候直接拿不用每次重新创建资源提高效率例子1每次临时要钱需要的时候再去申请流程麻烦、速度慢不使用池2一次性把一年生活费放到卡里要用直接取池化思想资源提前备好随取随用其他池例子1数据库连接池提前创建一批数据库连接放池子里程序直接拿不用每次新建连接2线程池提前创建线程复用线程避免频繁创建销毁线程开销池化的共同目的减少创建 / 销毁资源的开销提升性能String s1 hello;执行逻辑1) JVM 去StringTable常量池 HashTable查找有没有hello2) 存在直接返回池内字符串的引用复用3) 不存在在常量池中创建hello字符串存入哈希表返回引用String s2 new String(hello);1) 先去常量池找 hello没有就放入常量池2) 在堆内存新建一个 String 对象对象内部 value 数组引用常量池的字符数据结论s1 s2 → falses1 指向常量池对象s2 指向堆上新对象面试问题1为什么 JDK7 把 StringTable 移到堆永久代内存空间小而且永久代 GC 回收效率差放永久代容易内存溢出放到堆中GC 可以正常回收不再使用的字符串常量2StringTable 底层是什么哈希表 HashTable哈希表好处查询快字符串内存存储例1String str1abc; String str2abc; System.out.println(str1str2); //true栈存放局部变量str1、str2里面保存引用地址0x112233堆内的 StringTable 常量池存放字符串字面量 abc代码逻辑1) 执行str1 abcJVM 去常量池查找abc不存在 → 在常量池创建abc地址0x112233栈中str1保存这个地址2) 执行str2 abc去常量池查找已经存在 abc不会新建直接复用池里对象。栈中str2也保存地址0x1122333)str1 str2比较引用地址两者地址完全一样结果true结论双引号直接赋值优先走常量池相同字面量会复用同一个对象例2String str1new String(abc); String str2new String(abc); System.out.println(str1str2); //false代码逻辑1第一次new String(abc)a先处理双引号字面量abc去常量池查找没有就把abc放进 StringTable地址0x112233bnew关键字在堆上创建一个全新的 String 对象地址0x556677这个对象内部value数组引用常量池里的abcc栈变量str1保存堆上新对象地址0x5566772第二次new String(abc)aabc常量池已经存在不再重复创建常量bnew 又在堆上新建另一个 String 对象地址0x998877内部 value 依然指向常量池的abcc栈变量str2保存这个新对象地址0x9988773str1 str2比较栈里保存的堆对象地址0x998877 ≠ 0x556677→false重点1) 只要写new一定会在堆中创建新 String 对象哪怕内容相同2) 两个new String(abc)字符串内容相同但是堆上是两个独立对象地址不同3) 常量池里的abc只会创建一次被两个堆 String 对象的 value 共享面试高频踩坑点1)比较的是引用地址不是字符串内容2) 如果想比较字符串内容要用str1.equals(str2)equals 比较字符内容常用方法String 对象比较运算符核心规则基本数据类型比较变量里面存储的值是否相等引用数据类型String 属于引用类型比较对象的地址判断是不是同一个对象int a10; int b20; int c10; System.out.println(ab); //false 值不相等 System.out.println(ac); // true 值相等 String s1new String(hello); String s2new String(hello); String s3new String(world); String s4s1; System.out.println(s1s2); //false 两个new出来是不同的对象地址不同 System.out.println(s2s3); //false 地址不同 System.out.println(s1s4); //true s4引用s1,指向同一个对象重点new String()每次都会在堆上创建新对象所以哪怕内容一样地址也不一样结果是 falseequals()方法1)Object类原生equals()默认就是比较地址2)String 重写了 equals不再比较地址逐个比较字符串里面的字符内容是否一样 返回booleantrue/falseString s1new String(hello); String s2new String(hello); String s3new String(world); System.out.println(s1s2); //false 地址不同 System.out.println(s1s3); //false 地址不同 System.out.println(s1.equals(s2)); //true 二者字符内容相同、 System.out.println(s1.equals(s3)); //false 二者字符内容不同开发场景判断两个字符串内容是否相等用equals()不要用compareTo()方法字典序比较返回 int1) 字典序按照字符的 Unicode 编码大小依次比较2) 返回值规则a 从第一个字符开始对比遇到不相等字符 → 返回两个字符编码差值b 前面所有字符都相同 → 返回两个字符串长度差值a 返回0两个字符串完全一样b 返回负数当前字符串 参数字符串c 返回正数当前字符串 参数字符串String s1new String(abc); String s2new String(ac); String s3new String(abc); String s4new String(abcdef); // 遇到不相等字符 → 返回两个字符编码差值 // 前面所有字符都相同 → 返回两个字符串长度差值 System.out.println(s1.compareTo(s2)); //-1第2位 b比c小差值为-1 System.out.println(s1.compareTo(s3)); //0全部字符相同 System.out.println(s1.compareTo(s4)); //-3 前3个字符相同长度差值为3compareToIgnoreCase()1)逻辑和compareTo()完全一致忽略大小写进行字典序比较String s1new String(abc); String s2new String(ac); String s3new String(ABC); String s4new String(abcdef); System.out.println(s1.compareToIgnoreCase(s2)); //-1,第2位c和b字符编码差值为1 System.out.println(s1.compareToIgnoreCase(s3)); //0忽略大小写相等 System.out.println(s1.compareToIgnoreCase(s4)); //-3 前3个字符相同二者长度差值为3String 字符串查找方法清单String saaabbbcccaaabbbccc; //获取下标3位置的字符 System.out.println(s.charAt(3)); // b //从字符串开头往后找返回字符c第一次出现的下标 System.out.println(s.indexOf(c)); // 6 //从下标10的位置开始往后查找返回字符c 第一次出现的下标 System.out.println(s.indexOf(c,10)); //15 //从开头向后查找子串bbb 返回子串bbb的起始下标 System.out.println(s.indexOf(bbb,10)); //12 //从字符串末尾向前查找返回字符 c 最后一次出现的下标 System.out.println(s.lastIndexOf(c)); //17 //在下标10 以及他之前的范围从后往前查找字符 c System.out.println(s.lastIndexOf(c,10)); // 8 //从末尾向前查找子串bbb 返回子串的起始下标 System.out.println(s.lastIndexOf(bbb)); // 12 // 在下标10以及它之前的范围从后往前查找子串bbb System.out.println(s.lastIndexOf(bbb,10)); //31indexOf正向查找左→右找第一次出现2lastIndexOf反向查找右→左找最后一次出现带第二个参数fromIndex1indexOf从 fromIndex 位置向右搜索2lastIndexOf从 fromIndex 位置向左搜索注意1所有查找方法找不到目标时统一返回-12charAt 如果下标超出范围不是返回 - 1而是抛出异常转换数值和字符串相互转换数字 / 对象 → 字符串String.valueOf()1String.valueOf(任意类型)可以把数字、布尔、对象全部转为字符串2对象调用 valueOf 时底层会自动调用对象的 toString() 方法如果 Student 类没有重写 toString ()会输出对象地址重写后输出自定义内容3public class Student { String name; int age; public Student(String name, int age) { this.name name; this.age age; } Override public String toString() { return Student : this.name this.age; } }public static void main(String[] args) { //数字转字符串 String s1String.valueOf(1234); String s2String.valueOf(12.34); String s3String.valueOf(true); String s4String.valueOf(new Student(zhangsan,16)); System.out.println(s1); System.out.println(s2); System.out.println(s3); System.out.println(s4); }字符串 → 数值包装类的 parseXxx()1规则aInteger.parseInt(字符串)字符串必须是合法数字不能带字母否则报错NumberFormatException bDouble.parseDouble(字符串)解析浮点数字符串2//字符串转数字 int data1Integer.parseInt(1234); double data2Double.parseDouble(12.34); System.out.println(data1); System.out.println(data2);Integer、Double 是基本数据类型对应的包装类重要坑点1字符串转数字时字符串内容必须是纯数字Integer.parseInt(12a3); // 编译报错2String.valueOf() 几乎万能支持所有基本类型和对象转字符串3区分两个方法aparseInt字符串 → intbString.valueOf任意类型 → StringString 大小写转换核心方法1字符串.toUpperCase()小写 → 大写2字符串.toLowerCase()大写 → 小写3Java 中 String 是不可变字符串调用这两个方法不会修改原来的字符串会返回一个新字符串String s1hello; String s2HELLO; //小写转大写 System.out.println(s1.toUpperCase()); //HELLO //大写转小写 System.out.println(s2.toLowerCase()); //hello重点1String 一旦创建内容不能修改toUpperCase() 只是生成新字符串保留结果 得用变量接收String sabc; s.toUpperCase(); System.out.println(s); // abc 小写转大写原字符串没变 String s2s.toUpperCase(); System.out.println(s2); //ABC补充1字符串里面的数字、符号不受影响只会转换英文字母String strhello123!; System.out.println(str.toUpperCase()); // HELLO123!字符串与字符数组互转核心方法1字符串 → char 字符数组字符串.toCharArray()2char 字符数组 → 字符串new String(字符数组)String shello; //字符串转字符串数组 char[] chs.toCharArray(); for(int i0;ich.length;i) { System.out.print(ch[i]); // hello } System.out.println(); //字符数组转回字符串 String s2new String(ch); System.out.println(s2); //hello重点1toCharArray()返回新的字符数组原字符串不会改变String 不可变2new String(char[])根据字符数组创建新字符串3用途场景需要修改字符串里单个字符的时候可以先转成 char 数组修改数组元素再转回字符串例修改字符串的第 0 个字符//字符串转字符数组 -- 修改元素后字符数组转字符串 String s1hello; char[] arrs1.toCharArray(); arr[0]H; for(int i0;iarr.length;i) { System.out.print(arr[i] ); } //H e l l o System.out.println(); String newStrnew String(arr); System.out.println(newStr); // HellotoCharArray()把字符串里的每一个字符提取出来存入char[]数组String.format () 字符串格式化常用占位符1%d整数decimal2%s字符串string3%f浮点数float4%c单个字符char语法String.format(格式字符串, 参数1, 参数2, 参数3...)作用按照指定模板拼接生成新字符串String sString.format(%d-%d-%d,2026,10,6); System.out.println(s); //%s 字符串占位 String name张三; String str1String.format(姓名%s,name); System.out.println(str1); // %f 浮点数 .2f代表保留2位小数 double score95.678; String str2String.format(成绩 %.2f,score); System.out.println(str2); //组合使用 String str3String.format(姓名%s , 年龄%d , 性别%s , 学号%s , 张三,21,男,2026106); System.out.println(str3);考点1String.format() 返回新字符串原字符串不受影响2占位符数量、类型必须和后面参数匹配否则报错String.format(%d, abc); // 编译报错类型没和后面传入的参数类型匹配3和直接字符串拼接相比模板更清晰适合日期、报表等格式化输出字符串替换// 字符串替换 String strhello world; System.out.println(str.replaceAll(l,_)); // he__o wor_d System.out.println(str.replaceFirst(l,_)); // he_lo worldreplace(CharSequence target, CharSequence replacement)它不使用正则只是纯文本替换重点1String 不可变替换方法不会修改原来的字符串返回的是全新字符串String strhello world; str.replaceAll(l,_); System.out.println(str); //hello world 字符串替换后原字符串不会改变如果想保存结果需要用变量接收String strhello world; str.replaceAll(l,_); System.out.println(str); //hello world 字符串替换后原字符串不会改变 String newStrstr.replaceAll(l,_); System.out.println(newStr); //he__o wor_d2replaceAll和replaceFirst的第一个参数是正则表达式坑举例.在正则里代表任意字符String sa.b.c; System.out.println(s.replaceAll(.,#)); // 输出 ##### . 匹配了所有字符不是匹配 . 字符 // 匹配文本 . 需要转义 System.out.println(s.replaceAll(\\.,#)); // a#b#ca) 匹配文本.需要转义replaceAll(\\., #)b) 如果只是想做普通文本替换不想碰正则优先使用 replace(原文本,新文本)System.out.println(s.replace(\\.,#)); // a#b#c小结1replaceAll全部替换支持正则2replaceFirst只替换第一个支持正则3replace全部替换纯文本不支持正则
阅读完成 · 觉得有帮助?
咨询建站