RuntimeExceptionとExceptionの違い

ブログ2本目!!

今回はJavaの例外で、通常のExceptionとRuntime系Exceptionの違いと、バグの元となりそうな挙動について記載してみたいと思います。

 

まず、通常の例外の定義方法を以下に記載してみます。

f:id:daisuke6106:20141213204114p:plain

これは通常通りの挙動です。

 

処理内にて例外が発生する可能性がある場合、

・自身のメソッドの中で例外を処理

・呼び出し元に発生した例外を投げて丸投げする

を決めることができます。

 

以下の例では自分で作成したExceptionを継承した例外クラスをthrowしてみます。

f:id:daisuke6106:20141213212234p:plain

 当然ですが、throwsを取り除くとコンパイルエラーとなります。

f:id:daisuke6106:20141213212423p:plain

 

ではRuntimeExceptionを継承した例外クラスをthrowするように修正します。 

f:id:daisuke6106:20141213212546p:plain

  

上記のキャプチャを見るとわかりますが、throwする例外がRuntimeExceptionを継承した例外クラスである場合、throws宣言をしなくてもコンパイルが通ります。

  

この上でコーディングの時の注意点

現場で既存のコードを読んでいる際に例外をキャッチする場合、以下のようにExceptionや、Throwableにてキャッチするのをよく見ます。

また以下のような場合は稼働させても正しく動きます。

f:id:daisuke6106:20141213221458p:plain

 

ただし、呼び出し先メソッド(この場合、splitメソッド)に不具合があり、RuntimeExceptionを継承した例外クラスであるNullPointerException(通称ぬるぽ)が発生した場合、呼び出し元のcatchでExceptionもしくは、ThrowableはこのNullPointerExceptionをcatchしてしまいます。

f:id:daisuke6106:20141213221958p:plain

 RuntimeException系例外すらもキャッチするという要件を元に作成するのならいいのですが、通常RuntimeException系例外は「これ以上処理できないような致命的な異常が発生した」という状態であるためJVMや、フレームワーク等に処理方法を任せるのが本来の姿です。

 

よって、必ずcatchするときはExceptionはThrowableではなく発生する例外クラスをcatchしするべきです

 

ExceptionやThrowableでcatchしてしまうと、バグでNullPointerExceptionが発生しているにも関わらず、ログにNullPointerExceptionが発生している旨が表示されず、ハマるといったケースがあり、大変危険です。

 

自分でthrowした例外をキャッチする場合は、通常どおりcatchの中に入る。

f:id:daisuke6106:20141213223908p:plain

RuntimeException系例外クラスであるNullPointerExceptionが発生した場合は、JVMが例外を処理しコンソールにNullPointerExceptionが発生した旨が出力された。

f:id:daisuke6106:20141213223447p:plain

以上!!!!!

Javaのボクシングは危険なかほり

バルテック アドベントカレンダー2014 Advent Calendar 2014 - Adventar

 

バルテックアドベントカレンダー2014年の12月6日担当として『Javaのボクシングは危険なかほり』と題して、Javaのボクシング機能のちょっと危険な挙動を簡単に紹介してみたいと思います。。

 

まず、ボクシング、アンボクシングとは

ボクシング・アンボクシングは、プリミティブ型と対応するラッパクラス参照(たとえば int と Integer)を自動的に相互変換するするものです。J2SE1.4までは、プリミティブ型とラッパクラス参照を相互変換するためには、明示的にデータ変換しなければいけませんでした。コレクションにはオブジェクトしか登録できないため、プリミティブ型データをコレクションに登録したりコレクションから取得する際など、毎回データ変換が必要でした。J2SE5.0では、そのような変換が自動的に行われるようになりました。

引用元:TECHSCOREさんから引用させていただきました。

3. オートボクシング | TECHSCORE(テックスコア)

 

要するにこんな感じ

f:id:daisuke6106:20141203233959p:plain

 

ボクシング、アンボクシングの説明にも書いてありますが、よくintなどのプリミティブ型をリストに突っ込んだりするときに使います。

 

ボクシング、アンボクシングを使用した変数での比較に注意

 

この機能はプリミティブか、ラッパーかを意識しなくてすむので便利に思えますが、比較したりするときには注意しなければなりません。

 

例えば以下のように

f:id:daisuke6106:20141204003006p:plain

この挙動は正しく見えます。

 

ただし、変数に入れている値をちょっと変えてみると・・・・

f:id:daisuke6106:20141204003410p:plain

a1、b1に1を足した、a2、b2から1を引いただけなのに比較結果はfalseに変化しました。

 

何故か?

ラッパークラスはあくまでクラスのインスタンスであるため、==比較した場合、値の比較ではなく、通常のクラスと同じように参照場所(メモリ上にあるインスタンスが存在する場所)の比較となります。

