Java反射机制

 ̄綄美尐妖づ 提交于 2020-01-13 04:36:05

一切的操作都将使用Object完成,类,数组的引用都可以使用Object来接收

1,认识Class类

以前要是想知道一个类中的属性,成员变量,方法等等的信息的话,需要通过new这个类的对象才能得到这些信息,但是在Java中,也允许一个实例化对象找到一个类的完整的信息,这个类就是Class类,Class类是继承Object类的.

正常方式:  引入需要的"包.类"名称 -->通过new实例化-->取得实例化对象

反射方式:  实例化对象-->getClass()方法(从Object类中继承而来),这里是得到Class类对象-->得到完整的"包.类"名称

getClass()这个方法的返回值是Class类,实际上该类是Java反射的源头

Class类中没有定义构造方法,那么得到Class类对象的方法有3种:

(1)使用forName()的静态方法实例化对象  (2) 类.class  (3)对象.getClass()

package cn.reflect;

class Demo{
    
}
public class RefDemo01 {
    public static void main(String[] args) throws ClassNotFoundException {
        Class c1 = Class.forName("cn.reflect.Demo");
        Class c2 = Demo.class;
        Class c3 = new Demo().getClass();
        System.out.println("类名称:"+c1.getName());
        System.out.println("类名称:"+c2);
        System.out.println("类名称:"+c3);
    }
}

3种实例化Class对象的方式是一样的,但是forName()这个方式比较常用,它只需要传入类所在的位置的名称的字符串参数就可以了,这样使程序具有很大的灵活性.

2,Class类的使用

2.1通过无参构造函数实例化对象

Class类在开发中最常用的是,利用forName这个方法,传入完整的包.类的路径的字符串形式,来实例化一个类的对象

想要通过Class本身来实例化其他类的对象,则可以使用Class类中的newInstance()方法来完成,但是要保证被实例化的类中存在这一个无参的构造方法

(1)forName传入需要实例化的类的完整包.类名称

(2)使用newInstance()方法,是实例化需要被实例化的对象,需要强转

package cn.reflect;

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;
    }
    
}
public class RefInstance {

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        Class clazz = Class.forName("cn.reflect.Person");
        Person p = (Person)clazz.newInstance();
        p.setName("wjd");
        p.setAge(25);
        System.out.println("name:"+p.getName()+"  age:"+p.getAge());//name:wjd  age:25
    }

}

上述代码中,Person类并没有new一个新的对象,而是通过Class类,来完成了对Person类中的属性的访问.一般做在使用Class类来实例化对象时,一定要在类中编写无参的构造方法

2.2调用有参构造实例化对象

(1)通过Class类中的getConstructors()取得本类中的所有的构造方法

(2)向构造方法中传递一个对象数组中去,里面包含了构造方法是所需的各个参数

(3)通过Constructor实例化对象

package cn.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

class Worker{
    private String name;
    private int age;
    public Worker(String name,int age){
        this.setName(name);
        this.setAge(age);
    }
    public void setName(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }
    
    public void setAge(int age){
        this.age = age;
    }
    public int getAge(){
        return age;
    }
}
public class InstanceDemo01 {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Class c = Class.forName("cn.reflect.Worker");
        Constructor[] con = c.getConstructors();
        Worker worker = (Worker)con[0].newInstance("吴杰栋",25);//Worker中构造方法中有参数的,这里实例化的时候传参数,注意顺序,只有一个构造函数,所以数组中的0就可以
        System.out.println("age:"+worker.getAge() +" name:"+ worker.getName());
    }

}

在上述代码中,newInstance()方法是Constructor类中的, 查看API可以发现这个方法中传入的参数都是对象,25其实是Integer对象(在使用是进行了自动拆箱)

在java类中是有可能存在多个构造函数的,它们只是构造函数中的参数类型不同而已,在API中,查阅到Class类中newIntance()这个类只能获取到带有无参构造函数的对象,那么带有参数的构造函数的时候,我们该如何获取该类的对象呢?

(1),构造函数没有参数的时候,产生该类对象,直接通过Class类中的newIntance()来获取

(2).构造函数有参数的时候,通过Class类中getConstructor()方法,在方法中传入构造函数参数类型的class文件,返回值是一个Constructor对象,查阅API发现在Constructor这个类中,也有一个带这参数类型的newIntance的方法,这样就能获取到带有参数类型的构造函数的类的对象了

package cn.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
//拿Person类中的构造函数
public class ReflectDemo2 {
    public static void main(String[] args) throws Exception {
        //createNewObject();
        createNewObject_2();
    }
    
    public static void createNewObject_2() throws NoSuchMethodException, SecurityException, ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        String name = "cn.bean.Person";
        Class<?> clazz = Class.forName(name);
        //获取到了Person中带有参数类型的构造方法
        Constructor<?> con = clazz.getConstructor(int.class,String.class);
        //这里通过constructor类中的方法来初始化类
        Object obj = con.newInstance(23,"wjd");
    }

    public static void createNewObject() throws Exception{
        String name = "cn.bean.Person";
        //找寻该文件的类文件并加载进内存,并产生class对象
        Class<?> clazz = Class.forName(name);
        //产生该类的对象
        Object obj = clazz.newInstance();
    }
}

 

3,反射的应用---得到类的属性

在反射中得到一个类的完成结构的话,使用到java.lang.reflect包中的以下几个类

(1) Constructor:表示类中的构造方法 (2)Field:表示类中的属性  (3)Method:表示类中的方法

Constructor类中常用的方法

