第45条: 将局部变量的作用域最小化

该条目与第13条(使类和成员的可访问性最小)本质上是类似的。要使局部变量的作用域最小化,最有利的方法就是在第一次使用它的地方声明。在每个局部变量的声明处都应该包含一个初始化表达式。还要使方法小而集中。

第46条: for-each循环优于传统的for循环

传统的for循环的迭代器和索引变量在每个循环中会出现三次,这很容易出错。考虑下面的例子:

public class Suits {

    public static void main(String[] args) {
// TODO Auto-generated method stub
Collection<Suit> suits = Arrays.asList(Suit.values());
Collection<Rank> ranks = Arrays.asList(Rank.values());
List<Card> deck = new ArrayList<>();
for(Iterator<Suit> i = suits.iterator(); i.hasNext(); ){
Suit suit = i.next();
for(Iterator<Rank> j = ranks.iterator(); j.hasNext(); ){
// deck.add(new Card(i.next(), j.next()));
System.out.println(suit + " " + j.next());
}
}
System.out.println("-----------更好的方法---------------");
for(Suit suit : suits){
for(Rank rank : ranks){
System.out.println(suit + " " + rank);
}
} } }
enum Suit {CLUB, DIAMOND, HEART, SPADE}
enum Rank {ACE, DEUCE, THREE , FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING}
class Card {
private Suit suit;
private Rank rank;
Card(Suit suit, Rank rank){
this.suit = suit;
this.rank = rank;
}
}

如果打开代码中的注释,我们会发现一个bug,迭代器对外部的集合(suits)调用了太多次的next方法,导致程序结果不是我们想要的。修复这个bug可以在外层添加 Suit suit = i.next();这段代码。但也有更好的方式,使用for-each循环,如上面代码中所示。

总之,for-each循环在简洁性和预防bug方面有着传统for循环无法比拟的优势。但下面三种情况无法使用for-each循环:

1.需要遍历集合,并删除选定的元素

2.需要遍历数组或列表,并取代它部分或全部的值

3.需要并行遍历多个集合

第47条:了解和使用类库

JDK中内置了大量的工具类库,但很多“不为人知”。这实际上考研的是编程人员对Java基础的掌握程度,例如输出数组的方法:Arrays.toString等等,再比如判断是否字符串为空是实际上有isEmpty方法的。书中建议每个程序员都应该熟悉java.lang、java.util

在进行工程项目类的开发时,不应重复造轮子,利用现有的已成熟的技术能避免很多bug和其他问题。除非自己业余爱好研究,重复造轮子我认为就很能提高编程水平了。

第48条:如果需要精确答案,请避免使用float和double

float和double类型不适合用于货币计算。因为要让它们精确地表示0.1(或10的任何其他负数次方值)是不肯能的。

看下面的例子:

public class FloatTest {

    public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(1.03 - .42);
double funds = 1.00;
int itemsBought = 0;
for(double price = .10; funds >= price; price += .10){
funds -= price;
itemsBought++;
}
System.out.println("itemsBought: " + itemsBought); //
System.out.println("Change: " + funds); //0.3999999999999
} }

程序的结果不是我们想要的,按理第一条输出语句应返回4,第二条返回0.可以用BigDecimal类型代替double。

public class BigDecimalTest {
public static void main(String[] args) {
final BigDecimal TEN_CENTS = new BigDecimal(".10");
int itemsBought = 0;
BigDecimal funds = new BigDecimal("1.00");
for(BigDecimal price = TEN_CENTS; funds.compareTo(price) >= 0; price = price.add(TEN_CENTS)){
itemsBought++;
funds = funds.subtract(price);
}
System.out.println(itemsBought + " items bought");
System.out.println("Money left over: $" + funds);
} }

这里结果就没问题了。当然如果不介意记录十进制的小数点,我们可以以分为单位,而不是以元为单位。

总而言之,如果需要精确答案,不要使用float或double。可以使用BigDecimal,如果性能关键,可以使用int或long,9位十进制数以内用int,不超过18位可以用long,超过18位就必须使用BigDecimal。

第49条:基本类型优先于装箱基本类型

基本类型和装箱基本类型有三个主要区别:

1.基本类型只有值,而装箱基本类型具有它们值不同的同一性,换句话说,两个装箱基本类型可以具有相同的值,但其对象引用不同。

2.基本类型有功能完备的值,而后者有非功能值-null

3.前者比后者更节省空间

来看一个有严重缺陷的例子:

public class IntegerTest {
public static void main(String[] args) {
Comparator<Integer> naturalOrder = new Comparator<Integer>() {
@Override
public int compare(Integer first, Integer second) {
return first < second ? -1 : (first == second ? 0 : 1);
// 修正方案
// int f = first;
// int s = second;
// return f < s ? -1 : (f == s ? 0 : 1);
}
};
int result = naturalOrder.compare(new Integer(42), new Integer(42));
System.out.println(result); //返回1 对装箱基本类型做==操作,执行对象同一性比较,导致结果与预期不一致
}
}

