JAVA、%username%を知っていますか? パート2

JAVA Evil Edition 1月上旬に、興味深いJavaテストに関する記事を書きました。 それは非常に多くの関心を呼び起こし、興味深いタスクがまだ残っていたので、続けます。



すぐにいくつかの質問に答えます。その答えはコメントで失われる可能性があります。

最初に、彼らはトピックで何を読むべきか尋ねました。 この本を強くお勧めます。 私はロシア語で見たことがありませんが、何らかの理由でほとんどのプログラミングに関する本よりもずっと読みやすいので、ほとんどの場合これは問題になりません。 第二に、彼らはそのようなパズルをどこで手に入れるか尋ねました。 ここでは具体的なことはお勧めしません。いくつかの非営利テストセットを含むさまざまなソースからのタスクです。したがって、オプションとして、ソースに注意を払うことができます。



たまたまこの部分には簡単なタスクが含まれていたため、結果はより良くなるはずです。 それで、habrakatの下での次のテスト( 注意、後半の回答と説明 )。



テスト



特に明記しない限り、正しい答えは1つだけです。



質問1


次のコードが利用可能です:

class Super { public String name = "Tort"; public String getName() { return name; } } class Sub extends Super { public String name = "Habr"; public static void main(String[] args) { Super s = new Sub(); System.out.println(s.name + " " + s.getName()); } }
      
      





コンソールには何が表示されますか?

  1. ハブラー不法行為
  2. ハブラハブラ
  3. 不法行為
  4. 不法行為不法行為
  5. コンパイルエラー


質問2


実行の結果として得られるもの:

 boolean b1 = false; boolean b2 = false; if (b2 != b1 = !b2) { System.out.println("YES"); } else { System.out.println("NO"); }
      
      





オプション:

  1. コンパイルエラー
  2. ランタイムエラー
  3. はいが表示されます
  4. いいえが表示されます
  5. 何も出力されません


質問3


 class Test{ public static void main(String[] args) { String s = "old"; print(s, s = "new"); } static void print(String s1, String s2) { System.out.println(s1 +" "+ s2); } }
      
      





結果が表示されます:

  1. 新旧
  2. 古い新しい
  3. 新しい新しい
  4. 老人
  5. コードはコンパイルされません


質問4


次のコードが利用可能です:

 class A { A() { print(); } void print() { System.out.println("A"); } } class B extends A { int i = Math.round(3.5f); public static void main(String[] args) { A a = new B(); a.print(); } void print() { System.out.println(i); } }
      
      





何が表示されますか?

  1. A 4
  2. AA
  3. 0 4
  4. 4 4
  5. 別の答え


質問5


次のコードが利用可能です:

 class Threader extends Thread { public void run() { System.out.println("In Threader"); } } class Pooler extends Thread { public Pooler(){ } public Pooler(Runnable r) { super( r ); } public void run() { System.out.println("In Pooler"); } } public class TestClass { public static void main(String[] args) { Threader t = new Threader(); Thread h = new Pooler(t); h.start(); } }
      
      





その実装の結果、次のようになります。

  1. プーラーで
  2. スレッダーで
  3. コードはコンパイルされません
  4. ランタイムエラー


質問6


このコードについて何が言えますか?

 class Habr implements I1, I2{ public void m() { System.out.println("habr"); } public static void main(String[] args) { Habr habr = new Habr(); habr.m(); } } interface I1{ int VALUE = 1; void m(); } interface I2{ int VALUE = 2; void m(); }
      
      





回答オプション(おそらく正しい回答):

  1. 「habr」が表示されます
  2. HabrはVALUEにアクセスできません
  3. コードは、いずれかのインターフェイスでVALUEを削除した場合にのみ機能します
  4. コードはコンパイルされません


質問7


次のコードが利用可能です:

 class Test{ public static void main(String[] args) throws Exception { int[] a = null; int i = a [ m() ]; } public static int m() throws Exception { throw new Exception("Another Exception"); } }
      
      





このコードはNullPointerExceptionをスローしますか?

  1. はい
  2. いや




注意-回答





したがって、正しい答えは次のとおりです。

  1. 4
  2. 1
  3. 2
  4. 3
  5. 1
  6. 1
  7. 2


説明





質問1


だから

 class Super { public String name = "Tort"; public String getName() { return name; } } class Sub extends Super { public String name = "Habr"; public static void main(String[] args) { Super s = new Sub(); System.out.println(s.name + " " + s.getName()); } }
      
      





