《Effective Java》学习笔记 —— 通用程序设计
本章主要讨论局部变量、控制结构、类库、反射、本地方法的用法及代码优化和命名惯例。
第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》学习笔记 —— 通用程序设计的更多相关文章
- Effective Java 学习笔记----第7章 通用程序设计
第7章 通用程序设计 第29条 将局部变量的作用域最小化 使一个局部变量的作用域最小化,最有力的技术室在第一次使用它的地方声明. 第30条 了解和使用库 效率提高.如果你不知道库 ...
- [Effective Java]第八章 通用程序设计
声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...
- Effective Java 学习笔记之第七条——避免使用终结(finalizer)方法
避免使用终结方法(finalizer) 终结方法(finalizer)通常是不可预测的,也是很危险的,一般情况下是不必要的. 不要把finalizer当成C++中析构函数的对应物.java中,当对象不 ...
- Effective Java 学习笔记之所有对象都通用的方法
一.覆盖equals时请遵守通用约定 1.满足下列任何一个条件时,不需要覆盖equals方法 a.类的每个实例本质上都是唯一的.此时就是Object中equals方法所表达的含义. b.不关心类是否提 ...
- Effective Java学习笔记
创建和销毁对象 第一条:考虑用静态工厂方法替代构造器 For example: public static Boolean valueOf(boolean b){ return b ? Boolean ...
- Effective Java 学习笔记之创建和销毁对象
一.考虑用静态工厂方法代替构造器 1.此处的静态工厂方法是指返回指为类的对象的静态方法,而不是设计模式中的静态工厂方法. 2.静态工厂方法的优势有: a.使用不同的方法名称可显著地表明两个静态工厂方法 ...
- effective java学习笔记之不可实例化的类
在没有显式声明一个类的构造方法时,编译器会生成默认的无参构造方法,在设计工具类时,我们通常将方法设置成静态方法,以类名.方法名的形式调用,此时这个类就没有必要创建实例,我们知道抽象类不可以被实例化,但 ...
- Effective Java学习笔记--创建和销毁对象
创建和销毁对象 一.静态工厂方法代替构造器 静态工厂方法的优缺点 优点: 1.可以自定义名称(可以将功能表述的更加清晰) 2.不必每次调用都创建新的对象(同一对象重复使用) 3.返回的类型可以是原返回 ...
- 《Effective Java》笔记45-56:通用程序设计
将局部变量的作用域最小化,可以增强代码的可读性和可维护性,并降低出错的可能性. 要使用局部变量的作用域最小化,最有力的方法就是在第一次使用它的地方才声明,不要过早的声明. 局部变量的作用域从它被声明的 ...
随机推荐
- spring mvc 接收 put参数
web.xml中: <!-- 用户put提交参数 --> <filter> <filter-name>HttpMethodFilter</filter-nam ...
- 常用vimrc记录
记录下vim 的一些常用配置.每当换到一台新电脑的时候,使用vim的时候,缩进等各种方式都不友好.每次都要到互联网上去找,还要找半天,这篇博客,记录下我常用的vim配置,以及扩展,能够快速的配置开发环 ...
- Java读取json文件并对json数据进行读取、添加、删除与修改操作
转载:http://blog.csdn.net/qing_yun/article/details/46865863#t0 1.介绍 开发过程中经常会遇到json数据的处理,而单独对json数据进行 ...
- digital ocean 内存不足时增加swap文件的方法
买了比较低配的digitalocean 云主机,在执行composer update的时候出现内存不足的问题,但是内存大小已经固定了,除非加钱,还有别的方法吗? 有,增加swap分区,这样就可以弥补内 ...
- Camstar MES 5.8 發現Ajax事件失效
從Camstar4.5升級到5.8後,發現原來用戶在4.5下可以正常使用的不良信息收集功能,列出的不良只有第一頁可以顯示,無法自動裝載下一頁. 嘗試發出,IE以下這個選項沒有選中,選中後,就可以正常工 ...
- Java中Map根据键值(key)或者值(value)进行排序实现
我们都知道,java中的Map结构是key->value键值对存储的,而且根据Map的特性,同一个Map中 不存在两个Key相同的元素,而value不存在这个限制.换句话说,在同一个Map中Ke ...
- Python3 中 sys.argv[ ]的用法解释
sys.argv[]说白了就是一个从程序外部获取参数的桥梁,这个“外部”很关键,所以那些试图从代码来说明它作用的解释一直没看明白.因为我们从外部取得的参数可以是多个,所以获得的是一个列表(list), ...
- CentOs7 编译安装PHP7.1.5
1 创建php用户和用户组,并在github下载php7源码 #######新建php用户和php组 [root@typecodes ~]# groupadd -r www && us ...
- 关于 X509Certificate2 找到文件路径的问题
由于微信退款功能需要用到证书,当调用 X509Certificate2 的时候,会提示找不到文件而报错. X509Certificate2 cert = new X509Certificate2(文件 ...
- 【转】如何打开注册表编辑器中存储用户信息的SAM文件?
sam文件怎么打开 (Security Accounts Manager安全帐户管理器)负责SAM数据库的控制和维护.SAM数据库位于注册表HKLM\SAM\SAM下,受到ACL保护,可以使用rege ...