详解String类【Java】

目录

前言

一、String类的常用方法

1.1 常用的字符串构造

1.2 String类的大小比较

1.2.1 引用对象比较

1.2.2 字符大小的比较

1.3 字符串查找

1.4 数据类型之间的转化

1.4.1 数字与字符串

1.4.2 大小写转换

1.4.3 字符串转数组

1.4.4 格式化

1.4.5 字符串替换

1.4.6 字符串拆分

1.4.7 字符串截取

二、字符串常量池

2.1 intern()方法详解

三、StringBuilder和StringBuffer


前言

在Java中是没有“字符串”这一说法的,C语言中的字符串用标准库将数据和操作方法分离,这是不符合Java语言面相对象的思想,所以Java提供了String类,这更能方便程序员使用和操作字符串。


一、String类的常用方法

1.1 常用的字符串构造

跟数组的定义时差不多的

public class Test {
    public static void main(String[] args) {
        String str = "java";//跟数组一样,简写了
        String str1 = new String("java");
        char[] chars = {'j','a','v','a'};
        String str2 = new String(chars);

        System.out.println(str);
        System.out.println(str1);
        System.out.println(str2);
        System.out.println(str.length());//打印长度
    }
}

在Java中,String类是一个引用类型(final修饰不能被继承),但是呢内部并不存储字符串本身,我们可以同过String类的原码可以得出结果:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];//value是一个数组,没有分配空间,字符串就存储在value数组中

    /** Cache the hash code for the string */
    private int hash; // Default to 0

 从原码中可以得出,String类在堆区中存了两个数据元素,hash和value[]:

 

chars在堆区中开辟了一块内存,用来存放字符串"java";str2在堆区中也创建了一块内存用于存放value[]和hash,然后拷贝chars引用的对象放入一块新的内存,将value引用 引用这块拷贝好的内存。

1.2 String类的大小比较

1.2.1 引用对象比较

public static void main(String[] args) {
        //对于引用类型来说 ==比较的是引用的对象的地址
        String str = "java";//跟数组一样,简写了
        String str1 = new String("Python");
        char[] chars = {'j','a','v','a'};
        String str2 = new String(chars);
        str = str1;
        System.out.println(str2==str1);//false在栈区上开辟空间,比较的是引用的对象的内存地址
        System.out.println(str1==str);//true
    }

1.2.2 字符大小的比较

String类中用equals方法比较, equals方法属于父类Object类,默认的比较方式用“==”比较(内存);我们可以重写equals方法来根据自己的需求来比较大小(String类自己已经重写了equals方法,用来比较里面的值是否相同):

列表中的方法都有一一实现:

public static void main(String[] args) {
        String str = "java";
        String str1 = "JAVA";
        System.out.println(str.equals(str1));//所引用的对象不同
        System.out.println(str.compareTo(str1));//结果是ASCII的差值
        System.out.println(str.compareToIgnoreCase(str1));//忽略大小写比较
        System.out.println(str.equalsIgnoreCase(str1));//忽略大小写比较是否相同 常量池下面讲解
    }

1.3 字符串查找

字符串的常用方法已经列举在下面的代码中了。

//char charAt(int index);int indexOf(int ch);
//int indexOf(int ch, int fromIndex);int indexOf(String str)
//int indexOf(String str, int fromIndex);int lastIndexOf(int ch)
//int lastIndexOf(String str, int fromIndex)
public static void main(String[] args) {//字符串查找
        String str = "asd";
        for (int i = 0; i < str.length(); i++) {
            char ret = str.charAt(i);//注意越界,根据下标查找
            System.out.print(ret+" ");
        }
        System.out.println("=========");
        int ret = str.indexOf('s',1);//获取d第一次出现的位置,从s位置开始查找 字符对应的ASCII值
        System.out.println(ret);
        System.out.println("=========");
        ret = str.indexOf("asd");//查找字符串,返回第一个字符的起始位置 KMP算法:最高效的算法
        System.out.println(ret);
        System.out.println("=========");
        str = "asd.dsa";
        System.out.println(str.lastIndexOf('.',2));//从后往前找,3代表在位置3前面的字符里面找
        System.out.println(str.lastIndexOf("asd",3));
    }

