Java的RTTI和反射机制代码分析
发布时间 - 2026-01-11 03:19:38 点击率:次RTTI,即Run-Time Type Identification,运行时类型识别。运行时类型识别是Java中非常有用的机制,在Java运行时,RTTI维护类的相关信息。RTTI能在运行时就能够自动识别每个编译时已知的类型。

很多时候需要进行向上转型,比如Base类派生出Derived类,但是现有的方法只需要将Base对象作为参数,实际传入的则是其派生类的引用。那么RTTI就在此时起到了作用,比如通过RTTI能识别出Derive类是Base的派生类,这样就能够向上转型为Derived。类似的,在用接口作为参数时,向上转型更为常用,RTTI此时能够判断是否可以进行向上转型。
而这些类型信息是通过Class对象(java.lang.Class)的特殊对象完成的,它包含跟类相关的信息。每当编写并编译一个类时就会产生一个.class文件,保存着Class对象,运行这个程序的Java虚拟机(JVM)将使用被称为类加载器(Class Loader)的子系统。而类加载器并非在程序运行之前就加载所有的Class对象,如果尚未加载,默认的类加载器就会根据类名查找.class文件(例如,某个附加类加载器可能会在数据库中查找字节码),在这个类的字节码被加载时接受验证,以确保没有被破坏并且不包含不良Java代码。这也是Java中的类型安全机制之一。一旦某个类的Class对象被载入内存,就可以创建该类的所有对象。
package typeinfo;
class Base {
static { System.out.println("加载Base类"); }
}
class Derived extends Base {
static { System.out.println("加载Derived类");}
}
public class Test {
static void printerInfo(Class c) {
System.out.println("类名: " + c.getName() +
"是否接口? [" + c.isInterface() + "]");
}
public static void main(String[] args) {
Class c = null;
try {
c = Class.forName("typeinfo.Derived");
} catch (ClassNotFoundException e) {
System.out.println("找不到Base类");
System.exit(1);
}
printerInfo(c);
Class up = c.getSuperclass(); // 取得c对象的基类
Object obj = null;
try {
obj = up.newInstance();
} catch (InstantiationException e) {
System.out.println("不能实例化");
System.exit(1);
} catch (IllegalAccessException e) {
System.out.println("不能访问");
System.exit(1);
}
printerInfo(obj.getClass());
} /* 输出:
加载Base类
加载Derived类
类名: typeinfo.Derived是否接口? [false]
类名: typeinfo.Base是否接口? [false]
*/
}
上述代码中,forName方法是静态方法,参数是类名,用来查找是否存在该类,如果找到则返回一个Class引用,否则会抛出ClassNotFoundException异常。
如果类不是在默认文件夹下,而是在某个包下,前面的包名需要带上,比如这里的typeinfo.Derived。
可以通过getSuperclass方法来返回基类对应的Class对象。使用newInstance方法可以按默认构造创建一个实例对象,在不能实例化和不能访问时分别抛出。会抛出InstantiationException和IllegalAccessException异常。
Java还提供了一种方法来生成对Class对象的引用,即类字面常量。对上述程序来说,up等价于Base.class。
对于基本数据类型的包装类来说,char.class等价于Character.TYPE,int.class等价于Integer.TYPE。其余的ab.class等价于Ab.TYPE。(比如void.class等价于Void.TYP)。另外,Java SE5开始int.class和Integer.class也是一回事。
泛化的Class引用,见下面代码
Class intClass = int.class;
Class<Integer> genericIntClass = int.class;
genericIntClass = Integer.class; // 等价
intClass = double.class; // ok
// genericIntClass = double.class; // Illegal!
Class<Integer>对象的引用指定了Integer对象,所以不能将引用指向double.class。为了放松限制可以使用通配符?,即Class<?>,效果跟Class是一样的,但是代码更为优雅,使用Class<?>表示你并非是碰巧或疏忽才使用一个非具体的类引用。同时,可以限制继承的类,示例如下
class Base {}
class Derived extends Base {}
class Base2 {}
public class Test {
public static void main(String[] args) {
Class<? extends Base> cc = Derived.class; // ok
// cc = Base2.class; // Illegal
}
}
向Class引用添加泛型语法的原因仅仅是为了提供编译期类型检查,以便在编译时就能发现类型错误。
总结下来,我们已知的RTTI形式包括:
1、传统的类型转换,由RTTI保证类型转换的正确性,如果执行一个错误的类型转换,就会抛出ClassCastException异常;
2、代表对象的类型的Class对象,通过查询Class对象(即调用Class类的方法)可以获取运行时所需的信息。
在C++中经典的类型转换并不使用RTTI,这点具体见C++的RTTI部分。(说句题外话,以前学C++时看到RTTI这章只是随便扫了眼,现在才记起来dynamic_cast什么的都是为了类型安全而特地添加的,C++在安全方面可以提供选择性,就像Java的StringBuilder和StringBuffer,安全和效率不可兼得?而Java在类型安全上则更为强制,就像表达式x = 1不能被隐式转型为boolean类型)。
而Java中RTTI还有第3种形式,就是关键字instanceof,返回一个布尔值,告诉对象是不是某个特定类型的示例,见下列代码。
class Base {}
class Derived extends Base {}
public class Test {
public static void main(String[] args) {
Derived derived = new Derived();
System.out.println(derived instanceof Base); // 输出true
}
}
利用 instanceof 可以判断某些类型,比如基类Shape派生出各种类(Circle、Rectangle等),现在某方法要为所有Circle上色,而输入参数时一堆Shape对象,此时就可以用instandof判断该Shape对象是不是Circle对象。
RTTI可以识别程序空间的所有类,但是有时候需要从磁盘文件或网络文件中读取一串字节码,并且被告知这些字节代表一个类,就需要用到反射机制。
比如在IDE中创建图形化程序时会使用到一些控件,只需要从本地的控件对应class文件中读取即可,然后再主动修改这些控件的属性。(题外话:大概.net组件就是这样的?学C#时总听到反射,但总没感觉用过,前几天做.net项目的同学也跟我说他从来都没用过委托和事件……)
Class类与java.lang.reflect类库一起对反射的概念进行了支持,该类库包含Field、Method和Constructor类(每个类都实现了Member接口),这些类型的对象都是JVM在运行时创建的,用以表示未知类里对应成员。
这样就可以用Constructor创建未知对象,用get()和set()方法读取和修改与Field对象关联的字段,用invoke方法调用与Method对象关联的字段,等等。
// 使用反射展示类的所有方法, 即使方法是在基类中定义的
package typeinfo;
// Print类的print方法等价于System.Out.Println,方便减少代码量
import static xyz.util.Print.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.regex.Pattern;
// {Args: typeinfo.ShowMethods}
public class ShowMethods {
private static String usage =
"usage:\n" +
"ShowMethods qualified.class.name\n" +
"To show all methods in class or:\n" +
"ShowMethods qualified.class.name word\n" +
"To search for methods involving 'word'";
// 去掉类名前面的包名
private static Pattern p = Pattern.compile("\\w+\\.");
public static void main(String[] args) {
if (args.length < 1) {
print(usage);
System.exit(0);
}
int lines = 0;
try {
Class<?> c = Class.forName(args[0]);
// 反射获得对象c所属类的方法
Method[] methods = c.getMethods();
// 反射获得对象c所属类的构造
Constructor[] ctors = c.getConstructors();
if (args.length == 1) {
for (Method method : methods)
print(p.matcher(method.toString()).replaceAll(""));
for (Constructor ctor : ctors)
print(p.matcher(ctor.toString()).replaceAll(""));
}
} catch (ClassNotFoundException e) {
print("No such class: " + e);
}
} /*
public static void main(String[])
public final void wait() throws InterruptedException
public final void wait(long,int) throws InterruptedException
public final native void wait(long) throws InterruptedException
public boolean equals(Object)
public String toString()
public native int hashCode()
public final native Class getClass()
public final native void notify()
public final native void notifyAll()
public ShowMethods()
*/
}
简单来说,反射机制就是识别未知类型的对象。反射常用于动态代理中。举例如下:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
class DynamicProxyHandler implements InvocationHandler {
private Object proxied; // 代理对象
public DynamicProxyHandler(Object proxied) {
// TODO Auto-generated constructor stub
this.proxied = proxied;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
System.out.println("代理类: " + proxy.getClass() + "\n"
+ "代理方法: " + method + "\n"
+ "参数: " + args);
if (args != null)
for (Object arg : args)
System.out.println(" " + arg);
return method.invoke(proxied, args);
}
}
interface Interface { void doSomething(); }
class RealObject implements Interface {
@Override
public void doSomething() {
// TODO Auto-generated method stub
System.out.println("doSomething");
}
}
public class DynamicProxyDemo {
public static void consumer(Interface iface) {
iface.doSomething();
}
public static void main(String[] args) {
RealObject realObject = new RealObject();
// 使用动态代理
Interface proxy = (Interface)Proxy.newProxyInstance(
Interface.class.getClassLoader(),
new Class[] { Interface.class },
new DynamicProxyHandler(realObject));
consumer(proxy);
} /* 输出:
代理类: class $Proxy0
代理方法: public abstract void Interface.doSomething()
参数: null
doSomething
*/
}
代理是基本的设计模式之一,即用代理类为被代理类提供额外的或不同的操作。而动态代理则需要一个类加载器,就像Java实现RTTI时需要类加载器加载类的信息,这样就可以知道类的相关信息。
关键方法是:
Object java.lang.reflect.Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
传入三个参数:代理接口的加载器(通过Class对象的getClassLoader方法获取),代理的方法接口,代理对象
前两个参数很好理解,就是要代理的方法所属的接口对应的Class对象(主语)的加载器和Class对象本身,主要是参数3,要设计一个实现InvocationHandler接口的类,作为代理对象,一般命名以Handler结尾,Handler翻译为处理者,很形象,就是代替原对象进行处理的处理者(即代理),在程序设计中经常被翻译成“句柄”。
这个类通过传入代理对象来构造,比如这里传入的是Object对象。然后必须覆盖invoke方法。
通过最后输出和invoke方法的具体实现可以发现,return method.invoke(proxied, args);是相当于原对象调用该方法(类似C++的回调函数?)
由于有类加载器,所以代理对象可以知道原对象的具体类名、方法、参数,本示例在调用方法前就输出了这些。
实际应用中可能会针对类名而有所选择。比如接口中有好多个类,你可以选择性的对特定的类、方法、参数进行处理
比如 if(proxied instanceof RealObject) {} 或者 if(method.getName.equals("doSomething")) {}
PS:我这个示例没有参数所以没有距离
参考:《Java编程思想》第四版,更多细节见书上第14章
总结
以上就是本文关于Java的RTTI和反射机制代码分析的全部内容,希望对大家有所帮助。
# java
# rtti
# 反射机制
# Java编程反射机制用法入门与实例总结
# Java反射机制实例代码分享
# JAVA反射机制中getClass和class对比分析
# Java反射机制概念、原理与用法总结
# Java 反射机制的实例详解
# 详解java中反射机制(含数组参数)
# 关于Java反射机制 你需要知道的事情
# Java反射机制深入理解
# 浅析Java 反射机制的用途和缺点
# 加载
# 时就
# 就像
# 抛出
# 都是
# 就会
# 是在
# 可以用
# 相关信息
# 前就
# 方法来
# 中非
# 就可以
# 的是
# 类库
# 很好
# 就在
# 派生类
# 在这个
# 你可以
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
Laravel如何配置.env文件管理环境变量_Laravel环境变量使用与安全管理
javascript中的数组方法有哪些_如何利用数组方法简化数据处理
如何在云虚拟主机上快速搭建个人网站?
Firefox Developer Edition开发者版本入口
制作企业网站建设方案,怎样建设一个公司网站?
Laravel如何自定义错误页面(404, 500)?(代码示例)
如何在 Go 中优雅地映射具有动态字段的 JSON 对象到结构体
如何确保FTP站点访问权限与数据传输安全?
怎么制作网站设计模板图片,有电商商品详情页面的免费模板素材网站推荐吗?
Laravel如何使用API Resources格式化JSON响应_Laravel数据资源封装与格式化输出
如何快速搭建虚拟主机网站?新手必看指南
三星网站视频制作教程下载,三星w23网页如何全屏?
laravel服务容器和依赖注入怎么理解_laravel服务容器与依赖注入解析
Laravel Seeder怎么填充数据_Laravel数据库填充器的使用方法与技巧
免费制作统计图的网站有哪些,如何看待现如今年轻人买房难的情况?
Laravel定时任务怎么设置_Laravel Crontab调度器配置
Laravel如何处理JSON字段的查询和更新_Laravel JSON列操作与查询技巧
JS中使用new Date(str)创建时间对象不兼容firefox和ie的解决方法(两种)
大同网页,大同瑞慈医院官网?
香港服务器网站推广:SEO优化与外贸独立站搭建策略
如何用ChatGPT准备面试 模拟面试问答与职场话术练习教程
Laravel怎么实现观察者模式Observer_Laravel模型事件监听与解耦开发【指南】
php json中文编码为null的解决办法
如何确认建站备案号应放置的具体位置?
高配服务器限时抢购:企业级配置与回收服务一站式优惠方案
Python文件流缓冲机制_IO性能解析【教程】
如何基于云服务器快速搭建网站及云盘系统?
如何构建满足综合性能需求的优质建站方案?
Laravel Asset编译怎么配置_Laravel Vite前端构建工具使用
如何使用 jQuery 正确渲染 Instagram 风格的标签列表
今日头条AI怎样推荐抢票工具_今日头条AI抢票工具推荐算法与筛选【技巧】
如何快速搭建高效可靠的建站解决方案?
Laravel Livewire是什么_使用Laravel Livewire构建动态前端界面
免费网站制作appp,免费制作app哪个平台好?
如何在云主机上快速搭建多站点网站?
JS经典正则表达式笔试题汇总
Midjourney怎么调整光影效果_Midjourney光影调整方法【指南】
ChatGPT怎么生成Excel公式_ChatGPT公式生成方法【指南】
Laravel怎么防止CSRF攻击_Laravel CSRF保护中间件原理与实践
Laravel如何实现RSS订阅源功能_Laravel动态生成网站XML格式订阅内容【教程】
阿里云高弹*务器配置方案|支持分布式架构与多节点部署
香港代理服务器配置指南:高匿IP选择、跨境加速与SEO优化技巧
Laravel怎么在Controller之外的地方验证数据
Laravel请求验证怎么写_Laravel Validator自定义表单验证规则教程
如何用VPS主机快速搭建个人网站?
如何在Windows服务器上快速搭建网站?
Laravel怎么返回JSON格式数据_Laravel API资源Response响应格式化【技巧】
Laravel如何实现事件和监听器?(Event & Listener实战)
佐糖AI抠图怎样调整抠图精度_佐糖AI精度调整与放大细化操作【攻略】
利用JavaScript实现拖拽改变元素大小

