9

私の理解では、コンパイル中に型チェックを実行できる場合、型キャストはコンパイル中に実行され、実行時のオーバーヘッドは発生しません。

例えば

public Child getChild() {
  Parent o = new Child();
  return (Child) o;
}

型キャストはコンパイル中または実行時に行われますか?

また、型キャストをjavacコンパイラで行うかVMで行うかを決定する一般的なルールはありますか?

4

5 に答える 5

2

実際、この場合、3つの可能性があります。

  1. javacコンパイラーは最適化を実行できます。
  2. JITコンパイラは最適化を実行できます。
  3. JITコンパイラによるネイティブコードには、ランタイムタイプチェックを実行するためのコードを含めることができます。

オプション1または2だと思いますが、これはプラットフォーム固有である可能性があります。


実際、私のシステムでは、バイトコードは最適化されていません。最適化が行われる場合は、JITコンパイラがそれを行います。(これは私が聞いたことと一致します...ほとんどのJavaバイトコードコンパイラは、バイトコードを生成する前に最適化の方法をほとんど実行しません。)

Compiled from "Test.java"
public class Test extends java.lang.Object{
public Test();
  Code:
   0:   aload_0
   1:   invokespecial   #8; //Method java/lang/Object."<init>":()V
   4:   return

public Child getChild();
  Code:
   0:   new #16; //class Child
   3:   dup
   4:   invokespecial   #18; //Method Child."<init>":()V
   7:   astore_1
   8:   aload_1
   9:   checkcast   #16; //class Child
   12:  areturn

}
于 2012-07-24T13:47:22.443 に答える
1

実行中のプログラムがオブジェクト参照を別のタイプにキャストしようとすると、仮想マシンは、キャスト先のタイプが参照オブジェクトの実際のクラスであるか、そのスーパータイプの1つであるかを確認する必要があります。プログラムがinstanceof操作を実行するときに、同じ種類のチェックを実行する必要があります。

いずれの場合も、仮想マシンは参照されるオブジェクトのクラスデータを調べる必要があります。プログラムがインスタンスメソッドを呼び出すとき、仮想マシンは動的バインディングを実行する必要があります。参照のタイプではなく、オブジェクトのクラスに基づいて呼び出すメソッドを選択する必要があります。これを行うには、オブジェクトへの参照のみが指定されたクラスデータに再度アクセスできる必要があります。

編集:

一部のバインディングが実行時にのみ発生するように、Javaコンパイラはキャストが正しいかどうかをチェックする責任を負いません。Java仮想マシンは、実行時にチェックを実行して、実際の参照オブジェクトが新しいタイプの正当なオブジェクトであるかどうかを確認します。そうでない場合は、実行時例外ClassCastExceptionが発生します。

于 2012-07-24T13:41:26.603 に答える
0

コンパイルしたとき

public class Test {
    public Child getChildVersion1() {
        Parent o = new Child();
        return (Child) o;
    }
    public Child getChildVersion2() {
        return new Child();
    }
}

javap -c TestJava 7(Windows 7 64ビット)を使用してそのコードを逆コンパイルすると、この結果が得られました

Compiled from "Test.java"
public class Test {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public Child getChildVersion1();
    Code:
       0: new           #2                  // class Child
       3: dup
       4: invokespecial #3                  // Method Child."<init>":()V
       7: astore_1
       8: aload_1
       9: checkcast     #2                  // class Child
      12: areturn

  public Child getChildVersion2();
    Code:
       0: new           #2                  // class Child
       3: dup
       4: invokespecial #3                  // Method Child."<init>":()V
       7: areturn
}

したがって、コンパイラがメソッドgetChildVersion1を最適化していないgetChildVersion2ため、コンパイルに型をチェックする以外に、実行時にチェックすることもできます(9: checkcast #2)。しかし、Stephen Cが述べたように、それはプラットフォーム(OS、Javaバージョン)に関連している可能性があります。

于 2012-07-24T14:06:48.963 に答える
0

私はそれが両方の段階で行われたと思います。コンパイル時に、コンパイラーは、強い型の言語のように、型を混同しないように適切なキャストを実行するように強制します。

Objectただし、たとえば、パラメータとしてString(実際に存在するオブジェクトに対して機能する)にキャストした場合instanceof Stringでも、JVMは、の実装クラスがObject本当に拡張されているかどうかを確認する必要があり、そうでない場合Stringはを取得します。 ClassCastExceptiont。

于 2012-07-24T13:41:43.897 に答える
0

実行時のテストを必要としない変換の場合、コンパイラーが実行時のキャストを回避するためにいくつかの最適化を行う可能性があります。

実行時にテストが必要な変換のタイプについて詳しく知るには、 JLSの第5章「変換とプロモーション」を読むことをお勧めします。

例5.0-1。コンパイル時と実行時の変換

A conversion from type Object to type Thread requires a run-time check to make sure that the run-time value is actually an instance of class Thread or one of its subclasses; if it is not, an exception is thrown.

A conversion from type Thread to type Object requires no run-time action; Thread is a subclass of Object, so any reference produced by an expression of type Thread is a valid reference value of type Object.

A conversion from type int to type long requires run-time sign-extension of a 32-bit integer value to the 64-bit long representation. No information is lost.

A conversion from type double to type long requires a nontrivial translation from a 64-bit floating-point value to the 64-bit integer representation. Depending on the actual run-time value, information may be lost.

5.1.6。参照変換の絞り込み

このような変換では、実際の参照値が新しいタイプの正当な値であるかどうかを確認するために、実行時にテストが必要です。そうでない場合は、ClassCastExceptionがスローされます。

5.1.8。開箱変換; 変換は実行時に続行されます。

5.5.3も参照してください。実行時にチェックされたキャスト

たとえば、変換がいつ行われたかを判断するのはそれほど簡単ではありません。

public class Main {

    private static class Child extends Parent{

        public Child() {
        }
    }

    private static class Parent {

        public Parent() {
        }
    }

    private static Child getChild() {
        Parent o = new Child();
        return (Child) o;
    }


    public static void main(final String[] args) {
        Child c = getChild();
    }
}

によって与えられる結果javap -c Mainは次のとおりです。

public class Main extends java.lang.Object{
public Main();
Code:
0:  aload_0
1:  invokespecial   #1; //Method java/lang/Object."<init>":()V
4:  return

public static void main(java.lang.String[]);
Code:
0:  invokestatic    #4; //Method getChild:()LMain$Child;
3:  astore_1
4:  return

}

メソッド宣言をpublic static Child getChild()結果に変更すると、次のようになります。

public class Main extends java.lang.Object{
public Main();
Code:
0:  aload_0
1:  invokespecial   #1; //Method java/lang/Object."<init>":()V
4:  return

public static Main$Child getChild();
Code:
0:  new #2; //class Main$Child
3:  dup
4:  invokespecial   #3; //Method Main$Child."<init>":()V
7:  astore_0
8:  aload_0
9:  checkcast   #2; //class Main$Child
12: areturn

public static void main(java.lang.String[]);
Code:
0:  invokestatic    #4; //Method getChild:()LMain$Child;
3:  astore_1
4:  return

}

アクセサを変更するだけで、可能な最適化に大きな影響を与える可能性があることがわかります。

于 2012-07-24T13:49:57.037 に答える