トリッキーなJavaタスク

最近では、 OCA Java SE 7プログラマーIに合格しました 準備中、私は膨大な数の問題を解決し、そこから言語の多くの微妙な点を抽出することができました。 最も面白くて賢い-将来のために保管されています。 そして今、私は小さな個人的なコレクションを蓄積しました。その最大の部分をここで説明します。



素晴らしい記事「JAVA、%username%およびその2番目の部分 を知っていますか」 で、著者は準備後に彼の経験も共有しました。 しかし、私は自分の何かを追加できるという結論に達しました。 そして、この記事が生まれました。





タスク

それでは始めましょう。 私はすべてのトリックを、あなたのために特別に作成した小さなタスクに分割しました。 言語の微妙さは純粋な形で際立っています-テストの場合のように、不必要な飾りや誤りがありません。 また、最初にキャラクターに正確な質問答えてどこかに書き留めてから、正しい答えを見ることをお勧めします。 このテストを解決したユーザーの半数以上が回答するのだろうか? そして、これらの例はすべてJava 7向けであることを忘れないでください。



1)このコードはコンパイルされますか?その場合、出力はどうなりますか?

long year = 201l; System.out.print(year);
      
      



答え
201
説明
目立ったキャッチが生じないように、特にバックライトをオフにしました。 そうしないと、最後の手紙が視覚的に目立つようになります-そして、その中のすべての問題の塩。



英語では、小文字のlは数字の1に非常に似ています。この例では、警告を表示します。Javaで許可されていますが、 l smallを使用して長いリテラルを表すことはできません 。 また、ユニットが潜在的に存在する可能性がある場所では使用しないでください。 ほとんどのプログラマーが行うように、大文字のLを使用することをルールにします。



注意深く見ると、ユニットが視覚的にわずかに異なっていることに気付くでしょう。 ちょっとだけ。 しかし、私たちはすべてを細部にわたって考えたいと思いますか?



一見すると思われるかもしれませんが、結論は201 1ではなく201になります。


2)このコードはコンパイルされますか?その場合、出力はどうなりますか?

 int[][] array = {{1, 2, 3}, {0, 0, 0,},}; System.out.println(Arrays.deepToString(array));
      
      



答え
[[1、2、3]、[0、0、0]]
説明
最後にある2つの「余分な」コンマは、通常混乱を引き起こします。



テストの1つでそのようなコードを初めて見たとき、コードがコンパイルされないことを固く決心しました-そしてそれは間違っていることが判明しました。 一部のユーザーは、指定されていない要素にはデフォルト値が入力されると想定する場合がありますが、これも間違っています。



そして、結論は簡単です- [[1、2、3]、[0、0、0]] 。 コンパイラーは、配列の末尾にある余分なコンマを単純に無視することがわかります。 そして、たった1〜2行ですでにコンパイルエラーが発生します。



言語仕様に問題のないこの状況の説明が見つかりました- 配列初期化子の最後の式の後に末尾のコンマが表示されることがあり、無視されます。



しかし実際には-あるアレイから別のアレイに手動でコピーするときの便宜のために作成されました。 コンパイラが余分なコンマの挿入を許可しなかった場合、ある配列から別の配列の最後に値をコピーして貼り付けるか、最後の余分なものを削除するときに、最後にカンマを追加する必要があります。



3)このコードはコンパイルされますか?その場合、出力はどうなりますか?

 double $ = 0XD_EP2F; System.out.print($);
      
      



答え
888.0
説明
おそらく、このコードはコンパイルされないでしょう。 そしていや! 彼はかなり働いています。



Javaを学んでいたとき、すぐに、HEXのアンダースコアと指数から何かワイルドなことができることに気付きました。 もちろん、この例は実際のプロジェクトで使用するためではなく 、知識を訓練するためのものです。 これは記事の最も複雑な例とハイライトだと思います。 最初の2つは簡単なトレーニングでした。 誰かが最初に正しく答えましたか?



