Java反射机制

介绍

Java反射机制是很多框架使用的基础.

当程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言。
我们认为java并不是动态语言,但是它却有一个非常突出的动态相关机制,俗称:反射。

反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意一个方法和属性;
这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

反射机制主要提供了以下功能:
1,在运行时判断任意一个对象所属的类;
2,在运行时构造任意一个类的对象;
3,在运行时判断任意一个类所具有的成员变量和方法;
4,在运行时调用任意一个对象的方法;
5,生成动态代理。

Demo

为了学习,我做了一个测试的demo:
基本框架如下:

public class ReflectionTest {

    public static void main(String[] args) {
        test7();

    }

    /****测试方法****/
}

class F{
    public String name;
    public int id;
    public F(){

    }
    public F(String name,int id){
        this.name=name;
        this.id=id;
    }
} 

F是用来做例子的类,main函数里调一个个测试的demo,这样就只需要建一个文件了。下面是一个一个实验:

Test1:

    //第一个实验,了解Class类的概念,Java中"万物皆为class",java.lang包中的位置说明了他的大佬地位
    //本实验,将会练习如何动态获得一个对象或者一个类的类名,让你更将深入地了解Class
    static void test1(){
        F f=new F();//定义和初始化一个类对象
        Class c1=F.class;//定义一个Class对象,初始化为F.class,
                        //很多博客在这一点上解释为,class是F的一个静态变量,或者说每一个类都有一个隐式的class域
                        //这似乎挺对,也有助于理解,然而我有点不同意见,
                        //如果class是一个静态变量,那么它的实例也有这个变量,然而实际上,当我输入Class c2=f.class的时候,是会报错的
                        //因此我认为这句话就是把F.class本身的实体赋予了c1,在Java编译时就加载进去.
        Class c2=f.getClass();//通过实例对象的getClass方法也可以得到类的实体,值得说明的是这个getClass()是Object类的方法,返回这个对象的类型类,好绕
                        //有趣吧,由此我们更深地了解“万物皆为class”这句话,Class is a class!!
        Class c3=null;
        try {
            c3=Class.forName("F");//第三种获取类型类的方式Class.forClass("XXXX")
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(c1==c2);
        System.out.println(c2==c3);//从这里看出绝对地址相同,指向同一个东西
    }

Test2

    //通过类型类来初始化实例
    static void test2(){
        Class c=F.class;
        F f=null;
        try {
            f=(F)c.newInstance();//newInstance是Class类的方法,返回一个新的泛型对象,强制转型为F对象
            //这句话如果改为f=f.getClass().newInstance()不需要转型
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        System.out.println(f);
        System.out.println(f.getClass());
        System.out.println(f.getClass().getName());
    }

Test3

    //为了加深实验二印象,做了实验三,发现重新初始化前后的绝对地址发生变化,确定确实初始化了一个新实例,改变了f的引用
    static void test3(){
        F f=new F();
        System.out.println(f);
        try {
            f=f.getClass().newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        System.out.println(f);
    }

Test4

    //实验四,
    static void test4(){
        Class c1=String.class;
        Object c2=void.class;
        Class c3=void.class;
        Class c4=int.class;
        System.out.println(c1.getName());//通过Class的getName方法可以获得类的名称,

        System.out.println(c1.getSimpleName());//getSimpleName获取不含包完整路径的类名

        System.out.println(c2.getClass().getName());//如果是对象需要先获得为对应的类型类

        System.out.println(c3.getName());//我们会发现void也有一个类型

        System.out.println(c4.getName());//int是基本数据类型,没有所属的包,void也是如此
    }

Test5

    //实验五,动态获取类型方法
    public static void test5(){
        ReflectionTest rt=new ReflectionTest();
        Method[] ms=rt.getClass().getMethods();//Class的getMethods()方法,返回类型的所有共有方法
        //Method[] ms=rt.getClass().getDeclaredMethods();//把上一行替换成这个看看输出的方法有什么区别
        for(Method m:ms){
            System.out.println(m);
            System.out.println(m.getName());//method.getName()获取方法名
            Class returnType=m.getReturnType();//method.getReturnType()获取返回参数类型
            System.out.println(returnType);
            System.out.println(returnType.getName());//获取返回参数类型的名字
            Class[] parameters=m.getParameterTypes();//method.getParameters获取方法传入参数的类型
            for(Class p:parameters){
                System.out.print(p+",");
            }
            System.out.println();
        }
    }

    //实验五的辅助函数,
    //这个函数的返回类型输出如下:int,class [I,class [D,class [Ljava.lang.String;,class [Lcom.way.reflection.F;,
    //这是描述标识字符,就是将对象转换成的字节码
    public static int test5add(int a,int[] b,double[] c,String[] d,F[] e){
        return 0;
    }

描述标识字符就是将.java文件编译成.class文件后对象转换成的字节码。表示字符所代表的含义如下:

这里写图片描述

Test6

    //实验六,获取成员变量
    static void test6(){
        F ff=new F("test",1);
        Class c=F.class;
        Field[] fields=c.getFields();
        for(Field f:fields){
                try {
                    System.out.println(f.get(ff));//field.get(Object obj)获取对象的对应成员变量对象(Object)
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
                System.out.println(f.getType());//field.获取类型成元变量的类型类

        }
    }

Test7

    //实验七
    static void test7(){
        Class c=F.class;
        Constructor[] cs=c.getConstructors();
        for(Constructor constructor:cs){
            System.out.println(constructor.getName());
            Class[] parameters=constructor.getParameterTypes();
            if(parameters.length==0){
                System.out.println("No parameter constructor");
            }else{
                for(Class parameter:parameters){
                    System.out.println(parameter.getName());
                }
            }
        }
    }

Test8

反射可以拿到一个类所有的方法和属性,包括父类和接口。很多时候我们不想获取父类和接口的东西,只想要本类中声明的属性和方法。于是就有了Class.getDeclaredXXXX()的方法,在前面试验过的各种get方法中间插入Declared,就可以获取本类的方法,实验8就是为了说明这件事。

为了说明问题,需要对F类稍作改动,就是需要F继承任意一个类,或者实现任意的接口,在这里我让他继承了Thread类,其他都不变。

class F extends Thread{
    //实验8,测试Declared的get方法
    static void test8(){
        Class c=null;
        try {
            c=Class.forName("F");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        System.out.println(c.getMethods().length);
        System.out.println(c.getDeclaredMethods().length);

    }

输出结果

50
0

可以得知,Thread有50个公共方法被F类继承了。类似的Declared的get方法概不赘述了。

总结

**获取类类型:**

Class c=F.class;
Class c=Class.forName("F");
Class c=f.getClass(); //Object.getClass()

**获取类实例:**
F f=new F(); //非反射做法
F f=c.newInstance(); //Class.newInstance()

**获取类名称:**

String fullName=c.getName();
String simpleName=c.getSimpleName();


**获取类方法:**

Method method=c.getMethod(String name,Class<?>... parameterTypes); //java.lang.reflect.Method
Method[] methods=c.getMethods(); //java.lang.reflect.Method[]
Method[] methods=c.getDeclaredMethods();

method.getName(); //String
method.getParameterTypes(); //Class[]
method.getParameterCount(); //int
method.getReturnType(); //Class

**获取类属性:**

Field field=c.getField(String name); //java.lang.reflect.Field
Field[] field=c.getFields(); 

field.getName(); //String
field.getType(); //Class
field.get(obj); //Object (the field object of obj)

**获取构造器:**

Constructor constructor=c.getConstructor(Class<?>... parameterTypes); //java.lang.reflect.constructor
Constructor[] constructors=c.getConstructors(); 

constructor.getName() 
constructor.getParameterTypes() //Class[]

**Declared方法**

getDeclaredXXX(); //获取本类的属性,方法构造器,等等

以上的方法只是一部分,详细可以看源码。

参考:
http://www.jianshu.com/p/6277c1f9f48d
http://www.jianshu.com/p/b560b30726d4
这位博主文章写得挺好

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值