🚀 创建字符串 String
// 方式一:使用常量串构造
String str = "Hello Bit";
// 方式二:直接newString对象
String str2 = new String("Hello Bit");
// 方式三:使用字符数组进行构造
char[] array = {'a', 'b', 'c'};
String str3 = new String(array);
🔥 常量池(了解):由于String 类型描述的字符串内容是常量不可改变,因此 Java 虚拟机将首次出现的字符串放入常量池中,若后续代码中出现了相同字符串内容则直接使用池中已有的字符串对象而无需申请内存及创建对象,从而提高了性能
在下面的例子中, String类的两种实例化操作, 直接赋值和 new 一个新的 String
(1)直接赋值
String str1 = "hello" ;
String str2 = "hello" ;
String str3 = "hello" ;
System.out.println(str1 == str2); // true
System.out.println(str1 == str3); // true
System.out.println(str2 == str3); // true
为什么现在并没有开辟新的堆内存空间呢? String类的设计使用了共享设计模式 在JVM底层实际上会自动维护一个对象池(字符串常量池)
理解 "池" (pool)
(2)采用构造方法
类对象使用构造方法实例化是标准做法。分析如下程序:
String str = new String("hello") ;
这样的做法有两个缺点:
// 该字符串常量并没有保存在对象池之中
String str1 = new String("hello") ;
String str2 = "hello" ;
System.out.println(str1 == str2);
// 执行结果
false
String str1 = new String("hello").intern() ;
String str2 = "hello" ;
System.out.println(str1 == str2);
// 执行结果
true
因此我们就可以知道String类中两种对象实例化的区别
字符串是一种不可变对象. 它的内容不可改变.
举个例子 :
String str = "hello" ;
str = str + " world" ;
str += "!!!" ;
System.out.println(str);
// 执行结果
hello world!!!
形如 += 这样的操作, 表面上好像是修改了字符串, 其实不是. 内存变化如下:
+= 之后 str 打印的结果却是变了, 但是不是 String 对象本身发生改变, 而是 str 引用到了其他的对象.
回顾引用
那么如果实在需要修改字符串, 例如, 现有字符串 str = "Hello" , 想改成 str = "hello" , 该怎么办?
a. 常见办法:
String str = "Hello";
str = "h" + str.substring(1);
System.out.println(str);
// 执行结果
hello
String str = "Hello";
b. 特殊办法(选学):
String str = "Hello"
// 获取 String 类中的 value 字段. 这个 value 和 String 源码中的 value 是匹配的.
Field valueField = String.class.getDeclaredField("value");
// 将这个字段的访问属性设为 true
valueField.setAccessible(true);
// 把 str 中的 value 属性获取到.
char[] value = (char[]) valueField.get(str);
// 修改 value 的值
value[0] = 'h';
System.out.println(str);
// 执行结果
hello
关于反射
为什么 String 要不可变?(不可变对象的好处是什么?) (选学)
综上,我们对于下面这种代码就知道它会产生大量的临时对象,效率比较低,应该被避免
String str = "hello" ;
for(int x = 0; x < 1000; x++) {
str += x ;
}
System.out.println(str);
public static void main(String[] args) {
String s1 = new String("hello");
String s2 = new String("world");
String s3 = s1;
System.out.println(s1 == s2); //false
System.out.println(s2 == s3); //false
System.out.println(s1 == s3); //true
}
在String重写了Object类的equals方法后,就大大简化了我们的比较过程
案例:
String str = new String("Hello World");
//注意:开发中作比较一般常量值在前,变量值在后
System.out.println("hello world".equals(str));//false
System.out.println("hello world".equalsIgnoreCase(str));//true
equals 注意事项
现在需要比较字符串 s 和 "hello" 两个字符串是否相等, 我们该如何来写呢?
// 方法1
System.out.println("hello".equals(s));
// 方法2
System.out.println(s.equals("hello"));
在上面的代码中, 哪种方式更好呢? 我们更推荐使用 "方式1",一旦 str 是 null, 方式2 的代码会抛出异常, 而方式二不会
注意事项: "Hello" 这样的字面值常量, 本质上也是一个 String 对象, 完全可以使用 equals 等 String 对象的方法
相比于上面 equals比较返回 true,compareTo 返回的是 int 类型
比较大小的方法:
注意:比较出大小就不往后进行,即从第一个字符串开始比较,相同则比较下一个,直到比较出大小或比较到最后一个字符。
案例:
String str = "hello";
System.out.println(str.compareTo("world")); // 'h' - 'w' => 104 - 119 => -15
System.out.println(str.compareTo("haha")); // 'e' - 'a' => 101 - 97 => 4
System.out.println(str.compareTo("heihei")); // 'l' - 'i' => 108 - 105 => 3
System.out.println(str.compareTo("helloworld")); // 长度: 5 - 10 => -5
System.out.println(str.compareToIgnoreCase("HELLO")); // 0
方法名称 | 作用 |
---|---|
char charAt (int index) | 返回index位置上字符,如果index为负数或者越界,抛出 IndexOutOfBoundsException 异常。 |
int indexOf (String str) | 返回 str 第一次出现的位置,没有返回 -1 |
int indexOf (String str, int fromIndex) | 从 fromIndex 位置开始找 ch 第一次出现的位置,没有返回 -1 |
int indexOf (String str) | 返回 str 第一次出现的位置,没有返回 -1 |
int indexOf (String str, int fromIndex) | 从 fromIndex 位置开始找 str 第一次出现的位置,没有返回 -1 |
int lastIndexOf (String str) | 从后往前找,返回 str 第一次出现的位置,没有返回 -1 |
Int lastIndexOf (String str, int fromIndex) | 从 fromIndex 位置开始找,从后往前找 ch 第一次出现的位置,没有返回-1 |
int lastIndexOf (String str) | 从后往前找,返回 str 第一次出现的位置,没有返回 -1 |
int lastIndexOf(String str, int fromIndex) | 从 fromIndex 位置开始找,从后往前找 str 第一次出现的位置,没有返回-1 |
boolean contains (CharSequence s) | 判断当前字符串是否包含参数指定的内容,参数 CharSequence 为一个接口,CharSequence 是 char 值的可读序列,参数可以为String、StringBuilder、StringBuffer等类型 |
boolean startWith (String prefix) | 判断是否以指定字符串 prefix 开头 |
boolean startWith (String prefix, int toffset) | 从指定位置开始判断是否以指定字符串 prefix 开头 |
boolean endWith (String prefix) | 判断是否以指定字符串 prefix 结尾 |
案例:
String s = "aaabbbcccaaabbbccc";
System.out.println(s.charAt(3)); //'b'
System.out.println(s.indexOf('c')); //6
System.out.println(s.indexOf('c', 10)); //15
System.out.println(s.indexOf("bbb")); //3
System.out.println(s.indexOf("bbb", 10)); //12
System.out.println(s.lastIndexOf('c'));//17
System.out.println(s.lastIndexOf('c', 10)); //8
System.out.println(s.lastIndexOf("bbb"));//12
System.out.println(s.lastIndexOf("bbb",10));//5
System.out.println(s.contains("aa")); // true
System.out.println(s.startsWith("bb")); // false
System.out.println(s.startsWith("bb", 3)); // true
System.out.println(s.endsWith("cc")); // true
方法名称 | 作用 |
---|---|
String replace (char oldChar, char newChar) | 使用参数newChar替换此字符串中出现的所有参数oldChar |
String replace (char oldChar, char newChar) | 用新字符串replacement替换所有的旧字符串target |
String replaceAll (String regex, String replacement) | 替换所有指定内容 |
String replaceFirst (String regex, String replacement) | 替换首个内容 |
⭐ 注意事项: 由于字符串是不可变对象, 替换不修改当前字符串, 而是产生一个新的字符串!!
可以将一个完整的字符串按照指定的分隔符划分为若干个子字符串
方法名称 | 作用 |
---|---|
String[] split (String regex) | 将字符串全部拆分 |
String[] split (String regex, int limit) | 将字符串以指定的格式,拆分为 limit 组 |
代码示例: 实现字符串的拆分处理
String str = "hello world island" ;
String[] result = str.split(" ") ; // 按照空格拆分
for(String s: result)
{
System.out.println(s);
}
// 结果如下:
hello
world
island
代码示例: 字符串的部分拆分
String str = "hello world island" ;
String[] result = str.split(" ",2) ;
for(String s: result) {
System.out.println(s);
}
// 结果如下:
hello
world island
注意:拆分是特别常用的操作. 一定要重点掌握. 另外有些特殊字符作为分割符可能无法正确切分, 需要加上转义.
// ip 地址拆分
String str1 = "192.168.1.1" ;
String[] result1= str1.split("\\.") ;
for(String s: result1) {
System.out.println(s);
}
// 多次拆分
String str2 = "name=island1314&age=18" ;
String[] result2 = str2.split("&") ;
for (int i = 0; i < result2.length; i++) {
String[] temp = result2[i].split("=") ;
System.out.println(temp[0]+" = "+temp[1]);
}
方法名称 | 作用 |
---|---|
String[] substring (int beginIndex) | 从指定索引截取到结尾 |
String[] substring (int beginIndex, int endIndex) | 截取部分内容 |
String str = "helloworld" ;
System.out.println(str.substring(5));
System.out.println(str.substring(0, 5));
注意事项:
方法名称 | 作用 |
---|---|
String trim () | 去掉字符串的左右空格,保留中间空格 |
String toUpperCase () | 字符串转大写 |
String toLowerCase () | 字符串转小写 |
native String intern () | 字符串入池操作 |
String concat (String str) | 字符串连接,等同于 ”+“,不入池 |
int length () | 获取字符串长度 |
boolean isEmpty () | 判断是否为空字符串,注:不是 null ,而是长度为 0 |
首先来回顾下String类的特点:
🍎 通常来讲String的操作比较简单,但是由于String的不可更改特性,为了方便字符串的修改,提供StringBuffer和StringBuilder类。而StringBuffer和StringBuilder大部分功能是相同的,这里我们主要介绍 StringBuffer
🍊 StringBuffer类和String类一样,也用来代表字符串,只是由于StringBuffer的内部实现方式和String不同,StringBuffer在进行字符串处理时,不生成新的对象,所以在内存使用上,StringBuffer类要优于String类。
// 方法1:
StringBuffer stringBuffer = new StringBuffer(); //初始化出的StringBuffer对象是一个空的对象
// 方法2:
StringBuffer stringBuffer = new StringBuffer("Hello World!"); //创建带有内容的StringBuffer对象
StringBuffer stringBuffer = new StringBuffer("Hello");
stringBuffer.append("World!");
System.out.println(stringBuffer);
// 执行结果
HelloWorld!
reverse()方法将把当前字符序列反转后返回,请看示例:
StringBuffer stringBuffer = new StringBuffer("abc");
System.out.println(stringBuffer.reverse());
// 执行结果:
cba
// 错误
StringBuffer s = "abc"; //赋值类型不匹配
StringBuffer s = (StringBuffer)"abc"; //不存在继承关系,无法进行强转
// 正确
String string1 = "Hello World!";
StringBuffer stringBuffer = new StringBuffer(string1); //String转换为StringBuffer
String string2 = stringBuffer.toString(); //StringBuffer转换为String
以上我们就把String 类的相关知识讲完啦,我们了解了 String的基本性质以及常量池的概念,而且对于 String 字符串的操作方法,大家要多去熟悉,后面可以多多实践,在 StringBuffer和String 运用的时候,大家要记得两者的区别以及特定场景下的使用。
【*★,°*:.☆( ̄▽ ̄)/$:*.°★* 】那么本篇到此就结束啦,如果我的这篇博客可以给你提供有益的参考和启示,可以三连支持一下 !!