Java入门——String类

一、JDK中String的声明

  1. String继承的接口 :
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
  1. 为什么String类被final修饰?

被final修饰的类无法被继承,String类不存在子类。保证所有用JDK的人,用到的String类就一个,就不会造成子类行为不一致的问题。

否则,所有子类都能使用String的方法,都能自己设计一套equals方法等。这样无法一致性。会造成这个程序能用,下个程序不能用的情况,会产生很多版本。

二、创建字符串的四种方式

  1. 直接赋值

  2. 通过构造方法产生对象

  3. 通过字符数组产生对象

  4. 通过String静态方法valueOf(任意数据类型),转换为字符串

public class StringTest {
    public static void main(String[] args) {
        //第一
        String str = "hello";
        //第二
        String str2 = new String("hello");
        //第三
        char[] data = new char[]{'a','b','c'};
        String str3 = new String(data);
        //第四
        String str4 = String.valueOf(10);
    }
}

三、字符串的字面量

定义:直接写出来的数值称为字面量

10 —— int字面量

10.1 —— double字面量

true —— boolean字面量

“abc” —— String字面量 ->String是个引用类型,就是一个字符串对象

public static void main(String[] args){
    String str = "hello";
    String str1 = str;
    str1 = "hello world";
    System.out.println(str);
}
输出:
hello

str和str1是两个对象,开辟了两块内存空间

下面来看一个代码

public static void main(String[] args) {
    String userName = null;
    System.out.println(userName.equals("张三")); //1
    System.out.println("张三".equals(userName)); //2
}
1 输出:NullPointerException
2 输出:false

牵扯到用户输入就一定要做判空处理。

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

四、字符串常量池

看一个代码

public static void main(String[] args) {
        String str = "hello";
        String str1 = "hello";
        String str2 = "hello";
        String str3 = new String("hello");
        String str4 = new String("hello");
        System.out.println(str == str1); //1
        System.out.println(str1 == str2); //2
        System.out.println(str3 == str4); //3
}
输出:
true
true
false

"=="比较的是两个对象的地址,1,2代码返回的是true,说明这三个对象指向的是同一块内存。3是false,说明这两个对象属于不同的内存空间,new一个对象就开辟了一块内存,所以毋庸置疑这两个对象肯定不是一个内存空间的。

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

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

大多数"池"—— 都是共享设计模式的思想,目的是为了节省空间,节约资源

内存图
在这里插入图片描述
在这里插入图片描述

4.1手工入池方法

String类提供的intern方法

源码如下:

public native String intern();

用法:

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

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

一道考试题

 public static void main(String[] args) {
        String str1 = new String("hello"); //1
        str1.intern(); //2
        String str2 = "hello"; //3
        System.out.println(str1 == str2); //4
 }
输出:
false
  1. new一个对象,在堆中产生一块空间。hello是字符串字面量,也是一个对象,便在常量池中存放一份。str1指向的是堆内存中的hello,而不是常量池中的hello
  2. 由于常量池中已经存在hello对象,则不再产生新的对象。
  3. str2指向的是常量池中的hello
  4. 两个不是一个地址,所以返回false

如何让结果返回true呢?

public static void main(String[] args) {
        String str1 = new String("hello"); //1
        str1 = str1.intern(); //2
        String str2 = "hello"; //3
        System.out.println(str1 == str2); //4
 }
输出:
true

当常量池中已经存在hello时,代码2会返回常量池中的String对象地址,被str1接收,所以str1也指向了常量池中的hello。结果返回true。

再看一道题

public static void main(String[] args) {
        char[] data = new char[]{'a', 'b', 'c'};
        String str1 = new String(data);
        str1.intern();
        String  str2 = "abc";
        System.out.println(str1 == str2);
}
输出:
true

字符数组data在堆中开辟空间。若当前常量池中不存在该对象,则将该对象入池,返回入池后的地址。str2发现常量池中已有“abc”,直接复用。结果就相等。

内存图

在这里插入图片描述

五、字符串的不可变性

字符串不可变指的是字符串的内容不可变。字符串对象一旦产生并赋值就不能改其内容。而字符串引用是可变的,它可以按需指向各个字符串。

提问:为什么字符串对象内容无法修改而其他类的对象可以修改内容?

因为字符串实际上时一个字符数组,字符串保存的值是实在数字中保存的。以下是String源码

//The value is used for character storage. 
private final char value[];

他是一个私有权限,对外部隐藏,并且没有提供getter和setter方法,外部拿不到value数组,所以字符串的内容无法修改。

六、修改字符串内容

6.1 通过反射破坏数组的封装(了解,不推荐)

public class StringChange {
    public static void main(String[] args) throws Exception {
        String str = "hello";
        Class<String> cls = String.class;
        //获取value成员属性
        Field field = cls.getDeclaredField("value");
        //设置成可更改的,破坏封装
        field.setAccessible(true);
        //在String类外部通过反射拿到value数组
        char[] value = (char[]) field.get(str);
        value[0] = 'H';
        System.out.println(str);
    }
}
输出:
Hello

