> 文档中心 > 【Java学习—(15)你真的认识String类么?】

【Java学习—(15)你真的认识String类么?】

(点击跳转即可哦)

java学习专栏

LeetCode刷题专栏


认识String类


目录

    • JDK中String类的声明
    • 1 创建字符串的四种方式
    • 2 字符串比较相等
    • 3 关于字符串常量池问题
      • 手工入池:String类提供的intern方法
    • 4 字符串的不可变性
    • 5 如何修改字符串内容
      • 使用 StringBuilder类
      • StringBuilder -> String
      • String -> StringBuilder
      • StringBuilder类的其他方法
      • 请解释String、StringBuilder、StringBuffer的区别?
    • 6 String类 字符串的常见方法使用
      • 1 字符串比较
      • 2 字符和字符串的相互转换

JDK中String类的声明

public final class String

为何String类 被final修饰?

被final修饰的类无法被继承,所以String类不存在子类。这样的话就可以保证所有使用JDK的人用到的String类是同一个String类。

假设现在String允许继承,每个人都可以继承String类,修改它的方法等,多个完全不同的子类都可以向上转型为String,那别人拿来使用时,根本不知道String对象的行为是什么。

继承和方法覆写 在带来灵活性的同时,也会带来很多子类行为不一致导致的问题。


1 创建字符串的四种方式

  1. 直接赋值
String str1 = "hello word";
  1. 通过构造方法产生对象
String str2 = new String("hello word");
  1. 通过字符数组产生对象
char[] data = new char[]{'a', 'b','c'};String str3 = new String(data);
  1. 通过String的静态方法valueOf(任意数据类型) -> 转为字符串
String str4 = String.valueOf(13);

字面量: 直接写出来的数值就称之为字面量

10 -> int字面量

1.5 -> double字面量

true -> boolean字面量

“abc” -> String字面量 ,就是一个字符串的对象

String str = "hello word;

右边的就是字符串字面量,也是字符串的对象。


String str = "hello word";String str1 = str;str1 = "Hello";System.out.println(str);

输出结果:

hello word

为何输出结果是 “hello word”呢?

因为 “Hello” 也是字符串的字面量,是一个新的字符串对象,str1 实际上指向了新的字符串对象"Hello", str 指向的仍然是原字符串对象"hello word"。

String是不可变的。


2 字符串比较相等

所有引用数据类型在比较是否相等时,使用equals方法比较,JDK常用类,都已经覆写了equals方法,直接使用即可。

引用数据类型使用“==”,比较的仍然是数值(地址是否相等)

equals 在比较时,是会进行区分大小写的比较。

equalsIgnoreCase 在比较时,是不区分大小写的比较。


当在实际中,牵扯到用户输入就一定要做判空处理。

//这个变量由用户从浏览器输入String user = null;System.out.println(user.equals("张三")); //有可能会报空指针异常System.out.println("张三".equals(user));

因为我们要比较的特定内容本身就是字符串的字面量,一定不是空对象,把要比较的内容放在equals的前面,就可以方便处理user为空的问题。

3 关于字符串常量池问题

当使用直接赋值法产生字符串对象时,JVM会维护一个字符串的常量池,若该对象在常量池中还不存在,则产生一个新的字符串对象加入到字符串常量池中。

当继续使用直接赋值法产生字符串对象时,JVM发现该引用指向的内容已经在常量池中存在了,则此时不再新建字符串对象,而是复用已有的对象。

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

“hello” 是字符串常量,

第一次出现时,此时字符串常量池中不存在,新建一个该对象加入常量池。

str2 和 str3 仍然使用直接赋值法产生对象,但是该内容已经在常量池中存在,此时并不会产生新的对象,而是复用已有对象。


String str1 = new String("hello");String str2 = new String("hello");String str3 = new String("hello");

第一行代码——先产生“hello”字符串常量,此时字符串常量池中没有,把"hello"放入字符串常量池,然后new 了一个新的对象,在堆中开辟了新的空间,存储了"hello"。

第二行代码——还是先产生了"hello"字符串常量,但是此时字符串常量池中已经有了该字符串,直接复用常量池中的"hello",继续new,在堆中开辟新的空间,存储"hello"。

