Java学习苦旅(十四)——String

本篇博客将详细讲解java中的String类。

创建字符串

常见的构造String的方式

//方式一
String str = "hello";

//方式二
String str1 = new String("Hello");

//方式三
char[] array = {'a', 'b', 'c'};
String str2 = new String(array);

注意:

  • "hello"这样的字符串字面值常量,类型也是String。
  • String也是引用类型。

我们来看下面这段代码:

public class TestDemo {
    public static void func(String s, char[] array) {
        s = "abc";
        array[0] = 'x';
    }

    public static void main(String[] args) {
        String str = "aaa";
        char[] chars = {'b','b','c','c'};
        func(str, chars);
        System.out.println(str);
        System.out.println(Arrays.toString(chars));
    }
}

这段代码的结果为:

image-20220130225440437

为什么会出现这样的结果呢?我们来看看这段代码的内存图。

image-20220130230146711

所以才会上述的结果。

常量池

常量池一般有三类:Class文件常量池、运行时常量池和字符串常量池。

Class文件常量池:用于存放编译器生成的各种字面量和符号引用。

运行时常量池:当程序把编译好的字节码文件,加载到JVM当中后会生成一个运行时常量池。运行时常量池是存放在方法区当中的。

字符串常量池:主要存放字符串常量,本质上是一个哈希表。

字符串比较

在Java中,比较两个字符串是否相等,一般使用String类提供的equals方法。例如:

String str1 = new String("hello");
String str2 = new String("hello");
System.out.println(str1.equals(str2));

执行结果为:

image-20220205205152013

注意:

如果出现下面这样的代码,那么就会报错:

String str1 = null;
String str2 = new String("hello");
System.out.println(str1.equals(str2));

执行结果为:

image-20220205205347565

理解字符串不可变

我们先来看这样一段代码:

String str = "hello";
str = str + " world";
str += "!!!";
System.out.println(str);

执行结果为:

image-20220205214712848

这段代码,表明上好像是修改了字符串,但实际上并不是,而是创建了五个变量:

image-20220205215040959

如果实在是想修改字符串,可以这样做:

String str = "hello";
str = "a" + str.substring(1);
System.out.println(str);

执行结果为:

image-20220205215211916

当然,也可以使用反射的方法去修改:

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] = 'a';
System.out.println(str);

执行结果为:

image-20220205215450136

为什么 String 要不可变?

  1. 方便实现字符串对象池。如果 String 可变,那么对象池就需要考虑何时深拷贝字符串的问题了。

  2. 不可变对象是线程安全的。

  3. 不可变对象更方便缓存 hash code,作为 key 时可以更高效的保存到 HashMap 中。

字符、字节与字符串

字符与字符串

字符串内部包含一个字符数组,String可以和char[]相互转换。

No方法名称类型描述
1public String(char value[])构造将字符数组中所有内容变为字符串
2public String (char value[], int offset, int count)构造将部分字符数组中的内容变为字符串
3publilc char charAt(int index)普通取得指定索引位置的字符,索引从0开始
4public char[] toCharArray()普通将字符串变为字符数组返回

代码示例:

将字符合并为字符串

char[] value = {'a','b','c','d','e'};
String str = new String(value);
System.out.println(str);

执行结果为:

image-20220205221221640

获取字符串中的某个字符

String str = "abcdef";
char val = str.charAt(3);
System.out.println(val);

执行结果为:

image-20220205221417415

将字符数组中部分字符合并成字符串

char[] value = {'a','b','c','d','e'};
String str = new String(value,1,3);
System.out.println(str);

执行结果为:

image-20220205222030496

将字符串转换为字符

String str = "abcdef";
char[] chars = str.toCharArray();
System.out.println(Arrays.toString(chars));

执行结果为:

image-20220205222420628

判断字符串是否由数字构成

public static void main(String[] args) {
    String str = "123a456";
    System.out.println(isNumber(str));
}

