反射

认识发射

反射指的是对象的反向处理操作,根据对象倒推类的组成。既然是反向处理,我们先来观察一下"正"的操作。在默认情况下,必须要先导入一个包,而后才能产生类的实例化对象

范例:观察正常处理

import java.util.Date;

public class Test {
    public static void main(String[] args) throws Exception{
        Date date = new Date();
    }
}

以上是我们正常的关于对象的处理流程:根据**包名.类名找到类** 。所谓的"反"指的是根据对象来取得对象的来源信息,而这个"反"的操作核心的处理就在于Object类的一个方法: 取得Class对象:

* @return The {@code Class} object that represents the runtime
*         class of this object.
* @jls 15.8.2 Class Literals
*/
public final native Class<?> getClass();

在反射的世界里面,看重的不再是一个对象,而是对象身后的组成(类、构造、普通、成员等)

class对象的三种实例化方式

Class类是描述整个类的概念,也是整个反射的操作源头,在使用Class类的时候需要关注的依然是这个类的对象。 任何一个类的class对象由JVM加载类后产生(该对象在JVM中全局唯一),用户只能调用指定方法来取得该对象。

而这个类的对象的产生模式一共有三种:

  1. 任何类的实例化对象可以通过调用Object类提供的getClass()取得该类的Class对象
  2. 类名称.Class”可以直接根据某个具体类来取得其Class对象
  3. 调用Class类的静态方法:public static Class<?> Class.forName(String className) throws ClassNotFoundException 传入类的全名称来取得其Class对象
import java.util.Date;