1.4 数据类型之间的转化

1.4.1 数字与字符串

public static void main(String[] args) {//其他的数据类型转化为字符串
        //valueOf();有许多的重载的方法,看自己具体的实现
        //数字转字符串
        String str = String.valueOf(12);
        String str1 = String.valueOf("dasd");
        System.out.println(str1);
        System.out.println(str);
        //讲一个对象转换成字符串
        String str2 = String.valueOf(new Student("小明"));
        System.out.println(str2);
        System.out.println("=========");
        //字符串转数字
        //将"123"转换成数字,在进行进制的转换
        int str3 = Integer.valueOf("123",8);//radix:进制,将十进制的数转换成八进制
        System.out.println(str3);

    }

1.4.2 大小写转换

public static void main(String[] args) {//大小写转换,不会改变当前的字符串,会产生新的对象
        String str = "sadsDDDa上大";
        System.out.println(str.toUpperCase());
        System.out.println(str.toLowerCase());
    }

1.4.3 字符串转数组

public static void main(String[] args) {//字符串转化成数组
        String str = "hello";
        char[] chars = str.toCharArray();//拷贝
        for (char x : chars) {
            System.out.println(x);
        }
        System.out.println(str);
    }

1.4.4 格式化

public static void main(String[] args) {//格式化
        String str = String.format("%d-%d-%d",2022,07,27);
        System.out.println(str);
    }

1.4.5 字符串替换

public static void main(String[] args) {//字符串替换
        String str = "asdasdasdwqeqwewe";
        String ret = str.replace('a','z');
        System.out.println(ret);
        ret = str.replace("asd","dsa");
        System.out.println(ret);
        ret = str.replaceAll("asd","dsa");
        System.out.println(ret);
        ret = str.replaceFirst("asd","dsadasdasdas");
        System.out.println(ret);
    }

1.4.6 字符串拆分

public static void main(String[] args) {//字符串拆分
        String str = "asd  asd  asd  wqe  wew";
        String[] ret = str.split("  ",3);//最多拆分3组,中间的空格与你字符串的空格要与之对应
        for (String s : ret) {
            System.out.println(s);
        }
        System.out.println();
        str = "2022.07.27";
        ret = str.split("\\.");
        for (String s : ret) {
            System.out.println(s);
        }
        System.out.println();
        str = "2022\\07\\27";
        ret = str.split("\\\\");
        for (String s : ret) {
            System.out.println(s);
        }
        System.out.println();
        str = "2022 07&27";//如果一个字符串中有多个分隔符,可以用"|"作为连字符
        //ret = str.split(" |&");
        ret = str.split("[ &]");//正则表达式
        for (String s : ret) {
            System.out.println(s);
        }
        System.out.println();
        //多次分割
        str = "2022&07&27 星期三";
        ret = str.split("&");
        for (String s : ret) {
            //s[0] = 2022
            //s[1] = 07
            //s[2] = 27 星期三
            String[] ss = s.split(" ");
            for (String s1 : ss) {
                System.out.println(s1);
            }
        }
    }

1.4.7 字符串截取

public static void main(String[] args) {//字符串截取
        String str = "asdasdasdwqewew";
        System.out.println(str.substring(1));//从给出的位置开始截取
        str = str.substring(1,6);//截取1位置到5位置的字符串,截取后又成了一个新的对象
        System.out.println(str);
        System.out.println();
        str = "   asdasdasdwqewew      " +
                " ";
        System.out.println(str.trim());//去掉左右的空格 换行 制表符等
    }

二、字符串常量池

常量池:为了使程序的运行速度更快,减少内存的消耗,java中提供了8中类型的常量池,这里重点讲字符串常量池(专门存放字符串)。下面我们通过一段代码来展开学习:

