Java高质量代码之 — 字符串
来源:互联网 发布:峨眉山旅游 知乎 编辑:程序博客网 时间:2024/04/28 03:56
前言:由于上一个星期工作繁忙,利用上下班和晚上睡前空余的时间拜读了秦小波老师的《改善Java程序的151建议》,感觉廓然开朗,注意到了很多平时在编写代码中并不会注意的问题,甚至感觉自己对Java只是略懂皮毛,不足以登大雅之堂,特此与读者分享读书笔记,以下内容摘自《改善Java程序的151建议》一书和笔者的理解
Java高质量代码系列文章
面向对象篇:http://ray-yui.iteye.com/blog/1926984
数据类型篇:http://ray-yui.iteye.com/blog/1927251
字符串篇:http://ray-yui.iteye.com/blog/1927647
数组与集合(1):http://ray-yui.iteye.com/blog/1928170
继上一章讲了数据类型,但在Java当中,什么数据类型使用频率最高,最受大家喜爱?那就是我们无限兼容的String类型,拥有诸多方法的String类型,近乎万能的String类型,而在使用String类型当中我们要注意什么?
1.推荐使用String直接赋值
首先我们来看一道经典的题目
以上代码,相信老鸟并不陌生,或者在学习过程中接触,或者在面试题上坑过,原因是Java当中为了避免系统大量的产生String对象,于是就设计出一个字符串常量池,当创建一个String时,会首先在常量池当中检查是否存在这个Hello这个常量,若然不存在,创建,若然存在,将内存地址指向此常量地址,str1赋值时,首先在常量池中创建了Hello,而str2再赋值时,Java检查到常量池中有Hello,直接将Hello的地址赋予了str2,造成str1==str2的情况,而new String的情况下,Java不会去常量池寻找,而是直接在堆中建立对象,所以使用str1==str3自然不成立,通过上面的介绍,由于常量池是由JVM本身进行维护的,所以JVM本身已对常量池进行了大量优化,所以使用直接赋值的方式会比使用new String的方式效率更高,更节省内存空间.
2.注意正则表达式引发的问题
请观察以下一段代码
注意观察//2 ,大家还记得正则表达式吗?这是因为replaceAll的方法其实是接受一个正则表达式,而$符号刚好是正则表达式的结束符号,所以出现了//2的情况,以后使用replaceAll时需要注意
3.注意String的不变性
请观察以下代码
在上面的代码当中,//1中,究竟创建了多少个String?一共是创建了3个,第一为Hello,第二为World,第三为Hello World,因为直接赋值的方式是在字符串常量池中生成的常量,什么是常量?不可变就为常量,因为不可变,所以//1中产生的了3个String,而//2中为什么我替换了还是等于Hello World呢?这也是因为不变性,仔细的你会发现,String类中提供的修改字符串的方法,包括substring,replace,concat等都是返回一个新的字符串,这是因为字符串的不变性造成的,所以在调用这些方法时需要用另一个或本调用的string去进行接收,//3同理
4.注意字符串的位置
请观察以下代码
笔者认为String是一个霸道的类型,而且霸道得我很欢喜,因为任何与String类型进行+号操作的其他类型,都会自动升格为String类型,而上例中是因为首先执行1+2的操作,再偶遇到String的Hello,再进行了自动升格,而第二个例子中,在还没进行整形的加法运算时,就首先偶遇到了String,已经自动提升为String,所以就等于Hello1+2的操作,自然等于Hello12
5.正确使用String,StringBuffer,StringBuilder
在上文当中,曾经提到过String的不变性,在String原因下,就产生出了StringBuffer和StringBuilder,后2者为可变的字符串,亦可以称为缓冲字符串,主要原理其实很简单,就是缓冲字符串中的字符串形式是char数组,以下来分析StringBuffer和String的几点不同
1.在频繁的字符串运算,例如拼接,删除,增加,替换,解释XML,进行SQL拼接
的时候,请优先考虑使用StringBuffer
2.在性能考虑方面,由于StringBuffer带有缓冲区,而且最终使用toString()
方法转换成1个字符串,我们试想,StringBuffer无论里面的信息是多么的
复杂,但最终是生成了1个字符串对象,效率会比用+号拼接不停生成字符串
的效率要高
3.想使用更多功能时,例如字符串翻转reverse,字符串插入insert,这些都是
String所不提供的,而StringBuffer却支持,所以想增加某些功能时,使用
StringBuffer
4.StringBuffer和StringBuilder区别?很简单,StringBuffer是线程安全的,
在多线程的环境底下应该使用StringBuffer,而StringBuilder线程是不
安全的,由于现在流行的SSH框架,而struts2中Action是线程安全的,所以
请大胆的使用StringBuilder
6.推荐在复杂字符串操作中使用正则表达式
正则表达式是笔者非常喜爱的东西,对字符串的操作而言,简直是万能,但却不容易一眼看穿究竟正则想表达些什么,因为毕竟是以符号来表达,在String中很多操方法都支持正则表达式,例如replace,split,substring等等,都知道正则表达式的参数,所以在操作复杂的字符串例如邮箱验证时,请务必使用正则表达式
7.使用字符串解决编码问题
相信各位接触过Web开发经验的开发人员都肯定接触过乱码问题,有时是从Web接受时遇到的,有时是从数据库中读取到乱码,而乱码的元凶就在于我们的IDE,使用javac进行编译时,JDK默认生成的编码是UTF-8的UNICODE编码,但在IDE开发进行编译时,若没有指定的话就会使用机器默认的语言,例如Window就会使用GBK等,而怎么样解决这个问题呢?除了在框架配置中解决,还可以在String当中使用代码来解决乱码,请看以下例子
8.对字符串排序持宽容心态
例如创建了一个字符串数组,使用Arrays.sort()进行自然排序,注意是自然排序,就会出现排序混乱的情况,为什么呢?因为我们Java对字符串排序时是根据了UNICODE编码来进行排序,是UNICODE编码对汉字的顺序并不是连贯连续的,所以若然要对字符串进行精确排序,可以选择使用pingyin4j转换成拼音后再首字母排序
总结:
笔者在本文章中只从《改善Java程序的151建议》中提取部分进行归纳性叙述,推荐各位读者购买这本书,该书不仅从事例中学习,而且涉及到原理,底层的实现,不仅告诉你应该怎么做,还告诉你为什么要这样做.
Java高质量代码系列文章
面向对象篇:http://ray-yui.iteye.com/blog/1926984
数据类型篇:http://ray-yui.iteye.com/blog/1927251
字符串篇:http://ray-yui.iteye.com/blog/1927647
数组与集合(1):http://ray-yui.iteye.com/blog/1928170
继上一章讲了数据类型,但在Java当中,什么数据类型使用频率最高,最受大家喜爱?那就是我们无限兼容的String类型,拥有诸多方法的String类型,近乎万能的String类型,而在使用String类型当中我们要注意什么?
1.推荐使用String直接赋值
首先我们来看一道经典的题目
- public static void main(String[] args) {
- String str1 = "Hello";
- String str2 = "Hello";
- String str3 = new String("Hello");
- System.out.println(str1 == str2);
- // 输出结果为true
- System.out.println(str1 == str3);
- // 输出结果为false
- }
以上代码,相信老鸟并不陌生,或者在学习过程中接触,或者在面试题上坑过,原因是Java当中为了避免系统大量的产生String对象,于是就设计出一个字符串常量池,当创建一个String时,会首先在常量池当中检查是否存在这个Hello这个常量,若然不存在,创建,若然存在,将内存地址指向此常量地址,str1赋值时,首先在常量池中创建了Hello,而str2再赋值时,Java检查到常量池中有Hello,直接将Hello的地址赋予了str2,造成str1==str2的情况,而new String的情况下,Java不会去常量池寻找,而是直接在堆中建立对象,所以使用str1==str3自然不成立,通过上面的介绍,由于常量池是由JVM本身进行维护的,所以JVM本身已对常量池进行了大量优化,所以使用直接赋值的方式会比使用new String的方式效率更高,更节省内存空间.
2.注意正则表达式引发的问题
请观察以下一段代码
- public static void main(String[] args) {
- // 1
- String str1 = "AHelloA";
- str1 = str1.replaceAll("A", "");
- System.out.println(str1.equals("Hello"));
- // 输出为true
- // 2
- String str2 = "$Hello$";
- str2 = str2.replaceAll("$", "");
- System.out.println(str2.equals("Hello"));
- // 输出为false
- // 3
- String str3 = "$Hello$";
- str3 = str3.replaceAll("\\$", "");
- System.out.println(str3.equals("Hello"));
- // 输出为true
- // 4
- String str4 = "$Hello$";
- // 更改了replace方法
- str4 = str4.replace("$", "");
- System.out.println(str4.equals("Hello"));
- // 输出true
- }
注意观察//2 ,大家还记得正则表达式吗?这是因为replaceAll的方法其实是接受一个正则表达式,而$符号刚好是正则表达式的结束符号,所以出现了//2的情况,以后使用replaceAll时需要注意
3.注意String的不变性
请观察以下代码
- public static void main(String[] args) {
- // 1
- String str1 = "Hello";
- str1 += " World";
- System.out.println(str1);
- // 输出Hello World
- // 2
- str1.replace("World", "");
- System.out.println(str1);
- // 输出Hello World
- // 3
- str1.substring(3);
- System.out.println(str1);
- // 输出Hello World
- }
在上面的代码当中,//1中,究竟创建了多少个String?一共是创建了3个,第一为Hello,第二为World,第三为Hello World,因为直接赋值的方式是在字符串常量池中生成的常量,什么是常量?不可变就为常量,因为不可变,所以//1中产生的了3个String,而//2中为什么我替换了还是等于Hello World呢?这也是因为不变性,仔细的你会发现,String类中提供的修改字符串的方法,包括substring,replace,concat等都是返回一个新的字符串,这是因为字符串的不变性造成的,所以在调用这些方法时需要用另一个或本调用的string去进行接收,//3同理
4.注意字符串的位置
请观察以下代码
- public static void main(String[] args) {
- String str1 = 1 + 2 + "Hello";
- System.out.println(str1);
- // 输出3Hello
- String str2 = "Hello" + 1 + 2;
- System.out.println(str2);
- // 输出Hello12
- }
笔者认为String是一个霸道的类型,而且霸道得我很欢喜,因为任何与String类型进行+号操作的其他类型,都会自动升格为String类型,而上例中是因为首先执行1+2的操作,再偶遇到String的Hello,再进行了自动升格,而第二个例子中,在还没进行整形的加法运算时,就首先偶遇到了String,已经自动提升为String,所以就等于Hello1+2的操作,自然等于Hello12
5.正确使用String,StringBuffer,StringBuilder
在上文当中,曾经提到过String的不变性,在String原因下,就产生出了StringBuffer和StringBuilder,后2者为可变的字符串,亦可以称为缓冲字符串,主要原理其实很简单,就是缓冲字符串中的字符串形式是char数组,以下来分析StringBuffer和String的几点不同
1.在频繁的字符串运算,例如拼接,删除,增加,替换,解释XML,进行SQL拼接
的时候,请优先考虑使用StringBuffer
2.在性能考虑方面,由于StringBuffer带有缓冲区,而且最终使用toString()
方法转换成1个字符串,我们试想,StringBuffer无论里面的信息是多么的
复杂,但最终是生成了1个字符串对象,效率会比用+号拼接不停生成字符串
的效率要高
3.想使用更多功能时,例如字符串翻转reverse,字符串插入insert,这些都是
String所不提供的,而StringBuffer却支持,所以想增加某些功能时,使用
StringBuffer
4.StringBuffer和StringBuilder区别?很简单,StringBuffer是线程安全的,
在多线程的环境底下应该使用StringBuffer,而StringBuilder线程是不
安全的,由于现在流行的SSH框架,而struts2中Action是线程安全的,所以
请大胆的使用StringBuilder
6.推荐在复杂字符串操作中使用正则表达式
正则表达式是笔者非常喜爱的东西,对字符串的操作而言,简直是万能,但却不容易一眼看穿究竟正则想表达些什么,因为毕竟是以符号来表达,在String中很多操方法都支持正则表达式,例如replace,split,substring等等,都知道正则表达式的参数,所以在操作复杂的字符串例如邮箱验证时,请务必使用正则表达式
7.使用字符串解决编码问题
相信各位接触过Web开发经验的开发人员都肯定接触过乱码问题,有时是从Web接受时遇到的,有时是从数据库中读取到乱码,而乱码的元凶就在于我们的IDE,使用javac进行编译时,JDK默认生成的编码是UTF-8的UNICODE编码,但在IDE开发进行编译时,若没有指定的话就会使用机器默认的语言,例如Window就会使用GBK等,而怎么样解决这个问题呢?除了在框架配置中解决,还可以在String当中使用代码来解决乱码,请看以下例子
- public static void main(String[] args) throws UnsupportedEncodingException {
- String str1 = "你好";
- // 第一种方法,此种方法需要知道来源字符串的编码
- byte[] byte1 = str1.getBytes("GBK");
- String str2 = new String(byte1);
- // 第二种方法,此种方法需要知道转变为什么格式的字符串,推荐使用
- String str3 = new String(str1.getBytes(), "UTF-8");
- }
8.对字符串排序持宽容心态
例如创建了一个字符串数组,使用Arrays.sort()进行自然排序,注意是自然排序,就会出现排序混乱的情况,为什么呢?因为我们Java对字符串排序时是根据了UNICODE编码来进行排序,是UNICODE编码对汉字的顺序并不是连贯连续的,所以若然要对字符串进行精确排序,可以选择使用pingyin4j转换成拼音后再首字母排序
总结:
笔者在本文章中只从《改善Java程序的151建议》中提取部分进行归纳性叙述,推荐各位读者购买这本书,该书不仅从事例中学习,而且涉及到原理,底层的实现,不仅告诉你应该怎么做,还告诉你为什么要这样做.
- Java高质量代码之 — 字符串
- Java高质量代码之 — 面向对象
- Java高质量代码之 — 泛型与反射
- Java高质量代码之 — 泛型与反射
- Java高质量代码之 — 泛型与反射
- Java高质量代码之反射
- 详解JAVA高质量代码之数组与集合
- 详解JAVA高质量代码之数组与集合
- [C#高质量代码的建议]字符串
- 高质量代码的演进之旅
- 编写高质量代码之读书笔记1
- 编写高质量代码之读书笔记2
- 编写高质量代码之读书笔记3
- C/C++之写出高质量代码
- 如何编写高质量JAVA代码
- 高质量JAVA代码编写规范
- 敏捷开发中的高质量Java代码
- 高质量JAVA代码编写规范
- Fibonacci数列
- android之自定义ViewGroup和自动换行的布局的实现
- centos6.4 安装源配置
- 简单实现Android图片翻转动画效果
- android父子控件手势冲突的解决
- Java高质量代码之 — 字符串
- Bar Chart 学习例子
- AutoMapper的配置方法
- 算法入门—局部变量和全局变量大小限制
- Android中Menu控件的add方法
- Java高质量代码之 — 面向对象
- 火车运煤问题 - 增加一个简单算法实现
- SAFEARRAY使用实例
- JavaScript 函数的引用传递