java 反射机制

来源:互联网 发布:建表外键sql 编辑:程序博客网 时间:2024/05/18 02:33

一、概述

1 什么是反射

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

反射是Java中一种强大的工具,能够使我们很方便的创建灵活的代码,这些代码可以再运行时装配,无需在组件之间进行源代码链接。但是反射使用不当会成本很高!

IT行业里这么说,没有反射也就没有框架,现有的框架都是以反射为基础。在实际项目开发中,用的最多的是框架,填的最多的是类,反射这一概念就是将框架和类揉在一起的调和剂。所以,反射才是接触项目开发的敲门砖!

2 反射机制

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

3 反射机制的作用

(1)反编译:.class–>.java
(2)通过反射机制访问java对象的属性,方法,构造方法等。
(3)生成动态代理
举个实例说明一下反射机制的作用:
假如有两个程序员,一个程序员在写程序的时需要使用第二个程序员所写的类,但第二个程序员并没完成他所写的类。那么第一个程序员的代码是不能通过编译的。此时,利用Java反射的机制,就可以让第一个程序员在没有得到第二个程序员所写的类的时候,来完成自身代码的编译。

4 哪里用到反射

有些时候,我们用过一些知识,但是并不知道它的专业术语是什么,在刚刚学jdbc时用过一行代码,Class.forName(“com.mysql.jdbc.Driver.class”).newInstance();但是那时候只知道那行代码是生成驱动对象实例,并不知道它的具体含义。这就是反射,现在很多开框架都用到反射机制,hibernate、struts都是用反射机制实现的。反射机制就是专门帮我们做那些重复的有规则的事情,所以现在很多的自动生成代码的软件就是运用反射机制来完成的。

5 反射机制的优缺点

为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念。
(1)静态编译:在编译时确定类型,绑定对象,即通过。
(2)动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。
优点:一句话,反射机制的优点就是可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发中它的灵活性就表现的十分明显。比如,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如这样的话,这个软件肯定是没有多少人用的。采用静态的话,需要把整个程序重新编译一次才可以实现功能的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功能。
缺点:它的缺点是对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。

二、class类

要正确使用Java反射机制就得使用java.lang.Class 这个类。它是Java反射机制的起源。当一个类被加载以后,Java虚拟机就会自动产生一个Class对象。通过这个Class对象我们就能获得加载到虚拟机当中这个Class对象对应的方法、成员以及构造方法的声明和定义等信息。

1 什么是Class类

在面向对象的世界里,万事万物皆是对象。而在java语言中,static修饰的东西不是对象,但是它属于类。普通的数据类型不是对象,例如:int a = 5;它不是面向对象,但是它有其包装类 Integer 或者分装类来弥补了它。除了以上两种不是面向对象,其余的包括类也有它的面向对象,类是java.lang.Class的实例化对象(注意Class是大写)。也就是说:
Class A{}
当我创建了A类,那么类A本身就是一个对象,谁的对象?java.lang.Class的实例对象。那么这个对象又该怎么表示呢?
我们先看一下下面这段代码:

public class Demo(){F f=new F();}class F{}

这里的F的实例化对象就可以用f表达出来。同理F类也是一个实例化对象,Class类的实例化对象。我们可以理解为任何一个类都是Class类的实例化对象。

2 Class对象的获取

(1)对象的getClass()方法;
(2)类的.class(最安全/性能最好)属性;
(3)运用Class.forName(String className)动态加载类,className需要是类的全限定名(最常用);

这种实例化对象有三种表示方法:

