java字符串

一、String类概述

1.String是java定义好的一个类,定义在java.lang包(java核心包)下,所以使用时不需要导包。

2.java程序中所有的字符串文字(例如"abcd"),都被视为此类的对象。

3.字符串不可变,它们的值在创建后不能被更改。(下面会详细解释)

二、创建字符串对象方式

1.使用直接赋值的方式获取一个字符串对象

注:用该方法时,系统会先检查该字符串是否在串池(字符串常量池)中存在。

        不存在,创建新的,将地址传给变量

        存在,将地址传给变量

优点:字符串重复,会复用,节约内存

public class StringDemo {
    public static void main(String[] args) {
        String s1 = "abc";
        String s2 = "abc";
    }
}

起始串池为空,对于s1,在串池中开辟一个新的空间存放"abc",将地址0x0011赋值给s1。

                        对于s2,会先检查串池中是否存在"abc",发现存在后,将地址0x0011赋值给s2。

这种情况下只创建了一个对象,s1和s2都指向串池中的同一个对象

 

为什么说字符串不可变?

字符串的内容是不会发生改变的,他的对象在创建后不会被更改。

举个🌰🌰🌰

public static void main(String[] args) {
    String name = "zhangsan";
    name = "lisi";
}

给一个已有字符串"zhangsan",第二次重新赋值成"lisi",不是在原内存地址上修改数据,而是重新指向一个新对象,新地址

所以一共创建了两个字符串对象,原来的字符串对象"zhangsan"仍在,并未修改。

2.使用new的方式来获取一个字符串对象

 注:通过该方法,字符串不放在串池中,而是在直接放在堆中。(例子见③)

每new一次,就会在堆中开辟一个新的空间,所以这种方式不会复用,会浪费内存空间。

①空参构造:可以活动一个空白的字符串对象-->""

String str1 = new String();
System.out.println("a" + str1 + "b");//ab

②传递一个字符串,根据传递的字符串内容在创建一个新的字符串对象(没有必要,用的最少)

String str2 = new String("abc");
System.out.println(str2);//abc

 ③传递一个字符数组,根据字符数组的内容在创建一个新的字符对象。

应用场景:由于字符串不可修改,所以如果想修改字符串的内容,可以先将字符串拆成字符数组,修改数组中的内容后,再利用该方法进行拼接。

String str="dbca";
//toCharArray方法可以将字符串变成字符数组
char[] data=str.toCharArray();
//交换第一个和最后一个元素
char temp = data[0];
data[0]=data[data.length-1];
data[data.length-1]=temp;
String str3 = new String(data);
System.out.println(str3);//abcd

内存模型: 

所以,每new一次,就会在堆中开辟一个新的空间,字符串对象不会复用。

④传递一个字节数组,根据字节数组的内容去ASCII表中查找对应字符,在创建成一个字符对象。

应用场景:在网络中传输的数据都是字节信息,一般要把字节信息进行转换通过该方法转成字符串

byte[] bytes = {97, 98, 99, 100, 101};
String str4 = new String(bytes);
System.out.println(str4);//abcde

三、字符串的比较 

1.==比较的是什么

①比较基本数据类型:比的是数据值

②比较引用数据类型:比的是地址值

public class CompareString {
    public static void main(String[] args) {
        String s1=new String("abc");
        String s2="abc";
        System.out.println(s1==s2);//false
    }
}

由上可知,s1是在堆内存中创建的字符串对象,而s2实在串池中创建的字符串对象。两个对象的地址是不一样的,所以为false。

2.equals()比较

比较字符串对象的内容是否严格相等(不忽略大小写)

public class CompareString {
    public static void main(String[] args) {
        String s1=new String("abc");
        String s2="abc";
        String s3="ABC";

        boolean result1=s1.equals(s2);
        System.out.println(result1);//true
        boolean result2=s1.equals(s3);
        System.out.println(result2);//false
    }
}

3.equalsIgnoreCase()比较

