资讯专栏INFORMATION COLUMN

Java反射详细介绍

ingood / 1799人阅读

摘要:通过反射获取带参无返回值成员方法并使用设置安全检查,访问私有构造函数必须创建实例这种不行,注意和方法需要传递参数测试复制这个功能获取私有方法,同样注意和的区别赋予访问权限调用方法。

反射 目录介绍

1.反射概述

1.1 反射概述

1.2 获取class文件对象的三种方式

1.3 反射常用的方法介绍

1.4 反射的定义

1.5 反射的组成

1.6 反射的作用有哪些

2.反射的相关使用

2.1.1 通过反射获取无参构造方法并使用

2.1.2 通过反射获取带参构造方法并使用

2.1.3 通过反射获取私有构造方法并使用

2.1.4 通过反射获取成员变量并使用

2.1.5 通过反射获取无参无返回值成员方法并使用

2.1.6 通过反射获取带参无返回值成员方法并使用

2.1.7 通过反射获取带参带返回值成员方法并使用

2.1.8 通过反射获取无参带返回值成员方法并使用

3.相关知识点

3.1.1 设置setAccessible(true)暴力访问权限

3.1.2 获取Filed两个方法的区别

3.1.3 获取Field的类型

3.1.4 Method获取方法名,获取方法参数

3.1.5 Method方法的invoke()方法执行

关于链接

1.技术博客汇总

2.开源项目汇总

3.生活博客汇总

4.喜马拉雅音频汇总

5.程序员聊天笔记汇总

5.其他汇总

0.问题答疑

0.1 被反射的类是否一定需要无参构造方法?为什么?

0.2 反射的使用有什么优势和劣势?为什么说反射可以降低耦合?

0.3 反射比较损耗性能,为什么这样说?能否通过案例对比说明反射机制损耗性能……

0.4 反射是一种具有与类进行动态交互能力的一种机制,为什么要强调动态交互呢?

0.5 Java反射中的setAccessible()方法是否破坏了类的访问规则

0.2 反射的使用有什么优势和劣势?

射的初衷不是方便你去创建一个对象,而是让你在写代码的时候可以更加灵活,降低耦合,提高代码的自适应能力。

0.4 反射是一种具有与类进行动态交互能力的一种机制,为什么要强调动态交互呢

动态加载,也就是在运行的时候才会加载,而不是在编译的时候,在需要的时候才进行加载获取,或者说你可以在任何时候加载一个不存在的类到内存中,然后进行各种交互,或者获取一个没有公开的类的所有信息,换句话说,开发者可以随时随意的利用反射的这种机制动态进行一些特殊的事情。

1.反射概述 1.1 反射概述

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

对于任意一个对象,都能够调用它的任意一个方法和属性;

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

要想解剖一个类,必须先要获取到该类的字节码文件对象。

而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象

1.2 获取class文件对象的三种方式

1.2.1 这三种方式为:

a:Object类的getClass()方法

b:静态属性class

c:Class类中静态方法forName()

1.2.2 第一种方法【Object类的getClass()方法】

1.在内存中新建一个Person的实例,对象p对这个内存地址进行引用

2.对象p调用getClass()返回对象p所对应的Class对

3.调用newInstance()方法让Class对象在内存中创建对应的实例,并且让p2引用实例的内存地址

Person p = new Person();
Class cls=p.getClass();
Person p2=(Person)cls.newInstance();

1.2.3 第二种方法【静态属性class】

1.获取指定类型的Class对象,这里是Person

2.调用newInstance()方法在让Class对象在内存中创建对应的实例,并且让p引用实例的内存地址

Class cls=Person.Class();
Person p=(Person)cls.newInstance();

1.2.4 第三种方法【Class类中静态方法forName()】

1.通过JVM查找并加载指定的类(上面的代码指定加载了com.fanshe包中的Person类)

2.调用newInstance()方法让加载完的类在内存中创建对应的实例,并把实例赋值给p

注意:如果找不到时,它会抛出 ClassNotFoundException 这个异常,这个很好理解,因为如果查找的类没有在 JVM 中加载的话,自然要告诉开发者。

Class cls=Class.forName("com.yc.Person"); //forName(包名.类名)
Person p= (Person) cls.newInstance();

1.3 通过反射获取无参构造方法并使用

A:获取所有构造方法

public Constructor[] getConstructors()

public Constructor[] getDeclaredConstructors()

B:获取单个构造方法

public Constructor getConstructor(Class... parameterTypes)

public Constructor getDeclaredConstructor(Class... parameterTypes)

方法关键字

getDeclareMethods() 获取所有的方法

getReturnType() 获取方法的返回值类型

getParameterTypes() 获取方法的传入参数类型

getDeclareMethod("方法名,参数类型.class,....") 获得特定的方法

构造方法关键字

