- 浏览: 3015001 次
- 性别:
- 来自: 海外
文章分类
- 全部博客 (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类的构造器只能通过两种方式调用,一个是通过new表达式,另一个是通过反射调用构造器。这两种方式对Java程序员来说都是“整体”的,但实际新建对象的动作分两步走:
1、创建出空对象(此时类型已经是正确的了),对应字节码是new
2、调用某个版本的构造器,对应字节码是invokespecial "<init>"。
默认的Java反序列化机制同样是分两步走,但变成:
1、创建出空对象(此时类型已经是正确的了);
2、调用用户定义的反序列化方法(readObject,如果有的话)或者调用默认反序列化方法。
这就是为什么反序列化可以看作是“隐藏的构造器”。
如果想自己试试去玩创建出空对象但却不调用构造器的,可以试试sun.misc.Unsafe.allocateInstance()
用Groovy控制台来演示一下:
可以看到,创建f2指向的Foo对象时,构造器并没有被调用(没有输出"foo ctor!"),实例的状态(value)也并未按用户指定的值初始化(12345),整个对象的所有字段都处于默认状态(0或者null或者false之类)。
只是借这个话题用Unsafe举例说明Java对象的创建是分两步走、调用构造器只是其中一步。并不是说反序列化的时候就一定用了Unsafe哦,这个请注意区分 ^_^
实际上在Sun JDK的实现里,Java层面的反射类库与JVM层面的反射实现相互配合来完成反序列化。java.io.ObjectStreamClass通过跟反射方法/构造器调用类似的机制获取所谓的“序列化构造器”,在反序列化的时候调用这个版本的构造器。
创建这个“序列化构造器”时要在继承链里从最具体向最抽象的方向搜索,找出第一个不可序列化的类(没有实现Serializable接口的类),并找出它的无参构造器来调用。也就是说,反序列化的时候并不是完全不调用用户代码里声明的构造器,只是不调用实现了Serializable的类的而已。
关于构造器,之前还有别的讨论可以参考:
实例构造器是不是静态方法?
======================================================================
用以前介绍过的办法,把原帖里的例子拿来做一下实验,可以更形象的说明问题。
原帖代码(稍微修改,去掉了包名):
MyFilter:
编译的时候用:
然后先运行:
别让它退出,用jps查出它的进程ID,然后用ClassDump得到class文件:
这样就得到了./sun/reflect/GeneratedSerializationConstructorAccessor1.class文件。那么用javap就能查看到它的内容:
对应的Java代码(示意,里面的逻辑用Java无法直接表示,因为Java里new表达式同时包含了创建对象与调用构造器,而且这两个动作必须针对同一类型;而这里创建了TestClass的实例却调用了Object的无参构造器):
(注:上面代码为了省事用了Java 7的multi-catch语法)
可以留意一下一段有趣的注释:
一些相关的值得关注的方法和类有:
java.io.ObjectStreamClass.getSerializableConstructor()
sun.reflect.ReflectionFactory.newConstructorForSerialization()
sun.reflect.MethodAccessorGenerator.generateSerializationConstructor()
sun.reflect.SerializationConstructorAccessorImpl
======================================================================
上面用Sun JDK来演示了反序列化的一种可能的实现方式。事实上Sun JDK也是从1.4开始才采用这种方式的,之前使用的是别的方式。这种方式使用了无法通过校验(verification)的字节码序列,硬要说的话是与JVM规范冲突的;为了能执行它而不出错,HotSpot VM里专门开了后门。
请注意区分“规范”与实现之间的差异。
规范一般定得会比较紧,而实现则可能在许多地方“走捷径”,只要不让上面的用户能感知到走了捷径就没问题 ∩^_^∩
3 楼 ouchxp 昨天 引用
回1,2楼 看<深入JAVA虚拟机>
我也刚看个开头
<深入JAVA虚拟机>
记得确实对这些有描述
第七章吧,对象实例化,四种
new
反射
克隆
反序列化
膈应人.:twisted:
把之前我引用过的一段也贴上来:
RednaxelaFX 写道
嗯顺带推荐Effective Java, Second Edition的第74条
引用
A second cost of implementing Serializable is that it increases the likelihood
of bugs and security holes. Normally, objects are created using constructors;
serialization is an extralinguistic mechanism for creating objects. Whether
you accept the default behavior or override it, deserialization is a “hidden constructor”
with all of the same issues as other constructors. Because there is no
explicit constructor associated with deserialization, it is easy to forget that you
must ensure that it guarantees all of the invariants established by the constructors
and that it does not allow an attacker to gain access to the internals of the object
under construction. Relying on the default deserialization mechanism can easily
leave objects open to invariant corruption and illegal access (Item 76).
of bugs and security holes. Normally, objects are created using constructors;
serialization is an extralinguistic mechanism for creating objects. Whether
you accept the default behavior or override it, deserialization is a “hidden constructor”
with all of the same issues as other constructors. Because there is no
explicit constructor associated with deserialization, it is easy to forget that you
must ensure that it guarantees all of the invariants established by the constructors
and that it does not allow an attacker to gain access to the internals of the object
under construction. Relying on the default deserialization mechanism can easily
leave objects open to invariant corruption and illegal access (Item 76).
在Java语言层面看,Java类的构造器只能通过两种方式调用,一个是通过new表达式,另一个是通过反射调用构造器。这两种方式对Java程序员来说都是“整体”的,但实际新建对象的动作分两步走:
1、创建出空对象(此时类型已经是正确的了),对应字节码是new
2、调用某个版本的构造器,对应字节码是invokespecial "<init>"。
默认的Java反序列化机制同样是分两步走,但变成:
1、创建出空对象(此时类型已经是正确的了);
2、调用用户定义的反序列化方法(readObject,如果有的话)或者调用默认反序列化方法。
这就是为什么反序列化可以看作是“隐藏的构造器”。
如果想自己试试去玩创建出空对象但却不调用构造器的,可以试试sun.misc.Unsafe.allocateInstance()
用Groovy控制台来演示一下:
Groovy Shell (1.7.2, JVM: 1.6.0_23) Type 'help' or '\h' for help. ---------------------------------------------------------------- groovy:000> class Foo { groovy:001> int value = 12345; groovy:002> Foo() { println "foo ctor!" } groovy:003> int getValue() { println "getValue"; value } groovy:004> } ===> true groovy:000> f1 = new Foo() foo ctor! ===> Foo@10f0625 groovy:000> f1.value getValue ===> 12345 groovy:000> f2 = sun.misc.Unsafe.theUnsafe.allocateInstance(Foo) ===> Foo@38fff7 groovy:000> f2.value getValue ===> 0 groovy:000> quit
可以看到,创建f2指向的Foo对象时,构造器并没有被调用(没有输出"foo ctor!"),实例的状态(value)也并未按用户指定的值初始化(12345),整个对象的所有字段都处于默认状态(0或者null或者false之类)。
只是借这个话题用Unsafe举例说明Java对象的创建是分两步走、调用构造器只是其中一步。并不是说反序列化的时候就一定用了Unsafe哦,这个请注意区分 ^_^
实际上在Sun JDK的实现里,Java层面的反射类库与JVM层面的反射实现相互配合来完成反序列化。java.io.ObjectStreamClass通过跟反射方法/构造器调用类似的机制获取所谓的“序列化构造器”,在反序列化的时候调用这个版本的构造器。
创建这个“序列化构造器”时要在继承链里从最具体向最抽象的方向搜索,找出第一个不可序列化的类(没有实现Serializable接口的类),并找出它的无参构造器来调用。也就是说,反序列化的时候并不是完全不调用用户代码里声明的构造器,只是不调用实现了Serializable的类的而已。
关于构造器,之前还有别的讨论可以参考:
实例构造器是不是静态方法?
======================================================================
用以前介绍过的办法,把原帖里的例子拿来做一下实验,可以更形象的说明问题。
原帖代码(稍微修改,去掉了包名):
import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.Serializable; public class TestClass implements Serializable { private static final long serialVersionUID = 0L; public TestClass() throws Exception { throw new Exception("!!!"); } public static void main(String[] args) throws Exception { byte[] head = { -84, -19, 0, 5, 115, 114, 0 }; byte[] ass = { 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 120, 112 }; String name = TestClass.class.getName(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); baos.write(head); baos.write(name.length()); baos.write(name.getBytes()); baos.write(ass); baos.flush(); baos.close(); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())); TestClass o = (TestClass) ois.readObject(); ois.close(); System.out.println("Created: " + o); System.in.read(); // 暂停进程以便有足够时间来dump class } }
MyFilter:
import sun.jvm.hotspot.tools.jcore.ClassFilter; import sun.jvm.hotspot.oops.InstanceKlass; public class MyFilter implements ClassFilter { @Override public boolean canInclude(InstanceKlass kls) { String klassName = kls.getName().asString(); return klassName.startsWith("sun/reflect/GeneratedSerializationConstructorAccessor"); } }
编译的时候用:
javac -classpath ".:$JAVA_HOME/lib/sa-jdi.jar" MyFilter TestClass
然后先运行:
java TestClass
别让它退出,用jps查出它的进程ID,然后用ClassDump得到class文件:
java -classpath ".:./bin:$JAVA_HOME/lib/sa-jdi.jar" -Dsun.jvm.hotspot.tools.jcore.filter=MyFilter sun.jvm.hotspot.tools.jcore.ClassDump 7566
这样就得到了./sun/reflect/GeneratedSerializationConstructorAccessor1.class文件。那么用javap就能查看到它的内容:
Classfile /D:/experiment/test_java_deserialize/sun/reflect/GeneratedSerializationConstructorAccessor1.class Last modified 2010-12-23; size 1313 bytes MD5 checksum 6d59fc9bb0c7d58458cdc76714829a0f public class sun.reflect.GeneratedSerializationConstructorAccessor1 extends sun.reflect.SerializationConstructorAccessorImpl minor version: 0 major version: 46 flags: ACC_PUBLIC Constant pool: // ... { public sun.reflect.GeneratedSerializationConstructorAccessor1(); flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #36 // Method sun/reflect/SerializationConstructorAccessorImpl."<init>":()V 4: return public java.lang.Object newInstance(java.lang.Object[]) throws java.lang.reflect.InvocationTargetException; flags: ACC_PUBLIC Exceptions: throws java.lang.reflect.InvocationTargetException Code: stack=6, locals=2, args_size=2 0: new #6 // class TestClass 3: dup 4: aload_1 5: ifnull 24 8: aload_1 9: arraylength 10: sipush 0 13: if_icmpeq 24 16: new #22 // class java/lang/IllegalArgumentException 19: dup 20: invokespecial #29 // Method java/lang/IllegalArgumentException."<init>":()V 23: athrow 24: invokespecial #12 // Method java/lang/Object."<init>":()V 27: areturn 28: invokespecial #42 // Method java/lang/Object.toString:()Ljava/lang/String; 31: new #22 // class java/lang/IllegalArgumentException 34: dup_x1 35: swap 36: invokespecial #32 // Method java/lang/IllegalArgumentException."<init>":(Ljava/lang/String;)V 39: athrow 40: new #24 // class java/lang/reflect/InvocationTargetException 43: dup_x1 44: swap 45: invokespecial #35 // Method java/lang/reflect/InvocationTargetException."<init>":(Ljava/lang/Throwable;)V 48: athrow Exception table: from to target type 0 24 28 Class java/lang/ClassCastException 0 24 28 Class java/lang/NullPointerException 24 27 40 Class java/lang/Throwable }
对应的Java代码(示意,里面的逻辑用Java无法直接表示,因为Java里new表达式同时包含了创建对象与调用构造器,而且这两个动作必须针对同一类型;而这里创建了TestClass的实例却调用了Object的无参构造器):
package sun.reflect; public class GeneratedSerializationConstructorAccessor1 extends SerializationConstructorAccessorImpl { public GeneratedMethodAccessor1() { super(); } public Object newInstance(Object[] args) throws InvocationTargetException { try { // create an unitialized TestClass instance TestClass temp = newUnitializedTestClassInstance(); // new #6 // class TestClass // dup // check parameters if (args.length != 0) throw new IllegalArgumentException(); } catch (final ClassCastException | NullPointerException e) { throw new IllegalArgumentException(e.toString()); } // invoke Object() constructor try { invokeObjectConstructor(temp); // invokespecial #12 // Method java/lang/Object."<init>":()V return temp; // areturn } catch (Throwable t) { throw new InvocationTargetException(t); } } }
(注:上面代码为了省事用了Java 7的multi-catch语法)
可以留意一下一段有趣的注释:
package sun.reflect; /** <P> Java serialization (in java.io) expects to be able to instantiate a class and invoke a no-arg constructor of that class's first non-Serializable superclass. This is not a valid operation according to the VM specification; one can not (for classes A and B, where B is a subclass of A) write "new B; invokespecial A()" without getting a verification error. </P> <P> In all other respects, the bytecode-based reflection framework can be reused for this purpose. This marker class was originally known to the VM and verification disabled for it and all subclasses, but the bug fix for 4486457 necessitated disabling verification for all of the dynamically-generated bytecodes associated with reflection. This class has been left in place to make future debugging easier. </P> */ abstract class SerializationConstructorAccessorImpl extends ConstructorAccessorImpl { }
一些相关的值得关注的方法和类有:
java.io.ObjectStreamClass.getSerializableConstructor()
sun.reflect.ReflectionFactory.newConstructorForSerialization()
sun.reflect.MethodAccessorGenerator.generateSerializationConstructor()
sun.reflect.SerializationConstructorAccessorImpl
======================================================================
上面用Sun JDK来演示了反序列化的一种可能的实现方式。事实上Sun JDK也是从1.4开始才采用这种方式的,之前使用的是别的方式。这种方式使用了无法通过校验(verification)的字节码序列,硬要说的话是与JVM规范冲突的;为了能执行它而不出错,HotSpot VM里专门开了后门。
请注意区分“规范”与实现之间的差异。
规范一般定得会比较紧,而实现则可能在许多地方“走捷径”,只要不让上面的用户能感知到走了捷径就没问题 ∩^_^∩
评论
6 楼
beritha
2011-10-11
受教了
5 楼
liuleigang
2010-12-24
引用
3 楼 ouchxp 昨天 引用
回1,2楼 看<深入JAVA虚拟机>
我也刚看个开头
<深入JAVA虚拟机>
记得确实对这些有描述
第七章吧,对象实例化,四种
new
反射
克隆
反序列化
4 楼
ouchxp
2010-12-23
引用
throw new Exception("!!!");
膈应人.:twisted:
3 楼
ouchxp
2010-12-23
回1,2楼 看<深入JAVA虚拟机>
我也刚看个开头
我也刚看个开头
2 楼
xgj1988
2010-12-23
还有就是希望给一个 地址。 专门讲 CLASS文件 JAVAP 之后的 的汇编代码的指令意思。
1 楼
xgj1988
2010-12-23
看得似懂非懂的。看来需要了解一下VM 规范和JAVA规范。
发表评论
-
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 16131以前要在Java里实现所谓“静态工具类”(static uti ... -
Java 8的default method与method resolution
2014-03-19 02:23 10324先看看下面这个代码例子, 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 22246(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 21734之前有同学在做龙书(第二版)题目,做到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 48102刚写了个学习JVM用的豆列跟大家分享。 豆列地址:http: ...
相关推荐
9.java学习第九章——对象的创建和使用+构造方法+方法重载——作业的形式:创建对象在JVM中的存储状态(内存图).pdf
本次实验进一步学习了Java的相关知识,深入理解了面向过程与面向对象语言的区别,了解了面向对象语言的三大特征,掌握类的声明和构造方法,掌握了对象的创建方法与创建机制,掌握了成员变量、类变量和局部变量区别,...
●类的定义 ●对象的定义 ●类与对象 ●对象定义类的示例 ●实例变量 ●实例方法 ●实例成员的访问 ●对象的创建 ●构造方法 ●构造方法的特点 ●实例成员访问形式
内容概要: 本文详细介绍了Java中的构造方法概念,并通过一个示例演示了如何使用构造方法来创建对象和初始化对象的属性。读者将学习构造方法的定义、作用和使用方式,并掌握在类的实例化过程中自动调用构造方法的...
【Flutter】Dart 面向对象 ( 命名构造方法 | 工厂构造方法 | 命名工厂构造方法 ) https://hanshuliang.blog.csdn.net/article/details/113746569 博客源码快照
46 | 建造者模式:详解构造函数、set方法、建造者模式三种对象创建方式2020-02-17 王争设计模式之美进入课程讲述:冯永吉时长 10:31大小 8.4
java小程序求圆面积和判断闰年, 定义一个圆形Circle类。属性: r:半径 构造方法: 无参构造方法满参构造方法 成员方法: get/set方法 showArea方法:打印圆形面积...定义测试类,创建MyDate对象,并测试。
类有一种特殊的成员方法叫做构造方法,构造方法与类同名,在创建对象时,会自动调用类的构造方法。 构造方法没有返回类型,更不能定义为void,不可加其他修饰符。 若没有被定义,则在编译的时候,会自动加上一个默认...
在VS.Net中,有很多种方法动态调用对象的构造函数。一是通过Activator类的CreateInstance()方法。这个方法我们在Remoting中...(还有其他重载方法)注意它的返回值为object,MSDN对返回值的描述是:对新创建对象的引用
测试一要求如下:(1)利用空构造方法创建一个新的长方体对象cuboidA,然后利用setter方法设置长宽高分别为1.5、2.6和18.6,(2)调用printCuboid()方法输出cuboidA的基本信息,调用isCube()方法判断是否正方体;...
毕业论文:哈希函数的构造方法,仅供参考。毕业论文 哈希函数
尽管构造方法看起来和一般的成员方法没有差别,但它不是方法,也不是类的成员。因此,构造方法不能直接调用,只能由new 操作符调用。构造方法对于类是十分重要的,对象的初始化任务要靠构造方法来完成。重载构造方法...
构造方法是对象创建完成后第一个被对象自动调用的方法,这是我们在对象中使用构造方法的原因。而析构方法是对象在销毁之前最后一个被对象自动调用的方法,这也是我们在对象中使用析构方法的原因。所以通常使用构造...
调用构造方法创建对象;对象的内存模型;定义多个构造方法;使用对象;使用对象;1、 radius变量是Cirlce类的成员变量,语句中使用radius时指的是当前对象的radius 。 2、什么是当前对象?谁调用getArea方法,谁就是...
内容包括三章:1、构造方法与匿名对象;2、实例讲解—类设计分析(学生类);3、String类。
程序中描述了子类对象构建调用父类的构造方法,以及类变量和实例变量创建的不同过程
主要介绍了Java编程构造方法与对象的创建详解,具有一定参考价值,需要的朋友可以了解下。
PHP的类、对象、构造方法。看懂了这个就懂了面和对象的程序设计想法。跨越各种语言的障碍。
面向对象与Java实现(类、对象、构造方法
使用构造函数创建对象的语法为“new 构造函数名()”,在小括号中可以传递参数给构造函数,如果没有参数,小括号可以省略。 1.创建对象的方式 创建对象 利用构造函数创建对象 1.创建对象的方式 遍历对象的属性和方法 ...