第8章 通用程序设计

45、将局部变量的作用域最小化

       要使局部变量的作用域最小化,最有力的方法就是在第一次使用它的地方声明。(因为较早的程序设计语言(如C语言)要求局部变量必须在一个代码块的开头处进行声明,导致现在习惯性这么做···。java允许在任何可以出现语句的地方声明变量)。

       声明一般应该包含初始化表达式。但也有例外,如果表达式为函数,且该函数有异常,则声明在外,初始化用try-catch语句包含。

46、for-each循环优先于传统的for循环

       for循环的弊端:很多人容易犯如下错误:

       for(Iterator<Face> i =faces.iterator();i.hasNext();)//假设有6个元素

              for(Iterator<Face>j = faces.iterator();j.hasNext();)//假设有6个元素

                     输出i.next()+” “+j.next();

这边只会执行6次,而不是6*6=36次。解决的方法就是在第一个for循环下面跟上Face face = i.next();下面的输出就可以直接输出face,不需要每次都错误性的执行i.next()。

       如果用嵌套的for-each循环就好办了:

       for(Face f1 : faces)

              for(Face f2 : faces)

                     输出 f1+” “+f2;//很简单就可以办到。

       因此,如果你编写的类型表示的是一组元素,即使你选择不让它实现Collection,也要让它实现Iterable,这样可以用for-each循环遍历你的类型。如:在implements Iterable<E>内部应该要实现iterator方法:

1.  public Iterator<E> iterator() {  

2.          // 实现Iterator接口的匿名内部类  

3.          return new Iterator<E>() {  

4.              int count = 0;  

5.    

6.              public boolean hasNext() {  

7.                  return count < size;  

8.              }  

9.    

10.             public E next() {  

11.                 return get(count++);  

12.             }  

13.   

14.             public void remove() {  

15.                 // TODO Auto-generated method stub  

16.                   

17.             }   

18.         };  

19.     }

       三种常见的情况无法使用for-each循环:

(1)过滤——如果需要遍历集合,并删除选定的元素,就需要显示的迭代器以便可以调用它的remove方法。(2)转换——如果需要遍历列表或者数组,并取代它部分或者全部的元素值。(3)平行迭代——如果需要并行地遍历多个,就要显示地控制迭代器或者索引变量,以便所有迭代器或者索引变量都可以得到同步前移。

47、了解和使用类库

       对于编写private static final Random rnd = newRandom();

       static int random(int n){returnMath.abs(rnd.nextInt())%n;}上述产生[0,n)之间的随机整数。

       有三个缺点:(1)如果n是一个比较小的2的乘方,经过一段相当短的周期后,它产生的随机数序列将会重复。(2)如果n不是2的乘方,那么平均起来,有些数会比其他的数出现得更为频繁(random方法产生的数字有2/3落在随机数取值范围的前半部分)。(3)返回一个落在指定范围之外的数。若nextInt()返回Integer.MIN_VALUE,那么Math.abs也会返回Integer.MIN_VALUEMath.abs对最大负数求绝对值还是负数···),如果n不是2的乘方,那么取模操作符(%)将返回一个负数。

       用标准类库Random.nextInt(int)可以规避上述缺点。

       总结:了解类库文档还是很重要的,至少可以让你不用重复发明“轮子”。

48、如果需要精确的答案,请避免使用float和double

       (1)用BigDecimal来表示(缺点:操作麻烦并且效率低),它允许你从8种舍入模式中选择其一。(2)如果性能非常关键,并且你又不介意自己记录十进制小数点,而且所涉及的数值不大,就可以使用int或者long。int数值范围不能超过9位数,long不能超过18位数,否则用BigDecimal。

       BigDecimal用法,如:BigDecimal price = new BigDecimal(“1.00”);,再定义一个BigDecimal变量用于表示糖果价钱,用price.add()和subtract()来加或减BigDecimal参数就可以了。

49、基本类型优先于装箱基本类型

       基本类型与装箱基本类型的区别:(1)两个装箱基本类型可以具有相同的值,但用==操作符返回false(可以在比较的时候将装箱基本类型赋值给基本类型来避免);(2)每个装箱基本类型除了它对应基本类型的所有功能之外,还有个非功能值:null(如Integer没有初始化,则是null,但若碰到需要自动拆箱(跟int比较)则会抛出NullPointerException异常);(3)基本类型比装箱基本类型更节省时间和空间。

       用到装箱基本类型的地方:(1)作为集合中的元素、键和值,参数化类型;(2)在进行反射的方法调用时,必须使用装箱基本类型。

