深入浅出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还可以自定义正则表达式扫描,在扫描复杂数据的时候非常有用。
- Java 深入浅出String
- 深入浅出Java String(中)
- 深入浅出Java String(上)
- 深入浅出Java String(下)
- 深入浅出java String
- 饿猫学java——String深入浅出
- java中字符串String的深入浅出
- String intern深入浅出
- 深入浅出 Java clone 技术
- 深入浅出Java clone技术
- 深入浅出Java clone技术
- 深入浅出java多线程
- 深入浅出Java多线程程序设计
- 深入浅出Java多线程程序设计
- 深入浅出Java多线程程序设计
- 深入浅出Java clone技术
- 深入浅出Java clone技术
- 深入浅出JAVA反射机制
- 华为清理老员工:真的是兔死狗烹?
- 04 iostream和namespace命名空间
- 判断正在播放哪一个动画
- 0216
- Coconuts HDU - 5925 (离散化+dfs求联通块)
- 深入浅出java String
- python 爬虫中时间和时间戳的互相转换
- 可以ping通,但是ssh无法连接linux(主机连接虚拟机里面的linux)
- jQuery禁止|禁用|屏蔽鼠标右键
- C++对象内存布局-单一继承
- Kafka跨集群同步工具——MirrorMaker
- Java SE 基础知识
- 进制转换
- 【项目管理】如何定义开放性问题和如何在项目中跟踪开放性问题