Effective Java读后感

《Effective Java》读后感

1       创建和销毁对象

1.1    考虑用静态工厂方法代替构造器

静态工厂方法优点:

  • 静态工厂方法与构造器(构造方法)不同的第一大优势在于,它们有名称。见名知意,突出区别。
  • 静态工厂方法与构造器不同的第二大优势在于,不必在每次调用它们的时候都创建一个新对象。
  • 静态工厂方法与构造器不同的第三大优势在于,它们可以返回原返回类型的任何子类型的对象。
  • 静态工厂方法与构造器不同的第四大优势在于,在创建参数化类型实例的时候,它们使代码变得更加简洁。

例如:Map<String,List<String>> map=newHashMap<String,List<String>>();

假设HashMap提供了静态工厂方法:(目前是没有该静态方法)

public static <K,V> HashMap<K,V> newInstance(){

return newHashMap<K,V>();

}

上面的代码就可以简写为:Map<String,List<String>> map= HashMap.newInstance();

静态工厂方法缺点:

主要缺点在于,类如果不含公有的或者受保护的构造器,就不能被子类化;

第二个缺点在于,它们与其他的静态方法实际上没有任何区别。

1.2    遇到多个构造器参数时要考虑用构建器

假如我们的一个实体类有很多的属性值,但是这些属性值又是可选的。如果我们遇到这样的是类,如何设计出方便的实体类呢?

  • 通常解决办法一:重叠构造器

复制代码

public class User {

private String id; // id(必填)

private String name; // 用户名(必填)

private String email; // 邮箱(可选)

private int age; // 年龄(可选)

private String phoneNumber; // 电话(可选)

private String address; // 地址(可选)

public User(String id, String name) {

this(id, name, "qq.com", 0, "120", "广州");

}

public User(String id, String name, String email) {

this(id, name, email, 0, "120", "广州");

}

public User(String id, String name, String email, int age) {

this(id, name, email, age, "120", "广州");

}

public User(String id, String name, String email, int age, StringphoneNumber) {

this(id, name, email, age, phoneNumber, "广州");

}

public User(String id, String name, String email, int age, StringphoneNumber, String address) {

this.id = id;

this.name = name;

this.email = email;

this.age = age;

this.phoneNumber = phoneNumber;

this.address = address;

}

public String getId() {

return id;

}

public String getName() {

return name;

}

public String getEmail() {

return email;

}

public int getAge() {

return age;

}

public String getPhoneNumber() {

return phoneNumber;

}

public String getAddress() {

return address;

}

}

注:许多你不想设置的参数,但是还不得不为他们传递值

  • 通常解决办法二: JavaBean模式

调用一个无参构造器来创造对象,然后调用setter方法来设置每个必须的参数,以及每个相关的可选参数

复制代码

public class User {

private String id; // id(必填)

private String name; // 用户名(必填)

private String email; // 邮箱(可选)

private int age; // 年龄(可选)

private String phoneNumber; // 电话(可选)

private String address; // 地址(可选)publicUser() {

super();

}

public void setId(String id) {

this.id = id;

}

public void setName(String name) {

this.name = name;

}

public void setEmail(String email) {

this.email = email;

}

public void setAge(int age) {

this.age = age;

}

public void setPhoneNumber(String phoneNumber) {

this.phoneNumber = phoneNumber;

}

public void setAddress(String address) {

this.address = address;

}

public String getId() {

return id;

}

public String getName() {

return name;

}

public String getEmail() {

return email;

}

public int getAge() {

return age;

}

public String getPhoneNumber() {

return phoneNumber;

}

public String getAddress() {

return address;

}

@Override

public String toString() {

return "User [id=" + id + ", name=" + name + ",email=" + email + ", age=" + age + ", phoneNumber="

+ phoneNumber + ",address=" + address + "]";

}

}

注:JavaBeans模式自身有着很严重的缺点。因为构造过程被分到几个调用中,在构造过程中JavaBean可能处于非一致的状态。JavaBeans模式阻止了把类做成不可变的可能,这就需要确保他的线程安全。

  • 解决办法三:构建器

