本文部分内容参考博客。点击链接可以查看原文。


1. 反射的概念

反射是指在运行时将类的属性、构造函数和方法等元素动态地映射成一个个对象。通过这些对象我们可以动态地生成对象实例,调用类的方法和更改类的属性值。

2. 使用场景

什么情况下运用JAVA反射呢?如果编译时根本无法预知对象和类可能属于哪些类,程序只依靠运行时信息来发现该对象和类的真实信息,此时就必须使用反射。

使用反射可以实现下面的功能:

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

3. 获得Class对象的几种方式

前面已经介绍过了,每个类被加载之后,系统就会为该类生成一个对应的Class对象,通过该Class对象就可以访问到JVM中的这个类。在Java程序中获得Class对象通常有如下3种方式。

  • 使用Class类的forName(String clazzName)静态方法。该方法需要传入字符串参数,该字符串参数的值是某个类的全限定类名(必须添加完整包名)。
  • 调用某个类的class属性来获取该类对应的Class对象。例如,Person.class将会返回Person类对应的Class对象。
  • 调用某个对象的getClass()方法。该方法是java.lang.Object类中的一个方法,所以所有的Java对象都可以调用该方法,该方法将会返回该对象所属类对应的Class对象。

对于第一种方式和第二种方式都是直接根据类来取得该类的Class对象,相比之下,第二种方式有如下两种优势。

  • 代码更安全。程序在编译阶段就可以检查需要访问的Class对象是否存在。
  • 程序性能更好。因为这种方式无须调用方法,所以性能更好。

也就是说,大部分时候我们都应该使用第二种方式来获取指定类的Class对象。但如果我们只有一个字符串,例如“java.lang.String”,若需要获取该字符串对应的Class对象,则只能使用第一种方式,使用Class的forName(String clazzName)方法获取Class对象时,该方法可能抛出一个ClassNotFoundException异常。一旦获得了某个类所对应的Class对象之后,程序就可以调用Class对象的方法来获得该对象和该类的真实信息了。

4. Class类 API介绍

通过class类我们能够获取大量的信息:

  1. 获取构造函数
  • Connstructor getConstructor(Class<?>... parameterTypes):返回此Class对象对应类的指定public构造器。
  • Constructor<?>[] getConstructors():返回此Class对象对应类的所有public构造器。
  • Constructor getDeclaredConstructor(Class<?>... parameterTypes):返回此Class对象对应类的指定构造器,与构造器的访问权限无关。
  • Constructor<?>[] getDeclaredConstructors():返回此Class对象对应类的所有构造器,与构造器的访问权限无关。
  1. 获取方法
  • Method getDeclaredMethod(String name, Class<?>... parameterTypes):返回此Class对象对应类的指定方法,与方法的访问权限无关。
  • Method[] getDeclaredMethods():返回此Class对象对应类的全部方法,与方法的访问权限无关。
  1. 获取属性
  • Field getField(String name):返回此Class对象对应类的指定public Field。
  • Field[] getFields():返回此Class对象对应类的所有public Field。
  • Field getDeclaredField(String name):返回此Class对象对应类的指定Field,与Field的访问权限无关。
  • Field[] getDeclaredFields():返回此Class对象对应类的全部Field,与Field的访问权限无关。
  1. 获取Class对应类上所包含的Annotation。
  1. 获取Class对象对应类包含的内部类。
  • Class<?>[] getDeclaredClasses():返回该Class对象对应类里包含的全部内部类。

    如下方法用于访问该Class对象对应类所在的外部类。
  • Class<?> getDeclaringClass():返回该Class对象对应类所在的外部类。

    如下方法用于访问该Class对象对应类所继承的父类、所实现的接口等。
  • Class<?>[] getInterfaces():返回该Class对象对应类所实现的全部接口。
  1. 获取Class对象对应类所继承的父类
  • Class<? super T> getSuperclass():返回该Class对象对应类的超类的Class对象。
  1. 获取Class对象对应类的修饰符、所在包、类名等基本信息。
  • int getModifiers():返回此类或接口的所有修饰符。修饰符由public、protected、private、final、static、abstract等对应的常量组成,返回的整数应使用Modifier工具类的方法来解码,才可以获取真实的修饰符。
  • Package getPackage():获取此类的包。
  • String getName():以字符串形式返回此Class对象所表示的类的名称。
  • String getSimpleName():以字符串形式返回此Class对象所表示的类的简称。
  1. 判断该类是否为接口、枚举、注释类型等
  • boolean isAnnotation():返回此Class对象是否表示一个注释类型(由@interface定义)。
  • boolean isAnnotationPresent(Class<? extends Annotation> annotationClass):判断此Class对象是否使用了Annotation注释修饰。
  • boolean isAnonymousClass():返回此Class对象是否是一个匿名类。
  • boolean isArray():返回此Class对象是否表示一个数组类。
  • boolean isEnum():返回此Class对象是否表示一个枚举(由enum关键字定义)。
  • boolean isInterface():返回此Class对象是否表示一个接口(使用interface定义)。
  • boolean isInstance(Object obj):判断obj是否是此Class对象的实例,该方法可以完全代替instanceof操作符。

