深入浅出java String

来源:互联网 发布:解决json包含html标签 编辑:程序博客网 时间:2024/06/05 01:18

一、不可变String

String类中的每一个看起来会修改String值的方法,实际上都是创建了一个全新的String对象。

    @Test    public void testString(){        String a = "hello";        String b = a.toUpperCase();        System.out.println(a);        System.out.println(b);    }    // hello    // HELLO

而这正式我们想要的。对于一个方法而言,参数是为该方法提供信息的,而不是想让该方法改变自己的。这种保障,使代码易于编写和阅读

注意:插播一条重要考题

@Testpublic void test30(){    String a = "java";    change(a);    System.out.println(a);}private void change(String a) {    a = "bbb";}

答案:a的值为“java”,保持不变。

二、重载“+”与StringBuilder

不可变性带来一定的效率问题
比如:

    String aa = "aa";    String result = "bb"+"aa"+"cc";

由于String的不可变性,上述代码会怎么工作呢?可能是这样的:首先生成一个新的String保存“bb”和“aa”连接后的字符串,然后再生成一个新的String保存连接“cc”后的字符串。
但实际上,java工程师早已考虑到这个问题了,他们怎么解决的呢?

编译器的优化行为
为了解决这个低效问题,编译器创建了一个StringBuilder对象,并调用append方法,最后toString生成结果。

不能完全依赖编译器
虽然编译器有这样的优化,但是我们不能完全依赖编译器。
例如:

    // 循环拼接字符串时,编译器会在每次循环都创建一个新的StringBuilder    public void test1(String[] fields){        String result = "";        for(int i=0;i<100;i++){            result += fields[i];        }        System.out.println(result);    }
    // 手动创建StringBuilder,只需要一个就可以    public void test2(String[] fields){        StringBuilder result = new StringBuilder();        for(int i=0;i<100;i++){            result.append(fields[i]);        }        System.out.println(result);    }

三、String常见操作