序号

方法

描述

1

public int getModifiers() 得到构造函数的修饰符

2

public String getName() 得到构造函数的名称

3

public Class<?>[] getParameterTypes() 得到构造函数中的参数类型

4

public String toString() 返回此构造函数的信息

5

public T newInstance(Object... initargs) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException 向构造函数中传入参数,实例化对象,就是需要拿到有参数构造函数的类的对象的时候,就需要用到这个

Field类中常用的方法

序号

方法

描述

1 public Object get(Object obj) throws IllegalArgumentException, IllegalAccessException 得到一个对象中属性的内容
2 public void set(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException 设置指定对象中属性的具体内容
3 public int getModifiers() 得到属性的修饰符
4 public String getName() 返回此属性的名称
5 public String toString() 返回此Field类的信息
6 public void setAccessible(boolean flag) throws SecurityException 设置一个属性是否被外部访问(需要访问私有属性需要用这个方法true一下)暴力访问
7 public static void setAccessible(AccessibleObject[] array, boolean flag) throws SecurityException 设置一组属性是否被外部访问

Method类中常用的方法

序号 方法 描述
1 public int getModifiers() 得到方法的访问修饰符
2 public String getName() 得到方法的名称
3 public Class<?>[] getParameterTypes() 得到方法的全部参数类型
4 public Class<?> getReturnType() 得到方法的全部返回值类型
5 public Class<?>[] getExceptionTypes() 得到一个方法全部抛出的类型
6 public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException 当通过反射得到类的对象,类的方法的名称,方法中的参数的时候,用这个方法,我们就能完美的运行该类

这三个类都是AccessibleObject类的子类

以上三个类基本覆盖了反射所需要用到的类包括类中的方法,必须要熟练记忆

在反射操作中,得到一个类中的全部属性,有两种不同的操作

(1)得到实现的接口或者父类中的公共属性     在Class类中的getFields()方法

(2)得到本类中的所有属性,包括私有属性      在Class类中的getDeclaredFields()

根据以前学过的知识,要想操作一个类中的属性,肯定得要先得到一个类的对象,那么在反射中,也不例外,也需要通过newIntance()来得到一个类的对象,这样才能去取得类中的属性

总之你想得到我的属性,必须给我对象

package cn.reflect;

import java.lang.reflect.Field;

//拿Person类中的字段
public class ReflectDemo3 {

    /**
     * @param args
     * @throws Exception 
     */
    public static void main(String[] args) throws Exception {
        getField();
    }
//拿到字节码文件的中的字段
    public static void getField() throws Exception {
        Class<?> clazz = Class.forName("cn.bean.Person");
        //拿到Person类中的age对象,返回的是一个Field的对象
    //Field f = clazz.getField("age");//只能获取共有的的
        Field field = clazz.getDeclaredField("age");//可以获取本类.包含私有
        //对私有的属性,取消其权限的检查,暴力访问
        field.setAccessible(true);
        //设置属性的时候,必须要用对象来设置属性,所以必须先弄个对象出来
        Object obj = clazz.newInstance();
        field.set(obj, 23);
        Object o = field.get(obj);
        System.out.println(o);
    }

}

 

4,反射的应用---得到类中的方法

得到类中的方法,是使用Class类中的getMethod()方法,此方法返回的是一个Method类的对象数组

需要运行一个类

(1)需要先得到该类的方法,方法中的参数的class对象

(2)然后得到该类的对象

(3)调用Method中的invoke方法,将对象,和方法的参数传入该方法

package cn.reflect;

import java.lang.reflect.Method;

//获取Person类中的方法
public class ReflectDemo4 {
    public static void main(String[] args) throws Exception {
    //    getMethod();
        getMethod_2();
    }

    public static void getMethod_2() throws Exception {
        Class<?> clazz = Class.forName("cn.bean.Person");
        Method method = clazz.getMethod("paramMethod", String.class,int.class);
        //方法要运行起来,也需要对象的支持,所以要造个对象出来
        Object obj = clazz.newInstance();
        //Method中有个方法,能传入对象和方法,这样的话,方法就能完美的运行起来了
        method.invoke(obj, "wjd",23);
    }

    public static void getMethod() throws Exception {
        Class<?> clazz = Class.forName("cn.bean.Person");
        Method[] methods = clazz.getMethods();//这个获取的都是共有的方法
        methods = clazz.getDeclaredMethods();//只能获取本类中的方法,但是包含了私有的方法
        for(Method method : methods){
            System.out.println(method);
        }
    }
}

5,反射的应用---得到类中的的字段(暴力访问的话,可以得到类中的私有字段)

package cn.reflect;

import java.lang.reflect.Field;

//拿Person类中的字段
public class ReflectDemo3 {

    /**
     * @param args
     * @throws Exception 
     */
    public static void main(String[] args) throws Exception {
        getField();
    }
//拿到字节码文件的中的字段
    public static void getField() throws Exception {
        Class<?> clazz = Class.forName("cn.bean.Person");
        //拿到Person类中的age对象,返回的是一个Field的对象
    //Field f = clazz.getField("age");//只能获取共有的的
        Field field = clazz.getDeclaredField("age");//可以获取本类.包含私有
        //对私有的属性,取消其权限的检查,暴力访问
        field.setAccessible(true);
        //设置属性的时候,必须要用对象来设置属性,所以必须先弄个对象出来
        Object obj = clazz.newInstance();
        field.set(obj, 23);
        Object o = field.get(obj);
        System.out.println(o);
    }

}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!