public static void main(String[] args) {
        String s = "hello";
        String s1 = "hello";//第二次存储这个字符串的时候,都回去常量池里面去寻找是否存有,创建与否,提高了效率
        System.out.println(str == str1);//打印true
    }

通过上面的学习我们知道,String是一个引用类型,在堆区中会有一个引用数组value[],数组引用了字符串"java"。上面的代码那就应该是创建了两个内存空间,那为什么最后会打印true呢?这就是常量池所带来的结果。简单画个图来了解常量池:

字符串常量池在JVM中是StringTable类,实际是一个固定大小的HashTable(一种高效用来进行查找的数据结构)。 String 类型,JVM 内部使用 HashTable 进行缓存,我们知道,HashTable 的结构是一个数组,数组中每个元素是一个链表。

字节码文件加载的时候,s和s1就会被创建,但是在创建之前他们会先去字符串常量池当中寻找是否有和s和s1相同内容的字符串,如果有就会引用常量池当中的字符串(相当于字符串引用赋值给s或s1),如果没有就在常量池当中创建并保存。所以上面的代码中,s就会先在常量池当中创建,s1和s的字符串相同,那么s1就会引用s在常量池当中创建好了的字符串。s和s1都同时指向了同一块内存空间(只要是""括起来都会存在字符串常量池当中;可以避免频繁的创建字符串,减少了空间的损耗,提高了效率)。

2.1 intern()方法详解

先看一段代码:

public static void main(String[] args) {
        char[] chars = new char[]{'j','a','v','a'};
        String str = new String(chars);
        String str1 = "java";
        System.out.println(str==str1);//打印false
    }

上面中str和str1引用的是不同的对象,由第一章图变可知,如果我们加入intern()后,会有什么影响呢?下面请看代码:

public static void main(String[] args) {
        char[] chars = new char[]{'j','a','v','a'};
        String str = new String(chars);
        str.intern();//手动入池
        String str1 = "java";
        System.out.println(str==str1);//打印true
    }

 对str使用intern()方法后,整段代码的结果就变了,下面图解:

intern()方法就是手动入池,他会检查str在常量池当中是否有的相同的数据(是否存在),不存在就把str的引用的对象保存到常量池当中;如果存在,则返回这个对象。所以当str使用了intern()方法后,常量池当中就会存在字符串"java",所以str和str1都同时引用了字符串"java"这个对象。

三、StringBuilder和StringBuffer

这两个方法是由于 String 的不可更改特性,为了方便字符串的修改而提供的。
先看一段代码:
public static void main(String[] args) {
        String str = "java";
        str+="EE";//不是在原来的str上拼接的,重新开辟了一块空间
        System.out.println(str);
    }

对于这种情况来说,我们操作字符串的时候大多数的时候都会创建新的空间,操作的字符串过多时,就会发生占用大量的空间,降低效率的情况。StringBuilder和StringBuffffer方法就是来解决出现这样的情况的。

再看一段代码:

public static void main(String[] args) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("java");
        stringBuilder.append("EE");
        System.out.println(stringBuilder);//打印javaEE
        System.out.println("=========");
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("java");
        stringBuffer.append("EE");
        System.out.println(stringBuffer);//打印javaEE
    }

对于StringBuilder和StringBuffer方法来说,拼接字符串(不只有append方法,有许多的重载方法)的时候,他们都是在同一个对象里面操作字符串的,地址没有改变,改变的只是里面的值。两个的作用都是一样的,但是他们在处理不同的场景的时候,功能的大小有所不同。

StringBuilder:一般处理单线程

StringBuffer:一般处理多线程,我们查看原码是发现StringBuffer方法多了synchronized修饰,synchronized是对象锁的意思,多线程操作的时候,我们会将他们锁进一个封闭的房间里面,知道他们的所有操作完成,这样就对他们提供了安全保障。(开锁和关锁需要消耗资源,所以看场景使用方法)

StringBuffer 采用同步处理,属于线程安全操作;而 StringBuilder 未采用同步处理,属于线程不安全操作。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

霄百

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值