String
- 1 创建字符串
- 2 比较字符串
- 2.1 比较相同
- 2.2 比较大小
- 3 字符串常量池
- 4 String 不可变
- 5 String 和字符数组相互转换
- 6 String 和字节数组相互转换
- 7 字符串常用操作
- 7.1 字符串查找
- 7.2 字符串替换
- 7.3 字符串拆分
- 7.4 字符串截取
- 7.5 去左右空白符
- 7.6 变大小写
- 8 StringBuffer / StringBuilder
1 创建字符串
String 在java.lang 这个包中,这是一个特殊的包,最基础最常用,使用这个包不需要导入(import)。
// 第一种
String str = "hello";
// 第二种
String str2 = new String("hello");
// 第三种
char[] array = {'a','b','c'};
String str3 = new String(array);
// 注意:和C不同,Java中的字符串String和字符数组 char[]无关
String 还支持很多的构造方式,可见 API官方文档
2 比较字符串
2.1 比较相同
String a = "hello";
String b = "hello";
String c = new String ("hello");
// 引用a 和 引用b中存储的地址是相同的
System.println(a == b); // true
// 两个字符串中存储的内容一样
System.println(a.equals(b)); // true
// c 是新创建出来的,和 a 的地址不同
System.println(a == c); // false
// 存储内容相同
System.println(a.equals(c)); // true
注意区分
(1) " == " 比较引用 a 和 b 中存储的地址;
(2) " .equals " 比较的是两个字符串的内容,依次遍历字符串的内容。
内存中的样子:
String a = null;
// 这样写,如果 a 是空指针,会出现异常
// if(a.equals("hello")) {
// }
// 这样是可以通过的
if(("hello").equals(a)) {
//括号中是 false
}
2.2 比较大小
String a = "Hello";
String b = "hallo";
// a 比 b 大,返回 >0 的结果
// a 比 b 小,返回 <0 的结果
// a 和 b 相等,返回 0
// (并不是 0、1、-1)
// 这里是ASCII码值作差的结果,但不同版本的 JDK不同
// 区分大小写
int result = a.compareTo(b); // 小于 0 的结果
int result2 = a.compareToIgnoreCase(b); // 0
// 不区分大小写
int result3 = a.equalsIgnoreCase(b); // true
3 字符串常量池
所谓常量,就是一个不可变的量,而池在计算机中的应用很多,是一个比较重要的术语,是为了降低开销,提高效率,把会频繁使用的东西保存好,然后要用的时候就不用重新创建,直接就可以用。
如果某个字符串已经存在在常量池中,而我们又重新创建了一个,这样就相当于浪费了内存,这时,有一个办法可以解决这样的问题。
String a = "hello";
String b = new String("hello");
System.out.println(a == b); // false
String a = new String("hello").intern();
System.out.println(a == "hello"); // true
这里 .intern( ),调用该方法就会在字符串常量池中找和这个新创建的字符串相同的,如果找到了就将字符串常量池中的字符串的地址返回给引用,然后这里新创建的对象,因为没有引用指向它,就会被垃圾回收机制给回收掉;如果没有找到就返回新创建的这个字符串的地址。
常量池是JVM划分的一个内存区域,其中的常量一方面来自于编译 java 代码时,识别出字面常量值就会自动加到常量池中,另一方面,调用 .intern 方法时,没有在池中找到当前字符串,就会将该字符串加进去。
4 String 不可变
String 之所以不可变,一方面,内容不能被外面访问到;另一方面,没有public 方法可以让我们在类的外部修改它。
特殊手段:反射:
String a = "hello";
// 1)获取到 String 的类对象
// 2)根据 ”value“ 这个字段名字,在类对象中拿到对应的字段
// (仍然是图纸的一部分)
Field valueField = String.class.getDeclaredField("value");
// 让 value 这个 private 的成员也可以被访问到
valueField.setAccessible(true);
// 3)根据图纸,把 a 这个对象给拆开,取出里面的零件
char[] value = (char[])valueField.get(a);
// 4) 修改零件内容
value[4] = 'a';
System.out.println(a);
反射与封装相对,使用反射打破封装;代码比较复杂,容易出错;牺牲了编译器自身的一些检查校验的机制。
5 String 和字符数组相互转换
//
char[] value = {'a','b','c'};
String s = new String(value);
//
String a = "abcd";
System.out.println(a.charAt(0));
System.out.println(a.charAt(1));
System.out.println(a.charAt(2));
System.out.println(a.charAt(3));
System.out.println(a.intern());
// 相当于重新创建了一个新的字符数组并返回
// 修改这个返回值,不会影响到 a 本身的内容
char[] value2 = a.toCharArray();
for(char v: value){
System.out.println(v);
}
value[0] = 'x';
// 并未改变
System.out.println(a);
6 String 和字节数组相互转换
// 字节=>字符串
// 字节数组来构造字符串,需要字节数组中保存的内容符合Java字符的编码方式
byte[] value = {'a','b','c'};
String s = new String(value);
System.out.println(s);
//字节=>字符串
// 把字符串的内容拷贝一份,放到字节数组中
String s = "hello";
s.getBytes();
7 字符串常用操作
7.1 字符串查找
1、判断字符串是否存在
// b 是 a 的子串
String a = "hello world";
String b = "hello";
System.out.println(a.contains(b)); // true
2、返回要找的字符串起始位置的下标,从左到右找,有重复只返回第一个找到的
String a = "hello world world";
String b = "hello";
int result = a.indexOf(b);
System.out.println(result);
// 找重复的
result = a.indexOf(b,result + 1);
System.out.println(result); // 12
3、从后往前查找
String a = "hello world";
String b = "hello";
System.out.println(a.lastIndexOf(b)); // 12
4、判断是否以制定字符串开头、结束
String a = "hello world world";
String b = "hello";
System.out.println(a.startsWith(b)); // true
System.out.println(a.endsWith(b)); // false
7.2 字符串替换
String 是不可变对象,针对字符串的替换操作,不是对原字符串,而是创建了一个新的字符串。
String a = "hello world world";
String b = "hello";
String result = a.replaceAll(b,"java"); // "hello java java"
String result2 = a.replaceFirst(b,"java"); // "hello java world"
// 原字符串不变
System.out.println(a); //"hello world world"
7.3 字符串拆分
把字符串按照一定的分割符,拆成几个部分,放到一个数组中。
String a = "hello world java";
String[] result = a.split(" ");
System.out.println(Arrays.toString(result));
// [hello, world, java]
由于 .split 方法的参数是正则表达式,而 " . " 在正则表达式中有特殊含义," . " 也是其中之一,所以我们需要用正则表达式中的转义字符。
" . " 才会被当成 . 本身看待,但是 " . " 会被字符串当成转义字符,所以要再加一个 \ ,用 " \. " 表示才是一个点
String b = "192.168.1.1";
String[] result = b.split("\\.");
System.out.println(Arrays.toString(result));
// [192, 168, 1, 1]
7.4 字符串截取
注意前闭后开
String a = "hello world java";
System.out.println(a.substring(6,11)); // world
System.out.println(a.substring(6)); // world java
7.5 去左右空白符
String a = " hello world \n";
System.out.println(a.trim()); // "hello world"
7.6 变大小写
String a = "Hello";
System.out.println(a.toUpperCase()); // HELLO
System.out.println(a.toLowerCase()); // hello
8 StringBuffer / StringBuilder
可变版本的 String,StringBuffer 和 StringBuilder 操作基本相同。
StringBuilder stringBuilder = new StringBuilder("hello");
stringBuilder.reverse(); // olleh
stringBuilder.delete(2,4); // heo
stringBuilder.insert(2,"world"); // heworldllo
区别:
StringBuffer 采用同步处理,是线程安全的;StringBuilder是异步处理,线程不安全的。