Java 认识String类

1. 创建字符串

常见的构造 String 的方式

//方式一:
String str = "hello world";
//方式二:
String str2 = new String("Hello world");
//方式三:
char[] array = {'a','b','c'};
String str3 = new String(array);

注意事项:

  • " hello " 这样的字符串字面值常量,类型也是 String.
  • String 也是引用类型, String str = " hello " ; 这样的代码内存布局如下
    在这里插入图片描述

我们曾经在学习数组的时候就提到了引用的概念。
引用类似于C语言中的指针,只是在栈上开辟了一小块内存空间保存一个地址,但是引用和指针又不太相同,指针能进行各种数字运算,但是引用不能,这是一种" 没那么灵活的指针 “。
另外,也可以把引用想象成一个标签,可以” 贴 "到对象上,一个对象可以贴一个标签,也可以贴多个。如果一个对象上面一个标签也没有,那么这个对象就会被JVM当成垃圾回收掉。

由于String 是引用类型,因此对于以下代码

String str1 = "hello";
String str2 = str1;

内存布局如图:

那么有同学就会说,是不是修改 str1 , str2 也会随之变化呢?

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

执行结果:
在这里插入图片描述
我们发现," 修改 " str1 之后,str2 并没有发生变化,输出的结果仍然是 hello .这是为什么呢?
事实上,str1 = " world " 这样的代码并不算" 修改 "字符串,而是让 str1 这个引用指向了一个新的String 对象。
在这里插入图片描述

2. 字符串比较相等

2.1 使用 == 比较

对于内置类型,== 比较的是变量中的值。
代码一:

int x = 10;
int y = 10;
System.out.println(x == y);

执行结果:
在这里插入图片描述
对于引用类型,== 比较的是引用中的地址。
代码二:

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

执行结果:
在这里插入图片描述
看起来貌似没什么问题,再换个代码试一下,就会发现意外的小惊喜!

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

执行结果:
在这里插入图片描述
我们来分析一下两种创建String 方式的差异。
代码一内存布局:
在这里插入图片描述
我们发现,str1 和 str2 是指向同一个对象的。此时如 " hello " 这样的字符串常量是在字符串常量池中的。

关于字符串常量池:
如" hello " 这样的字符串字面值常量,也是需要一定的内存空间来存储的,这样的常量有一个特点,就是不需要修改。所以如果代码中有多个地方引用都需要使用 " hello " 的话,就之间引用到常量池的这个我位置就行了,而没必要把 " hello " 在内存中存储多次。

代码二内存布局:
在这里插入图片描述
我们可以得出结论:
通过 String str1 = new String ( " hello" ) ; 这样的方式创建的String 对象相当于在堆上另外开辟了空间来存储 " hello " 的内容,也就是此时内存中存在两份 " hello " .
String 使用 == 比较的并不是字符串中的内容,而是比较两个引用是否指向了同一个对象。

2.2 使用equals比较

Java中要想比较字符串中的内容,必须采用 String 类提供的 equals 方法。

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

执行结果:
在这里插入图片描述
equals 使用注意事项
现在需要比较 str 和 " hello " 两个字符串是否相等,我们应该如何来写呢?

String str = new String("hello");
//方式一:
System.out.println(str.equals("hello"));
//方式二;
System.out.println("hello".equals(str));

那么在上面的代码中,哪种方式更好呢?
我们更推荐使用" 方式二 " ,一旦 str 是 null ,方式一 的代码会抛出异常,而方式二不会。
注意事项:
" hello " 这样的字面值常量,本质上也是一个 String 对象,完全可以使用 equals 等String 对象的方法。

2.3 使用compareTo 方法比较

与equals不同的是,equals返回的是boolean类型,而 compareTo 返回的是 int 类型。具体比较方式:
1.先按照字典次序进行比较,如果出现不等的字符,直接返回两个字符的大小差值。
2.如果前k个字符相等(k为两个字符串长度中的较小值),返回值为两个字符串的长度差值。

public static void main(String[] args) {
    String s1 = new String("abc");
    String s2 = new String("ac");
    String s3 = new String("abc");
    String s4 = new String("abcdef");
    System.out.println(s1.compareTo(s2));
    System.out.println(s1.compareTo(s3));
    System.out.println(s1.compareTo(s4));
}

解析:

  1. 遇到了不同的字符,输出字符差值-1.
  2. 两个字符串相同,输出0.
  3. 前k个字符完全相同,输出长度差值-3.

2.4使用 compareToIgnoreCase方法比较

注意:此方法与compareTo方法的不同之处在于 忽略大小写比较

