java字符串:String类,StringBuffer类,StringTokenizer类:常见用法合集

目录

字符char型

字符类型的包装类型Character

字符串String

内存模型

字符串常量池的一些理解

字符串的拼接操作

举个栗子

字符串的构造方法

字符串转换为字符数组

字符串的拼接

填充字符串

移除空白字符

多行文本块——TextBlock

空串与空白串的判断

hashCode方法

字符串比较-1:compareTo

字串的比较-2:regionMatches

字串查找-1:indexOf

字串查找-2:

提取子串

语法

参数

可修改字符串(StringBuffer)

StringBuffer对象的创建

向StringBuffer中追加数据

操作缓冲区中的字符

StringBuffer的容量与长度

StringTokenizer类:定界符分割


​​​​​​​

字符char型

char变量保存的数据占两个字节,被解释为特定字符的Unicode值,支持多国语言字符。

字符类型的包装类型Character

char对应着一个Character包装类型,封装了与字符操作相关的一些功能。

下面这个例子中包括了大部分常用的方法

import java.util.Scanner;

public class UseCharacter {

   public static void main(String[] args)
   {
      //characterBasic();
      testGetCharInfo();
   }


   static void testGetCharInfo() {
      var scanner = new Scanner(System.in);
      System.out.print("输入一个字符,之后敲回车键:");
      var userInput = scanner.nextLine();
      //移除前后的空格
      userInput = userInput.strip();
      //取出第一个字符
      String result = getCharInfo(userInput.charAt(0));
      System.out.println(result);
   }

   static String getCharInfo(char c) {
      String info =
              "是否在Unicode标准字符集中: " + Character.isDefined(c) +
                      "\n是否数字: " + Character.isDigit(c) +
                      "\n是否可作为Java标识符的第一个字符:" +
                      Character.isJavaIdentifierStart(c) +
                      "\n是否可作为Java标识符的其他字符: " +
                      Character.isJavaIdentifierPart(c) +
                      "\n是不是字母: " + Character.isLetter(c) +
                      "\n是字母或者数字: " +
                      Character.isLetterOrDigit(c) +
                      "\n是小写字母: " + Character.isLowerCase(c) +
                      "\n是大写字母: " + Character.isUpperCase(c) +
                      "\n转为大写: " + Character.toUpperCase(c) +
                      "\n转为小写: " + Character.toLowerCase(c);
      return info;
   }
}

字符串String

事实上,String类内部是使用字节数据byte[](而不是char[])来保存字符信息的,这么做的原因主要是为了尽可能少地占用内存。

内存模型

按照官方的说法:Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配。

JVM主要管理两种类型内存:堆和非堆,堆内存(Heap Memory)是在 Java 虚拟机启动时创建,非堆内存(Non-heap Memory)是在JVM堆之外的内存。

简单来说,非堆包含方法区、JVM内部处理或优化所需的内存(即时编译后的代码缓存)、每个类结构(如运行时常数池、字段和方法数据)以及方法和构造方法的代码。

