女朋友:你能给我讲讲单例模式吗?
public class GirlFriend {
private String name;
private Integer age;
public GirlFriend(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "GirlFriend{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
接着程序员小强就可以new出来一个女朋友的实例了,只需要传进去姓名和年龄就可以了,如下:
public class Programmer {
public static void main(String[] args){
GirlFriend girlFriend = new GirlFriend("小美",20);
System.out.println(girlFriend.toString());
}
}
打印出的结果是GirlFriend{name='小美', age=20}
public class Programmer {
public static void main(String[] args){
GirlFriend girlFriend = new GirlFriend("小美",20);
GirlFriend girlFriend2 = new GirlFriend("小红",18);
GirlFriend girlFriend3 = new GirlFriend("小丽",19);
System.out.println(girlFriend.toString());
System.out.println(girlFriend2.toString());
System.out.println(girlFriend3.toString());
}
}
打印结果如下:
GirlFriend{name='小美', age=20}
GirlFriend{name='小红', age=18}
GirlFriend{name='小丽', age=19}
但是不久就被老板发现了,因为内存中存在多个女朋友实例对象,严重浪费了公司的资源,老板决定只能给小强分配一个女朋友,老板绞尽脑汁,终于想出了应对方法。
public class GirlFriend {
private String name;
private Integer age;
//定义一个变量来存储创建好的类实例
private static GirlFriend girlFriend = null;
//私有化构造方法,防止外部调用
private GirlFriend(String name, Integer age) {
this.name = name;
this.age = age;
}
//定义一个方法为程序员类提供女朋友实例
public static GirlFriend getGirlFriend(){
//判断存储实例是否为空
if(girlFriend==null){
//如果没值,就new出一个实例,并赋值给存储实例的变量
girlFriend = new GirlFriend("小美",28);
}
return girlFriend;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "GirlFriend{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
主要的核心思想有三点:
public class Programmer {
public static void main(String[] args){
GirlFriend girlFriend = GirlFriend.getGirlFriend();
System.out.println(girlFriend.toString());
}
}
直接使用GirlFriend类调用获取对象的getGirlFriend方法获取到实例对象,打印结果如下:
GirlFriend{name='小美', age=28}
public class Singleton{
//定义变量存放创建好的实例,因为要在静态方法中使用,所以变量也必须是静态的
private static Singleton uniqueInstance = null;
//私有化构造方法,可以在内部控制创建实例的数目
private Singleton(){
}
//定义一个方法为客户端提供类实例,synchronized同步保证线程安全
public static synchronized Singleton getInstance(){
//判断是否已经有实例
if(uniqueInstance == null){
uniqueInstance = new Singleton();
}
//有就直接用
return uniqueInstance;
}
}
这里使用到了synchronized用来保证线程安全,如果不加会带来什么问题呢?比如两个线程A和B,就有可能导致并发的问题,如图所示:
public class Singleton{
//对保存实例的变量添加volatile修饰
private volatile static Singleton instance = null;
private Singleton(){
}
public static Singleton getInstance(){
//第一次检查
if(instance == null){
//同步块,线程安全的创建实例
synchronized (Singleton.class){
//第二次检查
if(instance==null){
instance = new Singleton();
}
}
}
return instance;
}
}
这种方式即可以安全的创建线程,又不会对性能造成太大的影响。
public class Singleton{
//定义一个变量来存储创建好的实例,直接在这里创建实例,只能创建一次
//static变量在类加载时进行初始化,并且只被初始化一次。
private static Singleton uniqueInstance = new Singleton();
//私有化构造方法,可以在内部控制创建实例的数目,防止在外部创建
private Siingleton(){
}
//定义一个方法为客户端提供类实例,方法上加static将该方法变为静态
//目的是不需要对象实例就可以在外部直接通过类来调用
public static Singleton getInstance(){
//直接使用已经创建好的实例
return uniqueInstance;
}
}
单例模式作用范围:目前Java里面实现的单例是一个虚拟机的范围,虚拟机在通过自己的`ClassLoader`装载饿汉式实现的单例类时就会创建一个类实例。如果一个虚拟机中有多个类加载器或者一个机器中有多个虚拟机,那么单例就不再起作用了。
import java.util.HashMap;
import java.util.Map;
public class GirlFriend {
private String name;
private Integer age;
private static int maxNumsOfGirlFriends = 2;//最大数量
private static int number = 1;//当前编号
//定义一个变量来存储创建好的类实例
private static Map<String,GirlFriend> girlFriendMap = new HashMap<String, GirlFriend>();
//私有化构造方法,防止外部调用
private GirlFriend(String name, Integer age) {
this.name = name;
this.age = age;
}
//定义一个方法为程序员类提供女朋友实例
public static GirlFriend getGirlFriend(){
GirlFriend girlFriend = girlFriendMap.get(number+"");
if(girlFriend==null){
//new一个新实例,并放到map中,用number当做key,实例是value
girlFriend = new GirlFriend("小美",28);
girlFriendMap.put(number+"",girlFriend);
}
number++;
if(number>maxNumsOfGirlFriends){
number = 1;
}
return girlFriend;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
程序员类进行获取女朋友实例,如下:
public class Programmer {
public static void main(String[] args){
GirlFriend girlFriend1 = GirlFriend.getGirlFriend();
System.out.println(girlFriend1);
GirlFriend girlFriend2 = GirlFriend.getGirlFriend();
System.out.println(girlFriend2);
GirlFriend girlFriend3 = GirlFriend.getGirlFriend();
System.out.println(girlFriend3);
GirlFriend girlFriend4 = GirlFriend.getGirlFriend();
System.out.println(girlFriend4);
}
}
上面代码获取了四次,看看打印的结果如何:
GirlFriend@6e0be858
GirlFriend@61bbe9ba
GirlFriend@6e0be858
GirlFriend@61bbe9ba
可以看出,第一次和第三次是一样的,第二次和第四次是一样的,一共就只有两个对象,解决了这个问题。但是如何判断哪个女朋友实例是小强的哪个是小华的呢?一种简单的方法是通过给获取实例的函数getGirlFriend传参,比如小强获取的时候传如number = 1,小华的number = 2。
public class Singleton{
//类级内部类,该内部类的实例与外部类的实例没有绑定关系,
// 而且只有被调用到时才会装载,从而实现延迟加载
private static class SingletonHolder{
//静态初始化器,由jvm保证线程安全
private static Singleton instance = new Singleton();
}
private Singleton(){
}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}
当getInstance方法第一次被调用的时候,它第一次读取 SingletonHolder.instance导致SingletonHolder类得到初始化,从而创建Singleton实例。
public enum Singleton{
uniqueInstance;
//单例自己的操作函数
public void singletonOperation(){
//功能处理
}
}
使用枚举来实现单例控制更加简洁,而且无偿地提供了序列化的机制,并由JVM从根本上提供保障,绝对防止多次实例化。
热 文 推 荐
☞
☞
☞
☞
☞
点击阅读原文,即刻参加!