Java工具类之字符串类(超详细)

1、 字符串类

字符串是我们在编程中最常使用的一种数据类型,Java中用类来描述字符串,其中最常用的字符串处理类是String,此外还有StringBuffer和StringBuilder。在本节,我们会了解每种字符串处理类的特点,以便能在应用时选择合适的字符串类型。字符串不属于8种基本数据类型,而是一种引用类型。String对象代表一组不可改变的Unicode字符序列,String类对象的内容一旦被初始化就不能再改变,对它的任何修改实际上又产生一个新的字符串。String类是final类型的类,不能被继承,所有的方法不允许被覆盖。StringBuffer对象代表一组可改变的Unicode字符序列,StringBuilder是JDK5.0版本后引入的字符串处理类,其中的方法与StringBuffer类的相同。他们都属于java.lang包,在使用的时候不需要用import语句导入。

1. String对象的创建

String是比较特殊的数据类型,它不属于基本数据类型,但是可以和使用基本数据类型一样直接赋值,也可以像引用数据类型一样,使用关键字new进行实例化。

String类型对象的实例化有两种方式: 

–静态方式(常用):直接给变量赋值,

如:String s1 = "abc";  String s2 = "abc"; 

–动态方式:使用new运算符动态的给变量赋值,

如:String s3 = new String("abc"); String s4 = new String("abc");

这两种方式创建的字符串对象是有区别的。

区别在于:使用静态方式创建的字符串,如果前后两次创建的字符串内容相同,则在堆区的常量池中只会产生一个字符串对象,即两个引用指向同一块地址。而使用动态方式创建的字符串,不管前后两次创建的字符串内容是否相同,每创建一次,都会在堆内存中会产生出不同的对象,即两个引用指向不同的地址。

动态创建String对象时需要用到构造方法,String类的常用的构造方法如下,更多构造方法请直接查阅Java API。

  • 初始化一个新创建的String 对象,它表示一个空字符序列。

String 变量名 = new String() ;

初始化一个新创建的String对象,表示一个空字符串(" ");注意空字符串与null的区别,空字符串表示String对象的内容为空,而null表示String类的变量不指向任何的String对象。

  • 初始化一个新创建的 String对象,表示一个与该参数相同的字符序列。

String 变量名 = new String (String value) ;

  • String(char chars[]) 使用一个字符数组创建一个String对象。

注意,当使用"+"运算符进行运算时,如果参与运算的有字符串,则"+"的含义为进行字符串连接,当参与运算的没有字符串时,则"+"的含义为算术运算符加号。例如:

String str1 = "hello ";
String str2 = "world"; 
System.out.println (str1 + str2);  //输出结果为"hello world"
System.out.println(5 + 6 + 'A');  //输出结果为76
System.out.println(5 + 6 + "A");  //输出结果为11A
System.out.println(5 + "A" +6);  //输出结果为5A6

2. String 对象的不可变性

任何一个String对象在创建之后都不能对它的内容作出任何改变。 String的不可变性具体讲就是每个String的实例会包含一个名为 private final char [] value的字符数组实例,一旦一个String类被实例后,这个value是不会被改变的,即具有不可变性(immutability)。String还包含一个hash字段,它的值取决于value的内容,value不变hash也不会改变的,所以String是一个不可变类。

String对于连接、获得子串和改变大小写等操作,如果返回值同原字符串不同,实际上是产生了一个新的String对象,在程序的任何地方,相同的字符串字面常量都是同一个对象,下面的代码会改变字符串s的内容吗?

String s = "Java";
s = "HTML";

答案是不会。第一条语句创建了一个内容为"Java"的String对象,并将其引用赋值给s。第二条语句创建了一个内容为"HTML"的新String对象,并将其引用赋值给s。赋值后第一个String对象仍然存在,但是不能再访问它,因为变量s现在指向了新的对象HTML,如图所示。

上图字符串是不可改变的,一旦创建,它们的内容不能修改

因为字符串在程序设计中是不可改变的,但同时又会被频繁地使用,所以Java虚拟机为了提高效率并节省内存,对具有相同字符串序列的字符串直接使用同一个实例。例如下面的语句:

 Field field = String.class.getDeclaredField("value");
 field.setAccessible(true);
 
 String str0 = "aa";
 String str1 = "aa";
 String str2 = new String("aa");
 System.out.println(str0 == str1);  //  true
 System.out.println(str0 == str2);  // false
 
 char[] value0 = (char[]) field.get(str0);
 char[] value2 = (char[]) field.get(str2);
 System.out.println(value0 == value2); // true