6.2 使用StringBuilder或StringBuffer类(重点)

若需要经常使用字符串的拼接,使用StringBuilder类的append方法。

这个类可以修改字符串对象的内容。

StringBuilder和StringBuffer的使用方法完全一样

StringBuilder类目的是为了解决字符串拼接的问题。StringBuilder类的对象是可以修改内容的。

StringBuilder和String时两个独立的类

StringBuilder类和String类的转换

  1. StringBuilder -> String : 调用这个类的toString方法
public static void main(String[] args) {
    StringBuilder stringBuilder = new StringBuilder("hello");
    stringBuilder.append("world");
    //修改成String类型
    String str = stringBuilder.toString();
    System.out.println(str);
    //查看数据类型
    System.out.println(str.getClass().getName());
}
输出:
helloworld
java.lang.String

利用StringBuilder的toSting方法将其转换成Sting类型

  1. String -> StringBuilder: 使用StringBuilder的构造方法或者append方法
public static void main(String[] args) {
    StringBuilder stringBuilder = new StringBuilder("hello"); //1
    stringBuilder.append("world");  //2
    System.out.println(stringBuilder);
    //查看数据类型
    System.out.println(stringBuilder.getClass().getName());
}
输出:
helloworld
java.lang.StringBuilder

1 将hello字符串字面量转换成StringBuilder类

2 将world字符串字面量转换成StringBuilder类

StringBuilder的常用方法

  1. 字符串的反转:reverse()
public static void main(String[] args) {
    StringBuilder stringBuilder = new StringBuilder("hello");
    stringBuilder.append("world");
    System.out.println(stringBuilder);
    stringBuilder.reverse();
    String str = stringBuilder.toString();
    System.out.println(str);
}
输出:
helloworld
dlrowolleh
  1. 字符串添加:append()
public static void main(String[] args) {
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append("hello");
    stringBuilder.append("world");
    System.out.println(stringBuilder);
}
输出:
helloworld
  1. 删除指定范围的字符:delete(int start,int end)——左闭右开区间
public static void main(String[] args) {
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append("hello");
    stringBuilder.append("world");
    stringBuilder.delete(5,10);
    System.out.println(stringBuilder);
}
输出:
hello
  1. 字符串插入:insert(int start,value)
public static void main(String[] args) {
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append("hello");
    stringBuilder.append("world");
    //在索引为5的位置插入28
    stringBuilder.insert(5,28);
    System.out.println(stringBuilder);
}
输出
hello28world

StringBuilder,StringBuffer和String类的区别

  1. String的对象无法修改,其他两个对象内容可以修改
  2. StringBuffer是线程安全的操作,性能较差;StringBuilder是线程不安全的操作,性能较高。

七、字符串的常用操作

String类的所有针对字符串的操作方法都不会修改原字符串,而是产生了一个新的字符串

7.1 字符串的比较

方法名称作用
public boolean equals(Object anObject)区分大小写的比较
public boolean equalsIgnoreCase(String anotherString)不区分大小写的比较
public int compareTo(String anotherString)比较两个字符串大小的关系

String实现了Comparable这个接口,覆写了compareTo方法,返回ASCII码的差值

按照“字典序”排列字符串,就是按照字符串的ASCII码大小排序

public static void main(String[] args) {
	String str1 = "abc";
	String str2 = "Abc";
	System.out.println(str1.compareTo(str2));
}
输出:
32

7.2 字符和字符串的相互转换(重要)

方法名称作用
public Sting(char value[])将字符数组中的所有内容变为字符串
public String(char value[], int offest, int count)将部分字符数组中的内容变为字符串
public char charAt(int index)取得指定索引位置,从索引0开始
public char[] toCharArray()将字符串变为字符数组返回

字符数组转换为字符串,通过String类的构造方法

public static void main(String[] args) {
    char[] data = new char[]{'a','b','c'};
    String str = new String(data);//全部转换
    String str1 = new String(data,1,2);//部分转换
    System.out.println(str);
    System.out.println(str1);
}
输出:
abc
bc

字符串转为字符数组的两种方式

public static void main(String[] args) {
    String str = "hello";
        //取出字符串中索引为1位置的值
        char ch = str.charAt(1);
        System.out.println(ch);
        //将字符串转为字符数组
        char[] data = str.toCharArray();
        data[0] = 'H';
        System.out.println(data);
        System.out.println(str);
}
输出:
e
Hello
hello

修改字符数组中值对原字符串无影响。这里是产生了一个新的字符数组,将字符串的内容复制到字符数组中。字符串不可变性

一个小题目:如何判断一个输入的字符串是由纯数字组成,若是返回true,不是返回false

