effective java

1.考虑用静态工厂方法代替构造函数

对于一个类,为了让客户获得他的一个实例,最通常的方法是提供一个共有的构造函数。

实际上还有另外一种技术,尽管较少为人所知,但也应该成为每个程序员的工具箱中的一

部分,类可以提供一个公有的  静态工厂方法 。所谓静态工厂方法,实际上只是一个简单

的静态方法,他返回的是类的一个实例。

类可以提供一些静态工厂方法,来代替构造函数,或者同时也提供好一些构造函数。用静态

工厂来代替公有的构造函数,既有好处,也有不足之处。

静态工厂方法的一个好处是,与构造函数不同,静态工厂方法具有名字,返回值等,可以通过

名字就能知道代码的含义。

静态工厂方法的第二个好处是,与构造函数不同,他在被调用时,并不要求非得创建一个新的对象。

静态工厂方法的第三个好处是,与构造函数不同,他们可以返回一个原返回类型的子类型的对象。

静态工厂方法的主要缺点是,类如果不含公有的或者受保护的构造函数,就不能被子类化。

静态工厂方法的第二个缺点是,他们和其他的静态方法没有任何区别。

2.使用私有构造函数强化singleton属性

singleton是指这样的类,他只能实例化一次,singleton通常被用来代表那些本质上具有唯一性的

系统组件。

实现singleton有两种方法:这两种方法都要把构造函数保持私有的,并且提供一个静态成员,以便

允许客户能够访问该类的唯一实例,在第一种方法中,公有静态成员是一个final域:

public class Elvis{

public static final Elvis INSTANCE = new Elvis();

private Elvis(){

}

}

私有构造函数仅被调用一次,用来实例化公有的静态final域,由于缺少公有或者受保护的构造函数,所以保证了全局唯一性,一旦类被实例化之后,只有一个实例存在--不多也不少。

客户的任何行为都不会改变这一点。

第二种方法提供了一个公有的静态工厂方法,而不是共有的静态final域:

public class Elvis{

private static final Elvis INSTANCE = new Elvis();

private Elvis(){

}

public static Elvis getInstance(){

return INSTANCE;

}

}

所有对于静态方法的调用,杜辉返回同一个对象引用,所以,不会有别的实例被创建。

第一种方法的主要好处在于,组成类的成员的申明很清楚的表明了这个雷是一个singleton.

公有的静态域是final的,所以该域将总是包含相同的对象引用,第一种方法可能在性能上稍微

领先,但是在第二种方法中,一个优秀的jvm实现应该能够通过将静态工厂方法的调用内联化,来消除这种差别。

第二种方的主要好处在于,他提供了灵活性,在不改变API的前提下,允许我们改变想法,把该类做成singleton,

或者不做成singleton,singleton的静态工厂方法返回该类的唯一实例,但是他也容易被修改,比如说,为每一个

调用该方法的线程返回一个唯一的实例。

总而言之,如果你确信该类将永远是一个单例,那么使用第一种方法是有意义的,如果你希望留有余地,那么请使用

第二种方法。

为了使一个单例类变成可序列化,仅仅在申明中加上“ implements Serializable” 是不够的,为了维护单例性,你必须也要提供

一个readResolve方法,否则的话,一个序列化的实例在每次反序列化的时候,都会导致创建一个新的实例。

3.通过私有构造函数强化不可实例化的能力

企图通过将一个类做成抽象类来强化该类不能被实例化,这个是行不通的,该类可以被子类化,而子类可以被实例化。我们只要

让这个类包含单个显示的私有构造函数,那么他就不能被实例化了。

4.避免创建重复对象

5.消除过期的对象引用

6.避免使用终结函数

7.在改写equals的时候请遵守通用约定

一个类的每一个实例本质上都是唯一的。

不关心一个类是否提供了逻辑相等的测试功能。

超类已经改写了equals,从超类继承过来的行为对于子类也是合适的。

一个类时私有的,或者是包级私有的,并且可以确定他的equals方法永远也不会被调用。尽管如此,在这样的情况下,应该要改写equals方法,以免万一有一天他会被调用

public boolean equals(Object o){

throw new UnsupportedOperationException();

}

那么,什么时候应该改写equals呢?当一个类有他自己特有的逻辑相等概念,而且超类没有改写equals以实现期望的行为,这是我们需要改写,这通常适合于值类的情形。