复制代码

public class User {

private String id;                // id(必填)

private String name;            // 用户名(必填)

private String email;            // 邮箱(可选)

private int age;                // 年龄(可选)

private String phoneNumber;     //电话(可选)

private String address;         //地址(可选)

public static class Builder{

private String id;                // id(必填)

private String name;            // 用户名(必填)

private String email;            // 邮箱(可选)

private int age;                // 年龄(可选)

private String phoneNumber;     //电话(可选)

private String address;         //地址(可选)

public Builder(String id, String name) {

super();

this.id = id;

this.name = name;

}

public Builder email(String email){

this.email = email;

return this;

}

public Builder age(int age){

this.age = age;

return this;

}

public Builder phoneNumber(String phoneNumber){

this.phoneNumber = phoneNumber;

return this;

}

public Builder address(String address){

this.address = address;

return this;

}

public User builder(){

return new User(this);

}

}

private User(Builder builder){

this.id = builder.id;

this.name = builder.name;

this.email = builder.email;

this.age = builder.age;

this.phoneNumber = builder.phoneNumber;

this.address = builder.address;

}

@Override

public String toString() {

return "User [id=" + id + ", name=" + name + ",email=" + email + ", age=" + age + ", phoneNumber="

+ phoneNumber + ",address=" + address + "]";

}

public String getId() {

return id;

}

public String getName() {

return name;

}

public String getEmail() {

return email;

}

public int getAge() {

return age;

}

public String getPhoneNumber() {

return phoneNumber;

}

public String getAddress() {

return address;

}

}

注:不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造器(或者静态工厂),得到一个builder对象。然后客户端在builder对象上调用类似于setter的方法,来设置每个相关的可选参数。最后,客户端调用无参的build方法来生成不可变的对象。

测试代码

public static void main( String[] args )

{

User user = new User.Builder(UUID.randomUUID().toString(),"parry").address("广州").builder();

System.out.println(user.toString());

}

1.3    用私有构造器或者枚举类型强化Singleton属性

1.4    通过私有构造器强化不可实例化的能力

1.5    避免创建不必要的对象

  • 例如:Strings=new String(“abc”); // Don’t do this

该语句每次被执行的时候都创建一个新的String实例,但是这些创建对象的动作全都是不必要的。传递给String构造器的参数(”abc”),本身就是一个String实例,功能方面等同于构造器创建的所有对象。如果这个方法在一个循环中,就会创建成千上万的不必要的String实例。

正确的写法:String s=”abc”;

  • 在Java1.5版本的新特性中,有一种创建多与对象的新方法:自动装箱!

例如:

Long sum=0L;

for(long i=0; i<Integer.MAX_VALUE; i++){

sum+= i;

}

system.out.println(sum);

执行以上代码,耗时大约43秒;

这段程序算出的答案是正确的,但是比实际情况要更慢一些,只因为变量sum使用的Long类型,而不是long,这意味着程序构造了大约2的31次方个多余的Long实例。如果将Long改为基本类型long。程序运行时长为6.8秒附近。

结论很明显:优先使用基本类型而不是装箱基本类型,要当心无意识的自动装箱。

1.6    消除过期的对象引用

内存泄漏:如果一个栈先是增长,然后再收缩,那么从栈中弹出来的对象将不会被当做垃圾回收,即使使用栈的程序不再引用这些对象,它们也不会被回收。因为,栈内部维护着对这些对象的过期引用(obsolete reference)。所谓的过期引用,是指永远也不会再被解除引用。

出现内存泄漏的三种需要注意的情况:

一:类是自己管理内存;

二:缓存; 把对象放入缓存中,很容易被遗忘。

三:监听器和其他回调。

1.7    避免使用终结方法

2       对于所有对象都通用的方法

尽管Object是一个具体类,但是设计它主要是为了扩展。它的所有非final方法(equals、hashCode、toString、clone和finalize)都用明确的通用约定,因为它们被设计成要被覆盖(override)的。

2.1    覆盖equals时请遵守通用约定

