常见的设计模式
单例设计模式
该设计模式要求在程序运行中,同一对象只能在JVM中存在一个实例。
其实这一点很简单实现,只要我们创建类的时候,将构造方法私有,外界无法创建对象。(不保证特殊情况下破坏单例的状况,如反射技术,序列化和反序列化)
但是构造方法私有了,外界怎么获得呢?
这个时候应该想到之前学过的许多工厂类(例如线程池Executors)的设计思路,在类中首先创建对象,再通过对外界提供静态方法将对象实例返回给外界使用。那么请看代码实现
/**
* 饿汉式
* */
public class Singleton {
//构造方法私有
private Singleton(){
}
//内部创建对象
private final static Singleton ME = new Singleton();
//外部获取该类对象的方法
public static Singleton getInstance(){
return ME;
}
}
这就是饿汉式:即不管是否现在用,先创建了,用的时候在拿走就好了。
那么还有一种就是懒汉式
/**
* 懒汉式
* */
public class Singleton1 {
//构造方法私有
private Singleton1(){
}
//创建对象,不实例化
private static Singleton1 ME = null;
//外部获取该类对象的方法(线程安全的)
public synchronized static Singleton1 getInstance(){
if (ME == null){
ME = new Singleton1();
}
return ME;
}
}
所谓懒汉式,因为他比较懒,没有人用的时候绝对不会优先创建对象,只有有人使用的时候才会创建对象。
以上条件满足:
1、如果多线程
2、存在共享变量
3、存在多条语句操作共享变量
这个时候就存在线程安全问题,这里引用了互斥锁机制(synchronized),但是由于锁都是比较占用资源的,我们仔细分一下,只有第一次创建对象的时候才会存在线程安全问题,但是一但方法这样设计,每一次都需要加锁,解锁,所以就会特别耗费资源,而且无用,我们是不是可以只对第一次解决线程不安全,后面无需处理。所以我们就想到了改进的方式。
package org.ymh.design;
//单例模式--终极版
public class Singleton2 {
private Singleton2(){
}
private static class inner{
private static final Singleton2 ME = new Singleton2();
}
public static Singleton2 getInstance(){
return inner.ME;
}
}
这里使用静态内部类,因为静态内部类只会在类被调用时候加载一次,后面就不会加载,所以可以保证线程是安全的,而且也替代了锁机制,减少了资源浪费。
享元设计模式
享元设计模式要求我们能使用已经创建好的,就不要在新创建对象。
我们先来看一下这道面试题
public class Flyweight {
public static void main(String[] args) {
System.out.println(Integer.valueOf(10) == Integer.valueOf(10));
System.out.println(Integer.valueOf(100) == Integer.valueOf(100));
System.out.println(Integer.valueOf(129) == Integer.valueOf(129));
}
}
执行结果肯定是:true true false
我们也知道为什么他们是这样的结果,下面看一下原码就知道了
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
可以看出来凡是在low----high这个范围的数,都直接返回来缓存数组里面的值,但是超过这个范围的就会重新new新的对象返回。这就表明10,100在这个范围内,而129不在这个范围内,所以调用两次valueOf()就会创建两个对象所以 == 比较以后结果是false。
其实这个设计模式就是享元设计模式,Integer的享元范围是-128 ~ 127,除了此类还有Byte, Short, Charater, Long。
原型模式
设计过程中发现一个类中具有100多个属性,但是每次实际需要变换的属性只有几个这个时候我们就要一种设计模式使得我们设计的对象跟之前对象保持一样的属性值,这样我们只需要修改部分属性值就可以达到效果,这个时候就需要一种设计模式--原型模式。
一个概念
浅拷贝:只是克隆了该对象的地址值,这个时候会出现一个问题,就是一旦你修改了克隆对象的引用数据类型的值,原始对象也会随之改变,这个时候就需要序列化和反序列化的支持,看这段代码。
//实现两个接口
public class Person implements Cloneable , Serializable {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
//重写Cloneable的clone方法,该方法内部实现序列化和反序列化
@Override
protected Object clone() throws CloneNotSupportedException {
ByteArrayOutputStream bo = new ByteArrayOutputStream();
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(bo);
objectOutputStream.writeObject(this);
byte[] bytes = bo.toByteArray();
ByteArrayInputStream bs = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(bs);
Object object = objectInputStream.readObject();
return object;
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
}
public class PersonTest {
public static void main(String[] args) throws CloneNotSupportedException {
Person person = new Person();
person.setName("zhangsan");
person.setAge(15);
Person person1 = (Person) person.clone();
System.out.println(person == person1);
person.setAge(10);
System.out.println(person.getAge());
System.out.println(person1.getAge());
}
}
建造者模式
优点:可以使得创建对象赋值变得灵活一些
public class User {
private String username;
private String password;
private Integer id;
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", id=" + id +
'}';
}
public User(String username, String password, Integer id) {
this.username = username;
this.password = password;
this.id = id;
}
public static class UserBuilder {
private String username;
private String password;
private Integer id;
//设计技巧:返回值是该内部类对象为了调用时候实现链式编程
public UserBuilder username(String username) {
this.username = username;
return this;
}
public UserBuilder password(String password) {
this.password = password;
return this;
}
public UserBuilder id(Integer id) {
this.id = id;
return this;
}
public User build() {
return new User(this.username, this.password, this.id);
}
}
}
测试类代码
public class BuilderTest {
public static void main(String[] args) {
User user = new User.UserBuilder().password("sss").username("ssss").build();
}
}
这样设计的目的是为了让User类中少写一些有参构造
迭代器模式
public class IteratorDemo {
public static void main(String[] args) {
//List集合遍历
ArrayList<String> list = new ArrayList<String>();
list.add("张三");
list.add("李四");
list.add("王五");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String next = iterator.next();
System.out.println(next);
}
//Set集合遍历
HashSet<String> set = new HashSet<>();
Iterator<String> iterator1 = set.iterator();
while (iterator.hasNext()) {
String next = iterator1.next();
System.out.println(next);
}
}
}
就像设计模式定义的:以一种一致的方法对集合内的元素进行遍历,而不用在乎集合内的数据结构
策略模式
就拿之前学的Arrays类和Collections类中的sort()方法来说,这个方法有个重载需要两个参数,一个是数组或者集合一个是Comparator接口,这个接口中可以写一种策略,这个策略保证我们以什么方式排列元素(降序,升序)。但是底层算法实现就固定的,因为java设计人员选择的算法都是比较优秀的。这就是策略模式的应用。
public class Strategy {
public static void main(String[] args) {
Integer[] arr ={5,4,3,54,48,41,56};
Arrays.sort(arr, (Integer o1, Integer o2) ->
-(o1 -o2)
);
System.out.println(Arrays.toString(arr));
}
}
实现数组元素的逆序输出。当然还可以这样写
public class Strategy {
public static void main(String[] args) {
Integer[] arr ={5,4,3,54,48,41,56};
Arrays.sort(arr, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return -(o1-o2);
}
});
System.out.println(Arrays.toString(arr));
}
}