Java String类型

String

String类型是一个引用类型,本质上是一个class,Java对它提供了特殊的支持使得我们可以使用“…”的形式表示,如:

String s = "abc";

String的内部通过一个char[]数组存储字符串的各个字符:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];
    ........
}

创建字符串

可以通过上面提过的"…"形式创建字符串:

String s = "abc";

也可以通过new创建字符串:

String s3 = new String(new char[]{'H','e','l','l','o'});
String s4 = new String("Hello");

构造方法可以接受一个已有的字符串,也可以是一个char[]数组,new创建与“…”的区别在于是否会在堆栈区创建String对象

  • 通过"…"直接指定或通过+拼接字符串时,jvm会在常量池中查找,如果有则返回,没有就在常量池中创建
  • 通过new创建时则一定会在堆栈中创建一个新的String对象
intern方法

intern方法可以扩充常量池,当一个String实例调用该方法时,如果常量池中存在与该实例值相同的字符串常量,则返回该常量的引用,否则创建一个与该实例值相同的字符串常量放入常量池并返回引用

String s1 = "Hello";
String s3 = new String(new char[]{'H','e','l','l','o'});
System.out.println(s1 == s3);            // false
System.out.println(s3.intern() == s1);   // true
System.out.println(s3.intern() == s3);   // false

需要注意的是当常量池中不存在时会在常量池中重新创建字符串常量而不是直接将实例放入常量池,因此,s3.intern()s3是不同的引用

String的不可变性

在上面的源码中,我们可以看到在String中保存的char[]数组是final的,同时在String内部也没有提供任何修改value的方法,这意味着一个String不可变的,我们在改变一个字符串的时候只能改变它的引用而不能它的值:

String s1 = "HELLO";
String s2 = s1;
System.out.println("s1="+s1+",s2="+s2); // s1=HELLO,s2=HELLO

s1 = "hello";
System.out.println("s1="+s1+",s2="+s2); // s1=hello,s2=HELLO

s1s2指向相同的对象,可以看到s1改变后,s2并没有改变,通过idea等工具我们也可以看到在执行s1 = "hello";s1的引用地址发生了变化

字符串长度

通过length方法可以获得字符串的长度

String s = "hello";
int len = s.length() // 5

字符串判空

通过isEmpty()方法可以判断是否为空字符串:

System.out.println("".isEmpty());  // true

去除首尾空白字符

trim()方法可以去除包括空格、\n\t\r等空白字符

String s = "   hello world \n\t\r".trim()+"!"; // hello world!

但是它无法去除unicode空格,例如\u3000

String s = "   hello world \n\t\r\u3000".trim()+"!"; // hello world 
 														!	

字符串比较

在比较两个字符串是否相同时,要使用equals方法而不能使用==

通过==比较

class类型的数据在通过==进行比较时实际上是在比较它们的引用是否相同

String s1 = "hello";
String s2 = "hello";
String s3 = "Hello";
String s4 = new String("hello");

System.out.println("s1==s2:"+(s1==s2)); // s1==s2:true
System.out.println("s1==s3:"+(s1==s3)); // s1==s3:false
System.out.println("s1==s4:"+(s1==s4)); // s1==s4:false

s1s2s3的比较都符合预想,但是s1s4比较就会存在问题,这是因为s1s2指向常量池中的同一个字符串常量,自然引用也是相同的,而s4通过new关键字,会在堆栈中创建新的对象并获得它的引用,就和s1不同了

通过equals比较

equals就是比较两个字符串的是否相同

String s1 = "hello";
String s2 = "hello";
String s3 = "Hello";
String s4 = new String("hello");

System.out.println("s1 equals s2:"+s1.equals(s2)); // s1 equals s2:true
System.out.println("s1 equals s3:"+s1.equals(s3)); // s1 equals s3:false
System.out.println("s1 equals s4:"+s1.equals(s4)); // s1 equals s4:true

另外String还提供了equalsIgnoreCase方法可以忽略大小写比较:

s1.equalsIgnoreCase(s3); //true
通过compareTo比较

compareTo方法也是比较两个字符串的值

s1.compareTo(s3);

equals方法不同的是它返回一个int类型,s1大于s2时返回大于零的值,s1等于s2时返回零,s1小于s2时返回小于零的数,这个方法主要用于排序

compareTo也有对应的忽略大小写版本compareToIgnoreCase

s1.compareToIgnoreCase(s3)

字符串搜索

1、 contains

