单例模式:一个类模板中,在整个系统运行过程中,只允许产生一个实例(有且只有一个)。
保证单例的技术:
饿汉式:在实例使用之前,不管用不用,都先new出来,避免了线程安全问题。
特点:它是在类加载的时候立即初始化,并创建对象。
优点:没有任何的锁,执行效率高。
缺点:类加载的时候就创建对象。浪费了内存,占着茅坑不拉屎。
例子:
/**
* 饿汉式单例
* Created by zwz on 2018/9/4.
*/
public class Hungry {
private Hungry(){
}
private static final Hungry hungry = new Hungry();
public static Hungry getInstance(){
return hungry;
}
}
懒汉式:默认加载时不实例化,在需要时才实例化(延时加载)。
第一种:线程不安全的懒汉式单例:
/**
* 懒汉式单例(线程不安全)
* Created by zwz on 2018/9/4.
*/
public class LazyOne {
private LazyOne(){}
private static LazyOne lazyOne = null;
public static LazyOne getInstance(){
if(lazyOne == null){
//可能多个线程同时进来创建多个对象
lazyOne = new LazyOne();
}
return lazyOne;
}
}
编写一个测试线程安全类:
package patterns.singleton.test;
import patterns.singleton.hungry.Hungry;
import patterns.singleton.lazy.LazyOne;
import java.util.concurrent.CountDownLatch;
/**
* 线程安全测试
* Created by zwz on 2018/9/4.
*/
public class ThreadSafeTest {
public static void main(String[] args) {
int count = 300;
//发令枪
final CountDownLatch latch = new CountDownLatch(count);
long start = System.currentTimeMillis();
for(int i = 0; i< count;i++){
new Thread(){
@Override
public void run(){
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
Object obj = LazyOne.getInstance();
System.out.println(obj);
}
}.start();
latch.countDown();
}
long end = System.currentTimeMillis();
System.out.println("总耗时:"+(end - start));
}
}
测试创建LazyOne对象(线程不安全)结果中显示出现了不一样的对象,即不是单例的。
第二种:synchronized关键字实现线程安全的懒汉式单例
package patterns.singleton.lazy;
/**
* 懒汉式单例(线程安全)
* 虽然synchronized能保证线程安全,但效率较低
* Created by zwz on 2018/9/4.
*/
public class LazyTwo {
private LazyTwo(){}
private static LazyTwo lazyOne = null;
public synchronized static LazyTwo getInstance(){
if(lazyOne == null){
lazyOne = new LazyTwo();
}
return lazyOne;
}
}
第三种:较为高效的懒汉式单例,这里巧妙的利用了内部类会在外部类调用时才会加载和内部类在方法调用之前一定会初始化的特点,巧妙的避免了多线程的问题。
package patterns.singleton.lazy;
/**
* Created by zwz on 2018/9/4.
*/
public class LazyThree {
private boolean initialized = false;
/**
* 防止构造器反射注入
*/
private LazyThree(){
synchronized (LazyThree.class){
System.out.println(initialized);
if(initialized == false){
initialized = !initialized;
}else{
throw new RuntimeException("单例已被侵犯");
}
}
}
public static final LazyThree getInstance(){
return LazyHolder.LAZY;
}
public static class LazyHolder{
private static final LazyThree LAZY = new LazyThree();
}
}
通过一个测试类测试各种单例实现的性能问题:
package patterns.singleton.test;
import patterns.singleton.lazy.LazyOne;
import patterns.singleton.lazy.LazyThree;
import patterns.singleton.lazy.LazyTwo;
/**
* Created by zwz on 2018/9/4.
*/
public class BeanCreateTest {
public static void main(String[] args) {
int count = 20000000;
long start = System.currentTimeMillis();
for(int i = 0;i
LazyOne耗时:5
LazyTwo耗时:483
LazyThree耗时:6
可见,LazyThree方式即兼顾了饿汉式的内存问题,又兼顾了synchronized引起的性能问题。
注册登记式单例:每使用一次,都往一个固定的容器中去注册并且将使用过的对象进行缓存,下次取对象的时候直接从缓存中取值,以保证每次获取的都是同一个对象。IOC中的单例模式,就是典型的注册式单例。
package patterns.singleton.register;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 注册式单例,Spring中运用最多
* Created by zwz on 2018/9/4.
*/
public class RegisterMap {
private static Map registor = new ConcurrentHashMap();
private RegisterMap(){}
public synchronized static RegisterMap getInstance(String name){
if(name==null){
name = RegisterMap.class.getName();
}
if(registor.get(name) == null){
registor.put(name,new RegisterMap());
}
return (RegisterMap)registor.get(name);
}
}
枚举式:注册登记式的一种
package patterns.singleton.register;
/**
* Created by zwz on 2018/9/4.
*/
public enum Color {
RED(){
private int r = 255;
private int g = 0;
private int b = 0;
},BLACK(){
private int r = 0;
private int g = 0;
private int b = 0;
},WHITE(){
private int r = 255;
private int g = 255;
private int b = 255;
};
}
系列化及反系列化单例:加入readResolve(反系列化时默认执行)反系列化才能保证单例。
package patterns.singleton.seriable;
import java.io.ObjectInputStream;
import java.io.Serializable;
/**
* Created by zwz on 2018/9/4.
*/
public class Seriable implements Serializable{
public final static Seriable INSATNCE = new Seriable();
private Seriable(){}
public static Seriable getInsatnce(){
return INSATNCE;
}
private Object readResolve(){
return INSATNCE;
}
}
测试类:
package patterns.singleton.test;
import patterns.singleton.seriable.Seriable;
import java.io.*;
/**
* Created by zwz on 2018/9/4.
*/
public class SeriableTest {
public static void main(String[] args) {
Seriable s1 = null;
Seriable s2 = Seriable.getInsatnce();
try{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Seriable.obj"));
oos.writeObject(s2);
oos.flush();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Seriable.obj"));
s1 = (Seriable) ois.readObject();
ois.close();
System.out.println(s1);
System.out.println(s2);
System.out.println(s1==s2);
}catch (Exception e){
e.printStackTrace();
}
}
}
未加入readResolve方法时,结果未实现单例:
patterns.singleton.seriable.Seriable@4dd8dc3
patterns.singleton.seriable.Seriable@7f31245a
false
加入readResolve时,结果保证了单例:
patterns.singleton.seriable.Seriable@7f31245a
patterns.singleton.seriable.Seriable@7f31245a
true
原型(prototype)模式:DTO、VO、POJO、Entity。把对象中配置的依赖关系,在每次使用对象之前,都会创建一个新的对象,并且会将依赖关系完整的赋值给这个新创建的对象。即java中的克隆。
java中的浅克隆和深克隆:
package patterns.prototype;
import java.io.Serializable;
/**
* Created by zwz on 2018/9/4.
*/
public class Addresss implements Serializable{
public Addresss(String name) {
this.name = name;
}
private String name;
@Override
public String toString() {
return "Addresss{" +
"name='" + name + '\'' +
'}';
}
}
package patterns.prototype;
import java.io.*;
/**
* Created by zwz on 2018/9/4.
*/
public class Person implements Cloneable,Serializable{
private String name;
private int age;
private Addresss adresss;
public Person(){}
public Person(String name, int age, Addresss adresss) {
this.name = name;
this.age = age;
this.adresss = adresss;
}
/**
* 浅克隆
* @return
* @throws CloneNotSupportedException
*/
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Addresss getAdresss() {
return adresss;
}
public void setAdresss(Addresss adresss) {
this.adresss = adresss;
}
/**
* 系列化实现深克隆
* @return
*/
public Person deepClone(){
try {
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bo);
oos.writeObject(this);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bo.toByteArray()));
return (Person) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
package patterns.prototype;
import patterns.prototype.Addresss;
import patterns.prototype.Person;
/**
* Created by zwz on 2018/9/4.
*/
public class CloneTest {
public static void main(String[] args) {
Addresss a1 = new Addresss("深圳");
Person p1 = new Person("小明",23,a1);
Person p2 = (Person) p1.deepClone();
System.out.println(p1.getAdresss().toString());
System.out.println(p2.getAdresss().toString());
System.out.println(p1.getAdresss() == p2.getAdresss());
System.out.println(p1 == p2);
}
}
浅克隆测试结果中,两个对象中的Address值时同一个,即并没有复制完全。
Addresss{name='深圳'}
Addresss{name='深圳'}
true
false
深克隆测试结果:
Addresss{name='深圳'}
Addresss{name='深圳'}
false
false
反射复制实现原型模式
java8中的复制