Q1:什么是单例?
A:全局唯一的对象,在运行过程中某类只有一个实例。
这个定义很像是一个静态变量呀!获取单例是否可以这样设计
public class A{
public static A a = new A();
}
public class client{
public static void main(String... args){
System.out.println(A.a.hashCode());//846492085
System.out.println(A.a.hashCode());//846492085
}
}
Q:这样的设计做到了获取单例,但是它又有什么缺点呐?
A:将对象赋值给静态变量,·意味着当类加载时就需要获取到实例·,当许多耗费资源的不同对象需要在加载时被获取但是运行中又没有被使用到时,这个方法就似乎不太可行了。
Q:对象在需要使用的时候才被创建,按照这个思路设计:类中有一个方法将变量赋给静态变量,调用方法时,才将变量赋值给静态变量
public class A{
public static A a;
public A getA(){
if(a == null){
this.a = new A();
}
return this.a;
}
}
public class Client {
public static void main(String[] args) {
A a = new A();
A a1 = new A();
System.out.println(a.getA().hashCode());//895947612
System.out.println(a1.getA().hashCode());//895947612
System.out.println(a.hashCode());//1096283470
System.out.println(a1.hashCode());//152005629
}
}
Q:调用方法返回静态变量成功的返回了单例,执行过程中对象却不是同一个,有什么方法解决?
A:不能new 对象,需要修改构造器的修饰符,没有了a 和 a1这样的实例,还想要调用方法,既用类名.方法() 需要修改方法的修饰符
public class A {
public static A a ;
private A(){}
public static A getA(){
if(a == null){
a = new A();
}
return a;
}
}
public class Client {
public static void main(String[] args) {
A instance1 = A.getA();
A instance2 = A.getA();
System.out.println(instance1.equals(instance2));//true
}
}
完整代码和线程安全单例模式代码
饱汉和饿汉式不做赘述,取决于静态变量什么时候被赋值。
public class MyObject {
private static MyObject instance = null ;
private MyObject(){
}
public static MyObject getInstance(){
if(instance == null){
synchronized (MyObject.class){
//线程安全,锁(this)(class)(对象)分别有什么情况
//https://blog.csdn.net/weixin_42476601/article/details/86065377
if(instance == null){
instance = new MyObject();
}
}
}
return instance;
}
}
Q2:如何判断获取到的对象是否是同一个实例呐?
A:hashCode()
Q3:获取类的实例有几种方式?
A:1)使用new关键字
2)利用反射,调用Class对象的newInstance()方法
3)调用clone()方法,对现有实例的拷贝
4)利用反序列化,通过ObjectInputStream的readObject()方法反序列化类
Q4:使用new关键字能获取单实例吗?
A:使用new 关键词获取类的实例,调用了类的public 构造方法。public 修饰的构造器让该类被任意类调用获取实例(非单例)
public class A {
//javabean 提供了一个public的构造器
}
public class Client {
public static void main(String[] args) {
A a1 = new A();
A a2 = new A();
System.out.println(a1.hashCode()); //846492085
System.out.println(a2.hashCode()); //1096283470
}
}
要想获取唯一实例,就不能对外提供可以调用的构造方法,需要修改构造器访问符为private!!
public class A {
private A(){}
}
Q5:私有的构造器有什么问题?
A:private修饰的方法,能被本类中的代码调用以及使用 instance.method()调用,私有的构造器想要被调用需要先有实例,获取实例又需要调用构造器,出现了先有鸡还是先有蛋的问题。因此使用instance.method()的方式调用私有构造器不可行。
Q6:所以想要调用该私有构造器还有什么办法?
A:本类中的方法调用构造方法
public class A {
private A(){}
A getA(){
return new A();
}
}
Q7:调用这个getA方法就能获取实例了吗?
A:想要调用getA方法还是需要先有实例,instance.method() 不可行的情况下,考虑class.method()调用方法,因此需要对修饰符进行修改,public static 修饰的方法才能使用类名.方法的方式调用。
public class A {
private A(){}
public static A getA(){
return new A();
}
}
public class Client {
public static void main(String[] args) {
A a = A.getA();
A a1 = A.getA();
System.out.println(a.hashCode());//895947612
System.out.println(a1.hashCode());//846492085
}
}
现在确保A.getA()返回的是一个全局唯一的对象就可以