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 ...
随机推荐
- Web安全开发规范手册V1.0
一.背景 团队最近频繁遭受网络攻击,引起了部门技术负责人的重视,笔者在团队中相对来说更懂安全,因此花了点时间编辑了一份安全开发自检清单,觉得应该也有不少读者有需要,所以将其分享出来. 二.自检清单 检 ...
- 剑指Offer(二十八):数组中出现次数超过一半的数字
剑指Offer(二十八):数组中出现次数超过一半的数字 搜索微信公众号:'AI-ming3526'或者'计算机视觉这件小事' 获取更多算法.机器学习干货 csdn:https://blog.csdn. ...
- 【入门】广电行业DNS、DHCP解决方案详解(三)——DNS部署架构及案
[入门]广电行业DNS.DHCP解决方案详解(三)——DNS部署架构及案 DNS系统部署架构 宽带业务DNS架构 互动业务DNS架构 案例介绍 案例一 案例二 本篇我们将先介绍DNS系统部署架构体系, ...
- Node.js+Navicat for MySQL实现的简单增删查改
前提准备: 电脑上必须装有服务器环境,Navicat for MySQL(我用的是这款MySQL,可随意),Node环境 效果如图所示: 源码地址: GitHub:https://github.com ...
- ES6-数组的新方法
1.Array.of() 方法创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型. Array.of() 和 Array 构造函数之间的区别在于处理整数参数:Array.of(7)创建一个 ...
- CentOS7 小技巧总结
1.CentOS7 解决无法使用tab自动补全 原因:CentOS在最小化安装时,没有安装自动补全的包,需要手动安装. yum -y install bash-completion 安装好后,重新登陆 ...
- NOIP2009 1.多项式输出
题目: 其中,aixi称为 i 次项,ai 称为 i 次项的系数.给出一个一元多项式各项的次数和系数,请按照如下规定的格式要求输出该多项式: 1. 多项式中自变量为 x,从左到右按照次数递减顺序给出多 ...
- STL容器(Stack, Queue, List, Vector, Deque, Priority_Queue, Map, Pair, Set, Multiset, Multimap)
一.Stack(栈) 这个没啥好说的,就是后进先出的一个容器. 基本操作有: stack<int>q; q.push(); //入栈 q.pop(); //出栈 q.top(); //返回 ...
- WebDriver 将浏览器窗口最大化
package com.entrym.main; import java.io.File; import java.io.IOException; import org.openqa.selenium ...
- gh-ost 原理剖析
gh-ost 原理 一 简介 上一篇文章介绍 gh-ost 参数和具体的使用方法,以及核心特性-可动态调整 暂停,动态修改参数等等.本文分几部分从源码方面解释gh-ost的执行过程,数据迁移,切换细节 ...