JVM/Java,是不是在运行时强制执行方法可访问性规则?
Posted
技术标签:
【中文标题】JVM/Java,是不是在运行时强制执行方法可访问性规则?【英文标题】:JVM/Java, are method accessibility rules enforced at runtime?JVM/Java,是否在运行时强制执行方法可访问性规则? 【发布时间】:2011-12-26 16:45:05 【问题描述】:我很好奇 JVM 是如何工作的。 JVM 是否承认方法可访问性规则(如“私有”保护)还是仅在编译时完成?
例如,是否可以在 line37 左右进行一些字节码操作并调用受保护的方法,比如 test3?通常编译器不会让我调用该方法,因为它被声明为受保护的。但我很好奇该受保护规则是否在运行时强制执行?
u.test1(); // 是否可以在运行时通过字节码操作调用“test3” // @line37
package org.berlin.algo.basic.test;
public class RunX
private String zzz = "rrrrr";
public void test1()
// Note: I am intentionally use 'new' here as part of my test, not a
// good practice I know but allowed by the language.
Object x = new String("Test1 -----[1.1] " + zzz);
x = new String("Test1 --- [1.2]" + x.toString());
System.out.println(x);
this.test2();
this.test3();
/**
* Here, I noticed that the compiler removed my 'test2' code block.
* Does that always happen?
*/
private void test2()
Object x = new String("Test2@line21--->>> [2.1]");
System.out.println(x);
protected void test3()
Object x = new String("Test3@line27 Will the JVM enforce the 'protected' method rule for test3? --->>> [3.1]");
x = new String("Test3@line28--->>> [3.2]");
System.out.println(x);
public static void main(final String [] args)
System.out.println("Running");
RunX u = new RunX();
u.test1();
// Is it possible at runtime, to call 'test3' through bytecode manipulation
// @line37
System.out.println("Done");
// End of the Class //
/*
JVM bytecode: javap -v RunX
Compiled from "RunX.java"
public class org.berlin.algo.basic.test.RunX extends java.lang.Object
SourceFile: "RunX.java"
minor version: 0
major version: 50
Constant pool:
const #1 = class #2; // org/berlin/algo/basic/test/RunX
const #2 = Asciz org/berlin/algo/basic/test/RunX;
...
...
const #84 = Asciz SourceFile;
const #85 = Asciz RunX.java;
public org.berlin.algo.basic.test.RunX();
Code:
Stack=2, Locals=1, Args_size=1
0: aload_0
1: invokespecial #10; //Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #12; //String rrrrr
7: putfield #14; //Field zzz:Ljava/lang/String;
10: return
LineNumberTable:
line 3: 0
line 5: 4
line 3: 10
LocalVariableTable:
Start Length Slot Name Signature
0 11 0 this Lorg/berlin/algo/basic/test/RunX;
public void test1();
Code:
Stack=5, Locals=2, Args_size=1
0: new #21; //class java/lang/String
3: dup
4: new #23; //class java/lang/StringBuilder
7: dup
8: ldc #25; //String Test1 -----[1.1]
10: invokespecial #27; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
13: aload_0
14: getfield #14; //Field zzz:Ljava/lang/String;
17: invokevirtual #30; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
20: invokevirtual #34; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
23: invokespecial #38; //Method java/lang/String."<init>":(Ljava/lang/String;)V
26: astore_1
27: new #21; //class java/lang/String
30: dup
31: new #23; //class java/lang/StringBuilder
34: dup
35: ldc #39; //String Test1 --- [1.2]
37: invokespecial #27; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
40: aload_1
41: invokevirtual #41; //Method java/lang/Object.toString:()Ljava/lang/String;
44: invokevirtual #30; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
47: invokevirtual #34; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
50: invokespecial #38; //Method java/lang/String."<init>":(Ljava/lang/String;)V
53: astore_1
54: getstatic #42; //Field java/lang/System.out:Ljava/io/PrintStream;
57: aload_1
58: invokevirtual #48; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
61: aload_0
62: invokespecial #54; //Method test2:()V
65: aload_0
66: invokevirtual #57; //Method test3:()V
69: return
LocalVariableTable:
Start Length Slot Name Signature
0 70 0 this Lorg/berlin/algo/basic/test/RunX;
27 43 1 x Ljava/lang/Object;
protected void test3();
Code:
Stack=3, Locals=2, Args_size=1
0: new #21; //class java/lang/String
3: dup
4: ldc #66; //String Test3@line27 Will the JVM enforce the 'protected' method rule for test3? --->>> [3.1]
6: invokespecial #38; //Method java/lang/String."<init>":(Ljava/lang/String;)V
9: astore_1
10: new #21; //class java/lang/String
13: dup
14: ldc #68; //String Test3@line28--->>> [3.2]
16: invokespecial #38; //Method java/lang/String."<init>":(Ljava/lang/String;)V
19: astore_1
20: getstatic #42; //Field java/lang/System.out:Ljava/io/PrintStream;
23: aload_1
24: invokevirtual #48; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
27: return
LocalVariableTable:
Start Length Slot Name Signature
0 28 0 this Lorg/berlin/algo/basic/test/RunX;
10 18 1 x Ljava/lang/Object;
public static void main(java.lang.String[]);
Code:
Stack=2, Locals=2, Args_size=1
0: getstatic #42; //Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #72; //String Running
5: invokevirtual #74; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: new #1; //class org/berlin/algo/basic/test/RunX
11: dup
12: invokespecial #76; //Method "<init>":()V
15: astore_1
16: aload_1
17: invokevirtual #77; //Method test1:()V
20: getstatic #42; //Field java/lang/System.out:Ljava/io/PrintStream;
23: ldc #79; //String Done
25: invokevirtual #74; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
28: return
LocalVariableTable:
Start Length Slot Name Signature
0 29 0 args [Ljava/lang/String;
16 13 1 u Lorg/berlin/algo/basic/test/RunX;
*/
【问题讨论】:
如果你直接操作字节码,你可以为所欲为(在极限内,你可以重写所有代码。)所以我不确定你的问题是什么? @BerlinBrown,你的最后一句话是错误的。 ruak 关于为什么必须对信息进行编码的逻辑是合理的。我还为您提供了 JLS 的引述,它直接解决了是否在运行时检查可访问性的问题。 【参考方案1】:To the JLS!
15.12.4 方法调用的运行时评估 在运行时,方法调用需要五个步骤。首先,可以计算目标参考。其次,评估参数表达式。 第三,检查被调用方法的可访问性。 第四,定位被执行方法的实际代码。第五,创建一个新的激活帧,必要时进行同步,并将控制权移交给方法代码。
JLS 的措辞表明将在运行时检查可访问性。
【讨论】:
【参考方案2】:JVM 确实承认这些。它们可以被覆盖,通过调用setAccessible(true)
就像 Prashant Bhate 所做的那样,但默认情况下它们是强制执行的。 (见http://download.oracle.com/javase/6/docs/api/java/lang/reflect/AccessibleObject.html#setAccessible%28boolean%29。)
顺便说一句,您写道“编译器不会将类型方法可见性规则编码到 Java 字节码文件中”;但确实如此。除了上述之外,出于多种原因,它必须对这些进行编码。例如:
即使您只有 B 类的编译版本,您也可以编译引用 B 类的 A 类。 您可以通过反射检查方法的可见性(getModifiers()
方法)。
私有方法不是虚拟的 -slash- 不能被子类覆盖。
【讨论】:
【参考方案3】:如果您想从当前类外部调用此方法,您可以使用反射调用私有和受保护的方法。
Method m = RunX.class.getDeclaredMethod("test3");
m.setAccesible(true);
m.invoke(u);
但是,您可以直接从 main()
调用此受保护(也是私有)方法,而不会出现任何问题。
【讨论】:
【参考方案4】:Oli 正确地提到,如果您达到字节码操作的程度(如果操作正确!!!),最终您可以做任何事情。 尽管我想回答您在 Java 运行时的可访问性荣誉问题。如果您有任何疑问,请继续使用反射从另一个类调用一个类的私有方法,您将得到答案。 Java 在加载时会在运行时创建类的函数表,并允许在可访问性规则限制内引用函数。然而,Java 提供了一种便利,您可以在调用私有方法之前在方法引用上使用 setAccessible(true) 通过反射调用私有方法。
【讨论】:
以上是关于JVM/Java,是不是在运行时强制执行方法可访问性规则?的主要内容,如果未能解决你的问题,请参考以下文章