比较字符串对象的内容是否相等(忽略大小写)

public class CompareString {
    public static void main(String[] args) {
        String s1=new String("abc");
        String s2="abc";
        String s3="ABC";

        boolean result3=s1.equalsIgnoreCase(s3);
        System.out.println(result3);//true
    }
}

Test:模拟登录系统,键盘输入一个字符串abc,再在代码中定义一个字符串abc,用==比较,是否一样?

public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        System.out.println("请输入一个字符串");
        String str1=sc.next();

        String str2="abc";
        System.out.println(str1==str2);//false
}

原因:通过源码发现,next的底层逻辑是通过new string()出来的

结论:只要涉及字符串比较,就必须用string类中的方法。

四、字符串的遍历,截取与替换

1.遍历

两个函数:

①length()

在数组中length是数组的属性不用加();而在字符串中,length是string类的方法需要加()

②charAt() --> 根据索引返回字符

import java.util.Scanner;

public class IterateString {
    public static void main(String[] args) {
        //键盘录入一个字符串
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入一个字符串");
        String str = sc.next();

        //统计字符串中大写字母,小写字母,数字的个数
        int bigCount = 0;
        int smallCount = 0;
        int numCount = 0;
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            //char类型的变量在参与比较或者计算的时候会自动提升为int型(根据ASCII码表转换)
            if (c >= 'a' && c <= 'z') {
                smallCount++;
            } else if (c >= 'A' && c <= 'Z') {
                bigCount++;
            }else if(c >= '0' && c <= '9'){
                //这里不能写c >= 0 && c <= 9  -->  0所对应的ASCII码是48
                numCount++;
            }
        }
        System.out.println("小写字母有"+smallCount+"个");
        System.out.println("大写字母有"+bigCount+"个");
        System.out.println("数字字母有"+numCount+"个");
    }
}

2.截取

 注:由于字符串不可修改,调用函数后并未对原字符串进行修改,而是返回一个新的截取后的字符串对象

①subString(开始索引,结束索引)     包头不包尾,包左不包右

②subString(开始索引)      默认截取到最后

public class SubString {
    public static void main(String[] args) {
        //手机号屏蔽:13112349468-->131****9468
        String phone = "13112349468";

        String start = phone.substring(0, 3);//131

        String end = phone.substring(7);//9468

        String result = start + "****" + end;
        System.out.println(result);//131****9468
    }
}

3.替换

 注:由于字符串不可变,原字符串并没有被修改,而是返回了一个新的被替换后的字符串对象

replace(旧值,新值); 

public class ReplaceString {
    public static void main(String[] args) {
        String talk = "NM,你真厉害,TMD";
        
        String[] arr = {"TMD", "NM"};
        //循环依次替换每一个敏感词
        for (int i = 0; i < arr.length; i++) {
            talk = talk.replace(arr[i], "***");
        }
        System.out.println(talk);//***,你真厉害,***
    }
}

4.判断首字母 

 startsWith("xxx")        判断字符串是否以xxx开头,是返回true,不是返回false

String str = "abc";
boolean result = str.startsWith("ab");
System.out.print(result);//true

5.正则表达式 

matches("正则表达式")  校验字符串是否满足规则,满足返回true,不满足返回false

具体细节见:

java正则表达式icon-default.png?t=N7T8https://blog.csdn.net/xpy2428507302/article/details/139185880?spm=1001.2014.3001.5502

五、StringBuilder类

1.引言

String的缺点:由于字符串不可变,每次对字符串进行修改都会创建一个新的String对象(见六)导致频繁的内存分配和垃圾回收。所以在大量修改或者拼接字符串时,内存开销会非常大。

StringBuilder可以看成是一个容器,创建之后里面的内容是可变的

原因:StringBuilder的容量是动态调整的。规则如下:

作用:提高字符串的操作效率(见六3)

应用场景:

①字符串的拼接

②字符串的反转

2.构造方法

3.常用方法

注:

