学习这个的原因原因是在做java框架的时候回顾底层源码的时候发现一些程序的设计模式,思来想去学校好像没教,所以就先学几个常用的。以下的内容都是手打的花费时间是一周,总的来说学习了设计模式的原则和几个简单的设计模式,但是主要是单例设计模式有代码 其他的由于需要写很多的类所以就不贴出来了(单例模式还是比较熟练的(我指的是多并发的情况下我也能说清楚为啥这样使用比如双重锁结构))
设计模式之单例模式
黄伟晟的设计模式课堂笔记
设计模式的目的
代码重用性:相同代码,不多次编写
可读性:即是规范性
可扩展性:可维护性
可靠性:当增加功能后,对原有代码无影响
高内聚,低耦合
设计模式需要遵守的7大原则
单一职责原则 singleresponsibility
对类来说,一个类只负责一项功能。
接口隔离原则 segregation
客户端不应该依赖它不需要的接口,即是一个类对另一个类的依赖应该建立在最小的接口上
拆分接口~~~
依赖倒转原则 inversion
1.高层模块不应该依赖低层模块,二者都应该依赖其抽象
2.抽象不应该依赖细节,细节应该依赖抽象
3.依赖倒转的中心思想是面向接口编程
4.使用接口或者抽象类的目的是制定好规范,不涉及任何具体的操作,把展现的细节交给他们的实现类来完成
注意事项
低层模块尽量都要有抽象类或者接口,或者两者都有,这样的程序稳定性更好,
变量的声明类型精力是抽象类或接口,这样我们的变量引用和实际对象间。纯在一个缓冲层,有利于程序的扩展和优化
继承准寻里式替换原则
里氏替换原则 loskov
子类尽量不要重写父类的方法
适当情况下通过聚合,组合,依赖来解决问题
开闭原则 ocp
1.一个软件实体如类,模块和函数应该对扩展开放,对修改关闭,用抽象构建框架,用实现扩展细节
2.当软件需要变化的时候,尽量通过扩展软件的实体的行为来实现变化,而不是通过修改已有的代码来实现变化
迪米特法则 demeter
一个对象应该对其他对象保持最少的了解
类与类关系越密切,耦合度越大
即是一个类对自己依赖的类知道的越少越好1.即是对于被依赖的类不管多复杂,都尽量将逻辑封装在类的内部,对外提供public方法不对外泄露信息。
合成复用原则 composite
尽量使用合成/聚合的方式,而不是聚合
UML
## 单例模式 Singleton
new 有三个指令 1. 分配对象的内存空间 2. 初始化对象 3. 设置instance指向的内存空间
所谓类的单例设计模式,就是采取一定的方法保证在整个软件系统中,对某个类**智能存在一个对象实例**并且该类只提供一个取得其对象实例的方法。
### 饿汉式(静态常量)
1.构造器私有化(防止new)
2.类的内部创建对象
3.向外暴露一个静态的公有方法。getInstance
4.代码实现
```java
public class SingletonTest01 {
public static void main(String[] args) {
//测试
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance == instance2); // true
System.out.println("instance.hashCode=" + instance.hashCode());
System.out.println("instance2.hashCode=" + instance2.hashCode());
}
}
//饿汉式(静态变量)
class Singleton {
//1. 构造器私有化, 外部不能new
private Singleton() {
}
//2.本类内部创建对象实例
private final static Singleton instance = new Singleton();
//3. 提供一个公有的静态方法,返回实例对象
public static Singleton getInstance() {
return instance;
}
}
```
优缺点说明
优点:这种写法很简单,就是在类装载的时候就完成实例化。避免了线程同步的问题。
缺点:在类装载的时候就要完成实例化,没有达到Lazy Loading(懒加载)的效果,如果从始至终都没有使用过这个实例,就会造成内存的浪费。
这种方式基于classload机制避免了多线程的同步问题,不过,instance在类装载的时候就实例化,在单例模式中大多数都是调用getInstance方法,但是导致类装载的原因有很多,因此不能确定有没有其他的方式导致类装载,就不能起到懒加载的效果。
结论:这种单例模式可用,就是可能造成内存浪费。
### 饿汉式(静态变量)
```java
public class SingletonTest02 {
public static void main(String[] args) {
//测试
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance == instance2); // true
System.out.println("instance.hashCode=" + instance.hashCode());
System.out.println("instance2.hashCode=" + instance2.hashCode());
}
}
//饿汉式(静态变量)
class Singleton {
//1. 构造器私有化, 外部能new
private Singleton() {
}
//2.本类内部创建对象实例
private static Singleton instance;
static { // 在静态代码块中,创建单例对象
instance = new Singleton();
}
//3. 提供一个公有的静态方法,返回实例对象
public static Singleton getInstance() {
return instance;
}
}
```
### 懒汉式(线程不安全)
```java
public class SingletonTest03 {
public static void main(String[] args) {
System.out.println("懒汉式1 , 线程不安全~");
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance == instance2); // true
System.out.println("instance.hashCode=" + instance.hashCode());
System.out.println("instance2.hashCode=" + instance2.hashCode());
}
}
class Singleton {
private static Singleton instance;
private Singleton() {}
//提供一个静态的公有方法,当使用到该方法时,才去创建 instance
//即懒汉式
public static Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
```
优缺点说明:
1. 起到了懒加载的效果,但是只能在单线程下使用
2. 如果是多线程下,一个线程进入了if(singleton == null)判断语句块,还没来得及往下执行又有一个线程访问这个语句,就会创建多个实例,所以这个在多线程环境下不适用
3. 结论:在实际的开发中,不要使用这种方式
### 懒汉式(线程安全)
```java
public class SingletonTest04 {
public static void main(String[] args) {
System.out.println("懒汉式2 , 线程安全~");
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance == instance2); // true
System.out.println("instance.hashCode=" + instance.hashCode());
System.out.println("instance2.hashCode=" + instance2.hashCode());
}
}
// 懒汉式(线程安全,同步方法)
class Singleton {
private static Singleton instance;
private Singleton() {}
//提供一个静态的公有方法,加入同步处理的代码,解决线程安全问题
//即懒汉式
public static synchronized Singleton getInstance() {
if(instance == null) {
// synchronized(Singleton.class)
instance = new Singleton();//(这里加同步代码块没有用,解决不了线程安全问题)
}
return instance;
}
}
```
优缺点说明:
1. 解决了线程不安全的问题
2. 效率太低了,每一个线程在获得实现类实例的时候,执行getInstance()方法都要进行同步,而其实这个方法值执行一次实例化代码就够了,后面的想获得该类实例直接return即可
3. 结论:实际开发中,不推荐使用这种方法
### 懒汉式(线程安全,同步方法)
```java
package com.atguigu.singleton.type6;
public class SingletonTest06 {
public static void main(String[] args) {
System.out.println("双重检查");
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance == instance2); // true
System.out.println("instance.hashCode=" + instance.hashCode());
System.out.println("instance2.hashCode=" + instance2.hashCode());
}
}
// 懒汉式(线程安全,同步方法)
class Singleton {
private static volatile Singleton instance;
//如果不使用volatile关键字,可能出现异常,因为instance = new SingleTo();不是一个原子操作
//即是保证一个线程更新了instance其他的线程立刻得知,即是一个线程更新立刻刷到内存中 防止初始化独享和分配内存地址直接的重排序
private Singleton() {}
//提供一个静态的公有方法,加入双重检查代码,解决线程安全问题, 同时解决懒加载问题
//同时保证了效率, 推荐使用
public static synchronized Singleton getInstance() {
if(instance == null) {
synchronized (Singleton.class) {
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
```
优缺点说明
1. Double-check概念是多线程开发中常使用到的,如代码中所示,我们进行了两次if(singleton == null)检查,这样可以保证线程安全了
2. 这样,实例化代码只用执行一次,后面再次访问的时候,判断if(singleton == null),直接return实例化对象,也避免反复进行方法的同步。
3. 线程安全,延迟加载,效率较高
4. 结论:在实际的开发中,推荐使用这样的单例设计模式
### 静态内部类完成单例模式
```JAVA
package com.atguigu.singleton.type7;
public class SingletonTest07 {
public static void main(String[] args) {
System.out.println("使用静态内部类完成单例模式");
Singleton instance = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
System.out.println(instance == instance2); // true
System.out.println("instance.hashCode=" + instance.hashCode());
System.out.println("instance2.hashCode=" + instance2.hashCode());
}
}
// 静态内部类完成, 推荐使用
class Singleton {
private static volatile Singleton instance;
//构造器私有化
private Singleton() {}
//写一个静态内部类,该类中有一个静态属性 Singleton
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
//提供一个静态的公有方法,直接返回SingletonInstance.INSTANCE
public static synchronized Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
```
优缺点说明
1. 这种方式采用了类装载的机制来保证初始化实例的时候只有一个线程
2. 静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化的时候调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化
3. 类的静态属性保证只会才第一次加载类的时候初始化,所以在此,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的
4. 优点,避免了线程不安全,利用静态内部类她点实现延迟加载,效率高
5. 结论:推荐使用
### 枚举实现单例
```java
public class SingletonTest08 {
public static void main(String[] args) {
Singleton instance = Singleton.INSTANCE;
Singleton instance2 = Singleton.INSTANCE;
System.out.println(instance == instance2);
System.out.println(instance.hashCode());
System.out.println(instance2.hashCode());
instance.sayOK();
}
}
//使用枚举,可以实现单例, 推荐
enum Singleton {
INSTANCE; //属性
public void sayOK() {
System.out.println("ok~");
}
}
```
诶,博客园不支持md语法~