16

Java で例外を作成、スロー、およびキャッチすることによるパフォーマンス コストはありますか?

「例外駆動型開発」をより大きなプロジェクトに追加する予定です。独自の例外を設計してメソッドに組み込み、開発者がキャッチして適切な作業を行うように強制したいと考えています。

たとえば、名前に基づいてデータベースからユーザーを取得するメソッドがあるとします。

public User getUser(String name);

ただし、ユーザーが null である可能性があり、ユーザーの public メソッドを使用する前にこれを確認するのを忘れがちです。

User user = getUser("adam");

int age = user.getAge();

その結果、NullPointerException とクラッシュが発生します。ただし、チェックを行った場合、ユーザーオブジェクトを返す前に、null の場合は「UserIsNullException」をスローします。

public User getUser(String name) throws UserIsNullException;

私は実装者に考えて行動するように強制します:

try {

    User user = getUser("adam");

    int age = user.getAge();

}catch( UserIsNullException e) {

}

これにより、予期しないクラッシュに対してコードがより安全になり、より多くのバグが排除されます。Web サイトに 1 時間あたり数百人の訪問者がいて、このデザイン パターンがほぼどこでも使用されているとします。

このような設計アプローチはパフォーマンスにどのように影響しますか? メリットがコストを上回っているのか、それとも単純に悪いコーディングなのか?

助けてくれてありがとう!

アップデート!明確にするために、私の注意は、私の例が示唆するように、NullPointerException をラップすることではありません。目標は、実装者に try/catch を書くように強制することです。これにより、次の理由から、実際のクラッシュの頭痛の種を節約できます。

ユーザー == null

忘れられました。問題は、これら 2 つの設計モデルの比較に関するものです。

int age;

try {

User user = getUser("adam");

age = user.getAge();

}catch( UserIsNullException e) {

age = 0;

}

対:

int age;

User user = getUser("adam");

if( user != null ) {
age = user.getAge();
} else {
age = 0;
}
4

14 に答える 14

10

例外をスローするとパフォーマンスが低下しますが、例外処理コードは例外的な場合にのみ実行されるため、通常は許容されます。フロー制御に例外を使用し始めると、通常のケースが逆転し、期待どおりの動作になります。私はそれに対して強くお勧めします。

于 2010-02-02T15:10:40.397 に答える
8

"which would result in NullPointerException and a crash."

a NullPointerException is an exception, and can be caught in the a catch.

So the additional exception is redundant unless it adds clarity to the code. In this instance, it really doesn't. In fact, you've just taken an unchecked programmer error exception and promoted it to a checked exception.

Creating a checked exception for a programmer's error actually means your code has to explicitly handle the possibility that the programmer introduced an error, when they haven't.

于 2010-02-02T15:02:34.600 に答える
8

パフォーマンス コストはかかりますが、それは主な問題ではありません。例のようにコードに catch ブロックを散らかしたいですか? それは恐ろしいです。

通常、Web アプリケーションにはメインの例外ハンドラーがあり、そこでキャッチされなかったものはすべて終了します。少なくとも、エラーが発生した場合は、処理が完全に中断されます。プロセス フローでこのような例外のキャッチが行われると、階段を数段降りるようなものになります。

一部の例外は、予期して処理できる非常に特殊なケースです。ただし、一般に、予期しない、または制御不能な問題が発生した場合、例外がポップアップします。その時点から、オブジェクトは悪い状態になる可能性があります。これは、後続のステップで、例外が原因で発生しなかったことが予想され、処理を続行しようとしても、さらに多くの例外がトリガーされるだけだからです。中央のハンドラーがキャッチできるように、予期しない例外を放置する方がはるかに優れています。

于 2010-02-02T15:03:38.190 に答える
5

個人的には、それは悪くて不快な考えだと思います。

null値をチェックするように人々を促す通常の方法は、@Nullable(およびその反対の、 null@NotNull以外を返すことが保証されている関数の)のような注釈を使用することです。パラメータに同様のアノテーションを設定することで(パラメータの期待値が設定されるように)、高品質のIDEとバグチェッカー(FindBugsなど)は、コードが十分なチェックを行わない場合に必要なすべての警告を生成できます。

これらのアノテーションはJSR-305で利用可能であり、リファレンス実装もあるようです。


パフォーマンスに関する限り、例外の作成はコストのかかる部分です(特に、スタックトレースの充填が原因であると読みました)。例外をスローすることは安価であり、JRubyで使用される制御転送技術です。

于 2010-02-02T15:06:52.183 に答える
5

一般に、例外はJavaで非常に高価であり、フロー制御には使用しないでください。

例外のポイントは、例外的なものを説明することです。たとえば、NumberIsZeroException実際にはそれほど例外的ではありませんが、PlanesWingsJustDetachedException明らかに本当に例外的なものです。nullデータが破損しているか、ID番号がそのようなものと一致しないためにユーザーがソフトウェアで本当に例外的である場合は、その例外を使用しても問題ありません。

