你必须知道的Java反射机制

反射

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

反射可以用来生成动态代理。


反射机制的相关类

对于一个字节码文件.class,虽然表面上我们对该字节码文件一无所知,但该文件本身却记录了许多信息。Java在将.class字节码文件载入时,JVM将产生一个java.lang.Class对象代表该.class字节码文件,从该Class对象中可以获得类的许多基本信息,这就是反射机制。所以要想完成反射操作,就必须首先认识Class类。

反射机制所需的类主要有java.lang包中的Class类和java.lang.reflect包中的Constructor类、Field类、Method类和Parameter类。Class类是一个比较特殊的类,它是反射机制的基础,Class类的对象表示正在运行的Java程序中的类或接口,也就是任何一个类被加载时,即将类的.class文件(字节码文件)读入内存的同时,都自动为之创建一个java.lang.Class对象。Class类没有公共构造方法,其对象是JVM在加载类时通过调用类加载器中的defineClass()方法创建的,因此不能显式地创建一个Class对象。通过这个Class对象,才可以获得该对象的其他信息。

每个类被加载之后,系统都会为该类生成一个对应的Class对象,通过Class对象就可以访问到JVM中该类的信息,一旦类被加载到JVM中,同一个类将不会被再次载入。被载入JVM的类都有一个唯一标识就是该类的全名,即包括包名和类名。在Java中程序获得Class对象有如下3种方式。

(1)使用Class类的静态方法forName(String className),其中参数className表示所需类的全名。如“Class cObj=Class.forName(“java.lang.String”);”。另外,forName()方法声明抛出ClassNotFoundException异常,因此调用该方法时必须捕获或抛出该异常。

(2)用类名调用该类的class属性来获得该类对应的Class对象,即“类名.class”。如,语句“Class cObj=Cylinder.class;”将返回Cylinder类所对应的Class对象赋给cObj变量。

(3)用对象调用getClass()方法来获得该类对应的Class对象,即“对象.getClass()”。该方法是Object类中的一个方法,因此所有对象调用该方法都可以返回所属类对应的Class对象。如例8.8中的语句“Person per=new Person(“张三”);”可以通过以下语句返回该类的Class对象:Class cObj=per.getClass();

通过类的class属性获得该类所对应的Class对象,会使代码更安全,程序性能更好,因此大部分情况下建议使用第二种方式。但如果只获得一个字符串,例如获得String类对应的Class对象,则不能使用String.class方式,而是使用Class.forName(“java.lang.String”)。注意:如果要想获得基本数据类型的Class对象,可以使用对应的打包类加上.TYPE,例如,Integer.TYPE可获得int的Class对象,但要获得Integer.class的Class对象,则必须使用Integer.class。在获得Class对象后,就可以使用getClass()方法来取得Class对象的基本信息。


反射包reflect中的常用类

反射机制中除了上面介绍的java.lang包中的Class类之外,还需要java.lang.reflect包中的Constructor类、Method类、Field类和Parameter类。Java 8以后在java.lang.reflect包中新增了一个Executable抽象类,该类对象代表可执行的类成员。Executable抽象类派生了Constructor和Method两个子类。

java.lang.reflect.Executable类提供了大量方法用来获取参数、修饰符或注解等信息,以下是Executable的一些常用方法
//返回所有参数的类型
public abstract Class<?>[] getParameterTypes();

//返回参数的个数
public int getParameterCount()

//返回所有形参
public Parameter[] getParameters()

//返回整数表示的修饰符关键字常量
public abstract int getModifiers();
getModifiers()方法返回的是以整数表示的修饰符。此时引入Modifier类,通过调用Modifier.toString(int mod)方法返回修饰符常量所应的字符串。

例如:Modifier.FINAL
public static final int FINAL = 0x00000010;//是用16进制的0x00000010表示Modifier.FINAL,数值为16

java.lang.reflect.Constructor类是java.lang.reflect.Executable类的直接子类,用于表示类的构造方法。通过Class对象的getConstructors()方法可以获得当前运行时类的构造方法。以下是Constructor类的常用方法

//返回构造函数的名称
public String getName() {
return getDeclaringClass().getName();
}

public T newInstance(Object … initargs)通过该构造方法创建一个对象,initargs为构造方法所需的参数,如果没有参数则表示无参构造方法

public void setAccessible(boolean flag) //私有构造方法是不允许通过反射来创建对象的,但是如果先执行该方法,传参为true,则允许创建