获取目标位置的字符

    /**     * 常用String工具方法     * length     * charAt 目标索引上的字符     */    @Test    public void test5(){        String str = "hello jack";        // 求长度        int length = str.length();        System.out.println(length);        // 取得索引上的char        char c = str.charAt(5);        System.out.println(c);    }

获取字符、字节数组

    /**     * getChars() 将自定义字符串部分,转向一个字符数组容器     * getBytes() 获取字节数组     * toCharArray() 获取字符数组     *     */    @Test    public void test6() throws UnsupportedEncodingException {        String str = "hello jack";        char[] chars = new char[10];        // 把str的[1,3)字符插入到chars数组中的[2,...)位置。        str.getChars(1,3,chars,2);         System.out.println(chars);        // res:  el 注意el前面有两个空格        char[] chars2 = str.toCharArray();        System.out.println(chars2);        // res:hello jack        byte[] bytes = str.getBytes();        System.out.println(Arrays.toString(bytes));        // res:[104, 101, 108, 108, 111, 32, 106, 97, 99, 107]        byte[] bytes1 = str.getBytes("iso8859-1");        System.out.println(Arrays.toString(bytes1));        // res:[104, 101, 108, 108, 111, 32, 106, 97, 99, 107]    }
判断相等
注意
contentEquals 只要是CharSequence接口的子类就可以比较,不会因为类型不同而返回false。CharSequence 的子类有StringBuffer,StringBuilder,String等等,这里挑一个最不熟悉的CharArray试试
    /**     * equals     * equalsIgnoreCase     * contentEquals      */    @Test    public void test7(){        String str = "hello world";        char[] ch = new char[]{'h','e','l','l','o',' ','w','o','r','l','d'};        CharArray charArray = new CharArray(ch, 0, ch.length, false);        boolean res1 = str.equals(charArray);        boolean res2 = str.contentEquals(charArray);        System.out.println(res1 );        // res:false 类型不同导致        System.out.println(res2 );        // res:true  类型不同也没有关系    }

比较大小

    /**     * compareTo 按字典顺序,大小写不等价     */    @Test    public void test8(){        String str1 = "aabbc";        String str2 = "aabbC";        int i = str1.compareTo(str2);        System.out.println(i);        // res:32        System.out.println(Arrays.toString("c".getBytes()));        // res:[99]        System.out.println(Arrays.toString("C".getBytes()));        // res:[67]    }

包含

    /**     * contains     */    @Test    public void test9(){        String str1 = "hello world";        String ch = "lo";        boolean contains = str1.contains(ch);        System.out.println(contains);        // res:true    }

局部比较

    /**     * regionMatches     * 字符串区域比较 参数1:一号字符串起始位置,参数二:二号字符串,参数三:二号字符串起始位置,参数四:比较深度     */    @Test    public void test11(){        String str1 = "hello";        String str2 = "allo";        boolean b = str1.regionMatches(2,str2,1,3);        System.out.println(b);        // res:true    }

位置判断

    /**     * startWith     * endWith     */    @Test    public void test12(){        String str1 = "hello";        boolean h = str1.startsWith("h");        boolean e = str1.startsWith("e");        boolean o = str1.endsWith("o");        System.out.println(h);        // res:true        System.out.println(e);        // res:false        System.out.println(o);        // res:true    }    /**     * indexOf     * lastIndexOf     */    @Test    public void test13(){        String str = "hello";        int l = str.indexOf("l");        int l1 = str.lastIndexOf("l");        System.out.println(l);        // res:2        System.out.println(l1);        // res:3    }

字符串截取拼接

    /**     * substring [a,b)     */    @Test    public void test14(){        String str = "hello";        String substring = str.substring(1, 2);        System.out.println(substring);        //res:e    }    /**     * concat     */    @Test    public void test15(){        String s1 = "hello";        String s2 = " world";        String concat = s1.concat(s2);        System.out.println(concat);        // res:hello world    }    /**     * replace     * toLowerCase     * toUpperCase     */    @Test    public void test16(){        String s1 = "hello o";        String replace = s1.replace("o", "world");        System.out.println(replace);        // res:hellworld world    }    /**     * trim     */    @Test    public void test17(){        String s1 = " aa ";        System.out.println(s1);        // res: aa        System.out.println(s1.trim());        // res:aa    }

转换

    /**     * valueOf     */    @Test    public void test18(){        char[] s = new char[]{'h','e','l','l','o'};        String s2 = String.valueOf(s);        System.out.println(s2);    }

将堆中的字符串对象拷贝到常量池中

    /**     * intern     */    @Test    public void test19(){        String s1 = new String("hello");        String s2 = new String("hello");        System.out.println(s1==s2);//false        String s3 = s1.intern();        String s4 = s2.intern();        System.out.println(s3==s4);//true    }
    /**     * jdk1.7中,首次出现的常量,会添加到方法区中的运行时常量池中     * "java"在执行s2的时候已经出现过,不属于第一次。仅"java"特殊     */    @Test    public void test19a(){        String s1 = new StringBuilder("计算器").append("软件").toString();        String s2 = new StringBuilder("ja").append("va").toString();        System.out.println(s1.intern()==s1);        // res:true        System.out.println(s2.intern()==s2);        // res:false    }    @Test    public void test19b(){        String s1 = new StringBuilder("计算器").append("软件").toString();        String s2 = new StringBuilder("计算器").append("软件").toString();        System.out.println(s1.intern()==s1);        // res:true        System.out.println(s2.intern()==s2);        // res:false    }

四、java的正则表达式

在其他语言中,\\表示“我想要在正则表达式中插入一个普通的反斜杠,没有任何特殊意义”。而在java中,\\的意思是“我要插入一个正则表达式的反斜线,所以其后的字符具有特殊的意义”。
    /**     * 正则表达式,java中要表示一个反斜线,需要四个反斜线     */    @Test    public void test(){        System.out.println("\\");        // java中前两个反斜线转义一个正则反斜线,后两个反斜线转义一个正则反斜线,然后,前面的反斜线转义后面的反斜线,输出一个反斜线        System.out.println("\\".matches("\\\\"));// true    }

例子:可能是以一个加号和减号开头的数字

    /**     * 例子:可能以一个加号和减号开头的数字     * 括号可以将表达式分组     * ?表示可能的意思,可能有可能没有     * |是或者的意思     * (-|+)?的意思就是可能有-号或者+号,但是+是特殊字符,需要转义,所以(-|\\+)?     * \\d+表示一位或者多为数字     */    @Test    public void test21(){        System.out.println("15".matches("(-|\\+)?\\d+"));        System.out.println("-15".matches("(-|\\+)?\\d+"));        System.out.println("+15".matches("(-|\\+)?\\d+"));    }

例子:正则分隔split

    /**     * split     * s1表示以空格分隔     * s2表示以非单词字符分隔。+加号表示一个或多个     * s3表示以d和后面的非单词字符分隔     */    @Test    public void test22(){        String str = "hello world,,im kong";        String[] s1 = str.split(" ");        String[] s2 = str.split("\\W+");        String[] s3 = str.split("d\\W+");        for(String s:s1){            System.out.print(s+" ");        }        // res:hello world,,im kong         System.out.println();        for(String s:s2){            System.out.print(s+" ");        }        // res:hello world im kong        System.out.println();        for(String s:s3){            System.out.print(s+" ");        }        // res:hello worl im kong        System.out.println();    }

例子:替换replace

    /**     * replace     *     */    @Test    public void test23(){        String str = "hello world,im wang";        // 匹配第一个world替换成java        String s1 = str.replaceFirst("world", "java");        // 匹配第一个w开头的单词,替换成java        String s2 = str.replaceFirst("w\\w+", "java");        // 匹配所有w开头的单词,替换成java        String s3 = str.replaceAll("w\\w+", "java");        System.out.println(s1);        System.out.println(s2);        System.out.println(s3);        //hello java,im wang        //hello java,im wang        //hello java,im java    }

例:运用字符类的简单匹配。[abc],表示abc中的任意一个,.表示任意,*表示零个或多个。后面还有详细的列表参考,这里简单了解即可。

@Testpublic void test25(){    for(String pattern: new String[]{"hello","[h]ello","hell[aeiou]","he[lL].","he[lL]*","he[lL].*"}){        System.out.println("hello".matches(pattern));    }}truetruetruefalsefalsetrue
@Testpublic void test27(){    String[] args = new String[]{"[^h].*","^h.*"};    for(String arg: args){        System.out.println("hello".matches(arg));    }}false   [^h] 表示不以h开头true    ^h 表示以h开头

Pattern和Matcher的使用
功能强大的正则表达式对象,使用方式可以参考量词中test26,非常简单,主要方法有
matches() 判断是否匹配
find()查找多个匹配
group()返回前一次匹配的的结果
reset() 重新应用一个新的字符串作为matcher中的字符串序列。

难以理解的贪婪型,勉强型,占有型

/**     * 贪婪型(最大匹配):匹配字符串尽可能长,如<.+>的结果“<tr>aava </tr>”     * 勉强型(最小匹配):匹配字符串尽可能短,如<.+?>的结果两个 “<tr>” 和 “</tr>”     * 占有型(完全匹配):一种不回溯的匹配模式,更严格,     *      如<.++>,由于字符串“a<tr>aava </tr>abb”中,从第一个"<"开始,每一个字符都满足“.+”的条件,所以会一直匹配下去,到最后,当到最后一个"b"的时候发现匹配完了,根本就没有字符再去匹配“>”了,所以没有可匹配的结果     *      如<[a-z/]++>,与上面不同的是,当遇到字符串中第一个">"时,[a-z/]+无法匹配上,那么就接着匹配">",结果正中靶心。而这种情况一共两次,所以结果集是“<tr>”和“</tr>”     *     */    @Test    public void test26(){        String[] args = new String[]{"<.+>","<.+?>","<.++>","<[a-z/]++>"};        for(String arg: args){            Pattern pattern = Pattern.compile(arg);            Matcher matcher = pattern.matcher("a<tr>aava </tr>abb");            while(matcher.find()){                System.out.println("匹配"+arg);                System.out.println(matcher.group());            }        }    }

常用字符、字符类、边界符、操作符、量词表
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

关于正则表达式,还有很多内容,这里就简单介绍这么多

五、扫描输入

Scanner

    /**     * Scanner     */    @Test    public void test28(){        Scanner scanner = new Scanner("aa\n15");        System.out.println("你的名字");        String name = scanner.nextLine();        System.out.println(name);        System.out.println("你的年龄");        String age = scanner.nextLine();        System.out.println(age);    }    /**     * Scanner的定界符     */    @Test    public void test29(){        Scanner scanner = new Scanner("aa, 15");        scanner.useDelimiter("\\s*,\\s*");        System.out.println("你的名字");        String name = scanner.next();        System.out.println(name);        System.out.println("你的年龄");        String age = scanner.next();        System.out.println(age);    }

scanner还可以自定义正则表达式扫描,在扫描复杂数据的时候非常有用。

0 0
原创粉丝点击