看完《Effective Java》感觉写的相当好,中间大部分都以作为参考手册,故整理出来。结合框架源码,jdk源码,自己的demo,给大家更多的示例,便于大家理解。每一条的介绍为个人理解,如果有任何不对,希望指出。对于博客有任何建议也希望给出建议。
1.用静态工厂方法代替构造器
传统创建对象通常是使用者使用构造器来完成,第一条给了另外一种方式,就是通过静态方法,返回一个对象,该对象可以每次new一个或者反复使用同一个对象,这个完全交给开发者来决定。
举个例子
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
java.lang.Integer中的valueOf方法,在初始化的时候就创建了-128~127的Integer对象,大于这个范围的就会new一个对象。创建对外不可知,开发者可以自己进行一些优化
那么这种方式由那些优势和弊端呢。
-
优势:
- 有名称,更加直观,例如
Integer.longValue()Integer.floatValue()Integer.doubleValue()能一目了然的看出区别 - 可以往复利用同一个对象(上面有例子了啊)
- 可以返回返回类型的子类型
//Collections中的源码 public static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m) { return new SynchronizedSortedMap<>(m); }static class SynchronizedSortedMap<K,V> extends SynchronizedMap<K,V> implements SortedMap<K,V> { private static final long serialVersionUID = -8798146769416483793L; ... }- 返回的子类型,可以随着版本的不同而不同
//这个是书中给的例子,这个不太好找,就用书中的了 public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) { Enum<?>[] universe = getUniverse(elementType); if (universe == null) throw new ClassCastException(elementType + " not an enum"); if (universe.length <= 64) return new RegularEnumSet<>(elementType, universe); else return new JumboEnumSet<>(elementType, universe); }- 可以返回的对象的类可以先不存在
// Worker method called by the public getConnection() methods. private static Connection getConnection( String url, java.util.Properties info, Class<?> caller) throws SQLException { /* * When callerCl is null, we should check the application's * (which is invoking this class indirectly) * classloader, so that the JDBC driver class outside rt.jar * can be loaded from here. */ ClassLoader callerCL = caller != null ? caller.getClassLoader() : null; synchronized(DriverManager.class) { // synchronize loading of the correct classloader. if (callerCL == null) { callerCL = Thread.currentThread().getContextClassLoader(); } } if(url == null) { throw new SQLException("The url cannot be null", "08001"); } println("DriverManager.getConnection(\"" + url + "\")"); // Walk through the loaded registeredDrivers attempting to make a connection. // Remember the first exception that gets raised so we can reraise it. SQLException reason = null; //在注册的驱动列表中遍历 for(DriverInfo aDriver : registeredDrivers) { // 没有加载驱动的权限就跳过 if(isDriverAllowed(aDriver.driver, callerCL)) { try { println(" trying " + aDriver.driver.getClass().getName()); Connection con = aDriver.driver.connect(url, info); if (con != null) { // 这里返回的con,可能是第三方的子类 println("getConnection returning " + aDriver.driver.getClass().getName()); return (con); } } catch (SQLException ex) { if (reason == null) { reason = ex; } } } else { println(" skipping: " + aDriver.getClass().getName()); } } // if we got here nobody could connect. if (reason != null) { println("getConnection failed: " + reason); throw reason; } println("getConnection: no suitable driver found for "+ url); throw new SQLException("No suitable driver found for "+ url, "08001"); } - 有名称,更加直观,例如
-
劣势
- 没有共有/保护的构造器,就不能被继承
- 乱取名不容易被发现及使用(要遵循统一规范,见名知意)
-
个人demo
/**
* 第一条:
* 静态工厂方法
* 优势:
* 1.创建对象的一种方式
* 2.有名称,更直观
* 3.可以往复利用同一个对象
* 4.可以返回返回类型的子类型
* 5.返回的子类型,可以随着版本的不同而不同
* 6.可以返回的对象的类可以先不存在
* eg. jdbc
* 劣势:
* 没有公有构造器,不能被子类化
* 很难发现这些接口
*/
public class StaticFactoryDemo {
private static final StaticFactoryDemo instance=new StaticFactoryDemo();
private StaticFactoryDemo(){}
public static void main(String[] args) {
Boolean.valueOf("ss");
}
public static StaticFactoryDemo getInstance(){
return instance;
}
public static StaticFactoryDemo getNewInstance(){
return new StaticFactoryDemo();
}
}
public static void main(String[] args) {
StaticFactoryDemo staticFactoryDemo=StaticFactoryDemo.getNewInstance();
System.out.println(staticFactoryDemo);
System.out.println(staticFactoryDemo.getInstance());
}
2.当构造方法参数过多时使用builder模式
如果一个类有许多可选参数,可有可无,有的可以用默认值,这时候就会有很多构造器,一个构造器调用另一个构造器(重叠构造器),这种会随着参数的增加而增加,感觉十分笨重。例如
public final class String
public String() {
this.value = "".value;
}
//还有些,碍于篇幅删了
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
public String(byte bytes[], String charsetName)
throws UnsupportedEncodingException {
this(bytes, 0, bytes.length, charsetName);
}
public String(byte bytes[], Charset charset) {
this(bytes, 0, bytes.length, charset);
}
public String(byte bytes[], int offset, int length) {
checkBounds(bytes, offset, length);
this.value = StringCoding.decode(bytes, offset, length);
}
public String(byte bytes[]) {
this(bytes, 0, bytes.length);
}
public String(StringBuffer buffer) {
synchronized(buffer) {
this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
}
}
public String(StringBuilder builder) {
this.value = Arrays.copyOf(builder.getValue(), builder.length());
}
}
除了重叠构造器的模式,还可以用builder模式,就是有一个内部类,来创建对象。
//hutool中ExecutorBuilder就用到了builder模式
public class ExecutorBuilder implements Builder<ThreadPoolExecutor> {
private static final long serialVersionUID = 1L;
private int corePoolSize;
private int maxPoolSize = 2147483647;
private long keepAliveTime;
private BlockingQueue<Runnable> workQueue;
private ThreadFactory threadFactory;
private RejectedExecutionHandler handler;
private Boolean allowCoreThreadTimeOut;
public ExecutorBuilder() {
this.keepAliveTime = TimeUnit.SECONDS.toNanos(60L);
}
public ExecutorBuilder setCorePoolSize(int corePoolSize) {
this.corePoolSize = corePoolSize;
return this;
}
public ExecutorBuilder setMaxPoolSize(int maxPoolSize) {
this.maxPoolSize = maxPoolSize;
return this;
}
public ExecutorBuilder setKeepAliveTime(long keepAliveTime, TimeUnit unit) {
return this.setKeepAliveTime(unit.toNanos(keepAliveTime));
}
public ExecutorBuilder setKeepAliveTime(long keepAliveTime) {
this.keepAliveTime = keepAliveTime;
return this;
}
public ExecutorBuilder setWorkQueue(BlockingQueue<Runnable> workQueue) {
this.workQueue = workQueue;
return this;
}
public ExecutorBuilder useSynchronousQueue() {
return this.useSynchronousQueue(false);
}
public ExecutorBuilder useSynchronousQueue(boolean fair) {
return this.setWorkQueue(new SynchronousQueue(fair));
}
public ExecutorBuilder setThreadFactory(ThreadFactory threadFactory) {
this.threadFactory = threadFactory;
return this;
}
public ExecutorBuilder setHandler(RejectedExecutionHandler handler) {
this.handler = handler;
return this;
}
public ExecutorBuilder setAllowCoreThreadTimeOut(boolean allowCoreThreadTimeOut) {
this.allowCoreThreadTimeOut = allowCoreThreadTimeOut;
return this;
}
public static ExecutorBuilder create() {
return new ExecutorBuilder();
}
public ThreadPoolExecutor build() {
return build(this);
}
//也达到了重叠构造器的作用,设置了默认值
private static ThreadPoolExecutor build(ExecutorBuilder builder) {
int corePoolSize = builder.corePoolSize;
int maxPoolSize = builder.maxPoolSize;
long keepAliveTime = builder.keepAliveTime;
BlockingQueue workQueue;
if (null != builder.workQueue) {
workQueue = builder.workQueue;
} else {
workQueue = (BlockingQueue)(corePoolSize <= 0 ? new SynchronousQueue() : new LinkedBlockingQueue());
}
ThreadFactory threadFactory = null != builder.threadFactory ? builder.threadFactory : Executors.defaultThreadFactory();
RejectedExecutionHandler handler = (RejectedExecutionHandler)ObjectUtil.defaultIfNull(builder.handler, new AbortPolicy());
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, TimeUnit.NANOSECONDS, workQueue, threadFactory, handler);
if (null != builder.allowCoreThreadTimeOut) {
threadPoolExecutor.allowCoreThreadTimeOut(builder.allowCoreThreadTimeOut);
}
return threadPoolExecutor;
}
}
使用
public static void main(String[] args) {
ExecutorBuilder.create().setCorePoolSize(5).
setMaxPoolSize(20).
setWorkQueue(new LinkedBlockingDeque<Runnable>(20)).
setKeepAliveTime(20, TimeUnit.SECONDS).
setHandler(new ThreadPoolExecutor.AbortPolicy()).
setThreadFactory(Executors.defaultThreadFactory()).build();
}
- 优点
- 明了
- 子类可以继承重写builder
- 缺点
- 创建某个对象要额外创建它的构造器
- 个人demo
/**
* 构造器模式
* 优点:
* 明了
* 子类可以继承充血builder
* 缺点:
* 创建某个对象要额外创建它的构造器
*/
public class ConstructorDemo {
private final int id;
private final int age;
private final String name;
private final String email;
public static class Builder{
//必须的参数
private final int id;
private final String name;
//非必需的参数
private int age;
private String email;
public Builder(int id,String name){
this.id=id;
this.name=name;
}
public Builder age(int age){
this.age=age;
return this;
}
public Builder email(String email){
this.email=email;
return this;
}
public ConstructorDemo build(){
return new ConstructorDemo(this);
}
}
private ConstructorDemo(Builder builder){
this.id=builder.id;
this.age=builder.age;
this.name=builder.name;
this.email=builder.email;
}
}
- 调用
public static void main(String[] args) {
ConstructorDemo constructorDemo=new ConstructorDemo.Builder(1,"hello").age(12).email("hh@qq.com").build();
}
3.用私有构造器或者枚举类型强化Singleton属性
单例就是只能被实例话一次,提供唯一获取的对象的方式,可以直接访问,不需要实例化该类的对象。这样在内存中只有一个实例,减少了频繁的创建销毁的操作。实现单例模式也十分简单,static修饰,私有化构造器。根据创建方式不同,分成懒汉式和饿汉式(谁取得名字),也可以直接直接公有化成员变量。 具体如下:
- 代码
/**
* 单例模式
*/
public class SingletonDemo {
//公有模式
public static final SingletonDemo publicInstance=new SingletonDemo();
private SingletonDemo(){}
}
/**
* 单例模式
*/
public class SingletonDemo {
//私有模式 饿汉式
private static final SingletonDemo singleton=new SingletonDemo();
private SingletonDemo(){}
public static SingletonDemo getInstance(){
return singleton;
}
}
public class SingletonDemo {
//私有模式 懒汉汉式
private static volatile SingletonDemo singleton = null;
private SingletonDemo(){}
public static SingletonDemo getInstance(){
//双重检查
if (singleton == null) {
synchronized (SingletonDemo.class) {
if (singleton == null) {
singleton = new SingletonDemo();
}
}
}
return singleton;
}
}
传统的懒汉式
/**
* 单例模式
*/
public class SingletonDemo {
//私有模式 饿汉汉式
private static volatile SingletonDemo singleton = null;
private SingletonDemo(){}
public static SingletonDemo getInstance(){
if (singleton == null) {
singleton = new SingletonDemo();
}
return singleton;
}
}
懒汉式就是用到的时候再创建,饿汉式就是一开始就创建了。这个就是2者的区别。
- 并发环境问题
- 饿汉式:final可以禁止指令重拍序,所以饿汉式加上final就可以在并发环境中使用,
- 懒汉式:为了最小化锁粒度,随意用了锁代码块,但是很多指令可能制定到第一层的
if (singleton == null)后恰巧切换到别的线程,所以在锁内部又检查了一次。这就是双重检查。但是还不够,new SingletonDemo();就这个语句其实对应了3个指令- 申请内存,赋值默认值
- 成员变量初始化
- 赋值给对象引用 这就有指令重拍序的风险,可能23对调,可能先获取对象,再初始化,就有风险,所以需要增加voliate保证可见性。(本人之前也整理过并发相关文章链接)
- 枚举 创建单例还有第三种方式,就是枚举
public enum SingletonEnum {
SINGLET;
public void doSomething() {
System.out.println("doSomething");
}
}
public class TestDemo {
public static void main(String[] args) throws Exception {
//singleton();
singleton1();
}
private static void singleton1() throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
SingletonEnum s=SingletonEnum.SINGLET;
//Exception in thread "main" java.lang.NoSuchMethodException
Constructor<SingletonEnum> constructor = SingletonEnum.class.getDeclaredConstructor();
constructor.setAccessible(true);
SingletonEnum si=constructor.newInstance();
System.out.println(si==s);
}
private static void singleton() throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
//单例
SingletonDemo singletonDemo=SingletonDemo.getInstance();
Constructor<SingletonDemo> constructor = SingletonDemo.class.getDeclaredConstructor();
constructor.setAccessible(true);
SingletonDemo sReflection = constructor.newInstance();
//false
System.out.println(sReflection==singletonDemo);
}
}
constructor.setAccessible(true);可以调用私有构造器。枚举方式也是作者推荐的一种方式
- 其他 网上还看到一个枚举单例的另外一种形式
public class Singleton {
}
public enum SingletonEnum {
SINGLET;
private Singleton instance;
public void doSomething() {
instance=new Singleton();
}
public Singleton getInstance() {
return instance;
}
}
public static void singleton2() {
Singleton s=SingletonEnum.SINGLET.getInstance();
Singleton s1=new Singleton();
System.out.println(s==s1);
}
这种方式我觉得有歧义啊,单例就是为了不让创建第二个,这样不是脱裤子放屁么
4.通过私有构造器强化不可实例化的能力
如果某个类不希望被实例话,例如工具类,要显示声明一个私有的构造器,不声明构造器,编译器会自动提供一个共有的无参的构造器,例如java.util.Collections
public class Collections {
// Suppresses default constructor, ensuring non-instantiability.
// 抑制默认构造器,确保不会被创建
private Collections() {
}
}