目录
1.懒汉模式: 延迟加载,只有在真正使用的时候,才开始实例化
设计模式
找到代码在 变化中稳定的部分, 才有导入设计模式价值.
稳定的代码和过于变化 没有规律的代码都没有导入设计模式的价值
单例模式: Singleton
1.模式定义/应用场景/类图分析
模式定义:
保证一个类只有一个实例,并且提供一个全局访问点
应用场景:
重量级的对象,不需要多个实例,如线程池,数据库连接池.
类图分析:
Singleton : 类名
singleton: Singleton 定义一个私有对象
Singleton() 构造方法私有化
getInstance() 公开的获取私有对象的方法
2.加载模式:
1.懒汉模式: 延迟加载,只有在真正使用的时候,才开始实例化
-
优点:
第一次调用时才初始化,避免内存浪费
-
缺点:
必须加锁才能保证单例,影响效率.
1)线程安全问题
多线程进行访问创建时,可能会出现线程安全问题.(加上Thread.sleep()
可以使问题放大),破坏了单例的定义
public static LazySingleton getInstance(){
if(instance == null){
try{
Thread.sleep(200);
}catch(Exception e){
e.printStackTrace();
}
instance = new LazySingleton();
}
return instance;
}
2)加锁优化
在获取对象的方法加上synchronized,能保证单例,但会损耗性能.
public synchronized static LazySingleton getInstance(){
if(instance == null){
instance = new LazySingleton();
}
return instance;
}
延迟加锁:
防止资源浪费,在instance判空后,进行加锁,在instance不为空时,不进行加锁.
由于在判空后进行加锁, 还是可能会出现线程安全问题.
所以在加锁后再进行一次判空
在加锁处还是可能会出现线程并发,资源浪费的问题.
public static LazySingleton getInstance(){
if(instance == null){
synchronized (LazySingle.class){
if(instance == null){
instance = new LazySingleton();
//字节码层
//JIT . CPU
//指令重排
//1.分配空间
//2.初始化
//3.引用赋值
}
}
}
return instance;
}
3)编译器(JIT),CPU有可能对指令进行重排序
导致使用到尚未初始化的实例,可以通过添加volatile关键字进行修饰,对于volatile修饰的字段,可以防止指令重排.
如果两个线程并发,
第一个线程执行了 (指令被重排)1,3步骤时(instance引用赋值了,但并未初始化)
此时第二个线程进来,在最外层判空为false,
直接返回,此时可能会出现空指针异常.
private volatile static LazySingleton instance;
private LazeSingleton(){}
public...
2.饿汉模式:
class HungrySingleton{
private static HungrySingleton instance = new HungrySingleton();
private HungrySingleton(){}
public static HungrySingleton getInstance(){
return instance;
}
}
类加载的初始化阶段就完成了实例的初始化.本质上就是借助于jvm类加载机制,保证实例的唯一性.
类加载过程:
-
1.加载二进制数据到内存中,生成对应的Class数据结构,
-
2.连接:
a.验证 class文件是否符合jvm规范
b.准备(给类的静态成员变量赋默认值),
c.解析 如常量池中的符号引用转化成直接引用
-
初始化:给类的静态变量赋初值.
jvm能保证以上三步操作只完成一次
只有在真正使用对应的类时,才会触发初始化.(如当前类是启动类即main函数所在类,直接进行new操作,访问静态属性,访问静态方法,用发射访问类,初始化一个类的子类等.)
-
优点
没有加锁,执行效率高
-
缺点
类加载时就初始化,浪费内存.
3.静态内部类
属于懒加载模式
class innerClassSingleton{
private static class InnerClassHolder{
private static InnerClassSingleton instance = new InnerClassSingleton();
}
private InnerClassSingleton{}
public static InnerClassSingleton getInstance(){
return InnerClassHoldler.instance();
}
}
4.反射攻击
单例模式会被反射破坏
饿汉模式,静态内部类模式可以进行防护,懒汉模式没法进行防护.
//
枚举类型:
1)天然不支持反射创建对应的实例,且有自己的反序列化机制
2)利用类加载机制保证线程安全.
序列化:
1)可以利用指定方法来替换从反序列化流中的数据
序列化就是把一个java对象变成二进制内容,本质上就是一个byte[]数组.
为什么要把要把java对象序列化呢?
因为序列化后可以把byte[]数组保存到文件中或者通过网络传输到远程
反序列化:
就是把一个文件中的或者网络上接收到的二进制内容(即byte[]数组)变回java对象.
5.单例模式在Spring框架&JDK源码中的应用
工厂方法模式: Factory Method
模式定义:
定义一个用于创建对象的接口,让子类决定实例化哪一个类.
工厂方法模式的本质:
Factory Method使得一个类的实例化延迟到子类.
public class FactoryMethod {
public static void main(String[] args) {
Application application = new Application();
ProductA product = application.getObject();
product.method1();
}
}
class ProductA {
public void method1(){
System.out.println("ProductA.method1.executed.");
}
}
class Application{
private ProductA createProduct(){
// ... init
// ...
return new ProductA();
}
ProductA getObject(){
ProductA product = createProduct();
return product;
}
}
简单工厂:(不是设计模式)
public class FactoryMethod {
public static void main(String[] args) {
Application application = new Application();
Product product = application.getObject("0");
product.method1();
}
}
interface Product{
public void method1();
}
class ProductA implements Product{
public void method1(){
System.out.println("ProductA.method1.executed.");
}
}
class ProductA1 implements Product{
public void method1(){
System.out.println("ProductA1.method1.executed.");
}
}
class SimpleFactory{
public static Product createProduct(String type){
if (type.equals("0")){
return new ProductA();
}else if (type.equals("1")){
return new ProductA1();
}else {
return null;
}
}
}
class Application{
private Product createProduct(String type){
// ... init
// ...
return SimpleFactory.createProduct(type);
}
Product getObject(String type){
Product product = createProduct(type);
return product;
}
}
工厂方法模式:
分析:
这段代码中,product对象的创建是稳定的
Product的对象的种类是变化的,可能是product1,可能是product2
开闭原则(OPEN CLOSE):对拓展开放,对修改关闭.
public class FactoryMethod {
public static void main(String[] args) {
Application application = new ConcerateProductA();
Product product = application.getObject();
product.method1();
}
}
interface Product{
public void method1();
}
class ProductA implements Product{
public void method1(){
System.out.println("ProductA.method1.executed.");
}
}
class ProductA1 implements Product{
public void method1(){
System.out.println("ProductA1.method1.executed.");
}
}
class SimpleFactory{
public static Product createProduct(String type){
if (type.equals("0")){
return new ProductA();
}else if (type.equals("1")){
return new ProductA1();
}else {
return null;
}
}
}
abstract class Application{
/*把稳定的部分定义成抽象,让子类去实现*/
abstract Product createProduct();
Product getObject(){
Product product = createProduct();
//..
//..
return product;
}
}
/*子类具体实现父级的抽象方法*/
class ConcerateProductA extends Application{
@Override
Product createProduct() {
//...
return new ProductA();
}
}
应用场景:
-
当你不知道该使用对象的确切类型的时候
-
当你希望为库或框架提供扩展其内部组件的方法时
主要优点:
-
将具体产品和创建者解耦
-
符合单一职责原则
-
符合开闭原则
源码中的应用:
//java api
//静态工厂方法
Calender.getInstance()
java.text.NumberFormat.getInstance()
java.util.ResourceBundle.getBundle()
//工厂方法
java.net.URLStreamHandlerFactory //流协议处理的通用超类
javax.xml.bind.JAXBContext.createMarshaller
抽象工厂模式: Abstract Factory
模式定义:
提供一个创建一系列相关或互相依赖对象的接口,而无需指定它们具体的类.
(一组工厂方法的接口, 一系列相关的,互相依赖的产品组成的一组接口 就是抽象工程模式)
本质就是是由一些工厂方法模式组合而成.
类图:
原始代码:
public class AbstractFactoryTest {
public static void main(String[] args) {
IDatabaseUtils iDatabaseUtils = null;
IConnection connection = iDatabaseUtils.getConnection();
connection.connect();
ICommand command = iDatabaseUtils.getCommand();
command.command();
}
}
// 变化: mysql , oracle ...
// connection, command,
interface IConnection{
void connect();
}
interface ICommand{
void command();
}
interface IDatabaseUtils{
IConnection getConnection();
ICommand getCommand();
}
抽象工厂模式
public class AbstractFactoryTest {
public static void main(String[] args) {
IDatabaseUtils iDatabaseUtils = new OracleDataBaseUtils();
IConnection connection = iDatabaseUtils.getConnection();
connection.connect();
ICommand command = iDatabaseUtils.getCommand();
command.command();
}
}
// 变化: mysql , oracle ...
// 不变: connection, command,
/*定义connection规范*/
interface IConnection{
void connect();
}
/*定义command规范*/
interface ICommand{
void command();
}
/*定义databaseutils规范*/
interface IDatabaseUtils{
IConnection getConnection();
ICommand getCommand();
}
class MysqlConnection implements IConnection{
@Override
public void connect() {
System.out.println("mysql connection...");
}
}
class OracleConnection implements IConnection{
@Override
public void connect() {
System.out.println("oracle connection...");
}
}
class MysqlCommand implements ICommand{
@Override
public void command() {
System.out.println("mysql command...");
}
}
class OracleCommand implements ICommand{
@Override
public void command() {
System.out.println("oracle command...");
}
}
class MysqlDataBaseUtils implements IDatabaseUtils{
@Override
public IConnection getConnection() {
return new MysqlConnection();
}
@Override
public ICommand getCommand() {
return new MysqlCommand();
}
}
class OracleDataBaseUtils implements IDatabaseUtils{
@Override
public IConnection getConnection() {
return new OracleConnection();
}
@Override
public ICommand getCommand() {
return new OracleCommand();
}
}
应用场景:
程序需要处理不同系列的相关产品,但是您不希望他依赖于这些产品的具体类时,可以使用抽象工厂
优点:
-
可以确信你从工厂得到的产品彼此是兼容的
-
可以避免具体产品和客户端代码之间的紧密耦合
-
符合单一职责原则
-
符合开闭原则
在JDK源码中的应用:
java.sql.Connection
java.sql.Driver