第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. JavaScript的文档对象模型DOM

    小伙伴们之前我们讲过很多JavaScript的很多知识点,可以点击回顾一下: <JavaScript大厦之JS运算符>: <JavaScript工作原理:内存管理 + 如何处理4个常 ...

  2. 解析Java分布式系统中的缓存架构(上)

    作者 陈彩华 文章转载交流请联系 caison@aliyun.com 本文主要介绍大型分布式系统中缓存的相关理论,常见的缓存组件以及应用场景. 1 缓存概述 2 缓存的分类 缓存主要分为以下四类 2. ...

  3. post上传和压缩、插件模拟请求

      gzip gzip一种压缩方式,或者是文件形式,它主要用于网络传输数据的压缩 gzip压缩好不好用 浏览器:网速一定.内容越小.请求响应的速度是不是更快 手机server:返回数据类型是json/ ...

  4. 4 年前端狗,2 年 CTO

    阅读本文大概需要 14 分钟. 本文系作者:『Scott』原创发布于慕课网. 我,Scott,一家创业公司的 CTO. 从业6年却很少写文章,近一年来接触了几十个刚毕业的前端新人,也面试了100多个前 ...

  5. ActiveMQ 的安装与使用

    消息中间件简介 消息中间件(MOM:Message Orient middleware) 消息中间件有很多的用途和优点: 1. 将数据从一个应用程序传送到另一个应用程序,或者从软件的一个模块传送到另外 ...

  6. Kali学习笔记30:身份认证与命令执行漏洞

    文章的格式也许不是很好看,也没有什么合理的顺序 完全是想到什么写一些什么,但各个方面都涵盖到了 能耐下心看的朋友欢迎一起学习,大牛和杠精们请绕道 实验环境: Kali机器:192.168.163.13 ...

  7. 安卓TabLayout+ViewPager实现切页

    安卓使用TabLayout+ViewPager+Fragment 实现页面切换,可实现左右滑动切换视图界面和点击切换 可自定义菜单栏是在顶部还是在底部 一.实现效果: 二.实现过程: 2.1 一些重要 ...

  8. nginx、swoole高并发原理初探

    阅前热身 为了更加形象的说明同步异步.阻塞非阻塞,我们以小明去买奶茶为例. 同步与异步 同步与异步的重点在消息通知的方式上,也就是调用结果通知的方式. 同步:当一个同步调用发出去后,调用者要一直等待调 ...

  9. 如何将云原生工作负载映射到 Kubernetes 中的控制器

    作者:Janakiram MSV 译者:殷龙飞 原文地址:https://thenewstack.io/how-to-map-cloud-native-workloads-to-kubernetes- ...

  10. 如何在mpvue下收集小程序的formId

    什么是formId formId是小程序可以向用户发送模板消息的通行证,简单而言,你只有获取到formId,把它交给后台,后台同学才能向用户发送通知消息,而这个通行证的有效期只有七天.这是微信为了防止 ...