java反射

java反射是指是在运行状态中,对于任意一个类都可以通过它的Class字节码文件得到对应的的Class类型的对象,从而获取到该类的所有内容,包括所有的属性与方法。当然也可以调用到它的所有内容。

 

那么这个字节码文件是怎么来的呢?我们在对java文件进行编译时会执行javac XXX.java,这个时候就会产生一个xxx.class文件,此即对应的字节码文件。

 

是如何通过Class字节码文件得到对应的的Class类型的对象的呢?类加载器。

 

类加载器是什么呢?

类加载器负责将class文件加载到内存中,并为之生成对应的Class对象。在java中类加载器把一个类装入JVM,需要以下几个步骤:

1.装载:查找和导入Class文件

2.链接:执行校验、准备、解析步骤,其中解析步骤是可以选择的:

a)校验:是否有正确的内部结构,并和其他类协调一致

b)准备:负责为类的静态成员分配内存,并设置默认初始化值

c)解析:将类的二进制数据中的符号引用替换为直接引用

3.初始化:对类的静态变量、静态代码执行初始化工作

类加载器的组成:

bootstrap ClassLoader 根类加载器 (负责将存话在<JAVA_HOME>\lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如rt.jar,名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机内存了中。开发者不能直接引用启动类加载器)

extension ClassLoader 扩展类加载器(这个加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载<JAVA_HOME>\lib\ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器)

Application ClassLoader 应用程序类加载器(这个类加载器由sun.misc.Lanucher$AppClassLoader实现。由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也称它为系统类加载器。它负责加载用户类路径上所指定的类库)

这三种加载器是父子关系,加载的方式为双亲委派模型(parents delegation model)。即如果一个加载器收到了类加载的请求,首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,以此类推,只有当代加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。

 

扯远了

 

说回反射。现在我们的类加载器从字节码文件中获取到的Class对象,共有以下三种方式获取

1.Class clazz = new A().getClass()

2.Class clazz = A.class;

3.Class clazz = Class.forName(<类的全路径名,如cn.zzy.demo.A>)

开发中建议用方式三,因为类的全路径名可以写在配置文件里,这样很灵活。

 

那么我们如何使用反射呢?代码如下

package ReflectDemo;

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

/**
 * Created by zuzhaoyue on 2019/2/13.
 */
public class ReflectTest {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        Class clazz = Class.forName("model.Person");
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        Class clazz4 = classLoader.loadClass("model.Person");
        System.out.println(clazz);
        System.out.println(clazz4);

        //得到所有的构造方法
        /*Constructor[] constructors = clazz.getDeclaredConstructors();
        for(Constructor constructor:constructors){
            System.out.println("构造方法:"+constructor);
            A a = (A) constructor.newInstance("aa",11);
            System.out.println("A a:"+a.getAge());
        }*/

        //得到单个带参构造方法,不带参的类似
        Constructor constructor = clazz.getConstructor(String.class,int.class);//返回构造方法对象,通过Class对象使用其中的成员变量对象
        System.out.println("带参构造方法"+constructor);
        Object p1 =  constructor.newInstance("zzy",11);
        System.out.println(p1.toString());

        //访问私有的构造方法
        Constructor privateConstructor = clazz.getDeclaredConstructor();
        System.out.println("私有构造方法:" + privateConstructor);
        privateConstructor.setAccessible(true);//暴力访问
        Object p2 = privateConstructor.newInstance();
        System.out.println(p2.toString());

        /*//获取成员变量
        Field[] fields = clazz.getDeclaredFields();//获取所有的成员变量
        for(Field field:fields){
            System.out.println("成员变量:" + field);
        }*/
        //给成员变量赋值
        Field name = clazz.getDeclaredField("name");
        name.setAccessible(true);//因为是私有的,所以又来暴力访问了
        name.set(p2,"aaaaa");//给p2的name属性赋值
        System.out.println(p2.toString());

        Field age = clazz.getDeclaredField("age");
        age.setAccessible(true);
        age.set(p2,13);
        System.out.println(p2.toString());


        //method的调用
//        Method[] methods = clazz.getMethods();//获取自己的及父亲的方法
        Method[] methods = clazz.getDeclaredMethods();//获取自己的方法,不包括父亲的
        for(Method method:methods){
            String name1 = method.getName();
            Class[] arg = method.getParameterTypes();
            System.out.println("方法名:" + name1);
            for(Class a: arg){
                System.out.println("参数类型:" + a);
            }
        }

        Method method =  clazz.getDeclaredMethod("say");//获取一个方法
        method.invoke(p2);//调用

        Method method1 = clazz.getMethod("say2",String.class);
        method1.invoke(p2,"祖祖侠");

        Method method2 = clazz.getMethod("say3",String.class);
        Object object = method2.invoke(p2,"mm");
        System.out.println("返回值:" + object);


    }
}

  

package model;

/**
 * Created by zuzhaoyue on 2019/2/14.
 */
public class Person {
    private String name;
    private int age;

    private Person()
    {

    }
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

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

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

    public void say(){
        System.out.println("大家好,我是" + this.name + ",今年" + age + "岁!");
    }
    public void say2(String friend){
        System.out.println("大家好,我是" + this.name + ",今年" + age + "岁!" + "我最好的朋友是" + friend);
    }
    public String say3(String enName){
        System.out.println("英文名是"+enName+"....瞎说一气,重点是返回值。");
        return "hello world";
    }
}

  

 

 方法具体怎么使用不是那么重要,api文档里都能找到,关键是理解反射是从字节码文件获取到Class对象,然后获取到相应的Method,Field,Constructor对象,与new Person().say()等使用是反着来的,就可以了。

 

 

posted @ 2019-02-14 16:02  zuxiaoyuan  阅读(196)  评论(0编辑  收藏  举报