JAVA反射调用详解

来源:互联网 发布:vm14安装ubuntu 教程 编辑:程序博客网 时间:2024/04/30 12:50

JAVA反射调用的确是一种很神奇的机制,在项目中使用后恋恋不忘,现将其好好整理一下。


参考: 学习JAVA应该如何理解反射?知乎 https://www.zhihu.com/question/24304289

全面细致讲解反射机制系列 http://www.365yg.com/item/6414118008049893889/?iid=9697930986&app=news_article&wxshare_count=1&tt_from=weixin&utm_source=weixin&utm_medium=toutiao_android&utm_campaign=client_share

深入理解JAVA反射 http://www.sczyh30.com/posts/Java/java-reflection-1/#%E4%B8%80%E3%80%81%E5%9B%9E%E9%A1%BE%EF%BC%9A%E4%BB%80%E4%B9%88%E6%98%AF%E5%8F%8D%E5%B0%84%EF%BC%9F

反射机制概述

反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。

这种动态获取的信息以及动态调用对象的方法的功能称为JAVA语言的反射机制。

反射机制主要提供了以下功能:

  • 在运行时判断任意一个对象所属的类;
  • 在运行时构造任意一个类的对象;
  • 在运行时判断任意有一个类所具有的的成员变量和方法;
  • 在运行时调用任意一个对象的方法;
  • 生成动态代理
  • 反射最重要的用途就是开发各种通用框架。大大提高了程序的扩展性 。很多框架(比如Spring、Tomcat)都是配置化的(比如通过XML文件配置JavaBean,Action之类的),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射——运行时动态加载需要加载的对象。

举个例子:

Tomcat服务器程序,提供了处理请求和应答的方式。因为具体的处理动作不同,所以对外提供了一个接口,由开发者来实现具体的请求和应答处理。该接口还有一个配置文件可供修改。

interface Servlet //Tomcat提供的接口class MySevlet implements Servlet{//定义了具体的请求和应答处理方式}

配置文件: web.xml

<servlet-name>MySevlet</servlet-name>

Tomcat动态的获取自定义的类,并调用其方法,这个过程就是反射调用。

从JAVA虚拟机看反射

作者:郑剑锋
链接:https://www.zhihu.com/question/24304289/answer/147529485
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

image

首先我们了解一下JVM,什么是JVM,Java的虚拟机,java之所以能跨平台就是因为这个东西,你可以理解成一个进程,程序,只不过他的作用是用来跑你的代码的。上图是java的内存模型,我们关注的点,一个方法区,一个栈,一个堆,初学的时候老师不深入的话只告诉你java的内存分为堆和栈,易懂点吧!

来看创建对象的过程:

Object o = new Object();

首先JVM会启动,你的代码会编译成一个.class文件,然后被类加载器加载进jvm的内存中,你的类Object加载到方法区中,创建了Object类的class对象到堆中,注意这个不是new出来的对象,而是类的类型对象,每个类只有一个class对象,作为方法区类的数据结构的接口。jvm创建对象前,会先检查类是否加载,寻找类对应的class对象,若加载好,则为你的对象分配内存,初始化也就是代码:new Object()。

上面的流程就是你自己写好的代码扔给jvm去跑,跑完就over了,jvm关闭,你的程序也停止了。

为什么要讲这个呢?因为要理解反射必须知道它在什么场景下使用。题主想想上面的程序对象是自己new的,程序相当于写死了给jvm去跑。假如一个服务器上突然遇到某个请求哦要用到某个类,哎呀但没加载进jvm,是不是要停下来自己写段代码,new一下,哦启动一下服务器,(脑残)!

反射是什么呢?当我们的程序在运行时,需要动态的加载一些类这些类可能之前用不到所以不用加载到jvm,举个例子我们的项目底层有时是用mysql,有时用oracle,需要动态地根据实际情况加载驱动类,这个时候反射就有用了,假设 com.java.dbtest.myqlConnection,com.java.dbtest.oracleConnection这两个类我们要用,这时候我们的程序就写得比较动态化,通过Class tc = Class.forName(“com.java.dbtest.TestConnection”);通过类的全类名让jvm在服务器中找到并加载这个类,而如果是oracle则传入的参数就变成另一个了。这时候就可以看到反射的好处了,这个动态性就体现出java的特性了!当然这里只是举了反射的一个应用,实际还有其他作用,只是这个例子能更好地理解!