public class Test {
    public static void main(String[] args) {
        Date date = new Date();
        // 通过类对象.getClass()
        System.out.println(date.getClass());
        // 通过类名称.class
        System.out.println(Date.class);
        // 通过调用Class类提供的静态方法forName(String className)
        try {
            System.out.println(Class.forName("java.util.Date"));
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

在以上给出的三个方法中我们可以发现,除了第一种方法会产生Date类的实例化对象之外,其他的两种都不会产生 Date类的实例化对象。

于是取得了Class类对象有一个最直接的好处:可以通过反射实例化对象,在Class类中定义有如下方法:

public T newInstance()
        throws InstantiationException, IllegalAccessException

范例:反射实例化对象

import java.util.Date;

public class Test {
    public static void main(String[] args) throws Exception{
        // 取得类的实例化对象
        Class<Date> cls = (Class<Date>)Class.forName("java.util.Date");
        // 通过反射取得Date类实例化对象
        Date date = cls.newInstance();  // 实例化对象,等价于 new java.util.Date() 
        System.out.println(date);
    }
}

现在发现除了关键字new之外,对于对象的实例化模式有了第二种做法,通过反射进行

反射与工厂设计模式

工厂设计模式曾经给过原则:如果是自己编写的接口,要想取得本接口的实例化对象,最好使用工厂类来设计。但是也需要知道传统工厂设计所带来的问题。

传统工厂类:https://blog.csdn.net/sifanchao/article/details/83420681
传统工厂类在实际开发之中根本用不到。(问题就在于new)。每增加一个接口的子类就需要修改工厂类
如果要想解决关键字new带来的问题,最好的做法就是通过反射来完成处理,因为Class类可以使用newInstance() 实例化对象,同时Class.forName()能够接收类名称。

interface IFruit{
    void eat();
}
class Apple implements IFruit {
    @Override
    public void eat() {
        System.out.println("吃苹果");
    }
}
class Orange implements IFruit{
    @Override
    public void eat() {
        System.out.println("吃橘子");
    }
}
class FruitFactory {
    public FruitFactory() {}
    public static IFruit getInstance(String className) {
        IFruit fruit = null;
        try {
            fruit = (IFruit) Class.forName(className).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return fruit;
    }
}
public class Test1 {
    public static void main(String[] args) throws Exception{
        IFruit fruit = FruitFactory.getInstance("www.bit.MyTest.Apple");
        fruit.eat();
    }
}

引入反射后,每当新增接口子类,无需去修改工厂类代码就可以很方便的进行接口子类扩容

反射与类操作

利用反射可以做出一个对象具备的所有操作行为,最为关键的是这一切的操作都可以基于Object进行

取得父类信息

在java中任何的程序类都一定会有父类,在Class类中就可以通过如下方法来取得父类或者实现的父接口:

  • 取得类的包名称
    public Package getPackage() {
        return Package.getPackage(this);
    }
  • 取得父类的Class对象
    public native Class<? super T> getSuperclass();
  • 取得实现的父接口
    public Class<?>[] getInterfaces()

范例:取得包名称、父类、父接口

interface IFruit{}
interface IMessage{}
class Person{}
class Student extends Person implements IFruit,IMessage{}
public class Test1 {
    public static void main(String[] args) throws Exception{
        Class<?> cls = Student.class;  // 取得Class类对象
        // 取得Package名称
        System.out.println(cls.getPackage().getName());
        // 取得父类名称
        System.out.println(cls.getSuperclass().getName());
        // 取得实现的父接口对象 
        Class<?>[] iclass = cls.getInterfaces();
        for (Class<?> classes: iclass){
            System.out.println(classes.getName());
        }
    }
}

Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.MyTest.Test1
www.bit.MyTest
www.bit.MyTest.Person
www.bit.MyTest.IFruit
www.bit.MyTest.IMessage

反射调用构造—Constructor(描述类中构造方法)

  • 取得类中指定public权限参数类型的构造
    @CallerSensitive
    public Constructor<T> getConstructor(Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        return getConstructor0(parameterTypes, Member.PUBLIC);
    }
  • 取得类中指定参数(包含private权限)类型的构造
    @CallerSensitive
    public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        return getConstructor0(parameterTypes, Member.DECLARED);
    }
  • 取得类中public权限的所有参数类型构造
    @CallerSensitive
    public Constructor<?>[] getConstructors() throws SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        return copyConstructors(privateGetDeclaredConstructors(true));
    }
  • 取得类中所有参数(包含private权限)类型构造
    @CallerSensitive
    public Constructor<?>[] getDeclaredConstructors() throws SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        return copyConstructors(privateGetDeclaredConstructors(false));
    }

范例:取得类中构造信息

import java.lang.reflect.Constructor;

class Person {
    public Person(){}
    protected Person(String name){}
    private Person(String name,int age){}
    public Person(String name,int age,String id){}
}
public class Test1 {
    public static void main(String[] args) throws Exception{
        Class<?> cls = Person.class;
        // 取得类中指定`public权限`参数类型的构造
        Constructor<?> constructor1 = cls.getConstructor(String.class.getClasses());
        System.out.println(constructor1);
        System.out.println("------------------------------------");

        // 取得类中`public权限`的所有参数类型构造
        Constructor<?>[] constructor2 = cls.getConstructors();
        for (Constructor<?> constructor : constructor2){
            System.out.println(constructor);
        }
        System.out.println("------------------------------------");

        // 取得类中指定参数(`包含private权限`)类型的构造
        Constructor<?> constructor3 = cls.getDeclaredConstructor(String.class,int.class);
        System.out.println(constructor3);
        System.out.println("------------------------------------");
        
        // 取得类中所有参数(`包含private权限`)类型构造
        Constructor<?>[] constructors = cls.getDeclaredConstructors();
        for (Constructor<?> constructor : constructors){
            System.out.println(constructor);
        }
    }
}

Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.MyTest.Test1
public www.bit.MyTest.Person()
------------------------------------
public www.bit.MyTest.Person(java.lang.String,int,java.lang.String)
public www.bit.MyTest.Person()
------------------------------------
private www.bit.MyTest.Person(java.lang.String,int)
------------------------------------
public www.bit.MyTest.Person(java.lang.String,int,java.lang.String)
private www.bit.MyTest.Person(java.lang.String,int)
protected www.bit.MyTest.Person(java.lang.String)
public www.bit.MyTest.Person()

Process finished with exit code 0
  • Constructor类提供了实例化对象的方法
    @CallerSensitive
    public T newInstance()
        throws InstantiationException, IllegalAccessException

在定义简单java类的时候一定要保留有一个无参构造
范例:观察Class实例化对象的问题

class Person {
    private String name;
    private int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Test1 {
    public static void main(String[] args) throws Exception{
        Class<?> cls = Person.class;
        System.out.println(cls.newInstance());
    }
}

Exception in thread "main" java.lang.InstantiationException: www.bit.MyTest.Person
	at java.lang.Class.newInstance(Class.java:427)
	at www.bit.MyTest.Test1.main(Test1.java:150)
Caused by: java.lang.NoSuchMethodException: www.bit.MyTest.Person.<init>()
	at java.lang.Class.getConstructor0(Class.java:3082)
	at java.lang.Class.newInstance(Class.java:412)
	... 1 more

Process finished with exit code 1

Class类通过反射实例化类对象的时候,只能够调用类中的无参构造。如果现在类中没有无参构造则无法使用Class 类调用,只能够通过明确的构造调用实例化处理

范例:通过Constructor类实例化对象

class Person {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Test1 {
    public static void main(String[] args) throws Exception{
        Class<?> cls = Person.class;
        // 取得指定参数类型的构造方法对象
        System.out.println(cls.getConstructor(String.class,int.class).newInstance("改革开放",40));
    }
}

反射调用普通方法

