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

类的一个实例,通常使用类的公有的构造方法获取。也可以为类提供一个公有的静态工厂方法(不是设计模式中的工厂模式)来返回类的一个实例。例如:


//将boolean类型转换为Boolean类型
public static valueOf(boolean b) {
return b ? Boolean.TRUE : Boolean.FALSE;
}

使用静态工厂方法代替构造器的优势:

  • 静态工厂方法有名称,更易读。静态工厂方法能够使用方法名称进行自注释,来描述被返回的对象。例如:BigInteger.probablePrime的静态方法表示返回的BigInteger为素数。当一个类需要多个带有相同签名(参数列表仅在参数类型的顺序上有所不同)的构造器时,应使用静态工厂方法并且慎重的选择名称以便突出它们的区别。

  • 静态工厂方法能够为重复的调用返回相同的对象。将创建好的对象缓存起来,重复利用。

  • 静态工厂方法可以返回原返回类型的任何子类型的对象。例如:在基于接口的框架(通过接口来引用对象)中,为了隐藏类的实现,通常会使用API返回对象的实例。由于接口中不能有静态方法,通常把静态工厂方法放在实现了接口的不可实例化类(private构造函数)中。

  • 创建参数化类型实例时,静态工厂方法使代码变的更简洁。(java 8加入泛型的推导后,优势不再)


Map<String, List<String>> map = new HashMap<String, List<String>>(); // java 1.7之前必须这样写,前后两次都要写泛型列表
Map<String, List<String>> map = new HashMap<>(); // java 1.8可以这样写 //静态工厂方法
//HashMap类中加入
public static <K, V> HashMap<K, V> newInstance() {
return new HashMap<K, V>();
}
//使用
Map<String, List<String>> map = HashMap.newInstance();

缺点:

  • 类如果不含有public或protected型的构造器,则不能被继承。

2、遇到多个构造器参数时考虑用构建器

当有多个构造器参数(其中一些为可选参数)时,代码的编写通常有几种方式:

  • 使用重叠的构造器,根据可选参数提供不同的构造器。

  • 使用JavaBean模式:先调用一个无参的构造器来创建对象,然后使用setter方法来设置必要的参数。

当参数很多时,使用重叠的构造器代码的编写将很繁琐,难以阅读,同时调用时容易出错。而是用JavaBean模式,因为对象的构造过程被分到了几个调用中,在构造过程中JavaBean处于不一致状态。试图使用(特别是在多线程中)不一致状态的对象,将导致错误并且很难调试。并且JaveBean模式阻止了将类做成不可变的可能。

这时可以考虑使用构建器(Builder模式),不直接生成想要的对象,而是通过Builder对象来构造对象。

代码:


public class AlertDialog { private final String title; private final String message; private final boolean cancelable; private AlertDialog(Builder builder) {
this.title = builder.title;
this.message = builder.message;
this.cancelable = builder.cancelable;
} public static class Builder { private final String title; private String message; private boolean cancelable; public Builder(String title) {
this.title = title;
} public Builder setMessage(String message) {
this.message = message;
return this;
} public Builder setCancelable(boolean cancelable) {
this.cancelable = cancelable;
return this;
} public AlertDialog create() {
return new AlertDialog(this);
}
} public String toString() {
return "title: " + title + ", message: " + message + ", cancelable: " + cancelable;
} public static void main(String[] args) {
AlertDialog dialog = new AlertDialog.Builder("地点")
.setMessage("知识的荒漠")
.setCancelable(true).create();
System.out.println(dialog);
}
}

Builder模式的优点:

  • 比JavaBean更安全,比重叠的构造器更易于阅读和编写

  • 适用于参数较多,且大多数参数可选的情况

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

实现Singleton的几种方式:

方式一:饿汉方式,线程安全


public class Singleton {
private static final Singleton singleton = new Singleton(); private Singleton() {} public static Singleton getInstance() {
return singleton;
}
}

这种方式基于classloader机制避免了线程同步的问题。但singleton在类被装载时立即实例化,没有实现类的延时加载(lazy loading)。另外这种方式可以通过反射机制(借助Class对象的setAccessible方法)来生成对象。

方式二:懒汉方式,线程不安全