①由于StringBuilder是可变的,有时为了避免对象被修改,需要使用toString()方法将其转换为不可变的String对象再传递或返回。

②因为StringBuilder是java已经写好的类,在底层做了一些特殊处理,使得打印输出的不是地址值,而是属性值(内容)

由于StringBuilder是可变的,调用方法修改后会直接对容器中的字符串发生改变

public class StringBuilderDemo {
    public static void main(String[] args) {
        StringBuilder sb=new StringBuilder();
        System.out.println(sb);//空字符串

        //添加字符串
        sb.append(1).append(2.3).append(true);
        System.out.println(sb);

        //反转
        sb.reverse();
        System.out.println(sb);

        //获取长度
        int len=sb.length();
        System.out.println(len);

        //把StringBuilder变回字符串
        String str=sb.toString();
        System.out.println(str);
    }
}

运行结果:

六、字符串拼接的底层原理

在编译时,java会做一个检查,检查是否有变量参与拼接

1.如果没有变量参与,都是字符串直接相加

触发字符串的优化机制,编译之后就已经是最终拼接后的结果。

String s="a"+"b"+"c";等价于String s="abc";

所以字符串是在串池中创建的,后面会复用串池中的字符串。

Test:下列带代码运行结果是?

由上可知,String s2 = "a"+"b"+"c";等价于String s2 = "abc"; s1和s2为直接赋值的方式,s1所创建的字符串变量会存储在串池中,留给s2复用,所以此时s1和s2指向同一个对象,所以答案为true。

2.如果有变量参与

JDK8之前:

对于 s2 = s1 + "b";  首先创建一个StringBuilder对象,通过append方法将s1和"b"中的内容都添加到该对象中,在通过toString方法变回字符串对象。

s3 = s2 + "c";  同理

需要注意的是,toString方法的底层实际上是new string(),所以每一个+号都等于一次性创建了两个对象(一个StringBuilder对象和一个String对象),浪费内存

JDK8之后:

先去预估最终字符串的长度,在根据长度创建一个数组,将内容填充到数组中后,再去根据该数组创建一个字符串对象。

需要注意的是,虽然JDK8之后进行了优化,但每次拼接时还是会new String()创建一个新的字符串对象,仍会造成内存浪费。

结论:

如果很多字符串拼接,不要直接+。每一次+都会创建对象,多个+会在底层创建多个对象,浪费时间,浪费性能

Test:下列带代码运行结果是?

由上可知,+拼接最终一定会new String()产生一个字符串对象,该对象s3存储在堆内存中,而s1直接赋值存储在串池中,所以两者地址不同,答案为false。

3.StringBuilder提高效率的原理图

StringBuilder是一个内容可变的容器,通过append方法将所有的要拼接的数据往同一个StringBuilder对象中存储。至始至终都只有一个对象,而不是像上述String一样多次拼接后产生了多个对象,从而减小了内存开销。

七、StringJoiner类

1.概述 

JDK8后出现的一个可变的操作字符串的容器,可以更高效、方便的拼接字符串。

其性质和StringBuilder类似,注意点基本都和StringBuilder一样。

在拼接的时候,可以指定间隔符号,开始符号,结束符号。

2.构造方法

3.常用方法

public class StringJoinerDemo {
    public static void main(String[] args) {
        StringJoiner sj1=new StringJoiner("---");
        StringJoiner sj2=new StringJoiner(", ","[","]");

        System.out.println(sj1);//空字符串
        System.out.println(sj2);//[]
        
        //添加数据
        sj1.add("aaa").add("bbb").add("ccc");
        sj2.add("a").add("b").add("c");
        System.out.println(sj1);
        System.out.println(sj2);

        //获取长度
        int len1=sj1.length();
        int len2=sj2.length();
        System.out.println(len1);//15
        System.out.println(len2);//9

        //把StringJoiner变回字符串
        String str1=sj1.toString();
        String str2=sj2.toString();
        System.out.println(str1);
        System.out.println(str2);
    }
}

运行结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值