contains方法搜索字符串中是否存在子串:

s.contains("el");

注意: contains方法只接受CharSequence类型,这是一个接口,Stirng实现了这个接口,不能传入单个字符

2、indexOf

indexOf方法从前向后查找子串,找到后返回第一个匹配子串的下标,参数允许字符和字符串,同时可以增加一个fromIndex参数表示从第几位开始查找

String s = "hello world!hello world!";

System.out.println(s.indexOf('l'));     // 搜索字符,返回正序第一个的下标,输出2
System.out.println(s.indexOf('l',5));   // 搜索字符,从第几位开始向后查找,输出10
System.out.println(s.indexOf("rl"));    // 搜索子串,返回正序第一个的下标,输出9
System.out.println(s.indexOf("rl",10)); // 搜索子串,从第几位开始向后查找,输出21
3、lastIndexOf

lastIndexOfindexOf基本相同,区别在于它从后向前查找子串

String s = "hello world!hello world!";

System.out.println(s.lastIndexOf('l'));     // 搜索字符,返回正序第一个的下标,输出22
System.out.println(s.lastIndexOf('l',20));  // 搜索字符,从第几位开始向后查找,输出16
System.out.println(s.lastIndexOf("rl"));    // 搜索子串,返回正序第一个的下标,输出21
System.out.println(s.lastIndexOf("rl",19)); // 搜索子串,从第几位开始向后查找,输出9

需要注意的是,indexOf方法中的fromIndex表示从第几位开始向后查,而在lastIndexOf方法中表示从第几位开始向前

4、startWith

startWith查找字符串是否以子串开头,可以增加toffset参数表示第几位开始以子串开头

String s = "hello world!hello world!";

System.out.println(s.startsWith("hel")); //true
System.out.println(s.startsWith("o",4)); //true
5、endWith

endWith查找字符串是否以子串结尾,只接受一个字符串参数

System.out.println(s.endsWith("!")); //true

字符串提取

1、charAt

charAt传入下标,返回下标对应的字符

String s = "hello world!";
System.out.println(s.charAt(1));  // e
2、substring

substring传入起始下标和结束下标,返回对应范围的子串,如果只传入一个参数,就返回该下标开始到字符串结束为止组成的子串

System.out.println(s.substring(1));
System.out.println(s.substring(1,3));
3、subSequence

API Note:
This method is defined so that the String class can implement the CharSequence interface

subSequence方法和substring类似,但是它必须传入两个参数,不能省略结束下标,另外,substring方法返回String类型而subSequence返回一个CharSequence类型。根据JDK文档,这个方法只是为了实现CharSequence接口,其行为与substring相同,源码中也可以看出:

public CharSequence subSequence(int beginIndex, int endIndex) {
    return this.substring(beginIndex, endIndex);
}

由于它返回的对象实际是String类型,因此可以强制转化为String

String s1 = (String) s.subSequence(1,3); // el

替换子串

字符串替换子串主要有两种方式:

  1. 根据字符或字符串替换
  2. 根据正则表达式替换
1、replace

replace将字符串中对应的字符或字符串全部转换为新的字符或字符串

String s = "Hello,my name is coco,I'm 18 years old.";

s.replace('o','k');  // "Hellk,my name is ckck,I'm 18 years kld.",将所有的'o'变为'k'
s.replace("co","k"); // "Hello,my name is kk,I'm 18 years old.", 将所有的'co'变为'k'
2、replaceAll

replaceAll根据提供的正则表达式将符合的字符串全部转换为新的字符串

String s = "Hello,my name is coco,I'm 18 years old.";

s.replaceAll("\\d","4"); // "Hello,my name is coco,I'm 44 years old.",将所有数字转换为4
3、replaceFirst

replaceFirst同样使用正则表达式,但是它只转换第一个匹配的字符串

String s = "Hello,my name is coco,I'm 18 years old.";

s.replaceFirst("\\d","k"); // "Hello,my name is coco,I'm k8 years old."
4、大小写转换

String还提供了toUpperCasetoLowerCase方法,通过它可以便利地转换大小写:

String s = "Hello,my name is coco,I'm 18 years old.";
s.toUpperCase(); // HELLO,MY NAME IS COCO,I'M 18 YEARS OLD.
s.toLowerCase(); // hello,my name is coco,i'm 18 years old.

**注意:**替换方法返回一个替换后的新的字符串,不会修改原来的字符串

字符串分割

split()

通过split()方法可以将字符串分割为字符串数组,需要一个正则表达式作为参数

