Effective java笔记5--通用程序设计
一、将局部变量的作用域最小化
本条目与前面(使类和成员的可访问能力最小化)本质上是类似的。将局部变量的作用域最小化,可以增加代码的可读性和可维护性,并降低出错的可能性。
使一个局部变量的作用域最小化,最有力的技术是在第一次使用它的地方声明。
几乎每一个局部变量的声明都应该包含一个初始化表达式。
在循环中经常要用到最小化变量作用域这一个规则。for循环使你可以声明循环变量(loop varialbe),它们的作用域被限定在正好需要的范围值内(这个范围包括循环体,以及循环体之前的初始化、测试、更新部分)。因此,如果在循环终止之后循环变量的内容不再被需要的话,则for循环优于while循环。
例如,下面是一种对集合进行迭代的首选做法:
for(Iterator i = c.iterator();i.hasNext();){
doSomething(i.next());
}
对比while循环,以及一个错误:
Iterator i = c.iterator();
while(i.hasNext()){
doSomething(i.next());
}
... Iterator i2 = c2.iterator();
while(i.hasNext()){ //BUG!
doSomethingElse(i2.next());
}
如果类似的”剪切-粘贴“错误发生在前面for循环的用法中,则结果代码根本不能通过编译。
for循环比while循环的另外一个优势,使用for循环要少一行代码,有助于把方法装入到大小固定的编辑器窗口中,从而增强可读性。
二、了解和使用库
1、通过使用标准库,你可以充分利用这些编写标准库的专家知识,以及在你之前其他人的使用经验。
2、使用标准库,它们的性能会不断提高,而无需你做任何努力。
3、标准库也会随着时间而增加新的功能。
4、使用标准库,会使自己的代码融入主流。
三、如果要求精确的答案,请避免使用float和double
float和double类型的主要设计目标是为了科学计算和工程计算。它们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的。然而没哟提供完全精确的结果,所以不应该被用于要求精确结果的场合。float和double类型对于货币计算尤为不合适。因为要让一个float或者double精确地表达0.1(或者10的任何其他负数次方值)是不可能的。
例如,假设你的口袋中有$1.03,花掉了42之后还剩下多少钱呢?下面是一个很简单的程序片段,企图回答这个问题:
System.out.println(1.03-.42);
不幸的是,它的输出结果是0.6000000000001.
要解决这个问题正确的方法是使用BigDecimal、int或者long进行货币计算。然而,使用BigDecimal有两个缺点:与使用原语运算类型相比,这样做很不方便,而且更慢。
总而言之,对于有些要求精确答案的计算任务,请不要使用float或者double。如果你希望系统来处理十进制小数点,并且不介意因为不使用原语类型而带来的不便,那么请使用BigDecimal.使用BigDecimal还有一个额外的好处,它允许你完全控制舍入:当一个操作涉及到舍入的时候,它让你从8种舍入模式中选择其一。如果你正在进行商务计算,并且要求特别的舍入行为,那么使用BigDecimal是非常方便的。
如果性能非常关键,并且你又不介意自己处理十进制小数点,而且所涉及的数值又不太大,那么可以使用int或者long(采用更小的单位,如上面采用美分)。如果数值范围没有超过9位十进制数字,则你可以使用int;如果不超过18位数字,则可以使用long。如果数值范围超过了18位数字,你就必须使用BigDecimal。
四、如果其他类型更适合,则尽量避免使用字符串
字符串被用来表示文本,它在这方面也确实做得很好。因为字符串很常用,并且java也支持的很好,所以自然地就会有这样一种倾向:即使在不适合于使用字符串的场合,人们往往也会 使用字符串。
字符串不适合代替其他的值类型。
字符串不适合代替枚举类型。
字符串不适合代替聚集类型。
字符串也不适合代替能力表(capabilities)。
五、了解字符串连接的性能
字符串连接操作符(+,string concatenation operator)是把多个字符串合并为一个字符串的便利途径。为连接n个字符串而重复地使用字符串连接操作符,要求n的平方级的时间。这是由于字符串是非可变的而导致的不幸结果。当两个字符串被连接的时候,它们的内容都要被拷贝。
为了获得可接受的性能,请使用StringBuffer替代String,用来存储构造过程中的账单声明:
public String statement(){
StringBuffer s = new StringBuffer(numItems()*LINE_WIDTH);
for(int i=0;i<numItems();i++)
s.append(lineForItem(i));
return s.toString();
}
+做法的开销随项目数量呈平方级增加,StringBuffer做法是线性增加,所以,项目越大,性能的差别会越显著。
原则很简单:不要使用字符串连接操作符来合并多个字符串,除非性能无关紧要。相反,应该使用StringBuffer的append方法,或者采用其他的方案,比如使用字符数组,或者每次只处理一个字符串,而不是将它们组合起来。
六、通过接口引用对象
例如,Vector是List接口的一个实现,在声明变量的时候应该养成这样的习惯:
//Good -uses interface as type
List subscribers = new Vector(); //而不是这样声明:
//Bad -uses class as type!
Vector subscribers = new Vector();
如果你养成了使用接口作为类型的习惯,那么你的程序将会更加灵活。
如果没有合适的接口存在的话,那么,用类而不是接口引用一个对象,是完全合适的。
第一、值类(value class),比如String和BigInteger。记住,值类很少有多种实现。它们通常是完全合适的。更一般地,如果一个具体类没有相关联的接口,那么不管它是否表示一个值,你都没有别的选择,只有通过这个类来引用它的对象。
第二、对象属于一个框架,而框架的基本类型是类,不是接口。如果一个对象属于这样一个基于类的框架(class-based framework),那么应该用相关的基类(base class)来引用这个对象,而不是使用它的实现类。java.util.TimerTask类就属于这种情形。
第三、一个类实现了一个接口,但是它提供了接口中不存在的额外方法--例如LinkedList。如果程序依赖于这些额外的方法,那么这样的类应该只被用来引用它的实例:它永远也不应该被用作参数类型。
七、接口优先于映像机制
映像设施(reflection facility)java.lang.reflect提供了”通过程序访问关于已装载的类的信息“的能力。映射机制(reflection)允许一个类使用另一个类,即使当前者被编译的时候后者还根本不存在,然而,这种能力也需要付出代价:
- 你损失了编译时类型检查的好处,也包括异常检查,如果一个程序企图用映射方式调用一个不存在的方法,或者一个不可访问的方法,那么在允许时它将会失败,除非你采取了特别的预防措施。
- 要求执行映射访问的代码非常笨拙和冗长。编写这样的代码非常乏味,阅读这样的代码页很困难。
- 性能损失。映射机制调用比普通方式调用慢2倍。
如果只是在很有限的情况下使用映像机制,那么虽然也会付出少许代价,但你可以获得许多好处。
八、谨慎地使用本地方法
Java Native Interface(JNI)允许Java应用可以调用本地方法(native method),所谓本地方法是指本地程序设计语言(比如C或者C++)来编写特殊方法。
从历史上看,本地方法主要有三种途径:
- 它们提供了”访问于平台相关的设施“的能力,比如访问注册表(registry)和文件锁。
- 它们也提供了访问老式代码库的能力,通过这些老式代码库进一步可以访问老式数据(legacy data)。
- 应用程序可以使用本地语言,实现性能关键部分,以提供系统的性能。(随着高版本的出现,这个方法已经不提倡)
九、谨慎的进行优化
优化的三条格言:
- 很多计算上的过失都被归咎于效率原因(没有获得必要的效率),而不是其他的原因---甚至包括盲目地做傻事。
- 不要去计较一些小的效率上的得失,在97%的情况下,不成熟的优化是一切问题的根源。
- 在优化方面的两条规则:
A、规则1:不要做优化。
B、规则2:(仅针对专家)还是不要做优化---也就是说,在你还没有绝对清晰的未优化方案之前,请不要做优化。
十、遵守普遍接受的命名惯例
Java平台有一套建立得很好的命名惯例(naming convention),其中许多被包含在The java language specification中。不严格地讲,这些命名惯例分为两大类:字面的(typographical)和语法的(grammatical)。
字面命名惯例:
包的名字应该是层次状的,用句号分隔每一部分。每一部分包括小写字母和数字(很少使用数字)。如果你的包将在你的组织之外被使用,那么包的名字应该以你的组织的Internet域名作为开头,并且顶级域名放在前面,例如,edu.cmu、com.sun、gov.nsa。标准库和一些课选的库,其名字以java和javav作为开头,它们是这条规则的例外。
包名字的剩余部分应该包括一个或者多个描述该包的组成部分。通常不超过8个字符。鼓励使用有意义的缩写形式,例如,使用util而不是utilities,只取首字母的缩写形式也是可以接受的。如awt。
类和接口的名字应该包括一个或者多个单词,每个单词的首字母大写,缩写应该尽量避免,除非是一些首字母缩写和一些通用的缩写,如max和min等。
方法和域的名字与类和接口的名字遵循相同的字面惯例,只不过方法或者域名的名字第一个字母应该小写,如remove、ensureCapacity.
“常量域”的名称应该包含一个或者多个大写的单词,中间用下划线符号隔开。
局部变量名称的字面命名惯例与成员名称类似,只不过它也允许缩写。
类型参数名称通常由单个字母组成,这个字母通常是以下五种类型之一:T表示任意的类型,E表示集合的元素类型,K和V表示映射的键和值类型,X表示异常。
语法惯例:
语法命名惯例比字面惯例更灵活,也更有争议。
对于包而言,没有语法命名惯例。
类通常用一个名词或者名词短语命名。如Timer或者BufferedWriter
接口的命名与类相似,如,Collection。或者用一个以“-able”或“-ible”结尾的形容词来命名。如Runnable或Accessible。
由于注解类型有那么多用处,因此没有单独安排词类。
执行某个动作的方法通常用动词或者动词短语来命名。对于返回boolean值的方法,其名称往往以单词“is”开头,后面跟名词或名词短语,或者任何具有形容词功能的单词或短语。
如果方法返回被调用对象的一个非boolean的函数或者属性,它通常用名词、名词短语,或者以动词“get”开头的动词短语来命名。
如果方法所在的类是个Bean,就要强制使用以“get”开头的形式。
转换对象类型的方法、返回不同类型的独立对象的方法,通常被称为toType,例如toString和toArray。返回视图的方法通常被称为asType,例如asList。返回一个与被调用对象同值的基本类型的方法,通常被称为typeValue,例如intValue。静态工厂的常用名称为valueOf、of、getInstance、netInstance、getType和NewType。
boolean类型的域的命名与boolean类型的访问方法很类似,但是省去了初始的“is”。其他类型的域通常用名词或者名词短语来命名。局部变量的语法惯例类似于域的语法惯例,但是更弱一些。
Effective java笔记5--通用程序设计的更多相关文章
- [Effective Java]第八章 通用程序设计
声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...
- Effective Java笔记一 创建和销毁对象
Effective Java笔记一 创建和销毁对象 第1条 考虑用静态工厂方法代替构造器 第2条 遇到多个构造器参数时要考虑用构建器 第3条 用私有构造器或者枚举类型强化Singleton属性 第4条 ...
- Effective java笔记(二),所有对象的通用方法
Object类的所有非final方法(equals.hashCode.toString.clone.finalize)都要遵守通用约定(general contract),否则其它依赖于这些约定的类( ...
- effective java笔记之单例模式与序列化
单例模式:"一个类有且仅有一个实例,并且自行实例化向整个系统提供." 单例模式实现方式有多种,例如懒汉模式(等用到时候再实例化),饿汉模式(类加载时就实例化)等,这里用饿汉模式方法 ...
- effective java笔记之java服务提供者框架
博主是一名苦逼的大四实习生,现在java从业人员越来越多,面对的竞争越来越大,还没走出校园,就TM可能面临失业,而且对那些增删改查的业务毫无兴趣,于是决定提升自己,在实习期间的时间还是很充裕的,期间自 ...
- Effective java笔记(七),通用程序设计
45.将局部变量的作用域最小化 将局部变量的作用域最小化,可以增强代码的可读性和可维护性,并降低出错的可能性. Java允许在任何可以出现语句的地方声明变量(C语言中局部变量要在代码块开头声明),要使 ...
- Effective Java 读书笔记之七 通用程序设计
一.将局部变量的作用域最小化 1.在第一次使用变量的地方声明 2.几乎每个变量的声明都应该包含一个初始化表达式:try-catch语句是一个例外 3.使方法小而集中是一个好的策略 二.for-each ...
- Effective java笔记6--异常
充分发挥异常的优点,可以提高一个程序的可读性.可靠性和可维护性.如果使用不当的话,它们也会带来负面影响. 一.只针对不正常的条件才使用异常 先看一段代码: //Horrible abuse of ex ...
- effective java 笔记1--序言
一.序言 程序设计的几条基本原则: 1.清晰性和简洁性最为重要,模块的用户永远也不应该被模块的行为所迷惑,所以写良好的注释是必需的. 2.模块要竟可能小,但也不能太小,好一个深奥的哲学问题. 3.代码 ...
随机推荐
- 超级内存NVDIMM
1.序言 基于非易失性内存(NVDIMM)的新一代内存条规格已经研制成功,其中集成了DRAM和非易失性存储芯片,能够在完全断电的时候完整保存内存数据,并支持主内存在持久高速高性能计算上的应用.区别于普 ...
- Mysql笔记——DQL
DQL就是数据查询语言,数据库执行DQL语句不会对数据进行改变,而是让数据库发送结果集给客户端. 语法: SELECTselection_list /*要查询的列名称*/ FROM table_lis ...
- 【原创】【Android New Features】—— 关于ADT 17的BuildConfig.DEBUG
在日常开发中,我们使用android.util.Log来打印日志,方便我们的开发调试.但是在打包发布时,需要手工把Log关闭,多少会有些不便,而且不排除打包者忘记关闭Log的情况.那么有没 ...
- 正确的理解this 和 super
this和super是Java的两个关键字. 先明确一个问题,有人错误的认为它们是对象里的“属性”,这只能怪老师没有讲清楚计算机的本质了.因为计算机的处理器只能用指令去处理数据,像C语言之类的容易理解 ...
- __init__ 和 self
看代码 class A: def __init__(self, val): self.name = val def printName(self): print self.name a = A(&qu ...
- HDU 4643 GSM 算术几何
当火车处在换基站的临界点时,它到某两基站的距离相等.因此换基站的位置一定在某两个基站的中垂线上, 我们预处理出任意两基站之间的中垂线,对于每次询问,求询问线段与所有中垂线的交点. 检验这些交点是否满足 ...
- POJ-2718 Smallest Difference
http://poj.org/problem?id=2718 从一些数里面选择一个子集组成一个数,余下的数组成另外一个数,(数不能以0开头)问两个数的差的绝对值最小是多少! 不管是奇数还是偶数,要想绝 ...
- 自定义View(7)官方教程:自定义View(含onMeasure),自定义一个Layout(混合组件),重写一个现有组件
Custom Components In this document The Basic Approach Fully Customized Components Compound Controls ...
- Windows 7 32位上硬盘安装linux[ubuntu13.04] 双系统
本内容介绍如何在window7上安装ubuntu双系统 一.准备工具 1. EasyBCD : 用来制作引导菜单选项 2.Wingrub : 用来确定磁盘文件Linux表示法位置 3.分区助手 :用来 ...
- Qt之QHeaderView自定义排序(获取正确的QModelIndex)
简述 前几节中分享过关于自定义排序的功能,貌似我们之前的内容已经可以很好地解决排序问题了,但是,会由此引发一些很难发现的问题...比如:获取QModelIndex索引错误. 下面,我们先来实现一个整行 ...