getDeclaredConstructors() 获取所有的构造方法

getDeclaredConstructors(参数类型.class,....) 获取特定的构造方法

成员变量

getDeclaredFields 获取所有成员变量

getDeclaredField(参数类型.class,....) 获取特定的成员变量

父类和父接口

getSuperclass() 获取某类的父类

getInterfaces() 获取某类实现的接口

1.4 反射的定义

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

1.5 反射的组成

由于反射最终也必须有类参与,因此反射的组成一般有下面几个方面组成:

1.java.lang.Class.java:类对象;

2.java.lang.reflect.Constructor.java:类的构造器对象;

3.java.lang.reflect.Method.java:类的方法对象;

4.java.lang.reflect.Field.java:类的属性对象;

反射中类的加载过程

根据虚拟机的工作原理,一般情况下,类需要经过:加载->验证->准备->解析->初始化->使用->卸载这个过程,如果需要反射的类没有在内存中,那么首先会经过加载这个过程,并在在内存中生成一个class对象,有了这个class对象的引用,就可以发挥开发者的想象力,做自己想做的事情了。

1.6 反射的作用有哪些

前面只是说了反射是一种具有与Java类进行动态交互能力的一种机制,在Java和Android开发中,一般情况下下面几种场景会用到反射机制.

需要访问隐藏属性或者调用方法改变程序原来的逻辑,这个在开发中很常见的,由于一些原因,系统并没有开放一些接口出来,这个时候利用反射是一个有效的解决方法

自定义注解,注解就是在运行时利用反射机制来获取的。

在开发中动态加载类,比如在Android中的动态加载解决65k问题等等,模块化和插件化都离不开反射,离开了反射寸步难行。

2.反射的相关使用 2.1.4 通过反射获取成员变量[包含私有]并使用
clazz = Class.forName("com.ycbjie.ycpaidian.four.ReflexUtils");
Constructor constructor = clazz.getDeclaredConstructor(Context.class);
//设置安全检查,访问私有构造函数必须
constructor.setAccessible(true);
Object[] obj = new Object[]{mContext};
//创建实例
ReflexUtils clazzObj = (ReflexUtils) constructor.newInstance(obj);


//反射修改私有变量
// 获取声明的 code 字段,这里要注意 getField 和 getDeclaredField 的区别
Field gradeField = clazz.getDeclaredField("code");
// 如果是 private 或者 package 权限的,一定要赋予其访问权限
gradeField.setAccessible(true);
// 修改 student 对象中的 Grade 字段值
gradeField.set(clazzObj, 2);
LogUtils.e("点击3----"+clazzObj.getCode());
2.1.5 通过反射获取无参无返回值成员方法[包含私有]并使用
clazz = Class.forName("com.ycbjie.ycpaidian.four.ReflexUtils");
Constructor constructor = clazz.getDeclaredConstructor(Context.class);
//设置安全检查,访问私有构造函数必须
constructor.setAccessible(true);
Object[] obj = new Object[]{mContext};
//创建实例
ReflexUtils clazzObj = (ReflexUtils) constructor.newInstance(obj);


// 获取私有方法,同样注意 getMethod 和 getDeclaredMethod 的区别
Method goMethod = clazz.getDeclaredMethod("getMethod");
// 赋予访问权限
goMethod.setAccessible(true);
// 调用 goToSchool 方法。
goMethod.invoke(clazzObj);
2.1.6 通过反射获取带参无返回值成员方法并使用
clazz = Class.forName("com.ycbjie.ycpaidian.four.ReflexUtils");
Constructor constructor = clazz.getDeclaredConstructor(Context.class);
//设置安全检查,访问私有构造函数必须
constructor.setAccessible(true);
Object[] obj = new Object[]{mContext};
//创建实例
ReflexUtils clazzObj = (ReflexUtils) constructor.newInstance(obj);

//这种不行,注意getDeclaredMethod和invoke方法需要传递参数
/*Method copyText = clazz.getDeclaredMethod("copyText");
copyText.setAccessible(true);
copyText.invoke(clazzObj,"测试复制这个功能");*/

// 获取私有方法,同样注意 getMethod 和 getDeclaredMethod 的区别
Method copyText = clazz.getDeclaredMethod("copyText",String.class);
// 赋予访问权限
copyText.setAccessible(true);
// 调用 copyText 方法。
copyText.invoke(clazzObj,"测试复制这个功能");
2.1.7 通过反射获取带参带返回值成员方法并使用
clazz = Class.forName("com.ycbjie.ycpaidian.four.ReflexUtils");
Constructor constructor = clazz.getDeclaredConstructor(Context.class);
//设置安全检查,访问私有构造函数必须
constructor.setAccessible(true);
Object[] obj = new Object[]{mContext};
//创建实例
ReflexUtils clazzObj = (ReflexUtils) constructor.newInstance(obj);

