文章目录
编写高质量代码: 改善Java程序的151个建议
第四章 字符串
建议52:推荐使用String直接量赋值
-
一般对象都是通过new关键字生成,String还有第二种生成方式,即直接声明方式,如String str = “a”;String中极力推荐使用直接声明的方式,不建议使用new String(“a”)的方式赋值。
**原因:直接声明方式:**创建一个字符串对象时,首先检查字符串常量池中是否有字面值相等的字符串,如果有,则不再创建,直接返回池中引用,若没有则创建,然后放到池中,并返回新建对象的引用。
**使用new String()方式:**直接声明一个String对象是不检查字符串常量池的,也不会吧对象放到池中。String的intern方法会检查当前的对象在对象池中是否有字面值相同的引用对象,有则返回池中对象,没有则放置到对象池中,并返回当前对象。
public class Suggest52 { public static void main(String[] args) { String str1 = "Rocky编程日记"; String str2 = "Rocky编程日记"; String str3 = new String("Rocky编程日记"); String str4 = str3.intern(); System.out.println("两个直接量是否相等: " + (str1 == str2)); System.out.println("直接量和对象是否相等: " + (str1 == str3)); System.out.println("经过intern处理后的对象与直接量是否相等: " + (str1 == str4)); } }
建议53:注意方法中传递的参数要求
-
replaceAll方法传递的第一个参数是正则表达式
public class Suggest53 { public static void main(String[] args) { // 是 // 蓝蓝的天, // $是$ System.out.println(StringUtils.remove("好是好","好")); System.out.println(StringUtils.remove("蓝蓝的天,白云飘","白云飘")); System.out.println(StringUtils.remove("$是$","$")); } } class StringUtils { // 删除字符串 public static String remove(String source, String sub) { return source.replaceAll(sub,""); } }
建议54:正确使用String、StringBuffer、StringBuilder
- String使用“+”进行字符串连接,之前连接之后会产生一个新的对象,所以会不断的创建新对象,优化之后与StringBuilder和StringBuffer采用同样的append方法进行连接,但是每一次字符串拼接都会调用一次toString方法,所以会很耗时。StringBuffer与StringBuilder基本相同,只是一个字符数组的在扩容而已,都是可变字符序列,不同点是:StringBuffer是线程安全的,StringBuilder是线程不安全的
建议55:注意字符串的位置
-
在“+”表达式中,String字符串具有最高优先级)(Java对加号“+”的处理机制:在使用加号进行计算的表达式中,只要遇到String字符串,则所有的数据都会转换为String类型进行拼接,如果是原始数据,则直接拼接,如果是对象,则调用toString方法的返回值然后拼接。
public class Suggest55 { public static void main(String[] args) { String str1 = 1 + 2 + " apple"; String str2 = "apples:" + 1 + 2; System.out.println(str1);// 3 apple System.out.println(str2);// apples:12 } }
建议56:自由选择字符串拼接方式
-
字符串拼接有三种方法:
加号、concat方法及StringBuilder(或StringBuffer)的append方法。
字符串拼接性能中,StringBuilder的append方法最快,concat方法次之,加号最慢。
原因:
- “+”方法拼接字符串:虽然编译器对字符串的加号做了优化,使用StringBuidler的append方法进行追加,但是与纯粹使用StringBuilder的append方法不同:一是每次循环都会创建一个StringBuilder对象,二是每次执行完都要调用toString方法将其准换为字符串–toString方法最耗时;
- concat方法拼接字符串:就是一个数组拷贝,但是每次的concat操作都会新创建一个String对象,这就是concat速度慢下来的真正原因;
- append方法拼接字符串:StringBuidler的append方法直接由父类AbstractStringBuilder 实现,整个append方法都在做字符数组处理,没有新建任何对象,所以速度快
public class Suggest56 { public static void main(String[] args) { demo1(); System.gc(); demo2(); System.gc(); demo3(); System.gc(); demo4(); } private static void demo1() { long startTime = System.nanoTime(); StringBuilder sb = new StringBuilder("a"); for (int i = 0; i < 50000; i++) { sb.append("c"); } String str = sb.toString(); long endTime = System.nanoTime(); System.out.println("demo01 cost " + (endTime - startTime) + " ms"); } private static void demo2() { long startTime = System.nanoTime(); String sb = new String("a"); for (int i = 0; i < 50000; i++) { sb += "c"; } long endTime = System.nanoTime(); System.out.println("demo02 cost " + (endTime - startTime) + " ms"); } private static void demo3() { long startTime = System.nanoTime(); String sb = new String("a"); for (int i = 0; i < 50000; i++) { sb.concat("c"); } long endTime = System.nanoTime(); System.out.println("demo03 cost " + (endTime - startTime) + " ms"); } private static void demo4() { long startTime = System.nanoTime(); StringBuffer sb = new StringBuffer("a"); for (int i = 0; i < 50000; i++) { sb.append("c"); } long endTime = System.nanoTime(); System.out.println("demo04 cost " + (endTime - startTime) + " ms"); } }
demo01 cost 3563600 ms demo02 cost 1704332800 ms demo03 cost 3631100 ms demo04 cost 2863500 ms
建议57:推荐在复杂字符串操作中使用正则表达式
-
正则表达式是恶魔,威力强大,但难以控制
public class Suggest57 { public static void main(String[] args) { // demo01(); demo02(); } private static void demo01() { // 接收键盘输入 Scanner input = new Scanner(System.in); while (input.hasNext()) { String str = input.nextLine(); int wordsCount = str.split(" ").length; System.out.println(str + " 单词数: " + wordsCount); } } private static void demo02() { // 接收键盘输入 Scanner input = new Scanner(System.in); while (input.hasNext()) { String str = input.nextLine(); // 正则表达式对象 Pattern pattern = Pattern.compile("\\b\\w+\\b"); // 生成匹配器 Matcher matcher = pattern.matcher(str); // 记录单词数量 int wordsCount = 0; // 遍历查找匹配 while (matcher.find()) { wordsCount++; } System.out.println(str + " 单词数: " + wordsCount); } } }
Today is Monday Today is Monday 单词数: 3 Today is Monday Today is Monday 单词数: 4 Today is Monday?No! Today is Monday?No! 单词数: 3 I`m OK. I`m OK. 单词数: 2 // demo02 Today is Monday Today is Monday 单词数: 3 Today is Monday Today is Monday 单词数: 3 Today is Monay?No! Today is Monay?No! 单词数: 4 I`m Ok. I`m Ok. 单词数: 3
建议58:强烈建议使用UTF编码
-
一个系统使用统一的编码
// 使用该案例先手动调整编码格式,例如GBK,会乱码 public class Suggest58 { public static void main(String[] args) throws UnsupportedEncodingException { String str = "编程日记"; byte[] b = str.getBytes("UTF-8"); System.out.println(new String(b)); } }
Java程序涉及的编码包括两部分:
- Java文件编码 : 记事本创建 .Java 后缀的文件, 文件的编码格式就是操作系统默认的格式 , 如果是 IDE工具创建的, 则依赖于 IDE 的设置。
- Class文件编码 : 通过 javac 命令生成的后缀名为 .class 的文件是 UTF-8 编码的 UNICODE 文件, 这在任何操作系统上都是一样的, 只要是 .class 文件就会是 UNICODE格式。
建议59:对字符串排序持一种宽容的心态
-
如果排序不是一个关键算法,使用Collator类即可。主要针对于中文。
public class Suggest59 { public static void main(String[] args) { String[] strs = {"张三(Z)","李四(L)","王五(W)"}; demo01(strs); demo02(strs); } private static void demo02(String[] strs) { Arrays.sort(strs, Collator.getInstance(Locale.CHINA)); int i = 0; System.out.println("=====demo02====="); for (String str : strs) { System.out.println((++i) + "、" + str); } System.out.println("=====demo02====="); } private static void demo01(String[] strs) { // 排序 默认升序 Arrays.sort(strs); int i = 0; System.out.println("=====demo01====="); for (String str : strs) { System.out.println((++i) + "、" + str); } System.out.println("=====demo01====="); } }