Java中有许多对象在运行时都会出现两种类型:编译时类型和运行时类型。例如下面的语句:

Parent p = new Child();

变量p在编译时的类型为Parent,运行时类型为Child,体现了类的多态。另外,某些情况下程序又需要调用该对象运行时类的方法。为了解决类似的问题,程序需要在运行时发现对象和类的真实结构信息。

在程序运行时获取对象的真实性息有以下两种做法:

  • 在知道对象的具体类型的情况下,可以先使用instanceof运算符进行判断,再利用类型强制转换将其转换成运行时的类型变量即可
  • 在无法预知该对象属于哪些类的情况下,必须通过反射来发现该对象和类的真实信息

反射(Reflection)机制允许程序在运行时借助Reflection API获取任何类的内部信息,并能直接操作对象的内部属性及方法。反射被视为动态语言的关键。

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

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时获取任意一个类的所具有的成员变量和方法
  • 在运行时调用任意一个对象的方法
  • 生成动态代理

反射机制是实现很多流行框架的基础,如Spring,Hibernate等框架都采用反射机制。Reflection API提供了Constructor、Filed和Method类,这三个类定义在java.lang.reflect包中,分别用于描述类的构造方法、属性和方法。下面示例使用反射机制获取类对象的所有方法。

【示例】使用反射机制获取类对象的所有方法

Class c = Class.forName("java.lang.String");
//获取当前类对象的所有方法
Method[] mtds = c.getDeclaredMethods();
for (Method m : mtds)System.out.println(m);
}

上述代码首先使用Class.forName()方法来获取String类的Class对象;再调用getDeclaredMethods()方法获取String类的所有方法,该方法的返回类型是Method[]方法数组。

Executable抽象类

Executable抽象类代表可执行的类成员。Executable抽象类派生了Constructor和Method两个子类。Executable抽象类提供大量方法来获取参数、修饰符或注解等信息,常用方法如下

Executable类的方法
方法 功能描述
Parameter[] getParameters() 获取所有形参,返回一个Parameter[]数组
int getParameterCount() 获取形参个数
abstract int getModifiers() 获取修饰符,返回的整数是修饰符public、protected、private、final、static、abstract等关键字所对应的常量
boolean isVarArgs() 判断是否包含数量可变的形参

Constructor类

Constructor类用于表示类的构造方法,通过调用Class对象的getConstructors()方法可以获取当前类的构造方法的集合。Constructor常用方法及使用说明如下

Constructor类的方法
方法 功能描述
String getName() 返回构造方法的名称
Class []getParameterTypes() 返回当前构造方法的参数类型
intgetModifiers() 返回修饰符的整型标识,返回的整数是修饰符public、protected、private、final、static、abstract等关键字所对应的常量,需使用Modifier工具类的方法解码后才能获得真实的修饰符

下述代码调用getConstructors()方法获取指定类的构造方法信息

ConstructorReflectionaDemo.java

