JVM / Java, применяются ли правила доступности методов во время выполнения? - PullRequest
3 голосов
/ 11 ноября 2011

Мне было любопытно, как работает JVM.Признает ли JVM правила доступа к методу, такие как «закрытый» защищенный, или это делается только во время компиляции?

Например, возможно ли примерно в строке 37 выполнить некоторые манипуляции с байт-кодом и вызвать защищенный метод, скажем, 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;
}
 */

Ответы [ 4 ]

5 голосов
/ 11 ноября 2011

В JLS!

15.12.4 Оценка вызова метода во время выполнения
Во время выполнения вызов метода требует пять шагов.Во-первых, целевая ссылка может быть вычислена.Во-вторых, выражения аргумента оцениваются. В-третьих, проверяется доступность метода, который должен быть вызван. В-четвертых, находится фактический код для метода, который должен быть выполнен.В-пятых, создается новый кадр активации, при необходимости выполняется синхронизация и управление передается в код метода.

Формулировка JLS указывает, что доступность будет проверяться во время выполнения.

4 голосов
/ 11 ноября 2011

JVM это подтверждает.Их можно переопределить, вызвав setAccessible(true), как это делает Prashant Bhate, но по умолчанию они применяются.(См. http://download.oracle.com/javase/6/docs/api/java/lang/reflect/AccessibleObject.html#setAccessible%28boolean%29.)

Кстати, вы пишете, что «компилятор не кодирует правила видимости метода типа в файл Java bytecde»; но это так. В дополнение к вышесказанному, он имеет для их кодирования по ряду причин. Например:

  • вы можете скомпилировать класс A, который ссылается на класс B, даже если у вас есть только скомпилированная версия класса B.
  • вы можете проверить видимость метода с помощью отражения (метод getModifiers()).
  • частные методы не являются виртуальными -slash- не могут быть переопределены подклассами.
3 голосов
/ 11 ноября 2011

Если вы хотите вызывать этот метод из-за пределов текущего класса, вы можете вызывать закрытые и защищенные методы, используя отражение.

Method m = RunX.class.getDeclaredMethod("test3");
m.setAccesible(true);
m.invoke(u);

однако вы можете вызывать этот защищенный (а также закрытый) метод непосредственно из main()без проблем.

1 голос
/ 11 ноября 2011

Оли правильно сказал, что в конечном итоге вы можете делать все, что угодно, если вы дошли до степени манипулирования байтовым кодом (если все сделано правильно !!!). Хотя я хотел бы ответить на ваш вопрос о доступности честь во время выполнения в Java. Если у вас есть какие-либо сомнения, тогда, пожалуйста, продолжайте и используйте рефлексию, чтобы вызвать закрытый метод одного класса из другого класса, и вы получите свой ответ. Java создает таблицу функций класса во время выполнения при загрузке и позволяет ссылаться на функции в правиле ограничения доступности. Однако Java предоставляет средство, позволяющее вызывать закрытые методы с помощью отражения, используя setAccessible (true) в ссылке на метод, прежде чем вызывать его.

...