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


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. Rocket - util - IDPool

    https://mp.weixin.qq.com/s/Pe7FGKzfRufzzYDrl0fQ7g   介绍IDPool的实现.   ​​   1. 基本介绍   实现从ID池中分配和释放ID的功能. ...

  2. 【HIVE】(3)联合查询join、时间戳函数、字符串函数

    数据 t_join1.txt 1,a,1 2,b,2 3,c,4 t_join2.txt 1,2a 2,2b 3,2c 建表.导入: create table t_join1(id int, name ...

  3. Java实现 LeetCode 621 任务调度器(暴力大法)

    621. 任务调度器 给定一个用字符数组表示的 CPU 需要执行的任务列表.其中包含使用大写的 A - Z 字母表示的26 种不同种类的任务.任务可以以任意顺序执行,并且每个任务都可以在 1 个单位时 ...

  4. Java实现 蓝桥杯 算法训练 K好数

    算法训练 K好数 时间限制:1.0s 内存限制:256.0MB 提交此题 锦囊1 锦囊2 问题描述 如果一个自然数N的K进制表示中任意的相邻的两位都不是相邻的数字,那么我们就说这个数是K好数.求L位K ...

  5. Java实现 蓝桥杯 算法提高 计算行列式

    试题 算法提高 计算行列式 资源限制 时间限制:1.0s 内存限制:256.0MB 问题描述 //据说很多人的题目会有一大堆废话,本傻×就不在这里废话了. 给定一个N×N的矩阵A,求|A|. 输入格式 ...

  6. java实现 洛谷 P1540 机器

    import java.util.LinkedList; import java.util.Scanner; public class Main { private static Scanner ci ...

  7. java实现第五届蓝桥杯神奇算式

    神奇算式 题目描述 由4个不同的数字,组成的一个乘法算式,它们的乘积仍然由这4个数字组成. 比如: 210 x 6 = 1260 8 x 473 = 3784 27 x 81 = 2187 都符合要求 ...

  8. PAT 组个最小数

    给定数字 0-9 各若干个.你可以以任意顺序排列这些数字,但必须全部使用.目标是使得最后得到的数尽可能小(注意 0 不能做首位).例如:给定两个 0,两个 1,三个 5,一个 8,我们得到的最小的数就 ...

  9. Tomcat线程模型分析及源码解读

    1 四种线程模型  配置方法:在tomcat conf 下找到server.xml,在<Connector port="8080" protocol="HTTP/1 ...

  10. linux系统判断内存是否达到瓶颈的小技巧

    1.linux下最常用的系统状态监控工具top 工具,可以使用top -c 来进行查看当前内存的占用情况 free 为内存的剩余状态,当前为3.8G的空闲内存,总的物理内存是8G,按键 shift+m ...