public static boolean isNumber(String s) {
    for (int i = 0; i < s.length(); i++) {
        char c = s.charAt(i);
        if (c < '0' || c > '9') {
            return false;
        }
    }
    return true;
}

执行结果为:

image-20220205222805956

字节与字符串

字节常用于数据传输以及编码转换的处理之中,String也能方便的和byte[]相互转换。

No方法名称类型描述
1public String(byte bytes[])构造将字节数组变为字符数组
2public String(byte bytes[], int offset, int length)构造将部分字节数组中的内容变为字符串
3public byte[] getBytes()普通将字符串以字节数组的形式返回
4public byte[] getBytes(String charsetName)throws UnsupportedEncodingException普通编码转换处理

代码示例:

实现字符串与字节数组的转换

byte[] bytes = {97,98,99,100,101,102};
String str = new String(bytes);
System.out.println(str);
System.out.println("========================");
String str2 = "abcdefg";
byte[] bytes1 = str2.getBytes();
System.out.println(Arrays.toString(bytes1));

执行结果为:

image-20220205224156173

编码转换处理

String str2 = "你好";
byte[] bytes1 = str2.getBytes("utf-8");
System.out.println(Arrays.toString(bytes1));

执行结果为:

image-20220205224628986

小结

byte[] 是把 String 按照一个字节一个字节的方式处理,这种适合在网络传输,数据存储这样的场景下使用,更适合针对二进制数据来操作。

char[] 是把 String 按照一个字符一个字符的方式处理,更适合针对文本数据来操作,尤其是包含中文的时候。

字符串常见操作

字符串比较

No方法名称类型描述
1public boolean equals(Object anObject)普通区分大小写的比较
2public boolean equalsIgnoreCase(String anotherString)普通不区分大小写的比较
3public int compareTo(String anotherString)普通比较两个字符串大小关系

代码示例:

String str1 = "hello";
String str2 = "HELLO";
System.out.println(str1.equals(str2));
System.out.println(str1.equalsIgnoreCase(str2));
System.out.println(str1.compareTo(str2));

运行结果为:

image-20220206155951210

在String类中compareTo()方法是一个非常重要的方法,该方法返回一个整型,该数据会根据大小关系返回三类内容:

  1. 相等:返回0
  2. 小于:返回内容小于0
  3. 大于:返回内容大于0

字符串查找

No方法名称类型描述
1public boolean contains(CharSequence s)普通判断一个子字符串是否存在
2public int indexOf(String str)普通从头开始查找指定字符串的位置,查到了返回位置的开始索引,如果查不到返回-1
3public int indexOf(String str, int fromIndex)普通从指定位置开始查找子字符串位置
4public int lastIndexOf(String str)普通由后向前查找子字符串位置
5public int lastIndexOf(String str, int fromIndex)普通从指定位置由后向前查找
6public boolean startsWith(String prefix)普通判断是否以指定字符串开头
7public boolean startsWith(String prefix, int toffset)普通从指定位置开始判断是否以指定字符串开头
8public boolean endsWith(String suffix)普通判断是否以指定字符串结尾

代码示例:

String str1 = "abcdefabcdef";
String str2 = "bcd";
System.out.println(str1.contains(str2));
System.out.println(str1.indexOf(str2,3));
System.out.println(str1.lastIndexOf(str2));
System.out.println(str1.startsWith("abba"));
System.out.println(str1.endsWith("ef"));

执行结果为:

image-20220206164412130

字符串替换

No方法名称类型描述
1public String replaceAll(String regex, String replacement)普通替换所有的指定内容
2public String replaceFirst(String regex, String replacement)普通替换首个内容

代码示例:

String str1 = "abcabcabcddee";
System.out.println(str1.replace('a','x'));
System.out.println(str1.replace("ab","xy"));
System.out.println(str1.replaceAll("abc","y"));
System.out.println(str1.replaceFirst("abc","pppp"));