程序运行结果:

true

false

true

说明str0和str2是两个独立的对象,但是value字段是公用的。这段代码在内存图解如下:

3. 字符串的比较

String类提供了多种对字符串比较的方法,具体如表1-6所示。

表1-6 字符串比较方法

方法

含义

boolean equals(String)

判断两个字符串对象的内容是否相等

boolean equalsIgnoreCase(String)

比较两个字符串的内容是否相等,忽略大小

int compareTo(s1:String)

返回一个大于0、等于0或者小于0的整数以表明这个字符串是大于、等于还是小于s1

int compareToIgnoreCase(String)

除了不区分大小写外,其他都和compareTo是一样的

boolean regionMatches(int, String, int, int)

如果这个字符串指定的子域精确匹配字符串s1中指定的子域则返回true

boolean startsWith(String prefix)

测试此字符串是否以指定的前缀开始

boolean endsWith(String suffix)

测试此字符串是否以指定的后缀结束

例如,下面的语句先显示true,然后显示false.

String s1 = new String("welcome to Java");
String s2 = "welcome to Java";
String s3 = "welcome to C++";
System.out.println(s1.equals(s2)); //true
System.out.println(s1.equals(s3));//false

【例】字符串equals方法练习

String s1 = "abc";
String s2 = new String("abc");
String s3 = new String("abc");
String s4 = "abc";
System.out.println(s1.equals(s2));   //true
System.out.println(s1.equals(s4));   // true
System.out.println(s2.equals(s3));   //true

程序分析:equals方法用来比较两个字符串是否相等。

【例】重写equals()方法,判断不同的点,是否是坐标系上的同一点。

    //点类
    public class Point {
        private int x; //x坐标
        private int y;//y坐标
        public Point(int x,int y){
            this.x = x;
            this.y = y;
        }
        public int getX() {
            return x;
        }
        public void setX(int x) {
            this.x = x;
        }
        public int getY() {
            return y;
        }
        public void setY(int y) {
            this.y = y;
        }
        @Override
        public String toString() {
            return "("+x+","+y+")";
        }    
        //如果x,y的值相同,就认为是坐标上的同一点
        public boolean equals(Object obj) {
            if(obj == null){
                return false;
            }
            if(this == obj){
                return true;
            }
            if(!(obj instanceof Point)){
                return false;
            } else {
                Point p = (Point)obj;
                if(p.x==x&&p.y==y){
                    return true;
                } else {
                    return false;
                }
            }
        }
    }
//测试类
public class TestPoint {
    public static void main(String[] args) {
        Point p1 = new Point(55,66);
        Point p2 = new Point(55,66);
        Point p3 = new Point(22,33);
        System.out.println(p1.equals(p2));
        System.out.println(p1==p2);
        System.out.println(p1.equals(p3));
        System.out.println(p1==p3);
    }
}

程序运行结果:

true

false

false

false

程序分析:上例测试中main()方法中实例化了3个点,分别使用equals方法与==进行比较,因为Point类覆盖了父类的equals()方法,该方法判断如果两个Point对象的x坐标和y坐标相等,则返回true,所以第1行输出为true,"=="运算符比较引用数据类型时,判断两个引用是否指向同一个对象,p1,p2,p3都分别是不同的对象,所以第2行和第4行输出为false。

compareTo方法也可以用来对字符串进行比较。方法如下:

s1.compareTo(s2)

如果s1与s2相等,那么该方法返回值为0;如果按字典序(即以统一码的顺序)s1小于s2,那么方法返回值小于0;如果按字典序s1大于s2,方法返回值大于0。

方法compareTo返回的实际值是依据s1和s2从左到右数第一个不同字符之间的距离得出的,例如,假设s1为“abc”,s2为“abg”,那么s1.compareTo(s2)返回-4。首先比较的是s1与s2中第一个位置的字符(a与a)。因为它们相等,所以比较第二个位置的两个字符(b与b)。因为它们也相等,所以比较第三个位置的两个字符(c与g)。由于字符c比字符g小4,所以比较之后返回-4。如果使用像>、>=、<或<=这样的比较运算符比较两个字符串,就会发生错误,替代的方法就是使用s1.compareTo(s2)来进行比较。

