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 反射机制的用途和缺点
# 加载
# 时就
# 就像
# 抛出
# 都是
# 就会
# 是在
# 可以用
# 相关信息
# 前就
# 方法来
# 中非
# 就可以
# 的是
# 类库
# 很好
# 就在
# 派生类
# 在这个
# 你可以
相关文章:
实现虚拟支付需哪些建站技术支撑?
制作销售网站教学视频,销售网站有哪些?
已有域名建站全流程解析:网站搭建步骤与建站工具选择
成都网站制作报价公司,成都工业用气开户费用?
建站之星安装需要哪些步骤及注意事项?
如何快速重置建站主机并恢复默认配置?
建站主机系统SEO优化与智能配置核心关键词操作指南
建站之星后台密码遗忘?如何快速找回?
建站之星安装步骤有哪些常见问题?
制作无缝贴图网站有哪些,3dmax无缝贴图怎么调?
香港服务器网站生成指南:免费资源整合与高速稳定配置方案
宝塔面板如何快速创建新站点?
建站主机数据库如何配置才能提升网站性能?
建站之星客服服务时间及联系方式如何?
香港服务器WordPress建站指南:SEO优化与高效部署策略
如何获取免费开源的自助建站系统源码?
如何选择高效稳定的ISP建站解决方案?
无锡制作网站公司有哪些,无锡优八网络科技有限公司介绍?
如何快速生成ASP一键建站模板并优化安全性?
Android使用GridView实现日历的简单功能
建站之星CMS五站合一模板配置与SEO优化指南
建站之星在线版空间:自助建站+智能模板一键生成方案
,怎么在广州志愿者网站注册?
建站之星如何助力网站排名飙升?揭秘高效技巧
宝塔建站助手安装配置与建站模板使用全流程解析
广州建站公司哪家好?十大优质服务商推荐
建站之星2.7模板快速切换与批量管理功能操作指南
如何在阿里云虚拟机上搭建网站?步骤解析与避坑指南
建站之星安全性能如何?防护体系能否抵御黑客入侵?
网站设计制作企业有哪些,抖音官网主页怎么设置?
C++时间戳转换成日期时间的步骤和示例代码
临沂网站制作公司有哪些,临沂第四中学官网?
python的本地网站制作,如何创建本地站点?
制作网站的模板软件,网站怎么建设?
香港服务器租用每月最低只需15元?
黑客如何利用漏洞与弱口令入侵网站服务器?
网站按钮制作软件,如何实现网页中按钮的自动点击?
深圳网站制作培训,深圳哪些招聘网站比较好?
建站之星价格显示格式升级,你的预算足够吗?
企业微网站怎么做,公司网站和公众号有什么区别?
建站之星代理费用多少?最新价格详情介绍
邀请函制作网站有哪些,有没有做年会邀请函的网站啊?在线制作,模板很多的那种?
网站制作大概多少钱一个,做一个平台网站大概多少钱?
无锡营销型网站制作公司,无锡网选车牌流程?
家具网站制作软件,家具厂怎么跑业务?
建站主机选哪种环境更利于SEO优化?
建站之星展会模板:智能建站与自助搭建高效解决方案
香港服务器建站指南:免备案优势与SEO优化技巧全解析
高端建站三要素:定制模板、企业官网与响应式设计优化
小视频制作网站有哪些,有什么看国内小视频的网站,求推荐?
*请认真填写需求信息,我们会在24小时内与您取得联系。