执行结果为:

image-20220206170103565

字符串拆分

No方法名称类型描述
1public String[] split(String regex)普通将字符串全部拆分
2public String[] split(String regex, int limit)普通将字符串部分拆分,该数组长度就是limit极限

代码示例:

String str = "name=zhangsan&age=19";
String[] strings = str.split("&");
for (String s:strings) {
    System.out.println(s);
}
System.out.println("=================================");
for (String s:strings) {
    String[] ss = s.split("=");
    for (String tmp:ss) {
        System.out.println(tmp);
    }
}

执行结果为:

image-20220207221641440

注意事项:

  1. 字符"|","*","+"都得加上转义字符,前面加上“\\”。
  2. 如果是"\",那么就得写成"\\"。
  3. 如果一个字符串中有多个分隔符,可以用"|"作为连字符。

例如:

String str = "192.68.1.1";
String[] strings = str.split("\\.");
for (String s:strings) {
    System.out.println(s);
}
System.out.println("==========================");
String str1 = "192.68.1.1";
String[] strings1 = str1.split("\\.",3);
for (String s:strings1) {
    System.out.println(s);
}
System.out.println("++++++++++++++++++++++++++");
String string = "java30 12&21#hello";
String[] strings2 = string.split(" |&|#");
for (String s:strings2) {
    System.out.println(s);
}

执行结果为:

image-20220207223753118

字符串截取

No方法名称类型描述
1public String substring(int beginIndex)普通从指定索引截取到结尾
2public String substring(int beginIndex, int endIndex)普通截取部分内容

注意事项:

  1. 索引从0开始。
  2. 注意前闭后开区间的写法,substring(0,5)表示包含0号下标的字符,不包含5号下标的字符。

代码示例:

String str = "abcdefg";
System.out.println(str.substring(5));
System.out.println(str.substring(2,6));

执行结果为:

image-20220207225757454

其他操作方法

No方法名称类型描述
1public String trim()普通去掉字符串中的左右空格,保留中间空格
2public String toUpperCase()普通字符串转大写
3public String toLowerCase()普通字符串转小写
4public native String intern()普通字符串入池操作
5public String concat(String str)普通字符串连接,等同于“+”,拼接之后的对象不入池
6public int length()普通取得字符串长度
7public boolean isEmpty()普通判断是否为空字符串,但不是null,而是长度为0

StringBuffer和StringBuilder

首先来回顾下String类的特点:

任何的字符串常量都是String对象,而且String的常量一旦声明不可改变,如果改变对象内容,改变的是其引用的指向而已。通常来讲String的操作比较简单,但是由于String的不可更改特性,为了方便字符串的修改,提供StringBuffer和StringBuilder类。StringBuffer 和 StringBuilder 大部分功能是相同的,在String中使用"+"来进行字符串连接,但是这个操作在StringBuilder类中需要更改为append()方法。

我们先来看一下StringBuilder,

sb.append("abcdef");
sb.append("123");
System.out.println(sb);
System.out.println("========================");
System.out.println(sb.reverse());

运行结果为:

image-20220214203007780

注意:String和StringBuffer类不能直接转换。如果要想互相转换,可以采用如下原则:

  • String变为StringBuffer:利用StringBuffer的构造方法或append()方法。

  • StringBuffer变为String:调用toString()方法。

例如:

public static StringBuilder func() {
    String str = "abcd";
    return new StringBuilder(str);
}

public static String fun() {
    StringBuilder sb = new StringBuilder();
    return sb.toString();
}

String、StringBuffer、StringBuilder的区别:

  • String的内容不可修改,StringBuffer与StringBuilder的内容可以修改。

  • StringBuffer与StringBuilder大部分功能是相似的。

  • StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操作。

结尾

本篇博客到此结束。
上一篇博客:Java学习苦旅(十三)——多态
下一篇博客:Java学习苦旅(十五)——异常

评论 30
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值