第三行代码——和第二行代码一样。

所以,这三行代码产生了四个字符串对象,其中 一个在常量池中,其余三个在堆中。

手工入池:String类提供的intern方法

intern方法

public native String intern();

调用intern方法会将当前字符串引用 指向的对象 保存到字符串常量池中。

  1. 若当前常量池中已经存在了该对象,则不在产生新的对象,返回常量池中的String对象。
  2. 若当前常量池中不存在该对象,则将该对象入池,返回入池后的地址。

看这个代码会输出什么?

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

输出结果为:

false

因为在第一行代码中,先在字符串常量中加入了一个"hello"的字符串常量,然后又new 了一个对象,在堆中存储了一个 “hello”,

第二行代码:常量池中已经存在了"hello",不会再产生新的对象,直接返回常量池中"hello" 的地址。但是此时没有对象接收返回的地址,所以str1 指向的还是在new 的那个地址。

str1 = str1.intern();

此时,str1 就接收到了常量池中的"hello"的地址。


再看这个代码:

char[] data = new char[]{'a','b',c'};String str1 = new String(data);str1.intern();String str2 = "abc";System.out.println(str1 == str2);

输出结果:

true

此时是因为第二行代码 传入的参数是一个字符串数组,此时没有产生字符串对象。所以此时intern的操作是 当前常量池中不存在该对象,则将该对象入池,返回入池后的地址。此时的入池操作是直接将str1 指向的字符串对象移动到常量池中,str1此时就指向的字符串就已经在常量池中了。


4 字符串的不可变性

所谓的字符串不可变指的是 字符串对象的内容不能变,而不是字符串引用不能变。

为何字符串的对象无法修改内容,而其他类的对象能修改内容?

字符串其实就是一个字符数组 -> char[], 字符串保存的值实际上在数组中保存。而String类中定义的是

private final char value[];

被private final 修饰,所以字符串对象的内容就无法修改, String类的外部根本拿不到value这个数组。


5 如何修改字符串内容

1 在运行时通过反射破坏 value数组的封装。(不推荐,反射是所有框架的基础)

2 更换使用 StringBuilder 或者StringBuffer类,已经和String不是一个类型了

StringBuffer 使用方法和StringBuilder 完全一样,线程安全,性能较差。


使用 StringBuilder类

若需要频繁进行字符串的拼接,使用StringBilder类的append方法。

StringBuilder 类可以修改对象的内容。

    public static void main(String[] args) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("张三"); stringBuilder.append("李四"); System.out.println(stringBuilder);    }

关于StringBuilder类的具体使用:

StringBuilder 类和 String类是两个独立的类,StringBuilder就是为了解决字符串拼接问题产生的。

因为String的对象无法修改内容,为了方便字符串的拼接操作,产生了StringBuilder类,StringBuilder类的对象是可以修改内容的。


StringBuilder -> String

调用StringBuilder类的toString方法

    public static void main(String[] args) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("张三"); stringBuilder.append("李四"); //变为String类 String str = stringBuilder.toString();    }

String -> StringBuilder

使用StringBuilder 的构造方法

StringBuilder stringBuilder = new StringBuilder("hello");

使用StringBuilder的append方法

stringBuilder.append("word");

StringBuilder类的其他方法

因为StringBuilder类可以修改内容,因此具备一些String类不具备的修改内容的功能,除了拼接append方法外。

  1. 字符串反转操作,StringBuilder 类提供的 reverse();
    public static void main(String[] args) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("张三"); stringBuilder.append("李四"); stringBuilder.reverse(); System.out.println(stringBuilder);    }
  1. 删除指定范围的数据,delete(int start, int end); 删除从start索引开始end之前所有内容。[start,end)
 StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("abcdefg"); stringBuilder.delete(4,7); System.out.println(stringBuilder);

输出结果:

abcd
  1. 插入操作,insert(int start, 各种数据类型); 将新元素插入当前StringBuilder类的对象,插入后新数值的起始索引为 start
 StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("abcdefg"); stringBuilder.insert(4,"插入"); System.out.println(stringBuilder);

输出:

abcd插入efg