java.lang.reflect.Method类是java.lang.reflect.Executable类的直接子类,用于封装成员方法的信息,调用Class对象的getMethod()方法或getMethods()方法可以获得当前运行时类的指定方法或所有方法。以下是java.lang.reflect.Method类的常用方法。

//返回方法的名称
public String getName()

//跟构造方法的setAccessible用法一样
public void setAccessible(boolean flag)

//返回方法返回值的类型
public Class<?> getReturnType() {
return returnType;
}

//重写的equals方法
public boolean equals(Object obj) {
//判断传入对象是否为空,是否为Method类型
if (obj != null && obj instanceof Method) {
Method other = (Method)obj;
//比较类型和名字
if ((getDeclaringClass() == other.getDeclaringClass())
&& (getName() == other.getName())) {
//比较返回值
if (!returnType.equals(other.getReturnType()))
return false;
//比较参数是否相等
return equalParamTypes(parameterTypes, other.parameterTypes);
}
}
return false;
}

//使用传入的对象和参数执行方法
public Object invoke(Object obj, Object… args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException

java.lang.reflect.Field类用于封装成员变量信息,调用Class对象的getField()方法或getFields()可以获得当前运行时类的指定成员变量或所有成员变量。java.lang.reflect.Field类的常用方法如下面所示。

//返回成员变量的名称
public String getName() {
return name;
}

//返回成员变量的类型
public Class<?> getType() {
return type;
}

//返回成员变量的值
public Object get(Object obj)
throws IllegalArgumentException, IllegalAccessException

//返回成员变量的值
public boolean getBoolean(Object obj)
throws IllegalArgumentException, IllegalAccessException

//给对象中的成员变量赋值
public void set(Object obj, Object value)
throws IllegalArgumentException, IllegalAccessException

//返回成员变量的类型
public Class<?> getType() {
return type;
}

java.lang.reflect.Parameter类是参数类,每个Parameter对象代表方法的一个参数。java.lang.reflect.Parameter类中提供了许多方法来获取参数信息,以下是java.lang.reflect.Parameter类的常用方法。

//返回修饰符的数值表示,使用Modifier的toString方法,可以得到名称
public int getModifiers() {
return modifiers;
}

//返回参数的名称
public String getName() {
return name;
}

//返回参数的类型
public Class<?> getType()

//是否为可变参数
public boolean isVarArgs() {
return executable.isVarArgs() &&
index == executable.getParameterCount() - 1;
}


例子

  public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchFieldException {
        /**
         * 获取TestObject类的Class对象并且创建TestObject类实例
         */
        Class<?> tagetClass = Class.forName("cn.mytest.TestObject");
        TestObject targetObject = (TestObject) tagetClass.newInstance();
        /**
         * 获取所有类中所有定义的方法
         */
        Method[] methods = tagetClass.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method.getName());
        }
        /**
         * 获取指定方法并调用
         */
        Method publicMethod = tagetClass.getDeclaredMethod("testMethod",
                String.class);

        publicMethod.invoke(targetObject, "hello world");
        /**
         * 获取指定成员变量并进行修改
         */
        Field field = tagetClass.getDeclaredField("name");
        //为了对类中的参数进行修改我们取消安全检查
        field.setAccessible(true);
        field.set(targetObject, "my");
        /**
         * 调用 private 方法
         */
        Method privateMethod = tagetClass.getDeclaredMethod("privateMethod");
        //为了调用private方法我们取消安全检查
        privateMethod.setAccessible(true);
        privateMethod.invoke(targetObject);
    }
}

注意:
getxxxx和getDeclaredxxxx的区别在于是否返回父类的相关信息,下面是getMethods()和getDeclaredMethods()的区别

1:getMethods(),该方法是获取本类以及父类或者父接口中所有的公共方法(public修饰符修饰的)

2:getDeclaredMethods(),该方法是获取本类中的所有方法,包括私有的(private、protected、默认以及public)的方法。(不包含父类方法)


优点:

反射机制极大的提高了程序的灵活性和扩展性,降低模块的耦合性,提高自身的适应能力。

通过反射机制可以让程序创建和控制任何类的对象,无需提前硬编码目标类。

使用反射机制能够在运行时构造一个类的对象、判断一个类所具有的成员变量和方法、调用一个对象的方法。

反射机制是构建框架技术的基础所在,使用反射可以避免将代码写死在框架中。
正是反射有以上的特征,所以它能动态编译和创建对象,极大的激发了编程语言的灵活性,强化了多态的特性,进一步提升了面向对象编程的抽象能力,因而受到编程界的青睐

缺点:

1、性能较差

2、安全问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值