1、“正”操作是指当使用一个类时,先导包,然后对类进行对象实例化操作,再依靠对象调用类中方法。而反射所谓的“反”即根据实例化对象反推出其类型。
实现“反”的操作,首先要实现Object类中的一个方法:
获取Class对象信息:public final Class<?> getClass() //通过实例化对象找到对象的根源(包)
2、class类对象的三种实例化模式
反射中所有的核心操作都是通过Class类对象展开的。从JDK1.5开始在进行类定义的时候可以使用泛型进行标记,这样的用法主要是为了避免对象的向下转型。
①Object类支持:Object类可以根据实例化对象获取Class对象:public final Class<?> getClass()
Person pr = new Person() ;
class<? extends Person> cls = pr.getClass() ;
System.out.println(cls) ; //class com.demo.Person
System.out.println(cls.getName()) ; //com.demo.Person类的完整名称
②JVM直接支持:采用"类.class"的形式实例化
class<? extends Person> cls = Person.Class ;
System.out.println(cls.getName()) ; //com.demo.Person类的完整名称
③Class类支持:在Class类中提供了一个static方法:
加载类:public static Class<?> forName(String className) throws ClassNotFoundException
class<?> cls = class.forName("com.demo.Person") ;
System.out.println(cls.getName()) ;
这种模式最大的特点是可以直接采用字符串的形式定义要使用的类型。无需再使用import导包。
3、反射应用案例
class<?> cls = class.forName("com.demo.Person") ;
// Object obj = cls.newInstance() ; //实例化对象,JDK1.9之后废除,调用类中无参构造
Object obj = cls.getDeclaredConstructor().newInstance() ; //构造方法实例化
System.out.println(obj) ; //输出对象调用toString()方法
通过反射实现的对象实例化处理,依然要调用类中的无参构造,因为默认的Class类中的newInstance()方法只能够调用无参构造,所以被替代了。
4、反射与工厂设计模式
工厂设计模式的最大特点在于客户端的程序类不直接牵扯到对象的实例化管理,只与接口发生关联,通过工厂类获取指定接口的实例化对象。
标准工厂设计模式:
interface IMessage {
public void send() ;
}
class MessageImpl implements IMessage {
public void send() {
system.out.println("消息") ;
}
}
class Factory {
private Factory() {} //没有产生实例化的意义,所以构造方法私有化
public static IMessage getInstance(String className) {
if("MessageImpl".equalsIgnoreCase(className)) {
return new MessageImpl() ;
}
return null ;
}
}
public class JavaDemo {
public static void main(String[] args) {
//如果直接实例化会出现耦合问题,因为一个接口不可能只有一个子类,有多次实例化
IMessage msg = new MessageImpl() ;
//利用工厂设计模式解决问题
IMessage msg = Factory.getInstance("MessageImpl") ;
msg.send() ;
}
}
工厂设计模式属于静态设计模式,如果要追加一个子类,那么工厂类需要做出修改,增加判断。工厂设计模式最有效解决的是子类与客户端的耦合问题,解决的核心思想是提供一个工厂类作为过渡,但是接口会不断有多个子类。此时,最好的解决方案是不使用关键字new来完成,因为关键字new在使用的时候需要有一个明确的类存在。
class Factory {
private Factory() {} //没有产生实例化的意义,所以构造方法私有化
public static IMessage getInstance(String className) {
IMessage instance = null ;
try {
instance = (IMessage)class.forName(className).getDeclaredConstructor().newInstance() ;
}catch (Exception e) {
e.printStackTrace() ;
}
return instance ;
}
}
public class JavaDemo {
public static void main(String[] args) {
//不使用new关键字的工厂类设计模式
IMessage msg = Factory.getInstance("com.demo.MessageImpl") ;
msg.send() ;
}
}
【重要】进一步实现一个工厂类不仅能实现一个接口被多个子类实现,还要能实现多个接口的实现。通过泛型:
class Factory {
private Factory() {} //没有产生实例化的意义,所以构造方法私有化
public static <T> T getInstance(String className,Class<T> cls) {
T instance = null ;
try {
instance = (T)class.forName(className).getDeclaredConstructor().newInstance() ;
}catch (Exception e) {
e.printStackTrace() ;
}
return instance ;
}
}
public class JavaDemo {
public static void main(String[] args) {
IMessage msg = Factory.getInstance("com.demo.MessageImpl",IMessage.class) ;
msg.send() ;
}
}
5、反射与单例设计模式
单例设计模式的核心本质在于:类内部的构造方法私有化,在类的内部产生实例化对象之后通过static方法获取实例化对象进行类中的结构调用。单例设计模式有两类:懒汉式和饿汉式。
单例设计模式的最大特点是在整体的运行过程之中只允许产生一个实例化对象,但是,当有若干个线程时就会产生多个实例化对象,此时就不是单例设计模式了。而问题出现的原因是在实例化和返回实例化对象的过程中出现了不同步的问题。
当使用synchronized直接处理不同步问题时(public static synchronized Singleton getInstance(){})会降低程序执行效率,因为整体代码中仅需要对象实例化处理部分需要同步处理,而不必使整个过程都同步。
所以改进后的懒汉式单例设计模式:
public class BDemo {
public static void main(String[] args) throws Exception {
for(int x = 0 ; x < 3 ; x ++) {
new Thread(()->{
Singleton.getInstance().print() ;
},"单例消费端" + x).start() ;
}
}
}
class Singleton {
private static volatile Singleton instance = null ;
private Singleton() {
System.out.println(Thread.currentThread().getName() + "实例化类对象") ;
}
public static Singleton getInstance() {
if(instance == null) {
synchronized (Singleton.class) {
if(instance == null) {
instance = new Singleton() ; //当对象被实例化时,应该立刻与主内存中的数据对象保持同步,不应该保存副本,所以应使用volatile关键字
}
}
}
return instance ;
}
public void print() {
System.out.println("一只瓶子a") ;
}
}
【重要】单例设计模式:
编写饿汉式单例设计模式,并实现构造方法私有化;
在Java中哪里使用到了单例设计模式?Runtime类,Spring框架
懒汉式单例设计模式的问题?