相关知识点:
1.概述
单例模式是java中最简单的设计模式之一,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类。
2.使用单例模式的三个关键步骤
a.将构造方法私有化—防止外界创建对象
b.创建自己的对象
c.给外界访问方式
3.优缺点
优点:
提供了对唯一实例的受控访问。
由于在系统内存中只存在一个对象,因此可以节约系统资源。
允许可变数目的实例。
缺点:
单例类的扩展有很大的困难。
单例类的职责过重。
4.饿汉模式
这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁synchronized,所以严格意义上并不算单例模式。但是这种方式lazy loading很明显,不要线程安全,在多线程不能正常工作。
缺点:浪费空间,且不支持多线程。
5.懒汉模式
优点:第一次调用才初始化,避免浪费空间
缺点:必须加锁synchronized才能保证单例,但加锁会影响效率。
getInstance()的性能对应用程序不是很关键(该方法使用不太频繁)
a.普通的懒汉模式
这种方式具备很好的layz loading,能够在多线程中很好的工作,但是效率很低,99%情况下不需要同步。
b.支持线程安全的懒汉模式
可以有效的解决线程的安全问题
c.支持双重锁的懒汉模式
采用双锁机制,安全且高效
d.静态内部类的懒汉模式
这种方式可以达到与双锁方式一样的功效,但实现更加简单。这种方式只适用于静态域的情况,双锁方式可在实例域需要延迟初始化时使用。
6.应用实例
i.引入单例模式的案例
Person01.java
package demo_singleton01;
public class Person01 {
//无参构造方法
Person01(){
}
}
Test01.java
package demo_singleton01;
public class Test01 {
public static void main(String[] args) {
Person01 p1=new Person01();
Person01 p2=new Person01();
//判断这两个对象的内存地址是否相同
System.out.println(p1==p2);
System.out.println(p1);
System.out.println(p2);
//运行后可以发现,在一个项目中,某一指定的类型对象唯一
}
}
运行结果图
ii.单例模式的应用案例-----饿汉模式
Person02.java
package demo_singleton02;
//饿汉模式
public class Person02 {
//1.将构造方法私有化---防止外界创建对象
private Person02() {
}
//2.创建自己的对象
//每次调用都要初始化,非常浪费空间
private static Person02 p=new Person02();
//3.给外界访问方式
public static Person02 getInatance() {
return p;
}
}
Test02.java
package demo_singleton02;
public class Test02 {
public static void main(String[] args) {
//Person02 p1=new Person02();
Person02 p1=Person02.getInatance();
//Person02 p2=new Person02();
Person02 p2=Person02.getInatance();
//判断这两个对象的内存地址是否相同
System.out.println(p1==p2);
System.out.println(p1);
System.out.println(p2);
//运行后可以发现,两次创建的对象其对象内存地址相同。
}
}
运行结果图
iii.单例模式的应用案例-----普通的懒汉模式
Person03.java
package demo_singleton03;
//懒汉模式
public class Person03 {
//1.将构造方法私有化---防止外界创建对象
private Person03(){
}
//2.创建自己的对象
private static Person03 p;
/*3.给外界访问方式
*a.创建对象
*b.若之前已被创建,则直接使用之前的对象
*/
public static Person03 getInstance() {
//第一次创建时
if(p==null) {
p=new Person03();
}
//若之前已被创建,则直接使用之前的对象
return p;
}
}
Test03.java
package demo_singleton03;
public class Test03 {
public static void main(String[] args) {
Person03 p1=Person03.getInstance();
Person03 p2=Person03.getInstance();
//判断创建出来的两个对象的内存地址是否相同
System.out.println(p1==p2);
System.out.println(p1);
System.out.println(p2);
//运行后可以发现其内存地址相同
}
}
运行结果图
iv.单例模式的应用案例-----支持线程安全的懒汉模式
Person04.java
package demo_singleton04;
//懒汉模式(线程安全)
public class Person04 {
//1.将构造方法私有化---防止外界创建对象
private Person04(){
}
//2.创建自己的对象
private static Person04 p;
//3.给外界访问方式
//方法添加线程锁synchronized保证线程安全
public static synchronized Person04 getInstance() {
//第一次创建时
if(p==null) {
p=new Person04();
}
//若之前已被创建,则直接使用之前的对象
return p;
}
}
Test04.java
package demo_singleton04;
public class Test04 {
public static void main(String[] args) {
Person04 p1=Person04.getInstance();
Person04 p2=Person04.getInstance();
//判断创建出来的两个对象的内存地址是否相同
System.out.println(p1==p2);
System.out.println(p1);
System.out.println(p2);
//运行后可以发现其内存地址相同
}
}
运行结果图
v.单例模式的应用案例-----支持双重锁的懒汉模式
Person05.java
package demo_singleton05;
//懒汉模式(双重锁)
//目的:在保证线程安全的情况下尽可能的提高效率
public class Person05 {
//1.将构造方法私有化---防止外界创建对象
private Person05(){
}
//2.创建自己的对象
private static Person05 p;
//3.给外界访问方式
public static Person05 getInstance() {
//第一次创建时
if(p==null) {
synchronized(Person05.class) {
if(p==null) {
p=new Person05();
}
}
}
//若之前已被创建,则直接使用之前的对象
return p;
}
}
Test05.java
package demo_singleton05;
public class Test05 {
public static void main(String[] args) {
Person05 p1=Person05.getInstance();
Person05 p2=Person05.getInstance();
//判断创建出来的两个对象的内存地址是否相同
System.out.println(p1==p2);
System.out.println(p1);
System.out.println(p2);
//运行后可以发现其内存地址相同
}
}
运行结果图
vi.单例模式的应用案例-----支持静态内部类的懒汉模式
Person06.java
package demo_singleton06;
//懒汉模式(静态内部类)
public class Person06 {
//1.将构造方法私有化---防止外界创建对象
private Person06(){
}
//2.创建自己的对象(内部类)
private static class SingletonPerson{
private static final Person06 p=new Person06();
};
//3.给外界访问方式
public static Person06 getInstance() {
return SingletonPerson.p;
}
}
Test06.java
package demo_singleton06;
public class Test06 {
public static void main(String[] args) {
Person06 p1=Person06.getInstance();
Person06 p2=Person06.getInstance();
//判断创建出来的两个对象的内存地址是否相同
System.out.println(p1==p2);
System.out.println(p1);
System.out.println(p2);
//运行后可以发现其内存地址相同
}
}
运行结果图