- 浏览: 3052758 次
- 性别:
- 来自: 海外
文章分类
- 全部博客 (430)
- Programming Languages (23)
- Compiler (20)
- Virtual Machine (57)
- Garbage Collection (4)
- HotSpot VM (26)
- Mono (2)
- SSCLI Rotor (1)
- Harmony (0)
- DLR (19)
- Ruby (28)
- C# (38)
- F# (3)
- Haskell (0)
- Scheme (1)
- Regular Expression (5)
- Python (4)
- ECMAScript (2)
- JavaScript (18)
- ActionScript (7)
- Squirrel (2)
- C (6)
- C++ (10)
- D (2)
- .NET (13)
- Java (86)
- Scala (1)
- Groovy (3)
- Optimization (6)
- Data Structure and Algorithm (3)
- Books (4)
- WPF (1)
- Game Engines (7)
- 吉里吉里 (12)
- UML (1)
- Reverse Engineering (11)
- NSIS (4)
- Utilities (3)
- Design Patterns (1)
- Visual Studio (9)
- Windows 7 (3)
- x86 Assembler (1)
- Android (2)
- School Assignment / Test (6)
- Anti-virus (1)
- REST (1)
- Profiling (1)
- misc (39)
- NetOA (12)
- rant (6)
- anime (5)
- Links (12)
- CLR (7)
- GC (1)
- OpenJDK (2)
- JVM (4)
- KVM (0)
- Rhino (1)
- LINQ (2)
- JScript (0)
- Nashorn (0)
- Dalvik (1)
- DTrace (0)
- LLVM (0)
- MSIL (0)
最新评论
-
mldxs:
虽然很多还是看不懂,写的很好!
虚拟机随谈(一):解释器,树遍历解释器,基于栈与基于寄存器,大杂烩 -
HanyuKing:
Java的多维数组 -
funnyone:
Java 8的default method与method resolution -
ljs_nogard:
Xamarin workbook - .Net Core 中不 ...
LINQ的恶搞…… -
txm119161336:
allocatestlye1 顺序为 // Fields o ...
最近做的两次Java/JVM分享的概要
转自Java获得泛型类型的回复,内容有细微调整。
Java泛型有这么一种规律:
位于声明一侧的,源码里写了什么到运行时就能看到什么;
位于使用一侧的,源码里写什么到运行时都没了。
什么意思呢?“声明一侧”包括泛型类型(泛型类与泛型接口)声明、带有泛型参数的方法和域的声明。注意局部变量的声明不算在内,那个属于“使用”一侧。
上面代码里,带有注释的行里的泛型信息在运行时都还能获取到,原则是源码里写了什么运行时就能得到什么。针对1的GenericClass<T>,运行时通过Class.getTypeParameters()方法得到的数组可以获取那个“T”;同理,2的T、3的java.lang.String与T、4的T与U都可以获得。源码文本里写的是什么运行时就能得到什么;像是T、U等在运行时的实际类型是获取不到的。
这是因为从Java 5开始class文件的格式有了调整,规定这些泛型信息要写到class文件中。以上面的map为例,通过javap来看它的元数据可以看到记录了这样的信息:
乍一看,private java.util.Map map;不正好显示了它的泛型类型被擦除了么?
但仔细看会发现有两个Signature,下面的一个有两字节的数据,0x0A。到常量池找到0x0A对应的项,是:
也就是内容为“Ljava/util/Map<Ljava/lang/String;TT;>;”的一个字符串。
根据Java 5开始的新class文件格式规范,方法与域的描述符增添了对泛型信息的记录,用一对尖括号包围泛型参数,其中普通的引用类型用“La/b/c/D;”的格式记录,未绑定值的泛型变量用“Txxx;”的格式记录,其中xxx就是源码中声明的泛型变量名。类型声明的泛型信息也以类似下面的方式记了下来:
详细信息请参考官方文档:http://java.sun.com/docs/books/jvms/second_edition/ClassFileFormat-Java5.pdf
该文档也将会被整合到JVM规范第三版中。可惜第三版现在只有草案,最终版本什么时候发布还遥遥无期。
相比之下,“使用一侧”的泛型信息则完全没有被保留下来,在Java源码编译到class文件后就确实丢失了。也就是说,在方法体内的泛型局部变量、泛型方法调用之类的泛型信息编译后都消失了。
上面代码中,1留下的痕迹是:main()方法的StackMapTable属性里可以看到:
但这里是没有留下泛型信息的。这段代码只所以写了个空的for循环就是为了迫使javac生成那个StackMapTable,让1多留个影。当整个方法只有一个基本块的时候javac就不会生成StackMapTable属性,就看不到这可爱的数据结构了。
如果main()里用到了list的方法,那么那些方法调用点上也会留下1的痕迹,例如如果调用list.add("");,则会留下“java/util/List.add:(Ljava/lang/Object;)Z”这种记录。
2留下的是“java/util/ArrayList."<init>":()V”,同样也丢失了泛型信息。
由上述讨论可知,想对带有未绑定的泛型变量的泛型类型获取其实际类型是不现实的,因为class文件里根本没记录实际类型的信息。觉得这句话太拗口的话用例子来理解:要想对java.util.List<E>获取E的实际类型是不现实的,因为List.class文件里只记录了E,却没记录使用List<E>时E的实际类型。
想对局部变量等“使用一侧”的已绑定的泛型类型获取其实际类型也不现实,同样是因为class文件中根本没记录这个信息。例子直接看上面讲“使用一侧”的就可以了。
知道了什么信息有记录,什么信息没有记录之后,也就可以省点力气不去纠结“拿不到T的实际类型”、“建不出T类型的数组”、“不能对T类型做instanceof”之类的问题了orz
那种情况在文章中描述的情况以及包括了,正好说明了class文件记录了什么而又没有记录什么——一个非泛型的派生类继承一个泛型基类时,派生类的class文件对基类的记录就包含了已经确定了值了泛型参数,相对泛型基类来说,这里仍然是“声明”一侧,于是源码中泛型信息在文本怎么写的就在class文件里记录了什么。
是的,没有意思,关键是要运行时,非申明式的定义。
获得参数类型是徒劳的,一般我会HardCode去做。
那种情况在文章中描述的情况以及包括了,正好说明了class文件记录了什么而又没有记录什么——一个非泛型的派生类继承一个泛型基类时,派生类的class文件对基类的记录就包含了已经确定了值了泛型参数,相对泛型基类来说,这里仍然是“声明”一侧,于是源码中泛型信息在文本怎么写的就在class文件里记录了什么。
Java泛型有这么一种规律:
位于声明一侧的,源码里写了什么到运行时就能看到什么;
位于使用一侧的,源码里写什么到运行时都没了。
什么意思呢?“声明一侧”包括泛型类型(泛型类与泛型接口)声明、带有泛型参数的方法和域的声明。注意局部变量的声明不算在内,那个属于“使用”一侧。
import java.util.List; import java.util.Map; public class GenericClass<T> { // 1 private List<T> list; // 2 private Map<String, T> map; // 3 public <U> U genericMethod(Map<T, U> m) { // 4 return null; } }
上面代码里,带有注释的行里的泛型信息在运行时都还能获取到,原则是源码里写了什么运行时就能得到什么。针对1的GenericClass<T>,运行时通过Class.getTypeParameters()方法得到的数组可以获取那个“T”;同理,2的T、3的java.lang.String与T、4的T与U都可以获得。源码文本里写的是什么运行时就能得到什么;像是T、U等在运行时的实际类型是获取不到的。
这是因为从Java 5开始class文件的格式有了调整,规定这些泛型信息要写到class文件中。以上面的map为例,通过javap来看它的元数据可以看到记录了这样的信息:
private java.util.Map map; Signature: Ljava/util/Map; Signature: length = 0x2 00 0A
乍一看,private java.util.Map map;不正好显示了它的泛型类型被擦除了么?
但仔细看会发现有两个Signature,下面的一个有两字节的数据,0x0A。到常量池找到0x0A对应的项,是:
const #10 = Asciz Ljava/util/Map<Ljava/lang/String;TT;>;;
也就是内容为“Ljava/util/Map<Ljava/lang/String;TT;>;”的一个字符串。
根据Java 5开始的新class文件格式规范,方法与域的描述符增添了对泛型信息的记录,用一对尖括号包围泛型参数,其中普通的引用类型用“La/b/c/D;”的格式记录,未绑定值的泛型变量用“Txxx;”的格式记录,其中xxx就是源码中声明的泛型变量名。类型声明的泛型信息也以类似下面的方式记了下来:
public class GenericClass extends java.lang.Object Signature: length = 0x2 00 12 // ... const #18 = Asciz <T:Ljava/lang/Object;>Ljava/lang/Object;;
详细信息请参考官方文档:http://java.sun.com/docs/books/jvms/second_edition/ClassFileFormat-Java5.pdf
该文档也将会被整合到JVM规范第三版中。可惜第三版现在只有草案,最终版本什么时候发布还遥遥无期。
相比之下,“使用一侧”的泛型信息则完全没有被保留下来,在Java源码编译到class文件后就确实丢失了。也就是说,在方法体内的泛型局部变量、泛型方法调用之类的泛型信息编译后都消失了。
import java.util.ArrayList; import java.util.List; public class TestClass { public static void main(String[] args) { List<String> list = null; // 1 list = new ArrayList<String>(); // 2 for (int i = 0; i < 10; i++) ; } }
上面代码中,1留下的痕迹是:main()方法的StackMapTable属性里可以看到:
StackMapTable: number_of_entries = 2 frame_type = 253 /* append */ offset_delta = 12 locals = [ class java/util/List, int ] frame_type = 250 /* chop */ offset_delta = 11
但这里是没有留下泛型信息的。这段代码只所以写了个空的for循环就是为了迫使javac生成那个StackMapTable,让1多留个影。当整个方法只有一个基本块的时候javac就不会生成StackMapTable属性,就看不到这可爱的数据结构了。
如果main()里用到了list的方法,那么那些方法调用点上也会留下1的痕迹,例如如果调用list.add("");,则会留下“java/util/List.add:(Ljava/lang/Object;)Z”这种记录。
2留下的是“java/util/ArrayList."<init>":()V”,同样也丢失了泛型信息。
由上述讨论可知,想对带有未绑定的泛型变量的泛型类型获取其实际类型是不现实的,因为class文件里根本没记录实际类型的信息。觉得这句话太拗口的话用例子来理解:要想对java.util.List<E>获取E的实际类型是不现实的,因为List.class文件里只记录了E,却没记录使用List<E>时E的实际类型。
想对局部变量等“使用一侧”的已绑定的泛型类型获取其实际类型也不现实,同样是因为class文件中根本没记录这个信息。例子直接看上面讲“使用一侧”的就可以了。
知道了什么信息有记录,什么信息没有记录之后,也就可以省点力气不去纠结“拿不到T的实际类型”、“建不出T类型的数组”、“不能对T类型做instanceof”之类的问题了orz
评论
4 楼
0704681032
2010-12-07
呵呵 学习了啊!楼主大牛啊!!关键是class字节码什么记录下来,什么没记录!
然后根据这些原则
位于声明一侧的,源码里写了什么到运行时就能看到什么;
位于使用一侧的,源码里写什么到运行时都没了。
要好好学习了呀 感觉学习的道路很漫长啊
然后根据这些原则
位于声明一侧的,源码里写了什么到运行时就能看到什么;
位于使用一侧的,源码里写什么到运行时都没了。
要好好学习了呀 感觉学习的道路很漫长啊
3 楼
mercyblitz
2010-06-30
RednaxelaFX 写道
sohuexe 写道
也不是完全没有办法,最典型的就是《java Persistence with hibernate》 的例子可以参照http://community.jboss.org/wiki/GenericDataAccessObjects。
那种情况在文章中描述的情况以及包括了,正好说明了class文件记录了什么而又没有记录什么——一个非泛型的派生类继承一个泛型基类时,派生类的class文件对基类的记录就包含了已经确定了值了泛型参数,相对泛型基类来说,这里仍然是“声明”一侧,于是源码中泛型信息在文本怎么写的就在class文件里记录了什么。
是的,没有意思,关键是要运行时,非申明式的定义。
获得参数类型是徒劳的,一般我会HardCode去做。
2 楼
RednaxelaFX
2010-06-23
sohuexe 写道
也不是完全没有办法,最典型的就是《java Persistence with hibernate》 的例子可以参照http://community.jboss.org/wiki/GenericDataAccessObjects。
那种情况在文章中描述的情况以及包括了,正好说明了class文件记录了什么而又没有记录什么——一个非泛型的派生类继承一个泛型基类时,派生类的class文件对基类的记录就包含了已经确定了值了泛型参数,相对泛型基类来说,这里仍然是“声明”一侧,于是源码中泛型信息在文本怎么写的就在class文件里记录了什么。
1 楼
sohuexe
2010-06-23
也不是完全没有办法,最典型的就是《java Persistence with hibernate》 的例子可以参照http://community.jboss.org/wiki/GenericDataAccessObjects。
发表评论
-
The Prehistory of Java, HotSpot and Train
2014-06-02 08:18 0http://cs.gmu.edu/cne/itcore/vi ... -
MSJVM and Sun 1.0.x/1.1.x
2014-05-20 18:50 0当年的survey paper: http://www.sym ... -
Sun JDK1.4.2_28有TieredCompilation
2014-05-12 08:48 0原来以前Sun的JDK 1.4.2 update 28就已经有 ... -
IBM JVM notes (2014 ver)
2014-05-11 07:16 0Sovereign JIT http://publib.bou ... -
class data sharing by Apple
2014-03-28 05:17 0class data sharing is implement ... -
Java 8与静态工具类
2014-03-19 08:43 16293以前要在Java里实现所谓“静态工具类”(static uti ... -
Java 8的default method与method resolution
2014-03-19 02:23 10465先看看下面这个代码例子, interface IFoo { ... -
HotSpot Server VM与Server Class Machine
2014-02-18 13:21 0HotSpot VM历来有Client VM与Server V ... -
Java 8的lambda表达式在OpenJDK8中的实现
2014-02-04 12:08 0三月份JDK8就要发布首发了,现在JDK8 release c ... -
GC stack map与deopt stack map的异同
2014-01-08 09:56 0两者之间不并存在包含关系。它们有交集,但也各自有特别的地方。 ... -
HotSpot Server Compiler与data-flow analysis
2014-01-07 17:41 0http://en.wikipedia.org/wiki/Da ... -
字符串的一般封装方式的内存布局 (1): 元数据与字符串内容,整体还是分离?
2013-11-07 17:44 22408(Disclaimer:未经许可请 ... -
字符串的一般封装方式的内存布局
2013-11-01 12:55 0(Disclaimer:未经许可请 ... -
关于string,内存布局,C++ std::string,CoW
2013-10-30 20:45 0(Disclaimer:未经许可请 ... -
对C语义的for循环的基本代码生成模式
2013-10-19 23:12 21884之前有同学在做龙书(第二版)题目,做到8.4的练习,跟我对答案 ... -
Java的instanceof是如何实现的
2013-09-22 16:57 0Java语言规范,Java SE 7版 http://docs ... -
oop、klass、handle的关系
2013-07-30 17:34 0oopDesc及其子类的实例 oop : oopDesc* ... -
Nashorn各种笔记
2013-07-15 17:03 0http://bits.netbeans.org/netbea ... -
《深入理解Java虚拟机(第二版)》书评
2013-07-08 19:19 0值得推荐的中文Java虚拟机入门书 感谢作者赠与的样书,以下 ... -
豆列:从表到里学习JVM实现
2013-06-13 14:13 48397刚写了个学习JVM用的豆列跟大家分享。 豆列地址:http: ...
相关推荐
Java开发规范是编程实践中至关重要的一个方面,它不仅关乎代码的可读性、可维护性和团队协作效率,也直接影响到软件项目的质量和长期发展。本文将深入探讨Java开发规范中的核心原则,以及如何通过遵循这些规范来提升...
1. **使用synchronized关键字**:Java中的`synchronized`关键字可以保证同一时间只有一个线程访问特定代码块,从而避免数据竞争。它可以应用于方法或代码块,确保线程安全。 2. **使用java.util.concurrent包下的...
标题中的“答复: 通过代码简单介绍JDK 7的MethodHandle,并与.NET的委托对比(二)”表明本文将深入探讨Java中的MethodHandle概念,并将其与.NET平台上的委托进行对比。MethodHandle是JDK 7引入的一个强大特性,它...
java-adsb 这是Java的Mode S和ADS-B解码库。 它是OpenSky Network项目( )的产品。 它基于以下两个参考: 国际民航组织航空电信附件10第4卷(监视雷达和防撞系统) RTCA DO-260B“ 1090ES的最低运行性能标准(MOPS...
- 肯定答复与否认答复:对一般疑问句的回答,肯定答复用“Yes, + 主语 + 助动词/情态动词。”,否定答复用“No, + 主语 + 助动词/情态动词 + not。” 2. 具体题目解答: - 1. It is fun at school. 否认句:It is...
博文链接:https://eric2007.iteye.com/blog/158580
当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。 看到这可能比较晕,没关系,我的理解是这样...
性能是一个关键问题,因为用户的每次击键都可以调用一个查询,并且每个查询都应该在几毫秒内得到答复。 更重要的是,因为用户在打字时经常会出现拼写错误,所以自动完成应该容忍错误和表现形式的差异。 毋庸置疑,...
标题中的“答复:答复‘通向开放未来的捷径’”暗示了这是一篇对某个讨论或议题的回应,特别是关于开放科学和知识发布的路径。在描述中提到的SpringerNature是一家知名的学术出版机构,它涉及的社论可能讨论了如何在...
开发语言:Java 框架:ssm+jsp 架构:B/S 数据库:mysql 【演示视频-编号:595】 https://pan.quark.cn/s/b3a97032fae7 【实现功能】 高校班级同学录网站实现的功能包括班级校友管理,学院主管管理,学校主管管理...
肯定答复:Yes, there are. 否定答复:No, there aren't. 对于情态动词,如can,否定形式是在其后加"not",一般疑问句则将其提前到句首。例如: 2. 肯定句:Li Ming's father can drive the car. 否定句:Li ...
Java聊天服务器在Java上与服务器和客户端进行简单聊天如果要尝试,请在IDE上下载客户端和服务器类,然后在客户端之后首先运行服务器●客户端连接到服务器时,它应发送:服务器问候,然后接收:hi。 用户名在加入...
1. **JAIN SIP API**:Java Advanced Internet Services (JAIN) SIP 是Oracle提供的Java库,用于开发SIP应用。它提供了一系列接口和类,使得开发者可以轻松地创建SIP会话。 2. **SIP会话管理**:包括创建、修改、...
目录克隆Java桌面应用程序可扫描和存储所提供路径的整个结构,并监视其随时间的变化。 可以用于存储文件列表作为备份措施,或者仅用于跟踪电影和已拥有的电影。关于工具使用Scene Builder在JAVA上制作。 这些方法是...
- 答复示例:`She is wearing a green sweater, an orange skirt, and a brown scarf.`:她穿着一件绿色毛衣,一条橘色短裙和一条棕色围巾。 - `Is this your cap?`:这是你的帽子吗? - 肯定答复:`Yes, it is.`...
Reply是一个电子邮件应用程序,使用Material Design组件和Material Theming来创建品牌交流体验。 该项目试图在中提供“的完美像素实现。 重点放在UI(动画,布局,过渡等)上,并应展示Flutter作为UI框架的功能。...
Java正在成长但不仅仅是Java。Java成长路线,但学到的不仅仅是Java。 成长Java正在成长但不仅仅是Java。Java成长路线,但学到的不仅仅是JAVA。维护人员(排名不分底部)@林大塔、@ lzx2005、@ TGhoul、@ ...
开发语言:Java 框架:ssm+jsp 架构:B/S 数据库:mysql 【演示视频-编号:595】 https://pan.quark.cn/s/b3a97032fae7 【实现功能】 高校班级同学录网站实现的功能包括班级校友管理,学院主管管理,学校主管管理...