如果两个字符串相等,equals方法返回true;如果不相等,方法返回false。compareTo方法会根据一个字符串是否等于、大于或小于另一个字符串,分别返回0、正整数或负整数。

4. 字符串与数组之间的转换

字符串不是数组,但是字符串可以转换成字节数组和字符数组,反之亦然。

(1)与字符数组之间的转换

为了将字符串转换成一个字符数组,可以使用toCharArray方法。例如,下述语句将字符串"Java "转换成一个字符数组:

Char[] chars= "Java".toCharArray();

因此chars[0]是'J',chars[1]是'a',chars[2]是'v',chars[3]是'a'。

还可以使用方法getChars(int srcBegin,int srcEnd,char[]dst,int dstBegin)将下标从srcBegin到srcEnd-1的子串复制到字符数组dst中下标从dstBegin开始的位置。例如,下面的代码功能是把字符串"CS3720"中下标从2到6-1的子串"3720"复制到字符数组dst中,赋值时下标从4开始的位置:

Char[] dst={'j','a','v','a','1','3','0','1'};

"CS3720".getChars(2,6,dst,4);

这样,dst就变成了{'j','a','v','a','3','7','2','0'}。

为了将一个字符数组转换成字符串,应该使用构造方法String(char[])或者方法valueOf(char[])。例如,下面的语句使用String构造方法把一个字符数组构造成一个字符串:

char[ ] chr = new char[]{'j','a','v','a'};

String str = new String( chr );

下面的语句使用valueOf方法把一个字符数组构造成一个字符串:

char[ ] chr = new char[]{'j','a','v','a'};

String str = String.valueOf( chr );

(2)与字节数组之间的转换

Java中能够把字符串转换为字节数组,有3种形式。

形式1:public byte[] getBytes() //方法定义:以默认编码把字符串转换为字节数组

形式2:public byte[] getBytes(Charset charset) //方法定义:按照指定的字符集把字符串转换为字节数组

形式3:public byte[] getBytes(String charsetName) //方法定义:按照指定的字符集把字符串转换为字节数组

【例】 使用平台的默认字符集,统计一个字符串所占用的字节数。

    public class TestStringByte{ 
        public static void main(String[] args) {
            String str = "Java语言程序设计";
            byte bytes[] = str.getBytes();
            System.out.println(bytes.length);
        } 
    }

程序运行结果:

16

程序分析:通过getBytes()方法把字符串转换成字节数组,字节数组的长度就是字符串所占的字节数。因为一个字符需要1~2个字节,或者更多,所以数节数与字符数不同。

【例】通过字节数组创建字符串对象

    public class TestStringCharset { 
        public static void main(String[] args) {
            byte[] bName = new byte[10]; 
            String name1 = "张三"; 
            try{ 
                bName = name1.getBytes("utf-8"); //这种编码方式一个中文占三个字节
                String name2 = new String(bName,"utf-8"); 
                System.out.println("name2="+name2); 
                for(int i = 0;i< bName.length;i++){ 
                    System.out.print(bName[i]); 
                } 
            }catch (UnsupportedEncodingException e){ 
                e.printStackTrace(); 
            } 
        } 
    }

程序运行结果:

name2=张三

-27-68-96-28-72-119

程序分析:getBytes()是将一个字符串转化为一个字节数组。String的getBytes()方法是得到一个系统默认的编码格式的字节数组。将一个String类型的字符串中包含的字符转换成byte类型并且存入一个byte数组中。在Java中的所有数据底层都是字节,字节数据可以存入到byte数组。存储字符数据时(字符串就是字符数据),会先进行查表,然后将查询的结果写入设备,读取时也是先查表,把查到的内容打印到显示设备上,getBytes()是使用默认的字符集进行转换,getBytes(“utf-8”)是使用UTF-8编码表进行转换。

5. String中的常用方法

String的一些常用方法如表1-7所示。

String类的常用方法

方法

含义

byte[] getBytes(Charset charset)

使用给定的 charset将此String 编码到byte 序列,并将结果存储到新的byte 数组

String[] split(String regex)

根据给定正则表达式的匹配拆分此字符串。

String replace(char oldChar, char newChar)

返回一个新的字符串,它是通过用newChar 替换此字符串中出现的所有oldChar 得到的

String toUpperCase()

将String对象中的所有字符都转换为大写