那什么时候该用装箱基本类型呢?第一是作为集合中的元素、键和值。第二在参数化类型中用装箱基本类型作为类型参数。最后,在进行反射的方法调用时,必须使用装箱基本类型。总之,可以选择的时候,基本类型要优先于装箱基本类型。自动装箱减少了使用装箱基本类型的繁琐性,但并没有减少它的风险。

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

在开发过程中我们不应该为了省事,所有类型都定义为String类型。应该编写更加适当的数据类型,避免使用字符串来表示对象,若使用不当,字符串类型比其它类型更加笨拙、更加不灵活、速度更慢、也更容易出错。经常被错误地用字符串来代替的类型包括基本类型、枚举类型和聚集类型。

第51条:当心字符串连接的性能

String字符串是不可变的,每次对一个字符串变量的赋值实际上都在内存中开辟了新的空间。如果要经常对字符串做修改应该使用StringBuilder(线程不安全)或者StringgBuffer(线程安全),其中StringBuilder由于不考虑线程安全,它的速度更快。

第52条:通过接口引用对象

应该优先使用接口而不是类来引用对象,例如:

List<String> list = new ArrayList<String>();

这样带来的好处就是可以更换list的具体实现只需一行代码,之前有谈到将接口作为参数的类型,这两者配合使用就能最大限度实现程序的灵活性。

但如果是类实现了接口,但是它提供了接口中不存在的额外方法,且程序依赖这些额外方法,这个时候用接口来代替类引用对象就不合适了。

第53条:通过接口引用对象

反射机制能在运行时获取已装载类的信息,比如Constructor、method、field。但这种方式是有影响的:

丧失了编译时类型检查

执行反射访问所需要的代码非常笨拙和冗长(这需要一定的编码能力)

性能损失

所以要慎用反射机制,但如果以非常有限的形式使用反射机制,可以获得许多好处,还是值得的。比如可以用反射方式创建实例,然后通过它们的接口或超类,以正常的方式返回这些实例。下面程序创建了一个Set<String>实例。

public class Reflective {
public static void main(String[] args) {
Class<?> c1 = null;
String className = args[0];
try {
c1 = Class.forName(className);
} catch (ClassNotFoundException e) {
System.out.println("class not found!");
System.exit(1);
}
//instantiate the class
// @SuppressWarnings("unchecked")
Set<String> s = null;
try {
s = (Set<String>) c1.newInstance();
} catch (InstantiationException e) {
System.out.println("class not accessible!");
System.exit(1);
} catch (IllegalAccessException e) {
System.out.println("class not instantiable!");
System.exit(1);
}
//exercise the set
s.addAll(Arrays.asList(args).subList(1, args.length));
System.out.println(s);
try {
test();
}catch (Exception e){
System.out.println("catch a exception!");
} }
public static void test(){
throw new RuntimeException();
}
}

简而言之,反射机制是一种功能强大的机制,对于特定的复杂的系统编程任务,它是非常必要的。如有可能,应该使用反射机制来实例化类,而访问对象则用编译时已知的某个接口或超类。

第54条:谨慎地使用本地方法

所谓的本地方法就是在JDK源码中你所看到在有的方法中会有“native”关键字的方法,这种方法表示用C或者C++等本地程序设计语言编写的特殊方法。之所以会存在本地方法的原因主要有:访问特定平台的接口、提高性能。

实际上估计很少很少在代码中使用本地方法,就算是在设计比较底层的库时也不会使用到,除非要访问很底层的资源。当使用到本地方法时唯一的要求就是全面再全面地测试,以确保万无一失。

第55条:谨慎地使用本地方法

我在实际编码过程中,常常听到别人说,这么实现性能可能会好一点,少了个什么什么性能会好一点,甚至是少了个局部变量也会提到这么性能要好一点,能提高一点是一点。

但实际上是在编码中如果你没有考虑清楚就冒然想当然的去做优化,常常可能是得不偿失,就像我开头提到的那样,甚至为了优化性能而去减少一个局部变量。正确的做法应该是,写出结构优美、设计良好的代码,不是写出快的程序。性能的问题应该有数据做支撑,也就是有性能测试软件对程序测试来评判出性能问题出现在哪个地方,从而做针对性的修改。

第56条:遵守普遍接受的命名惯例

我们在编码时要把标准的命名惯例当做一种内在机制来看待,遵守普遍接受的命名惯例。