例外が引き起こすのは、「幸せな道」からの逸脱です。これはサンプルコードには当てはまりませんが、プレーンを返す代わりにNullObjectnullを使用すると非常に有益な場合があります。

于 2010-02-02T15:07:15.010 に答える
4

Building a stack trace amounts to about a thousand basic instructions. It has a definite cost.


Even though you don't ask, many people will probably tell you the approach you envision does not seem very attractive ... :-(

The code you are forcing on your callers is really ugly, hard to write and maintain.

To be more specific, and try to get understood, I will try to highlight several points.

  • The other developer is responsible for checking the nullity of the User received from your API (if the documentation states it clearly). His code does such checks internally regularly, so he can do it also after a call to your API. Even more, that will give his code some homogeneity.

  • When reusing an existing exception is appropriate, it is much better than creating your own. For example, if it was an error to call your API and ask for an non-existing User, you can throw an IllegalArgumentException.

于 2010-02-02T15:01:32.827 に答える
2

See this answer about performance with exceptions.

Basically in your idea you are wrapping a RuntimeException, NullPointerException, into a checked Exception; imho I would say that the problem can be managed at the business level with an ObjectNotFoundException: you did not find the user, the fact that user is null and that this generates errors comes after.

于 2010-02-02T15:03:22.433 に答える
1

ヌルオブジェクトはあなたに役立つかもしれない何かです。先日、Martin Fowlerの本「リファクタリング」を読んでいましたが、nullを返す代わりに機能する特定のオブジェクトを使用して、NULLを常にチェックする必要がないことを説明しています。

それは本の中で非常によく説明されているので、ここでは説明しようとはしません。

于 2010-02-02T15:06:10.817 に答える
1

API を使用している誰かの視点から何が起こっているのかが非常に明確になるので、このスタイルのコーディングが気に入っています。API メソッドgetMandatoryUser(String)に名前を付けて、決して返さないメソッドと返すことができるgetUserOrNull(String)メソッドを区別することもあります。nullnull

パフォーマンスに関しては、非常にレイテンシが重要なコードを記述していない限り、パフォーマンスのオーバーヘッドは無視できます。ただし、try / catch ブロックに関しては、いくつかのベスト プラクティスがあることに注意してください。たとえば、Effective Java では、ループの外側に try / catch ブロックを作成して、反復ごとにブロックを作成しないようにすることを推奨していると思います。例えば

boolean done = false;

// Initialise loop counter here to avoid resetting it to 0 if an Exception is thrown.
int i=0;    

// Outer loop: We only iterate here if an exception is thrown from the inner loop.
do {
  // Set up try-catch block.  Only done once unless an exception is thrown.    
  try {
    // Inner loop: Does the actual work.
    for (; i<1000000; ++i) {
      // Exception potentially thrown here.
    }

    done = true;
  } catch(Exception ex) {
    ... 
  }
} while (!done);
于 2010-02-02T15:03:30.247 に答える
1

例外を作成し、(スタック トレースを使用して) 例外をスローし、例外をキャッチしてから、その例外を (最終的に) ガベージ コレクションすることは、単純な if チェックを行うよりもはるかに遅くなります。

最終的には、おそらくスタイルに帰着することができますが、それは非常に悪いスタイルだと思います.

于 2010-02-02T15:03:40.247 に答える
0

このような設計アプローチはパフォーマンスにどのように影響しますか?メリットはコストを上回りますか、それとも単なる悪いコーディングですか?

このプログラミングスタイルのメリットはほとんどないと言っていました。例外は(まあ...)例外として理解されるべきです。これは、通常ではない状況でのみ発生するはずです。「通常の状況」をどのように定義するかは、かなり議論の余地のある考えです...

于 2010-02-02T15:07:20.100 に答える
0

例外を忘れて、このようなパフォーマンスの問題を回避できます。これにより、API がそれ自体を代弁します。

int getUserAge(String userName) 
{
    int age = 0;
    UserSearchResult result = getUser(userName);
    if (result.getFoundUser())
    {
        User user = result.getUser();
        age = user.getAge();
    }
    return age;
}
于 2010-02-02T15:10:41.287 に答える
0

最後の編集に従って。(Tony が提案したように) ここでは NullObject パターンを使用する方が便利だと思います。

次の 3 番目のシナリオを考えてみましょう。

class NullUser extends User {

    public static NullUser nullUser = new NullUser();

    private NullUser(){}

    public int getAge() {
        return 0;
    }
}

//Later...
int age;

User user = getUser("adam"); // return nullUser if not found.
age = user.getAge();         // no catch, no if/null check, pure polymorphism
于 2010-02-02T15:21:46.303 に答える
0

例外がスローされるたびに、スタック トレースを作成してデータを入力する必要があるため、例外には多くのコストがかかります。

資金不足のために 1% のケースで失敗する残高転送操作を想像してみてください。このように故障率が比較的低い場合でも、パフォーマンスに深刻な影響が及ぶ可能性があります。ソースコードとベンチマーク結果はこちらをご覧ください。

于 2011-02-08T03:49:37.650 に答える