• Java的反射机制是Java特性之一,反射机制是构建框架技术的基础所在。灵活掌握Java反射机制,对以后学习框架技术有很大的帮助。
  • 本篇文章用到的代码在我的github上面:BitHachi/JJava_core_book/tree/master/src/JavaSE/Chapter5/Section57。
  • 本篇文章已收录到个人博客,阅读体验更佳哦:https://www.bithachi.cn/posts/6e3bcb10.html

1.什么是Java的反射呢?

大家都知道,要让Java程序能够运行,那么就得让Java类要被Java虚拟机加载。Java类如果不被Java虚拟机加载,是不能正常运行的。现在我们运行的所有的程序都是在编译期的时候就已经知道了你所需要的那个类的已经被加载了。
       Java的反射机制是在编译并不确定是哪个类被加载了,而是在程序运行的时候才加载、探知、自审。使用在编译期并不知道的类。这样的特点就是反射。
       反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。

2.Java反射有什么作用呢?

假如我们有两个程序员,一个程序员在写程序的时候,需要使用第二个程序员所写的类,但第二个程序员并没完成他所写的类。那么第一个程序员的代码能否通过编译呢?这是不能通过编译的。利用Java反射的机制,就可以让第一个程序员在没有得到第二个程序员所写的类的时候,来完成自身代码的编译。
       Java的反射机制它知道类的基本结构,这种对Java类结构探知的能力,我们称为Java类的“自审”。大家都用过IDEA和eclipse。当我们构建出一个对象的时候,去调用该对象的方法和属性的时候。一按点,编译工具就会自动的把该对象能够使用的所有的方法和属性全部都列出来,供用户进行选择。这就是利用了Java反射的原理,是对我们创建对象的探知、自审。(反射是一种功能强大且复杂的机制。使用它的主要人员是工具构造者,而不是应用程序员。)

3.Class类

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

4.获取class类对象

既然class对象如此重要,那么我们如何获取class对象呢?这里有三种方法:

4.1 使用类对象的getClass()方法

  • 使用类对象的getClass()方法

4.2 Class.forName(classname)

  • 使用 Class.forName(classname) 静态方法。当你知道该类的全路径名时,你可以使用该方法获取 Class 类对象
  • 如果className不是类名或接口名,则forname抛出一个checked exception异常所以应该给这个方法提供一个异常处理器

4.3 .class 方法

5.通过反射创建类对象

  • 既然通过上文我们知道了如何获取class对象,那么我们是不是就可以根据这个类对象来创建实例对象呢?当然可以
  • 通过反射创建类对象主要有两种方式:通过 Class 对象的 newInstance() 方法、通过 Constructor 对象的 newInstance() 方法。

5.1 Class 对象的 newInstance() 方法

  • newlnstance方法调用默认的构造器(没有参数的构造器)初始化新创建的对象。如果这个类没有默认的构造器, 就会抛出一个异常。

5.2 Constructor 对象的 newInstance() 方法

  • 通过 Constructor 对象创建类对象可以选择特定构造方法,而通过 Class 对象则只能使用默认的无参数构造方法。
  • 这里getConstructor和newInstance使用时需要设置异常处理,我这里是直接在main后面throws了

6.获取类属性、方法、构造器的结构

  • 我们已经成功获取了class类对象,并学会了如何创建对象,现在我们还可以看看对象内部的结构是什么样的,比如属性、方法和构造器。
  • 在java.lang.reflect 包中有三个类 Field、Method 和 Constructor分别用于描述类的属性、 方法和构造器
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;

下面介绍一下 Field、Method 和 Constructor三个类的常用方法

  • Field类的getType 方法, 用来返回属性所属类型的 Class 对象
  • Method 类有一个getReturnType方法,返回return值所属类型的Class对象
  • Method 和 Constructor 类有一个共同的方法getParameterTypes,返回方法参数所属类型的Class对象
  • Field、Method 和 Constructor都有一个getName 方法,返回方法名的字符串
  • Field、Method 和 Constructor都有一个getModifiers方法,它将返回一个整型数值,用不同的位开关描述 public 和 static 这样 的修饰符使用状况。可以利用 Modifier.toString方法将 修饰符打印出来。