请解释String、StringBuilder、StringBuffer的区别?

  1. String的对象是无法修改的,其他两个类的对象都是可以修改的

  2. StringBuffer是线程安全的操作,性能较差;StringBuilder是线程不安全,性能较高。


6 String类 字符串的常见方法使用

1 字符串比较

//区分大小写的比较public boolean equals(Object anObject)
//不区分大小写的比较public boolean equalsIgnoreCase(String anotherString)
//比较两个字符串大小关系,逐个字符进行比较,返回当前对象字符与比较对象字符的差public int compareTo(String anotherString)

compareTo 方法

 String a = "abc"; String b = "ABC"; System.out.println(a.compareTo(b));

输出:

32

A的ASCII码值:A -> 65, a的ASCII码值:a -> 97

97-65 = 32

System.out.println(b.compareTo(a));

此时输出就是 -32 ,

65 - 97 = -32

String类 也实现了Conparable接口,覆写了compareTo方法。

字符串的compareTo方法是 按照字符串内部的每个数组进行ASCII的比较。

按照"字典序"排列字符串,就是按照字符串内部的字符的ASCII码大小进行排序。


2 字符和字符串的相互转换

字符串的内部实际上就是使用字符数组来存储的

char -> String, 通过String 的构造方法

char[] ch = new char[]{'a','b','c'};String str = new String(ch);

将字符数组的部分内容转为字符串对象

public String(char value[], int offset,int count)

offset : 字符数组开始的索引

count : 转换的字符个数

看具体使用代码:

char[] ch = new char[]{'a','b','c','d'};String str = new String (ch,1,2);Systm.out.println(str);

输出:

bc

String -> char

1 取出字符串中指定索引的字符

//索引从0开始,返回索引位置的字符public char charAt(int index)

看代码:

String str = "word";System.out.println(str.charAt(1));

输出:

o

2 将字符串中的内容转为字符数组 String => char[]

//将字符串变为字符数组 返回一个字符数组public char[] toCharArray()

产生了一个新的字符数组,将字符串的内容复制过去了

看代码:

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

输出:

[h, e, l, l, o]

