if/blockとは対照的に、try/catchブロックを使用するためのJavaにオーバーヘッドはありますか?そう)?
たとえば、文字列の「安全なトリム」メソッドの次の2つの単純な実装を考えます。
_public String tryTrim(String raw) {
try {
return raw.trim();
} catch (Exception e) {
}
return null;
}
public String ifTrim(String raw) {
if (raw == null) {
return null;
}
return raw.trim();
}
_
raw
入力がめったにnull
でない場合、2つの方法の間にパフォーマンスの違いはありますですか?
さらに、良いプログラミングパターンですかコードのレイアウトを簡素化するためにtryTrim()
アプローチを使用すること、特に多くの場合ifブロックまれなエラー条件をチェックできる場合コードを1つのtry/catchブロックで囲むことで回避できますか?
たとえば、_N parameters
_を使用するメソッドがよくあります。このメソッドでは、開始時に_M <= N
_を使用します。そのようなパラメーターが「無効」(nullまたは空の文字列)、残りのコードには影響を与えません。
このような場合、_k * M
_ if blocks(ここで、k
はパラメーターごとの平均チェック数です。たとえば、nullまたは空の場合は_k = 2
_文字列)、try/catchブロックはコードを大幅に短縮し、1〜2行のコメントを使用して「型にはまらない」ロジックを明示的に示すことができます。
このようなパターンは、特にエラー条件がめったに発生しない場合にメソッドを高速化し、プログラムの安全性を損なうことなくそれを行います(エラー条件が「通常」であると仮定します。存在することはめったにありませんが)許容されます。
パフォーマンスのオーバーヘッドについて質問していることは知っていますが、try
/catch
とif
を同じ意味で使用しないでください。
try
/catch
は、通常のプログラムフローではなく、制御できない問題のためのものです。たとえば、ファイルに書き込もうとしてファイルシステムがいっぱいになった場合この状況は通常、try
/catch
で処理する必要があります。
if
ステートメントは、通常のフローと通常のエラーチェックである必要があります。それで、たとえば、ユーザーは必須の入力フィールドに入力できませんか? if
/try
ではなく、catch
を使用してください。
あなたのサンプルコードは、if
/try
ではなくcatch
ステートメントが正しいアプローチであることを強く示唆しているように思えます。
あなたの質問に答えるには、try
よりもcatch
/if
の方が一般的にオーバーヘッドが大きいと思います。確実に知るために、Javaプロファイラーを取得して、気になる特定のコードを見つけてください。状況によって答えが異なる可能性があります。
この質問はほとんど「死に答えられた」が、役に立つことができるいくつかのポイントがあると思う。
例外的でない制御フローに_try / catch
_を使用するのはスタイルが悪い(Javaで)。 (「非例外的」が何を意味するかについてはしばしば議論がありますが、それは別のトピックです。)
スタイルが悪い理由の一部は、_try / catch
_が桁の大きさが通常の制御フローステートメントよりも高価であることです。1。実際の違いはプログラムとプラットフォームによって異なりますが、1000倍以上高価になると思います。とりわけ、creation例外オブジェクトはスタックトレースをキャプチャし、スタック上の各フレームに関する情報を検索してコピーします。スタックが深いほど、コピーする必要がある部分が多くなります。
スタイルが悪い理由の別の部分は、コードが読みにくいことです。
1-Java 7の最近のバージョンのJITは、例外処理を最適化してオーバーヘッドを大幅に削減できる場合があります。ただし、これらの最適化はデフォルトでは有効になっていません。
サンプルの記述方法にも問題があります。
Exception
をキャッチすることは非常に悪い習慣です。他の未チェックの例外を偶然キャッチする可能性があるためです。たとえば、raw.substring(1)
への呼び出しの周りでそれを行った場合、潜在的なStringIndexOutOfBoundsException
s ...およびhideバグもキャッチします。
あなたの例がやろうとしているのは、(おそらく)null
文字列の扱いが不十分な結果です。一般的な原則として、null
文字列の使用を最小限に抑え、それらの(意図的な)広がりを制限しようとする必要があります。可能な場合は、null
の代わりに空の文字列を使用して、「値なし」を意味します。また、needでnull
文字列を渡すか返す場合は、メソッドjavadocsに明確に文書化します。メソッドがnull
で呼び出されてはいけないときに呼び出されるとしたら、それはバグです。例外をスローします。 (この例では)null
を返すことでバグを補おうとしないでください。
[〜#〜] followup [〜#〜]
私の質問は、ヌル値だけでなく、より一般的でした。
...そして、私の答えのほとんどのポイントはヌル値に関するものではありません!
ただし、時折null値、または例外を生成する可能性のある他の値を許可し、それらを無視したい場合が多いことに留意してください。これは、たとえば、どこかからキー/ペア値を読み取り、上記のtryTrim()などのメソッドに渡す場合です。
はい、null
値が期待される状況があり、それらに対処する必要があります。
しかし、私はtryTrim()
がやっていることは(通常)null
を扱う間違った方法だと主張します。次の3つのコードを比較します。
_// Version 1
String param = httpRequest.getParameter("foo");
String trimmed = tryTrim(param);
if (trimmed == null) {
// deal with case of 'foo' parameter absent
} else {
// deal with case of 'foo' parameter present
}
// Version 2
String param = httpRequest.getParameter("foo");
if (param == null) {
// deal with case of 'foo' parameter absent
} else {
String trimmed = param.trim();
// deal with case of 'foo' parameter present
}
// Version 3
String param = httpRequest.getParameter("foo");
if (param == null) {
// treat missing and empty parameters the same
param = "";
}
String trimmed = param.trim();
_
最終的には、null
を通常の文字列とは異なる方法で処理する必要があり、それは通常できるだけ早くこれを行うことをお勧めします。 null
がそのポイントOriginからさらに伝播することが許可されるほど、プログラマーはforgetnull
値が可能性があり、バグのあるコードを書く可能性が高くなります。 null以外の値を想定しています。また、HTTPリクエストパラメータが欠落している可能性があることを忘れる(つまり_param == null
_)のは、これが発生する典型的なケースです。
tryTrim()
が本質的に悪いと言っているのではありません。しかし、プログラマがこのようなメソッドを記述する必要性を感じているという事実は、おそらく理想的なヌル処理が不十分であることを示しています。
2番目のバージョンを使用します。他の選択肢がある場合は、制御フローに例外を使用しないでください。例外は例外的な状況です。
トピックについては、ここでException
をキャッチしないでください。特にそれを飲み込まないでください。あなたの場合、NullPointerException
が期待されます。何かをキャッチする場合、それはあなたがキャッチするものです(ただし、パラグラフ1に戻り、donotこれを行います )。 Exception
をキャッチ(そして飲み込む!)するとき、あなたは「何が間違っていてもそれを処理できる。私はそれが何であるかは気にしない」と言っている。あなたのプログラムは取り消せない状態にあるかもしれません!対処する準備ができているものだけをキャッチし、そのレイヤーが最上位レイヤーであっても、canが処理するレイヤーに他のすべてを伝搬させ、例外をログに記録してから、イジェクトスイッチを押すだけです。
それ以外の場合、例外のスローとキャッチは高速ですが(if
はおそらくより高速ですが)、現在のすべてのスタックをウォークスルーする必要があるため、例外のスタックトレースが作成されます。 (一般に、制御フローに例外を使用するのは悪いことですが、それが本当に必要であり、例外が高速でなければならない場合、Throwable.fillInStackTrace()
メソッドをオーバーライドしてスタックトレースの構築をスキップするか、1つの例外を保存することができます常に新しい例外インスタンスを作成する代わりに、インスタンスを繰り返しスローします。)