  • 取得类中指定public权限参数类型的普通方法
    @CallerSensitive
    public Method getMethod(String name, Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        Method method = getMethod0(name, parameterTypes, true);
        if (method == null) {
            throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
        }
        return method;
    }
  • 取得类中指定参数(包含private权限)类型的普通方法
    @CallerSensitive
    public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
        if (method == null) {
            throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
        }
        return method;
    }
  • 取得类中public权限的所有参数类型普通方法
    @CallerSensitive
    public Method[] getMethods() throws SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        return copyMethods(privateGetPublicMethods());
    }
  • 取得类中所有参数(包含private权限)类型普通方法
    @CallerSensitive
    public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
        if (method == null) {
            throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
        }
        return method;
    }

范例:取得一个类中的全部普通方法

import java.lang.reflect.Method;

class Person {
    private String name;
    private int age;
    public Person(){}
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
public class Test {
    public static void main(String[] args) throws Exception{
        Class<?> cls = Person.class;
        Method[] methods = cls.getDeclaredMethods();
        for (Method method : methods){
            System.out.println(method);
        }
    }
}

Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.MyTest.Test1
public java.lang.String www.bit.MyTest.Person.toString()
public java.lang.String www.bit.MyTest.Person.getName()
public void www.bit.MyTest.Person.setName(java.lang.String)
public void www.bit.MyTest.Person.setAge(int)
public int www.bit.MyTest.Person.getAge()

Process finished with exit code 0

上面程序编写的简单java类中的getter、setter方法采用的都是明确的对象调用。
而现在有了反射机制处理之后,即使你没有明确的Person类型对象(依然需要实例化对象,Object对象描述,所有 的普通方法必须在有实例化对象之后才可以进行调用),就可以通过反射调用。

  • Method类中有提供调用类中普通方法的API
import java.lang.reflect.Method;

class Person {
    private String name;
    private int age;
    public Person() {}
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Test1 {
    public static void main(String[] args) throws Exception{
        // 1.拿到Person的Class对象
        Class<?> cls = Person.class;
        // 2.创建Person实例化对象
        Person person = (Person) cls.newInstance();
        // 3.拿到setName的Method对象
        Method setNameMethod = cls.getMethod("setName", String.class);
        // 4.通过invoke进行调用,相当于Person对象.setName("改革开发")
        setNameMethod.invoke(person,"改革开发");

        Method getNameMethod = cls.getMethod("getName");
        // 相当于Person对象.getName()
        System.out.println(getNameMethod.invoke(person));
    }
}

此类操作的好处是:不再局限于某一具体类型的对象,而是可以通过Object类型进行所有类的方法调用

反射调用类中属性

在之前已经成功的实现了类的构造调用、方法调用,除了这两种模式之外还有类中属性调用。

前提:类中的所有属性一定在类对象实例化之后才会进行空间分配,所以此时如果要想调用类的属性,必须保证有实例化对象。通过反射的newInstance()可以直接取得实例化对象(Object类型)