equals方法实现了等价关系:

  • 自反性(reflexive):对于任何非null的引用值x,x.equals(x)必须返回true
  • 对程序(symmetric):对于任何非null的引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)必须返回true
  • 传递性(transitive):对于任何非null的引用值x、y和z,如果x.equals(y)返回true,且y.equals(z)返回true,那么x.equals(z)必须返回true
  • 一致性(consistent):对于任何非null的引用值x和y,只要equals的比较操作再对象中所有的信息没有被修改,多次调用x.equals(y)就会一致地返回true,或者一致的返回false
  • 非空性(Non-nullity):对于任何非null的引用值x,x.equals(null)必须返回false
  • 覆盖equals时总要覆盖hashCode

引用java API,关于Object中hashCode方法的一段话:

返回该对象的哈希码值。支持此方法是为了提高哈希表(例如 java.util.Hashtable 提供的哈希表)的性能。

hashCode 的常规协定是:

  • 在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
  • 如果根据equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。
  • 如果根据equals(java.lang.Object) 方法,两个对象不相等,那么对这两个对象中的任一对象上调用 hashCode 方法不要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。
  • 始终要覆盖toString

  • 谨慎覆盖clone

  • 考虑实现Comparable接口

3       类和接口

3.1    使类和成员的可访问性最小化

对于成员(域、方法、嵌套类和嵌套接口)有四种可访问级别:下面根据可访问性递增顺序说明:

  • 私有的(private):只有在声明该成员的顶层类内部才可以访问这个成员;
  • 包级私有(package-private):声明该成员的包内部的任何类都可以访问这个成员。从技术上讲,它被称为“缺省访问级别”;
  • 受保护的(protected):声明该成员的类的子类可以访问这个成员(但是有一些限制[JLS,6.6.2])。
  • 公有的(public):在任何地方都可以访问。
  • 在公有类中使用访问方法而非公有域

  • 使可变性最小化

为了使类称为不可变,要遵循下面五条规则:

1,  不要提供任何会修改对象状态的方法;

2,  保证类不会被扩展;

3,  使所有的域都是final的;

4,  使所有的域都称为私有的;

5,  确保对于任何可变组件的互斥访问

3.4    复合优先于继承

3.5    要么为继承而设计,提供说明文档,要么就禁止继承

3.6    接口优于抽象类

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

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

接口允许我们构造非层次结构的类型框架。

3.7    接口只用于定义类型

3.8    类层次优于标签类

3.9    用函数对象表示策略

例如:比较器函数代表一种为元素排序的策略。

3.10        优先考虑静态成员类

4       泛型

4.1    请不要在新代码中使用原生态类型

4.2    消除非受检警告

4.3    列表优先于数组

4.4    优先考虑泛型和泛型方法

4.5    利用有限制通配符来提升API的灵活性

4.6    优先考虑类型安全的异构容器

5       枚举和注解

5.1    用enum代替int常量

5.2    用实例域代替序数

5.3    用EnumSet代替位域

5.4    用EnumMap代替序数索引

5.5    注解优先于命名模式

5.6    用标记接口定义类型

6       方法

6.1    检查参数的有效性

6.2    必要时进行保护性拷贝

6.3    谨慎设计方法签名

谨慎地选择方法的名称;见名知意

不要过于追求提供便利的方法;

避免过长的参数列表;

6.4    慎用重载

6.5    慎用可变参数

6.6    返回零长度的数组或者集合,而不是null

6.7    为所有导出的API元素编写文档注释

7       通用程序设计

7.1    将局部变量的作用域最小化

7.2    for-each循环优先于for循环

无法使用for-each循环的三种情况:

1,过滤:如果需要遍历集合,并删除选定的元素,就需要使用显式迭代器,以便可以调用它的remove方法;

2,转换:如果需要遍历列表或者数组,并取代它部分或者全部的元素值,就需要列表迭代器或者数组索引,以便设定元素的值

3,平行迭代:如果需要并行的遍历多个集合,需要显式的控制迭代器或者索引变量,以便所有迭代器或者索引变量都可以得到同步前移