public class Singleton {
private static final Singleton singleton = null; private Singleton() {} public static Singleton getInstance() {
if(singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}

这种方式实现了类的延时加载,但致命的是多线程下不安全。

方式三:双重校验锁,线程安全


public class Singleton {
private volatile static Singleton singleton = null;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}

方式四:枚举,线程安全,可避免反序列化时重新创建对象,推荐使用


public enum Singleton {
INSTANCE; private Singleton() {}
.....
} //使用
Singleton single = Singleton.INSTANCE;

这种方式可自由序列化,可有效避免反序列化时创建多个对象。保证只有一个实例(即使反射机制也无法多次实例化一个枚举量),线程安全。缺点是使用的人较少(java1.5后才有enum类型),失去了类的一些特性。单元素的枚举型是实现Singleton的最佳方式

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

编写一些只包含静态方法或静态域的类(工具类)时,可以使用private构造器来使类不能被实例化。这些类不能被继承。(因为子类的构造器都要显示或隐式调用父类的构造器)。自己编写的工具类要使用这种方法


public class UtilityClass {
private UtilityClass() {
throw new AssertionError(); //断言,确保不会在类内部被调用
}
.....
}

5、避免创建不必要的对象

重用对象而不是每次需要时创建一个相同的新对象,这可以显著程序提高性能。对于不可变的对象应始终重用,对于可变的对象应尽量重用。

例如:


String str1 = "abc";
String str2 = new String("abc");
String str3 = new String("abc");

内存模型:

new String("abc")每次执行都会创建一个新的String实例,在堆中分配一个新的空间。而使用String str1 = "abc"每次调用都将指向常量池中同一个常量,不会产生新的实例。

对于可变对象,那些一旦计算出来就不再变化的子对象或常量可单独提取到静态域中。


class Person {
private final Date birthDate;
private static final Date BOOM_STATE; //第一次初始化后将不改变,所有对象共用
private static final Date BOOM_END; static {
Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);
BOOM_STATE = gmtCal.getTime();
gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);
BOOM_END = gmtCal.getTime();
} public boolean isBabyBoomer() {
return birthDate.compareTo(BOOM_STATE) >= 0 && birthDate.compareTo(BOOM_END) < 0;
}
}

当心无意识的自动装箱,要优先使用基本类型而不是包装类

例如:计算所有int正值的和


Long sum = 0L; //应使用基本类型long
for(long i=0; i<Integer.MAX_VALUE; i++ ) {
sum += i; //自动装箱,将创造2^32个多余的Long实例,降低性能
}
System.out.println(sum);

6、消除过期的对象引用

过期引用是指永远也不会再被解除的引用。在java中内存泄漏是隐藏的(无意识的对象保持)。如果一个对象的引用被无意识的保留下来,那么垃圾回收机制不会回收这个对象及这个对象所持有的所有对象。

消除过期引用最好的方法是让包含该引用的变量结束其生命周期。只要类自己管理内存,就应该小心内存泄漏问题。

	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 o) {
ensureCapacity();
elements[size++] = o;
} public Object pop() {
if(size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; //【避免内存泄漏】
return result;
} private void ensureCapacity() {
if(elements.length == size) {
elements = Arrays.copyOf(elements, 2*size + 1);
}
}
}

对于垃圾回收器而言,elements数组中的所有对象的引用都同等有效,它无法区分哪些可以回收哪些不能回收。

内存泄漏的常见来源:

  • 数组

  • 缓存。使用WeakHashMap代表缓存,对于复杂的缓存必须使用java.lang.ref

  • 监听器及其回调。要及时取消注册,可以使用弱引用

7、避免使用终结方法

终结方法finalizer()为类Object中的方法,当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。

  • 终结方法是不可预测的,通常不要直接调用。

  • Java语言规范不保证终结方法会被立即执行,所以不应该依赖终结方法来更新重要的持久状态。

  • 终结方法可用作安全网或终止非关键的本地资源(记住调用super.finalize())