上面的多个getMethod()方法和getConstructor()方法中,都需要传入多个类型为Class<?>的参数,用于获取指定的方法或指定的构造器。关于这个参数的作用,假设某个类内包含如下3个info方法签名:

  • public void info()
  • public void info(String str)
  • public void info(String str , Integer num)

这3个同名方法属于重载,它们的方法名相同,但参数列表不同。在Java语言中要确定一个方法光有方法名是不行的,例如,我们指定info方法——实际上可以是上面3个方法中的任意一个!如果需要确定一个方法,则应该由方法名和形参列表来确定,但形参名没有任何实际意义,所以只能由形参类型来确定。例如,我们想要确定第二个info方法,则必须指定方法名为info,形参列表为String.class——因此在程序中获取该方法使用如下代码:

clazz.getMethod("info",String.class);

使用反射生成对象

  1. 利用构造函数生成对象
  • 使用Class对象的newInstance()方法来创建该Class对象对应类的实例,这种方式要求该Class对象的对应类有默认构造器,而执行newInstance()方法时实际上是利用默认构造器来创建该类的实例。
  • 先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建该Class对象对应类的实例。通过这种方式可以选择使用指定的构造器来创建实例。
Constructor c = clazz.getConstructor(String.class);
c.newInstance("xx");

调用方法

Method setProName = aClass.getDeclaredMethod("setProName",String.class);
setProName.setAccessible(true);
etProName.invoke(product,"我是一个产品");

操作属性

Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println("fieldName:"+declaredField.getName()+" filedType:"+declaredField.getType());
}
Field proName = aClass.getDeclaredField("proName");
proName.setAccessible(true);
proName.set(product,"我是一个产品");
System.out.println("修稿属性:"+product);

操作数组

//使用反射动态地创建数组
//创建一个元素类型为String,长度为3的数组
Object arr = Array.newInstance(String.class, 3);
//依次为arr数组中index为0,1,2的元素赋值
Array.set(arr, 0, "荣耀盒子");
Array.set(arr, 1, "荣耀8手机");
Array.set(arr, 2, "华为mate9保时捷版");
Object o1= Array.get(arr, 0);
Object o2= Array.get(arr, 1);
Object o3= Array.get(arr, 2);
System.out.println(o1);
System.out.println(o2);
System.out.println(o3);

5. 使用Demo

public class ReflectDemo {
public static void main(String[] args) throws Exception {
Class<Product> aClass = Product.class;
Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor.getName());
}
Constructor<Product> constructor = aClass.getConstructor(int.class, String.class);
Product product = constructor.newInstance(10, "ds");
System.out.println("创建对象:"+product); //获取方法并调用
Method setProName = aClass.getDeclaredMethod("setProName", String.class);
setProName.setAccessible(true);
setProName.invoke(product,"我是一个产品");
System.out.println("调用方法:"+product); //获取属性,并设置属性的值
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println("fieldName:"+declaredField.getName()+" filedType:"+declaredField.getType());
}
Field proName = aClass.getDeclaredField("proName");
proName.setAccessible(true);
proName.set(product,"我是一个产品");
System.out.println("修稿属性:"+product); //使用反射动态地创建数组
//创建一个元素类型为String,长度为3的数组
Object arr = Array.newInstance(String.class, 3);
//依次为arr数组中index为0,1,2的元素赋值
Array.set(arr, 0, "荣耀盒子");
Array.set(arr, 1, "荣耀8手机");
Array.set(arr, 2, "华为mate9保时捷版");
Object o1= Array.get(arr, 0);
Object o2= Array.get(arr, 1);
Object o3= Array.get(arr, 2);
System.out.println(o1);
System.out.println(o2);
System.out.println(o3); System.out.println("end...");
}
}

