zoukankan      html  css  js  c++  java
  • 漫谈Java字符串

    String, StringBuilder, StringBuffer

    通过一些例子说明String的运行机制,首先明确确定的字符串值会提前被放入

    1.最简单的常量赋值

        public void test() {
            String test = "test";
        }

    查看JVM字节码

    public void test();
      Code:
       0:    ldc    #2; //String test
       2:    astore_1
       3:    return

    解释:

    0: 从常量池获取常量#2(即"test")并将其压入栈顶

    2: 将栈顶字符串引用弹出并赋给本地变量 1 (即变量test)

    3: 返回

    2.使用new String()构造字符串

        public void test2() {
            String test = new String("test");
        }

    字节码

    public void test2();
      Code:
       0:    new    #3; //class java/lang/String
       3:    dup
       4:    ldc    #2; //String test
       6:    invokespecial    #4; //Method java/lang/String."<init>":(Ljava/lang/String;)V
       9:    astore_1
       10:    return

    解释:

    0: 新建一个String对象(分配内存空间)

    3: 将该内存空间的引用压入栈顶

    4: 将字符串常量压入栈顶

    6: 调用String的构造方法,此构造方法会用到dup压入栈中的内存空间引用

    9: 将字符串常量引用弹栈并赋给变量1

    10: 返回

    3. 简单赋值并new一个值一样的String

        public void test3() {
            String test = "test";
            String test2 = new String("test");
        }

    字节码

    public void test3();
      Code:
       0:    ldc    #2; //String test
       2:    astore_1
       3:    new    #3; //class java/lang/String
       6:    dup
       7:    ldc    #2; //String test
       9:    invokespecial    #4; //Method java/lang/String."<init>":(Ljava/lang/String;)V
       12:    astore_2
       13:    return

    解释:

    仅看 3~12, 需要注意test2 = new String("test")与test = "test" 使用的是同一个常量池字符串

    4.简单赋值并new一个值不一样的String

        public void test4() {
            String test = "test";
            String test2 = new String("test2");
        }

    字节码:

    public void test4();
      Code:
       0:    ldc    #2; //String test
       2:    astore_1
       3:    new    #3; //class java/lang/String
       6:    dup
       7:    ldc    #5; //String test2
       9:    invokespecial    #4; //Method java/lang/String."<init>":(Ljava/lang/String;)V
       12:    astore_2
       13:    return

    解释:

    和 test3() 唯一不同的地方在于第二个new String("test2")使用了新的字符串常量test2

    5.字符串拼接

        public void test5() {
            String test = "test" + "abc";
            String test2 = "test";
            test2 = test2 + "abc"; // 此处使用 test2 += "abc" 字节码是一样的
        }

    字节码:

    public void test5();
      Code:
       0:    ldc    #9; //String testabc
       2:    astore_1
       3:    ldc    #5; //String test
       5:    astore_2
       6:    new    #10; //class java/lang/StringBuilder
       9:    dup
       10:    invokespecial    #11; //Method java/lang/StringBuilder."<init>":()V
       13:    aload_2
       14:    invokevirtual    #12; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
       17:    ldc    #13; //String abc
       19:    invokevirtual    #12; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
       22:    invokevirtual    #14; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
       25:    astore_2
       26:    return

    解释:

    关键点在于理解

    1) 使用 test = "test" + "abc"; // 编译器会预先优化,将"test"+"abc"拼接成"testabc"并放入常量池

    2) 使用 test2 = test2 + "abc"; // 使用变量来拼接,将会造成"abc"作为常量先放入常量池,然后使用StringBuilder来拼接test2与"abc",最后使用toString()赋值给test2,而且虽然test2的值等于test的值,但是他们使用的却不是同一个常量池字符串.使用 test == test2 将为false

    其他一些要说的:

    String类的任何方法均不会改变源String,例如 Stirng.toUpperCase() String.toLowerCase()等等

    StringBuilder可以用来构造String,在对字符串进行操作时,他并不新建字符串,而是对源字符串进行操作,因此效率会比String高。尤其是在循环里对字符串进行操作时,应该使用StrinBuilder。

    StringBuffer与StringBuilder功能相同,只不过StringBuffer是同步类,也就是线程安全的

    特别注意即使是使用StringBuilder, 在使用重载操作符时,依然会生成临时的StringBuilder对象,例如 StringBuilder.append("Abc" + "efg"); 这里的+号就会生成新的StringBuilder,也就是说+,+=用多了,会产生很多的临时StringBuilder垃圾,最后被垃圾回收机制回收

    正则: Pattern, Matcher

    Java的正则语法比较奇怪,和PECL有点差别,主要在于

    1. 转移符号 \ 都要变成 \\, 例如 \w -> \\w , \d -> \\d, 如果要表示字符的反斜杠不转义, 则要用 \\\\

    2. \t \n \r 等空白字符不需要使用 \\t \\n \\r 的形式

    3. 标记语法与PECL不一样,例如 "(?imsx)\\w+", 这里 (?imsx)分别为:

    i: 忽略大小写

    m: 多行匹配,也就是说 ^$ 只匹配到当前行

    s: 让 . 也可以匹配换行符

    x: 忽略空格符和#开头行的注释

    没有PECL的u参数直接关闭贪婪匹配,只能在正则语句中用?关闭贪婪匹配

    Matcher.appendReplacement()方法

    这个方法可以用来配合正则处理对匹配到的group做一些处理

    package test;
    
    import static java.lang.System.out;
    
    import java.util.regex.*;
    
    public class Test {
        public static void main(String[] args) {
            String s = "BBabc abcef AbC,eOfg";
            Pattern p = Pattern.compile("(?sm)abc");
            Matcher m = p.matcher(s);
            StringBuffer sb = new StringBuffer();
            while (m.find()) {
                out.println(m.group());
                m.appendReplacement(sb, m.group().toUpperCase());
            }
            m.appendTail(sb);
            out.println(sb);
        }
    }

    输出

    abc
    abc
    BBABC ABCef AbC,eOfg

    这段程序每次查找到abc以后,可以将从当前索引到下一次匹配的到文本(下一个group之前的文本)中的group先替换成uppercase,然后将这段文本append到sb中,最后的appendTail()可以将最后一个group之后的文本append到sb中,m.group()获取的是group0.也就是整个表达式匹配到的文本

    编码问题

    char的编码式使用UTF-16来存储的,所以char占用两个字节(包括中文)

    对于String来说,String内部使用的也是char[]数组来存储

    getBytes("编码")

    getBytes("编码")的意思是使用指定编码将String编码为byte[]数组,如果不指定则是使用系统默认编码来编码

    如下

            String s = "我";
            byte[] c1 = s.getBytes();
            byte[] c2 = s.getBytes("UTF-8");
            byte[] c3 = s.getBytes("GBK");
            System.out.println(System.getProperty("file.encoding"));
            System.out.println(c1.length);
            System.out.println(c2.length);
            System.out.println(c3.length);

    输出

    UTF-8

    3
    3
    2

    对于 new String(byte[], "编码") 来说,是使用指定的编码来解码这个byte[]数组, 也就是说byte[]的编码是固定的,我们就要使用与这个byte[]对应的编码来解码,例如

    new String(s.getBytes("GBK"), "GBK") 这样就是正确的

    new String(s.getBytes("UTF-8"), "GBK") 这样肯定是乱码的

  • 相关阅读:
    谷歌地图移动版(Google Mobile Map)试用(附部分Latitude试用)
    WordPress to Micolog转换工具
    Edge 705试用
    报警点(电子狗)模型探究
    低调发布上海和北京地图
    如何制作一份导航电子地图(上)
    读Google2009开发者大会地图开发文档有感
    照片处理工作流(缩放+GPS信息+水印+IPTC+EXIF,软件推荐)
    浅谈导航电子地图的组成和制作流程
    我的2010世博地图1.0版发布
  • 原文地址:https://www.cnblogs.com/zemliu/p/2764165.html
Copyright © 2011-2022 走看看