import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;public class ConstructorReflectionDemo {public static void main(String[] args) {try {//获取String类对象Class clazz = Class.forName("java.lang.String");//返回所有构造方法Constructor[] ctors = clazz.getDeclaredConstructors();//遍历构造方法for(Constructor c : ctors){//获取构造方法的修饰符int mod = c.getModifiers();//使用Modifier工具类的方法获得真实的修饰符,并输出System.out.print(Modifier.toString(mod));//获取构造方法的名称,并输出System.out.print(" " + c.getName() + "(");//获取构造方法的参数类型Class[] paramTypes = c.getParameterTypes();//循环输出结构方法的参数类型for(int i = 0;i < paramTypes.length;i++){if(i>0){System.out.print(",");}//输出类型名称System.out.print(paramTypes[i].getName());}System.out.println(");");}} catch (ClassNotFoundException e) {e.printStackTrace();}}
}

上述代码通过反射操作,获取了java.lang.String类的所有构造方法及其参数信息。调用getModifiers()方法可以获取构造方法的修饰符,该方法的返回值是整数,即修饰符public、protected、private、final、static、abstract等关键字所对应的常量。此时引入Modifier类,通过调用Modifier.toString(int mod)方法,返回修饰符常量所对应的字符串。反之,可以通过下面语句来查看各修饰符的对应值

System.out.println(Modifier.PUBLIC);

查看java.lang.String类的API,与程序的运行结果对照,运行结果如下

public java.lang.String([B);
public java.lang.String([B,int,int);
public java.lang.String([B,java.nio.charset.Charset);
public java.lang.String([B,java.lang.String);
public java.lang.String([B,int,int,java.nio.charset.Charset);
 java.lang.String([C,int,int,java.lang.Void);
 java.lang.String(java.lang.AbstractStringBuilder,java.lang.Void);
public java.lang.String(java.lang.StringBuilder);
public java.lang.String(java.lang.StringBuffer);
 java.lang.String([B,byte);
public java.lang.String([C,int,int);
public java.lang.String([C);
public java.lang.String(java.lang.String);
public java.lang.String();
public java.lang.String([B,int,int,java.lang.String);
public java.lang.String([B,int);
public java.lang.String([B,int,int,int);
public java.lang.String([I,int,int);

Field类

Field类用于封装属性的信息,调用Class对象的getField()或getField()方法可以获取当前类的所有属性或指定属性。Filed类的常用方法如图

Filed类的常用方法
方法 功能描述
String getName() 返回属性的名称
intgetModifiers() 返回修饰符的整型标识
getXxx(Object obj) 获取属性的值,此处的Xxx对应Java8中的基本类型,如果属性是引用类型,则直接使用get(Object obj)方法
setXxx(Object obj,Xxx val) 设置属性的值,此处的Xxx对应Java8中的基本类型,如果是属性是引用类型,则直接使用get(Object obj,Object val)方法
Class[]getType() 返回当前属性的类型

下述代码调用getFileds()方法用于获取指定类的属性信息并显示

FieldDemo.java

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;class Person{private String name;private int age;private String address;@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +", address='" + address + '\'' +'}';}
}
public class FieldDemo {public static void main(String[] args) {try {//获取Person类对应的Class对象Class<Person>personClazz = Person.class;System.out.println("-----Person类的属性-----");//返回声明的所有属性,包括私有的和受保护的,但不包括超类属性Field[] fields = personClazz.getDeclaredFields();for (Field f : fields){//获取属性的修饰符int mod = f.getModifiers();//使用Modifier工具类的方法获得真实的修饰符,并输出System.out.print(Modifier.toString(mod));//获取属性的类型,并输出Class type = f.getType();System.out.print(" " + type.getName());//获取属性的名称,并输出System.out.println(" " + f.getName());}//创建一个Person对象Person p = new Person();//使用getDeclaredField()方法表明可获取各种访问控制符的成员变量//获取Person类的name属性对象Field nameField = personClazz.getDeclaredField("name");//设置通过反射访问该成员变量时取消访问检查nameField.setAccessible(true);//为p对象的name属性设置值,因为String是引用类型,所以直接使用set()方法nameField.set(p,"张三");//获取Person类的age属性对象Field ageField = personClazz.getDeclaredField("age");//设置通过反射访问该成员变量时取消访问权限检查ageField.setAccessible(true);//调用setInt()方法为p对象的age属性设置值ageField.setInt(p,36);//获取Person类的address属性对象Field addressField = personClazz.getDeclaredField("address");//设置通过反射访问该成员变量时取消访问权限检查addressField.setAccessible(true);//为p对象的name属性设置值,因String是引用类型,所以直接使用set()方法addressField.set(p,"上海");//输出对象p的信息System.out.println("-----Person实例-----");System.out.println(p);} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}
}

上述代码定义了一个Person类,该类有name、age和address三个属性。在main()方法中先获取Person类所对应的Class对象,再获取该类的属性并显示,最后分别设置属性值并显示。

注意调用getDeclaredFields()方法可以获取包括私有和受保护的所有属性,但不包括父类的属性;调用getFields()方法可以获取所有public属性,包括从父类继承的公共属性。

运行结果:

-----Person类的属性-----
private java.lang.String name
private int age
private java.lang.String address
-----Person实例-----
Person{name='张三', age=36, address='上海'}

Parameter类

Parameter类也是Java8新增的API,每个Parameter对象代表方法的一个参数。Parameter类中提供了许多方法来获取参数信息,常用方法如下

Parameter类的常用方法
方法 功能描述
int getModifiers() 获取参数的修饰符
String getName() 获取参数的形参名
Type getParameterizedType() 获取带泛型的新参类型
Class<?> getType() 获取形参类型
boolean isVarArgs() 判断该参数是否是可变参数
boolean isNamePresent() 判断.class文件中是否包含方法的形参信息

使用javac命令编译Java源文件时,默认生成的.class文件不包含方法的形参名信息,因此调用getName()方法不能得到参数的形参名,调用isNamePresent()方法将返回false。如果希望javac命令编译Java源文件时保留形参信息,则需要为编译命令指定-parameters选项

下述代码演示Java8的方法参数反射功能

MethodParameterDemo.java

class MyClass{public void setName(String name){}public void display(String str, List<String> list){}
}
public class MethodParameterDemo {public static void main(String[] args) throws Exception{//获取MyClass的类对象Class<MyClass>clazz = MyClass.class;//获取MyClass类的所有public方法Method[] mtds = clazz.getMethods();for (Method m : mtds){//输出方法名System.out.println("方法名:" + m.getName());//输出该方法参数个数System.out.println("参数个数:" + m.getParameterCount());//获取该方法所有参数Parameter[] parameters = m.getParameters();int index = 1;//遍历所有参数,并输出参数信息for(Parameter p : parameters){if (p.isNamePresent()){System.out.println("---第" + (index++) + "个参数信息---");System.out.println("参数名:" + p.getName());System.out.println("形参类型:" + p.getType());System.out.println("泛型类型:" + p.getParameterizedType());}}System.out.println("--------------------------------------------");}}
}

上述代码定义了一个简单的MyClass类用于测试,该类包含两个带参数的方法。在main()方法中,先获取MyClass类的所有方法,并使用嵌套循环来输出该类中的所有方法及参数信息。输出参数信息的前提是p.isNamePresent()为true,即只有当.class文件中包含参数信息时,程序才会执行条件体内输出参数信息的三行代码。因此,需要使用javac-parameters命令编译该程序代码。(就是需要在黑窗口里编译运行)

javac -parameters -d.MethodParameterDemo

再使用java命令运行程序代码:

java test05.MethodParameterDemo

什么是反射 反射怎么用相关推荐

  1. Proxy代理 和 Reflect反射(反射的是obj)的概念

    1. Proxy代理 // 供应商(原始对象)let obj = {time:'2018-01-03',name:'net',_r: "123"}// 创建代理商,传入obj数据l ...

  2. Unity C#基础之 反射反射,程序员的快乐

    反射反射,程序员的快乐 这句话想必大家都经常听过,基本上在绝大多数的框架和一些设计模式中都能看到反射的身影(MVC.IOC.AOP.O/RM), 反射:是.Net Framework提供的一个帮助类库 ...

  3. java 反射.问题_Java知识点总结(反射-反射机制性能问题)

    Java知识点总结(反射-反射机制性能问题) @(Java知识点总结)[Java, 反射] 性能相关注意点: setAccessible 启用和禁用访问安全检查的开关,值为 true 则指示反射的对象 ...

  4. 黑马程序员-反射-constructor-feld类-Method-数组的反射-反射的作用

    ---------------------- android培训.java培训.期待与您交流! ---------------------- 反射的基石→Class类 Java程序中的各个Java类属 ...

  5. Java反射——反射机制问题——第一章

    Java反射--反射机制问题--第一章 Java反射--Class类--第二章 Java反射--类加载--第三章 Java反射--反射获取类的结构信息--第四章 文章目录 1:一个需求引出反射 2:反 ...

  6. 抽象工厂+反射=反射工厂

    在我的上一篇文章(疑惑?改良?从简单工厂到工厂方法)中,详细论述了创建模式中简单工厂到工厂方法的演变过程,并试图结合工厂方法的设计以及.net中的反射机制之所长,改良出一种新型的工厂-反射工厂,这当然 ...

  7. 你必须了解的反射——反射来实现实体验证

    开发工作中,都会需要针对传入的参数进行验证,特别是针对实体进行验证,确保传入的参数格式正确.这里做了一个非常简单的组件进行验证.抛砖引玉,让大家深入思考下反射的应用. 需求 日常开发,都是通过API进 ...

  8. 方法的反射---反射学习笔记(二)

    方法的反射:其实就是通过方法的对象来调用方法. 如何获取某个方法:方法的名称和方法的参数列表才能唯一决定某个方法 方法反射如何操作呢? 1.要获取一个方法(就是获取类的信息),要获取类的信息首先要获取 ...

  9. 注解与反射 - 反射 - 操作反射

    获取类的运行时状态 //获取类的信息 public class Test05 {public static void main(String[] args) throws ClassNotFoundE ...

最新文章

  1. 区块链BaaS云服务(36)欧盟EBSI 区块链
  2. c#NPOI导出2007版本excel
  3. C/C++中宏使用总结
  4. 下列关于计算机图形的应用中 错误的是,计算机图形学题库及答案
  5. 利用python爬虫(案例8)--今天就是吃X我也要搞到有道
  6. html5声音播放音乐,HTML5 煽情的音乐播放器和音频可视化
  7. 如何学习IOS开发~三个字(学思做)
  8. java设置环境变量win7_Windows7系统配置java环境变量的详细教程
  9. yarn在vscode里启动报错
  10. 为什么稀疏自编码器的正则项选用了相对熵(KL散度)的函数?
  11. 台式计算机开机密码设置,设置台式电脑的开机锁屏密码的方法步骤
  12. 宏想固态无法格式化,SM2258XT主控开卡成功经验,SM2259XT可参考
  13. 微信小程序框架--weui
  14. 软件测试睡眠原理,测一测你的睡眠质量
  15. css规则中区块block,听晴空讲Drupal主题——第六章 主题中的CSS(10)
  16. es文件浏览器smb服务器,ES文件浏览器怎么连接电脑SMB,可以参考这篇文章
  17. Semi-Supervised Semantic Image Segmentation with Self-correcting Networks:基于自校正网络的半监督语义图像分割
  18. android 设置画布颜色,android – 如何设置笔触颜色以在画布上绘制矩形?
  19. TNF 又见 《Cell》
  20. 安装纯净版win10系统

热门文章

  1. 【数据结构】- 几个步骤教你认识并实现一个链表之带头(哨兵位)双向循环链表(上)
  2. 如何用Mindmanager画思维导图
  3. iOS基础:新浪微博授权机制、 版本新特性
  4. 是谁给我充的手机费?
  5. ERROR: torch-1.6.0+cu101-cp37-cp37m-win_amd64.whl is not a supported wheel on this platform.
  6. 三种算法求解最大公约数和最小公倍数
  7. 开源项目分析解读——基于Spring Cloud的在线考试系统
  8. JAVA8数组相并_【Java笔记】Java8中数组(引用类型)、String、List、Set之间的相互转换问题...
  9. linux灯控软件,Ubuntu下通过脚本控制键盘背光灯
  10. 如何建立起一套有效的APP监控体系