public static void main(String[] args) {
    String s1 = new String("abc");
    String s2 = new String("ac");
    String s3 = new String("ABc");
    String s4 = new String("abcdef");
    System.out.println(s1.compareToIgnoreCase(s2));
    System.out.println(s1.compareToIgnoreCase(s3));
    System.out.println(s1.compareToIgnoreCase(s4));
}

所以,上述代码的运行结果,和之前的结果一样。

2.5 字符串查找

1.char charAt ( int index )
功能:返回index位置上的字符,如果index为负数或者越界,抛出IndexOutOfBoundsException异常。

public static void main(String[] args) {
    String str = "hello";
    char ch = str.charAt(2);
    System.out.println(ch);
}

上述代码为获取字符串 “hello” 下标为2的字符,第一个字符的下标是0,那么依次往后,下标为2的字符就是 ’ l '.
2. int indexOf ( int ch )
功能:返回ch 第一次出现的位置,若字符串中没有ch, 那么返回-1.

public static void main(String[] args) {
    String str = "hello";
    int index = str.indexOf("l");
    System.out.println(index);
}

上述代码是获取字符串 “hello” 中 " l ",的下标,这里有一个点需要注意,字符串中有两个
" l ",但是这个方法它获取的是第一次出现的位置下标,所以输出的结果是2.
3. int indexOf ( int ch, int fromIndex )
功能:从fromIndex 位置开始找ch 第一次出现的位置,没有就返回-1.

public static void main(String[] args) {
    String str = "hello";
    int index = str.indexOf("l",3);
    System.out.println(index);
}

上述代码是从第三个位置开始找字符 " l ",我们可以看到第三个位置正好就是字符 " l ",所以结果输出3。另外我们也注意到,这里是从第3个位置开始找,同时也是包括3这个位置的。
4. int indexOf ( String str )
功能:返回 str 第一次出现的位置,没有返回-1.

public static void main(String[] args) {
    String str = "abcdefcd";
    int index = str.indexOf("cd");
    System.out.println(index);
}

上述代码是求字符串 " cd " 在字符串 " abcdefcd " 中第一次出现的位置,输出的结果是2(输出的是子字符串中首个字符的下标)。
5. int indexOf ( String str, int fromIndex )
功能:从fromIndex位置开始找str第一次出现的位置,没有就返回-1。

public static void main(String[] args) {
    String str = "abcdefcd";
    int index = str.indexOf("cd",5);
    System.out.println(index);
}

上述代码是求字符串 " cd " 在字符串 " abcdefcd " 中从5下标开始第一次出现的位置,输出的结果是6(输出的是子字符串中首个字符的下标).
6. int lastIndexOf ( int ch )
功能:从后往前找,返回字符ch第一次出现的位置,没有则返回-1.

public static void main(String[] args) {
    String str = "abcdefcd";
    int index = str.lastIndexOf("c");
    System.out.println(index);
}

上述代码是求字符 " c " 在字符串 " abcdefcd " 中最后的位置开始第一次出现的位置,输出的结果是6(输出的是子字符串中首个字符的下标).
7. int lastIndexOf ( int ch, int fromIndex)
功能:从fromIndex位置开始找,返回字符ch 第一次出现的位置,没有就返回-1.

public static void main(String[] args) {
    String str = "abcdabed";
    int index = str.lastIndexOf("c",1);
    System.out.println(index);
}

上述代码是求字符串 " c " 在字符串 " abcdabed " 中下标为1的位置开始从后往前查找,由于这样一来刚好就错过了c出现的位置,所以输出的结果是-1.
8. int lastIndexOf ( String str )
功能:从后往前找,返回字符串str第一次出现的位置,没有则返回-1.

public static void main(String[] args) {
    String str = "abcdefcd";
    int index = str.lastIndexOf("cd");
    System.out.println(index);
}

上述代码是求字符串 " cd " 在字符串 " abcdefcd " 中最后的位置开始第一次出现的位置,输出的结果是6(输出的是子字符串中首个字符的下标).
9. int lastIndexOf ( String str, int fromIndex )
功能:从fromIndex位置开始找,从后往前找字符串 str 第一次出现的位置,没有则返回-1.

public static void main(String[] args) {
    String str = "abcdabed";
    int index = str.lastIndexOf("cd",1);
    System.out.println(index);
}

上述代码是求字符串 " cd " 在字符串 " abcdabed " 中下标为1的位置开始从后往前查找,由于这样一来刚好就错过了cd出现的位置,所以输出的结果是-1.

2.6 转化

1.数值和字符串转化
1)数字转字符串