このコードは、Tort Tortを出力します。 多態性が機能するように思われ、ご存じのとおり、リンク自体のタイプではなく、リンクが参照するオブジェクトのタイプに従って選択が行われます。 しかし、Javaにはメソッドがオーバーライドされています。 それだけです。 オーバーライドされたフィールドまたはコンストラクターは単に存在しません。 つまり、メソッドの実装は、参照するオブジェクトに応じて常に選択されます。 反対に、クラスフィールドはリンクのタイプに基づいて選択されるため、ここでは再定義は機能しません。 したがって、この結果が得られます。



質問2


 boolean b1 = false; boolean b2 = false; if (b2 != b1 = !b2) { System.out.println("YES"); } else { System.out.println("NO"); }
      
      





ここでコンパイルエラーが発生します。 簡単で、唯一の質問はオペレーターの優先度です。 !=は優先度が高いため、false =!b2、つまり、定数に値を割り当てようとします。



質問3


 class Test{ public static void main(String[] args) { String s = "old"; print(s, s = "new"); } static void print(String s1, String s2) { System.out.println(s1 +" "+ s2); } }
      
      







メソッドの引数は左から右に評価され、2番目の引数でsに新しい値を割り当てても、最初の引数はすでに計算されているため、何も変更されません。 引数の1つの計算が失敗した場合、他のすべての引数はまったく計算されません。



質問4


 class A { A() { print(); } void print() { System.out.println("A"); } } class B extends A { int i = Math.round(3.5f); public static void main(String[] args) { A a = new B(); a.print(); } void print() { System.out.println(i); } }
      
      







print()メソッドはクラスBでオーバーライドされます。したがって、メソッドは実際のオブジェクトのタイプに応じて選択されます。 クラスBのオブジェクトが作成されると、コンストラクターAが最初に呼び出され、コンストラクターAがprint()メソッドを呼び出します。 クラスBのオブジェクトを作成しているため、クラスBのprint()バージョンが呼び出されますが、この時点では変数iにはまだ値が割り当てられていないため、デフォルト値(0)が表示されます。 さて、後で、初期化された値iが表示されます。 その結果、答えは0 4になります。



質問5


 class Threader extends Thread { public void run() { System.out.println("In Threader"); } } class Pooler extends Thread { public Pooler(){ } public Pooler(Runnable r) { super( r ); } public void run() { System.out.println("In Pooler"); } } public class TestClass { public static void main(String[] args) { Threader t = new Threader(); Thread h = new Pooler(t); h.start(); } }
      
      







Threadオブジェクトを作成するときに、Runnableオブジェクトをコンストラクターに渡すことができます。コンストラクターのrun()メソッドは、別のスレッドで実行されます。 したがって、予想される答えは「In Threader」の結論です。 非標準の動作の理由は、Poolerクラスのrun()メソッドです。 run()メソッドをオーバーライドすることにより、Threadクラスのデフォルトの動作が失われました。



質問6


 class Habr implements I1, I2{ public void m() { System.out.println("habr"); } public static void main(String[] args) { Habr habr = new Habr(); habr.m(); } } interface I1{ int VALUE = 1; void m(); } interface I2{ int VALUE = 2; void m(); }
      
      







このコードは完全に機能します。 同じm()メソッドが両方のインターフェイスに存在するという事実は、いずれの場合でも1つの実装があるため、問題を作成しません。 VALUEフィールドではさらに難しくなります。 あいまいさが生じるため、直接彼に連絡することはできません。 ただし、これは、たとえば次のように実行できます。



((I1)habr).VALUE;



オブジェクトをインターフェイスの1つに持ってくると、以前は使用できなかったフィールドにアクセスできます。



質問7


次のコードが利用可能です:

 class Test{ public static void main(String[] args) throws Exception { int[] a = null; int i = a [ m() ]; } public static int m() throws Exception { throw new Exception("Another Exception"); } }
      
      







この場合、NullPointerExceptionは発生しません。これは、オブジェクトの存在の確認など、配列を使用したアクションが要素インデックスが完全に計算された後にのみ発生するためです。



配列要素に何かを割り当てると、状況はさらに興味深いものになります。 たとえば、コードは次のとおりです。

 int i = 0 ; int[] arr = {1, 2} ; arr[i] = i = 6 ;
      
      





原則として、複数の割り当て操作は右から左に計算されます。 ただし、インデックスは例外です。 この場合、インデックス(arr [0])が最初に計算され、その後初めて変数iに新しい値が割り当てられます。



ご清聴ありがとうございました。 おそらく、私はもうこの形式で書くつもりはありません。私は本ではあまり取り上げられていない特定のトピックの検討に移るつもりです。



All Articles