Java的堆是一个运行时数据区,类的(对象从中分配空间。这些对象通过new、newarray、 anewarray和multianewarray等指令建立,它们不需要程序代码来显式的释放。堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。   

栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。栈中主要存放一些基本类型的变量数据(int, short, long, byte, float, double, boolean, char)和对象句柄(引用)。

虚拟机必须为每个被装载的类型维护一个常量池。常量池就是该类型所用到常量的一个有序集合,包括直接常量(string,integer和 floating point常量)和对其他类型,字段和方法的符号引用。   

对于String常量,它的值是在常量池中的。而JVM中的常量池在内存当中是以表的形式存在的, 对于String类型,有一张固定长度的CONSTANT_String_info表用来存储文字字符串值,注意:该表只存储文字字符串值,不存储符号引用。说到这里,对常量池中的字符串值的存储位置应该有一个比较明了的理解了。在程序执行的时候,常量池会储存在Method Area,而不是堆中。常量池中保存着很多String对象; 并且可以被共享使用,因此它提高了效率。

当然,如果直接使用new关键字创建字符串对象时,虽然值一致(都是“Hello”),但仍然是两个独立的对象。

    static void stringPool() {
        String s0 = "Hello";
        String s1 = "Hello";
        String s2 = "He" + "llo";
        System.out.println(s0 == s1);//true
        System.out.println(s0 == s2);//true

        String s3 = new String("Hello");
        String s4 = new String("Hello");
        System.out.println(s3 == s4);  // false
    }

再来一个栗子

    static void stringAssign() {
        String s1 = "a";
        String s2 = s1;
        System.out.println(s1 == s2); //true
        s1 += "b";
        System.out.println(s1 == s2); //false
        System.out.println(s1 == "ab"); //false
        System.out.println(s1.equals("ab")); //true
    }

“==”运算符用于比较两个字符串变量是否引用同一个字符串对象。

示例中用s1给字符串变量s2赋值意味着:两个变量(s1,s2)现在引用同一个字符串对象“a”,所以,s1 == s2 返回 true。

String对象的内容是只读的,示例中使用“+=”修改s1变量的值,实际上是得到了一个新的字符串对象,其内容为“ab”,而s2引用的还是原来的“a”,所以,这时,s1 == s2 返回 false。

另外,代码中的“ab”字符串是一个常量,它所代表的字符串对象与使用“+=”生成的并由s1所引用的“ab”对象无关,是两个独立的对象。

String.equals()方法可以比较两个字符串的内容(区分大小写),类似地,有一个equalsIgnoreCase()方法,比较时不理会大小写。

字符串常量池的一些理解

为什么要有字符串常量池?
字符串的分配,和其他的对象分配一样,耗费高昂的时间与空间代价,大量频繁的创建字符串,极大程度地影响程序的性能
JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化,所以为字符串开辟一个字符串常量池,类似于缓存区
创建字符串常量时,首先检查字符串常量池是否存在该字符串
字符串常量池中是不会存储相同内容的字符串的
常量池就类似一个JAVA系统级别提供的缓存

字符串的拼接操作

因为String的不可变性,那么字符串的拼接操作之后的字符串是在字符串常量池呢,还是是个对象存储在堆上呢?先给出结论

常量与常量的拼接结果在常量池,原理是编译期优化
只要其中有一个是变量,结果就在堆中。变量拼接的原理是StringBuilder
如果拼接的结果调用intern()方法,则主动将常量池中还没有的字符串对象放入池中,并返回此对象地址

举个栗子

下面操作一共创建了几个对象

String str1 = new String("1");    //2个(不包括引用类型)
String str2 = new String("1") + new String("2");    //6个(不包括引用类型)

  1. String str1 = new String("1");
    这一行代码创建了两个字符串对象:

    • 一个是 "1" 的字面量,在字符串池中(常量池)存在。
    • 另一个是通过 new String() 创建的 String 对象,这个对象在堆内存中。

    因此,总共有 2个字符串对象(不包括引用类型)。

  2. String str2 = new String("1") + new String("2");
    这一行代码的情况更复杂,涉及多个字符串对象的创建:

    • new String("1") 生成一个在堆内存中的字符串对象。
    • new String("2") 生成另一个在堆内存中的字符串对象。
    • new String("1") + new String("2") 的结果是一个新的字符串对象,这个对象也是在堆内存中生成的。

    因此,这里我们有以下几个对象:

    • "1" 字面量(常量池)— 1个
    • new String("1")(堆内存)— 1个
    • "2" 字面量(常量池)— 1个
    • new String("2")(堆内存)— 1个
    • new String("1") + new String("2") 的结果(堆内存)— 1个

    总共为 6个对象(不包括引用类型)。这里的计数包括常量池和堆中的字符串对象。

字符串的构造方法

//展示字符串的构造方法
    static void stringConstructors() {
        char charArray[] = {'b', 'i', 'r', 't', 'h', ' ',
                'd', 'a', 'y'};
        byte byteArray[] = {(byte) 'n', (byte) 'e', (byte) 'w',
                (byte) ' ', (byte) 'y', (byte) 'e',
                (byte) 'a', (byte) 'r'};

        String s, s1, s2, s3, s4, s5, s6, s7, s8, s9, output;

        s = new String("hello");
        //分配缓冲区
        var buffer = new StringBuffer("Welcome to Java Programming!");

        // 字符串对象的多种构造方法
        s1 = new String();    //缺省构造方法,可被简化为直接赋值空串
        s2 = new String(s);    //拷贝构造方法
        s3 = new String(charArray);    //以字符数组初始化字串
        //从字符数组第6个元素起取3个字符
        s4 = new String(charArray, 6, 3);
        s5 = new String(byteArray); //以字节数组初始化字串
        //从字节数组第4个元素起取4个字节
        s6 = new String(byteArray, 4, 4);
        //从StringBuffer对象中提取字符数据创建字串(注意,是复制)
        s7 = new String(buffer);
        //使用valueOf()系列方法,可以很方便地将各种类型的数据直接转换为字符串
        //将字符数组转换为字符串
        s8 = String.valueOf(charArray);
        //将double数值转换为字符串
        s9 = String.valueOf(100.0d);


        output = "s1 = " + s1 +
                "\ns2 = " + s2 +
                "\ns3 = " + s3 +
                "\ns4 = " + s4 +
                "\ns5 = " + s5 +
                "\ns6 = " + s6 +
                "\ns7 = " + s7 +
                "\ns8 = " + s8 +
                "\ns9 = " + s9;
        System.out.println(output);
    }

字符串转换为字符数组

    //从字符串中提取字符数组
    static void stringAndArray() {
        String str = "你好,中国!";
        //将整个字符串中的所有字符都提取出来
        char[] charArray = str.toCharArray();
        //输出字符数组的内容
        printStringArray(charArray);
        char[] chars = new char[2];
        //从字符串中提取字符,构建字符数组
        //四个参数的含义
        //1.被拷贝字符在字串中的起始位置
        //2.被拷贝的最后一个字符在字串中的下标再加1
        //3.目标字符数组
        //4.拷贝的字符放在字符数组中的起始下标
        str.getChars(3, 5, chars, 0);
        printStringArray(chars);
    }

    //打印字符数组的内容
    static void printStringArray(char[] chars) {
        String output = "";
        for (int i = 0; i < chars.length; i++)
            output += "[" + i + "] " + chars[i] + "\n";
        System.out.println(output);
    }

字符串的拼接

    //重复拼接子串,生成新字符串
    static void stringRepeat() {
        String a5 = "a".repeat(5);
        //生成:aaaaa
        System.out.println(a5);
    }

    //字符串的拼接
    static void stringConcat() {
        String s1 = "Happy ", s2 = "Birthday";
        //使用concat方法将两个字符串连接为一个新的字符串
        System.out.println(s1.concat(s2)); //Happy Birthday
        //也可以使用“+”达到相同的目的
        System.out.println(s1 + s2); //Happy Birthday
        //"+"运算符可以将字符串与任意一个对象相拼接
        System.out.println("99+1=" + (99 + 1)); //99+1=100
        //当前时间:2020-10-01
        System.out.println("当前时间:" + LocalDate.now());
    }

拼接之后,得到的是一个新的字符串,并不是修改原有的字符串实现的。

填充字符串

使用String.format方法,可以定义一个字符串模板,在运行时,将特定变量的值“填入”到模板中,动态地构建出一个字符串。

    //字符串的“填充”
    static void stringFill() {

        var item = "Shirt";
        var size = "M";
        var price = 14.99;
        var color = "Red";

        var template = "Clothing item: %s, size %s, color %s, $%.2f";
        var itemString = String.format(template,
                item, size, color, price);
        //Clothing item: Shirt, size M, color Red, $14.99
        System.out.println(itemString);
    }

String.format()方法的第一个参数是字符串模板,其中%打头的是可以被“填入”值的“空”,%s表示这里需要一个字符串,%.2f表示这里需要一个保留两位小数的float数值。

移除空白字符

    //移除首尾的空白字符(JDK11)
    static void stringStrip() {
        String example = "  前后都有空白字符的字符串  ";
        System.out.println("|" + example.strip() + "|");
        System.out.println("|" + example.stripLeading() + "|");
        System.out.println("|" + example.stripTrailing() + "|");
    }

在实际开发中,遇到需要用户从键盘输入的场景时,建议调用strip()方法移除字符前后的空白字符。

多行文本块——TextBlock

多行文本块特别适合于输出大段文字。

空串与空白串的判断

    //判断字符串是否为空
    static void stringIsBlankOrEmpty() {
        var str = ""; //空串,其长度为0
        //isBlank: JDK11引入,用于检测字符串是否仅由
        // white space(即空白字符)构成
        System.out.println(str.isBlank());//true
        //isEmpty(),JDK 6引入,仅用于检测空串
        System.out.println(str.isEmpty());//true
        str = "   "; //全部由空格组成的字符串
        System.out.println(str.isBlank());//true
        System.out.println(str.isEmpty());//false
        //Tab,也算作空白字符
        str = "   ";
        System.out.println(str.isBlank());//true
        System.out.println(str.isEmpty());//false
        str = "\n"; //用于换行的格式控制字符,也算是空白符
        System.out.println(str.isBlank());//true
        System.out.println(str.isEmpty());//false
    }

hashCode方法

只要两对象的“内容”相等,其hashCode值也应该相等

    static void stringHashCode() {
        String s1 = "hello",
                s2 = "Hello",
                s3 = new String(s2);
        //hello的hashCode=99162322
        System.out.println(s1 + "的hashCode=" + s1.hashCode());

        //s2与s3引用不同的字符串对象
        System.out.println(s2 == s3);  //false
        //s2与s3所引用字符串对象的值相等
        System.out.println(s2.equals(s3)); //true

        //Hello的hashCode=69609650
        System.out.println(s2 + "的hashCode=" + s2.hashCode());
        //Hello的hashCode=69609650
        System.out.println(s3 + "的hashCode=" + s3.hashCode());
    }

字符串比较-1:compareTo

compareTo:使用字典法进行比较,返回0表两字串相等,小于返回负值,大于返回正值。

    static void compareTest(){
        var s1 = new String("hello");
        var s2 = new String("hello1");
        var output = "s1.compareTo(s2) = " + s1.compareTo(s2) +
                "\ns1.compareTo(s2) = " + s2.compareTo(s1);
        System.out.println(output);
    }

示例中从字典序来看:s1<s2,所以第一个输出的是负数,第二个是正数。

字串的比较-2:regionMatches

比较两字符串中的某一部分是否相等。

    static void compareTest2(){
        var s1 = new String("hello2");
        var s2 = new String("hello1");
        //比较两字串中的某一部分是否相等
        //regionMatches方法参数的含义如下:
        //1.调用这个方法的字串中的起始下标
        //2.要比较的字串
        //3.要比较的字串的起始下标
        //4.要比较的字串比较的字符长度
        if(s1.regionMatches(0, s2,0, 5)){
            System.out.println("s3 和 s4 的前5个字符匹配\n");
        }
        else{
            System.out.println("s1 和 s1 的前5个字符不匹配");
        }
    }

字串查找-1:indexOf

在字串查找字符或子串,调用 indexOf 和 lastIndexOf方法即可。

indexOf是从参数的位置开始正向搜索

lastIndexOf是从参数的位置开始反向搜索

    static void stringIndex() {
        String letters = "abcdefghijklmabcdefghijklm";
        System.out.println(letters.indexOf('c')); //2
        //第2个参数是开始查找的起始下标
        System.out.println(letters.indexOf('c', 1));//2
        System.out.println(letters.indexOf('$'));//-1
        System.out.println(letters.lastIndexOf("lmabc"));//15
        System.out.println(letters.lastIndexOf('m', 5));//-1
    }

字串查找-2:

查询字串是否以某字串开头和结尾:startsWith 和 endWith方法。 

    static void stringStartEnd() {
        String[] strings = {"started", "starting",
                "ended", "ending"};
        for (String string : strings) {
            if (string.startsWith("st"))
                System.out.println(string + "以st开头");
        }
        for (String string : strings) {
            if (string.endsWith("ed"))
                System.out.println(string + "以ed结尾");
        }
    }

提取子串

substring() 方法返回字符串的子字符串。

语法

public String substring(int beginIndex)
这种情况是从起始索引到最后

或

public String substring(int beginIndex, int endIndex)

参数

  • beginIndex -- 起始索引(包括), 索引从 0 开始。

  • endIndex -- 结束索引(不包括)。

    //提取子串
    static void stringSlice() {
        String letters = "abcdefghijklmabcdefghijklm";
        //输出:hijklm
        System.out.println(letters.substring(20));
        //输出:bcdef
        //(起始下标,要拷贝子串的最后下标加1但不包括该下标)
        System.out.println(letters.substring(1, 6) );
    }

可修改字符串(StringBuffer)

前面我们己经知道,String对象创建后,它的内容是不可改的,如果确实需要一个可以修改内容的字符串,可以使用StringBuffer。

StringBuffer对象其内容可以修改,其占用的空间能自动增长

可以调用StringBuffer.append()方法不断地向StringBuffer对象“追加”新的内容,追加完毕之后,再调用它的toString()方法得到最终的字符串。在整个字符串构建过程中,都是在同一块内存区域中完成的。

在使用 StringBuffer 类时,每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,所以如果需要对字符串进行修改推荐使用 StringBuffer。

StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。

由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。

StringBuffer对象的创建

    static void stringBufferConstructors() {
        //常用的三种StringBuffer构造方法
        var buf1 = new StringBuffer();
        var buf2 = new StringBuffer(10);
        var buf3 = new StringBuffer("hello");

        //使用toString()方法将字符串缓冲区中的字符提取出来
        System.out.println(buf1.toString());
        System.out.println(buf2.toString());
        System.out.println(buf3.toString());

    }

向StringBuffer中追加数据

static void stringBufferAppend() {
        var buf = new StringBuffer();
        //append()方法有多种重载形式,可接收多种类型的数据
        buf.append("hello\n");
        char[] charArray = {'a', 'b', 'c', 'd', 'e', 'f'};
        buf.append(charArray);
        buf.append("\n");
        buf.append(charArray, 0, 3);
        //append()方法支持级联调用
        buf.append("\n").append(true).append("\n")
                .append('Z').append("\n").append(100);
        //输出最终结果
        System.out.println(buf.toString());
    }

当你查看append方法的源码时,你会看到它返回值是this,即这个对象本身,所以可以使用级联调用。

操作缓冲区中的字符

setCharAt() , getChars() , reverse()

    static void stringBufferChars() {
        var buf = new StringBuffer("hello there");
        System.out.println("\nCharacter at 0: " + buf.charAt(0));
        buf.setCharAt(0, 'H');
        buf.setCharAt(6, 'T');
        System.out.println(buf.toString());
        //提取字符串缓冲区中的字符到字符数组中
        var charArray = new char[buf.length()];
        //(起始下标,长度,字符数组名,起始下标)
        buf.getChars(0, buf.length(), charArray, 0);
        for (int i = 0; i < charArray.length; ++i)
            System.out.print(charArray[i]+",");
        System.out.println();
        //reverse:倒转字串
        buf.reverse();
        System.out.println(buf.toString());
    }

StringBuffer的容量与长度

• length:当前字符个数
• capacity:不另外分配内存可以存放的字符个数

    static void stringBufferCapLen() {

        var buf = new StringBuffer("Hello,");

        //length = 6
        System.out.println( "length = " + buf.length());
        //capacity = 22
        System.out.println("capacity = " + buf.capacity());
        //追加一个新的字符串
        buf.append("how are you?");
        //输出:Hello,how are you?
        System.out.println(buf.toString());
        //length = 18
        System.out.println( "length = " + buf.length());
        //capacity = 22
        System.out.println("capacity = " + buf.capacity());

        //ensureCapacity:保证StringBuffer的最小容量
        buf.ensureCapacity(75);
        //New capacity = 75
        System.out.println("New capacity = " + buf.capacity());

        //setLength:增大或减小StringBuffer的长度
        buf.setLength(10);
        System.out.println("New length = " + buf.length());
        //原有的内容被截断,输出:Hello,how
        System.out.println(buf.toString());
    }

StringTokenizer类:定界符分割

StringTokenizer 是Java的一个实用工具类,属于 java.util 包,主要用于将字符串分割成一系列的标记(tokens)。它可以使用默认的分隔符(如空格、制表符、换行符等)或者指定的分隔符来分割字符串。此外,StringTokenizer还允许选择是否返回分隔符作为标记。

StringTokenizer的构造方法

StringTokenizer类提供了三种构造方法,以适应不同的使用场景:

// 使用默认分隔符(空格、制表符、换行符等)构造StringTokenizer对象
StringTokenizer st1 = new StringTokenizer("Hello Runoob How are you");

// 使用指定的分隔符构造StringTokenizer对象
StringTokenizer st2 = new StringTokenizer("JAVA : Code : String", ":");

// 使用指定的分隔符构造StringTokenizer对象,并决定是否返回分隔符
StringTokenizer st3 = new StringTokenizer("JAVA : Code : String", ":", true);

StringTokenizer的常用方法

StringTokenizer类提供了一系列方法来操作分割后的字符串,包括:

// 计算剩余标记的数量
int count = st.countTokens();

// 检查是否还有更多的标记
boolean hasMore = st.hasMoreTokens();

// 获取下一个标记
String token = st.nextToken();

// 使用指定的分隔符获取下一个标记
String tokenWithDelim = st.nextToken(":");

实际应用示例

以下是使用StringTokenizer类的两个示例,展示了如何使用不同的构造方法和操作方法来处理字符串:

// 示例1: 使用逗号作为分隔符
String str = "runoob,google,taobao,facebook,zhihu";
StringTokenizer st = new StringTokenizer(str, ",");
while(st.hasMoreTokens()) {
System.out.println(st.nextToken());
}

// 示例2: 使用空格和冒号作为分隔符,并返回分隔符
StringTokenizer st = new StringTokenizer("JAVA : Code : String", " :", true);
while(st.hasMoreTokens()) {
System.out.println(st.nextToken());
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值