class Student{
    public String name;
    public int age;
    public Student(String name,int age){
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public static void main(String[] args) {
    String s1 = String.valueOf(1234);
    String s2 = String.valueOf(12.34);
    String s3 = String.valueOf(true);
    String s4 = String.valueOf(new Student("翠花",2));
    System.out.println(s1);
    System.out.println(s2);
    System.out.println(s3);
    System.out.println(s4);
}

运行结果:
在这里插入图片描述

s1是 int 向 String 的转化;
s2是double 向 String 的转化;
s3是boolean 向 String 的转化;
s4是 引用类型向 String 的转化,由于在Student类中重写了toString 方法,所以这里可以正常输出。
2. 字符串转数字

public static void main(String[] args) {
    int num1 = Integer.parseInt("1234");
    double num2 = Double.parseDouble("12.34");
    System.out.println(num1);
    System.out.println(num2);
}

上述代码分别是将字符串转为整型和double型,需要注意的一点是,在接收返回值的时候类型一定要匹配。
3. 大小写转换

小写转大写:

public static void main(String[] args) {
    String str1 = "hello";
    System.out.println(str1.toUpperCase());
}

大写转小写:

public static void main(String[] args) {
    String str1 = "HELLO";
    System.out.println(str1.toLowerCase());
}

4. 字符串转数组

public static void main(String[] args) {
    String str = "hello";
    char[] array = str.toCharArray();
    System.out.println(array[2]);//输出l
}

数组转字符串:

public static void main(String[] args) {
    char[] array = {'a','b','c'};
    String str = new String(array);
    System.out.println(str);//输出abc
}

2.7 字符串转换

使用一个特定的新的字符串换掉已有的字符串数据,可用的方法如下:
1)替换所有的指定内容:String replaceAll ( String regex, String replacement );

public static void main(String[] args) {
    String str = "cdabstab";
    System.out.println(str.replaceAll("ab", "oo"));
}

执行结果:
在这里插入图片描述
上述代码是将字符串中所有的 " ab “,都替换为了” oo".
2) 替换首个内容: String replaceFirst ( String regex, String replacement );

public static void main(String[] args) {
    String str = "cdabstab";
    System.out.println(str.replaceFirst("ab", "oo"));
}

上述代码是将字符串中的第一个 " ab “,替换为了” oo".
注意这里是直接输出,所以能看到替换的效果,但如果最后输出的是str,那输出的还是原来的对象,因为字符串是不可变对象,替换不会修改当前的字符串,而是会产生一个新的字符串。

2.8 字符串拆分

可以将一个完整的字符串按照指定的分隔符划分为若干个子字符串。
可用方法如下:
1)按照字符串中的字符将字符串全部拆分:String[ ] split ( String regex )

public static void main(String[] args) {
    String str = "hello world hello bit";
    String[] array = str.split(" ");
    System.out.println(Arrays.toString(array));
}

执行结果:
在这里插入图片描述
上述代码是将字符串str以空格拆分成了一个字符串数组。
2) 字符串的部分拆分:
将字符串以指定的格式,拆分为 limit 组:String[ ] split ( String regex, int limit )

public static void main(String[] args) {
    String str = "hello world hello bit";
    String[] array = str.split(" ",2);
    System.out.println(Arrays.toString(array));
}

执行结果:
在这里插入图片描述
拆分是特别常用的操作,需要我们去重点掌握。另外有些特殊字符作为分割符可能无法正确切分,需要加上转义。
例如,拆分IP地址:

public static void main(String[] args) {
    String str = "192.168.1.1";
    String[] array = str.split("\\.");
    System.out.println(Arrays.toString(array));
}

执行结果:
在这里插入图片描述
注意事项:

  1. 字符 " | ", " * " " + " " . "都得加上转义字符,前面加上 " \ "
  2. 如果是 " \ ",那么就得写成 " \\ "
  3. 如果一个字符串中有多个分割符,可以用 " | "作为连字符
    示例代码:多次拆分
public static void main(String[] args) {
    String str = "name=zhangsan&age=18";
    String[] array = str.split("=|&");
    System.out.println(Arrays.toString(array));
}

执行结果:
在这里插入图片描述

2.9 字符串截取

从一个完整的字符串中截取出部分内容,可用方法如下:
1)String substring ( int beginIndex )
功能:从指定索引截取到结尾

public static void main(String[] args) {
    String str1 = "abcdef";
    String str2 = str1.substring(2);
    System.out.println(str2);
}

上述代码是从字符串 str1 下标为2的地方开始截取(也包括2),一直截取到字符串结尾,输出的结果是 " cdef ".
2) String substring ( int beginIndex , int endIndex )
功能:截取字符串的部分内容