for (Method m : methods) {String modifiers = Modifier.toString(m.getModifiers());
}

该修饰符是java.lang.reflect.Modifier的静态属性。这里是用十进制表示的,源码里面是十六进制表示的。
对应表如下:
PUBLIC: 1
PRIVATE: 2
PROTECTED: 4
STATIC: 8
FINAL: 16
SYNCHRONIZED: 32
VOLATILE: 64
TRANSIENT: 128
NATIVE: 256
INTERFACE: 512
ABSTRACT: 1024
STRICT: 2048

  • 可以使用Modifiei类中的 isPublic、 isPrivate 或 isFinal 判断方法或构造器是否是 public、 private 或 final
  • Class类中的 getFields、 getMethods 和 getConstructors方 法将 分 别 返 回 类 提 供 的 所有public 属性、 方法和构造器数组, 其中包括超类的公有成员
  • Class 类的 getDeclareFields、 getDeclareMethods 和getDeclaredConstructors方法将分别返回类中声明的全部属性、 方法和构 造器, 其中包括private和protected成员,但不包括超类的成员

下面是一个代码案例,显示了如何打印一个类的全部信息的方法。
这个程序提醒用户输入一个类名,然后输出类中所有的属性、方法、构造器。里面有一些数字是我用来测试,类似-m8-

package JavaSE.Chapter5.Section57.cs573;import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Scanner;/*** This program uses reflection to print all features of a class.** @author Cay Horstmann* @version 1.1 2004-02-21* 利用反射分析类的能力,查看属性、构造器、方法的结构*/
public class ReflectionTest {public static void main(String[] args) {String name = "JavaSE.Chapter5.Section57.cs571.Employee";try {//获取输入字符串的类对象Class cl = Class.forName(name);System.out.println(cl + "-1-");//获取父类对象Class supercl = cl.getSuperclass();System.out.println(supercl + "-2-");//获取类的访问修饰符和属于public、private、还是finalString modifiers = Modifier.toString(cl.getModifiers());System.out.println(cl.getModifiers() + "-----cl.getModifiers");System.out.println(modifiers + "-4-");if (modifiers.length() > 0) System.out.print(modifiers + " ");System.out.print("class " + name);if (supercl != null && supercl != Object.class) System.out.print(" extends "+ supercl.getName());System.out.print("\n{\n");System.out.println("------------打印构造器方法-----------");printConstructors(cl);System.out.println("------------打印非构造器方法-----------");printMethods(cl);System.out.println("------------打印属性信息-----------");printFields(cl);System.out.println("}");} catch (ClassNotFoundException e) {e.printStackTrace();}System.exit(0);}/*** Prints all constructors of a class** @param cl a class*/public static void printConstructors(Class cl) {/*返回反映Constructor对象表示的类声明的所有Constructor对象的数组类 。这些是public,protected,default(package)访问和私有构造函数。返回的数组中的元素不会排序,并且不是任何特定的顺序。如果类有一个默认构造函数,它将包含在返回的数组中。如果类对象表示接口,原始类型,数组类或空值,则此方法返回长度为0的数组。*/Constructor[] constructors = cl.getDeclaredConstructors();System.out.println(Arrays.toString(constructors) + "-c5-");for (Constructor c : constructors) {String name = c.getName();System.out.print("   ");String modifiers = Modifier.toString(c.getModifiers());System.out.println(c.getModifiers() + "-----Counstructor.getModifiers");//打印构造方法的访问修饰符if (modifiers.length() > 0) System.out.print(modifiers + " ");//打印构造方法的名字System.out.print(name + "(");//获取类构造器的参数类型数组Class[] paramTypes = c.getParameterTypes();System.out.println("-6-" + Arrays.toString(paramTypes) + "-c6-");for (int j = 0; j < paramTypes.length; j++) {if (j > 0) System.out.print(", ");//打印参数类型名字System.out.print(paramTypes[j].getName());}System.out.println(");");}}/*** Prints all methods of a class** @param cl a class*/public static void printMethods(Class cl) {Method[] methods = cl.getDeclaredMethods();System.out.println(Arrays.toString(methods) + "-m7-");for (Method m : methods) {Class retType = m.getReturnType();System.out.println(retType + "-m8-");String name = m.getName();System.out.print("   ");// print modifiers, return type and method nameString modifiers = Modifier.toString(m.getModifiers());System.out.println(m.getModifiers() + "-----Method.getModifiers");//打印方法的访问修饰符if (modifiers.length() > 0) System.out.print(modifiers + " ");//打印方法返回类型和方法名System.out.print(retType.getName() + " " + name + "(");// print parameter typesClass[] paramTypes = m.getParameterTypes();System.out.println("-m9-" + Arrays.toString(paramTypes) + "-m9-");for (int j = 0; j < paramTypes.length; j++) {if (j > 0) System.out.print(", ");//打印方法参数类型System.out.print(paramTypes[j].getName());}System.out.println(");");}}/*** Prints all fields of a class** @param cl a class*/public static void printFields(Class cl) {Field[] fields = cl.getDeclaredFields();for (Field f : fields) {Class type = f.getType();//返回属性所属类型的 Class 对象System.out.println(type + "-f10-");String name = f.getName();System.out.print("   ");String modifiers = Modifier.toString(f.getModifiers());System.out.println(f.getModifiers() + "-----Field.getModifiers");//打印属性的访问修饰符if (modifiers.length() > 0) System.out.print(modifiers + " ");//打印属性的类型名和属性名字System.out.println(type.getName() + " " + name + ";");}}
}



7.获取或设置类对象的属性值

