第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. 利用Module模块把构建的神经网络跑起来

    训练一个神经网络往往只需要简单的几步: 准备训练数据 初始化模型的参数 模型向往计算与向后计算 更新模型参数 设置相关的checkpoint 如果上述的每个步骤都需要我们写Python的代码去一步步实 ...

  2. 【渗透攻防Web篇】SQL注入攻击高级

    前言 前面我们学习了如何寻找,确认,利用SQL注入漏洞的技术,本篇文章我将介绍一些更高级的技术,避开过滤,绕开防御.有攻必有防,当然还要来探讨一下SQL注入防御技巧. 目录 第五节 避开过滤方法总结 ...

  3. PDF 报表 Java 组件 iText5 中的单位注意事项

    这里面涉及到这几个单位: 点(磅)(pt).像素(px).英寸(inch).毫米(mm) 分辨率单位有: dpi(点每英寸):出现于打印或印刷领域. lpi (线每英寸):描述光学分辨率的尺度. pp ...

  4. 【app】Appium日志文件分析

    Appium在和客户端及手机端进行通讯的时候会输出很多日志,可以通过点击主面板的Get Raw Logs得到其原始日志: 现在我们另存到其他路径,并且以notepad工具打开进行查看 Appium日志 ...

  5. python(leetcode)-283移动零

    给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序. 示例: 输入: [0,1,0,3,12] 输出: [1,3,12,0,0] 说明: 必须在原数组上操作, ...

  6. [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了

    [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了 本文首发自:博客园 文章地址: https://www.cnblogs.com/yilezhu/p/ ...

  7. 《JQuery技术内幕》读书笔记——自调用匿名函数剖析

    Javascript语言中的自调用匿名函数格式如下: (function(){ //do somethings })(); 它还有另外两种等价写法如下: //等价写法一 (function(){ // ...

  8. 【python小工具】linux 低权限密码记录 提权小套路

    #!/usr/bin/python import os, sys, getpass, time current_time = time.strftime("%Y-%m-%d %H:%M&qu ...

  9. 始于阿里,回归社区:阿里8个项目进入CNCF云原生全景图

    破土而出的生命力,源自理想主义者心底对技术的信念. 云原生技术正席卷全球,云原生基金会在去年KubeCon +CloudNativeCon NA的现场宣布: 其正在孵化的项目已达14个,入驻的厂家或产 ...

  10. Thrift 基于zookeeper改造模式

    对于Thrift服务化的改造,主要是客户端,可以从如下几个方面进行: 1.服务端的服务注册,客户端自动发现,无需手工修改配置,这里我们使用zookeeper,但由于zookeeper本身提供的客户端使 ...