//这种不行,注意getDeclaredMethod和invoke方法需要传递参数
/*Method copyText = clazz.getDeclaredMethod("copyText");
copyText.setAccessible(true);
copyText.invoke(clazzObj,"测试复制这个功能");*/

// 获取私有方法,同样注意 getMethod 和 getDeclaredMethod 的区别
Method copyText1 = clazz.getDeclaredMethod("copyText",String.class,String.class);
// 赋予访问权限
copyText1.setAccessible(true);
// 调用 copyText 方法
boolean isSuccess = (boolean) copyText1.invoke(clazzObj,"测试复制这个功能","1111");
3.相关知识点
3.1.1 设置.setAccessible(true)暴力访问权限

一般情况下,我们并不能对类的私有字段进行操作,利用反射也不例外,但有的时候,例如要序列化的时候,我们又必须有能力去处理这些字段,这时候,我们就需要调用AccessibleObject上的setAccessible()方法来允许这种访问,而由于反射类中的Field,Method和Constructor继承自AccessibleObject,因此,通过在这些类上调用setAccessible()方法,我们可以实现对这些字段的操作。

Field gradeField = clazz.getDeclaredField("code");
// 如果是 private 或者 package 权限的,一定要赋予其访问权限
gradeField.setAccessible(true);

Method goMethod = clazz.getDeclaredMethod("getMethod");
// 赋予访问权限
goMethod.setAccessible(true);
3.1.2 获取Filed两个方法的区别

两者的区别就是 getDeclaredField() 获取的是 Class 中被 private 修饰的属性。 getField() 方法获取的是非私有属性,并且 getField() 在当前 Class 获取不到时会向祖先类获取。

//获取所有的属性,但不包括从父类继承下来的属性
public Field[] getDeclaredFields() throws SecurityException {}

//获取自身的所有的 public 属性,包括从父类继承下来的。
public Field[] getFields() throws SecurityException {}
3.1.3 获取Field的类型

可以看到 getGenericType() 确实把泛型都打印出来了,它比 getType() 返回的内容更详细。

public Type getGenericType() {}
public Class getType() {}

Field[] filed2 = clazz.getFields();
for ( Field f : filed2 ) {
    System.out.println("Field :"+f.getName());
    System.out.println("Field type:"+f.getType());
    System.out.println("Field generic type:"+f.getGenericType());
    System.out.println("-------------------");
}

//打印值
07-31 17:20:41.027 8700-8700/com.ycbjie.ycpaidian I/System.out: Field :cars
07-31 17:20:41.027 8700-8700/com.ycbjie.ycpaidian I/System.out: Field type:interface java.util.List
07-31 17:20:41.028 8700-8700/com.ycbjie.ycpaidian I/System.out: Field generic type:java.util.List
07-31 17:20:41.028 8700-8700/com.ycbjie.ycpaidian I/System.out: Field :map
07-31 17:20:41.028 8700-8700/com.ycbjie.ycpaidian I/System.out: Field type:class java.util.HashMap
07-31 17:20:41.028 8700-8700/com.ycbjie.ycpaidian I/System.out: Field generic type:java.util.HashMap
07-31 17:20:41.028 8700-8700/com.ycbjie.ycpaidian I/System.out: Field :name
07-31 17:20:41.028 8700-8700/com.ycbjie.ycpaidian I/System.out: Field type:class java.lang.String
07-31 17:20:41.028 8700-8700/com.ycbjie.ycpaidian I/System.out: Field generic type:class java.lang.String
3.1.4 Method获取方法名,获取方法参数

//获取方法名


//获取方法参数
//返回的是一个 Parameter 数组,在反射中 Parameter 对象就是用来映射方法中的参数。
public Parameter[] getParameters() {}


//Method获取方法名
Method[] declaredMethods1 = clazz.getDeclaredMethods();
for ( Method m : declaredMethods1 ) {
    System.out.println("method name:"+m.getName());
}
//获取方法参数
for ( Method m : declaredMethods1 ) {
    System.out.println("获取方法参数method name:"+m.getName());
    //获取参数
    Parameter[] paras;
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
        paras = m.getParameters();
        for (Parameter c : paras ) {
            System.out.println("获取参数parameter :"+c.getName()+" "+c.getType().getName());
        }
    }
    //获取所有的参数类型
    Class[] pTypes = m.getParameterTypes();
    for ( Class c : pTypes ) {
        System.out.print("参数类型method para types:"+ c.getName());
    }
    System.out.println();
    System.out.println("==========================================");
}