50、如果其他类型更合适,则尽量避免使用字符串

       (1)字符串不适合代替枚举类型;(2)字符串不适合代替剧集类型,好的办法是简单地编写一个类来描述这个数据集,通常是一个私有的静态成员类。(3)如果多个客户端传入相同的常量字符串(作为访问标识),那么相当于这些客户端无意间共享一个常量字符串,会导致失败,而且安全性差,恶意客户端可能有意地使用与另一个客户端相同的键,从而非法访问其他客户端数据。

51、当心字符串连接的性能

       不要使用字符串连接操作符合并多个字符串,除非性能无关紧要。相反应该用StringBuilder的append方法。另一种方法是使用字符数组或者每次只处理一个字符串而不是将它们组合起来。

52、通过接口引用对象

       例外(即不存在适当接口类型时,还是只能用类):(1)如果没有合适的接口存在,完全可以用类而不是接口来引用对象;(2)对象属于一个框架,而框架的基本类型是类,不是接口;(3)类实现了接口,但是它提供了接口中不存在的额外方法。如果程序依赖这些额外的方法,这种类就应该只被用来引用它的实例(常识)。

53、接口优先于反射机制

       反射机制所要付出的代价:(1)丧失了编译时类型检查的好处,包括异常检查。如果企图用反射方法调用不存在或不可访问方法,会运行失败。(2)执行反射访问所需要的代码非常笨拙和冗长。(3)性能损失。反射方法比普通方法调用慢很多。

       核心反射机制最初是为了基于组件的应用创建工具而设计的。

       需要使用反射机制的示例有类浏览器、对象监视器、代码分析工具、解释型的内嵌式系统、RPC(远程过程调用)等。

       如果你编写的程序必须要与编译时未知的类一起工作,如有可能,就应该仅仅使用反射机制来实例化对象,而访问对象时则使用编译时已知的某个接口或者超类

54、谨慎的使用本地方法

       原来还有本地方法这一说···Java Native Interface(JNI)允许java应用程序调用本地方法,所谓的本地方法是指用本地程序设计语言来编写的特殊方法。

       本地方法的用途:(1)提供了“访问特定与平台的机制”的能力(如注册表registry、文件锁);(2)提供了访问遗留代码库的能力,从而可以访问遗留数据。(3)本地方法可以通过本地语言,编写应用程序中注重性能的部分,以提高系统性能。

       使用本地方法前务必三思:极少数情况下会需要使用本地方法来提高性能。如果你必须要使用本地方法来访问低层的资源,或遗留代码库,也要尽可能少用本地代码,并且要全面进行测试。本地代码一个bug就有可能破坏整个应用程序。

55、谨慎地进行优化

       不要因为性能而牺牲合理的结构,要努力编写好的程序而不是快的程序。

       努力避免那些限制性能的设计决策。当一个系统设计完成之后,其中最难以更改的组件是那些指定了模块之间交互关系以及模块与外界交互关系的组件在这些设计组件之中,最主要的是API、线路层协议以及永久数据格式。这些设计组件不仅在事后难以/不可能改变,对性能造成限制。

       为获得好的性能而对API进行包装,这是一种非常不好的想法。

       JDK带了简单的性能剖析器,现代的IDE也提供了更加成熟的性能剖析器。

56、遵守普遍接受的命名惯例

       任何将在你的组织之外使用的包,其名称都应该以你的组织的Internet域名开头,并且顶级域名放在最前面。包名称的其余部分应该包括一个或者多个描述改包的组成部分,这些组成部分比较简短,通常不超过8个字符,鼓励使用有意义的缩写形式。

       类和接口的名称,包括枚举和注解类型的名称,都应该包括一个或者多个单词(除非是通用缩写),每个单词的首字母大写。

       常量域是唯一推荐使用下划线的情形。

       类型参数名称通常由单个字母组成。T表示任意的类型;E表示集合的元素类型;K和V表示映射的键和值类型,X表示异常。

       方法返回被调用对象的一个非boolean的函数或者属性,它通常用名词、名词短语或者以动词“get”开头的动词短语来命名,如size、hashCode、getTime。

     转换对象类型的方法、返回不同类型的独立对象的方法,通常称为toType,如toString。返回视图的方法通常被称为asType,如asList。返回一个与被调用对象同值的基本类型的方法,通常被称为typeValue,如intValue。

       静态工厂的常用名称:valueOf、of、getInstance、newInstance、getType、newType。

       也不要盲目遵从这些命名习惯。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值