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

第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. 判断Exception类中是否有InnerException属性

    public static class ExceptionExtend { /// <summary> /// 利用反射来判断对象是否包含某个属性 /// </summary> ...

  2. Linux内核参数基础优化

    web 服务负载均衡器常规网站服务器优化的基本配置: net.ipv4.tcp_fin_timeout =2 net.ipv4.tcp_tw_reuse =1 net.ipv4.tcp_tw_recy ...

  3. 从PFX文件中获取私钥、公钥证书、公钥

    https://blog.csdn.net/ZuoYanYouYan/article/details/77868584 该类具体功能:根据pfx证书得到私钥.根据私钥字节数组获取私钥对象.根据公钥字节 ...

  4. redis之禁用保护模式以及修改监听IP

    今天在安装filebeat的时候,出现了关于redis报错的问题,所以来总结一下: 报错信息是: (error) DENIED Redis is running in protected mode b ...

  5. 2.2Python基础语法(二)之运算符

    返回总目录 目录: 1.Python运算符的分类 2.算数运算符 3.复合运算符 4.比较运算符 5.逻辑运算符 (一)Python运算符的分类: (二)算数运算符: 注意下面三种算数符号: 1.** ...

  6. mysql workbench中my.ini路径不一样

    mysql workbench中的my.ini路径与mysql服务中的路径不一样 删除mysql workbench的配置文件即可解决 win7下的配置文件路径: %APPDATA\MySQL\Wor ...

  7. [luogu T71973]卡常者π酱

    [luogu T71973]卡常者π酱 题意 给定一个长度为 \(n\) 的字符串, 要求将字符串分割为若干段, 每一段要么是一个字符要么是前面几段的并的子串. 如果某一段是一个单独字符, 则产生 \ ...

  8. 1.HBase In Action 第一章-HBase简介(后续翻译中)

    This chapter covers ■ The origins of Hadoop, HBase, and NoSQL ■ Common use cases for HBase ■ A basic ...

  9. div宽度随屏幕大小变化

    题目: 一个页面上两个div左右铺满整个浏览器, 要保证左边的div一直为100px,右边的div跟随浏览器大小变化, 比如浏览器为500,右边div为400,浏览器为900,右边div为800. 方 ...

  10. Python2.7-pickle, cpickle

    pickle, cpickle模块,用于序列化和反序列化 python 对象数据,可以被序列化的有:布尔值,数值,字符串,包含以上三类的容器,定义在模块顶层的函数.内置函数和类,实例对象的 __dic ...