public class ClassTest {    public static void main(String[] args) {        F f = new F();        //第一种表达方式        Class c1 = F.class;//这种表达方式同时也告诉了我们任何一个类都有一个隐含的静态成员变量class        System.out.println(c1);        //第二种表达方式        Class c2=f.getClass();//这种表达方式在已知了该类的对象的情况下通过getClass方法获取        System.out.println(c2);        //第三种表达方式        try {            Class c3 = Class.forName("com.mianshi.test.F");//类的全称            System.out.println(c3);        } catch (ClassNotFoundException e) {            e.printStackTrace();        }    }}class F{}

以上三种表达方式,c1,c2,c3都表示了F类的类类型,也就是官方解释的Class Type。

System.out.println(c1 == c2)System.out.println(c1 == c3)

返回值为ture。这表明不论c1、c2或c3都代表了F类的类型,也就是说一个类只可能是Class类的一个实例对象。

3 创建类的对象

通过反射来生成对象的方式有两种:
(1)使用Class对象的newInstance()方法来创建该Class对象对应类的实例(这种方式要求该Class对象的对应类有默认构造器)。
(2)先使用Class对象获取指定的Constructor对象, 再调用Constructor对象的newInstance()方法来创建该Class对象对应类的实例(通过这种方式可以选择指定的构造器来创建实例)。

public class ClassTest {    public static void main(String[] args) {        F f = new F();        try {            //第一种表达方式            Class c1 = F.class;//这种表达方式同时也告诉了我们任何一个类都有一个隐含的静态成员变量class            Object o1 = c1.newInstance();            System.out.println("c1:"+c1);            System.out.println("o1:"+o1);            //第二种表达方式            Class c2 = f.getClass();//这种表达方式在已知了该类的对象的情况下通过getClass方法获取            Object o2 = c2.newInstance();            System.out.println(c2);            System.out.println(o2);            //第三种表达方式            Class c3 = Class.forName("com.mianshi.test.F");//类的全称            Object o3 = c3.newInstance();            System.out.println(c3);            System.out.println(o3);        } catch (ClassNotFoundException e) {            e.printStackTrace();        } catch (InstantiationException e) {            e.printStackTrace();        } catch (IllegalAccessException e) {            e.printStackTrace();        }    }}class F{}

4 Class中获取信息

获取类内信息

获取内容 方法签名 构造器 Constructor<T> getConstructor(Class<?>… parameterTypes) 包含的方法 Method getMethod(String name, Class<?>… parameterTypes) 包含的属性 Field getField(String name) 包含的Annotation <A extends Annotation> A getAnnotation(Class<A> annotationClass) 内部类 Class<?>[] getDeclaredClasses() 外部类 Class<?> getDeclaringClass() 所实现的接口 Class<?>[] getInterfaces() 修饰符 int getModifiers() 所在包 Package getPackage() 类名 String getName() 简称 String getSimpleName()

一些判断类本身信息的方法

判断内容 方法签名 注解类型? boolean isAnnotation() 使用了该Annotation修饰? boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) 匿名类? boolean isAnonymousClass() 数组? boolean isArray() 枚举? boolean isEnum() 原始类型? boolean isPrimitive() 接口? boolean isInterface() obj是否是该Class的实例 boolean isInstance(Object obj)

使用反射生成并操作对象
Method Constructor Field这些类都实现了java.lang.reflect.Member接口,程序可以通过Method对象来执行相应的方法,通过Constructor对象来调用对应的构造器创建实例,通过Filed对象直接访问和修改对象的成员变量值。

6 方法的反射

当获取到某个类对应的Class对象之后, 就可以通过该Class对象的getMethod来获取一个Method数组或Method对象.每个Method对象对应一个方法,在获得Method对象之后,就可以通过调用invoke方法来调用该Method对象对应的方法,Class类有一个最简单的方法,getName()。

public class Demo2 {    public static void main(String[] args) {        Class c1 = int.class;//int 的类类型        Class c2 = String.class;//String类的类类型        Class c3 = void.class;        System.out.println(c1.getName());        System.out.println(c2.getName());        System.out.println(c2.getSimpleName());        System.out.println(c3.getName());    }}

案例:

public class ClassUtil {    public static void printClassMethodMessage(Object obj){        //要获取类的信息》》首先我们要获取类的类类型        Class c = obj.getClass();        //我们知道Object类是一切类的父类,所以我们传递的是哪个子类的对象,c就是该子类的类类型。        //接下来我们要获取类的名称        System.out.println("类的名称是:"+c.getName());        /*        *我们知道,万事万物都是对象,方法也是对象,是谁的对象呢?        * 在java里面,方法是Method类的对象        *一个成员方法就是一个Method的对象,那么Method就封装了对这个成员        *方法的操作        */        //如果我们要获得所有的方法,可以用getMethods()方法,这个方法获取的是所有的Public的函数,包括父类继承而来的。如果我们要获取所有该类自己声明的方法,就可以用getDeclaredMethods()方法,这个方法是不问访问权限的。        Method[] ms = c.getMethods();//c.getDeclaredMethods()        //接下来我们拿到这些方法之后干什么?我们就可以获取这些方法的信息,比如方法的名字。        //首先我们要循环遍历这些方法        for(int i = 0; i < ms.length;i++){        //然后可以得到方法的返回值类型的类类型        Class returnType = ms[i].getReturnType();        //得到方法的返回值类型的名字        System.out.print(returnType.getName()+" ");        //得到方法的名称        System.out.print(ms[i].getName()+"(");        //获取参数类型--->得到的是参数列表的类型的类类型        Class[] paramTypes = ms[i].getParameterTypes();        for (Class class1 : paramTypes) {        System.out.print(class1.getName()+",");        }        System.out.println(")");        }    }}

总结思路:
通过方法的反射得到该类的名称步骤:
1.获取该类的类类型
2.通过类类型获取类的方法(getMethods())
3.循环遍历所获取到的方法
4.通过这些方法的getReturnType()得到返回值类型的类类型,又通过该类类型得到返回值类型的名字
5.getName()得到方法的名称,getParameterTypes()获取这个方法里面的参数类型的类类型。

7 成员变量的反射

首先我们需要认识到成员变量也是对象,是java.lang.reflect.Field类的对象,那么也就是说Field类封装了关于成员变量的操作。既然它封装了成员变量,我们又该如何获取这些成员变量呢?它有这么一个方法:

public class ClassUtil {    public static void printFieldMessage(Object obj){    Class c = obj.getClass();    //Field[] fs = c.getFields();}

通过Class对象的的getField()方法可以获取该类所包含的全部或指定的成员变量Field,Filed提供了如下两组方法来读取和设置成员变量值。
getXxx(Object obj): 获取obj对象的该成员变量的值, 此处的Xxx对应8中基本类型,如果该成员变量的类型是引用类型, 则取消get后面的Xxx。
setXxx(Object obj, Xxx val): 将obj对象的该成员变量值设置成val值.此处的Xxx对应8种基本类型, 如果该成员类型是引用类型, 则取消set后面的Xxx。
注: getDeclaredXxx方法可以获取所有的成员变量,无论private/public。

这里的getFields()方法获取的所有的public的成员变量的信息。和方法的反射那里public的成员变量,也有一个获取所有自己声明的成员变量的信息:

Field[] fs = c.getDeclaredFields();

我们得到它之后,可以进行遍历(既然封装了Field的信息,那么我们就可以得到Field类型)

for (Field field : fs) {    //得到成员变量的类型的类类型    Class fieldType = field.getType();    String typeName = fieldType.getName();    //得到成员变量的名称    String fieldName = field.getName();    System.out.println(typeName+" "+fieldName);}

8 构造函数的反射

不论是方法的反射、成员变量的反射、构造函数的反射,我们只需要知道:要想获取类的信息,首先得获取类的类类型。

public static void printConMessage(Object obj){    Class c = obj.getClass();    /*    * 首先构造函数也是对象,是java.lang.Constructor类的对象    * 也就是java.lang. Constructor中封装了构造函数的信息    * 和前面说到的一样,它也有两个方法:    * getConstructors()方法获取所有的public的构造函数    * getDeclaredConstructors()方法得到所有的自己声明的构造函数    */    //Constructor[] cs = c.getConstructors();    Constructor[] cs = c.getDeclaredConstructors();    for (Constructor constructor : cs) {        //我们知道构造方法是没有返回值类型的,但是我们可以:        System.out.print(constructor.getName()+"(");        //获取构造函数的参数列表》》得到的是参数列表的类类型        Class[] paramTypes = constructor.getParameterTypes();        for (Class class1 : paramTypes) {            System.out.print(class1.getName()+",");        }        System.out.println(")");    }}

9 使用反射获取泛型信息

为了通过反射操作泛型以迎合实际开发的需要, Java新增了java.lang.reflect.ParameterizedType,java.lang.reflect.GenericArrayType,java.lang.reflect.TypeVariable,java.lang.reflect.WildcardType几种类型来代表不能归一到Class类型但是又和原始类型同样重要的类型。

类型 含义 ParameterizedType 一种参数化类型, 比如Collection GenericArrayType 一种元素类型是参数化类型或者类型变量的数组类型 TypeVariable 各种类型变量的公共接口 WildcardType 一种通配符类型表达式, 如? ? extends Number ? super Integer

10 使用反射获取注解

(待补充。。。)

11 Class类的动态加载类

(待补充。。。)

参考文章:
Java 反射
谈谈Java反射机制
Java 反射机制详解

0 0