Java之反射


反射:

反射之中的所有核心操作都是通过Class类展开的,可以说Class类是反射的根源所在,那么获取Class类的实例化对象,可以采用三种方式实现;

首先观察java.lang.Class类的定义:

【JDK1.0】:public final class Class extends Object implements Serializable,GenericDeclaration, Type, AnnotatedElement;

从JDK1.5开始,Class类的定义可以使用泛型进行标记,这样的用法主要是希望可以避免所谓的向下转型;

通过三种操作观察三种实例化形式:

【Object类支持】Object类可以根据实例化对象获取Class对象:

但是这种方式有一个不是缺点的缺点:如果现在只是想获取Class类对象,则必须产生指定类后才能获取。

public final Class<?> getClass();
class Person{
}
public class MAIN {
    public static void main(String[] args) {
        Person pr = new Person();   // 已经存在有指定类的实例化对象
        Class<?> cls = pr.getClass();
        System.out.println(cls.getName());  // 获取的是类的完整名称
    }
}

【JVM直接支持】:采用“类.Class”直接实例化:

  - 特点:如果是使用这种方式,那么必须导入程序对应的开发包;

class Person{
}
public class MAIN {
    public static void main(String[] args) {
        Class<?> cls = Person.class;
        System.out.println(cls.getName());  // 获取的是类的完整名称
    }
}
 

【Class类支持】:在Class类中有一个static方法:

  - 采用的是字符串的形式定义要使用的类型,并且程序中不需要编写任何的import语句;

public static Class<?> forName?(String className) throws ClassNotFoundException;
package Demo_2_27_反射1_Person
package Demo_2_27_反射1
public class Person {
}


public class MAIN {
    public static void main(String[] args) throws Exception{
        Class<?> cls = Class.forName("Demo_2_27_反射1_Person.Person");
        System.out.println(cls.getName());  // 获取的是类的完整名称
    }
}

如果此时要使用的程序类不存在,那么会出现“ClassNotFoundException”的异常。

反射实例化对象:

获取了Class对象的最大意义实际上并不是在于只是对象的实例化操作形式,更重要的是Clss类里面提供有一个对象的实例化方法(代替了关键字new):

  - 在JDK1.9之前的实例化:

@Deprecated(since="9") public T newInstance() throws InstantiationException, IllegalAccessException;

  - 在JDK1.9之后:

clazz.getDeclaredConstructor().newInstance();

通过newInstance()方法实例化Person类对象:


package Demo_2_27_反射1_Person
package Demo_2_27_反射1
public class Person {
    // 任何情况下实例化对象都要调用类的构造方法
    public Person() {
        System.out.println("******* Person类构造方法 **********");
    }

    @Override
    public String toString() {
        return "我是Person类!";
    }
}
public class MAIN {
    public static void main(String[] args) throws Exception{
        Class<?> cls = Class.forName("Demo_2_27_反射1_Person.Person");
        Object obj = cls.newInstance();// 实例化对象,JDK1.9之后被废除
        System.out.println(obj);    // 输出对象调用toString()方法
    }
}

输出结果:

【cls.newInstance()等价于关键字new】:******* Person类构造方法 **********

【System.out.println(obj);】:我是Person类!

 

使用newInstance()方法依然要调用类的无参构造方法,其本质等价于“类 对象 = new 类()”,相当于隐含了关键字new,而直接使用字符串进行替代。

JDK1.9之后newInstance()被替代了:

public class MAIN {
    public static void main(String[] args) throws Exception{
        Class<?> cls = Class.forName("Demo_2_27_反射1_Person.Person");
        Object obj = cls.getDeclaredConstructor().newInstance();
        System.out.println(obj);    // 输出对象调用toString()方法
    }
}

因为默认的Class类中的newInstance()方法只能够调用无参构造,被认为描述不正确,所以将其变换了形式。

 反射与工厂设计模式:

静态工厂类:

工厂模式是避免客户端直接与类进行交集,通过返回接口的方式进行安全使用;

interface IMessage {
    public void send();
}

class NetMessage implements IMessage {
    @Override
    public void send() {
        System.out.println("发送网络消息!!");
    }
}

class Factory {
    private Factory() {
    } // 工厂类不需要进行实例化,所以进行构造方法私有化

    public static IMessage getInstance(String className) {   // 因为不会实例化,所以方法都使用static修饰,方便调用
        if ("NetMessage".equals(className)) {    // 传入的类名称判断
            return new NetMessage();
        }
        return null;
    }
}