Java 反射简介的更多相关文章

  1. Java反射简介

    Java反射简介 1.Class类 1) 在面向对象的世界里,万事万物皆对象.(java语言中,静态的成员.普通数据类型除外) 类是不是对象呢?类是(哪个类的对象呢?)谁的对象呢? 类是对象,类是ja ...

  2. Java 反射简介(转载)

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

  3. 理解Java反射机制

    理解Java反射机制 转载请注明出处,谢谢! 一.Java反射简介 什么是反射? Java的反射机制是Java特性之一,反射机制是构建框架技术的基础所在.灵活掌握Java反射机制,对学习框架技术有很大 ...

  4. java反射机制梳理

    java反射机制梳理 Java反射简介 反射简介 编译和运行 编译时刻加载类是静态加载类.运行时刻加载类是动态加载类 要让Java程序能够运行,那么就得让Java类要被Java虚拟机加载.Java类如 ...

  5. Java 反射(一)反射简介、原理和应用场景

    目录 一.动态语言和动态语言的比较 动态语言 静态语言 二.反射 简介 反射的常见使用 1. 代码编辑器 2. Spring等框架的IoC容器 3. 和注解的配合使用 原理 反射优缺点 调试查看 Cl ...

  6. java反射机制简介

    1.字节码.所谓的字节码就是当java虚拟机加载某个类的对象时,首先需要将硬盘中该类的源代码编译成class文件的二进制代码(字节码),然后将class文件的字节码加载到内存中,之后再创建该类的对象 ...

  7. 11.Java反射机制 哦对了,前面的序号来自随笔关于编程之路的思索第一篇

    基本概念 在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法? 答案是肯定的. 这种动态获取类的信息以及动态调用对象的方法的功能来自于J ...

  8. 转!!java反射机制

    Java 反射机制 基本概念 在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法? 答案是肯定的. 这种动态获取类的信息以及动态调用对象 ...

  9. Java 反射 设计模式 动态代理机制详解 [ 转载 ]

    Java 反射 设计模式 动态代理机制详解 [ 转载 ] @author 亦山 原文链接:http://blog.csdn.net/luanlouis/article/details/24589193 ...

随机推荐

  1. SpringBoot—单元测试模板(controller层和service层)

    介绍 概述   在开发过程中,我们经常会一股脑的写各种业务逻辑,经常等全部大功告成的时候,打个jar包放环境里跑跑看看能不能通,殊不知在各个业务方法中已经漏洞百出,修复一个打一个包,再继续修复,这种效 ...

  2. C#/WPF/WinForm/.NET程序代码实现软件程序开机自动启动的两种常用方法的示例与源码下载带详细注释-源码代码-注册表方式-启动目录快捷方式

    C#/WPF/WinForm/.NET程序代码实现软件程序开机自动启动的两种常用方法的示例与源码下载带详细注释-源码代码-注册表方式-启动目录快捷方式 C#实现自动启动的方法-两种方法 源码下载地址: ...

  3. Java实现 LeetCode 697 数组的度(类似于数组的map)

    697. 数组的度 给定一个非空且只包含非负数的整数数组 nums, 数组的度的定义是指数组里任一元素出现频数的最大值. 你的任务是找到与 nums 拥有相同大小的度的最短连续子数组,返回其长度. 示 ...

  4. Java实现蓝桥杯 历届试题 k倍区间

    历届试题 k倍区间 时间限制:2.0s 内存限制:256.0MB 问题描述 给定一个长度为N的数列,A1, A2, - AN,如果其中一段连续的子序列Ai, Ai+1, - Aj(i <= j) ...

  5. Java实现 蓝桥杯 历届试题 小数第n位

    历届试题 小数第n位 时间限制:1.0s 内存限制:256.0MB 问题描述 我们知道,整数做除法时,有时得到有限小数,有时得到无限循环小数. 如果我们把有限小数的末尾加上无限多个0,它们就有了统一的 ...

  6. Java实现 LeetCode 313 超级丑数

    313. 超级丑数 编写一段程序来查找第 n 个超级丑数. 超级丑数是指其所有质因数都是长度为 k 的质数列表 primes 中的正整数. 示例: 输入: n = 12, primes = [2,7, ...

  7. Java实现 洛谷 P1008 三连击

    public class Main { public static void main(String[] args){ for(int i = 123; i <= 329; i++){ int[ ...

  8. java实现文件管理

    ** 文件管理** 显示"DaSai"目录下以"Ex"开头的文件和目录,写了如下代码,请完善之: import java.io.*; class JavaFil ...

  9. java实现第五届蓝桥杯武功秘籍

    武功秘籍 小明到X山洞探险,捡到一本有破损的武功秘籍(2000多页!当然是伪造的).他注意到:书的第10页和第11页在同一张纸上,但第11页和第12页不在同一张纸上. 小明只想练习该书的第81页到第9 ...

  10. 四、归并排序 && 快速排序

    一.归并排序 Merge Sort 1.1.实现原理 如果要排序一个数组,我们先把数组从中间分成前后两部分,然后对前后两部分分别排序,再将排好序的两部分合并在一起,这样整个数组就都有序了. 归并排序使 ...