Java 基础篇之反射
反射
使用反射获取程序运行时的对象和类的真实信息。
获取 Class 对象
每个类被加载之后,系统会为该类生成一个对应的 Class 对象,通过该 Class 对象可以访问到 JVM 中的这个类。
使用 Class 类的 forName(String clazzName) 静态方法。字符串参数的值是某个类的全限定类名,必须包含完整的包名
调用某个类的 class 属性
调用某个对象的 getClass() 方法。该方法是 java.lang.Object 类中的一个方法,所有的 Java 对象都可以调用,返回该对象所属类对应的 Class 对象
获取 Class 对象中信息
Class 类提供了大量的实例方法来获取该 class 对象所对应的类的详细信息。更多请参考 API。
import java.lang.reflect.*;
import java.lang.annotation.*;
public class ClassTest {
private ClassTest() {
}
public ClassTest(String name) {
System.out.println("执行有参数的构造器");
}
public void info() {
System.out.println("执行无参数的info方法");
}
public void info(String str) {
System.out.println("执行有参数的info方法" + ",其 str 参数值: " + str);
}
class Inner {
}
public static void main(String[] args) throws Exception {
Class<ClassTest> clazz = ClassTest.class;
// 获取 clazz 对象所对应类的全部构造器
Constructor<?>[] ctros = clazz.getDeclaredConstructors();
System.out.println("ClassTest 的全部构造器如下: ");
for (Constructor c : ctros) {
System.out.println(c);
}
// 获取 clazz 对象所对应类的全部 public 构造器
Constructor<?>[] publicCtors = clazz.getConstructors();
System.out.println("ClassTest的全部public构造器如下:");
for (Constructor c : publicCtors) {
System.out.println(c);
}
// 获取 clazz 对象所对应类的全部 public 方法
Method[] mtds = clazz.getMethods();
System.out.println("ClassTest 的全部 public 方法如下: ");
for (Method md : mtds) {
System.out.println(md);
}
// 获取 clazz 对象所对应类的指定方法
System.out.println("ClassTest 里带一个字符串参数的 info 方法为:" + clazz.getMethod("info", String.class));
// 获取 clazz 对象所对应类的全部注解
Annotation[] anns = clazz.getAnnotations();
System.out.println("ClassTest 的全部 Annotation 如下: ");
for (Annotation an : anns) {
System.out.println(an);
}
// 获取 clazz 对象所对应类的全部内部类
Class<?>[] inners = clazz.getDeclaredClasses();
System.out.println("ClassTest 的全部内部类如下: ");
for (Class c : inners) {
System.out.println(c);
}
// 使用 Class.forName() 方法加载 ClassTest 的 Inner 内部类
Class inClazz = Class.forName("ClassTest$Inner");
// 访问该类所在的外部类
System.out.println("inClazz 对应类的外部类为: " + inClazz.getDeclaringClass());
System.out.println("ClassTest 的包为:" + clazz.getPackage());
System.out.println("ClassTest 的父类为:" + clazz.getSuperclass());
}
}
应用
Class 对象可以获得对应类的方法(由 Method 表示)、构造器(由 Constructor 表示)、成员变量(由 Field 对象表示),且这个三个类都实现了 java.lang.reflect.Member 接口。程序可以通过 Method 对象来执行对应的方法,通过 Constructor 对象来调用对应的构造器创建实例,通过 Field 对象直接访问并修改对象的成员变量值。
创建对象
使用 Class 对象的 newInstance() 方法来创建 Class 对象对应类的实例。要求该 Class 对象的对应类有默认构造器
先使用 Class 对象获取指定的 Constructor 对象,再调用 Constructor 对象的 newInstance() 方法来创建该 Class 对象对应类的实例。这种方式可以选择使用指定的构造器来创建实例
方式一
实现了一个简单的对象池,该对象池会根据配置文件读取 key-value 对,然后创建这些对象并放入 HashMap 中
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public class ObjectPoolFactory {
private Map<String, Object> objectPool = new HashMap<>();
private Object createObject(String clazzName) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
Class<?> clazz = Class.forName(clazzName);
// 使用 Class 对象对应的类的默认构造器
return clazz.newInstance();
}
public void initPool(String fileName) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
try (
FileInputStream fis = new FileInputStream(fileName)
) {
Properties props = new Properties();
props.load(fis);
for ( String name: props.stringPropertyNames()) {
objectPool.put(name, createObject(props.getProperty(name)));
}
} catch (IOException ex) {
System.out.println("读取" + fileName + "异常");
}
}
public Object getObject(String name) {
return objectPool.get(name);
}
public static void main(String[] args) throws Exception{
ObjectPoolFactory pf = new ObjectPoolFactory();
pf.initPool("obj.txt");
System.out.println(pf.getObject("a"));
System.out.println(pf.getObject("b"));
}
}
/*
obj.txt 内容:
a=java.util.Date
b=javax.swing.JFrame
*/
方式二
import java.lang.reflect.Constructor;
public class CreateJFrame {
public static void main(String[] args) throws Exception {
Class<?> jframeClazz = Class.forName("javax.swing.JFrame");
// 选择使用指定的构造器
Constructor ctor = jframeClazz.getConstructor(String.class);
Object obj = ctor.newInstance("测试窗口");
System.out.println(obj);
}
}
调用方法
每个 Method 对象对应一个方法,获得 Method 对象后,就可以通过该 Method 来调用它对应的方法。
Method 包含一个 invoke() 方法,该方法的签名如下:
- Object invoke(Object obj, Object... args):该方法中的 obj 是执行方法的主调(即类的实例对象),后面的 args 是执行该方法的实参
下面是对之前的对象工厂池进行增强,允许在配置文件中增加配置对象的成员变量值,对象池工厂会读取该对象配置的成员变量值,并利用该对象对应的 setter 方法设置成员变量的值:
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public class ExtendedObjectPoolFactory {
// 定义一个对象池,前面是对象名,后面是实际的对象
private Map<String, Object> objectPool = new HashMap<>();
private Properties config = new Properties();
// 从指定文件中初始化 Properties 对象
public void init(String fileName) {
try (
FileInputStream fis = new FileInputStream(fileName);
) {
config.load(fis);
} catch (IOException ex) {
System.out.println("读取" + fileName + "异常");
}
}
// 定义创建对象的方法
private Object createObject(String clazzName) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
// 根据字符串来获取对应的 Class 对象
Class<?> clazz = Class.forName(clazzName);
// 使用 clazz 对应类的默认构造器创建实例
return clazz.newInstance();
}
// 初始化对象池
public void initPool() throws InstantiationException, IllegalAccessException, ClassNotFoundException {
for (String name : config.stringPropertyNames()) {
if (!name.contains("%")) {
objectPool.put(name, createObject(config.getProperty(name)));
}
}
}
// 根据属性文件来调用指定对象的 setter 方法
public void initProperty() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
for (String name : config.stringPropertyNames()) {
if (name.contains("%")) {
String[] objAndProp = name.split("%");
Object target = getObject(objAndProp[0]);
String mtdName = "set" + objAndProp[1].substring(1);
// 通过 target 的 getClass() 获取它的实现类所对应的 Class 对象
Class<?> targetClass = target.getClass();
// 获取希望调用的 setter 方法
Method mtd = targetClass.getMethod(mtdName, String.class);
// 通过 Method 的 invoke 方法执行 setter 方法
mtd.invoke(target, config.getProperty(name));
}
}
}
public Object getObject(String name) {
// 从 objectPool 中取出指定 name 对应的对象
return objectPool.get(name);
}
public static void main(String[] args) throws Exception {
ExtendedObjectPoolFactory epf = new ExtendedObjectPoolFactory();
epf.init("extObj.txt");
epf.initPool();
epf.initProperty();
System.out.println(epf.getObject("a"));
}
}
/* extObj.txt 内容
a=java.util.Date
b=javax.swing.JFrame
# set the title of a
a%title=Test Title
*/
PS:当通过 Method 的 invoke() 方法来调用对应的方法时,Java 会要求程序必须有调用该方法的权限。如果需要调用某个对象的 private 方法,则可以先调用 Method 对象的如下方法:
- setAccessible(boolean flag):值为 true,表示该 Method 在使用时取消访问权限检查
访问成员变量值
Filed 提供如下两组方法来读取或设置成员变量值:
getXxx(Object obj):获取 obj 对象的该成员变量的值。此处的 Xxx 对应 8 中基本类型。如果成员变量的类型是引用类型,则直接使用 get
setXxx(Object obj, Xxx val):将 obj 对象的成员变量值设为 val 值。此处的 Xxx 对应 8 中基本类型。如果成员变量的类型是引用类型,则直接使用 set
public class Person {
private String name;
private int age;
public String toString() {
return "Person[name:" + name + ", age:" + age + "]";
}
}
import java.lang.reflect.Field;
public class FieldTest {
public static void main(String[] args) throws Exception {
Person p = new Person();
Class<Person> personClazz = Person.class;
Field nameField = personClazz.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(p, "crazy");
Field ageField = personClazz.getDeclaredField("age");
ageField.setAccessible(true);
ageField.setInt(p, 30);
System.out.println(p);
}
}
泛型在反射中的应用
在反射中使用泛型,反射生成的对象就不需要进行强制类型转换。
import java.util.Date;
public class CrazyitObjectFactory {
public static <T> T getinstance(Class<T> cls) {
try {
return cls.newInstance();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static void main(String[] args) {
// 获取实例后无需进行类型转换
Date d = CrazyitObjectFactory.getinstance(Date.class);
}
}
欢迎关注我的公众号

Java 基础篇之反射的更多相关文章
- 小白—职场之Java基础篇
java基础篇 java基础 目录 1.java是一种什么语言,jdk,jre,jvm三者的区别 2.java 1.5之后的三大版本 3.java跨平台及其原理 4.java 语言的特点 5.什么是字 ...
- Java基础篇(JVM)——类加载机制
这是Java基础篇(JVM)的第二篇文章,紧接着上一篇字节码详解,这篇我们来详解Java的类加载机制,也就是如何把字节码代表的类信息加载进入内存中. 我们知道,不管是根据类新建对象,还是直接使用类变量 ...
- java基础篇---I/O技术
java基础篇---I/O技术 对于任何程序设计语言而言,输入输出(I/O)系统都是比较复杂的而且还是比较核心的.在java.io.包中提供了相关的API. java中流的概念划分 流的方向: 输 ...
- 金三银四跳槽季,BAT美团滴滴java面试大纲(带答案版)之一:Java基础篇
Java基础篇: 题记:本系列文章,会尽量模拟面试现场对话情景, 用口语而非书面语 ,采用问答形式来展现.另外每一个问题都附上“延伸”,这部分内容是帮助小伙伴们更深的理解一些底层细节的补充,在面试中可 ...
- Java提升篇之反射的原理
Java提升篇之反射的原理 1.构造方法的反射 import java.lang.reflect.Constructor; public class ReflectConstructor { publ ...
- java基础篇---HTTP协议
java基础篇---HTTP协议 HTTP协议一直是自己的薄弱点,也没抽太多时间去看这方面的内容,今天兴致来了就在网上搜了下关于http协议,发现有园友写了一篇非常好的博文,博文地址:(http: ...
- 黑马程序猿————Java基础日常笔记---反射与正則表達式
------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 黑马程序猿----Java基础日常笔记---反射与正則表達式 1.1反射 反射的理解和作用: 首 ...
- java基础篇---I/O技术(三)
接上一篇java基础篇---I/O技术(二) Java对象的序列化和反序列化 什么叫对象的序列化和反序列化 要想完成对象的输入或输出,还必须依靠对象输出流(ObjectOutputStream)和对象 ...
- Java基础13:反射与注解详解
Java基础13:反射与注解详解 什么是反射? 反射(Reflection)是Java 程序开发语言的特征之一,它允许运行中的 Java 程序获取自身的信息,并且可以操作类或对象的内部属性. Orac ...
随机推荐
- 2018宁夏邀请赛I题 bubble sort(思维题
https://vjudge.net/problem/Gym-102222I 居然补到个防ak,刚开始不知道啥是循环左移右移(只能移一次),不好想.. 题意:以冒泡排序为背景 给你n,k 问在1~n的 ...
- 牛客-长沙理工校赛C-取手机
传送门:https://www.nowcoder.com/acm/contest/96/C 参考:http://www.cnblogs.com/Dillonh/p/8835074.html 题意: d ...
- HDU 4289 Control 最小割
Control 题意:有一个犯罪集团要贩卖大规模杀伤武器,从s城运输到t城,现在你是一个特殊部门的长官,可以在城市中布置眼线,但是布施眼线需要花钱,现在问至少要花费多少能使得你及时阻止他们的运输. 题 ...
- 树状数组求区间和模板 区间可修改 参考题目:牛客小白月赛 I 区间
从前有个东西叫树状数组,它可以轻易实现一些简单的序列操作,比如单点修改,区间求和;区间修改,单点求值等. 但是我们经常需要更高级的操作,比如区间修改区间查询.这时候树状数组就不起作用了,只能选择写一个 ...
- 洛谷 P4206 [NOI2005]聪聪与可可 题解
题面 输入 数据的第1行为两个整数N和E,以空格分隔,分别表示森林中的景点数和连接相邻景点的路的条数. 第2行包含两个整数C和M,以空格分隔,分别表示初始时聪聪和可可所在的景点的编号. 接下来E行,每 ...
- SSH项目,JSP项目,SSM项目源码附带环境安装指导视频教程
基于java开发的一个局域网端口扫描程序下载地址:https://www.icodedock.com/article/6.html 用JAVA实现对JAVA代码的格式化下载地址:https://www ...
- 告别组件之教你使用原生js和css写移动端轮播图
在工作中由于项目需要要写一个轮播图,本想使用组件直接调用实现快速开发,但是一想到自己经常使用组件但是让自己手写的话确实一点都不会. 一个不会手写组件的前端程序员不是一个好程序员!于是打算自己手写一个. ...
- 【Offer】[17] 【打印1到最大的n位数】
题目描述 思路分析 测试用例 Java代码 代码链接 题目描述 输入数字n,按顺序打印出从1最大的n位十进制数.比如输入3,则打印出1.2.3一直到最大的3位数即999. 思路分析 要考虑到大数问题, ...
- div标签嵌套原则详解(转载)
这个也许平时人们不注意,但是非常有用,尤其是当你实在找不到原因为什么网页显示错误的时候. XHTML 的标签有许多:div.ul.li.dl.dt.dd.h1~h6.p.a.addressa.span ...
- Yum未完成事务问题
1.安装 yum-complete-transaction [root@linux-node1 ~]# yum -y install yum-utils 2.清除yum缓存 [root@linux-n ...