Effective java笔记(一),创建与销毁对象的更多相关文章

  1. Effective Java笔记一 创建和销毁对象

    Effective Java笔记一 创建和销毁对象 第1条 考虑用静态工厂方法代替构造器 第2条 遇到多个构造器参数时要考虑用构建器 第3条 用私有构造器或者枚举类型强化Singleton属性 第4条 ...

  2. [Effective Java]第二章 创建和销毁对象

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  3. 《Effect Java》学习笔记1———创建和销毁对象

    第二章 创建和销毁对象 1.考虑用静态工厂方法代替构造器 四大优势: i. 有名称 ii. 不必在每次调用它们的时候都创建一个新的对象:   iii. 可以返回原返回类型的任何子类型的对象: JDBC ...

  4. 《Effective Java》读书笔记 - 2.创建和销毁对象

    Chapter 2 Creating and Destroying Objects item 1:Consider static factory methods instead of construc ...

  5. 【Effective Java读书笔记】创建和销毁对象(一):考虑使用静态工厂方法代替构造器

    类可以提供一个静态方法,返回类的一个静态实例,如Boolean包装类的一个获取实例的静态方法 public static Boolean valueOf(boolean b) { return (b ...

  6. 【Java基础】创建和销毁对象

    Num1:考虑用静态工厂方法代替构造器 对于类而言,常见的方法是提供一个公有的构造器,但其实还有一种方法叫做静态工厂方法(static factory method),它只是一个返回类的实例静态方法. ...

  7. Effective Java 读书笔记之一 创建和销毁对象

    一.考虑用静态工厂方法代替构造器 这里的静态工厂方法是指类中使用public static 修饰的方法,和设计模式的工厂方法模式没有任何关系.相对于使用共有的构造器来创建对象,静态工厂方法有几大优势: ...

  8. Effective Java 学习笔记之创建和销毁对象

    一.考虑用静态工厂方法代替构造器 1.此处的静态工厂方法是指返回指为类的对象的静态方法,而不是设计模式中的静态工厂方法. 2.静态工厂方法的优势有: a.使用不同的方法名称可显著地表明两个静态工厂方法 ...

  9. 《Effective Java》读书笔记(一)之创建和销毁对象

    最近在研读<Effective Java>一书,读书不做点笔记,感觉很容易就忘掉,于是用本篇博客来记录阅读此书的笔记. 郑重声明: 由于是<Effective Java>一书的 ...

  10. Effective Java学习笔记--创建和销毁对象

    创建和销毁对象 一.静态工厂方法代替构造器 静态工厂方法的优缺点 优点: 1.可以自定义名称(可以将功能表述的更加清晰) 2.不必每次调用都创建新的对象(同一对象重复使用) 3.返回的类型可以是原返回 ...

随机推荐

  1. python10作业思路及源码:类Fabric主机管理程序开发(仅供参考)

    类Fabric主机管理程序开发 一,作业要求 1, 运行程序列出主机组或者主机列表(已完成) 2,选择指定主机或主机组(已完成) 3,选择主机或主机组传送文件(上传/下载)(已完成) 4,充分使用多线 ...

  2. 虚拟机VMware12.05下安装Ubuntu16.04几个关键地方

    在踩了自己按照网上的教程安装Ubuntu之后,仍然踩了不少坑,鼓捣了一段时间,才达到自己想要的界面.   下面就来说说,大家可能也会遇到的情况:   1.安装ISO镜像时候,路径直接选择 你从Ubun ...

  3. 使用四元数解决万向节锁(Gimbal Lock)问题

    问题 使用四元数可以解决万向节锁的问题,但是我在实际使用中出现问题:我设计了一个程序,显示一个三维物体,用户可以输入绕zyx三个轴进行旋转的指令,物体进行相应的转动. 由于用户输入的是绕三个轴旋转的角 ...

  4. Angular2笔记:NgModule

    Angular的模块的目的是用来组织app的逻辑结构. 在ng中使用@NgModule修饰的class就被认为是一个ng module.NgModule可以管理模块内部的Components.Dire ...

  5. MapReduce剖析笔记之七:Child子进程处理Map和Reduce任务的主要流程

    在上一节我们分析了TaskTracker如何对JobTracker分配过来的任务进行初始化,并创建各类JVM启动所需的信息,最终创建JVM的整个过程,本节我们继续来看,JVM启动后,执行的是Child ...

  6. ABP源码分析七:Setting 以及 Mail

    本文主要说明Setting的实现以及Mail这个功能模块如何使用Setting. 首先区分一下ABP中的Setting和Configuration. Setting一般用于需要通过外部配置文件(或数据 ...

  7. ABP源码分析四十二:ZERO的身份认证

    ABP Zero模块通过自定义实现Asp.Net Identity完成身份认证功能, 对Asp.Net Identity做了较大幅度的扩展.同时重写了ABP核心模块中的permission功能,以实现 ...

  8. 从Fiddler抓包到Jmeter接口测试(简单的思路)

    备注:本文为博主的同事总结的文章,未经博主允许不得转载. Fiddler下载和配置安装 从网上下载fiddler的安装包即可,直接默认,一直点击下一步,直至安装完成. 安装完成后直接打开Fiddler ...

  9. iOS-开发进阶

    iOS完整学习路线图 iOS进阶介绍: 一.iOS-常用的第三方框架的介绍 二.iOS-提高iOS开发效率的方法和工具 三.常用的调试方法 1.iOS-调试技巧 2.iOS-Xcode的使用技巧 四. ...

  10. 解析大型.NET ERP系统 通用附件管理功能

    大型系统具备一个通用的附件管理功能,对于单据中无法清晰表达的字段,用一个附件图片或附件文档表示是最好的方法了.比如物料清单附加一张CAD图纸,销售订单评审功能中附加客户的各种表格,通用附件功能对系统起 ...