单例模式
1.饿汉式模式,先创建好对象,等待调用
利用私有构造方法,只能创建一个实例对象
public class Singleton1 {
/**
* 让构造方法私有,别人就没法创建此类的实例了
*/
private Singleton1() {
}
/**
* 自己创建这个实例
*/
private static final Singleton1 ME = new Singleton1();
/**
* 获取唯一实例
* @return
*/
public static Singleton1 getInstance() {
return ME;
}
}
2.懒汉模式,等到调用时再创建
(1)上述方法如果是多线程,就会破坏单例,所以用如下方法;
public class Singleton2 {
private Singleton2() {
}
private static Singleton2 ME;
// 线程1 , 线程2
/*
线程1 锁住了 Singleton2.class 对象
if (ME == null) {
线程2 等待 线程1 释放 Singleton2.class 对象上的锁
*/
public static synchronized Singleton2 getInstance() {
// 当一次调用时ME == null为真, 当后续调用时ME == null为假,就不会执行创建对象的操作了
if (ME == null) {
ME = new Singleton2();
}
return ME;
}
}
(2)懒汉式的最佳例子是利用私有静态内部类
public class Singleton4 {
static{
System.out.println("Singleton4类被加载了");
}
private Singleton4() {
}
// holder 拥有, 由静态内部类创建了他的唯一实例
private static class Holder {
static{
System.out.println("Holder类被加载了");
}
static Singleton4 ME = new Singleton4();
}
public static Singleton4 getInstance() {
return Holder.ME;
}
public static void test() {
System.out.println("Singleton4其它方法");
}
}
简化上面的
public class Singleton {
//静态内部类,只有类才加载一次,由静态内部类创建唯一实例
private Singleton(){}
private static class Inner{
static Singleton4 singleton=new Singleton();
}
public static Singleton getinterface(){
return Inner.singleton;
}
}
class Test3{
public static void main(String[] args) {
Singleton getinterface = Singleton.getinterface();
System.out.println(getinterface);
}
}
破坏单例的办法:
- 反射可以调用私有构造
- 反序列化可以破坏单例 (可以阻止)
还原的时候不会再调用对象的构造方法
享元模式 flyweight
提倡重用已有的对象,而不是创建新的对象
Integer的享元范围 -128 ~ 127
Byte, Short, Charater, Long
连接池–对数据库连接对象进行了重用
public class TestFlyweight {
public static void main(String[] args) {
System.out.println(Integer.valueOf(1) == Integer.valueOf(1)); // true
System.out.println(Integer.valueOf(100) == Integer.valueOf(100));// true
System.out.println(Integer.valueOf(200) == Integer.valueOf(200));// false
}
}
进入valueOf源码中看
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
如果值在一定的范围内,则返回缓存中的这个值,超出范围才重新创建一个对象
原型模式 prototype
根据已有对象来创建新的对象, 克隆
使用场景,当对象属性很多,希望新的对象的大部分属性从原有对象复制而来
(1)浅拷贝
public class User implements Cloneable {
private String name;
private int age; // 18 --> 18
private Date birthday; // #1234 --> #1234
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
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;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class TestClone {
public static void main(String[] args) throws CloneNotSupportedException {
copy();
}
public static void copy() throws CloneNotSupportedException {
User user = new User();
user.setAge(18);
user.setName("王小帅");
user.setBirthday(new Date());
User user2 = (User) user.clone();
System.out.println(user == user2); // false
System.out.println(user2.getAge());
System.out.println(user2.getName());
user2.getBirthday().setDate(28);
System.out.println("新克隆用户的生日:"+user2.getBirthday());
System.out.println("王小帅的生日:"+user.getBirthday());
}
}
运行结果:
false
18
王小帅
新克隆用户的生日:Fri Jun 28 17:42:51 CST 2019
王小帅的生日:Fri Jun 28 17:42:51 CST 2019
在上述代码中, 克隆用户修改出生日期,从而导致了原用户 的出生 日期也被修改,原因是此为浅拷贝,在拷贝时引用数据类型拷贝的是地址,而地址相同指向同一块区域,由此引出深拷贝,深拷贝,利用序列化
public class User2 implements Cloneable, Serializable {
private String name;
private int age; // 18 --> 18
private Date birthday; // #1234 --> #1234
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
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;
}
// ObjectOutputStream --> ByteArrayOutputStream --> byte[]
@Override
protected Object clone() throws CloneNotSupportedException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
// 把自己(当前对象)写入输出流
new ObjectOutputStream(os).writeObject(this);
// 拿到字节数组
byte[] bytes = os.toByteArray();
// 反序列化为新对象
ByteArrayInputStream is = new ByteArrayInputStream(bytes);
// 对象输入流
ObjectInputStream ois = new ObjectInputStream(is);
return ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
}
public class TestClone {
public static void main(String[] args) throws CloneNotSupportedException {
deepCopy();
}
public static void deepCopy () throws CloneNotSupportedException {
User2 user = new User2();
user.setAge(18);
user.setName("王小帅");
user.setBirthday(new Date());
User2 user2 = (User2) user.clone();
System.out.println(user == user2); // false
System.out.println(user2.getAge());
System.out.println(user2.getName());
user2.getBirthday().setDate(28);
System.out.println("新克隆用户的生日:"+user2.getBirthday());
System.out.println("王小帅的生日:"+user.getBirthday());
}
}
建造器模式
目的:让我们创建对象的过程更为灵活。适用于一步一步构建一个较为复杂的对象
jdk的体现:StringBuilder
public class Person {
private String name;
private String sex;
private Integer weight;
private Integer height;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", weight=" + weight +
", height=" + height +
'}';
}
// 建造器
public static class PersonBuilder{
private String name;
private String sex="男";
private Integer weight=50;
private Integer height;
// 返回值类型不再是void 而是建造器类型本身
public PersonBuilder name(String name) {
this.name = name;
return this;
}
public PersonBuilder sex(String sex) {
this.sex = sex;
return this;
}
public PersonBuilder weight(Integer weight) {
this.weight = weight;
return this;
}
public PersonBuilder height(Integer height) {
this.height = height;
return this;
}
public Person build() {
// 需要的信息收集齐了
// builer
return new Person(this.name,this.sex,this.weight,this.height);
}
}
private Person(String name, String sex, Integer weight, Integer height) {
this.name = name;
this.sex = sex;
this.weight = weight;
this.height = height;
}
public String getName() {
return name;
}
public String getSex() {
return sex;
}
public Integer getWeight() {
return weight;
}
public Integer getHeight() {
return height;
}
}
测试类
public class TestBuilder {
// new Person.PersonBuilder() 0 170 "张三" "男"
public static void main(String[] args) {
Person person = new Person.PersonBuilder()
.sex("女")
//.name("张三")
.height(170)
.build();
System.out.println(person);
String str = new StringBuilder().append("aa").append(1).append(333.444).toString();
System.out.println(str);
}
}
运行结果:
Person{name='null', sex='女', weight=50, height=170}
aa1333.444
迭代器模式(iterator)
定义:以一种一致的对集合内的元素进行遍历,而不用在乎集合内的数据结构
策略模式 (Strategy)
2.6 策略模式 (Strategy)
java 集合或数组的排序算法
Collections.sort
Arrays.sort
基本类型 双基点快速排序
对象类型 TimSort (早期使用归并排序)
规模小 插入排序
排序算法是固定的,排序的规则能否固定?–》 不能
把排序的规则抽取出来,形成比较器接口(Comparator),不同比较器的实现就称为策略
open close 开闭原则
算法不能改-- 体现的是close原则
比较器可以改 – 体现的是open原则
public class TestStrategy {
public static void main(String[] args) {
List<Student> list = new ArrayList<>();
list.add(new Student("zhangsan", 18));
list.add(new Student("lisi", 20));
list.add(new Student("wangwu", 16));
list.add(new Student("zhaoliu", 22));
list.add(new Student("zhaoliu", 20));
// 按年龄排
Collections.sort(list,(a, b) -> a.getAge() - b.getAge() );
System.out.println(list);
// 按名字排
Collections.sort(list, (a, b) -> a.getName().compareTo(b.getName()) );
System.out.println(list);
// 先按名字,再按年龄
Collections.sort(list, (a, b) -> {
int x = a.getName().compareTo(b.getName());
if(x != 0) {
return x;
} else {
return a.getAge() - b.getAge();
}
} );
System.out.println(list);
}
}
class Student {
private String name;
private int age;
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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}