  • 在编写程序时, 如果知道想要査看的属性和类型,查看指定的属性值是一件很容易的事情。而利用反射机制可以查看在编译时还不清楚的属性值。
  • Class t = f.getType();//获取属性类型,f为Field对象。
  • 我们可以用f.get(obj)获取 obj 属性的当前值。f为Field对象,obj是一个Object对象。
  • 可以获得就可以设置。调用f.set(obj,value)可以将 obj 对象的 f 属性设置成新值。f为Field对象。
  • (1)如果我们要查看某个private属性的值,由于受限于java的访问机制,我们需要调用Field、Method 或 Constructor 对象的 setAccessible 方法,来设置private的值的可访问性,x. setAccessible(true);,x为Field、Method 或 Constructor的对象。
    (2)也可以使用AccessibleObject.setAccessible(x, true);来设置private值的可访问性,它是 Field、 Method 和 Constructor 类的公共超类,x为Field、Method 或 Constructor 对象的数组引用。

接下来的一个例子将使用上面所说的方法,来查看访问对象的属性值

如下一个可供任意类使用的通用 toString方法。 其中使用 getDeclaredFileds 获得所有的数据属性, 然后使用 setAccessible 将所有的属性设置为可访问的。 对于每个属性,获得了名字和值。递归调用 toString方法,将每个值转换成字符串。(这个例子是java核心技术卷一里面的,这个例子看懂我感觉还是需要花时间的,有的地方我还没看懂……)

package JavaSE.Chapter5.Section57.cs574;import JavaSE.Chapter5.Section57.cs571.Employee;import java.lang.reflect.Field;/*** 在运行时使用反射分析对象,查看对象当前的各个属性值*/
public class ObjectAnalyzerTest {public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException, InstantiationException {Employee s = new Employee();System.out.println(new ObjectAnalyzer().toString(s));System.out.println("--------------------------------");String[] str = {"str11", "str22", "str33"};System.out.println(new ObjectAnalyzer().toString(str));System.out.println("--------------------------------");Class em = s.getClass();Object obj = em.newInstance();Field f = em.getDeclaredField("name");f.setAccessible(true);Object val = f.get(obj);//获取属性的值System.out.println(val);f.set(obj, "BitHachi");Employee em2 = (Employee) obj;System.out.println(em2.getName());}
}
package JavaSE.Chapter5.Section57.cs574;import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;public class ObjectAnalyzer {private ArrayList<Object> visited = new ArrayList<>();public String toString(Object obj) {if (obj == null) return "null";if (visited.contains(obj)) return "...";visited.add(obj);Class cl = obj.getClass();//如果对象是一个字符串对象,则直接打印其值if (cl == String.class) return (String) obj;//判断类对象是否是一个数组if (cl.isArray()) {//getComponentType返回对象数组的的Class类对象。 如果此类不表示数组类,则此方法返回null。String r = cl.getComponentType() + "[]{";System.out.println(r + "-1-");//返回指定数组对象的长度Array.getLength(obj)for (int i = 0; i < Array.getLength(obj); i++) {if (i > 0) r += ",";//返回指定数组对象中的索引组件的值。Object val = Array.get(obj, i);System.out.println(val + " -val -2-");/*确定指定类对象表示一个基本类型。有九个预定类对象代表八个原始类型和void。这些是由Java虚拟机创建,并且具有相同的名称为他们所代表的基本类型,即boolean , byte , char , short , int , long , float和double 。isPrimitive返回一个boolean值*/if (cl.getComponentType().isPrimitive()) r += "@" + val + "@";else r += toString(val);System.out.println(r + "-3-");}return r + "}";}String r = cl.getName();System.out.println(r + "-4-");// 检查此类和所有超类的字段do {r += "[";//获取类的所有属性得一个数组Field[] fields = cl.getDeclaredFields();/*setAccessible()为反射对象设置可访问标志。 true 表明屏蔽 Java语言的访问检查,使得对象的 private私有属性也可以被査询和设置。 */AccessibleObject.setAccessible(fields, true);//获取所有属性的名字和值for (Field f : fields) {if (!Modifier.isStatic(f.getModifiers())) {if (!r.endsWith("[")) r += ",";r += f.getName() + "=";System.out.println(r + "-5-");try {Class t = f.getType();//获取属性类型Object val = f.get(obj);//获取属性的值System.out.println(val + " -val -6-");if (t.isPrimitive()) {r += val;System.out.println(r + "-7-");} else {r += toString(val);System.out.println(r + "-7-");}} catch (Exception e) {e.printStackTrace();}}}r += "]";cl = cl.getSuperclass();}while (cl != null);return r;}
}

8.利用反射调用任意方法

  • 反射机制可以允许调用任意的方法
  • 在Method类中有一个invoke方法,它可以允许调用包装在Method对象中的方法


下面是一个代码示例:

package JavaSE.Chapter5.Section57.cs576;import java.lang.reflect.Method;public class MethodTableTest {public static void main(String[] args) throws Exception {// 获取相应方法的Method对象,通过类对象来获取方法对象Method square = MethodTableTest.class.getMethod("square", double.class);Method sqrt = Math.class.getMethod("sqrt", double.class);// 打印x和y值表printTable(1, 10, 10, square);printTable(1, 10, 10, sqrt);}public static double square(double x) {return x * x;}/*** Prints a table with x- and y-values for a method** @param from the lower bound for the x-values 上限* @param to   the upper bound for the x-values  下限* @param n    the number of rows in the table   个数* @param f    a method with a double parameter and double return value*/public static void printTable(double from, double to, int n, Method f) {// print out the method as table headerSystem.out.println("方法: " + f);double dx = (to - from) / (n - 1);//按上下限设置每次加的值for (double x = from; x <= to; x += dx) {try {double y = (Double) f.invoke(null, x);//调用这个方法对象进行计算System.out.printf("%10.4f | %10.4f%n", x, y);} catch (Exception e) {e.printStackTrace();}}}
}

9. 使用反射编写泛型数组代码,复制数组

  • 我们可以利用反射来扩充一个数组的容量
package JavaSE.Chapter5.Section57.cs575;import java.lang.reflect.Array;
import java.util.Arrays;public class CopyOfTest {public static void main(String[] args) {int[] a = {1, 2, 3};a = (int[]) goodCopyOf(a, 10);System.out.println(Arrays.toString(a));System.out.println("--------------------");String[] b = {"Tom", "Dick", "Harry"};b = (String[]) goodCopyOf(b, 10);System.out.println(Arrays.toString(b));}public static Object goodCopyOf(Object a, int newLength) {//第一步:获取a数组的类对象Class cl = a.getClass();System.out.println(cl + "---1");//第二步:判断a数组的类对象是否是一个数组if (!cl.isArray()) return null;//第三步:使用Class类的getComponentType方法确定数组对应的类型Class componentType = cl.getComponentType();System.out.println(componentType + "---2");//获取数组的长度int length = Array.getLength(a);System.out.println(length + "---3");//构造新数组newInstance方法//返回一个具有给定类型、给定长度的新数组Object newArray = Array.newInstance(componentType, newLength);/*arraycopy(Object src, int srcPos, Object dest, int destPos, int length)将指定源数组中的数组从指定位置复制到目标数组的指定位置。*/System.arraycopy(a, 0, newArray, 0, Math.min(length, newLength));return newArray;}
}

10. 可以用==比较两个Class对象

  • 虚拟机为每个类型管理一个 Class 对象。因此, 可以利用== 运算符实现两个类对象比较的操作。 例如:

11.收获与感受

  • 反射里面用到了多态的特性,这一点真的很重要,特别是Object与其它对象之间的互转,不懂得话,很容易懵圈
  • 4-8的应用是反射里面的核心点,当然还有很多的API没办法一次性讲完,其实只要懂了核心的部分,其它的API就比较好懂了
  • 关于反射的内容和应用还有很多,以后在工作中遇到了相关内容再进行补充叭,现在作为初学者,先总结整理这么多叭。

参考

  • https://www.cnblogs.com/chanshuyi/p/head_first_of_reflection.html#%E5%8F%8D%E5%B0%84%E5%B8%B8%E7%94%A8api
  • https://www.iteye.com/blog/762626559-qq-com-395402
  • https://blog.csdn.net/qq_40434646/article/details/82351488
  • https://blog.csdn.net/kjfcpua/article/details/8496911
  • http://yuncode.net/code/c_56768be18995515
  • https://www.cnblogs.com/fengmao/p/8609855.html
  • https://www.cnblogs.com/daimajun/p/6545533.html

肝了十几个小时的java反射,希望对大家有所帮助吧!相关推荐

  1. 半小时复习java全内容

    半小时复习Java全内容 来都来了点个赞呗 o(*≧▽≦)ツ 这段时间要急着考试的同学,可以看我画的重点,目录上有标识,如果时间充裕也可以详细看下去,会很有帮助的.我会用视频加图画来解释.这篇文章中, ...

  2. 技术直播:1小时突击Java工程师面试核心(限免报名)

    后疫情时代,连程序员这个多金的职业也遭受到了一定程度的打击.从各大招聘网站和多次面试经历中,相信大家已经意识到,面试官对程序员技能体系和项目经验考核似乎更严苛了.你在面试中常常为什么苦恼呢?简历撰写? ...

  3. 【学术相关】海外博士一般朝九晚五,国内博士动辄十几个小时科研时间。为什么普遍认为海外博士水平比较高?...

    有同学问:海外博士一般朝九晚五,国内博士动辄十几个小时科研时间.为什么普遍认为海外博士水平比较高? 本文来源:https://www.zhihu.com/question/430471502 回答一 ...

  4. 五十八、深入了解 Java 中的注解和自定义注解

    @Author:Runsen @Date:2020/7/9 人生最重要的不是所站的位置,而是内心所朝的方向.只要我在每篇博文中写得自己体会,修炼身心:在每天的不断重复学习中,耐住寂寞,练就真功,不畏艰 ...

  5. 十大排序算法(Java)

    文章目录 十大排序算法(Java) 一.冒泡排序(Bubble Sort) 二.选择排序(Selection Sort) 三.堆排序(Heap Sort) 四.插入排序(Insertion Sort) ...

  6. 【Java学习笔记之二十六】深入理解Java匿名内部类

    在[Java学习笔记之二十五]初步认知Java内部类中对匿名内部类做了一个简单的介绍,但是内部类还存在很多其他细节问题,所以就衍生出这篇博客.在这篇博客中你可以了解到匿名内部类的使用.匿名内部类要注意 ...

  7. [ Coding七十二绝技 ] 如何利用Java异常快速分析源码

    [ Coding七十二绝技 ] 如何利用Java异常快速分析源码 参考文章: (1)[ Coding七十二绝技 ] 如何利用Java异常快速分析源码 (2)https://www.cnblogs.co ...

  8. 2008年8月24号,星期天,晴。岁寒,然后知松柏之后凋也。 ——《论语•子罕》今天是我博士生涯的第49天,今天太太就要从安阳动身回九江了,又得要十几个小时颠簸了

    2008年8月24号,星期天,晴. 岁寒,然后知松柏之后凋也. --<论语•子罕> 今天是我博士生涯的第49天,今天太太就要从安阳动身回九江了,又得要十几个小时颠簸了,太太虽然口口声声说小 ...

  9. java语言程序设计第十版(Introduce to java 10th) 课后习题 chapter6-27

    java语言程序设计第十版(Introduce to java 10th) 课后习题 chapter6-27 自己纯手工,欢迎讨论 package chapter6;public class T27 ...

最新文章

  1. 2020长沙“科技之星”榜单重磅揭晓,近百家企业凭实力“出道”!
  2. 微信小程序:获取地理定位和显示相应的城市名称。
  3. Async和Await如何简化异步编程几个实例
  4. mysql:mysql error:Access denied for user 'root'@'localhost' (using password: YES)
  5. ORA-12012: error on auto execute of job quot;ORACLE_OCM
  6. OVS DPDK vhost-user详解(十一)
  7. C#判断Textbox是否为数字
  8. 探秘音视频网络优化与全球化部署最佳实践
  9. c51汇编语言如何定义全局变量_汇编语言期末复习笔记(七)
  10. LVS+keepalived负载均衡 ??待续
  11. vscode终端无法识别node
  12. 具体案例 快速原型模型_3D打印机器人手板模型,低成本快速原型打样
  13. highlightjs 详解
  14. 四大组件 之 Broadcast Receiver
  15. GPRS网络总体结构
  16. 金融风控模型前世今生
  17. html自定义菜单按钮图片,editormd,markdown 自定义导航栏按钮
  18. python实现向qq邮箱发送邮件
  19. 用Python进行文本分析时出现UnicodeDecodeError错误的解决方法
  20. c 语言中虚方法有什么作用是什么,虚函数的作用?

热门文章

  1. 上海市高校精品课程“网络安全技术”
  2. vue2.0 子组件和父组件之间的传值
  3. bzero, memset ,setmem 区别【转】
  4. 送给使用phpstorm+thinkphp开发者的福利
  5. Linux rpm 命令参数使用详解[介绍和应用]
  6. Http-tunnel突破单位网管封杀QQ、MSN端口的方法
  7. 【隐私】大数据下的隐私威胁【转载】
  8. 重磅!!kaggle训练, 终于不用怕断网了
  9. linux shell函数
  10. linux命令:java程序后台运行