Class类

深入研究java.lang.Class类 http://lavasoft.blog.51cto.com/62575/15433/

Java程序在运行时,Java运行时系统一直对所有的对象进行所谓的运行时类型标识。这项信息纪录了每个对象所属的类。虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类。Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建。

Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。

虚拟机为每种类型管理一个独一无二的Class对象。也就是说,每个类(型)都有一个Class对象。运行程序时,Java虚拟机(JVM)首先检查是否所要加载的类对应的Class对象是否已经加载。如果没有加载,JVM就会根据类名查找.class文件,并将其Class对象载入。

基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也都对应一个 Class 对象。

每个数组属于被映射为Class对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。

一般某个类的Class对象被载入内存,它就用来创建这个类的所有对象。

/*该类就可以获取字节码文件中的所有内容反射就是要依靠该类来完成的*/class Class{提供获取字节码文件中的内容。比如:名称字段构造函数一般函数}

获得Class对象

在反射调用中,如果想要对字节码文件进行解剖,必须要有字节码文件对象。

三种方式获取Class对象:

  1. Object类中的getClass方法。 这也是最常见的产生Class对象的方法。想要用这种方式,必须要明确具体的类,并创建对象,麻烦。
Person p = new Person();Class clazz = p.getClass();
  1. 直接获取某一个对象的class. 任何数据类型都具备一个静态的属性.class来获取其对应的class对象。相对简单,但还是要明确用到的类中的静态成员。还是不够扩展
Class clazz = Person.class;
  1. 使用Class类中的forName静态方法 只要通过给定的类的字符串名称就可以获取该类,更为扩展
    .但是这里的className**需要类的完全限定名**,因为它是字符串,和导没导包没任何关系
Class clazz = Class.forName("com.example.hgx.Person");//得添加异常ClassNotFoundException

反射的基本运用

反射方式创建实例

  1. 创建空参的实例。 使用Class对象的newInstance()方法来创建Class对象对应类的实例。
//原始:new的时候,先根据被new的类的名称找寻该类的字节码文件,并加载进内存,//并创建该字节码文件对象,并接着创建该字节文件对应的Person对象。Person p = new Person();//反射:找寻该类的名称,并加载进内存,并产生Class对象//在产生类的对象Strint className = "com.example.hgx.Person";Class clazz = Class.forName(className);Object obj = clazz.newInstance();

2、创建带参数的实例。 这时要通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。

    class Person {        public String name;        private int age;        public Person(String name, int age) {            this.name = name;            this.age = age;        }    }
//获取Person类带一个(String,int)参数的构造器Strint className = "com.example.hgx.Person";Class clazz = Class.forName(className);Constructor constructor = clazz.getConstructor(String.class,int.class);Object obj = constructor.newInstance("小明",24);

反射方式获取字段(成员变量)

主要是这几个方法

  • getField: 访问公有的成员变量
  • getDeclaredField:所有已声明的成员变量。但不能得到其父类的成员变量
Strint className = "com.example.hgx.Person";Class clazz = Class.forName(className);Object obj = clazz.newInstance();//访问public字段Field field = clazz.getField("name");field.set(obj,"小明");Object o = field.get(obj);//访问private字段,要取消权限检查.暴力访问Field field1 =clazz.getDeclaredField("age");field1.setAccessible(true);field1.set(obj,24);Object o1 = field1.get(obj);

反射方式获取方法

获取某个Class对象的方法集合,主要有以下几个方法:
- getDeclaredMethods() 方法返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
- getMethods() 方法返回某个类的所有公用(public)方法,包括其继承类的公用方法。
- getMethod方法返回一个特定的方法,其中第一个参数为方法名称,后面的参数为方法的参数对应Class的对象

public Method getMethod(String name, Class<?>... parameterTypes)

例子:

Class clazz = Class.forName("com.example.hgx.Person");Method[] methods = clazz.getMethods();//获得公有方法for(Method method: methods){    System.out.println(method);}Method[] all_methods = clazz.getDeclaredMethods();//只获取本类中的所有方法,包含私有//调用方法Method method1 = clazz.getMethod("show",null);//Person中的show()方法,空参Object obj = clazz.newInstance();method1.invoke(obj,null);//调用带参方法Method method = clazz.getMethod("paramMethod",String.class,int.class);Object obj1 = clazz.newInstance();method.invoke(obj1,"小明",24);
0 0
原创粉丝点击