  • 取得类中指定public权限参数类型的属性
    @CallerSensitive
    public Field getField(String name)
        throws NoSuchFieldException, SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        Field field = getField0(name);
        if (field == null) {
            throw new NoSuchFieldException(name);
        }
        return field;
    }
  • 取得类中指定参数(包含private权限)类型的属性
    @CallerSensitive
    public Field getDeclaredField(String name)
        throws NoSuchFieldException, SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        Field field = searchFields(privateGetDeclaredFields(false), name);
        if (field == null) {
            throw new NoSuchFieldException(name);
        }
        return field;
    }
  • 取得类中public权限的所有参数类型属性
    @CallerSensitive
    public Field[] getFields() throws SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        return copyFields(privateGetPublicFields(null));
    }
  • 取得类中所有参数(包含private权限)类型属性
    @CallerSensitive
    public Field[] getDeclaredFields() throws SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        return copyFields(privateGetDeclaredFields(false));
    }

范例:取得类中全部属性

import java.lang.reflect.Field;

class Person {
    public String name;
    public int age;
    protected String id;
}
class Student extends Person{
    private String school;
}

public class Test1 {
    public static void main(String[] args) throws Exception{
        Class<?> cls = Class.forName("www.bit.MyTest.Student");
        {
            Field[] fields = cls.getFields();
            for (Field field : fields){
                System.out.println(field);
            }
        }
        System.out.println("-----------------------");
        {
            Field[] fields = cls.getDeclaredFields();
            for (Field field : fields){
                System.out.println(field);
            }
        }
    }
}

Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.MyTest.Test1
public java.lang.String www.bit.MyTest.Person.name
public int www.bit.MyTest.Person.age
-----------------------
private java.lang.String www.bit.MyTest.Student.school

Process finished with exit code 0

因为在实际开发之中,属性基本上都会进行封装处理,所以没有必要去关注父类中的属性。也就是说以后所取得的属性都以本类属性为主。

而后就需要关注属性的核心描述类:java.lang.reflect.Field,在这个类之中有两个重要方法:

  • 设置属性内容 :
 public void set(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException 
  • 取得属性内容 :
 public Object get(Object obj) throws IllegalArgumentException, IllegalAccessException
  • 取得属性的类型
public Class<?> getType()

在这里插入图片描述
在这里插入图片描述

  • 动态设置封装

Constructor、Method、Field类都是AccessibleObject子类。
AccessibleObject提供动态设置封装方法(在本次JVM进行中有效且只能通过反射调用)

public void setAccessible(boolean flag) throws SecurityException {
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) sm.checkPermission(ACCESS_PERMISSION);
    setAccessible0(this, flag);
}

setAccessible(true)取消了Java的权限控制检查(注意不是改变方法或字段的访问权限),调用了类的private属性,并且能够修改private成员变量的值

import java.lang.reflect.Field;

class Person {
    public Person(){}
    public String name;
    private int age;
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Test1 {
    public static void main(String[] args) throws Exception{
        Class<?> cls = Person.class;
        Person person = (Person) cls.getConstructor().newInstance();

        Field nameField = cls.getField("name");
        nameField.set(cls,"刘德华");
        System.out.println(nameField.get(cls));
        System.out.println(nameField.getType().getName());

        Field ageField = cls.getDeclaredField("age");
        ageField.setAccessible(true);
        ageField.set(cls,45);
        System.out.println(ageField.get(cls));
        System.out.println(ageField.getType().getName());
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章