String toLowerCase()

将String对象中的所有字符都转换为小写

char charAt(int)

返回指定索引处的 char 值

String substring(int begin)

返回一个新字符串,该字符串是从begin开始的字符串的内容

String substring(int begin,int end)

返回一个新字符串,该字符串是从begin开始到end-1结束的字符串的内容

int indexOf/lastIndexOf(char)

返回指定字符在此字符串中第一次/最后一次出现处的索引。

int indexOf/lastIndexOf(char,int)

从指定的索引开始搜索,返回在此字符串中第一次/最后一次出现指定字符处的索引

int indexOf/lastIndexOf(String)

返回第一次出现的指定子字符串在此字符串中的索引

int indexOf/lastIndexOf(String,int)

从指定的索引开始搜索,返回在此字符串中第一次/最后一次出现指定字符串处的索引

String trim()

返回新的字符串,忽略前导空白和尾部空白

int length()

返回此字符串的长度

String concat(String str)

将指定字符串连接到此字符串的结尾

byte[] getBytes()

使用平台的默认字符集将此String 编码为byte 序列,并将结果存储到一个新的byte 数组中

下面对常用的一些方法进行详细说明,为了便于说明,方法中使用的示例字符串为:

str="this is a test!";

(1)求长度

方法定义:public int length() 。

方法描述:获取字符串中的字符的个数。

例如:

str.length()

结果:

15

(2)获取字符串中的字符

方法定义:public char charAt(int index)。

方法描述:获取字符串中的第index个字符,从0开始。

例如:

str.charAt(3)

结果:

s

注意:是第4个字符。

【例】 统计一个字符串中字符e出现的次数。

public class TestString {
    public static void main(String[] args) {
        String s = "abecedkjkacedjkdseddklj";
        int num = 0;
        for(int i=0; i<s.length(); i++){
            char c = s.charAt(i);
            if(c == 'e'){
                num++;
            }
        }
        System.out.println("该字符串中字符e出现"+num+"次");
    }
}

程序分析:程序中num的作用是用来记录字符'e'出现的次数

(3)取子串

有两种形式:

形式1如下:

方法定义:public String substring(int beginIndex,int endIndex)。

方法描述:获取从beginIndex 开始到endIndex-1 结束的子串,包括beginIndex,不包括endIndex。

例如:

str.substring(1,4)

结果:

his

形式2如下:

方法定义:public String substring(int beginIndex)

方法描述:获取从beginIndex开始到结束的子串

例如:

str.substring(5)

结果:

is a test!

(4)定位字符或者字符串

indexOf方法有4种重载形式:

形式1如下:

方法定义:public int indexOf(int ch)

方法描述:定位参数所指定的字符。

例如:

str.indexOf('i')

结果:

2

形式2如下:

方法定义:public int indexOf(int ch,int index)

方法描述:从index开始定位参数所指定的字符。

例如:

str.indexOf('i',4)

结果:

5

形式3如下:

方法定义:public int indexOf(String str)

方法描述:定位参数所指定的字符串。

例如:

str.indexOf("is")

结果:

2

形式4如下:

方法定义:public int indexOf(String str,int index)

方法描述:从index开始定位str所指定的字符串。

例如:

str.indexOf("is",6)

结果:

-1表示没有找到

(5)替换字符和字符串

replace方法有3种重载形式:

形式1如下

方法定义:public String replace(char c1,char c2) 

方法描述:把字符串中的字符c1替换成字符c2 

例如:

str.replace('i','I')

结果:

thIs Is a test!

形式2如下:

方法定义:public String replaceAll(String s1,String s2)

方法描述:把字符串中出现的所有的s1替换成s2

例如:

replaceAll("is","IS")

结果:

thIs IS a test!

形式3如下:

方法定义:public String replaceFirst(String s1,String s2)

方法描述:把字符串中的第一个s1替换成s2

例如:

replaceFirst("is","IS")

结果:

thIS is a test!

(6)比较字符串内容

根据是否需要考虑字符大小写,字符串比较有两种具体的实现方法:

方法1:public boolean equals(Object o)

方法描述:比较字符串内容是否与参数相同,区分大小写。

例如:

str.equals("this")

结果:

false

方法2:public boolean equalsIgnoreCase(Object o)

方法描述:比较是否与参数相同,不区分大写小。

例如:

str.equalsIgnoreCase("this")