(よって、明示的にnew を使って、メモリ上に別々のインスタンスを生成した場合での比較は以下のようにfalseとなります。)

f:id:daisuke6106:20141204001538p:plain

このことからボクシングを使った場合の比較も常に『false』になるのが正しく思えますが、実はIntegerクラスの中で『-128から127』の間のIntegerのインスタンスはメモリ上にキャッシュを持っており、ボクシングを行った場合、『-128から127』の範囲ではこのキャッシュのインスタンスを使用するといった処理になります。

 

なので『-128から127』の間では『true』、その範囲から外れると『false』ということになります。

 

リストから数値を取り出して比較する等ありがちな処理でも、気を付けないとこれによっていらぬバグを混入してしまう場合がありえます。

 

対策

対策としてボクシング・アンボクシングされた値を比較するときには常にプリミティブ型なのかラッパーのクラスなのか意識し、明示的に変換を行って比較を行うということで対処できます。

f:id:daisuke6106:20141204011017p:plain

 

めんどくさいなぁという人は以下の設定をeclipseに盛り込むことでボクシング・アンボクシングを検知できるようになります。

『設定』→『Java』→『コンパイラー』→『エラー/警告』→『ボクシング、及びアンボクシング変換』の項目を『エラー』、もしくは『警告』とすること。 

f:id:daisuke6106:20141204013113p:plain

『エラー』を設定した結果、ボクシングを使用している箇所は赤くなった。

f:id:daisuke6106:20141204013226p:plain

 

以上です!!

 

おまけ① 

アドベントカレンダーの12月5日にてeclipseの小技について書かれてたのでちょっと追記してみたいと思います。

 

WEBアプリケーションを作成している時にデバックするときは、自身のPC上(以下ローカル)のeclipseでサーバを稼働させて、ブレークポイントを設定すればデバックはできます。

 

が、よくビルドしたWEBアプリケーションをローカル以外のサーバにデプロイして、画面の結合試験を行うことがよくありますが(というか普通そうだとと思いますが・・・)、画面が期待値通りの動きをしない、けど原因がわからないからデバックしたい。でもローカル上でないからeclipseでデバックできないという状況がたまにあります。

 

その時に便利なのがeclipseのリモートデバックです。

 

準備

tomcatなどのサーバ起動用スクリプト(startup.shや、startup.bat)にはjavaに渡される引数がこのスクリプト内で渡されていますが、その中に

-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8080,suspend=n

という引数が加わるように設定します。

 

そののち、サーバを起動します

weblogicだと管理用画面から設定可能。)

 

デバック実施する場合

eclipseのメニューバーから「デバックの構成」を選択して、「リモートデバック」を選択します。

f:id:daisuke6106:20141206015240p:plain

 

ホストに現在WEBアプリケーションが稼働しているサーバのIPアドレス、ポートに起動スクリプトに記載したポート番号(上記の場合だと『address=8080』の部分)を記入して、「デバック」をすると接続されます。(うまく設定されてないとエラーがでます)

接続できたらeclipseソースコードに対してブレークポイントをおいて、画面を動かすことでデバックができるようになります。

 

 

おまけ②

12月3日にはJMockitについても書かれていたのでまた小ネタを入れてみます。

JMockitにはカバレッジを取得してくれる機能も持っています。

JMokitをダウンロードしてきたら「jmockit-coverage.jar」をクラスパスに含めてあげます。

f:id:daisuke6106:20141206112905p:plain

注意:この時クラスパスの順序はJUnitのライブラリより上にしてください。

f:id:daisuke6106:20141206115122p:plain

 

 

あとは、通常どおりJunitでテストコードを書いてJunitを動かすだけでカバレッジが取得される。

f:id:daisuke6106:20141206114332p:plain

f:id:daisuke6106:20141206114530p:plain

index.htmlを開けば今回のテスト結果のカバレッジが取得されている。

f:id:daisuke6106:20141206114710p:plain

javaファイル名のアンカーを押下すればJUNITでテストされていない箇所を見ることもできる。

f:id:daisuke6106:20141206114910p:plain

大量にクラスがあるときはこんな感じ。パッケージ毎に表示されて便利

f:id:daisuke6106:20141206115352p:plain

 以上です!!!!

 

また、趣味グラミングで作成したモノをGitHubで公開してるんで、興味ある人はcloneして見てくだしい、バグってたらプルリクおなしゃす。


daisuke6106 (Daisuke6106) · GitHub

 

 

次回(アドベントカレンダーとは関係なく、気が向いたら書くかもしれません)!! 

  • RuntimeExceptionと普通のExceptionの違い
  • Serializableとはなんぞや?
  • Javaでリストのソートをするときってどうやるんだっけ?
  • 自分の作ったクラスでequalsを実装するときに守らなければならないこと
  • サーバにデプロイされたプログラムのデバック方法 
  • 現場でよく使う便利なLinuxコマンド集