单例模式
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象
单例设计模式的作用:
- 目的: 保证一个类仅有一个实例,并提供一个访问它的全局访问点。
- 解决的问题: 一个全局使用的类频繁地创建与销毁。
- 什么时候使用: 当您想控制实例数目,节省系统资源的时候。
饥汉式
-
- 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
-
在该类内部产生一个唯一的实例化对象,并且将其封装为private static类型的成员变量。
-
定义一个静态方法返回这个唯一对象。
/**
*
* 单例设计模式 --- 饿汉式
*
* 能够在多线程环境下保证单例! -> 实际开发可以使用
*
* 缺点: 不符合懒加载的原则
*/
public class Singleton {
// 1. 私有构造方法, 不让别人创建对象
private Singleton() {
}
// 2. 单例的类中创建对象
// private
// static:
// 1. getInstance方法使用
// 2. static随着类的加载而加载, 使用类加载器的特点, 保证这个对象只创建一次!!!
// final: 防止本类修改
private static final Singleton instance = new Singleton();
// 3. 对外提供用于获取对象的方法
// static : 别的类可以直接使用
public static Singleton getInstance() {
return instance;
}
}
public class MyTest {
public static void main(String[] args) {
// 创建Singleton对象, 让Singleton是单例
Runnable r = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
Singleton s = Singleton.getInstance();
System.out.println(s);
}
}
};
for (int i = 0; i < 100; i++) {
new Thread(r).start();
}
}
}
懒汉式
- 懒汉单例设计模式就是调用getInstance()方法时实例才被创建,先不急着实例化出对象,等要用的时候才例化出对象。
普通实现
/**
* 单例设计模式 --- 懒汉式
*
* 保证了懒加载的原则
*
* 缺点: 多线程环境下不能保证单例
*
* 实际开发过程中, 不能用!!!
*
*/
public class Singleton {
// 1. 私有构造方法
private Singleton() {
}
// 2. 只做声明Singleton, 不创建
private static Singleton instance; // 默认值是null
// 3. 对外提供用于获取对象的方法
public static Singleton getInstance() {
// 只有第一次执行getInstance方法的时候需要创建对象,
// 后面再执行getInstance方法, 直接返回第一次创建的对象
if (instance == null) {
// 对象的创建
instance = new Singleton();
}
// 代码执行到这里, 说明instance一定不为null -> 直接返回
return instance;
}
}
public class MyTest {
public static void main(String[] args) {
// 创建Singleton对象, 让Singleton是单例
Runnable r = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
Singleton s = Singleton.getInstance();
System.out.println(s);
}
}
};
for (int i = 0; i < 100; i++) {
new Thread(r).start();
}
}
}
同步方法实现
/**
*
* 单例设计模式 --- 懒汉式(同步方法)
*
* 懒加载, 多线程环境下没有问题 -> 实际开发可以使用!!!
*
* 缺点: 每次执行都要获取锁和释放锁! 浪费资源!
*
*/
public class Singleton {
// 1. 私有构造方法
private Singleton() {
}
// 2. 只做声明Singleton, 不创建
private static Singleton instance; // 默认值是null
// 3. 对外提供用于获取对象的方法(同步方法)
public synchronized static Singleton getInstance() {
// 只有第一次执行getInstance方法的时候需要创建对象,
// 后面再执行getInstance方法, 直接返回第一次创建的对象
if (instance == null) {
// 对象的创建
instance = new Singleton();
}
// 代码执行到这里, 说明instance一定不为null -> 直接返回
return instance;
}
}
public class MyTest {
public static void main(String[] args) {
// 创建Singleton对象, 让Singleton是单例
Runnable r = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
Singleton s = Singleton.getInstance();
System.out.println(s);
}
}
};
for (int i = 0; i < 100; i++) {
new Thread(r).start();
}
}
}
使用同步锁
public class Singleton {
// 1. 私有构造方法
private Singleton() {
}
// 2. 只做声明Singleton, 不创建
private static Singleton instance; // 默认值是null
/*
如果这个方法写成同步方法, 无论什么时候, 执行getInstance方法都会获取锁和释放锁
我们希望, 先不着急获取锁, 等会再获取
现在的格式起到的效果: 在单线程的情况下, 保证只有第一次执行的时候, 获取锁创建对象
而后面的每一次执行都不会获取锁和创建对象
*/
// 3. 对外提供用于获取对象的方法(同步方法)
public static Singleton getInstance() {
// 只有第一次执行getInstance方法的时候需要创建对象,
// 后面再执行getInstance方法, 直接返回第一次创建的对象
if (instance == null) {
synchronized (Singleton.class) {
// 对象的创建
instance = new Singleton();
}
}
// 代码执行到这里, 说明instance一定不为null -> 直接返回
return instance;
}
}
public class MyTest {
public static void main(String[] args) {
// 创建Singleton对象, 让Singleton是单例
Runnable r = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
Singleton s = Singleton.getInstance();
System.out.println(s);
}
}
};
for (int i = 0; i < 100; i++) {
new Thread(r).start();
}
}
}
双重检查
/**
* 单例设计模式 --- 双重检查
*
* 优点 : 懒加载, 多线程保证只有一个对象, 不会每一次都获取锁和释放锁
*/
public class Singleton {
// 1. 私有构造
private Singleton() {
}
// 2. 声明引用, 不创建对象
// volatile : 禁止指令重排序
private static volatile Singleton instance;
// 3. 对外提供用于获取对象的方法
public static Singleton getInstance() {
// 判断
if (instance == null) { // 和下面的同步-> 减少获取锁的次数
synchronized (Singleton.class) {
if (instance == null) { // 保证单例!!!
instance = new Singleton();
}
}
}
return instance;
}
}
public class MyTest {
public static void main(String[] args) {
// 创建Singleton对象, 让Singleton是单例
Runnable r = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
Singleton s = Singleton.getInstance();
System.out.println(s);
}
}
};
for (int i = 0; i < 100; i++) {
new Thread(r).start();
}
}
}
多例模式
多例模式,是一种常用的软件设计模式。通过多例模式可以保证系统中,应用该模式的类有固定数量的实例。
实现步骤:
1.创建一个类, 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
2.在类中定义该类被创建的总数量
3.在类中定义存放类实例的list集合
4.在类中提供静态代码块,在静态代码块中创建类的实例
5.提供获取类实例的静态方法
/*
多例设计模式
创建对象的时候, 只能创建固定个数个实例
*/
public class Multition {
// 1. 私有构造
private Multition() {
}
// 2. 创建变量 , 用来代表集合中对象的个数
private static int count = 3;
// 3. 创建集合
private static ArrayList<Multition> list = new ArrayList<>();
// 在静态代码块中, 创建n个对象, 放到集合中
// 类加载的时候就执行, 只执行一次
static {
for (int i = 0; i < count; i++) {
list.add(new Multition());
}
}
// 对外提供用来获取对象的方法
public static Multition getInstance() {
// 创建随机数对象
Random r = new Random();
// 获取随机数(集合中的随机索引)
int randomIndex = r.nextInt(list.size());
// 获取这个索引对应的对象
Multition instance = list.get(randomIndex);
// 返回集合中的一个元素
return instance;
}
}
public class MyTest {
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
Multition m = Multition.getInstance();
System.out.println(m);
}
}
}
枚举
不使用枚举存在的问题:可以给性别传入任意的字符串,导致性别是非法的数据,不安全。
枚举的作用:
一个方法接收的参数是固定范围之内的时候,那么即可使用枚举。
枚举的概念:
枚举是一种特殊类。枚举是有固定实例个数的类型,我们可以把枚举理解成有固定个数实例的多例模式。
定义枚举的格式
枚举的本质是一个类,所以枚举中还可以有成员变量,成员方法等
配合 swich 用法
// RED, YELLOW, GREEN
// 都是Light类的对象,
// 默认修饰符: public static final
public enum Light {
RED, YELLOW, GREEN;
}
public class Demo01 {
public static void main(String[] args) {
// 指定灯是什么颜色
Light l = Light.GREEN;
switch (l) {
case GREEN:
System.out.println("绿灯行!~");
break;
case YELLOW:
System.out.println("黄灯等!~");
break;
case RED:
System.out.println("红灯停!~");
break;
}
}
}
用枚举对成员变量, 进行描述
public enum Season {
// 这几个都是Season类的对象
SPRING("春天在哪里"), SUMMER("夏天悄悄过去"), AUTUMN("秋天不回来"), WINTER("冬天里的一把火");
// 成员变量, 进行描述
private String describe;
// 构造方法给成员变量赋值
Season(String describe) {
this.describe = describe;
}
public String getDescribe() {
return describe;
}
}
public class Demo02 {
public static void main(String[] args) {
Season s = Season.AUTUMN;
System.out.println(s.getDescribe());
}
}
枚举实现单例模式
/**
* 枚举实现单例设计模式
*/
public enum Singleton {
INSTANCE;
}
public class MyTest {
public static void main(String[] args) {
// 创建Singleton对象, 让Singleton是单例
Runnable r = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
Singleton s = Singleton.INSTANCE;
System.out.println(s.hashCode());
}
}
};
for (int i = 0; i < 100; i++) {
new Thread(r).start();
}
}
}
枚举实现单例模式
- 它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。
普通工厂模式
工厂模式其实就是用来帮我们创建对象的
工厂模式的作用:
定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。可以解决类与类之间耦合的问题.
注意:
作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。
代码:
public class BenZ implements Car {
@Override
public void run() {
System.out.println("买了一辆奔驰, 跑的太慢!!!");
}
}
public class BMW implements Car {
@Override
public void run() {
System.out.println("买了一辆宝马, 操作还可以!");
}
}
public class Ferrari implements Car {
@Override
public void run() {
System.out.println("买了一辆法拉利, 跑的贼快!!~");
}
}
public class Porsche implements Car {
@Override
public void run() {
System.out.println("买了一辆破二手车, 太贵!!!");
}
}
public interface Car {
void run();
}
public class CarFactory {
// 对外提供用来创建对象的方法
public Car createInstance(String brand) {
if ("benz".equalsIgnoreCase(brand)) {
return new BenZ();
} else if ("bmw".equalsIgnoreCase(brand)){
return new BMW();
} else if ("porsche".equalsIgnoreCase(brand)){
return new Porsche();
} else if ("ferrari".equalsIgnoreCase(brand)){
return new Ferrari();
} else {
return null;
}
}
}
public class MyTest {
public static void main(String[] args) {
// 创建工厂的对象
CarFactory factory = new CarFactory();
// 使用工厂提供的方法, 创建Car的对象
BenZ benz = (BenZ) factory.createInstance("benz");
benz.run();
BMW bmw = (BMW) factory.createInstance("bmw");
bmw.run();
Car porsche = factory.createInstance("porsche");
porsche.run();
Car ferrari = factory.createInstance("ferrari");
ferrari.run();
}
}
方法引用
是什么:是对Lambda的简化
方法引用的前提:
不是所有的匿名内部类都可以改成Lambda
不是多有的lambda都可以改成方法引用!!!
Lambda表达式中的内容, 是已经定义好的.
格式:
适配器模式
public interface Monk {
void seat();
void speak();
void eatVegetable();
void dragTree();
void eatMeat();
}
public abstract class MonkAdapter implements Monk {
@Override
public void seat() {
}
@Override
public void speak() {
}
@Override
public void eatVegetable() {
}
@Override
public void dragTree() {
}
@Override
public void eatMeat() {
}
}
public class FlowerMonk extends MonkAdapter {
@Override
public void dragTree() {
System.out.println("倒拔垂杨柳");
}
@Override
public void eatMeat() {
System.out.println("大口吃肉!~");
}
}
public class OneRest extends MonkAdapter {
@Override
public void seat() {
System.out.println("打坐");
}
@Override
public void speak() {
System.out.println("格级格级");
}
@Override
public void eatVegetable() {
System.out.println("吃素");
}
}