目录
一、根据特定符号拆分,可以多次拆分,需要用String[ ] 接受。
String字符串不可以改变的根本原因:
String 类内部以 private final char[ ] value 的形式存储,
但类内并未提供修改 char[ ] 内值的方法。
字符串为何不可变的根本原因要看 String 源码:String 内字符串的存储是以字符数组的形式,而且该字符数组被 private final 修饰。

在这里要注意两点:
一、final 修饰后,value[ ] 中的 “value” 不能改变,即长度不可改变,但 value[ ] 中元素是可以改变的,如下图所示:

二、private 修饰后,类外不可访问该属性,但是类内可以有利用上述方法的间隙以此在String类内部修改元素,以此修改字符串。然而,String类并没有给出这种方法。
再次重申字符串不能改变的根本原因:“如果要增加长度或者减少长度就必须新创建一个字符数组,value的值就会变,这里被final修饰,所以长度就不能变。另一种情况就是长度不变,修改字符数组里的值也是不行的,因为String类没有提供这种方法。”
另外值得一提的是,像以下哪种情况:

且包含之后字符串的各种操作使得看似字符串发生了改变,实际上都是生成了新的对象,只不过是修改了s1这个引用类型的指向。
最后一点:
StringBuilder 和 StringBuffer 是可以修改内容的,因为这两个都是通过append() 方法进行赋值:

String类的 += 操作实际上是通过StringBuffer实现的。
String两种对象实例化的区别:
String str1 = "Java";
String str2 = new String("Java");
str1 是直接赋值:只开辟一块堆内存空间,该字符串可以自动保存在对象池(字符串常量池,字符串常量池在堆中)中供下次使用。
str2 是构造方法:开辟两块堆内存空间,通过一块引用内存指向另一块字符串内存,不会自动保存在对象池中,需用 intern() 手动入池。
常用直接赋值法。
字符和字符串的转化:
| 转化类型 | 特性 | 表达式 | 备注 |
| 字符转字符串 | 全转 | String str = new String(ch); | |
| 部分转 | String str = new String(ch, 0 ,2); | 0表示起始下标,2表示长度 | |
| 字符串转字符 | 全转 | Char[ ] ch = str.tocharAt(); | |
| 部分取 | Char ch = str.charAt(2); | 2表示下标 |
字节和字符串的转化:
| 转化类型 | 特性 | 表达式 | 备注 |
| 字节转字符串 | 全转 | String str = new String(bytes1); | |
| 部分转 | String str = new String(bytes1, 0 ,2); | 0表示起始下标,2表示长度 | |
| 字符串转字节 (全转) | 默认转 | byte[ ] bytes1 = str.getBytes(); | 按照UTF-8转化 |
| GBK转 | byte[ ] bytes1 = str.getBytes("GBK"); | 按照GBK转化 |
字符串的修改:
这里的修改是表面上的修改,并非改变内部value值,并非说string可变。
一、借助原字符串:
str = "A" + str.substring(1);
注意:()中的 1 代表在下标为 1 的前一个位置插入修改,且修改后会抛弃前面的内容。
//修改字符串之一:借助原字符串
String str = "lhou";
str = "z"+str.substring(1);
System.out.println(str); //结果为 zhou
str = "z"+str.substring(2);//前面的会扔掉
System.out.println(str); //结果为 zou
二、借助反射(不常用):
Field valueField = String.class.getDeclaredField("value");
valueField.setAccessible(true);
char[ ] value = (char[ ]) valueField.get(str);
value[0] = 'a';
通过反射修改该字段的访问属性,获取value后通过 value[ ] 修改值。
注意:[ ] 中的 0 代表该字符串下标,且修改后不会抛弃前面的内容。
//修改字符串之二:借助反射
String str = "moximoxi";
// 获取 String 类中的 value 字段. 这个 value 和 String 源码中的 value 是匹配的
Field valueField = String.class.getDeclaredField("value");
// 将这个字段的访问属性设为 true //main方法要抛异常
valueField.setAccessible(true);
// 把 str 中的 value 属性获取到.
char[] value = (char[]) valueField.get(str);
// 修改 value 的值
value[0] = 'o';
System.out.println(str);
value[3] = 'z';
System.out.println(str);//只修改具体某一位不会抛弃前面的
字符串的比较:
| 类型 | 代码 | 注意点 |
| 区分大小写的比较 | str1.equals(str2); | str1 > str2,返回 true str1 = str2,返回 0 str1 < str2,返回 false |
| 不区分大小写的比较 | str1.equalsIgnoreCase(str2); | str1 > str2,返回 true str1 = str2,返回 0 str1 < str2,返回 false |
| 比较两个字符串大小的关系 | str1.compareTo(str2); | str1 > str2,返回正整数 str1 = str2,返回 0 str1 < str2,返回负整数 |
注:str1 与 str2 的比较并非源码中的内容,只是该方法在本层面的直观体现。以下皆同。
字符串的查找:
| 类型 | 代码 | 注意点 |
| 查找一个字符串是否存在 | str1.contains(str2); | 存在返回 true,不存在返回 false |
| 从头开始查找一个字符串是否存在 | str1.indexOf(str2); | 存在返回下标,否则返回-1 |
| str1.indexOf(str2, 2); | 2代表从指定下标位置开始查找 | |
| 从后往前查找一个字符串是否存在 | str1.lastIndexOf(str2); | 存在返回下标,否则返回-1 |
| str1.lastIndexOf(str2, 2) | 2代表从指定下标位置开始查找 |
字符串的判断:
| 类型 | 代码 | 注意点 |
| 判断是否以指定字符串开头 | str1.startsWith("ab"); | 是返回 true,不存在返回 false |
| 判断指定位置是否以指定字符串开头 | str1.startsWith("a", 3); | 区分大小写的判断,3代表下标 |
| 判断是否以指定字符串结尾 | str1.endsWith("a"); | 不能指定位置 |
字符串的替换:
| 类型 | 代码 | 注意点 |
| 只替换首个 | str1.replaceFirst("zhou", "liu"); | 主要基于正则表达式 |
| 全部替换 | str1.replace("zhou", "liu"); | 主要基于字符和字符串 |
| str1.replaceAll("\\d", "L"); | 主要基于正则表达式 |
1、正则表达式:又名规则表达式,指通过指定的规则寻找字符串。
2、当生成一个 String 类的时候,从方法表面上看是替换了,实际上生成了新的对象。所以修改一个String类的时候,都生成了新的对象,是表面的指向变了。
字符串的截取:
| 类型 | 代码 | 注意点 |
| 有头无尾的截取 | str1.substring(3); | 3 代表元素下标 |
| 有头有尾的截取 | str1.substring(3, 7); | 3代表开始,7代表结束 [ ) |
字符串的去空格、转大小写、入池、拼接、长度、是否为空的:
| 类型 | 代码 | 注意点 |
| 去掉左右的空格 | str.trim(); | 中间空格仍旧保留 |
| 字符串转大写 | str.toUpperCase(); | 全部都转 |
| 字符串转小写 | str.toLowerCase(); | 全部都转 |
| 字符串入池 | str.intern(); | 手动入池 |
| 字符串拼接 | str.concat("enen!"); | 等于“+”,不入池 |
| 取得字符串长度 | str.length(); | 字符串结尾的“\0”只存在C语言中 |
| 判断是否为空字符串 | str.isEmpty(); | 非 null,而是字符串内部长度为空 空格( )并非为空 |
注:str.intern();使用时:常量池中存在直接返回,没有的话创建新的。
字符串的拆分:
一、根据特定符号拆分,可以多次拆分,需要用String[ ] 接受。
String str = "周周&刘刘!哲学&编程!冲冲&冲冲";
System.out.println(str);//先看看元代码
String[] strings = str.split("!"); //第一次按照!拆分
for(String s : strings) { //遍历循环打印出来
System.out.println(s); //添加这条打印后,结果会是一次分割穿插在二次分割的结果中。
String[] ss = s.split("&");
for (String tmp : ss){
System.out.println(tmp);
}
}
二、有的分割时由于部分符号具有特殊含义,所以需要转义。
1、拆分一个IP,其中的 “.” 需要注意。
String str = "192.168.1.1" ;
String[] result = str.split("\\.",7) ;//超出范围最多就分最多分多少组
for(String s: result) {
System.out.println(s);
}
2、两个“ \\ ”需要转义。
String str = "192\\168\\1\\1" ;
String[] result = str.split("\\\\",7);
for(String s: result) {
System.out.println(s);
}
3、当符号较多时可以用“ | ”连接起来。
String str = "哇 不要紧&其实#我*才是@老大" ;
String[] result = str.split(" |&|#|\\*|@", 7);
for(String s: result) {
System.out.println(s);
}
三、关于分割时候的转义需要注意以下几点:
1、字符 “ | ”、“ * ”、“ + ” 都得加上转义字符,前面加上 “ \ ”。
2、如果是 “ " " ”,那么就得写成 “ \\ ”。
3、如果一个字符串中有多个分隔符,可以用 “ | ” 作为连字符。
本文详细探讨了Java中String字符串的不可变性,分析了其内部实现原理,并介绍了字符串的创建方式、转换方法、修改(实际是创建新对象)、比较、查找、判断、替换、截取、去空格、转大小写、入池、拼接、长度和是否为空的检查,以及字符串的拆分操作,特别是涉及到特殊符号和转义字符的情况。
2121

被折叠的 条评论
为什么被折叠?



