Effective Java读书笔记
ahui2333
这个作者很懒,什么都没留下…
展开
-
(45):将局部变量的作用域最小化
(1):在第一次使用某个局部变量的地方进行声明。Java可以在任何可以出现语句的地方声明变量。 这条规则可以让我们在需要的地方声明变量,从而缩小作用域。(2):几乎每个局部变量的声明都应该包含一个初始化表达式。(3):for循环,都允许声明循环变量,它们的总用域被限定在正好需要的范围之内。(4):使用另一个局部变量来限制一个局部变量的迭代范围。for(int i =0,n = expe原创 2017-04-21 10:40:56 · 553 阅读 · 0 评论 -
(33):用EnumMap代替序数索引
当我们需要使用Map类型,且Map的key可以被做成枚举类型,我们应该考虑使用EnumMap。现在有这么一个问题,有一座花园,里面有很多花。花总共有三种类型,我们想把它分类分出来。public class Herb { public enum Type { ANNUAL, PERENNTAL, BIENNIAL } // private stati原创 2017-04-14 11:19:07 · 692 阅读 · 0 评论 -
(32):用EnumSet代替位域
位域:就是用int枚举模式,将2的不同倍数赋予每个常量,这种表示法让你用OR位运算将几个常量合并到一个集合中(int值),称作位域(bit field)。位域的写法就不介绍了,总之这种写法缺陷比较大。我们应该用EnumSet代替它。public class Text { public enum Style { BOLD, ITALIC, UNDERLINE, S原创 2017-04-14 10:54:21 · 559 阅读 · 0 评论 -
(31):用实力域代替序数
我们知道枚举数天生就是有值的,默认是从0开始,那么我们需要从1开始怎么办呢?可以这么写public enum Ensemble { SOLO, DUET, TRIO, QUARTET, QUINTET, SEXTET, SEPTET, OCTET, NONET, DECTET; public int numberOfMusicians() { return o原创 2017-04-14 10:25:45 · 270 阅读 · 0 评论 -
( 14 ) : 在公有类中使用访问方法而非公有域
class Point{public double x;public double y;}这种写法暴露了自己的成员,我们无法控制其他类访问这些成员。这是很违背面向对象的原则的。所以我们应该将类成员设为私有,改为使用set、get函数来控制其访问。原创 2017-04-05 17:31:45 · 294 阅读 · 0 评论 -
(38):检查参数的有效性
绝大多数方法和构造器对于传递给他们的参数值都会有某些限制。例如索引值必须是非负数,对象引用不能为null,等等。所以我们应该尽量对这些限制在程序开始进行检查。对于公有的方法,要用Javadoc的@throws标签 在文档中说明违反参数值限制时会抛出的异常。通常这样的异常为IllegalArgumentException,IndexOutOfBoundsException。但是并不是每个方法我们原创 2017-04-18 14:26:17 · 568 阅读 · 0 评论 -
(37):用标记接口定义类型
所谓标记接口,就是指一个接口里面什么都没有,只是标记实现类具有某种属性。例如serilizeable就是这样一个接口,他只是告诉jvm,继承于这个接口的CLASS需要序列化处理,而我们根本不用实现这个接口的方法。优点:1.标记接口定义的类型是由被标记类的实例实现的;标记注解则没有定义这样的类型。2.可以被更精确地进行锁定。如果注解类型利用@Target(ElementType.原创 2017-04-18 11:02:33 · 540 阅读 · 0 评论 -
(30):用enum代替int常量
我们常常会需要int全局常量 如下:public static final int APP_START=1;public static final int APP_PAUSE=0;public static final int APP_STOP =2;public static final int PLAY_START=1;public static final int P原创 2017-04-13 15:17:38 · 666 阅读 · 0 评论 -
(29):优先考虑类型安全的异构容器
泛型常用于以及一些单元素容器。但一般来说一个set只有一个类型参数,表示他的元素类型,一个Map有两个类型参数,表示键和值。考虑这么一种情况,数据库中的字段都是不同类型,怎么将一条数据加入到容器中。我们需要一个容器能够容纳不同类型的值。简单一想我们用Map不就行了,但这样有个缺点,我们无法通过键来确定值得类型,也就是说我们取值的时候是无法获得值得类型的。还有一种想法我们是不是可以制定一个规原创 2017-04-13 10:55:48 · 275 阅读 · 0 评论 -
(13):使类和成员的可访问性最小化
设计良好的模块会隐藏所有的实现细节,把它的API与它的实现清晰的隔离开来。然后,模块之间只通过它们的API进行通信,一个模块不需要知道其他模块的内部工作情况。这被称封装。封装可以有效的解除组成系统各个模块的耦合关系。访问控制机制(access mode)决定了类,接口,成员的可访问性。正确的使用访问修饰符,对于实现信息隐藏是非常关键的。第一条规则:尽可能使每个类和成员不被外界访问。尽可能使原创 2017-04-05 10:55:39 · 293 阅读 · 0 评论 -
(39):必要时进行保护性拷贝
看下面这个例子public final class Period{ private final Date startTime; private finale Date endTime; public Period(Date startTime , Date endTime) {原创 2017-04-19 10:30:20 · 293 阅读 · 0 评论 -
(15):使可变性最小化
不可变类只是其实例不能被修改的类。每个实例中包含的所有信息都必须在创建该实例的时候就提供,并在对象的整个生命周期内固定不变。为了使类成为不可变,要遵循下面五条规则:(1)不要提供任何会修改对象状态的方法(2)保证类不会被扩展(3)使所有的域都是final的(4)使所有的域都成为私有的(5)确保对于任何可变组件的互斥访问。public final class Complex{原创 2017-04-06 10:55:00 · 343 阅读 · 0 评论 -
(44):为所有导出的API元素编写文档注释
要想使一个API真正可用,就必须为其编写文档。Java提供了Javadoc工具,使得为API编写文档变得非常容易。Javadoc利用特殊格式的文档注释,根据源代码自动生成API文档。示例:/** * Returns the element at the specified position in this list. * * This method is not guaranteed原创 2017-04-21 10:06:39 · 359 阅读 · 0 评论 -
(36):坚持使用Override注解
这其实也没什么好说的。总之,当我们要重写时,一定要添加Override注解,这既可以告诉别人这是重写的方法,也可以使编译器替你防止大量的错误。原创 2017-04-17 11:12:48 · 275 阅读 · 0 评论 -
(35):注解优先于命名模式
所谓命名模式有些程序元素需要通过某种工具进行特殊处理。例如:JUnit测试框架要求用户一定要用test作为测试方法名称的开头。这种方法缺陷很大,列如不小心写错了名称,测试失败却没有任何提示。相比较而言,注解可以完美解决这些缺点。大多数程序员不必定义注解类型,但是所有的程序员都应该使用Java平台所提供的预定义的注解类型,还要考虑使用IDE或者静态分析工具所提供的任何注解 总之,有了原创 2017-04-17 10:41:37 · 293 阅读 · 0 评论 -
(34):使用接口模拟可伸缩的枚举
在java中一个枚举是无法直接去扩展另一个枚举的,但通过接口我们可以变相的实现这一点。public interface Operation { double apply(double x, double y);}public enum BasicOperation implements Operation { PLUS("+") { public double原创 2017-04-17 10:24:01 · 453 阅读 · 0 评论 -
(43):返回零长度的数组或者集合,而不是null
对于一个返回能够null的方法,我们需要额外的代码去处理它,这样做很容易出错。因为我们很容易忘记处理null有人认为null返回值比零长度数组更好,因为避免了分配数组所需要的开销。这种观点是错误的,原因有两点:(1):在这个级别上去担心性能是没有意义的,除非能证明这个方法正是造成性能问题的源头。(2):对于不返回任何类型调用,返回同一个零长度数组是可能的简而言之:返回类型为数组或者原创 2017-04-20 11:26:51 · 349 阅读 · 0 评论 -
(42):慎用可变参数
可变参数指的是可以接受一个或多个指定类型的参数static int sum(int... args) { int sum=0; for(int arg : args) sum += arg; return sum;}上面就是可变参数的实例,可以是sum()=0也可以是sum(1,2,3)=6。可变参数的机制通过先创建一个数组,数组的大小为在调用位置原创 2017-04-20 10:45:23 · 417 阅读 · 0 评论 -
(16):复合优先于继承
继承是实现代码重用的的有力手段,但它未必是最好的方法。对于普通的具体类进行跨越包边界的继承,则是非常危险的。这里的继承指的是一个类扩展另一个类的继承而非接口继承。继承的一大缺点在于打破了封装性。子类依赖于超类中特定的功能实现细节。意味着超类如果发生版本变化,子类有可能受到破坏。除非超类是专门为了扩展而设计的。public class InstrumentedHashSet extend原创 2017-04-06 15:02:58 · 343 阅读 · 0 评论 -
(41):慎用重载
对于重载的方法选择是静态的(而对于被覆盖的方法的选择是动态的),要调用哪个重载方法是在编译时做出决定的。看下面这个例子:public class CollectionClassifier{ public static String classify(Set s){ return "Set"; }原创 2017-04-19 11:31:50 · 241 阅读 · 0 评论 -
(40):谨慎设计方法签名
(1):谨慎的选择方法名称(2):不要过于追求提供便捷的方法。当一项操作被经常使用的时候,可以考虑。(3):避免过长的参数列表(4):对于参数类型优先使用接口而不是类。有三种方法可以缩短过长的参数列表(1):把方法分解成多个方法(2):创建辅助类,然后把参数放到辅助类例,我们只要传入辅助类的对象就行。(3):从对象构造到参数调用都采用Builder模式。(参见条目2)原创 2017-04-19 10:58:08 · 354 阅读 · 0 评论 -
(12):考虑实现Comparable接口
如果一个类实现了Comparable接口,就表明它的实例具有内在的排序关系。为实现了Comparable接口的对象数组排序就是这么简单。Array.sort(a); 一旦类实现了Comparable接口,它就可以跟许多泛型算法以及依赖于该接口的集合实现进行协作。你付出了很小的努力就可以获得非常强大的功能。事实上,Java平台类库中的所有值类都实现了Comparable接口。如果你正在编写原创 2017-04-01 10:39:17 · 323 阅读 · 0 评论 -
(11):谨慎的覆盖clone
当我们想要克隆一个对象时,也就是说我们想要两个一模一样的对象,但想要这两个对象各自开辟空间的时候,我们通常要实现cloneable接口。但是接口没给我们提供clone的实现,但Object类却给我们提供了一个受保护的clone实现。一般来讲我们直接在子类重写的clone方法调用super,clone()可以得到我们想要的结果。import java.util.Date;public cl原创 2017-03-31 15:25:02 · 375 阅读 · 0 评论 -
(10):始终要覆盖toString
虽然Object提供了toString的实现,但它返回的是类名+@+散列码,这不是我们想看到的,所以强烈建议重写toString方法,把它展示成我们想要的结果。原创 2017-03-31 14:59:27 · 324 阅读 · 0 评论 -
(26):优先考虑泛型
观察下面的代码:public class Stack { private E[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16; public Stack() { elements =原创 2017-04-12 11:29:30 · 314 阅读 · 0 评论 -
(25):列表优先于数组
数组是协变的(convariant),如果Sub是Super的子类型,那么数组类型Sub[]就是Super[]的子类型。Object[] obs=new Long[1]; obs[0]="hhe"; 这是可以的 泛型却是不可变的,List不是List的子类型。List list=new ArrayList();//编译错误list.add("hhe"); 数原创 2017-04-11 15:16:21 · 309 阅读 · 0 评论 -
(24):消除非受检警告
如果无法消除警告,同时又可以证明引起警告的代码是类型安全的。可以用一个@SuppressWarnings("unchecked")注解来禁止这条警告。 如果发现要在长度不止一行的方法或构造器中使用SuppressWarnings注解,可以将它移到一个局部变量的声名中,虽然必要声名一个新的局部变量,不过这么做还是值得的。SuppressWarnings注解方在return 语句中是非法的,每当使原创 2017-04-11 14:59:38 · 457 阅读 · 0 评论 -
(23):请不要在新代码中使用原生态类型
每种泛型都定义一个原生态类型(raw type),即不带任何实际类型参数的泛型名称。例如List为参数化类型List就是原生态类型。原生态类型是无法提供编译前检查的也就是说我们能在一个list中添加两个不同类型的值的。这是很不合理的,所以我们应该避免使用原生态类型。参数化类型就有效避免了这个问题。它能够提供编译前检查,并且可以自动完成类型转化。这个原则应该是刚开始学习编程就应该养成的习惯,这里原创 2017-04-11 11:01:03 · 236 阅读 · 0 评论 -
(22):优先考虑静态成员类
嵌套类是指被定义在另一个类的内部的类。嵌套类存在的目的应该只是为了它的外围类提供服务。如果嵌套类将来有可能会用于其他的某个环境,它就应该是顶层类。嵌套类有四种:静态成员类,非静态成员类,匿名类,局部类。除了第一种,其他三种称为内部类。静态成员类是最简单的一种嵌套类。最好把它看做是普通的类,只是碰巧被声明在另一个类的内部而已。从语法上来讲,静态成员类和非静态成员类之间唯一的区别是,静原创 2017-04-10 17:05:57 · 369 阅读 · 0 评论 -
(21):用函数对象表示策略
为了在java中实现策略模式,要声明一个接口来表示该策略,并且为每一个策略声明一个实现了该接口的类。当一个具体策略只被使用一次时,通常使用匿名类来声明和实例化这个具体策略类。当一个具体策略类时设计用来重复使用的时候,它的类通常就要被实现为私有的静态成员类,并通过公有的静态final域被导出,其类型为该策略接口。策略模式具体请看:http://blog.csdn.net/ahui2333/a原创 2017-04-10 15:45:36 · 255 阅读 · 0 评论 -
(20):类层次优先于标签类
所谓标签类,指的是一个类中充斥着许多样板代码,下面这个就是典型的标签类public class Figure1{ enum Shape { RECTANGLE, CIRCLE } // Tag field - the shape of this figure final Shape shape; //原创 2017-04-10 14:58:59 · 345 阅读 · 0 评论 -
(19):接口只用于定义类型
当类实现接口时,接口就充当可以引用这个类的实例类型。因此,类实现了接口,就表明客户端对这个类的实例可以实施某些动作。为了任何其他目的而定义的接口是不恰当的。常量接口就是一种错误的用法public class PhysicalConstants { private PhysicalConstants() {} public static final double AV原创 2017-04-10 14:43:04 · 635 阅读 · 0 评论 -
(18):接口优先于抽象类
因为java只允许单继承,所以抽象类受到了极大的限制。但是接口一点定义,被其他类广泛应用时,其扩张就变得很困难。而抽象类的演变比接口的演变容易得多。骨架通过对导出的每个重要接口都提供一个抽象的骨架实现类,把接口和抽象类的优点结合起来 。接口的作用仍然是定义类型,但是骨架实现类接管了所有域接口实现相关的工作 。static List intArrayAsList(final int[] a)原创 2017-04-10 10:38:01 · 432 阅读 · 0 评论 -
(27):优先考虑泛型方法
就如类可以从泛型中受益一般,方法也可以。静态工具方法尤其适合与泛型化。JDK中的Collections泛型中的所有“算法”方法都泛型化了。public static Set union(Set s1,Set s2){ Set result = new HashSet(s1); result.addAll(s2); return result;}原创 2017-04-12 15:14:16 · 317 阅读 · 0 评论 -
(28):利用有限制通配符来提高API的灵活性
正如我们所知参数化类型是不可变的,也就是说List和List是没有任何关系的即使type1和type2是父子类的关系。当type1参数作为泛型参数时,type2即使是type1的子类,也不能作为泛型参数。看下面的例子:public class Stack { public Stack(); public void push(E e); public E原创 2017-04-12 15:45:02 · 270 阅读 · 0 评论 -
(9):覆盖equals时总要覆盖hashCode
一个很常见的错误根源在于没有覆盖hashCode方法。在每个覆盖了equals方法的类中,也必须覆盖hashCode方法。如果不这样做,就会违反Object.hashCode的通用约定,从而导致该类无法结合所有基于散列的集合一起正常运作,这样的集合包括HashMap,HashSet和Hashtable。以下是约定:(1)在应用程序的执行期间,只要对象的equals方法的比较操作所用到的信息原创 2017-03-30 16:00:57 · 232 阅读 · 0 评论 -
(8):覆盖equals请遵循通用约定
再覆盖equals方法是,你应该遵循它的通用约定,下面是约定内容:自反性(reflexive):对于任何非null的引用值x,x.equals(x)必须返回true。 如果违背,当你把该类的实例添加到集合(collection)中,然后该集合的contains会告诉你,没有包含你刚刚添加的实例。 对称性(symmetric):对于任何非null的引用值x和y,当且仅当y.equals(原创 2017-03-30 15:09:57 · 237 阅读 · 0 评论 -
(7):避免使用终结方法
finalize()方法一般来说是不可预测的,也是很危险的,一般情况下是不必要的。终结方法的缺点在于不能保证被及时的执行,甚至不会执行。finalize的工作原理是这样的:一旦垃圾收集器准备好释放对象占用的存储空间,它首先调用finalize(),而且只有在下一次垃圾收集过程中,才会真正回收对象的内存.所以如果使用finalize(),就可以在垃圾收集期间进行一些重要的清除或清扫工作.而程序员无法原创 2017-03-30 14:45:55 · 250 阅读 · 0 评论 -
(6):消除过期的对象引用
java有自己的垃圾回收功能,这大大减少了程序员的负担。但对于有些被引用的对象,即使我们之后再也不使用它,它也不会被回收,这就是所谓的内存泄漏。下面是一个简单的栈实现例子:public class Stack { private Object[] elements; private int size = 0; private static final i原创 2017-03-30 10:25:54 · 364 阅读 · 0 评论 -
(5):避免创造不必要对象
一般来说,最好是能重用已有的对象,而不是每次有需要的时候就去创建一个相同功能的对象。比较下面两个例子(1).String s1 = “string1”;(2).String s2 = new String(“string1”);(2)比(1)多创造出了许多不必要的对象。由于String被实现为不可变对象,JVM底层将其实现为常量池,既所有值等于"string1" 的St原创 2017-03-29 16:44:10 · 240 阅读 · 0 评论