本章主要讨论局部变量、控制结构、类库、反射、本地方法的用法及代码优化和命名惯例。

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

  * 在第一次使用的它的地方声明局部变量(就近原则)。

  * 几乎每个局部变量的声明都应该包含一个初始化表达式。如果还没有足够的信息进行初始化,就延迟这个声明(例外:try-catch语句块)。

  * 如果在循环终止之后不再需要循环变量的内容,for循环优先于while循环。

  * 使方法小而集中(职责单一)。

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

  * 如果正在编写的类型表示的是一组元素,即使选择不实现Collection,也要实现Iterable接口,以便使用for-each循环。

  * for-each循环在简洁性和预防Bug方面有着传统for循环无法比拟的优势,且没有性能损失。但并不是所有的情况都能用for-each循环,如过滤、转换和平行迭代等。

  存在Bug的传统for循环代码示例:

 import java.util.*;

 /**
* @author https://www.cnblogs.com/laishenghao/
* @date 2018/10/7
*/
public class OrdinaryFor {
enum Suit {
CLUB, DIAMOND, HEART, SPADE,
}
enum Rank {
ACE, DEUCE, THREE, FOUR, FIVE,
SIX, SEVEN, EIGHT, NINE, TEN,
JACK, QUEEN, KING,
} public List<Card> createDeck() {
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(); ) {
for (Iterator<Rank> j = ranks.iterator(); j.hasNext(); ) {
deck.add(new Card(i.next(), j.next()));
}
}
return deck;
} static class Card {
final Suit suit;
final Rank rank; public Card(Suit suit, Rank rank) {
this.suit = suit;
this.rank = rank;
} // other codes
}
}

采用for-each循环的代码(忽略对Collection的优化):

     public List<Card> createDeck() {
Suit[] suits = Suit.values();
Rank[] ranks = Rank.values(); List<Card> deck = new ArrayList<>();
for (Suit suit : suits) {
for (Rank rank : ranks) {
deck.add(new Card(suit, rank));
}
}
return deck;
}

第47条 了解和使用类库

  * 优先使用标准类库,而不是重复造轮子。

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

  * float和double尤其不适合用于货币计算,因为要让一个float或double精确的表示o.1(或10的任何其他负数次方值)是不可能的。

System.out.println(1 - 0.9);

上述代码输出(JDK1.8):

  * 使用BigDecimal(很慢)、int或者long进行货币计算。

 

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

  * 在性能方面基本类型优于装箱基本类型。当程序装箱了基本类型值时,会导致高开销和不必要的对象创建。

  * Java1.5中增加了自动拆装箱,但并没有完全抹去基本类型和装箱基本类型的区别,也没有减少装箱类型的风险。

  如下代码在自动拆箱时会报NullPointerException:

  Map<String, Integer> values = new HashMap<>();
int v = values.get("hello");

  

  再考虑两个例子:

例子1:输出true

Integer num1 = 10;
Integer num2 = 10;
System.out.println(num1 == num2);

例子2:输出false

    Integer num1 = 1000;
Integer num2 = 1000;
System.out.println(num1 == num2);

  为啥呢?

  我们知道 “==” 比较的是内存地址。而Java默认对-128到127的Integer进行了缓存(这个范围可以在运行前通过-XX:AutoBoxCacheMax参数指定)。所以在此范围内获取的Integer实例,只要数值相同,返回的是同一个Object,自然是相等的;而在此范围之外的则会重新new一个Integer,也就是不同的Object,内存地址是不一样的。

  具体可以查看IntegerCache类:

     /**
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage. The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
*/ private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[]; static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h; cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++); // range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
} private IntegerCache() {}
}

IntegerCache

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

  * 字符串不适合代替其他的值类型。

  * 字符串不适合代替枚举类型。

  * 字符串不适合代替聚集类型(一个实体有多个组件)。

  * 字符串也不适合代替能力表(capacityies;capacity:能力,一个不可伪造的键被称为能力)。  

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

  * 构造一个较小的、大小固定的对象,使用连接操作符(+)是非常合适的,但不适合运用在大规模的场景中。

  * 如果数量巨大,为了获得可以接受的性能,请使用StringBuilder(非同步),或StringBuffer(线程安全,性能较差,一般不需要用到)。

第52条 通过接口引用对象

  * 这条应该与“面向接口编程”原则一致。

  * 如果有合适的接口类型存在,则参数、返回值、变量和域,都应该使用接口来进行声明。

如声明一个类成员应当优先采用这种方法:

private Map<String, Object> map = new HashMap<>();

而不是:

private HashMap<String, Object> map = new HashMap<>();

  * 如果没有合适的接口存在,则完全可以采用类而不是接口。

  * 优先采用基类(往往是抽象类)。

第53条 接口优先于反射机制

  * 反射的代价:

    (1)丧失了编译时进行类型检查的好处。

    (2)执行反射访问所需要的代码非常笨拙和冗长(编写乏味,可读性差)。

    (3)性能差。

  * 当然,对于某些情况下使用反射是合理的甚至是必须的。

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

  * 本地方法(native method)主要有三种用途:

    (1)提供“访问特定于平台的机制”的能力,如访问注册表(registry)和文件锁(file lock)等。

    (2)提供访问遗留代码库的能力,从而可以访问遗留数据(legacy data)。

    (3)编写代码中注重性能的部分,提高系统性能(不值得提倡,JVM越来越快了)。

  * 本地方法的缺点:

    (1)不安全(C、C++等语言的不安全性)。

    (2)本地语言与平台相关,可能存在不可移植性。

    (3)造成调试困难。

    (4)增加性能开销。在进入和退出本地代码时需要一定的开销。如果本地方法只是做少量的工作,那就有可能反而会降低性能(这点与Java8的并行流操作类似)。

    (5)可能会牺牲可读性。

