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 ...
随机推荐
- 关于dijkstra的优化 及 多源最短路
先来看这样一道题目 给你N个点,M条双向边,要求求出1号点到其他所有点的距离.其中 2 <= N <= 1e5, 1 <=M <= 1e6. 对于这样的一道题目 我们当然不可 ...
- 牛客小白月赛4 B 博弈论 思维 字符串
链接:https://www.nowcoder.com/acm/contest/134/B来源:牛客网 题目描述 铁子和顺溜在学习了博弈论的sg函数之后,解决了很多很多博弈题,现在他们遇到了一道难题. ...
- poj 1177 Picture(线段树周长并)
题目链接:http://poj.org/problem?id=1177 题意:给你n个矩形问你重叠后外边缘总共多长. 周长并与面积并很像只不过是处理的时候是 增加的周长=abs(上一次的线段的长度 ...
- JS数据结构第六篇 --- 二叉树力扣练习题
1.第226题:翻转二叉树 递归+迭代两种实现方式: /** 反转二叉树 * Definition for a binary tree node. * function TreeNode(val) { ...
- Robot Framework自动化测试
http://blog.csdn.net/wangjingna/article/details/49782001
- 常用logback.xml配置详解
选择logback的理由 ==logback==与==log4j==的简单对比一下: 1.首先,对于同样的代码路径,==logback==使用起来更快. 2.==logback==原生实现了log4j ...
- 基于Selenium+Python的web自动化测试框架
一.什么是Selenium? Selenium是一个基于浏览器的自动化测试工具,它提供了一种跨平台.跨浏览器的端到端的web自动化解决方案.Selenium主要包括三部分:Selenium IDE.S ...
- 每天学会一点点(spring-mvc.xml与web.xml配置文件)
1.spring-mvc.xml中拦截器的使用 首先在springMVC.xml配置如下代码: <!-- 拦截器 --> <mvc:interceptors> <bean ...
- JSP标签介绍
JSP标签也称之为Jsp Action(JSP动作)元素,它用于在Jsp页面中提供业务逻辑功能,避免在JSP页面中直接编写java代码,造成jsp页面难以维护. jsp的常用标签有以下三个 <j ...
- Spring Cloud同步场景分布式事务怎样做?试试Seata
一.概述 在微服务架构下,虽然我们会尽量避免分布式事务,但是只要业务复杂的情况下这是一个绕不开的问题,如何保证业务数据一致性呢?本文主要介绍同步场景下使用Seata的AT模式来解决一致性问题. Sea ...