public static void main(String[] args) {
    String str1 = "abcdef";
    String str2 = str1.substring(2,5);
    System.out.println(str2);
}

上述代码是从字符串 str1 2下标的位置开始截取(包括2),一直到下标为5的地方结束(不包括5),所以输出的结果是 " cde ".
需要注意的一点是这里的区间是左闭右开区间 [ ),假设x处于这个区间,那x的取值范围就是左边 ≤ x < 右边,也就是包含左边,但不包含右边。

其他操作

1)String trim ()
功能:去掉字符串中的左右空格,保留中间空格

public static void main(String[] args) {
    String str1 = " hello world ";
    String str2 = str1.trim();
    System.out.println(str1);
    System.out.println(str2);
}

我们可以分别输出 str1 和 str2 来观察,trim 会去掉字符串开头和结尾的空白字符(空格、换行、制表符等)。

3. 字符串的不可变性

String 是一种不可变对象,字符串中的内容是不可改变的,字符串不可修改,是因为:

  1. String类在设计时就是不可改变的,String类在描述中就已经说明了。
    在这里插入图片描述
    String类中的字符实际保存在内部维护的value字符数组中,该图还可以看出:
    1.String类被final 修饰,表明该类不能被继承
    2.value被final修饰,表明value自身的值不能改变,即不能引用其他字符数组,但是其引用空间中的内容可以修改。
  2. 所有涉及到可能修改字符串内容的操作都是创建一个新对象,改变的是新对象
    在这里插入图片描述
    上面提到的大小写转换也是一样:
    在这里插入图片描述
    final 修饰类表明该类不想被继承,final 修饰引用类型表明该引用变量不能引用其他对象,但是其引用对象中的内容是可以修改的。
public static void main(String[] args) {
    final int[] array = {1,2,3,4,5};
    array[0] = 100;
    array = new int[]{4,5,6};
}

执行结果:
在这里插入图片描述

为什么 String 要设计成不可变的?(不可变对象的好处是什么?)
1.方便实现字符串对象池。如果 String 可变,那么对象池就需要考虑写时拷贝的问题了。
2.不可变对象是线程安全的。
3.不可变对象更方便缓存 hash code,作为 key 时可以更高效的保存到 HashMap 中。

那如果想要修改字符串中的内容,该如何操作呢?
注意:尽量避免直接对 String 类对象进行修改,因为 String 类是不能修改的,所有的修改都会创建新对象,效率非常低下。
可以看待在对String类进行修改时,效率是非常慢的,因此:尽量避免对String的直接需要,如果要修改建议尽量使用StringBuffer 或者 StringBuilder.

4.StringBuffer 和 StringBuilder

4.1 StringBuilder 的介绍

由于String的不可修改特性,为了方便字符串的修改,Java 中又提供了StringBuffer 和 StringBuilder类。这两个类大部分功能是相同的,这里介绍StringBuilder常用的一些方法。

方法说明
StringBuffer append ( String str )在尾部追加,相当于String的+=,可以追加:boolean、char、char[]、double、float、int、long、Object、String、StringBuffer的变量
char charAt ( int index )获取index位置的字符
int length( )获取字符串的长度
int capacity( )获取底层保存字符串空间总的大小
void ensureCapacity ( int minimumCapacity )扩容
void setCharAt ( int index, char ch )将index位置的字符设置成ch
int indexOf (String str )返回str第一次出现的位置
int indexOf (String str, int fromIndex )从fromIndex位置开始查找str第一次出现的位置
int lastIndexOf ( String str )返回最后一次出现str的位置
int lastIndexOf ( String str,int fromIndex )从fromIndex位置开始查找str最后一次出现的位置
StringBuffer insert ( int offset, String str )在offset位置插入:八种基本类型&String类型&Object类型
StringBuffer deleteCharAt (int index )删除index位置的字符
StringBuffer delete ( int start, int end )删除[start, end]区间内的字符
StringBuffer replace ( int start, int end, String str )将[start, end]位置的字符替换为str
String substring ( int start )从start开始一直到末尾的字符以String的方式返回
String substring ( int start, int end )将[start, end]范围内的字符以String的方式返回
StringBuffer reverse ( )反转字符串
String toString ( )将所有字符按照String的方式返回
public static void main(String[] args) {
    String str = "hello";
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append(str);
    stringBuilder.append(" world");
    str = stringBuilder.toString();
    System.out.println(str);
}

运行结果:
在这里插入图片描述

面试题

  • String、StringBuffer、StringBuilder的区别
  • String的内容不可修改,StringBuilder和StringBuffer的内容可以修改
  • StringBuilder和StringBuffer的大部分功能是相似的
  • StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操作
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值