第55条 谨慎地进行优化

  * 有三条与优化相关的格言是每个人都应该知道的:

    (1)More computing sins are committed in the name of efficiency (without necessarily achieving it)than for any other single reason——including blind stupidity.

       —— William AWulf

    (2)We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.

      —— Donald E. Knuth

    (3)We follow two rules in the matter of optimization:

      Rule 1. Don't do it.
      Rule 2(for experts only). Don't do it yet——that is, not until you have a perfectly clear and unoptimized solution.

      —— M. J. Jackson

  以上格言说明:优化的弊大于利,特别是不成熟的优化。

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

    实现上的问题可以通过后期优化,但遍布全局且限制性能的结构缺陷几乎是不可能被改正的。但并不是说在完成程序之前就可以忽略性能问题。

  * 努力避免那些限制性能的设计决策,考虑API设计决策的性能后果。

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

  * 把标准的命名惯例当作一种内在的机制来看待。

本文地址:https://www.cnblogs.com/laishenghao/p/effective_java_note_general_programming.html

《Effective Java》学习笔记 —— 通用程序设计的更多相关文章

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

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

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

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

  3. Effective Java 学习笔记之第七条——避免使用终结(finalizer)方法

    避免使用终结方法(finalizer) 终结方法(finalizer)通常是不可预测的,也是很危险的,一般情况下是不必要的. 不要把finalizer当成C++中析构函数的对应物.java中,当对象不 ...

  4. Effective Java 学习笔记之所有对象都通用的方法

    一.覆盖equals时请遵守通用约定 1.满足下列任何一个条件时,不需要覆盖equals方法 a.类的每个实例本质上都是唯一的.此时就是Object中equals方法所表达的含义. b.不关心类是否提 ...

  5. Effective Java学习笔记

    创建和销毁对象 第一条:考虑用静态工厂方法替代构造器 For example: public static Boolean valueOf(boolean b){ return b ? Boolean ...

  6. Effective Java 学习笔记之创建和销毁对象

    一.考虑用静态工厂方法代替构造器 1.此处的静态工厂方法是指返回指为类的对象的静态方法,而不是设计模式中的静态工厂方法. 2.静态工厂方法的优势有: a.使用不同的方法名称可显著地表明两个静态工厂方法 ...

  7. effective java学习笔记之不可实例化的类

    在没有显式声明一个类的构造方法时,编译器会生成默认的无参构造方法,在设计工具类时,我们通常将方法设置成静态方法,以类名.方法名的形式调用,此时这个类就没有必要创建实例,我们知道抽象类不可以被实例化,但 ...

  8. Effective Java学习笔记--创建和销毁对象

    创建和销毁对象 一.静态工厂方法代替构造器 静态工厂方法的优缺点 优点: 1.可以自定义名称(可以将功能表述的更加清晰) 2.不必每次调用都创建新的对象(同一对象重复使用) 3.返回的类型可以是原返回 ...

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

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

随机推荐

  1. Character Sets: Migrating to utf8mb4 with pt_online_schema_change

    David Berube  | June 12, 2018 |  Posted In: MySQL Modern applications often feature the use of data ...

  2. 一、HttpServletRequest接口 二、HttpServletReponse接口 三、POST和GET请求方式及其乱码处理 四、ServletContext对象和ServletConfig对象

    一.HttpServletRequest接口 内部封装了客户端请求的数据信息 接收客户端的请求参数.HTTP请求数据包中配置参数 ###<1>常用方法 getContextPath()重要 ...

  3. 设置webstorm支持ES6语法

    1.  点击File目录下的Default Settings 2.  再依次点击Languages & Frameworks  ----->  JaveScript  ----> ...

  4. 【转】Poco 1.4.2 HTTPClientSession/HTTPRequest 使用使用代理(proxy)需要注意的一点

    Poco 1.4.2 HTTPClientSession/HTTPClientSession 在使用代理的时候,request的URI不能包含协议和主机.否则会出错. 不使用代理的时候,以下代码能正常 ...

  5. Scala学习之路 (六)Scala的类、对象、继承、特质

    一.类 1.类的定义 scala语言中没有static成员存在,但是scala允许以某种方式去使用static成员这个就是伴生机制,所谓伴生,就是在语言层面上,把static成员和非static成员用 ...

  6. Android开发-各种各样好看漂亮的进度条,指示器,加载提示汇总

    导读:之前项目中用到一些进度条,找了不少,打算写个demo自己总结一下,留着以后用, 有些是自己写的,有些是github上找的别人的库,如果大家觉得好看可以用,直接下载复制代码到项目里就可以用,ok ...

  7. 分享一个excel根据文件超链接获取链接文档的最后更新时间

    #获取制定单元格内超链接对应的链接地址Sub geturi() For Each cell In Range("E3:E43") If cell.Hyperlinks.Count ...

  8. 教你一些Linux中隐藏bash历史命令的小技巧

    导读 如果你登录过 Linux 系统,并敲过一些命令,那你应该知道,bash history 会记录你输入的所有命令.这个操作其实是有一定风险的. 我个人经常使用 Linux,所以我想着研究一番,看看 ...

  9. C++ vector 容器

    //vector类 resemble array 自动扩容... 暂存于内存中 //格式 vector<类(型)名> 对象名 example: vector<string> v ...

  10. jq插件封装格式

    (function($) { // closure $.fn.hilight = function( options ) { //将方法定义在$的fn上 var defaults = { textCo ...