String s = "1,2,3,4,5.5,6.6";

String[] ss = s.split(",");      // [1, 2, 3, 4, 5.5, 6.6]
String[] ss1 = s.split("[,.]");  // [1, 2, 3, 4, 5, 5, 6, 6]

还可以增加一个limit参数表示最多分为几份,分割不完的部分会放在最后一个元素里

String[] ss2 = s.split("[,.]",3);  // [1, 2, 3,4,5.5,6.6]

如果limit参数小于0,则与不加相同

String[] ss3 = s.split("[,.]",-1); // [1, 2, 3, 4, 5, 5, 6, 6]
toCharArray()

toCharArray()方法可以将一个字符串分割为一个字符数组

char[] chars = s.toCharArray();

字符串拼接

String类提供的静态方法join()可以实现拼接多个字符串,它用指定的字符串连接字符串数组

String[] ss = new String[]{"abc","def","gh"};
String.join(",",ss);  // abc,def,gh

它还支持变长参数:

String.join(",","hello","world"); // hello,world

字符串格式化

String提供了静态方法format实现字符串格式化,通过传入参数代替占位符生成新的字符串,常用的占位符包括:

  • %d:整数
  • %s:字符串
  • %f:浮点数
  • %x:十六进制整数
String.format("I'm %d %s",18,"years old");
String.format("%x",1234);
String.format("%f",3.3);

还可以指定输出格式,例如:

String.format("%6.2f",2.34345));  //   2.34,字符串长度至少为6位,缺少的用空格代替,并取两位小数
String.format("%06d",2345));	  // 002345,字符串长度至少为6位,缺少的用0代替

类型转换

1、其他类型转字符串

通过StringvalueOf静态方法可以将其他类型转换为String类型

基本类型
String.valueOf(true);  // true
String.valueOf(12);    // 12
String.valueOf(3.4);   // 3.4
字符数组

valueOf可以将char[]数组的部分组合为字符串,需要提供起始位置和长度,不提供则组合整个数组

String.valueOf(new char[]{'1','2','3','4','5','6'})// 123456
String.valueOf(new char[]{'1','2','3','4','5','6'},1,3); // 234
其他数组

除了char[]以外的其他数组通过valueOf将会获得其类型以及16进制的哈希值

String.valueOf(new int[]{1,23,4}); // [I@1b6d3586,表示int数组,哈希为1b6d3586
引用类型

以一个Cat类为例

class Cat{
    private String name;

    public Cat(String name) {
        this.name = name;
    }
}

如果该类型没有toString()方法,则返回其类型以及16进制的哈希值

String.valueOf(new Cat("cc"); // string.Cat@4554617c

如果有就调用toString()方法

@Override
public String toString() {
    return "Cat{" +
        "name='" + name + '\'' +
        '}';
}
String.valueOf(new Cat("cc")); // Cat{name='cc'}
valueOf的实现

除了booleanchar[]以外,valueOf的实现都是在调用toString()方法

引用类型如果有toString()方法则调用toString(),没有则会查找父类的toStirng()方法,如果有就调用,没有就再向上查找,直至Object类,ObjecttoString()方法为:

public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

这也就是上文中Cat类没写toString()方法时输出的内容

需要注意的是数组也是引用类型,而且没有重写toString(),因此也是调用的Object类的toString()方法

基本类型会调用对应的包装类的toString()方法,例如int对应的Integer:

public static String toString(int i) {
    if (i == Integer.MIN_VALUE)
        return "-2147483648";
    int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
    char[] buf = new char[size];
    getChars(i, size, buf);
    return new String(buf, true);
}

boolean由于只有两种值,因此直接在valueOf中进行了输出:

public static String valueOf(boolean b) {
    return b ? "true" : "false";
}

char[]数组则是调用的String类型的构造方法

public static String valueOf(char data[], int offset, int count) {
    return new String(data, offset, count);
}
2、字符串转其他类型

字符串转其他类型可以通过其他类型提供的对应方法,例如Integer类型:

int a = Integer.parseInt("12");

double类型:

double b = Double.parseDouble("12.3");

例外的是char[]类型可以通过String提供的toCharArray()获得,在 字符串分割 中提到过

参考

字符串和编码 - 廖雪峰的官方网站 (liaoxuefeng.com)

Java学习之字符串的创建 - 天翱~翔 - 博客园 (cnblogs.com)

java中subSequence方法和subString方法的区别_每天进步1%-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值