定义:它是java所有设计模式中最简单的一种,它提供了一个创建对象的最佳方法,
那些地方用到了单例模式?1、网站计数器,一般采用单例模式,否则难以同步
2、应用程序的日志应用一般采用单例模式
3、多线程的线程池一般也是采用单例模式
4、windows中的任务管理器是典型的单例模式,不可以开启两个
5、windows中的回收站也是单例模式
主要是懒汉式和饿汉式
1、饿汉式:类初始化的时候会立刻加载该对象,天生的线程安全,调用效率高。
饿汉式实现的步骤:
1、构造器私有化.
2、类的内部创建一个对象的实例。
3、向外提供一个静态的方法用来获取对象的实例。
优点:在类加载的过程中就创建的实例,避免的线程同步的问题(线程的聚绝对安全)。
缺点:如果从开始的时候就没有使用这个实例,就造成的内存的浪费。
class Singleton{
//1.私有化构造器
private Singleton(){
}
//类的内部创建一个对象实例
private final static Singleton instance =new Singleton();
//3.提供一个公有的静态的方法,返回对象的实例
public static Singleton getInstance(){
return instance;
}
}
public static void main(String[] args) {
//测试
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
//调用两个方法获取的对象实例,是相同的
System.out.println(instance==instance1);
//获取两个实例的hashcode
System.out.println(instance.hashCode());
System.out.println(instance1.hashCode());
}
true
1956725890
1956725890
2、懒汉式:
类初始化的时候,不会初始化该对象,真正使用的时候才会创建该对象,具有懒加载的功能。
实现步骤:
1、私有化构造器
2、向外提供一个静态的方法,用来获取对象的实例。
优点:实现了懒加载的效果,但是只能在单线程的情况下使用。
缺点:如果两个线程同时进入了if条件语句,那么就会创建多个实例(线线程不安全)
class Singleton{
//1.私有化构造器
private Singleton(){
}
private static Singleton instance;
//2.提供一个静态的公有方法,使用该方法创建一个对象的实例
public static Singleton getInstance(){
if(instance ==null){
instance = new Singleton();
}
return instance;
}
}
public static void main(String[] args) {
//测试
System.out.println("这是懒汉式,线程不安全");
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
//调用两个方法获取的对象实例,是相同的
System.out.println(instance==instance1);
//获取两个实例的hashcode
System.out.println(instance.hashCode());
System.out.println(instance1.hashCode());
}
这是懒汉式,线程不安全
true
1956725890
1956725890
3、内部静态类的方法:
结合了懒汉式和饿汉式的各自的优点,真正需要对象的时候才会加载,加载类是线程安全的。
class Singleton {
private static volatile Singleton instance;
//1.私有化构造器
private Singleton() {
}
//写一个静态内部类,该类中有一个静态的属性,Singleton
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
//2.提供一个静态的公有方法,直接返回SingletonInstance.INSTANCE
//synchronized关键字
public static synchronized Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
4、枚举单例:
使用枚举实现单例模式,实现简单,调用率高,枚举的本身就是一个单例模式,由JVM从根本上提供保障。
package com.lijie;
//使用枚举实现单例模式 优点:实现简单、枚举本身就是单例,由jvm从根本上提供保障!避免通过反射和反序列化的漏洞 缺点没有延迟加载
public class Type08 {
enum Singleton{
INSTANCE;//属性
public void Say(){
System.out.println("hello");
}
}
public static void main(String[] args) {
Singleton instacne = Singleton.INSTANCE;
Singleton instacne2 = Singleton.INSTANCE;
System.out.println(instacne == instacne2);
System.out.println(instacne.hashCode());
System.out.println(instacne2.hashCode());
instacne.Say();
}
}
5、通过双重锁检测创建
public class Singleton {
//先创建实例,步初始化
private static Singleton instance;
private Singleton() {
System.out.println("这是私有化构造器创建的实例");
}
public static Singleton getInstance(){
if(instance == null){
synchronized (Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
public static void main(String[] args) {
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2);
}
}
2、工厂模式?
1、它提供了一种创建对象的最佳方式。
2、在工厂模式中通过使用一个共同的接口来指向新创建的对象。实现了创建者和调用者分离。
工厂模式分为简单工厂、工厂方法、抽象工厂模式
简单工厂 :用来生产同一等级结构中的任意产品。
工厂方法 :用来生产同一等级结构中的固定产品。
抽象工厂 :用来生产不同产品族的全部产品。
优点:简单工厂模式能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象。明确区分了各自的职责和权力,有利于整个软件体系结构的优化。
1、 简单工厂:缺点:很明显工厂类集中了所有实例的创建逻辑,容易违反GRASPR的高内聚的责任分配原则
//首先创建一个图形类的公共接口
public interface Shape {
void draw();
}
//创建接口的一个具体的实现类,长方形
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
//正方形类
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
//圆的类
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
//实现一个创建图形的工厂
public class ShapeFactory {
//使用 getShape 方法获取形状类型的对象
public Shape getShape(String shapeType) {
if (shapeType == null) {
return null;
}
shapeType = shapeType.toLowerCase(); //将字符串所有的大写转换为小写
switch (shapeType) {
case "circle":
return new Circle();
case "rectangle":
return new Rectangle();
case "square":
return new Square();
default:
return null;
}
}
}
2、工厂方法:
工厂方法中,工厂不再实现具体产品的创建,而是将产品创建的具体操作交给了子类,工厂只要提供一个子类工厂继承的接口,实现具体创建方法就可以。
package com.lijie;
public interface Car {
public void run();
}
public interface CarFactory {
Car createCar();
}
public class AoDi implements Car {
public void run() {
System.out.println("我是奥迪汽车..");
}
}
public class Bmw implements Car {
public void run() {
System.out.println("我是宝马汽车...");
}
}
public class AoDiFactory implements CarFactory {
public Car createCar() {
return new AoDi();
}
}
public class BmwFactory implements CarFactory {
public Car createCar() {
return new Bmw();
}
}
public class Client {
public static void main(String[] args) {
Car aodi = new AoDiFactory().createCar();
Car jili = new BmwFactory().createCar();
aodi.run();
jili.run();
}
}
3、抽象工厂模式
简单来说就是工厂的工厂,可以通过抽象工厂创建具体工厂,然后再由具体工厂实现具体产品的创建
//首先创建一个汽车工厂接口
public interface Car {
void run();
}
//实现一个汽车工厂接口的子类
class CarA implements Car{
public void run() {
System.out.println("宝马");
}
}
//实现另一个汽车工厂的子类
class CarB implements Car{
public void run() {
System.out.println("摩拜");
}
}
//创建一个发动机工厂的接口
public interface Engine {
void run();
}
//实现一个发动机工厂的子类
class EngineA implements Engine {
public void run() {
System.out.println("转的快!");
}
}
//实现另一个发动机工厂的子类
class EngineB implements Engine {
public void run() {
System.out.println("转的慢!");
}
}
//实现一个抽象工厂,也就是普通工厂的工厂
public interface TotalFactory {
// 创建汽车
Car createChair();
// 创建发动机
Engine createEngine();
}
//总工厂实现类,由他决定调用哪个工厂的那个实例
class TotalFactoryReally implements TotalFactory {
public Engine createEngine() {
return new EngineA();
}
public Car createChar() {
return new CarA();
}
}
public class Test {
public static void main(String[] args) {
TotalFactory totalFactory2 = new TotalFactoryReally();
Car car = totalFactory2.createChar();
car.run();
TotalFactory totalFactory = new TotalFactoryReally();
Engine engine = totalFactory.createEngine();
engine.run();
}
}
1、代理模式
1.1静态代理
- 静态代理:由程序员创建,也就是在编译时就将接口、被代理类、代理类等确定下来,在程序运行之前,代理类.class文件以及生成。
- 它主要就是有一个公共的接口、一个被代理类,以及一个代理类,这两个类都实现了这个接口,这个公共的接口定义了被代理类的一个行为规范,代理类持有被代理类的具体实现方法,而且代理类还可以在执行被代理类的方法之前添加额外的行为,这就是代理模式的一个很大的优点,体现最直白的地方就是SpringAOP(面向切面编程),我们可以在切点之前和之后都添加一些操作,而这些切点就是一些方法,而这些方法所在的类就是被代理的,这也是面向编程的主要思想。
- 示例:
public interface Image {
void display();
}
public class RealImage implements Image {
private String fileName;
public RealImage(String fileName) {
this.fileName = fileName;
loadFromDisk(fileName);
}
@Override
public void display() {
System.out.println("Displaying " + fileName);
}
private void loadFromDisk(String fileName) {
System.out.println("Loading " + fileName);
}
}
public class ProxyImage implements Image {
private RealImage realImage;
private String fileName;
public ProxyImage(String fileName) {
this.fileName = fileName;
}
@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(fileName);
}
realImage.display();
}
}
public class ProxyPatternDemo {
public static void main(String[] args) {
Image image = new ProxyImage("test.png");
//图像将从磁盘加载
image.display();
System.out.println("");
//图像将无法从磁盘加载
image.display();
}
}
1.2动态代理
动态代理的两个核心部分:
- Proxy类:主要是通过这个类的一个静态方法创建一个代理类的实例,这个方法中传入三个参数,第一个是继承了invocationHandler接口实现类的类加载器;第二个是代理对象的实现的接口;invocationHandler实现类的实例对象。这样就可以返回一个代理类接口的对象,注意:这个类代理的是接口不是类,最后强转之后是一个接口类型的代理对象
- invocationHandler接口:这个接口中传入一个被代理对象的target,以及存在一个invoke()方法,这个方法主要执行被代理对象的所有方法,如果代理对象需要执行一些自己的操作就可以再invoke()方法的前后进行执行,这个invoke就好比是一个切面,AOP就是这个原理。
代码示例:
public interface Rent {
//被代理类需要实现发接口,里面有具体实现的方法。
void rent();
void fare();
}
public class Host implements Rent {
//实现接口的被被代理类,此处是房东
public Host() {
}
@Override
public void rent() {
System.out.println("房东想要租房子");
}
@Override
public void fare() {
System.out.println("收房租。。。");
}
}
public class MonitorUtil {
//一个简单的时间工具类,可以返回一个方法执行的时间。
private static ThreadLocal t1 = new ThreadLocal<>();
public static void start(){
t1.set(System.currentTimeMillis());
}
public static void finish(String methodName){
long finishTime = System.currentTimeMillis();
System.out.println("执行方法"+ methodName +"耗时:" + (finishTime-t1.get()) + "ms");
}
}
public class ProxyInvocationHandler implements InvocationHandler {
//传入的被代理类目标
private Object target;
//获取被代理类的一个方法
public ProxyInvocationHandler(Object target) {
this.target = target;
}
//invoke核心方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MonitorUtil.start();//方法执行的开始时间
//方法执行后返回一个结果result
Object result = method.invoke(target, args);
method(method.getName());
MonitorUtil.finish(method.getName());//方法执行的结束时间
return result;
}
//代理类可以自己执行的一些操作,这里指的是中介收取中介费
public void method(String name){
System.out.println(name + "方法执行了");
}
}
public class Cilent {
public static void main(String[] args) {
//首先创建被代理类的对象,以便于对
Host host = new Host();
//创建invocatioinHandler实现类的对象,构造器中传入目标对象
ProxyInvocationHandler phi = new ProxyInvocationHandler(host);
//通过代理类的newProxyInstance方法获得一个代理类的实例,强转为目标接口类
Rent proxy = (Rent) Proxy.newProxyInstance(phi.getClass().getClassLoader(), host.getClass().getInterfaces(), phi);
//可以通过代理类实现操作
proxy.rent();
proxy.fare();
}
}
房东想要租房子 rent方法执行了 执行方法rent耗时:0ms 收房租。。。 fare方法执行了 执行方法fare耗时:0ms3、观察者模式
- 对象之间存在着一对多的关系,那就使用观察者模式,比如当一个对象被修改的时候,可以自动通知它依赖的对象做出相对应的更新修改。
- 一般来说观察者模式主要有三各类,Subject(主题类),Observe抽象类(观察类),Cilent(客户端),Subject类主要实现存储观察者的一个ArrayList,将观察者添加到观察列表中的方法attach(),以及通知所有观察者的方法notifyAllObserve()方法。以及状态的修改方法setStatue()。
- 示例:
public class Subject {
//首先是一个添加观察者的列表
private List observers = new ArrayList<>();
//一个状态值,状态改变那么就通知其他的观察者改变
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
notifyAllObservers();
}
//添加到观察者列表之中
public void attach(Observer observer) {
observers.add(observer);
}
//通知所有的观察者去自动更新
public void notifyAllObservers() {
for (Observer observer : observers) {
observer.update();
}
}
}
//抽象的观察者方法,以便于观察者实现
public abstract class Observer {
protected Subject subject;
public abstract void update();
}
//创建多个实体类观察者,实现更新方法
public class BinaryObserver extends Observer {
public BinaryObserver(Subject subject) {
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
System.out.println("Binary String: "
+ Integer.toBinaryString(subject.getState()));
}
}
public class OctalObserver extends Observer {
public OctalObserver(Subject subject){
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
System.out.println( "Octal String: "
+ Integer.toOctalString( subject.getState() ) );
}
}
public class HexaObserver extends Observer {
public HexaObserver(Subject subject){
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
System.out.println( "Hex String: "
+ Integer.toHexString( subject.getState() ).toUpperCase() );
}
}
public class ObserverPatternDemo {
public static void main(String[] args) {
Subject subject = new Subject();
new BinaryObserver(subject);
new HexaObserver(subject);
new OctalObserver(subject);
System.out.println("First state change: 15");
subject.setState(15);
System.out.println();
System.out.println("Second state change: 10");
subject.setState(10);
}
}
First state change: 15 Binary String: 1111 Hex String: F Octal String: 17 Second state change: 10 Binary String: 1010 Hex String: A Octal String: 12



