Java反射

JVM简述

了解Java的反射,首先需要知道虚拟机(JVM)是如何处理Java程序的。附图如下:
JVM结构图

假定程序中有一句源码如下:

Object o = new Object();

详细流程如下:

  1. 你的代码(.java文件)会编译成(.class文件);
  2. JVM启动,调用main方法,.class文件会被类加载器加载进JVM的内存中,类(Object)的元数据信息会被加载到方法区,创建了该类的class对象(类型对象,每个类只有一个)到堆中;
  3. jvm识别到new指令,准备创建对象(引用o指向的对象)前,先检查类是否加载,若加载好,JVM会为新生对象分配内存(内存空间会初始化为零值);
  4. 这句话已经执行完毕。当所有程序(如果还有其他代码)跑完,jvm关闭,程序停止。

结论:类似这种创建对象的方式(new),对象的类型在编译时就已经确定下来了。

反射的功能

反射的核心是JVM在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。

Java反射框架提供的功能如下:

  1. 在运行时判断任意一个对象所属的类;
  2. 在运行时构造任意一个类的对象;
  3. 在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
  4. 在运行时调用任意一个对象的方法

反射的基本运用

我对反射的理解:类加载时生成的class对象,包含了类的所有信息。在创建该类对应的具体实例之前,可以直接通过操作class对象来调用方法或获取属性。例如:学校准备招聘一个数学老师(class模板就是一个可以教数学的老师)。现在需要对所有老师明年的教学任务进行排班,这个时候我们还不知道缺的这个数学老师具体是谁,但我们可以现在就可以对他/她安排任务如:明年数学老师(数学:class对象获取的属性)在每周二上午授课(授课:class对象调用方法)。

具体用法如下:

  • 获取Class对象
// 方式一:path为student对应的包路径(com.netopstec.reflectionlearning.entity.Student)
Class student = Class.forName(String path);

// 方式二:所有数据类型包括基本数据类型(.class)获取,包装类型(.TYPE)获取
Class intClass = int.class;
Class integerClass = Integer.TYPE;

// 方式三:具体对象的getClass方法
StringBuilder str = new StringBuilder("123");
Class strClass = str.getClass();
  • 判断是否为某个类的实例
// 这里涉及到java的多态,即判断某个实例是否为某种类型
public native boolean isInstance(Object obj);
  • 创建实例
// 方式一:class对象的newInstance()方法
Object str = String.class.newInstance();

// 方式二:获取到构造器后调用newInstance()方法
Class c = String.class;
Constructor constructor = c.getConstructor(String.class);
Object obj = constructor.newInstance("123");
  • 获取方法
// 获取该类中声明的方法(任何权限修饰符均会返回),但不会返回继承的方法
public Method[] getDeclaredMethods() throws SecurityException

// 获取该类中的所有公有方法(public修饰的),包括继承了的方法
public Method[] getMethods() throws SecurityException

// 返回某个特定公有方法(第一个参数为方法的名称,随后的参数是该方法的参数类型)
public Method getMethod(String name, Class<?>... parameterTypes)

测试代码如下:

package com.netops.java8_test1.demo17;

import java.lang.reflect.Method;

/**
 * @author zhenye 2018/2/7
 */
public class Reflection {
    public static void main(String[] args) throws NoSuchMethodException {
        Class clazz = MethodClass.class;
        System.out.println("getMethods方法返回该类实际拥有所有的公有方法:");
        Method[] totalPubMethods = clazz.getMethods();
        for (Method method : totalPubMethods) {
            System.out.println(method);
        }
        System.out.println("分割线--------------------------------------------------------------------------------------------------分割线");
        System.out.println("getDeclaredMethods方法返回该类显示声明的所有方法");
        Method[] totalDeclaredMethods= clazz.getDeclaredMethods();
        for (Method method : totalDeclaredMethods) {
            System.out.println(method);
        }
        System.out.println("分割线--------------------------------------------------------------------------------------------------分割线");
        System.out.println("getMethod方法获取某个特定公有方法,如果想要获取方法的访问修饰符权限不够,会有NoSuchMethodException,结果如下:");
        Method method = clazz.getMethod("add", int.class, int.class);
        System.out.println(method);
    }
    class MethodClass {
        public int add(int a, int b){
            return a+b;
        }
        private int sub(int a, int b){
            return a-b;
        }
    }
}

执行结果如下:
执行结果

  • 获取类的成员变量
    • getFields() 获取该类拥有的所有公有属性
    • getDeclaredFields() 获取该类中显示声明的所有属性
    • getField(String name) 获取该类的指定属性

用法参考Method

  • 方法的调用
public class Reflection {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class<?> klass = MethodClass.class;
        Object obj = klass.newInstance();
        Method[] methods = klass.getDeclaredMethods();
        for (Method method : methods){
//            method.setAccessible(true);
            System.out.println();
            System.out.println(method.getName() + ":" + method.invoke(obj,2,3));
        }
    }
}
class MethodClass {
    public int add(int a,int b) {
        return a+b;
    }
    private int sub(int a,int b) {
        return a-b;
    }
}

总结: invoke方法的第一个参数是被调用方法的实例(需要知道是具体哪个实例的方法,如果调用invoke方法的是静态方法可以传入null);此时的invoke方法只能调用公有方法,想要调用私有方法需要method.setAccessible(true),确保有权限调用该方法。

  • 创建数组
public static void testArray() throws ClassNotFoundException {
  Class<?> cls = Class.forName("java.lang.String");
  Object array = Array.newInstance(cls,25);
  //往数组里添加内容
  Array.set(array,0,"hello");
  Array.set(array,1,"Java");
  Array.set(array,2,"fuck");
  Array.set(array,3,"Scala");
  Array.set(array,4,"Clojure");
  //获取某一项的内容
  System.out.println(Array.get(array,3));
}

注意事项:

  1. 反射会额外消耗一定的系统资源。如果不需要动态创建对象,就不需要用反射。
  2. 反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值