public class CourseFactory {
public ICourse create(String name){
if("java".equals(name)){
return new JavaCourse();
}else if("python".equals(name)){
return new PythonCourse();
}else {
return null;
}
}
}
public class CourseFactory {
public ICourse create(String className){
try {
if (!(null == className || "".equals(className))) {
return (ICourse) Class.forName(className).newInstance();
}
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}
public class CourseFactory {
public ICourse create(Class extends ICourse> clazz){
try {
if (null != clazz) {
return clazz.newInstance();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
/*定义一个接口*/
public interface ICourseFactory {
ICourse create();
}
/*实现类*/
public class JavaCourseFactory implements ICourseFactory {
public ICourse create() {
return new JavaCourse();
}
}
/*实现类*/
public class PythonCourseFactory implements ICourseFactory {
public ICourse create() {
return new PythonCourse();
}
}
产品族:Java、Python ……不同语言
产品等级结构:笔记、视频(每个语言都有对应的笔记、视频等共性的东西)
思路:先创建笔记、视频接口,不同语言的笔记、视频交由子类具体实现,最后将各个语言的笔记、视频整合,交由工厂统一实现。
public interface INote {
void edit();
}
public class JavaNote implements INote {
public void edit() {
System.out.println("编写Java笔记");
}
}
public class PythonNote implements INote {
public void edit() {
System.out.println("编写Python笔记");
}
}
public interface IVideo {
void record();
}
public class JavaVideo implements IVideo {
public void record() {
System.out.println("录制Java视频");
}
}
public class PythonVideo implements IVideo {
public void record() {
System.out.println("录制Python视频");
}
}
public interface CourseFactory {
INote createNote();
IVideo createVideo();
}
public class JavaCourseFactory implements CourseFactory {
public INote createNote() {
return new JavaNote();
}
public IVideo createVideo() {
return new JavaVideo();
}
}
public class PythonCourseFactory implements CourseFactory {
public INote createNote() {
return new PythonNote();
}
public IVideo createVideo() {
return new PythonVideo();
}
}
确保一个类有且仅有一个实例,并全局调用。
应用场景:ServletContext、ServletContextConfig、ApplicationContext、数据库连接池
类加载时,创建单例对象
优点:
线程安全,没有任何形式的锁,执行效率高。
缺点:
类加载时就初始化,不管用不用都占用内存空间。
应用:
Spring IOC容器中ApplicationContext就是典型的饿汉式单例。
public class HungrySingleton {
private static final HungrySingleton hungrySingleton = new HungrySingleton();
private HungrySingleton(){} // private:防止外部new获取对象实例
public static HungrySingleton getInstance(){
return hungrySingleton;
}
}
public class HungryStaticSingleton {
private static final HungryStaticSingleton hungrySingleton;
static { //静态代码块
hungrySingleton = new HungryStaticSingleton();
}
private HungryStaticSingleton(){} // private:防止外部new获取对象实例
public static HungryStaticSingleton getInstance(){
return hungrySingleton;
}
}
外部类调用时内部类才会加载
public class LazyDoubleCheckSingleton {
private volatile static LazyDoubleCheckSingleton lazy = null; // volatile
private LazyDoubleCheckSingleton(){}
public static LazyDoubleCheckSingleton getInstance(){
if(lazy == null){
synchronized (LazyDoubleCheckSingleton.class){
if(lazy == null){
lazy = new LazyDoubleCheckSingleton();
}
}
}
return lazy;
}
}
第一个if(lazy == null),只有在一开始lazy尚未初始化时要抢锁,lazy初始化之后,就不存在抢锁的情况,性能得以保证。
第二个if(lazy == null),在一开始lazy尚未初始化时,会有大量线程进入第一个if(lazy == null),并等待锁,为了保证只有第一个抢到锁的线程才能初始化lazy,所以其他线程抢到锁之后还要再判断一次。
new LazyDoubleCheckSingleton()的过程包含内存分配并初始化零值、设置对象头、init方法初始化(详见JVM文档),内存分配并初始化零值时就可以将分配的内存地址赋值给lazy,然而对象可能还没有初始化,若此时另外一个线程进来,调用getInstance方法,可能获取的状态就是错的。问题的根本在于将分配的内存地址赋值给lazy和对象初始化的重排序导致,所以要依赖volatile,防止指令重排序。
内部类在被调用之前初始化,避免了线程安全的问题,也解决了饿汉式内存浪费的问题。
public class LazyInnerClassSingleton {
private LazyInnerClassSingleton(){}
// static 是为了使单例的空间共享
// final保证这个方法不会被重写,重载
public static final LazyInnerClassSingleton getInstance(){
return LazyHolder.LAZY;
}
private static class LazyHolder{
private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
}
}
构造方法private,虽然可以防止new,但如果通过反射调用构造方法,再getInstance(),还是会产生两个不同实例。在构造方法中判断,抛异常。
public class LazyInnerClassSingleton {
private LazyInnerClassSingleton(){
if(LazyHolder.LAZY != null){ // 防止反射破坏单例
throw new RuntimeException("不允许创建多个实例");
}
}
// static 是为了使单例的空间共享,final保证这个方法不会被重写,重载
public static final LazyInnerClassSingleton getInstance(){
return LazyHolder.LAZY;
}
private static class LazyHolder{
private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
}
}
当对象序列化到磁盘,下次用时再反序列时,会因为内存的重新分配而产生一个新的对象。
实现Serializable 接口,重写readResolve()。
public class SeriableSingleton implements Serializable {
public final static SeriableSingleton INSTANCE = new SeriableSingleton();
private SeriableSingleton(){}
public static SeriableSingleton getInstance(){
return INSTANCE;
}
private Object readResolve(){
return INSTANCE;
}
}
在ObjectInputStream类的readResolve()中,如果readResolve()重写了,则调用readResolve()。
缺陷:
其实通过源码了解到,对象实例化了两次,只不过新创建的对象没有被调用而已,所以如果需要大量的创建对象,内存开销必然很大。
通过枚举的唯一标识获取实例,枚举类的源码中针对反射、序列化破坏已经做了如上处理。
public enum EnumSingleton {
INSTANCE;
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static EnumSingleton getInstance(){
return INSTANCE;
}
}
枚举类单例如何避免反射破坏?
反射的源码中,有针对类的类型判断,如果是枚举类,则抛异常。
枚举类单例如何避免序列化破坏?
序列化的 readObject()方法中,针对枚举类的读方法中,是基于注册式单例(类+name)保证了唯一。
通过反射获取类对象,适用于很多实例需要创建的情况。
非线程安全。
public class ContainerSingleton {
private ContainerSingleton(){}
private static Map ioc =
new ConcurrentHashMap();
public static Object getInstance(String className){
synchronized (ioc) {
if (!ioc.containsKey(className)) {
Object obj = null;
try {
obj = Class.forName(className).newInstance();
ioc.put(className, obj);
} catch (Exception e) {
e.printStackTrace();
}
return obj;
} else {
return ioc.get(className);
}
}
}
}
ThreadLocal创建的对象不是全局唯一的。但能保证单个线程内唯一,线程安全。
public class ThreadLocalSingleton {
private static final ThreadLocal threadLocalInstance =
new ThreadLocal(){
@Override
protected ThreadLocalSingleton initialValue() {
return new ThreadLocalSingleton();
}
};
private ThreadLocalSingleton(){}
public static ThreadLocalSingleton getInstance(){
return threadLocalInstance.get();
}
}
拷贝对象
常用于:
1.类初始化消耗资源较多。Clone相比new的好处就在于避免了对象的初始化。
2.New 一个对象的过程非常繁琐(考虑数据、权限等)。
3.构造函数复杂
4.循环体中生产大量对象。
对象中引用类型clone的是地址引用,也就是说所有对象共用一个引用类型。
public interface Prototype{
Prototype clone();
}
public class ConcretePrototypeA implements Prototype {
private int age;
private String name;
private List hobbies;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List getHobbies() {
return hobbies;
}
public void setHobbies(List hobbies) {
this.hobbies = hobbies;
}
@Override
public ConcretePrototypeA clone() {
ConcretePrototypeA concretePrototype = new ConcretePrototypeA();
concretePrototype.setAge(this.age);
concretePrototype.setName(this.name);
concretePrototype.setHobbies(this.hobbies);
return concretePrototype;
}
}
public class Client {
private Prototype prototype;
public Client(Prototype prototype){
this.prototype = prototype;
}
public Prototype startClone(Prototype concretePrototype){
return (Prototype)concretePrototype.clone();
}
}
public class PrototypeTest {
public static void main(String[] args) {
// 创建一个具体的需要克隆的对象
ConcretePrototypeA concretePrototype = new ConcretePrototypeA();
// 填充属性,方便测试
concretePrototype.setAge(18);
concretePrototype.setName("prototype");
List hobbies = new ArrayList();
concretePrototype.setHobbies(hobbies);
System.out.println(concretePrototype);
// 创建Client对象,准备开始克隆
Client client = new Client(concretePrototype);
ConcretePrototypeA concretePrototypeClone =
(ConcretePrototypeA) client.startClone(concretePrototype);
System.out.println(concretePrototypeClone);
System.out.println("克隆对象中的引用类型地址值:" + concretePrototypeClone.getHobbies());
System.out.println("原对象中的引用类型地址值:" +
concretePrototype.getHobbies());
System.out.println("对象地址比较:"+
(concretePrototypeClone.getHobbies() == concretePrototype.getHobbies()));
}
}
通过序列化,重写引用类型。
继承Cloneable接口,重写clone()。
继承Serializable 接口,序列化。
public class QiTianDaSheng implements Cloneable,Serializable {
public JinGuBang jinGuBang;
public QiTianDaSheng(){
this.jinGuBang = new JinGuBang();
}
@Override
protected Object clone() {
return this.deepClone();
}
public Object deepClone(){
try{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis =
new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}catch (Exception e){
e.printStackTrace();
return null;
}
}
scope=’prototype’ // 作用域
JSON.parseObject()
将一个复杂对象的构建与它的表示分离,同样的构建过程可以创建不同的表示。用户只需要指定建造类型,无需了解建造的过程和细节。
适用于:
1、对象的构建需要很多步骤且顺序不固定
2、对象的构建结构非常复杂
3、将复杂对象的创建和使用分离
public class File {
private String name;
private String type;
private String url;
private String lastMotifile;
public void setName(String name) {
this.name = name;
}
public void setType(String type) {
this.type = type;
}
public void setUrl(String url) {
this.url = url;
}
public void setLastMotifile(String lastMotifile) {
this.lastMotifile = lastMotifile;
}
@Override
public String toString() {
return "File{" +
"name='" + name + '\'' +
", type='" + type + '\'' +
", url='" + url + '\'' +
", lastMotifile='" + lastMotifile + '\'' +
'}';
}
}
public static class FileBuilder{
private File file = new File();
public FileBuilder setName(String name) {
file.setName(name);
return this;
}
public FileBuilder setType(String type) {
file.setType(type);
return this;
}
public FileBuilder setUrl(String url) {
file.setUrl(url);
return this;
}
public FileBuilder setLastMotifile(String lastMotifile) {
file.setLastMotifile(lastMotifile);
return this;
}
public File build(){
return file;
}
}
public class Client {
public static void main(String[] args) {
File file = new File.FileBuilder()
.setLastMotifile("last")
.setName("fileName")
.setType("doc")
.setUrl("http://")
.build();
System.out.println(file);
}
}