单例模式的特点:在同一时期,某个类的对象一定最多只有1个!也许会尝试多次的获取对象,但是,获取到的一定是同一个对象!
假设项目中有King类:
public class King { }
很显然,目前它并不是单例的,因为,可以:
King k1 = new King(); King k2 = new King(); King k3 = new King();
以上代码就创建了3个King类型的对象!如果要实现单例,首先,就必须限制构造方法的访问,例如:
public class King { private King() { } }
每个类中都可以有若干个构造方法,如果某个类没有显式的声明任何构造方法,编译器就会自动添加1个公有的、无参数的构造方法!如果类中已经声明任何构造方法,则编译器不会自动添加构造方法!
由于将构造方法声明为私有的,则原有的King k1 = new King();这类代码就不能用于创建对象了!
限制构造方法的访问,其目的是“不允许随意创建对象”,并不是“不允许创建对象”,在King类的内部,还是可以创建对象的,可以添加方法,返回内部创建的对象:
public class King { private King king = new King(); private King() { } public King getInstance() { return king; } }
所以,当需要King类型的对象时,可以通过getInstance()方法来获取!
但是,以上代码是不可行的!因为,如果要调用getInstance()方法,必须先获取King的对象,而获取King对象的唯一方式就是调用getInstance()方法!为了解决这个问题,必须在getInstance()方法的声明之前添加static修饰符,最终,就可以通过类名.方法名()的语法格式来调用方法了!同时,由于“被static修饰的成员,不可以访问其它未被static修饰的成员”,所以,全局属性king也必须被static修饰:
public class King { private static King king = new King(); private King() { } public static King getInstance() { return king; } }
至此,基本的单例模式的代码就设计完成了!
以上代码是“饿汉式”的单例模式,另外,还有“懒汉式”的单例模式!
基本的懒汉式单例模式的代码是:
public class King { private static King king = null; private King() { } public static King getInstance() { if (king == null) { king = new King(); } return king; } }
注意:以上代码是多线程不安全的!
在开发领域中,只要数据的产生、变化不是开发人员预期的,就称之为“不安全”,也就是“数据安全问题”。
为了保障线程安全,应该为以上创建对象的代码片断“加锁”,例如:
public class King { private static King king = null; private King() { } public static King getInstance() { synchronized ("hello") { if (king == null) { king = new King(); } } return king; } }
当然,无论是哪个线程在什么时候执行以上代码,都必须先“锁住”代码片断后才能开始执行,是没有必要的,“锁”的性能消耗是浪费的,所以,可以进一步调整为:
public class King { private static King king = null; private King() { } public static King getInstance() { if (king == null) { // 判断有没有必要锁定接下来的代码 synchronized ("java") { if (king == null) { // 判断有没有必要创建对象 king = new King(); } } } return king; } }