单例设计模式
解决的问题:
一个应用范围内,一个class类,只应该存在一个对象。
单例设计模式的特点:
1.构造私有(不能通过正常手段进行new对象---因为通过反射还可以私有的方法进行调用)
2.静态成员变量(存储单例对象的引用)
3.提供一个公开静态的访问方法,获取单例对象。
两种方式:
懒汉式:会存在线程安全问题(面试最常问的就是这个)
饿汉式:不存在线程安全问题
饿汉式和懒汉式的区别:
饿汉式,是不管你用不用该对象,都创建。
------会造成资源的浪费。
------在创建对象的时候,不会出现多线程情况。
懒汉式,只有第一次访问的时候,才会创建。
----也叫延迟加载,由于被加载的时候,可能存在多线程访问,加上被访问的类中有成员变量。
单例模式常见的规范写法有多种,我们只讲两种:
1.双重检查锁(DCL)+ volatile
2.静态内部类
饿汉式的单例对象,什么时候创建?
当单例对象所对应的类被初始化的时候,才会创建该单例对象!!!
类的初始化时机有哪些?通过new Student、通过访问static成员变量。。。
在JVM加载类的时候,由JVM对加载类的线程加锁,
也就是说,类加载的时候,是线程安全的。不会存在多线程安全问题。
如何判断线程安全问题?
1:是否存在多线程 是
2:是否有共享数据 是
3:是否存在非原子性操作 (是针对字节码指令,不是高级语言的代码)
UserService{
private UserDao userDao;
public void method1(){
}
}
原子性的理解:
new Student();//是否是一个原子性操作呢?
i++;//是否是一个原子性操作呢?
1.先取出i变量的值
2.进行加1操作
3.将加1之后的值赋值给i
如何保证原子性呢?加锁
可见性的理解:
见图
可见性的解决方案:volatile关键字的一个作用:禁止cpu缓存使用
有序性的理解:
int x ;//step1
boolean a;//step2
x = 20;//step3
a = false;//step4
x 和 a之间是没有依赖性(有没有happened-before关系)
所以说,jvm会考虑性能问题,有可能对step3和step4进行指令重排序。
int x = 10;
int y ;
y = x + 10;
//此时x和y就存在了依赖性,所以说此处不会发生指令重排序。
指令重排序是不会影响最终的结果的。
有序性的解决,也是通过volatile关键字去解决。
volatile的另外一个作用就是禁止指令重排序。
动态代理模式
代理模式解决的问题:
1.将目标类不擅长的工作交给代理类去做。
2.对于模板性的代码,可以交给代理模式去做。
3.为了不修改原有代码的情况下,进行增强目标类的代码功能。
比如事务、日志统计等相应处理
代理模式的角色:
目标类
代理类
代理模式分成两种:静态代理、动态代理
静态代理是编译时期的代理,其实就是多写一个类去代理目标类
aop织入:动态织入和静态织入
静态织入就是通过在class文件被加载的时候,
可以使用一些工具对class文件进行修改(修改的就是代理的代码)
动态织入就是通过动态代理方式去实现的
常用的动态代理方式:
jdk
cglib(asm)
jdk:针对带有接口的类进行动态代理
(目标类和代理类的关系,是同级的,实现了共同的接口)
cglib:非接口的类,使用继承的方式进行代理。
(目标类和代理类是父子关系,目标类是父类,代理类是子类)
JDK动态代理实现的原理分析
见图
1.代理对象如何创建
public class proxy.target.Proxy$4 implements UserService{
private InvocationHandler h;
private Method m1;
public void saveUser() {
this.h.invoke(this,m1,null);
}
static{
m1 = Class.forName("proxy.target.UserServiceImpl").getMethod("saveUser",null);
}
}
jvm生成代理对象的过程
1.写源代码(java代码,JVM编写)----根据接口信息-----InvocationHandler
2.使用java自带的api,将java代码编译成字节码(可以保存到一个临时地点)
3.通过Classloader将class文件加载到jvm内存中
4.创建InvocationHandler对象
5.根据加载到的class信息去创建代理对象,并将invocationHandler对象传入
2.代理对象如何执行
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。