《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:通用程序设计
将局部变量的作用域最小化,可以增强代码的可读性和可维护性,并降低出错的可能性. 要使用局部变量的作用域最小化,最有力的方法就是在第一次使用它的地方才声明,不要过早的声明. 局部变量的作用域从它被声明的 ...
随机推荐
- c#中(&&,||)与(&,|)的区别和应用
对于(&&,||),运算的对象是逻辑值,也就是True/False &&相当与中文的并且,||相当于中文的或者 .(叫做逻辑运算符又叫短路运算符) 运算结果只有下列四种 ...
- Sql server 账号被锁住:"the account is currently locked out. The system administrator can unlock it."的解决办法(转载)
今天遇到的问题比较有意思.首先是很久没有打开测试数据库了,今天打开,使用service程序测试的时候出现下面的错误提示:Message: System.Data.SqlClient.SqlExcept ...
- 移除jboss响应中的中间件信息
JBoss 4.2 Suppressing the X-Powered-By header in JBoss 4.2.x can be done by modifying the web.xml fi ...
- python基础学习12----装饰器
装饰器可以在不修改目标函数代码的前提下, 在目标函数执行前后增加一些额外功能 例如有一个简单的函数 import time def func1(): print("这是一个简单的函数&quo ...
- 用JS制作《飞机大作战》游戏_第1讲(素材查找和界面框架搭建)-陈远波
一.查找素材: 二.分析游戏界面框架: 登录界面.游戏界面.暂停游戏界面.玩家死亡后弹出界面:并对应的界面包含什么元素: 三.分别搭建以上四个界面: 1.登录界面与游戏界面框架(隐藏游戏界面,四个界面 ...
- sql注入--mysql
mysql数据库结构: 数据库A --> 表名 --> 列名 --> 数据 数据库B --> 表名 --> 列名 --> 数据 mysql数据库信息: my ...
- python open 关于读、写、追加的总结
# -*- coding: utf-8 -*- # 测试文件名为: # text.txt # 测试文件内容为: # abcdefg # 每次操作后将文件复原 # r # 以只读方式打开文件,文件不可写 ...
- <数据结构与算法分析>读书笔记--函数对象
关于函数对象,百度百科对它是这样定义的: 重载函数调用操作符的类,其对象常称为函数对象(function object),即它们是行为类似函数的对象.又称仿函数. 听起来确实很难懂,通过搜索我找到一篇 ...
- 关于checkbox自动选中
checkbox是比较常用的,无论是权限管理还是博客的文章的标签类型的勾选,或者是上下级部门及其公司,都会用的到的. 今天主要讲的是checkbox自动根据值选中. 可参考我之前的文章:checkbo ...
- shiro实战系列(五)之Authentication(身份验证)
建议学习shiro读读官方文档,虽然不一定读的懂,但是建议要大致浏览,心中有个大概,这样对于学习还是有一定帮助 官网地址:https://shiro.apache.org/ Authenticatio ...