この例には2つの興味深い点があります。 最初は変数の名前です。 変数をドルと呼ぶのはかなり面白いです。 しかし、 決して実際のプロジェクトではこれを行わないでください 。 コンパイラはこのアプローチを禁止していませんが、技術的な目的でドルが使用されるため、推奨されません。 たとえば、匿名クラスおよびネストされたクラスに名前を付ける場合。 MyClass $ MyInnerClass、またはMyClass $ 1。 また、名前を簡単に折りたたむことができます-1つのクラスは単にMyClass $ MyInnerClassと呼ばれ、もう1つのクラスはMyInnerClassがネストされたMyClassです-それらは同じ名前になります。 したがって、変数にドルを含めることはお勧めしません。



このようなコードも非常に正しいです
 double _ = 8.0;
      
      





次に、2番目のポイント-リテラルそのものを見てみましょう。 私はできるだけ早くそれをねじった。 まず、この番号が16進形式で記述されていることに気付くのは簡単です。 しかし、彼女は文字としてA、B、C、D、E、Fのみを許可しています-あなたは思う。 Pはどこから来たのですか? そして、アンダースコアはどこにありますか?



順番にすべて。 末尾のFは、このリテラルがfloat型であることを意味します。 そして、私たちと一緒に自動的にdoubleにキャストされます。 さらに、非常に興味深い点-P2。 16進数を指数形式で書きたい場合、E + Nを使用することはできません。これは、Eが数値で使用でき、あいまいさが発生する可能性があるためです。 しかし、 BinaryExponentIndicatorの使用を妨げるものはいません。pと次数を指定します。 数値は、指定された程度まで2倍されます。 この場合、4。



しかし、アンダースコアはJava 7の非常に便利な革新です。これを数字に挿入して、たとえば数字やグループ番号で区切ることができます。 コンパイラは単にそれを切り取る-それはより便利な読書のために必要であり、それ以上はありません。



したがって、質問に正確に答えるためには、System.out.printが通常の10進数形式でdouble型の変数を出力することを覚えておく必要があります。 したがって、16進数から10進数に変換する必要があります。 これは完全に簡単な作業です。 DE 16 = 222 10 。 次に、222に2 2を掛けて888を取得します。最後に、double型では、小数部がない場合、ポイントとゼロが追加されることを忘れないでください。 したがって、答えは888.0です。



4)このコードはコンパイルされますか?その場合、出力はどうなりますか?

 public class Main{; public static void main(String[] args) { System.out.println(new Main().$_$()[2]); } ;short $_$()[] {{{ return new short[007]; }}}; };
      
      



答え
0
説明
最初に目を引くのは、使用できないセミコロンの使用です。 たとえば、クラス宣言の後(C ++など)。 またはクラスのメンバー間。 コンパイラがそこにそれらを置くことを可能にした理由-私はまだ知りませんでした。 しかし、これは完全に受け入れられます。



メソッド名は有効な識別子です。 ただし、戻り値の型はここでは短くなく、短い配列です。 コンパイラーは、2つの形式の配列宣言-識別子の前後の角括弧を許可します。 さらに、最初のケースはメソッド専用です。 しかし、メソッドの2番目のケースを試すとどうなりますか? これも正しいですが、見た目がひどいので、実際のプロジェクトでは絶対に使用しないでください 。 しかし、そのような機会があります。



さらに、メソッド内には、2つのネストされたコードブロックがあり、それらを簡単に削除できます。 そして最後に-このメソッドは、ゼロで初期化され、インデックス2の要素が0である7要素の長さのshort配列を単に返します。ああ、007は8進リテラルです。 しかし、これは何にも影響しません。



5)このコードはコンパイルされますか?その場合、出力はどうなりますか?

 public class Main { public static void main(String[] args) { ((Main) null).hara(); } static void hara() { System.out.println("Hello habrahabr!"); } }
      
      



答え
こんにちは、habrahabr!
説明
注意深い読者は、メソッドの名前にロシア文字bが含まれていることに気付くでしょう。 それだけでなく、Javaでは、識別子の名前を英語以外の言語で完全に記述することができるため、異なる言語の文字を混在させることができます。



私があなたに伝えたかった重要なポイントは、あなたが誤って別の言語から間違ったキャラクターを印刷し、それから長く苦痛な時間のために間違いを探すことができることです。 たとえば、英語のaとロシア語のaは視覚的に区別できません(少なくともこのフォントでは)。 ユニットが少なくとも何らかの形でlと区別できる場合、すべてが悪化しています。 状況を想像してください-何らかの理由で、クラスまたはメソッドの名前に誤って英語の文字ではなくロシア語の文字を入力しました。 あるいは、ロシアのレイアウトで既存のラテン名を編集した可能性が高いです。 これらの言語の間には多くの同様の文字がありますので、間違いの可能性が高いです。 オートコンプリートは国際識別子を挿入し、すべてが正常に機能します。 しかし、メソッドを呼び出したり、リフレクションを介してクラスを取得しようとすると、それほど簡単に検出できないエラーが発生します。 むしろ、名前が視覚的に一致するため、別のものを探すことになります-時間と労力を失います。



