1 设计模式(Design pattern)
代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
JAVA一共有23种设计模式,我们今天首先来学其中一种:单例设计模式
2 单例设计模式
单例模式可以说是大多数开发人员在实际中使用最多的,常见的Spring默认创建的bean就是单例模式的。
单例模式有很多好处,比如可节约系统内存空间,控制资源的使用。
其中单例模式最重要的是确保对象只有一个。
简单来说,保证一个类在内存中的对象就一个。
RunTime就是典型的单例设计,我们通过对RunTime类的分析,一窥究竟。
3 RunTime单例设计源码剖析
/**
* Every Java application has a single instance of class
* <code>Runtime</code> that allows the application to interface with
* the environment in which the application is running. The current
* runtime can be obtained from the <code>getRuntime</code> method.
* <p>
* An application cannot create its own instance of this class.
*
* @author unascribed
* @see java.lang.Runtime#getRuntime()
* @since JDK1.0
*/
public class Runtime {
//1.创建静态的全局唯一的对象
private static Runtime currentRuntime = new Runtime();
//2.私有化构造方法,不让外部来调用
/** Don't let anyone else instantiate this class */
private Runtime() {}
//3.通过自定义的静态方法获取实例
public static Runtime getRuntime() {
return currentRuntime;
}
}
通过分析,底层的实现思路一共分为了3个步骤:
- 对本类构造方法私有化,防止外部调用构造方法创建对象
- 创建全局唯一的对象,也做私有化处理
- 通过自定义的公共方法将创建好的对象返回(类似封装属性后的getXxx() )
那我们不妨自己尝试着来写写单例模式
4 练习:单例设计模式1-饿汉式实现方式
创建包: cn.tedu.design
创建类: Singleton1.java
package cn.tedu.design;
/*本类用于测试单例设计模式 1 - 饿汉式*/
public class Singleton1 {
public static void main(String[] args) {
//6.通过类名调用getSingle()方法获取本类对象
MySingle single1 = MySingle.getSingle();
MySingle single2 = MySingle.getSingle();
//7.测试获取到的这两个引用类型变量是否相等
System.out.println(single1 == single2);//true,==比较的是地址值,说明是同一个对象
System.out.println(single1);//cn.tedu.single.MySingle@1b6d3586
System.out.println(single2);//cn.tedu.single.MySingle@1b6d3586
}
}
//0.创建自己的单例程序
class MySingle{
//1.提供构造方法,并将构造方法私有化
/*1.构造方法私有化的目的:为了不让外界随意实例化/new本类对象*/
private MySingle(){ };
/*3.思考:构造方法和对象私有化后,通过公共的访问点来获取对象,那外界如何调用这个公共方法呢?
* 之前我们都是在外部创建本类对象并进行方法调用,但是现在单例程序中外部无法直接创建本类对象
* 解决方案:我们可以利用之前学习的静态的概念,将方法修饰成静态的,就可以通过类名直接调用啦
* 注意事项:静态只能调用静态,所以静态方法中返回的对象也需用静态修饰*/
//2.在类的内部,创建本类对象,并且私有化
//5.2本资源也需要使用static修饰,因为静态方法getSingle()只能调用静态资源
static private MySingle single = new MySingle();
/*2.也就是以公共的方式向外界提供获取本类私有对象的方法*/
//3.对外提供一个公共的全局访问点
//5.1用static关键字来修饰本方法,为了外部可以通过类名直接调用本方法
static public MySingle getSingle(){
//4.把内部创建好的对象返回到调用位置,谁调用这个方法,谁就能拿到返回的single对象
return single;
}
}
2.4 单例设计模式2-懒汉式实现方式
创建包: cn.tedu.design
创建类: Singleton2.java
package cn.tedu.single;
/*本类用于测试单例设计模式 2 - 懒汉式--面试重点!!!*/
/*总结:
* 关于单例设计模式的两种实现方式:
* 1.饿汉式 : 不管你用不用这个类的对象,都会直接先创建一个
* 2.懒汉式 : 先不给你创建这个类的对象,等你需要的时候再帮你创建--利用了延迟加载的思想
* 延迟加载的思想:是指不会在第一时间就把对象创建好来占用内存,而是什么时候用到,什么时候再去创建对象
* 3.线程安全问题 : 是指共享资源有线程并发的数据安全隐患,可以通过加锁的方式[同步代码块/同步方法]
* */
public class Singleton2 {
public static void main(String[] args) {
//6.创建对象进行测试
MySingle2 s1 = MySingle2.getMySingle2();
MySingle2 s2 = MySingle2.getMySingle2();
System.out.println( s1 == s2 );//true,比较的是地址值,说明是同一个对象
System.out.println( s1 );//cn.tedu.single.MySingle2@1b6d3586
System.out.println( s2 );//cn.tedu.single.MySingle2@1b6d3586
}
}
//0.创建单例程序
class MySingle2{
//1.私有化构造方法,为了防止外部调用构造方法直接创建本类对象
private MySingle2(){}
//2.在类的内部创建好引用类型变量(延迟加载的思想)--注意私有化
//5.2本处的引用类型变量也需要修饰成static的,因为静态只能调用静态,getMySingle2()是静态方法
static private MySingle2 single2;
//7.2.2同步代码块中使用的唯一的锁对象
static Object o = new Object();
/*问题:程序中有共享资源single2,并且有多条语句(3句)操作了共享资源
* 此时single2共享资源在多线程环境下一定会存在多线程数据安全隐患
* 解决方案1:同步代码块[加锁,范围是操作共享资源的所有代码]
* 解决方案2:同步方法[如果方法中的所有代码都需要被同步,那么这个方法可以修饰成同步方法]
* 注意事项:锁对象在静态方法中,不可以使用this,因为静态资源优先于对象加载
* 锁对象可以使用外部创建好的唯一的锁对象o,但请注意,需要是静态的,静态只能调用静态
* */
//3.对外提供公共的全局访问点
//5.1注意要使用static来修饰本公共方法,为了方便后续可以通过类名直接调用
//7.1将方法修饰成同步方法
synchronized static public MySingle2 getMySingle2(){
//4.当用户调用此方法时,才说明用到这个对象了,那么我们就把这个对象返回
/*注意:这里需要增加一个判断
如果调用方法时single2的值为null,说明之前没有new过,保存的是默认值
这时才需要new对象,如果single2的值不为null,直接return single2即可*/
//7.2可以将操作共享资源的多条语句放入同步代码块之中
synchronized (o) {
//synchronized (this) {
if (single2 == null) {
single2 = new MySingle2();//没有对象时才创建对象,并赋值给single2
}
return single2;//有对象则直接返回single2
}
}
}
单例设计模式到这里就结束啦,后续我们还可以继续学习下其他的设计模式哦~