public boolean isNumber(String str) {
    //将字符串转为字符数组
    char[] data = str.toCharArray();
    //循环遍历字符数组,找反例
    for (char c : data) {
        if (c < '0' || c > '9'){
            return false;
        }
    }
    return true;
}

7.3 字节与字符串的相互转换

方法名称作用
public String(byte byte[])将字节数组变为字符串
public String(byte bytes[],int offest,int length)将部分字节数组中的内容变为字符串
public byte[] getBytes()将字符串以字节数组的形式返回
public byte[] getBytes(String charsetName) throws UnsupportedEncondingException编码转换

字节数组转字符串调用字符串的构造方法就可以,与字符数组转字符串类似。

按照ASCII码值转为字符串

public static void main(String[] args) {
    byte[] data = new byte[]{97,98,99};
    String str = new String(data);//全部转换
    System.out.println(str);
    String str1 = new String(data,1,2);//部分转换
    System.out.println(str1);
}

字符串转为字节数组

  1. 按照当前默认的字符编码转为字节数组
  2. 按照指定的编码格式转为字节数组
public void main(String[] args){
    String str = "你好";
    byte[] data = str.getBytes(); //默认编码
    byte[] data1 = str.getBytes("gbk"); //指定编码
    System.out.println(Arrays.toString(data));
    System.out.println(Arrays.toString(data1));
}
输出:
[-28, -67, -96, -27, -91, -67]
[-60, -29, -70, -61]

7.4 字符串的查找

方法名称作用
public boolean contains(CharSquence s)判断一个子字符串是否存在
public boolean startsWith(String prefix, int toffset)从指定位置开始判断是否以指定字符串开头
public boolean endsWith(String suffix)判断是否以指定字符串结尾
public static void main(String[] args) {
        String str = "hello world";
    	//判断是否包含字串
        System.out.println(str.contains("world"));
    	//是否以指定字符串开头
        System.out.println(str.startsWith("hello"));
        System.out.println(str.startsWith("hello1"));
    	//是否以指定字符串结尾
        System.out.println(str.endsWith("world"));
        System.out.println(str.endsWith("world1"));
    }
输出:
true
true
false
true
false

7.5字符串替换操作

public static void main(String[] args) {
    String str = "helloworld";
    //替换所有的指定内容
    System.out.println(str.replaceAll("l","_"));
    //替换首个内容
    System.out.println(str.replaceFirst("l","_"));
    System.out.println(str);
}
输出:
he__owor_d
he_loworld
helloworld

7.6 字符串的拆分操作

public static void main(String[] args) {
    String str = "Hello world Hello you";
    //按照"_"拆封字符串
    String[] data1 = str.split("-");
    System.out.println(Arrays.toString(data1));
    System.out.println(data1.length);
    // 按照空格将str拆分,拆分后的字符串数组长度为2
    String[] data2 = str.split(" ",2);
    System.out.println(Arrays.toString(data1));
    System.out.println(Arrays.toString(data2));
    System.out.println(data2.length);
}
输出:
[Hello world Hello you]
1
[Hello world Hello you]
[Hello, world Hello you]
2

当按照指定的格式分割字符串得到了一个空数组,说明这个格式是特殊字符,需要转义处理

public static void main(String[] args) {
	String str = "192.168.1.1";
    String[] data = str.split("\\.");
    System.out.println(Arrays.toString(data));
}
输出:
[192, 168, 1, 1]

当指定的拆分格式在原字符串中不存在,就会输出原字符串

public static void main(String[] args) {
    String str = "Hello,Wolrld";
    String[] data = str.split("-");//不存在
    System.out.println(Arrays.toString(data));
}
输出:
[Hello,Wolrld]

7.7 字符串截取

方法名称描述
public String substring(int beginIndex)从指定索引截取到结尾
public String subSting(int beginIndex,int endIndex)截取部分内容
public static void main(String[] args) {
    String str = "HelloWolrd";
    System.out.println(str.substring(5));
    //前闭后开区间
    System.out.println(str.substring(0,5));
}
输出:
Wolrd
Hello

7.8 其他操作

方法名称描述
public String trim()去掉字符串中的左右空格,保留中间空格
public String toUpperCase()字符串转大写
public String toLowerCase()字符串转小写
public int length()取得字符串的长度
public boolean isEmpty()判断是否为空字符串,但不是null,只能判断长度是否为0
public static void main(String[] args) {
    String str = "  hello world  ";
    // 只会去掉str的左右空格,保留中间的空格
    System.out.println(str.trim());
    System.out.println(str.toUpperCase());
    System.out.println("HELLO".toLowerCase());
    // 5
    System.out.println("hello".length());
}
输出:
hello world
  HELLO WORLD  
hello
5

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

public static String firstUpper(String str) {
    //判空
    if (str == null || str.isEmpty()){
        return null;
    }
    //字符串只有一个元素
    if (str.length() == 1){
        return str.toUpperCase();
    }
    //截取 + 大写
    return str.substring(0,1).toUpperCase();
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值