以上情况,需要使用普通for循环。

7.3    了解和使用类库

7.4    精确的答案,避免使用float和double

精确计算,推荐使用BigDecimal(性能会相对慢一些)

7.5    基本类型优先于装箱基本类型

7.6    如果使用其他类型更合适,避免使用string类型

字符串不适合代替其他的值类型;

字符串不适合代替枚举类型;

字符串不适合代替聚集类型;

字符串不适合代替能力表;

7.7    当心字符串连接的性能

为了获得可以接收的性能,请使用StringBuilder或StringBuffer代替String进行字符串的拼接。

7.8    通过接口引用对象

7.9    接口优先于反射机制

使用反射机制的缺点:

1,丧失了编译时类型检查的好处,包括异常检查;

2,执行反射访问所需要的代码非常笨拙和冗长。

2,性能损失。反射方法调用比普通方法调用慢了很多。

7.10        谨慎的调用本地方法

7.11        谨慎的进行优化

不要去计较效率上的一些小小的得失,在97%的情况下,不成熟的优化才是一切问题的根源。

在优化方面,我们应该遵守两条规则:

1,  不要进行优化;

2,  还是不要进行优化;也就是说在你没有绝对清晰的优化方案之前,请不要进行优化。

总结:不要费力编写快速的程序,应该努力编写好的程序,速度自然随之而来。

7.12        遵守普遍接受的命名惯例

8       异常

8.1    只针对异常的情况才使用异常

8.2    对可恢复的情况使用受检异常,对编程错误使用运行时异常

8.3    避免不必要的使用受检异常

8.4    优先使用标准的异常

8.5    抛出与抽象相对应的异常

8.6    每个方法抛出的异常都要有文档或注释

8.7    在细节消息中包含能捕获失败的信息

8.8    努力让失败保持原子性

8.9    不要忽略异常

9       并发

9.1    同步访问共享的可变数据

9.2    避免过度同步

9.3    executor和task优先于线程

9.4    并发工具优先于wait和notify

9.5    线程安全性的文档化

9.6    慎用延迟初始化

9.7    不要依赖线程调度器

9.8    避免使用线程组

10  序列化

10.1        谨慎实现Serializable接口

缺点是:

1,  类实现该接口之后,一旦一个类被发布,就大大降低了“改变该类的实现”的灵活性。

2,  它增加了出现bug和安全漏铜的可能性;

3,随着类发行新的版本,相关的测试负担也增加了。

10.2        考虑使用自定义的序列化形式

10.3        保护性地编写readObject方法

Mutableperiod攻击。

编写更加健壮的readObject方法:

  • 对于对象引用域必须保持为私有的类,要保护性的拷贝这些域中的每个对象。不可变类的可变组件就属于这一类别;
  • 对于任何约束条件,如果检查失败,则抛出一个InvalidObjectException异常。这些检查动作应该跟在所有的保护性拷贝之后。
  • 如果整个对象图在被反序列化之后必须进行验证,就应该使用ObjectInputValidation接口
  • 无论是直接还是间接方式,都不用调用类中任何可被覆盖的方法
  • 对于实例控制,枚举类型优先于readResolve

如果依赖readResolve进行实例控制,带有对象引用类型的所有实例域则都必须声明为tansient的。否则,会被Mutableperiod攻击。

10.5        考虑用序列化代理代替序列化实例

时间: 2024-11-06 22:39:05

Effective Java读后感的相关文章

effective java 读后感

think in java  , effective java  这两本书一直都在java的生态圈中经久不衰.本来想着先翻过 think in java 这本大山,但是读到一半就放弃了.过长的篇幅,让人难以忍受,再大的兴趣,也被过多繁琐的文字所磨灭了,但是不可否认里面涵盖了许许多多经典的java知识,可能要等我再成熟一点,多一点耐心,再回来翻这本书吧. 在两三年前我买了 这本 effective java 来自google首席架构师 joshua bloch 的经典之作,当时我接触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月份的自己吧,希望在这半年的时间内完成对自己的蜕