外観が同じ2つの識別子に異なるアルファベットからの外見的に同じ文字が含まれる別の例を追加したいと思いますが、コードが異なるため、識別子も異なります。 そして、コードがコンパイルエラーを引き起こす理由を説明させます。 しかし、それはあまりにも残酷です-キャッチに気付く方法はありません。



次に-かなり独自の方法で静的メソッドを呼び出します-nullをキャストしてMain型にします。 そして、それはランタイムエラーを引き起こしません! 事実、静的メソッドの呼び出しはコンパイル段階で許可され、オブジェクトのタイプのみに依存します。



この記事へのコメントでは、ユーザーYurevich1が実際のプロジェクトの例を挙げています。

どういうわけかプロジェクトでは、クラスでロシア語の変数「c」を設定しました。 私はどこが間違いかを理解するためにどこかで時間をつぶしました。そしてコードの別の部分のプロジェクトは同じ変数cを誓います。それはすでに英語です。




6)このコードはコンパイルされますか?その場合、出力はどうなりますか?

 Byte[] Byte[] = {{0}}; System.out.println(Byte); System.out.println(Byte.class); System.out.println(Byte.length); System.out.println(new Byte("8"));
      
      



答え
[[Ljava.lang.Byte; @ 6f171e30( ハッシュは異なる場合があります

クラスjava.lang.Byte

1

8
説明
そのため、ここでは一見わかりにくい別のコード例を示します。 まず第一に、バイトシェルの配列の次の2つの連続した宣言と混同する必要があります。 ただし、言語を注意深く学習した場合、配列を宣言するには2つの方法があることを知っておく必要があります。名前の前後に角括弧があります。 さらに、これらの2つのメソッドを同時に使用することを誰も気にしません。目の前にあるのは2次元配列です。



次に、クラスの名前と完全に一致する配列の名前と混同する必要があります。 Javaでは、標準ライブラリのローカル変数クラスを呼び出すことができますが、それらはクラスをオーバーラップします。 ただし、Javaのコンパイラは優れています。 とても賢い。 彼は、クラスだけがnew演算子に従うことができ、 .classの前にもいることに気付くでしょう。 また、そのコンテキストでは、インスタンス名が重複する必要がありますが、Byteクラスが暗示されます。





まとめ

Javaは厳密な標準化が好きです。 仕様では、ほとんどすべての微妙な点を非常に高い精度で定義しています。 だから-これらの微妙さを一度勉強した-あなたはどこでもそれらを扱うことができます。



私は猫の上のコード画面がIDEAから取られたことを追加したいと思います。 私は自分でバックライト方式を開発し、その利点を最大限に活用しています。 誰もがそれを好きなら-私はそれを捨てることができます。



いくつかの異なるテストシステムでトレーニングを行いました。 まず第一に、良いサイトはクイズフルでしょう。 Java FundamentalsおよびJava Intermediateテストには、非常に多くの有用なタスクが含まれています。 次-ExamLabのテストは非常に優れています-SCJP 6を実行し、最後に-試験自体-Enthuwareを実行しました



数多くのテストに合格し、「頭の中で」問題を解決した後、コードの記述がどれほど快適で効果的になったかに嬉しく驚きました。 テストは知識を体系化して整理します。 私は心の中で多くの間違いを予見し始め、また最良の解決策を選択し始めました。 APIの一部を学んだことを後悔していませんでしたが、最初はメソッドとクラスの名前を暗記するべきではないと考えました。 私はJavaを世界で最も優れた言語の1つと考えており、その詳細な調査のためにテストを推奨しています。 そして、Java SE 7プログラマーIはまったく複雑ではありませんでした-100ポイントのうち96ポイントを簡単に獲得しました。 誰が取るつもりです-私はいくつかのヒントを与えることができます- 個人で書いてください



All Articles