Effective Java学习笔记
创建和销毁对象
第一条:考虑用静态工厂方法替代构造器
For example:
public static Boolean valueOf(boolean b){
return b ? Boolean.TRUE : Boolean.FALSE;
}
优势:
- 有名称
- 不必在每次调用它们的时候都创建一个新对象
- 它们可以返回原返回类型的任何子类型的对象
- 在创建参数化类型实例的时候,他们使代码变得更加简洁
缺点:
- 类如果不含公有的或者受保护的构造器,就不能被子类化
- 它们与其他的静态方法实际上没有任何区别
第二条:遇到多个构造器参数时要考虑用构造器
对于多个参数的构造器或者静态方法,一般习惯采用重叠构造器(telescoping constructor)模式
//Telescoping constructor pattern
public class PizzaIngredients{
private final int salt; //optional
private final int chess; //o
private final int sausage; //o
private final int flour; // required
private final int water; //r
... public PizzaIngredients(int flour, int water){
this(flour, water, 0);
}
public PizzaIngredients(int flour, int water, int salt){
this(flour, water, salt, 0);
}
public PizzaIngredients(int flour, int water, int salt, int chess){
this(flour, water, salt, chess, 0);
}
public PizzaIngredients(int flour, int water, int salt, int chess, int sausage){
this.flour = flour;
this.water= water;
this.salt= salt;
this.chess= chess;
this.sausage= sausage;
}
}
如果构造参数太多这种方式代码也会变得很难编写,且不易阅读。
第二种方式是采用JavaBeans模式,即设置每个参数的默认值,并给每个field加上setter方法,要设置某个值时直接调用其对应的setter方法。但是JavaBean在构造过程中可能会处于不一致状态,并且JavaBeans模式阻止了把类做成不可变的可能,这就需要付出额外的努力来确保它的线程安全。
第三种替代方法是Builder模式。客户端利用必要的参数得到一个builder对象,在builder对象上调用类似setter的方法,来设置每个相关的可选参数,最后调用无参的build方法来生成不可变对象。
public class PizzaIngredients{
private final int salt; //optional
private final int chess; //o
private final int sausage; //o
private final int flour; // required
private final int water; //r
...
public static class Builder{
// required parameters
private final int flour;
private final int water;
// optional
private int salt = 0;
private int chess = 0;
private int sausage = 0;
public Builder(int flour, int water){
this.flour = flour;
this.water= water;
}
public Builder salt(int val){
salt = val;
return this;
}
public Builder chess(int val){
chess = val;
return this;
}
public Builder sausage (int val){
sausage = val;
return this;
}
public PizzaIngredients build(){
return new PizzaIngredients (this)
}
}
private PizzaIngredients(Builder builder){
flour = builder.flour;
water = builder.water;
salt = builder.salt;
chess = builder.chess;
sausage = builder.sausage;
}
}
// 客户端代码
PizzaIngredients pizza = new PizzaIngredients.Builder(200, 150).salt(5).chess(20).sausage(25).build();
Builder模式优势在于参数可变,而且不需要考虑顺序。但是Builder模式比重叠构造器模式更加冗长,最好在很对参数是使用。
第三条:用私有构造器或者枚举类型强化Singleton属性
三种方式实现Singleton:
1. 公有静态成员是final域
//Singleton with public final field
public class Wills{
public static final Wills INSTANCE = new Wills();
private Wills(){
};
...
}
2. 公有成员是静态工厂方法
//Singleton with static factory
public class Wills{
private static final Wills INSTANCE = new Wills();
private Wills(){
};
public static Wills getInstance(){
return INSTANCE;
}
...
}
这两种方式在存在借助AccessibleObject.setAccessible方法,通过方式机制调用私有方法的可能。为抵御这种攻击,可以修改构造器,在实例被第二次创建时抛出异常。
并且每次反序列化一个序列化的实例时,都会创建一个新的实例,这需要声明所有实例域都是transient,并提供一个readResolve方法。
3. 单元素枚举类型
// Enum singleton - the preferred approach
public enum Wills{
INSTANCE;
...
}
这种方法在功能上类似公有域方法,但它更加简洁而且无偿的提供了序列化机制,绝对防止多次实例化。单元素枚举类型已经成为实现Singleton的最佳方法。
第四条:通过私有构造器强化不可实例化的能力
不需要被实例化的类,可以通过私有构造器使其不能被实例化。
Public class UtilityClass {
private UtilityClass(){
throw new AssertionError();
}
}
第五条:避免创建不必要的对象
除了不可变的对象,也可以重用已知不会修改的对象。
可以使用静态初始化器(initializer)来初始化代码块。
Public class Person{
private final static String name;
private final static String gender;
static {
Calendar cal = Calendar.getInstance(Timezone.getTimeZone("GMT"));
...
...
}
第六条:消除过期的对象引用
public class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack(){
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
public void push(Object e){
ensureCapacity();
elements[size++] = e;
}
public Object pop(){
if(size == 0)
throw new EmptyStackException();
return elements[--size];
}
private void ensureCapacity() {
if(elements.length == size){
elements = Arrays.copyOf(elements, 2 * DEFAULT_INITIAL_CAPACITY + 1);
}
}
}
这段代码中并没有明显的错误,但是这个程序中隐藏了一个问题,有一个“内存泄漏”,随着垃圾回回收器活动的增加,或者由于内存占用的不断增加,程序性能的降低会逐渐表现出来。
如果一个栈先是增长,然后再收缩,那么从栈中弹出来的对象将不会被当做垃圾回收,即使使用栈的程序不再引用这些对象,他们也不会被回收。这是因为,栈内部维护着对这些引用的过期引用(obsolete reference)。所谓过期引用,是指永远也不会再被解除的引用。修复这类问题的方法很简单,一旦对象引用已经过期,只需清空这些引用即可。对于本例,如下修改:
public Object pop(){
if(size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null;
return result;
}
清空对象引用应该是一种例外,而不是一种规范行为。
一般而言,只要是自己管理内存,程序员就应该警惕内存泄漏问题。
内存泄漏的另一个常见来源是缓存。
内存泄漏的第三个常见来源是监听器和其他回调。
Effective Java学习笔记的更多相关文章
- Effective Java 学习笔记之第七条——避免使用终结(finalizer)方法
避免使用终结方法(finalizer) 终结方法(finalizer)通常是不可预测的,也是很危险的,一般情况下是不必要的. 不要把finalizer当成C++中析构函数的对应物.java中,当对象不 ...
- Effective Java 学习笔记之所有对象都通用的方法
一.覆盖equals时请遵守通用约定 1.满足下列任何一个条件时,不需要覆盖equals方法 a.类的每个实例本质上都是唯一的.此时就是Object中equals方法所表达的含义. b.不关心类是否提 ...
- Effective Java 学习笔记之创建和销毁对象
一.考虑用静态工厂方法代替构造器 1.此处的静态工厂方法是指返回指为类的对象的静态方法,而不是设计模式中的静态工厂方法. 2.静态工厂方法的优势有: a.使用不同的方法名称可显著地表明两个静态工厂方法 ...
- Effective Java 学习笔记----第7章 通用程序设计
第7章 通用程序设计 第29条 将局部变量的作用域最小化 使一个局部变量的作用域最小化,最有力的技术室在第一次使用它的地方声明. 第30条 了解和使用库 效率提高.如果你不知道库 ...
- effective java学习笔记之不可实例化的类
在没有显式声明一个类的构造方法时,编译器会生成默认的无参构造方法,在设计工具类时,我们通常将方法设置成静态方法,以类名.方法名的形式调用,此时这个类就没有必要创建实例,我们知道抽象类不可以被实例化,但 ...
- Effective Java学习笔记--创建和销毁对象
创建和销毁对象 一.静态工厂方法代替构造器 静态工厂方法的优缺点 优点: 1.可以自定义名称(可以将功能表述的更加清晰) 2.不必每次调用都创建新的对象(同一对象重复使用) 3.返回的类型可以是原返回 ...
- 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁
什么是同步 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条 ...
- 0035 Java学习笔记-注解
什么是注解 注解可以看作类的第6大要素(成员变量.构造器.方法.代码块.内部类) 注解有点像修饰符,可以修饰一些程序要素:类.接口.变量.方法.局部变量等等 注解要和对应的配套工具(APT:Annot ...
- Java学习笔记(04)
Java学习笔记(04) 如有不对或不足的地方,请给出建议,谢谢! 一.对象 面向对象的核心:找合适的对象做合适的事情 面向对象的编程思想:尽可能的用计算机语言来描述现实生活中的事物 面向对象:侧重于 ...
随机推荐
- PHP的几个常用函数的使用总结
1.date函数:不得不说,PHP的date函数已经基本很完备了,如果你不去读PHP的手册的话,你不会发现对日期做了那么多的支持. time() 获取当前的时间戳 data() 获取自己想要的时间格 ...
- AC自动机及trie图 pascal
; type data=record sum,failed:longint; son:array ['a'..'z'] of longint; end; ..maxn] of data; que:.. ...
- mysql5.7 zip版的配置方法
下载了最新版的mysql,发现配置后使用net start mysql 服务无法启动,花了点时间找到了解决方案,按照如下步骤就可以了,关键在于创建data文件夹以及mysqld --initializ ...
- C#常用操作类库四(File操作类)
public class FileHelper : IDisposable { private bool _alreadyDispose = false; #region 构造函数 public Fi ...
- Huffman编码(测试源代码)
1.此程序为c++程序 2.以下代码可实现手动输入,即去掉代码中的/*...*/注释符,并同时去掉赋值代码段 3.源代码 #include<iostream> using namespac ...
- 使用ActionBar Tab
使用ActionBar Tab(地址) 本文实现将页面分为多个选项卡,并在每一个选项卡中显示一个ListView. 创建新Layout - ActionbarTab.axml, 并向页面中添加Fram ...
- 深入了解C#中的静态变量和静态构造函数
深入的剖析C#中静态变量和静态构造函数: 在日常的程序开发过程经常会使用到静态变量,众所周知,静态变量时常驻内存的变量,它的生命周期是从初始化开始一直到Application结束.但是,我们经常会忽略 ...
- Matlab从一系列图片导出AVI视频,导出GIF动图
平台:Win7,Matlab 2014a 从一系列图片导出AVI视频的M代码如下: clear all; % 清除变量 % 官方示例,命令窗口输入“doc VideoWriter” writerObj ...
- AIX用chsec命令修改快捷修改配置文件
前言 AIX的所有配置设置通过一个命令来进行更改配置文件中的键-值对,以达到修改配置的目的.如:group/user/limits/passwd等等 命令格式 chsec [-f file] [-s ...
- iOS页面传值-wang
iOS页面间传值的方式(NSUserDefault/Delegate/NSNotification/Block/单例) 实现了以下iOS页面间传值:1.委托delegate方式:2.通知notific ...