如何判断一个字符串的对象是否为纯数字组成

    public static boolean isNumber(String str){ char[] ch = str.toCharArray(); //循环遍历ch 中的每个字符,判断这个字符是否为数字字符 //['0',....'9'] for(char i : ch){//     if(i  '9'){//  //找到反例//  return false;//     }     if(!Character.isDigit(i)){  return false;     } } return true;    }

isDigit是char 包装类(Character)的方法,判断字符是否为数字,若是返回true,否则返回false.


3 字符串和字节之间的相互转换,将字符串保存到文件中或是通过网络传输都要用到字节数组

String -> byte[]

//将字符串以字节数组的形式返回,按照当前默认的字符编码转为字节public byte[] getBytes()
//编码转换处理,按照指定的编码格式转为字节数组public byte[] getBytes(String charsetName)throws UnsupportedEncodingException

看代码:

String str1 = "中国";byte[] by = str1.getBytes();//JDK 1.8 ,默认编码格式为 UTF-8System.out.println(Arrays.toString(by));

输出:

[-28, -72, -83, -27, -101, -67]

在UTF8编码下,一个汉字3个字节

在GBK编码下,一个汉字2个字节


byte[] -> String

按照ASCII码转换为 String

//将字节数组变为字符串,通过String的构造方法public String (byte byte[])
//将部分字节数组中的内容变为字符串,通过String的构造方法public String(byte byte[],int offset,int length)

看代码:

byte[] by = {97, 98,99,100};String str1 = new String(by);String str2 = new String(by,1,2);System.out.println(str1);System.out.println(str2);

输出:

abcdbc

4 字符串查找操作

从一个完整的字符串之中可以判断指定内容是否存在,对于查找方法由如下定义:

常用:contains,startsWith,endsWith

//判断一个子字符串是否存在public boolean contains(CharSequence s)
//从头开始查找指定字符串的位置,查到了返回位置的开始索引,查不到返回-1public int indexOf(String str)
//从指定位置开始寻找子字符串位置public int indexOf(String str,int fromIndex)
//由后向前查找子字符串位置public int lastIndexOf(String str)
//从指定位置由后向前查找子字符串位置public int lastIndexOf(String str,int fromIndex)
//判断是否以指定字符串开头public boolean startsWith(String prefix)
//从指定位置开始判断是否以指定字符串开头public boolean startsWith(String prefix, int toffset)
//判断是否以指定字符串结尾public boolean endsWith(String suffix)

看代码:

String str = "hello word! java";System.out.println(str.contains("llo")); // trueSystem.out.println(str.indexOf("llo")); // 2System.out.println(str.indexOf("llo",3));// -1System.out.println(str.lastIndexOf("l")); //3System.out.println(str.lastIndexOf("o",6)); //4System.out.println(str.startsWith("hel")); //trueSystem.out.println(str.startsWith("hello",3)); //falseSystem.out.println(str.endsWith("java")); //true

5 字符串替换操作

//替换所有的指定内容,regex -> 替换前的, replacement -> 替换后的public String replaceAll(String regex, String replacement)
//替换首个内容public String replaceFirst(String regex, String replacement)

注意:String类的所有针对字符串的操作方法都不会修改原字符串,而是产生一个新的字符串!!! 因为字符串的不可变性

看代码:

 String str = "hello word"; System.out.println(str.replaceAll("o", "~")); System.out.println(str.replaceFirst("l", "~"));

输出:

hell~ w~rdhe~lo word

6 字符串的拆分操作

//将字符串全部拆分public String[] split(String regex)

若字符串中没有指定拆分的子字符串,拆分后仍然得到原字符串

//将字符串部分拆分,该数组长度就是limit极限public String[] split(Stirng regex, int limit)

看代码:

String str = "hello word!!! hello java!!!";String[] str1 = str.split(" ");String[] str2 = str.split(" ",2);String[] str3 = str.split(" ",5);//极限为4,输出会分割为 4 个,不会报错System.out.println(Arrays.toString(str1));System.out.println(Arrays.toString(str2));System.out.println(Arrays.toString(str3));

输出:

[hello, word!!!, hello, java!!!][hello, word!!! hello java!!!][hello, word!!!, hello, java!!!]

再看这个代码:

String str = "198.168.100.1";String[] str1 = str.split(".");System.out.println(Arrays.toString(str1));

此时输出:

[]

这是因为你的分割字符是一个特殊字符,需要进行转义处理“\\”.

String str = "198.168.100.1";String[] str1 = str.split("\\.");System.out.println(Arrays.toString(str1));

此时输出就是正确的

[198, 168, 100, 1]

7 字符串的截取处理

//从指定索引截取到结尾public String substring(int beginIndex)
//截取部分内容public String substring(int beginIndex, int endIndex)

范围: [start,end),左闭右开

看代码:

String str = "hello java";String str1 = str.substring(3);String str2 = str.substring(6,8);System.out.println(str1);System.out.println(str2);

输出:

lo javaja

8 其他常用方法

//去掉字符串中的左右开格,保留中间空格public String trim()
//字符串转大写public String toUpperCase()
//字符串转小写public String toLowerCase()
//字符串入池操作public native String intern() 
//字符串连接,等同于 “+”public String concat(String str)
//取得字符串长度public int length()
//判断是否为空字符串,但不是null,而是长度为0public boolean isEmpty()
String str = null;

此时str.isEmpty(); 方法就不能使用了


写一个方法判断传入的字符串为空(要么是null,要么长度为0)

    public static boolean isNullEmpty(String str){// if(str == null || str.isEmpty()){//     return true;// }// return false; return str == null || str.isEmpty();    }

写一个方法将字符串的首字母大写处理

public static String firstUpper(String str){    //若字符串为 null 或长度为0 返回null    if(str == null || str.isEmpty()){ return null;    }    //若字符串长度为1 ,直接进行字符串转大写操作    if(str.length() == 1){ return str.toUpperCase();    }    return str.substring(0,1).toUpperCase() + str.substring(1);}

一定要记住,所有针对String的操作均无法修改原字符串的内容,都是产生了新的字符串做处理。


要是对大家有所帮助的话,请帮我点个赞吧。