采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例, 并且该类只提供一个取得其对象实例的方法(静态方法)。
单例模式分为 八种、
饿汉:先给我 ,就是什么都创建好 等着被调用 可能造成内存浪费
懒汉:饿了在给我 就是:什么时候用 什么时候创建
饿汉式(静态常量)饿汉式(静态代码块)懒汉式(线程不安全)懒汉式(线程安全,同步方法 效率低)双重检查 (volatile synchronized) 建议使用静态内部类 建议使用枚举 建议使用
单例模式注意事项和细节说明
- 单例模式保证了 系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使 用单例模式可以提高系统性能当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用 new
单例模式使用的场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:重量级 对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session 工厂等)
1.1、饿汉式静态变量实现步骤:
- 构造器私有化 (防止 new )本类的内部创建对象实例提供一个公有的静态方法,返回实例对象
优点:写法简单、在启动项目,类装载时 就完成了其实例化对象,避免线程同步的问题
缺点:如果这个类对象用不到,但还是实例化了,造成了内存浪费
结论:这种单例模式 可用,但 可能造成内存浪费,如果 这种单例太多,启动时一次性加载,啧啧啧,可怕的
public class A_SingletonPattern {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
// 证明对象是同个对象
System.out.println(instance == instance1); // true
System.out.println("instance hashCode = " + instance.hashCode()); // instance hashCode = 789451787
System.out.println("instance1 hashCode = " + instance.hashCode()); // instance1 hashCode = 789451787
}
}
// 饿汉式(静态常量)
class Singleton{
// 1、私有化 构造器
private Singleton(){
}
// 2、类的内部创建对象实例
private final static Singleton instance = new Singleton();
// 3、提供一个公有的静态方法,返回实例对象
public static Singleton getInstance(){
return instance;
}
}
解读代码:
对 Singleton类
1、私有化构造器,防止new它
// 1、私有化 构造器
private Singleton(){
}
2、类的内部创建对象实例 静态的且是常量 在启动项目是 就创建了该对象
private final static Singleton instance = new Singleton();
3、提供一个公有的静态方法,返回实例对象
public static Singleton getInstance(){
return instance;
}
main 方法 通过== 和 对象的hashcode 验证是同一个 Singleton 对象
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
// 证明对象是同个对象
System.out.println(instance == instance1); // true
System.out.println("instance hashCode = " + instance.hashCode()); // instance hashCode = 789451787
System.out.println("instance1 hashCode = " + instance.hashCode()); // instance1 hashCode = 789451787
}
1.2、饿汉式 静态代码块
class SingletonB {
// 1、私有化 构造器
private SingletonB() {
}
// 2、类的内部创建对象实例
private static SingletonB instance;
// 在静态代码块中,创建单例对象
static {
instance = new SingletonB();
}
// 3、提供一个公有的静态方法,返回实例对象
public static SingletonB getInstance() {
return instance;
}
}
代码解读:
静态常量类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候,就执 行静态代码块中的代码,初始化类的实例。
// 2、类的内部创建对象实例
private static SingletonB instance;
// 在静态代码块中,创建单例对象
static {
instance = new SingletonB();
}
1.3、懒汉式(单线程)
线程不安全 只能在单线程下使用
优缺点:
- 起到了 懒加载(Lazy Loading) 的效果,调用的时候再创建对象实例 但只能单线程下使用如果在多线程下 ,一个线程第一个走到 if (instance == null) 这是需要创建一个对象,
但是还没来得及往下走,另一个线程也走到这 也要去创建一个对象,这就会产生多个实例
结论:不要使用该方式 除非 上锁 synchronized 效率慢
public static void main(String[] args) {
SingletonC instance = SingletonC.getInstance();
SingletonC instance1 = SingletonC.getInstance();
// 证明对象是同个对象
System.out.println(instance == instance1); // true
System.out.println("instance hashCode = " + instance.hashCode()); // instance hashCode = 789451787
System.out.println("instance1 hashCode = " + instance.hashCode()); // instance1 hashCode = 789451787
}
}
class SingletonC{
// 1、私有化 构造器
private SingletonC(){
}
private static SingletonC instance;
// 3、提供一个公有的静态方法,调用的时候 再创建该对象的实例
public static SingletonC getInstance(){
if (instance == null){
instance = new SingletonC();
}
return instance;
}
}
1.4、 懒汉式(线程安全)
优缺点:
- 解决线程安全问题效率太低了 ,该方法每次只能有一个线程进入,效率降低
结论:不推荐使用这种方式
public static void main(String[] args) {
SingletonD instance = SingletonD.getInstance();
SingletonD instance1 = SingletonD.getInstance();
// 证明对象是同个对象
System.out.println(instance == instance1); // true
System.out.println("instance hashCode = " + instance.hashCode()); // instance hashCode = 789451787
System.out.println("instance1 hashCode = " + instance.hashCode()); // instance1 hashCode = 789451787
}
}
class SingletonD{
// 1、私有化 构造器
private SingletonD(){
}
private static SingletonD instance;
// 3、提供一个公有的静态方法,加入synchronized 解决线程安全问题 效率 低
public static synchronized SingletonD getInstance(){
if (instance == null){
instance = new SingletonD();
}
return instance;
}
}
使用 synchronized 保证线程安全
1.5、双重检查
优点: 线程安全 延迟加载 效率较高
结论:推荐使用
上代码:
public static void main(String[] args) {
SingletonE instance = SingletonE.getInstance();
SingletonE instance1 = SingletonE.getInstance();
// 证明对象是同个对象
System.out.println(instance == instance1); // true
System.out.println("instance hashCode = " + instance.hashCode()); // instance hashCode = 789451787
System.out.println("instance1 hashCode = " + instance.hashCode()); // instance1 hashCode = 789451787
}
class SingletonE{
// 1、私有化 构造器
private SingletonE(){}
private static volatile SingletonE instance;
// 3、提供一个公有的静态方法,加入synchronized 同步代码块
public static SingletonE getInstance(){
if (instance == null){
synchronized (SingletonE.class){
if (instance == null){
instance = new SingletonE();
}
}
}
return instance;
}
}
解读:
1、首先 私有化构造器 ,避免 外部 用new 创建此对象
// 1、私有化 构造器
private SingletonE(){}
2、其次 本类的对象置为全局参数,并 有 volatile 修饰
private static volatile SingletonE instance;
volatile:上个线程的操作对 后一个线程可见,保证 new 的时候不会发生指令重排
3、最后:
多线程情况下,一个线程 走进 synchronized 代码块中 判断为null 创建对象,
另一个线程 在同步代码块之外等待,因为volatile,上个线程 创建对象对该线程是可见的
所以 他的instance == null 不成立 直接返回 上个线程创建的对象。
双重检查是 两次判断本类对象是否为空
// 3、提供一个公有的静态方法,加入synchronized 同步代码块
public static SingletonE getInstance(){
if (instance == null){
synchronized (SingletonE.class){
if (instance == null){
instance = new SingletonE();
}
}
}
return instance;
}
1.6、静态内部类
优缺点说明:
- 这种方式采用了类装载的机制来保证初始化实例时只有一个线程。静态内部类方式在 Singleton 类被装载时并不会立即实例化,而是在需要实例化时,调用 getInstance 方法,才 会装载 SingletonInstance 类,从而完成 Singleton 的实例化。类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM 帮助我们保证了线程的安全性,在类进行 初始化时,别的线程是无法进入的。优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高
结论:推荐使用.
public static void main(String[] args) {
SingletonF instance = SingletonF.getInstance();
SingletonF instance1 = SingletonF.getInstance();
// 证明对象是同个对象
System.out.println(instance == instance1); // true
System.out.println("instance hashCode = " + instance.hashCode()); // instance hashCode = 789451787
System.out.println("instance1 hashCode = " + instance.hashCode()); // instance1 hashCode = 789451787
}
}
class SingletonF{
// 1、私有化 构造器
private SingletonF(){}
// 2、写一个静态内部类,该类中有静态属性
private static class SingletonInstance{
private static final SingletonF SINGLETON =new SingletonF();
}
// 3、提供一个静态公有方法,直接返回 SingletonInstance.SINGLETON
public static SingletonF getInstance(){
return SingletonInstance.SINGLETON;
}
}
1.7、枚举方式
优缺点说明:
- 这借助 JDK1.5 中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建 新的对象。这种方式是 Effective Java 作者 Josh Bloch 提倡的方式
结论:推荐使用
public static void main(String[] args) {
SingletonG instance = SingletonG.INSTANCE;
SingletonG instance1 = SingletonG.INSTANCE;
// 证明对象是同个对象
System.out.println(instance == instance1); // true
System.out.println("instance hashCode = " + instance.hashCode()); // instance hashCode = 789451787
System.out.println("instance1 hashCode = " + instance.hashCode()); // instance1 hashCode = 789451787
instance.sayOk();
}
}
// 使用枚举方式 实现单例模式
enum SingletonG{
INSTANCE;
public void sayOk(){
System.out.println("ok");
}
}
单例模式在JDK的应用
java.lang.Runtime 就是经典的单例模式(饿汉式)
分为 三类:
1、简单工厂模式
工厂类为一个类。根据不同类型生成不同产品
2、工厂方法模式
工厂类为 抽象类 , 有不同的子类(子工厂)继承他,并重写 生成产品的方法,满足不同子工厂生成不同产品需求
3、抽象工厂模式
2.1、 简单工厂模式工厂类为接口 有不同的子类(子工厂)继承他,并重写 生成产品的方法,满足不同子工厂生成不同产品需求
简单工厂模式:
- 介绍:属于 创建型模式,工厂模式的一种。简单工厂模式 是由一个工厂对象决定创建除哪一种产品的实例,是工厂模式中最简单实用的模式实现:定义一个创建对象的类,有这个类封装实例化对象的行为使用:在 软件开发中,当我们会用到大量的创建某种、某类或者某批对象时,会用到工厂模式
通过案例来解释 简单工厂模式
需求:要便于披萨种类的扩展,便于维护
1、披萨的种类很多(GreekPizza、CheesePizza…)2、披萨的制作有 prepare(预备)、bake(烘,焙)、cut(切)、box(打包)3、完成披萨店订购功能 2.1.1、传统模式
先看下不使用 工厂模式的设计
public class ASimpleFactory {
public static void main(String[] args) {
Order order = new Order();
}
}
// 抽象Pizza
@Data
abstract class Pizza {
// 披萨名字
protected String name;
// 准备原材料 不同的披萨是不一样的 所以是 抽象方法
public abstract void prepare();
public void bake() {
System.out.println(name + "baking");
}
public void cut() {
System.out.println(name + "cutting");
}
public void box() {
System.out.println(name + "boxing");
}
}
class GreekPizza extends Pizza {
@Override
public void prepare() {
System.out.println("制作GreekPizza 准备原材料");
}
}
class CheesePizza extends Pizza {
@Override
public void prepare() {
System.out.println("制作 CheesePizza 准备原材料");
}
}
// 1、新增披萨种类 胡椒披萨
class PepperPizza extends Pizza {
@Override
public void prepare() {
System.out.println("制作 PepperPizza 准备原材料");
}
}
// 订购 披萨
class Order {
// 构造器
public Order() {
Pizza pizza = null;
String orderType;
do {
orderType = getType();
if (orderType.equals("greek")) {
pizza = new GreekPizza();
pizza.setName("希腊 披萨");
} else if (orderType.equals("cheese")) {
pizza = new CheesePizza();
pizza.setName("奶酪披萨");
} else if (orderType.equals("pepper")) {
// 2、新增 披萨种类
pizza = new PepperPizza();
pizza.setName("胡椒披萨");
} else {
break;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} while (true);
}
// 获取用户订购披萨的种类
private String getType() {
try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza 种类:");
String str = strin.readLine();
return str;
} catch (IOException e) {
e.printStackTrace();
return "";
}finally {
}
}
}
解释代码:
首先 披萨的制作有prepare(预备)、bake(烘,焙)、cut(切)、box(打包),其中只有 预备原材料是每个披萨都不同的操作
所以把 抽象一个披萨类 让每种 披萨 继承这个类 重写 预备的方法
// 抽象Pizza
@Data
abstract class Pizza {
// 披萨名字
protected String name;
// 准备原材料 不同的披萨是不一样的 所以是 抽象方法
public abstract void prepare();
public void bake() {
System.out.println(name + "baking");
}
public void cut() {
System.out.println(name + "cutting");
}
public void box() {
System.out.println(name + "boxing");
}
}
class GreekPizza extends Pizza {
@Override
public void prepare() {
System.out.println("制作GreekPizza 准备原材料");
}
}
class CheesePizza extends Pizza {
@Override
public void prepare() {
System.out.println("制作 CheesePizza 准备原材料");
}
}
// 1、新增披萨种类 胡椒披萨
class PepperPizza extends Pizza {
@Override
public void prepare() {
System.out.println("制作 PepperPizza 准备原材料");
}
}
接下来是
用户手动输入订单类型的方法
// 获取用户订购披萨的种类
private String getType() {
try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza 种类:");
String str = strin.readLine();
return str;
} catch (IOException e) {
e.printStackTrace();
return "";
}finally {
}
}
最后 订单的操作:
Order类中的无参构造器,在new一个Order是 会调用这个无参构造器的方法,在此方法中,先获取订单的类型,根据类型判断调用 哪个披萨对象。
public Order() {
Pizza pizza = null;
String orderType;
do {
orderType = getType();
if (orderType.equals("greek")) {
pizza = new GreekPizza();
pizza.setName("希腊 披萨");
} else if (orderType.equals("cheese")) {
pizza = new CheesePizza();
pizza.setName("奶酪披萨");
} else if (orderType.equals("pepper")) {
// 2、新增 披萨种类
pizza = new PepperPizza();
pizza.setName("胡椒披萨");
} else {
break;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
} while (true);
}
主方法:
public static void main(String[] args) {
Order order = new Order();
}
传统模式:
- 优点:好理解、简单易操作缺点:违反了 开闭原则 ,对扩展开放,对修改关闭,给类增加新的功能,尽量不改或稍改代码
如:新增 披萨种类 需改两处代码
较于传统的模式 其 改进的思路:
把创建Pizza的对象封装到 一个类中,这样有新的种类,只需要修改该类即可
理解:简单工厂:
工厂生产 产品,所以单独有个工厂类 主要负责 制作产品,如 pizza 工厂 类 生成 pizza对象
public class BSimpleFactory {
// 订购 披萨
public static void main(String[] args) {
new OrderB(new SimpleFactory());
}
}
// 创建简单工厂类
class SimpleFactory{
// 根据 orderType 返回 对应的披萨对象
public PizzaB createPizza(String orderType){
System.out.println("简单工厂模式");
PizzaB pizza = null;
if (orderType.equals("greek")) {
pizza = new GreekPizzaB();
pizza.setName("希腊 披萨");
} else if (orderType.equals("cheese")) {
pizza = new CheesePizzaB();
pizza.setName("奶酪披萨");
} else if (orderType.equals("pepper")) {
// 2、新增 披萨种类
pizza = new PepperPizzaB();
pizza.setName("胡椒披萨");
}
return pizza;
}
}
// 抽象Pizza
@Data
abstract class PizzaB {
// 披萨名字
protected String name;
// 准备原材料 不同的披萨是不一样的 所以是 抽象方法
public abstract void prepare();
public void bake() {
System.out.println(name + "baking");
}
public void cut() {
System.out.println(name + "cutting");
}
public void box() {
System.out.println(name + "boxing");
}
}
class GreekPizzaB extends PizzaB {
@Override
public void prepare() {
System.out.println("制作GreekPizza 准备原材料");
}
}
class CheesePizzaB extends PizzaB {
@Override
public void prepare() {
System.out.println("制作 CheesePizza 准备原材料");
}
}
// 1、新增披萨种类 胡椒披萨
class PepperPizzaB extends PizzaB {
@Override
public void prepare() {
System.out.println("制作 PepperPizza 准备原材料");
}
}
// 订购 披萨
class OrderB {
// 定义一个简单工厂对象
SimpleFactory simpleFactory;
PizzaB pizza = null;
// 这使用的是聚合 通过构造器传入 SimpleFactory 对象,也可以直接new这个对象(组合关系)
public OrderB(SimpleFactory simpleFactory){
setFactory(simpleFactory);
}
public void setFactory(SimpleFactory simpleFactory){
this.simpleFactory = simpleFactory;
String orderType = "";// 用户输入的
do {
orderType = getType();
pizza = this.simpleFactory.createPizza(orderType);
if (pizza != null){
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}else {
System.out.println("订单失败");
break;
}
}while (true);
}
// 获取用户订购披萨的种类
private String getType() {
try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza 种类:");
String str = strin.readLine();
return str;
} catch (IOException e) {
e.printStackTrace();
return "";
}finally {
}
}
}
解读代码:
1、和传统模式一样 抽象pizza类 让各种pizza继承他重写预选原材料的方法。 以及获取用户输入披萨类型的方法。
2、重点来了:
创建一个简单工厂类,由这个类 来决定 披萨类型(也就是工厂生产的产品类型),有制作披萨的方法
class SimpleFactory{
// 根据 orderType 返回 对应的披萨对象
public PizzaB createPizza(String orderType){
System.out.println("简单工厂模式");
PizzaB pizza = null;
if (orderType.equals("greek")) {
pizza = new GreekPizzaB();
pizza.setName("希腊 披萨");
} else if (orderType.equals("cheese")) {
pizza = new CheesePizzaB();
pizza.setName("奶酪披萨");
} else if (orderType.equals("pepper")) {
// 2、新增 披萨种类
pizza = new PepperPizzaB();
pizza.setName("胡椒披萨");
}
return pizza;
}
}
3、订单类
首先 定义成员变量 工程类对象 和披萨类对象
SimpleFactory simpleFactory;
PizzaB pizza = null;
有参构造器参数为 工厂类,这样 创建这个订单类对象的时候 就要传一个 工程类的对象
// 这使用的是聚合 通过构造器传入 SimpleFactory 对象,也可以直接new这个对象(组合关系)
public OrderB(SimpleFactory simpleFactory){
setFactory(simpleFactory);
}
然后 set方法 将传入的工厂类对象 给 成员变量(工厂类) 并且调用这个工厂类 的制作披萨的方法
public void setFactory(SimpleFactory simpleFactory){
this.simpleFactory = simpleFactory;
String orderType = "";// 用户输入的
do {
orderType = getType();
pizza = this.simpleFactory.createPizza(orderType);
if (pizza != null){
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}else {
System.out.println("订单失败");
break;
}
}while (true);
}
2.1.3、静态工厂
简单工厂模式 也叫 静态工厂模式
和 上述简单工厂只有这两处改动
1、工厂类 中方法为 静态方法
// 创建静态工厂类
class SimpleFactoryC{
// 根据 orderType 返回 对应的披萨对象
public static PizzaC createPizza(String orderType){
System.out.println("简单工厂模式");
PizzaC pizza = null;
if (orderType.equals("greek")) {
pizza = new GreekPizzaC();
pizza.setName("希腊 披萨");
} else if (orderType.equals("cheese")) {
pizza = new CheesePizzaC();
pizza.setName("奶酪披萨");
} else if (orderType.equals("pepper")) {
// 2、新增 披萨种类
pizza = new PepperPizzaC();
pizza.setName("胡椒披萨");
}
return pizza;
}
}
2、 改为静态方法的目的是 订单处 不需要工厂对象也可以调用该方法
// 定义一个简单工厂对象
PizzaC pizza = null;
public OrderC(){
String orderType = "";// 用户输入的
do {
orderType = getType();
pizza = SimpleFactoryC.createPizza(orderType);
if (pizza != null){
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}else {
System.out.println("订单失败");
break;
}
}while (true);
}
2.2、工厂方法模式
1、看一个新的需求
披萨项目新的需求:客户在点披萨时,可以点不同口味的披萨,
比如 北京的奶酪 pizza、北京的胡椒 pizza 或 者是伦敦的奶酪 pizza、伦敦的胡椒 pizza
思路1、
使用简单工厂模式,创建不同的简单工厂类,比如 BJPizzaSimpleFactory、LDPizzaSimpleFactory 等等.
从当前 这个案例来说,也是可以的,但是考虑到项目的规模,以及软件的可维护性、可扩展性并不是特别好
思路2、
使用工厂方法模式
工厂方法模式 介绍:
- 工厂方法模式设计方案:将披萨项目的实例化功能抽象成抽象方法,在不同的口味点餐子类(子工厂)中具体实现。工厂方法模式:定义了一个创建对象的抽象方法,由子类决定要实例化的类。
理解:
抽象工厂方法就是 首先有个抽象类为 总工厂。然后里面有个抽象方法, 由不同的子工厂继承这个类并且实现该抽象方法,从而得到需要的产品。如
代码:
public class AMethodFactory {
public static void main(String[] args) {
// 创建北京口味的Pizza
new BJOrderPizza();
}
}
// 抽象Pizza
@Data
abstract class Pizza {
// 披萨名字
protected String name;
// 准备原材料 不同的披萨是不一样的 所以是 抽象方法
public abstract void prepare();
public void bake() {
System.out.println(name + "baking");
}
public void cut() {
System.out.println(name + "cutting");
}
public void box() {
System.out.println(name + "boxing");
}
}
class BJCheesePizza extends Pizza{
@Override
public void prepare() {
setName("北京的奶酪pizza");
System.out.println("北京的奶酪pizza 准备原材料");
}
}
class BJPepperPizza extends Pizza{
@Override
public void prepare() {
setName("北京的胡椒pizza");
System.out.println("北京的胡椒pizza 准备原材料");
}
}
class LDCheesePizza extends Pizza{
@Override
public void prepare() {
setName("伦敦的奶酪pizza");
System.out.println("伦敦的奶酪pizza 准备原材料");
}
}
class LDPepperPizza extends Pizza{
@Override
public void prepare() {
setName("伦敦的胡椒pizza");
System.out.println("伦敦的胡椒pizza 准备原材料");
}
}
// 披萨订单
abstract class OrderPizza{
// 定义一个抽象 工厂方法,createPizza, 让各个工厂子类实现
abstract Pizza createPizza(String orderType);
// 构造器
public OrderPizza(){
Pizza pizza = null;
String orderType;// 披萨类型
do {
orderType = getType();
pizza = createPizza(orderType);
// 制作过程
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}while (true);
}
// 获取用户订购披萨的种类
private String getType() {
try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza 种类:");
String str = strin.readLine();
return str;
} catch (IOException e) {
e.printStackTrace();
return "";
}finally {
}
}
}
// 工厂子类 北京Order
class BJOrderPizza extends OrderPizza{
@Override
Pizza createPizza(String orderType) {
System.out.println("工厂方法模式");
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new BJCheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new BJPepperPizza();
}
return pizza;
}
}
// 工厂子类 北京Order
class LDOrderPizza extends OrderPizza{
@Override
Pizza createPizza(String orderType) {
System.out.println("工厂方法模式");
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new LDCheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new LDPepperPizza();
}
return pizza;
}
}
解读代码:
1、抽象pizza类 让各种pizza继承他重写预选原材料的方法
// 抽象Pizza
@Data
abstract class Pizza {
// 披萨名字
protected String name;
// 准备原材料 不同的披萨是不一样的 所以是 抽象方法
public abstract void prepare();
public void bake() {
System.out.println(name + "baking");
}
public void cut() {
System.out.println(name + "cutting");
}
public void box() {
System.out.println(name + "boxing");
}
}
class BJCheesePizza extends Pizza{
@Override
public void prepare() {
setName("北京的奶酪pizza");
System.out.println("北京的奶酪pizza 准备原材料");
}
}
class BJPepperPizza extends Pizza{
@Override
public void prepare() {
setName("北京的胡椒pizza");
System.out.println("北京的胡椒pizza 准备原材料");
}
}
class LDCheesePizza extends Pizza{
@Override
public void prepare() {
setName("伦敦的奶酪pizza");
System.out.println("伦敦的奶酪pizza 准备原材料");
}
}
class LDPepperPizza extends Pizza{
@Override
public void prepare() {
setName("伦敦的胡椒pizza");
System.out.println("伦敦的胡椒pizza 准备原材料");
}
}
重点来啦
抽象 披萨订单 类 可以看作是总工厂,内有 抽象方法:制作pizza的方法,由各个工厂子类还实现该方法
// 披萨订单
abstract class OrderPizza{
// 定义一个抽象 工厂方法,createPizza, 让各个工厂子类实现
abstract Pizza createPizza(String orderType);
// 构造器
public OrderPizza(){
Pizza pizza = null;
String orderType;// 披萨类型
do {
orderType = getType();
pizza = createPizza(orderType);
// 制作过程
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}while (true);
}
// 获取用户订购披萨的种类
private String getType() {
try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza 种类:");
String str = strin.readLine();
return str;
} catch (IOException e) {
e.printStackTrace();
return "";
}finally {
}
}
}
// 工厂子类 北京Order
class BJOrderPizza extends OrderPizza{
@Override
Pizza createPizza(String orderType) {
System.out.println("工厂方法模式");
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new BJCheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new BJPepperPizza();
}
return pizza;
}
}
// 工厂子类 北京Order
class LDOrderPizza extends OrderPizza{
@Override
Pizza createPizza(String orderType) {
System.out.println("工厂方法模式");
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new LDCheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new LDPepperPizza();
}
return pizza;
}
}
北京口味的披萨
public static void main(String[] args) {
// 创建北京口味的Pizza
new BJOrderPizza();
}
2.3、抽象工厂模式
- 抽象工厂模式:定义了一个 interface 用于创建相关或有依赖关系的对象簇,而无需指明具体的类抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合。从设计层面看,抽象工厂模式就是对简单工厂模式的改进(或者称为进一步的抽象)。将工厂抽象成两层,AbsFactory(抽象工厂) 和 具体实现的工厂子类。程序员可以根据创建对象类型使用对应 的工厂子类。
这样将单个的简单工厂类变成了工厂簇,更利于代码的维护和扩展。
public class aAbstractFactory {
public static void main(String[] args) {
new OrderPizza(new BJFactory());
}
}
// 抽象Pizza
@Data
abstract class Pizza {
// 披萨名字
protected String name;
// 准备原材料 不同的披萨是不一样的 所以是 抽象方法
public abstract void prepare();
public void bake() {
System.out.println(name + "baking");
}
public void cut() {
System.out.println(name + "cutting");
}
public void box() {
System.out.println(name + "boxing");
}
}
class BJCheesePizza extends Pizza {
@Override
public void prepare() {
setName("北京的奶酪pizza");
System.out.println("北京的奶酪pizza 准备原材料");
}
}
class BJPepperPizza extends Pizza {
@Override
public void prepare() {
setName("北京的胡椒pizza");
System.out.println("北京的胡椒pizza 准备原材料");
}
}
class LDCheesePizza extends Pizza {
@Override
public void prepare() {
setName("伦敦的奶酪pizza");
System.out.println("伦敦的奶酪pizza 准备原材料");
}
}
class LDPepperPizza extends Pizza {
@Override
public void prepare() {
setName("伦敦的胡椒pizza");
System.out.println("伦敦的胡椒pizza 准备原材料");
}
}
// 一个抽象工厂模式的抽象层
interface AbsFactory {
// 让下面的工厂子类来具体实现
public Pizza createPizza(String orderType);
}
// 北京的工厂子类
class BJFactory implements AbsFactory {
@Override
public Pizza createPizza(String orderType) {
System.out.println("抽象工厂模式");
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new BJCheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new BJPepperPizza();
}
return pizza;
}
}
// 伦敦的工厂子类
class LDFactory implements AbsFactory {
@Override
public Pizza createPizza(String orderType) {
System.out.println("抽象工厂模式");
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new LDCheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new LDPepperPizza();
}
return pizza;
}
}
class OrderPizza{
AbsFactory factory;
// 构造器
public OrderPizza(AbsFactory factory){
setFactory(factory);
}
public void setFactory(AbsFactory factory){
Pizza pizza = null;
String orderType = ""; // 用户输入
this.factory = factory;
do {
orderType = getType();
// factory 可能是北京的工厂子类 也可能是 伦敦的工厂子类
pizza = factory.createPizza(orderType);
// 制作过程
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}while (true);
}
// 获取用户订购披萨的种类
private String getType() {
try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza 种类:");
String str = strin.readLine();
return str;
} catch (IOException e) {
e.printStackTrace();
return "";
}finally {
}
}
}
解读代码
1、抽象pizza类 让各种pizza继承他重写预选原材料的方法
// 抽象Pizza
@Data
abstract class Pizza {
// 披萨名字
protected String name;
// 准备原材料 不同的披萨是不一样的 所以是 抽象方法
public abstract void prepare();
public void bake() {
System.out.println(name + "baking");
}
public void cut() {
System.out.println(name + "cutting");
}
public void box() {
System.out.println(name + "boxing");
}
}
class BJCheesePizza extends Pizza {
@Override
public void prepare() {
setName("北京的奶酪pizza");
System.out.println("北京的奶酪pizza 准备原材料");
}
}
class BJPepperPizza extends Pizza {
@Override
public void prepare() {
setName("北京的胡椒pizza");
System.out.println("北京的胡椒pizza 准备原材料");
}
}
class LDCheesePizza extends Pizza {
@Override
public void prepare() {
setName("伦敦的奶酪pizza");
System.out.println("伦敦的奶酪pizza 准备原材料");
}
}
class LDPepperPizza extends Pizza {
@Override
public void prepare() {
setName("伦敦的胡椒pizza");
System.out.println("伦敦的胡椒pizza 准备原材料");
}
}
重点来了
定义一个抽象总工厂 接口 。内有方法,由具体子工厂来实现该方法
// 一个抽象工厂模式的抽象层
interface AbsFactory {
// 让下面的工厂子类来具体实现
public Pizza createPizza(String orderType);
}
// 北京的工厂子类
class BJFactory implements AbsFactory {
@Override
public Pizza createPizza(String orderType) {
System.out.println("抽象工厂模式");
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new BJCheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new BJPepperPizza();
}
return pizza;
}
}
// 伦敦的工厂子类
class LDFactory implements AbsFactory {
@Override
public Pizza createPizza(String orderType) {
System.out.println("抽象工厂模式");
Pizza pizza = null;
if (orderType.equals("cheese")) {
pizza = new LDCheesePizza();
} else if (orderType.equals("pepper")) {
pizza = new LDPepperPizza();
}
return pizza;
}
}
订单类 成员变量为 接口AbsFactory ,由 有参构造器的参数也是AbsFactory,new的时候要传入AbsFactory对象
然后由set方法将传的AbsFactory对象赋值给 成员变量AbsFactory。
class OrderPizza{
AbsFactory factory;
// 构造器
public OrderPizza(AbsFactory factory){
setFactory(factory);
}
public void setFactory(AbsFactory factory){
Pizza pizza = null;
String orderType = ""; // 用户输入
this.factory = factory;
do {
orderType = getType();
// factory 可能是北京的工厂子类 也可能是 伦敦的工厂子类
pizza = factory.createPizza(orderType);
// 制作过程
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
}while (true);
}
// 获取用户订购披萨的种类
private String getType() {
try {
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza 种类:");
String str = strin.readLine();
return str;
} catch (IOException e) {
e.printStackTrace();
return "";
}finally {
}
}
}
mian
public static void main(String[] args) {
new OrderPizza(new BJFactory());
}
工厂模式在 JDK中的应用
工厂模式小结Calendar 类中,就使用了简单工厂模式
- 工厂模式的意义 将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和主项目的依赖关系的解耦。从而提高项 目的扩展和维护性。三种工厂模式 (简单工厂模式、工厂方法模式、抽象工厂模式)设计模式的依赖抽象原则
建议:
- 创建对象实例时,不要直接 new 类, 而是把这个 new 类的动作放在一个工厂的方法中,并返回。有的书上说, 变量不要直接持有具体类的引用。不要让类继承具体类,而是继承抽象类或者是实现 interface(接口)不要覆盖基类中已经实现的方法。
原型模式:
就是将一个对象进行复制。深拷贝
Java 中 Object 类是所有类的根类,Object 类提供了一个 clone()方法,该方法可以将一个 Java 对象复制 一份,但是需要实现 clone 的 Java 类必须要实现一个接口 Cloneable,
该接口表示该类能够复制且具有复制的能力
比如:孙悟空 拔出猴毛,变出其他的孙悟空,一模一样
注意:
Cloneable的 clone方法对应 基本数据类型和String类型 是深拷贝
但是 对于 引用数据类型就是浅拷贝 ,不符合要求
public class BPrototype {
public static void main(String[] args) {
SheepB sheep1 = new SheepB("tom", 1, "白色");
SheepB sheep2 = (SheepB)sheep1.clone();
SheepB sheep3 = (SheepB)sheep1.clone();
System.out.println(sheep1);
System.out.println(sheep2);
System.out.println(sheep3);
}
}
@Data
class SheepB implements Cloneable{
String name;
int age;
String color;
public SheepB(String name, int age, String color) {
this.age = age;
this.name = name;
this.color = color;
}
// 克隆 该对象,使用 默认的clone 方法来完成
@Override
protected Object clone(){
SheepB sheepB = null;
try {
sheepB = (SheepB)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return sheepB;
}
}
解读代码:
需要copy的类 实现 Cloneable接口 并重写其clone方法()
// 克隆 该对象,使用 默认的clone 方法来完成
@Override
protected Object clone(){
SheepB sheepB = null;
try {
sheepB = (SheepB)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return sheepB;
}
深拷贝 和 浅拷贝
深拷贝
不仅复制对象的基本类,同时也复制原对象中的对象.就是说完全是新对象产生的.
浅拷贝
只复制对象的基本类型,对象类型,仍属于原来的引用
比如:B 复制 A
浅拷贝:修改A 的值 B 的值也会改变 因为是同一个引用地址 : 克隆羊是浅拷贝深拷贝:不会改变,因为B 是一个新的对象,和A 没关系了
深拷贝的实现方法
1、 重写 clone 实现深拷贝2、通过 对象序列化和反序列化实现深拷贝 3.1、重写 clone 实现深拷贝
1、基本类型 和 String 之间用 默认的clone方法2、A类中有 引用对象B 就要套一下了
首先在引用对象B 的类中也重写 clone,
然后在A clone方法中,调用B的clone方法后 赋值给A 的引用对象B
缺点:
如果引用对象中还有引用的话,就很麻烦 这个方法不推荐
public static void main(String[] args) throws CloneNotSupportedException {
DeepProtoType p = new DeepProtoType();
p.name = "松江";
p.deepCloneableTarget = new DeepCloneableTarget("大","小");
DeepProtoType p2 = (DeepProtoType)p.clone();
System.out.println(p.deepCloneableTarget.hashCode());
System.out.println(p2.deepCloneableTarget.hashCode());
}
}
class DeepCloneableTarget implements Cloneable {
private String cloneName;
private String cloneClass;
//构造器
public DeepCloneableTarget(String cloneName, String cloneClass) {
this.cloneName = cloneName;
this.cloneClass = cloneClass;
}
//因为该类的属性,都是 String , 因此我们这里使用默认的 clone 完成即可
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
class DeepProtoType implements Cloneable {
public String name; //String 属性
public DeepCloneableTarget deepCloneableTarget;// 引用类型
public DeepProtoType() {
super();
}
//深拷贝 - 方式 1 使用 clone 方法
@Override
protected Object clone() throws CloneNotSupportedException {
Object deep = null;
//这里完成对基本数据类型(属性)和 String 的克隆
deep = super.clone();
//对引用类型的属性,进行单独处理
DeepProtoType deepProtoType = (DeepProtoType) deep;
deepProtoType.deepCloneableTarget = (DeepCloneableTarget) deepCloneableTarget.clone();
return deepProtoType;
}
}
新对象引用对象和原引用对象的地址已经发生改变
public class DPrototype {
public static void main(String[] args) throws CloneNotSupportedException {
DeepProtoTypeD p = new DeepProtoTypeD();
p.name = "松江";
p.deepCloneableTarget = new DeepCloneableTargetD("大", "小");
DeepProtoTypeD p2 = (DeepProtoTypeD) p.deepClone();
System.out.println(p.deepCloneableTarget.hashCode());
System.out.println(p2.deepCloneableTarget.hashCode());
}
}
class DeepCloneableTargetD implements Serializable{
private String cloneName;
private String cloneClass;
//构造器
public DeepCloneableTargetD(String cloneName, String cloneClass) {
this.cloneName = cloneName;
this.cloneClass = cloneClass;
}
}
class DeepProtoTypeD implements Serializable {
public String name; //String 属性
public DeepCloneableTargetD deepCloneableTarget;// 引用类型
public DeepProtoTypeD() {
super();
}
// 深拷贝 -方法2、通过对象的序列化和反序列化实现(推荐)
Object deepClone() {
//创建流对象
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
//序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);//当前这个对象以对象流的方式输出
//反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
DeepProtoTypeD copyObj = (DeepProtoTypeD) ois.readObject();
return copyObj;
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
return null;
} finally {
//关闭流
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (Exception e2) {
// TODO: handle exception
System.out.println(e2.getMessage());
}
}
}
}