比如Integer或者Date,程序员在利用equals方法来比较两个指向值对象的引用的时候,希望知道他们逻辑上是否相等。而不是他们是否指向同一个对象。

在改写equals方法的时候,你必须要遵守他的通用约定,下面是约定的内容,来至Object:

(1)equals方法实现了等价关系:

自反性。对于任意的引用至x,x.equals(x)一定为TRUE。

对称性。对于任意的引用值x和y,当且仅当x.equals(y)返回TRUE,那么y.equals(x)也一定返回TRUE。

传递性。对于任意的引用值x,y和z,如果x.equals(y)返回TRUE,并且y.equals(z)也返回true,那么x.equals(z)也一定返回true。

一致性。对于任意的引用值x和y,如果用于equals比较的对象信息没有被修改的话,那么多次调用要么一致返回TRUE,那么一致的返回FALSE。

对于任意的非空引用值x,x.equals(null)一定返回false.

下面是为实现高质量equals方法的一个处方:

1.使用==操作符检查“实参是否为指向对象的一个引用”。如果是的话,则返回TRUE。这只不过是一种性能的优化,如果比较操作有可能非常耗时的话,这样做的值得的。

2.使用instanceof操作符检查实参是否正确的类型。

3.把实参转换成正确的类型。因为前面已经有了instanceof测试,所以这个转换可以确保成功。

4.对于该类中每一个关键域,检查实参中的域与当前对象中对应的域值是否匹配

5.当你编写完成了equals方法后,应该问自己三个问题:他是否对称的,传递的,一致的,如果答案是否定的,那么请找到这些特性未能满足的原因,在修改代码。

当你改写equals的时候,总是要改写hashcode.

不要试图让equals方法过于聪明。

不要使equals方法依赖不可靠的资源。

不要将equals申明中的Object对象替换为其他的类型。

8.改写equals时总要改写hashCode

一个常见的错误根源在于没有改写hashcode方法,在每一个改写了equals方法的类中,你必须也要改写hashcode,如果不这样做的话,就会违反Object.hashcode的通用约定,从而导致该类无法与所有基于散列值的集合类结合在一起正常工作。

下面是hashcode约定的内容,来至java.lang.Object的规范:

(1)在一个应用程序执行期间,如果一个对象的equals方法比较所用到的信息没有被修改的话,那么对该对象调用hashcode方法多次,他必须始终如一的返回同一个整数。

(2)如果两个对象根据equals(Object)方法时相等的,那么调用这两个对象中任一个对象的hashcode方法必须产生相同的整数结果。

(3)如果两个对象根据equals方法时不相等的,那么调用这两个对象中的任一个对象的hashcode方法,不要求必须产生不同的整数结果。然而,程序员应该意识到这样的事实,对于不同的对象产生截然不同的整数结果,有可能提高散列表的性能。

不要试图从散列码计算中排除一个对象的关键部分以提高性能。

9.总是要改写toString

虽然不重要,但是一个好的toString实现,可以使一个类用起来更加愉快。

在实际应用中,toString方法应该返回对象中包含的所有令人感兴趣的信息。

不管你是否决定指定格式,都应该在文档中明确的表明你的意图。

10.谨慎改写clone

为了实现cloneable接口,使它对一个类确实产生效果,它和所有的超类都必须遵守一个相当复杂的,

不可实施的,并且基本上没有文档说明的协议,由此得到一种语言本身之外的机制:无需调用构造函数就可以

创建一个对象。

Clone方法的通用约定是非常弱的,下面是来至java.lang.Object规范中的约定内容:

创建和返回该对象的一个拷贝,这里的拷贝精确的含义取决于该对象的类,一般的含义是,对于任何对象x,表达式

x.clone() != x

将会是TRUE,并且,表达式

x.clone().getClass() == x.getClass()

将会是TRUE,但是这些都不是绝对的要求,虽然,通常情况下,表达式

x.clone().equals(x)

将会返回TRUE,但是这也不是一个绝对要求,拷贝一个对象旺旺会导致创建该类的一个新实例,但同时他也会要求拷贝内部的数据结构。

这个过程中没有调用构造函数。

实际上,clone方法是另一个构造函数,你必须确保他不会伤害到原始的对象,并且正确的建立起被克隆对象中的约束关系。

clone结构与指向可变对象的final域的正常用法是不兼容的。

11.考虑实现comparable接口

compareTo方法的通用约定与equals方法的通用约定具有相似的特征,下面是她的内容,摘自Comparable的规范:

将当前这个对象与制定的对象进行顺序比较,当该对象小于等于或者大于指定对象的时候,分别返回一个负数,零或者正整数。如果由于指定对象的

类型而使得无法进行比较,则抛出ClassCastExceptions异常。

在下面的说明中,记号sgn表示数学上的signum函数,它根据expression的值为负值,零和正值,分别返回-1,0或1.

实现者必须保证对于所有的x和y,满足sgn(x.compareTO(y))==sgn(y.compareTo(x))

实现者也必须保证这个比较关系是可传递的:x.compareTo(y)>0 && y.compareTo(z)>0,那么x.compareTo(z)>0;

最后实现者保证x.compareTo(y)==0暗示着:对于所有的z,sgn(x.compareTo(z))==sgn(y.compareTo(z));

强力建议(x.compareTo(y) ==0) == (x.equals(y)),但这不是严格要求的,一般而言,任何实现了Compareble接口的类,若违反了这个条件,应该明确的予以说明,推荐使用这样的说法,注意这个类

具有内在的排序功能,但是与equals不一致。

12.使类和成员的可访问能力最小化

经验表明,你应该尽可能的是每一个类或成员不被外界访问。

具有公有的静态final数组域几乎总是错误的,

13.支持费可变性

1.不要提供任何会修改对象的方法。

2.保证没有可被子类改写的方法。

3.使所有的域都是final的。

4.使所有的域都称为私有的。

5.保证对于任何可变组件的互斥访问。

总而言之,坚决不要为每一个get方法编写一个对应的set方法,除非有很好的理由要让一个类称为可变类,否则就应该是非可变的。

14.复合优先于继承

继承是实现代码重用的有利手段,但他并不总是完成这项工作的最佳工具。不适当的使用继承或导致脆弱的软件,在一个包的内部使用继承是非常安全的

然而,对普通的具体类进行跨越包边界的继承,则是非常危险的。

与方法调用不同的是,继承破坏了封装性。

15.要么专门为继承而设计,要么禁止继承。

16.接口优先于抽象类。

已有的类可以很容易被更新,以实现新的接口。

接口是定义mixin(混合类型)的理想选择

接口使得我们可以构造出非层次结构的类型框架

接口使得安全的增强一个类的功能成为可能。

使用抽象类来定义允许多个实现的类型,比使用接口有一个明显的优势:抽象类的演化比接口的演化比接口的演化要容易很多。如果在后续的发型版本中,你希望在抽象类中增加一个新的方法,

那么,你总是可以增加一个具体方法,他包含了一个合理的默认实现,然后,,该抽象类的所有已有的实现都自动提供了这个新的方法。对于接口,这样做是行不通的。

17.接口只是被用于定义类型

当一个类实现了一个接口的时候,这个接口被用作一个类型,通过此类型可以引用这个类的实例,因此一个类实现了一个接口,就表明客户可以对这个类的实例实施是某些动作,为了任何其他目的

而定义接口是不合适。

常量接口模式是对接口的不良使用。

effective java,布布扣,bubuko.com

时间: 2024-08-08 22:07:05

effective java的相关文章

Effective Java之内部类

Effective Java中对类的权限,接口等的要求,总结起来就是够用就行,不要赋予过多的访问权限,类的定义也是,如果某个类只会在类的内部使用,那就将该类定义为内部类吧. 内部类分为四种: 1.静态内部类:静态内部类就是在class前面多了static关键词的内部类,这种类和类的静态方法和静态变量一样,针对类本省进行操作,在静态内部类中可以随意访问其所在类的静态方法和静态变量. 2.非静态内部类:和静态内部类相对于,其实在类内部定义的所有东西只是受到访问修饰符的限制,所以非静态内部类和类的非静

【总结】Effective java经验之谈,类与接口

转载请注明出处:http://blog.csdn.NET/supera_li/article/details/44940563 Effective Java系列 1.Effective java经验之谈,创建和销毁对象 2.Effective java经验之谈,泛型 3.Effective java经验之谈,类与接口 4.Effective java经验之谈,通用方法 5.Effective java经验之谈,枚举,注解,方法,通用设计,异常 6.Effective java经验之谈,并发编程

EFFECTIVE JAVA 第十章 并发