//打印日志如下所示:
07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: ==========================================
07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 获取方法参数method name:copyText
07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 获取参数parameter :arg0 java.lang.String
07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 参数类型method para types:java.lang.String
07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: ==========================================
07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 获取方法参数method name:copyText
07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 获取参数parameter :arg0 java.lang.String
07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 获取参数parameter :arg1 java.lang.String
07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 参数类型method para types:java.lang.String参数类型method para types:java.lang.String
07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: ==========================================
07-31 19:23:13.191 4022-4022/com.ycbjie.ycpaidian I/System.out: 获取方法参数method name:getCode
07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: ==========================================
07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: 获取方法参数method name:getUserInfo
07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: 获取参数parameter :arg0 java.lang.String
07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: 参数类型method para types:java.lang.String
07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: ==========================================
07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: 获取方法参数method name:setCode
07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: 获取参数parameter :arg0 int
07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: 参数类型method para types:int
07-31 19:23:13.192 4022-4022/com.ycbjie.ycpaidian I/System.out: ==========================================
3.1.5 Method方法的invoke()方法执行

Method 调用 invoke() 的时候,存在许多细节:

invoke() 方法中第一个参数 Object 实质上是 Method 所依附的 Class 对应的类的实例,如果这个方法是一个静态方法,那么 ojb 为 null,后面的可变参数 Object 对应的自然就是参数。

invoke() 返回的对象是 Object,所以实际上执行的时候要进行强制转换。

在对Method调用invoke()的时候,如果方法本身会抛出异常,那么这个异常就会经过包装,由Method统一抛InvocationTargetException。而通过InvocationTargetException.getCause() 可以获取真正的异常。

关于我的博客

我的个人站点:www.yczbj.org,www.ycbjie.cn

github:https://github.com/yangchong211

知乎:https://www.zhihu.com/people/...

简书:http://www.jianshu.com/u/b7b2...

csdn:http://my.csdn.net/m0_37700275

喜马拉雅听书:http://www.ximalaya.com/zhubo...

开源中国:https://my.oschina.net/zbj161...

泡在网上的日子:http://www.jcodecraeer.com/me...

邮箱:yangchong211@163.com

阿里云博客:https://yq.aliyun.com/users/a... 239.headeruserinfo.3.dT4bcV

segmentfault头条:https://segmentfault.com/u/xi...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/76581.html

相关文章

  • java反射-类

    摘要:使用反射可以在运行时检视类。类名类修饰符等包信息超类所实现的接口构造函数方法属性注解类中附加了很多信息,你可以在获得一个完整列表。全限定名包含所有的包名。构造函数你可以访问类的构造函数,代码如下构造函数的详细教程在章节。 使用反射可以在运行时检视Java类。检视类通常是使用反射时所做的第一件事情。从类中可以获得下面的信息。 类名 类修饰符(private、public、synchro...

    FrancisSoung 评论0 收藏0
  • Java 反射教程

    摘要:反射非常强大和有用。另外,反射可以用在映射结果集的列名到对象的方法。本教程将深入介绍反射。本教程还将清除一些关于范型信息在运行时可用性的认知混淆。类对象使用反射时,起点通常是需要使用反射检视的类的对象。 Java反射可以在运行时检视类、接口、属性和方法,而无需在编译时知道类名、方法名等等。它也同样使用反射支持实例化新的对象、调用方法和get/set属性值。 Java反射非常强大和有用...

    klivitamJ 评论0 收藏0
  • Java相关

    摘要:本文是作者自己对中线程的状态线程间协作相关使用的理解与总结,不对之处,望指出,共勉。当中的的数目而不是已占用的位置数大于集合番一文通版集合番一文通版垃圾回收机制讲得很透彻,深入浅出。 一小时搞明白自定义注解 Annotation(注解)就是 Java 提供了一种元程序中的元素关联任何信息和着任何元数据(metadata)的途径和方法。Annotion(注解) 是一个接口,程序可以通过...

    wangtdgoodluck 评论0 收藏0
  • java

    摘要:多线程编程这篇文章分析了多线程的优缺点,如何创建多线程,分享了线程安全和线程通信线程池等等一些知识。 中间件技术入门教程 中间件技术入门教程,本博客介绍了 ESB、MQ、JMS 的一些知识... SpringBoot 多数据源 SpringBoot 使用主从数据源 简易的后台管理权限设计 从零开始搭建自己权限管理框架 Docker 多步构建更小的 Java 镜像 Docker Jav...

    honhon 评论0 收藏0
  • Java反射-动态代理

    摘要:动态代理有多种不同的用途,例如,数据库连接和事务管理用于单元测试的动态模拟对象其他类似的方法拦截。调用序列和下面的流程类似单元测试动态对象模拟利用动态代理实现单元测试的动态存根代理和代理。框架把包装成动态代理。 使用反射可以在运行时动态实现接口。这可以使用类java.lang.reflect.Proxy。这个类的名称是我将这些动态接口实现称之为动态代理的原因。动态代理有多种不同的用途,...

    Acceml 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<