public class MAIN {
    public static void main(String[] args) {
        IMessage msg = Factory.getInstance("NetMessage");
        assert msg != null;
        msg.send();
    }
}

以上的工厂设计模式属于静态工厂模式,最大的缺点就是,当需要追加一个接口的子类的时候,那么工厂类必须进行修改。

如果现在再追加一个子类class CloudMessage:

class CloudMessage implements IMessage{
    @Override
    public void send() {
        System.out.println("云消息!!");
    }
}

动态工厂类:

那么现在的工厂类中的判断就必须进行修改:

class Factory {
    private Factory() {
    } // 工厂类不需要进行实例化,所以进行构造方法私有化

    public static IMessage getInstance(String className) {   // 因为不会实例化,所以方法都使用static修饰,方便调用
        if ("NetMessage".equals(className)) {    // 传入的类名称判断
            return new NetMessage();
        }else if ("CloudMessage".equals(className)){
            return new CloudMessage();
        }
        return null;
    }
}

如果不增加一个对CloudMessage的判断对象获取,那么将无法获取CloudMessage的实例化对象。

随着接口的使用,可能就会产生越来越多的子类,那么这样的修改是会让人无法接受的,所以就需要进行改进为另一种方式。

这种时候最好的解决方案就是不适用关键字new来完成,因为关键字new的使用需要一个明确的类存在。而newInstance()的方法只需要一个表示类名称的字符串即可应用。

现在对工厂类进行修改:

package Demo_2_27_反射与工厂设计模式;

interface IMessage {
    public void send();
}

class NetMessage implements IMessage {
    @Override
    public void send() {
        System.out.println("发送网络消息!!");
    }
}
class CloudMessage implements IMessage{
    @Override
    public void send() {
        System.out.println("云消息!!");
    }
}
class Factory {
    private Factory() {
    } // 工厂类不需要进行实例化,所以进行构造方法私有化

    public static IMessage getInstance(String className) {   // 因为不会实例化,所以方法都使用static修饰,方便调用
        IMessage instance = null;
        try {
            instance = (IMessage) Class.forName(className).getDeclaredConstructor().newInstance();  // 使用反射机制获取子类对象
        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }
}

public class MAIN {
    public static void main(String[] args) {
        IMessage msg = Factory.getInstance("Demo_2_27_反射与工厂设计模式.CloudMessage");
        assert msg != null;
        msg.send();
    }
}

此时再进行子类对象的追加的时候,工厂类都不会进行任何的修改。

静态和动态都是通过名称寻找,不同的是静态需要先确保子类存在并能通过识别判断,而动态则只需要确保子类存在即可,因为反射的机制会自动去寻找存在的子类并创建实例化对象。

思考(多接口的工厂模式):

此时的工厂类已经看似完善,但是在实际情况中,存在大量的接口,并且这些接口都可能通过工厂类进行实例化,所以工厂类不应该只是为某一个类进行服务,而是应该为所有的接口进行服务。

此时工厂类就需要再进行接口的泛型化:

interface IMessage {
    public void send();
}
interface IService{
    public void service();
}
class HouseService implements IService {
    @Override
    public void service() {
        System.out.println("【住宿服务】");
    }
}

class NetMessage implements IMessage {
    @Override
    public void send() {
        System.out.println("【发送网络消息】");
    }
}
class CloudMessage implements IMessage{
    @Override
    public void send() {
        System.out.println("【云消息】");
    }
}
class Factory {
    private Factory() {
    } // 工厂类不需要进行实例化,所以进行构造方法私有化

    @SuppressWarnings("unchecked")  // 强制转型的时候会有警告,进行压制即可
    public static  T getInstance(String className,Class cls) {    // 传入的参数1:接口的子类,包.类名称;传入的参数2:接口的类型
        T instance = null;
        try {
            instance = (T) Class.forName(className).getDeclaredConstructor().newInstance();  // 使用反射机制获取子类对象
        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }
}

public class MAIN {
    public static void main(String[] args) {
        IMessage msg = Factory.getInstance("Demo_2_27_反射与工厂设计模式.CloudMessage",IMessage.class);
        msg.send();
        IService sv = Factory.getInstance("Demo_2_27_反射与工厂设计模式.HouseService",IService.class);
        sv.service();
    }
}

 此时的工厂类不再受限于子类和接口追加。

未完待续。。。