EFFECTIVE  JAVA  第十章  并发 66.同步访问共享的可变数据 *java语言规范保证读或写一个变量是原子的(可以保证返回的值是某个线程保存在该变量中的),除非这个变量的类型为long或double.(但并不保证一个线程写入的值对于另一个线程是可见) *synchronized修饰方法.synchronized代码块可以实现同步 *volatile修饰的变量只保证读取的是主存里最新的值而不是内存中该值的拷贝,使用volatile变量必须遵循(即变量真正独立于其他变量和自己以前的值

1. effective java overview

ref: from book "effective java" This book is designed to help you be familiar with fundamental libs like java.lang, java.util. and to a lesser extent, java.util.concurrent and java.io. The book discusses other lib from time to time, but it does

[Effective Java]考虑用静态工厂方法代替构造器

本文主要介绍如何使用静态工厂方法已经在那种场合来使用这种方式代替构造方法. 众所周知,对于类而言,我们为了获得一个类的实例对象,通常情况下会提供一个公有的(public) 的构造器.当然除了这种方法以外,我们还可以通过给类提供一个public的静态工厂方法(static factory method)的方式来完成,让它返回一个类的实例. 先看一个简单的Boolean的示例,这个示例将boolean基本类型值转换成一个Boolean对象的引用. public static Boolean valu

Effective Java 读书笔记(2创建和销毁对象)

第一章是引言,所以这里不做笔记,总结一下书中第一章的主要内容是向我们解释了这本书所做的事情:指导Java程序员如何编写出清晰.正确.可用.健壮.灵活和可维护的程序. 2.1考虑用静态工厂方法代替构造器 静态工厂方法与构造器相比有四大优势: (1)静态工厂方法有名称,具有适当名称的静态工厂方法易于使用.易于阅读: (2)不必每次在调用它们的时候都创建一个新的对象: (3)可以返回原返回类型的任何子类型的对象: (4)在创建参数化类型实例的时候,它们使代码变得更加简洁. 同时静态工厂方法也有两大缺点

Effective Java通俗理解(持续更新)

这篇博客是Java经典书籍<Effective Java(第二版)>的读书笔记,此书共有78条关于编写高质量Java代码的建议,我会试着逐一对其进行更为通俗易懂地讲解,故此篇博客的更新大约会持续1个月左右. 第1条:考虑用静态工厂方法代替构造器 通常情况下我们会利用类的构造器对其进行实例化,这似乎毫无疑问.但“静态工厂方法”也需要引起我们的高度注意. 什么是“静态工厂方法”?这不同于设计模式中的工厂方法,我们可以理解它为“在一个类中用一个静态方法来返回这个类的实例”,例如: public st

《Thinking in Java》 And 《Effective Java》啃起来

前言 今天从京东入手了两本书,<Thinking in Java>(第四版) 和 <Effective Java>(第二版).都可以称得上是硬书,需要慢慢啃的,预定计划是在今年前把这两本书啃完.哈哈,可能有些人觉得时间安排不是很合理.但是出于我自身的考虑,毕竟现在是一个刚步入IT行业的实习生,公司有很多需要我学习的知识,重点应该放到这方面,在完成公司任务的基础上,利用自己的业余时间把这两本书啃完. 在这里写下这篇博客,可以说是写给12月份的自己吧,希望在这半年的时间内完成对自己的蜕

Effective java读书札记第一条之 考虑用静态工厂方法代替构造器

对于类而言,为了让客户端获取它资深的一个实例,最常用的方法就是提供一个共有的构造器.还有一种放你发,也应该子每个程序员的工具箱中占有一席之地.类可以提供一个共有的静态 工厂方法,它只是返回类的实例的静态方法. 类可以通过静态工厂方法类提供它的客户端(对象),而不是通过构造器.提这样做的好处有: 1.静态工厂方法与构造器不同的第一大优势在于,它们有名称.比如构造器BigInteger(int,int,Random)返回的BigInteger可能为素数,如果用名为BigInteger.probabl

Effective Java读书笔记(4 类和接口)

4.1 使类和成员的可访问性最小化 要区别设计良好的模块和设计不好的模块,最重要的因素在于,这个模块对于外部的其他模块而言,是否隐藏其内部数据和其他实现细节.设计良好的模块会隐藏所有的实现细节,把它的API与它的实现清晰的隔离开来,然后模块之间只通过API进行通信,一个模块不需要知道其他模块内部的工作情况,这个概念被称为信息隐藏或封装,是软件设计的基本原则之一. 4.2 在公有类中使用访问方法而非公有域 坚持面向对象程序设计思想:如果类可以在它所在的包的外部进行访问,就提供访问方法,以保留将来改