7.通用程序设计_EJ的更多相关文章

  1. [Effective Java]第八章 通用程序设计

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  2. Effective Java 学习笔记----第7章 通用程序设计

    第7章 通用程序设计 第29条 将局部变量的作用域最小化     使一个局部变量的作用域最小化,最有力的技术室在第一次使用它的地方声明.   第30条 了解和使用库      效率提高.如果你不知道库 ...

  3. 《Effective Java》笔记45-56:通用程序设计

    将局部变量的作用域最小化,可以增强代码的可读性和可维护性,并降低出错的可能性. 要使用局部变量的作用域最小化,最有力的方法就是在第一次使用它的地方才声明,不要过早的声明. 局部变量的作用域从它被声明的 ...

  4. 《Effective Java》第8章 通用程序设计

    第47条:了解和使用类库 Top 100 Java Libraries on Github 2016 Library Number of Projects Type % of projects jun ...

  5. Effective java笔记(七),通用程序设计

    45.将局部变量的作用域最小化 将局部变量的作用域最小化,可以增强代码的可读性和可维护性,并降低出错的可能性. Java允许在任何可以出现语句的地方声明变量(C语言中局部变量要在代码块开头声明),要使 ...

  6. 【Java基础】通用程序设计

    Num1:for-each循环优先于传统的for循环 java1.5版本发布之前的做法: for(int i=0;i<a.length;i++){ doSomething(a[i]); } ja ...

  7. Effective Java 读书笔记之七 通用程序设计

    一.将局部变量的作用域最小化 1.在第一次使用变量的地方声明 2.几乎每个变量的声明都应该包含一个初始化表达式:try-catch语句是一个例外 3.使方法小而集中是一个好的策略 二.for-each ...

  8. 《Effective Java》学习笔记 —— 通用程序设计

    本章主要讨论局部变量.控制结构.类库.反射.本地方法的用法及代码优化和命名惯例. 第45条 将局部变量的作用域最小化 * 在第一次使用的它的地方声明局部变量(就近原则). * 几乎每个局部变量的声明都 ...

  9. 《Effective Java》读书笔记七(通用程序设计)

    No45 将局部变量的作用域最小化 要使局部变量的作用域最小化,最有力的方法就是在第一次使用它的地方声明. 几乎每个局部变量的声明都应该包含一个初始化表达式.如果你还没有足够的信息来对一个变量进行有意 ...

随机推荐

  1. VSCode插件开发全攻略(九)常用API总结

    更多文章请戳VSCode插件开发全攻略系列目录导航. 本文提炼一些常见的API使用场景供参考,本文内容有待完善. 编辑器相关 修改当前激活编辑器内容 替换当前编辑器全部内容: vscode.windo ...

  2. Javascript高级编程学习笔记(54)—— DOM2和DOM3(6)范围选择

    范围 为了让开发人员更加方便地控制页面“DOM2级遍历和范围”模块定义了“范围”接口 通过该接口开发人员可以选择文档中的一个区域,而不必考虑元素的界限 在常规操作不能有效地修改文档时,使用范围往往可以 ...

  3. 吴恩达机器学习笔记2-代价函数I(cost function)

    我们选择的参数决定了我们得到的直线相对于我们的训练集的准确程度,模型所预测的值与训练集中实际值之间的差距(下图中蓝线所指)就是建模误差(modeling error). 我们的目标便是选择出可以使得建 ...

  4. JavaScript标识符与关键字和保留字

    区分大小写 JavaScript中的一切(变量.函数名.操作符)都区分大小写.例如,变量名itbsl和变量名ITbsl是两个不同的变量. 标识符 所谓标识符,就是指变量.函数.属性的名字,或者函数的参 ...

  5. [Postman]发出SOAP请求(18)

    使用Postman发出SOAP请求: 将SOAP端点作为URL.如果您使用的是WSDL,那么请将WSDL的路径作为URL. 将请求方法设置为POST. 打开原始编辑器,并将正文类型设置为“text / ...

  6. Redis之分布式锁

    目录 一.加锁原因 二.原子操作 三.分布式锁 四.分布式锁常见问题 一.加锁原因 在一些比较高并发的业务场景,经常听到通过加锁的方法实现线程安全. 下面简单介绍一下 1.1 加锁方式 数据库锁 数据 ...

  7. Jquery Ajax Realize whether the user is registered

    XMLHttpRequest对象可以在不向服务器提交整个页面的情况下,实现局部更新网页.当页面全部加载完毕后,客户端通过该对象向服务器请求数据,服务器端接受数据并处理后,向客户端反馈数据. XMLHt ...

  8. MyBatis别名与util类技能了解

    1.别名 在java中String类型就是String类型,但是在MyBatis中可不会识别java中的类型,在MyBatis中String类型的别名是'string',小写的String,或者也可以 ...

  9. chrome强制刷新,非ctrl+f5

    开发时,经常有ctrl+f5无法做到真正的强制刷新,以下可以帮到你 Ctrl+Shift+Del 清除Google浏览器缓存的快捷键 Ctrl+Shift+R 重新加载当前网页而不使用缓存内容

  10. jquery click嵌套 事件重复注册 多次执行的问题解决

    jquery click事件中嵌套click会重复注册内部的click事件,导致重复执行. 比如 $(...).click(function(){   $(...).click(function(){ ...