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

(点击跳转即可哦)

java学习专栏

LeetCode刷题专栏


认识String类



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 < '0' || 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-8
System.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);

输出:

abcd
bc

4 字符串查找操作

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

常用:contains,startsWith,endsWith

//判断一个子字符串是否存在
public boolean contains(CharSequence s)
//从头开始查找指定字符串的位置,查到了返回位置的开始索引,查不到返回-1
public 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")); // true
System.out.println(str.indexOf("llo")); // 2
System.out.println(str.indexOf("llo",3));// -1
System.out.println(str.lastIndexOf("l")); //3
System.out.println(str.lastIndexOf("o",6)); //4
System.out.println(str.startsWith("hel")); //true
System.out.println(str.startsWith("hello",3)); //false
System.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~rd
he~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 java
ja

8 其他常用方法

//去掉字符串中的左右开格,保留中间空格
public String trim()
//字符串转大写
public String toUpperCase()
//字符串转小写
public String toLowerCase()
//字符串入池操作
public native String intern() 
//字符串连接,等同于 “+”
public String concat(String str)
//取得字符串长度
public int length()
//判断是否为空字符串,但不是null,而是长度为0
public 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的操作均无法修改原字符串的内容,都是产生了新的字符串做处理。


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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值