结果:

false

(7)大小写转换

转换成大写

方法定义:public String toUpperCase()

方法描述:把字符串中的所有字符都转换成大写。

例如:

str.toUpperCase() 

结果:

THIS IS A TEST!

转换成小写:

方法定义:public String toLowerCase()

方法描述:把字符串中的所有字符都转换成小写。

例如:

str.toLowerCase() 

结果:

this is a test!

(8)前缀和后缀

判断字符串是否以指定的参数开始或者结尾。

判断前缀

方法定义:public boolean startsWith(String prefix)

方法描述:字符串是否以参数指定的子串为前缀。

例如:

str.startsWith("this")  

结果:

true

判断后缀:

方法定义:public boolean endsWith(String suffix)

方法描述:字符串是否以参数指定的子串为后缀。

例如:

str.endsWith("this")

结果:

false

【例】判断一个字符串中子字符串出现的次数

import java.io.DataInputStream;
public class StringTest {
    public static void main(String[] args) {
        String str1 ="ab";//读入一行数据
        String str2 = "abcedabsdabajab";
        String str3 = str2.replace(str1,"");//把str2中的str1用空串替换,并赋给str3
        int count = str2.length() - str3.length();//str2的长度减去str3的长度
        count /= str1.length(); // 为什么需要除以长度数呢?分析一下吧
        System.out.println(str1+"在"+str2+"中出现的次数为:"+count);
    }
}

程序运行结果:

ab在abcedabsdabajab中出现的次数为:4

【例】使用字符串中的常用方法实现Email格式的判断。


    public class EmailCheckException extends Exception {
        public EmailCheckException(String msg) {
            super(msg);
        }
        public class StringExercise {
            public static void checkEmail(String email) throws EmailCheckException {
                int len = email.length();         //email的长度
                int begin = email.indexOf('@');   //从开始检索字符@在email中的位置
                int end = email.lastIndexOf('@'); //从结尾检索字符@在email中的位置
                int dot = email.indexOf('.', begin);//检索.是否在@后存在
                //判断长度是否不超过20
                if (email.length() > 20)
                    throw new EmailCheckException("Email长度不能大于20");
                    //判断是否唯一包含@
                else if (begin != end)
                    throw new EmailCheckException("Email中含有多个@");
                    //判断@是否存在或在开头或在结尾
                else if (begin == -1 || begin == 0 || begin == (len - 1))
                    throw new EmailCheckException("Email中没有@或@位置错误");
                    //判断@后是否有.
                else if (dot == -1)
                    throw new EmailCheckException("@后缺少域分隔符");
                    //判断@是否在末尾
                else if (dot == (len - 1))
                    throw new EmailCheckException("分隔符错误");
            }
            public static void main(String args[]) {
                String email = args[0];
                try {
                    checkEmail(email);
                } catch (EmailCheckException e) {
                    e.printStackTrace();
                }
            }
        }
    }

程序分析:自定义了一个EmailCheckException异常类,当Email格式不符合要求时将抛出一个异常对象。程序中使用字符串的length()方法判断Email长度是否正确。通过indexOf()和lastIndexOf()分别从头和从尾查找@的位置,若从头查找和从尾查找到的@位置相等,说明字符串中只有一个@字符,否则存在多个@字符,则不是合法的Email格式。最后查找字符“.”的位置。若其位置在@字符后,且字符串中含有这个字符则是合法的Email格式。

6. StringBuffer和StringBuilder类

由于String对象的不可变性,所以一个字符串的内容如果经常需要变动,就不应该使用String,因为在变化的过程中实际上是不断创建对象的过程,这时候应该使用StringBuffer或者StringBuilder。

他们之间的集成关系树如下:

StringBuffer和StringBuilder用法基本相同。StringBuilder和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。

【例】StringBuilder常用方法及执行过程示意图如下:

public class RunoobTest{
    public static void main(String args[]){
        // 初始时指定缓冲器的长度
        StringBuilder sb = new StringBuilder(10);
        sb.append("Runoob..");
        System.out.println(sb);  
        sb.append("!");
        System.out.println(sb); 
        sb.insert(8, "Java");
        System.out.println(sb); 
        sb.delete(5,8);
        System.out.println(sb);  
    }
}

最终执行结果如下:

Runoob..
Runoob..!
Runoob..Java!
RunooJava!

  • 21
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值