学习准备
1、23种设计模式学习
1> 适配器模式:类的适配、对象适配、接口适配
* 类的适配
source 待适配的类
public class Source {
public void method1(){
System.out.println("this is original method");
}
}
Targeter 适配后的类
public interface Targeter {
public void method1();
public void method2();
}
Adapter适配器
public class Adapter extends Source implements Targeter{
@Override
public void method2() {
System.out.println("this is targetable method!");
}
}
Test 测试类
public class Test {
public static void main(String[] args) {
Targeter t = new Adapter();
t.method1();
t.method2();
}
}
* 对象的适配
适配器
public class Adapter implements Targeter{
public Source source;
public Adapter(Source source) {
this.source = source;
}
@Override
public void method2() {
System.out.println("this is targetable method!");
}
@Override
public void method1() {
source.method1();
}
}
测试类
public class Test {
public static void main(String[] args) {
Source s = new Source();
Targeter t = new Adapter(s);
t.method1();
t.method2();
}
}
*接口的适配
主要是为了解决这类问题:一个接口有好多方法,有的类用不到这么多,如果实现该接口就要实现所有的方法,代码多,这种情况可以考虑接口的适配,用一个抽象类实现该接口,之后的类继承该抽象类,不与接口直接打交道
抽象类:
public abstract class Wrapper implements Targeter{
@Override
public void method1() {}
@Override
public void method2() {
}
}
具体类
public class SourceSub extends Wrapper{
@Override
public void method1() {
System.out.println("this is method1");
}
}
public class SourceSub2 extends Wrapper{
@Override
public void method2() {
System.out.println("this is method2");
}
}
测试类
public class Test {
public static void main(String[] args) {
Wrapper w1 = new SourceSub();
Wrapper w2 = new SourceSub2();
w1.method1();
w2.method2();
}
}
2> 装饰模式
适合:能用到某个方法,但这个方法不完整,可以用装饰模式来修饰这个方法
被装饰类compennent
public interface Compon {
public void method();
}
public class Component implements Compon{
@Override
public void method(){
System.out.println("this is original method");
}
}
装饰类
public class Decorator implements Compon{
public Component c;
public Decorator(Component c) {
this.c = c;
}
@Override
public void method(){
System.out.println("before decorator");
c.method();
System.out.println("after decorator");
}
}
测试类
public class Test {
public static void main(String[] args) {
Component c =new Component();
Decorator d = new Decorator(c);
c.method();
d.method();
}
}
public class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
缺点:在多线程环境下,不安全
*
public class Singleton2 {
private static Singleton2 instance = null;
private Singleton2() {
}
public static synchronized Singleton2 getInstance() {
if (instance == null) {
instance = new Singleton2();
}
return instance;
}
}
缺点:效率不高,每次调用该方法都要对该对象加锁
*
private static Singleton3 instance = null;
private Singleton3() {
}
public static Singleton3 getInstance(){
if(instance == null){
synchronized(instance){
if(instance == null){
instance = new Singleton3();
}
}
}
return instance;
}
}
缺点:
将synchronized关键字加在了内部,也就是说当调用的时候是不需要加锁的,只有在instance为null,并创建对象的时候才需要加锁,性能有一定的提升。但是,这样的情况,还是有可能有问题的,看下面的情况:在Java指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton();语句是分两步执行的。但是JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Singleton实例分配空间,然后直接赋值给instance成员,然后再去初始化这个Singleton实例。这样就可能出错了,我们以A、B两个线程为例:
a>A、B线程同时进入了第一个if判断
b>A首先进入synchronized块,由于instance为null,所以它执行instance = new Singleton();
c>由于JVM内部的优化机制,JVM先画出了一些分配给Singleton实例的空白内存,并赋值给instance成员(注意此时JVM没有开始初始化这个实例),然后A离开了synchronized块。
d>B进入synchronized块,由于instance此时不是null,因此它马上离开了synchronized块并将结果返回给调用该方法的程序。
e>此时B线程打算使用Singleton实例,却发现它没有被初始化,于是错误发生了。
*比较完美
public class Singleton4 {
private Singleton4() {
}
private static class SingletonFactory{
private static Singleton4 instance = new Singleton4();
}
public static Singleton4 getInstance(){
return SingletonFactory.instance;
}
}
单例模式使用内部类来维护单例的实现,JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的。这样当我们第一次调用getInstance的时候,JVM能够帮我们保证instance只被创建一次,并且会保证把赋值给instance的内存初始化完毕,这样我们就不用担心上面的问题。同时该方法也只会在第一次调用的时候使用互斥机制,这样就解决了低性能问题。
4>工厂方法模式
普通工厂模式、多工厂模式、静态工厂模式 较简单,但违反开闭原则,即对扩展开放,对修改关闭,因为如果要扩展的话必须修改代码
5>抽象工厂模式
把工厂抽象,这样扩展的话,只要再实现抽象工厂增加类就可以,符合开闭原则
6>建造者模式
工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性。
- public interface Sender {
- public void Send();
- }
两个实现类:
- public class MailSender implements Sender {
- @Override
- public void Send() {
- System.out.println("this is mailsender!");
- }
- }
- public class SmsSender implements Sender {
- @Override
- public void Send() {
- System.out.println("this is sms sender!");
- }
- }
建造者模式实现
- public class Builder {
- private List<Sender> list = new ArrayList<Sender>();
- public void produceMailSender(int count){
- for(int i=0; i<count; i++){
- list.add(new MailSender());
- }
- }
- public void produceSmsSender(int count){
- for(int i=0; i<count; i++){
- list.add(new SmsSender());
- }
- }
- }
测试类
- public class Test {
- public static void main(String[] args) {
- Builder builder = new Builder();
- builder.produceMailSender(10);
- }
- }
7>原型模式
public class Prototype implements Cloneable , Serializable{
private static final long serialVersionUID = 6575573039264223590L;
private int i;
private Person p;
//浅复制
@Override
public Object clone() throws CloneNotSupportedException {
Prototype prototype = (Prototype) super.clone();
return prototype;
}
//深复制
public Object deepClone() throws CloneNotSupportedException, IOException, ClassNotFoundException {
ByteArrayOutputStream bao = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bao);
oo.writeObject(this);
ByteArrayInputStream bai = new ByteArrayInputStream(bao.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bai);
Object object = ois.readObject();
return object;
}
@Override
public String toString() {
return "Prototype [i=" + i + ", p=" + p + "]";
}
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
public Person getP() {
return p;
}
public void setP(Person p) {
this.p = p;
}
}
一个原型类,只需要实现Cloneable接口,覆写clone方法,此处clone方法可以改成任意的名称,因为Cloneable接口是个空接口,你可以任意定义实现类的方法名,如cloneA或者cloneB,因为此处的重点是super.clone()这句话,super.clone()调用的是Object的clone()方法,而在Object类中,clone()是native的。首先需要了解对象深、浅复制的概念:
浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。
深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。