静的変数が悪と見なされるのはなぜですか?


635

私は企業の世界で初めてJavaプログラマーです。最近、GroovyとJava を使用してアプリケーションを開発しました。私が書いたコード全体を通して、かなりの数の静的変数を使用しました。使用した静力学の数を減らすように、上級技術ロットに依頼されました。私は同じことについてググったところ、多くのプログラマーが静的変数の使用にかなり反対していることがわかりました。

静的変数の方が使いやすいと思います。そして、私もそれらが効率的であると思います(間違っている場合は修正してください)。クラス内の関数を10,000回呼び出す必要がある場合、メソッドを静的にClass.methodCall()して、代わりに単純なメソッドを使用するのがうれしいからです。クラスの10,000個のインスタンスでメモリを乱雑にしますよね?

さらに、静力学はコードの他の部分への相互依存を減らします。彼らは完璧な国家の保持者として行動することができます。これに加えて、静的はSmalltalkScalaなどの一部の言語で広く実装されていることがわかりました。それでは、なぜ静的に対するこの抑圧がプログラマの間で(特にJavaの世界では)普及しているのでしょうか。

PS:静力学に関する私の仮定が間違っている場合は、私を修正してください。


43
ただ言うために、SmalltalkやScalaには静的な変数やメソッドはありません。静的なメソッドや変数はOOPの原則に反しているからです。
マウリシオ・リニャレス

87
作成した少なくとも1つのステートメントは、「奇妙なことに、コードの他の部分への相互依存性を減らす」というかなり興味深いものです。一般に、それらは依存関係を強化します。呼び出しが行われるコードは、呼び出されたコードに非常に緊密にバインドされています。直接依存関係の抽象化はありません。
Arne Deutsch

11
良い質問...もっとProgrammers.SEの質問ですか?
WernerCD、2011

26
2番目の段落は、まったく異なる主題、つまり静的メソッドに関するものです。
ポール

8
関数型プログラミングは、グローバルな状態にも対応できません。あなたの場合は今まで(とあなたがすべき 1日のFPに入る)、グローバルな状態の概念を捨てるために準備すること。
new123456 2011

回答:


689

静的変数はグローバルな状態を表します。それを推論するのも、テストするのも難しいです。オブジェクトの新しいインスタンスを作成すると、テストでその新しい状態を推論できます。静的変数を使用しているコードを使用する場合、それは任意の状態である可能性があり、何でもそれを変更している可能性があります。

私はしばらく続けることができましたが、考えるべきより大きな概念は、何かの範囲が狭ければ狭いほど、それは推論しやすくなるということです。私たちは小さなことを考えるのは得意ですが、モジュール性がない場合、100万行のシステムの状態を推測するのは困難です。ちなみにこれは、静的変数だけでなく、あらゆる種類の物に当てはまります。


57
コードがテスト可能かどうかにかかわらず、それは最近の議論のようです。それはかなり欠陥のある推論です。議論は「良いデザイン」であるべきで、通常、良いデザインはテスト可能です。しかし、その逆ではありません。「そのため、テストすることはできません。これは、設計が悪いためです。」誤解しないでください。私はあなたの投稿に概ね同意します。
M Platvoet 2011

144
@M Platvoet:他の点では同等に有効な2つの設計から選択した場合、テスト可能な方が優れていると言えます。確かにテスト可能であることは、適切に設計されていることと同じではありませんが、テストできない優れたデザインに出くわすことはめったになく、テスト可能性を優れたデザインに向けた汎用の指標とすることに問題がないほど十分に珍しいと思います。
Jon Skeet

9
@M Platvoet-テスト容易性は、保守性と信頼性の両方に影響します。私は、設計の品質におけるそれらの主要な要素を検討します。確かにそれらだけが要因ではありませんが、特定のコードのコストは、マシンサイクル、開発者サイクル、およびユーザーサイクルの組み合わせです。テスト容易性は、これら3つのうち2つに当てはまります。
Justin Morgan

5
@M Platvoet-分離されたクラスは一般的に再利用しやすいため、テスト容易性は再利用性にも影響を与える傾向があります。
TrueWillは

13
M Platvoet-ここでの最初のコメントに同意しません。テストできないものがあるとしたら、それは悪い設計です。私がそれをテストすることができないなら、私はそれが機能することを知ることができないからです。営業担当者から「このモデルの設計ではテストできないため、実際に走るかどうかわかりません」と言われたら、車を購入しますか?テスト容易性は、ソフトウェア(および自動車)にとって非常に重要であり、その優れた設計には、それが含まれることが要求されます。
Dawood ibnカリーム2011

277

あまりオブジェクト指向ではありません。 静力学が一部の人々によって「悪」と見なされる理由の1つは、オブジェクト指向のパラダイムに反していることです。特に、データがオブジェクトにカプセル化されるという原則に違反します(拡張可能、情報の隠蔽など)。静的については、それらを使用して記述しているように、スコープなどの問題に対処することを避けるために、基本的には静的変数をグローバル変数として使用します。ただし、グローバル変数は手続き型または命令型プログラミングパラダイムを定義する特性の1つであり、「優れた」オブジェクト指向コードの特性ではありません。これは、手続きのパラダイムが悪いと言っているのではありませんが、上司が「優れたオブジェクト指向コード」を書いていて、本当に書こうとしていると期待しているような印象を受けます

Javaには、常にすぐにわかるとは限らないstaticの使用を開始したときに、多くの問題があります。たとえば、同じVMでプログラムの2つのコピーが実行されている場合、それらは静的変数の値を断片化し、互いの状態を台無しにしますか?または、クラスを拡張するとどうなりますか?静的メンバーをオーバーライドできますか?非常に多くの静的データがあり、そのメモリを他の必要なインスタンスオブジェクトに回収できないため、VMのメモリが不足していますか?

オブジェクトの 存続期間さらに、統計には、プログラムの実行時間全体に一致する存続期間があります。つまり、クラスの使用が終了しても、静的変数のメモリはガベージコレクションされません。たとえば、代わりに、変数を非静的にして、main()関数でクラスの単一のインスタンスを作成し、10,000回の呼び出しが行われた後で、クラスに特定の関数を10,000回実行するように要求した場合、および単一インスタンスへの参照を削除すると、すべての静的変数がガベージコレクションされて再利用される可能性があります。

特定の再利用の防止: また、静的メソッドを使用してインターフェイスを実装することはできないため、静的メソッドは特定のオブジェクト指向機能が使用できないようにする可能性があります。

その他のオプション: 効率が主な関心事である場合、速度の問題を解決するには、通常、作成よりも呼び出しの方が高速であるという利点のみを考慮するよりも、他のより良い方法があるかもしれません。一時的または揮発性修飾子がどこでも必要かどうかを検討してください。インライン化する機能を維持するために、メソッドを静的ではなく最終としてマークすることができます。メソッドのパラメーターやその他の変数をfinalとマークして、これらの変数を変更できるものについての仮定に基づいて特定のコンパイラーの最適化を許可できます。インスタンスオブジェクトは、毎回新しいインスタンスを作成するのではなく、複数回再利用できます。アプリ全体でオンにする必要があるコンパイラ最適化スイッチがある場合があります。おそらく、10,000回の実行をマルチスレッド化してマルチプロセッサコアを利用できるように設計を設定する必要があります。移植性がない場合

何らかの理由でオブジェクトの複数のコピーが不要な場合は、シングルトン設計パターンは、スレッドセーフ(シングルトンが適切にコーディングされていると想定)などの静的オブジェクトよりも優れており、遅延初期化を許可し、オブジェクトが使用時に適切に初期化されていることを保証し、サブクラス化、コードのテストとリファクタリングにおける利点、言うまでもなく、ある時点でオブジェクトの1つのインスタンスのみが必要であるという考えを変える場合、インスタンス変数を使用するようにすべての静的変数コードをリファクタリングするよりも、重複するインスタンスを防ぐためにコードを削除する方がはるかに簡単です。私は前にそれをしなければならなかった、それは面白くない、そしてあなたはより多くのクラスを編集しなければならず、それは新しいバグを導入するリスクを増大させる...物事を最初に「正しく」設定するのにずっと良い、欠点があるように見えても。私のために、何かの複数のコピーが必要であるとすぐに判断した場合に必要な再作業は、おそらくできるだけ頻繁に統計を使用しない最も説得力のある理由の1つです。したがって、静的は相互依存性を減らすというあなたの声明にも同意しません。「方法を知っているオブジェクト」ではなく、直接アクセスできる静的オブジェクトがたくさんある場合、より結合されたコードになると思います。何か」自体に。


11
私はあなたの答えが好きです。それは、同時実行性やスコープのようないくつかの赤いニシンではなく、静力学の周りで検討する適切なトレードオフに焦点を当てていると思います。そして、シングルトンの+1は、静的変数/メソッドとシングルトンの
どちら

2
シングルトン自体は(たとえば、synchronizedメソッドを使用して)スレッドセーフである可能性がありますが、呼び出しコードにシングルトン状態に関する競合状態がないという意味ではありません。
アンドレ・キャノン

8
また、静力学はOOPパラダイムに反しません。多くのOOP狂信者は、クラスはオブジェクトであり、静的メソッドはインスタンスではなくクラスオブジェクトのメソッドであると説明します。この現象はJavaではあまり見られません。Pythonなどの他の言語では、クラスを変数として使用でき、そのオブジェクトのメソッドとして静的メソッドにアクセスできます。
アンドレ・キャノン

4
3つ目の段落の最後の行は、私が間違っていなければ、すべての非静的変数を読む必要があります
スティーブ

1
Object Lifetimeは、@ jessicaが言及した非常に重要なポイントの1つです。
Abhidemon 2017年

93

悪は主観的な用語です。

作成と破棄の観点から静力学を制御しません。彼らはプログラムのロードとアンロードの要請で生きています。

staticは1つのスペースに存在するため、それらを使用するすべてのスレッドは、管理する必要があるアクセス制御を通過する必要があります。これは、プログラムがより結合されていることを意味し、この変更は(Jスキートが言っているように)予想および管理が困難です。これは、変更の影響を分離するという問題につながり、テストの管理方法に影響します。

これらは、私が抱えている2つの主要な問題です。


59

いいえ。グローバルステート自体は悪ではありません。しかしコードを適切に使用したかどうかを確認する必要があります。初心者がグローバルステートを悪用する可能性は十分にあります。彼がすべての言語機能を悪用するのと同じように。

グローバルな状態は絶対に必要です。グローバルな状態は避けられません。グローバルな状態についての推論は避けられません。-アプリケーションのセマンティクスを理解する必要がある場合。

そのためにグローバルな状態を取り除こうとする人々は必然的にはるかに複雑なシステムになり、グローバルな状態は依然としてそこに存在し、多くの間接的な層の下で巧妙に/慣習的に偽装されています。そして、すべての間接参照をアンラップした後も、グローバルな状態について推論する必要があります。

グローバル状態をxmlで惜しみなく宣言し、どういうわけかそれを優れていると考える春の人々のように。

@Jon Skeetにif I create a new instance of an objectは、2つの理由があります。オブジェクト内の状態と、オブジェクトをホストしている環境の状態です。


10
「私には2つの理由があります」。テストをオブジェクトの状態のみに依存させるのではありません。どちらがより簡単で、私が持っているよりグローバルな状態ではありません。
DJClayworth 2011

2
依存性注入は、グローバルな状態やグローバルな可視性とは何の関係もありません。コンテナ自体もグローバルではありません。「通常の」コードと比較して、コンテナ管理オブジェクトが目に見える唯一の余分なものは、コンテナ自体です。実際、DIはシングルトンパターンを回避するために非常に一般的に使用されています。
Floegipoky 2013年

31

静的変数には2つの主な問題があります。

  • スレッドセーフ-静的リソースは当然スレッドセーフではありません
  • コードの暗黙性-静的変数がインスタンス化されるタイミングと、別の静的変数の前にインスタンス化されるかどうかがわかりません

Jon Skeetはあなたが投稿したのと同じコメントを参照していたと思います。
RG-3

13
スレッドセーフポイントは取得できません。そうしないと、スレッドセーフになるものはないと思います。これは静的なものとはまったく関係がないようです。何か足りない場合は修正してください。
Zmasterは、2011

1
@Zmaster -それはスレッドの安全性は、静的変数に発行排他的ではないことは確かですが、その定義によって、彼らからして異なるコンテキストで呼び出されるので、彼らは彼らに多くのプルーンです
sternr

2
@sternr意味がわかります。「異なるコンテキスト」が必ずしも「異なるスレッド」と等しくない場合のイベントです。ただし、静的リソースではスレッドセーフを考慮する必要があることがよくあります。文章を明確にすることを検討してください。
Zmaster

たとえば、静的リソースの有効なスレッドセーフな使用があります。プライベート静的最終ロガーLOG = Logger.getLogger(Foo.class); プライベート静的最終AtomicInteger x =新しいAtomicInteger(0); 私が理解しているように、このようなリソースの静的割り当ては、クラスローダーによってスレッドセーフが保証されています。ロガーインスタンスは、どこにポインターを割り当てるかに関係なく、スレッドセーフであるか、スレッドセーフではありません。静的状態を維持することはおそらく良い考えではありませんが、スレッドセーフではない理由はありません。
teknopaul 2015

29

「static」キーワードを「final」キーワードなしで使用している場合、これは設計を慎重に検討するための合図となるはずです。変更可能な静的最終オブジェクトも同様に危険である可能性があるため、「最終」の存在でさえフリーパスではありません。

私は「ファイナル」なしで「スタティック」を見る時間の約85%を見積もるでしょう、それは間違っています。多くの場合、私はこれらの問題を隠蔽または非表示にする奇妙な回避策を見つけます。

静的な可変を作成しないでください。特にコレクション。一般に、コレクションは、それらが含まれているオブジェクトが初期化されるときに初期化され、それらが含まれているオブジェクトが忘れられたときにリセットまたは忘れられるように設計する必要があります。

静力学を使用すると、非常に微妙なバグが発生する可能性があり、エンジニアに何日も苦痛を強いることになります。私が知っているのは、私はこれらのバグを作成し、追跡したからです。

詳細については、次をお読みください...

Staticsを使用しないのはなぜですか?

テストの作成と実行、およびすぐには明らかではない微妙なバグなど、静的に関する多くの問題があります。

静的オブジェクトに依存するコードは簡単に単体テストすることはできず、静的オブジェクトは(通常)簡単にモック化することはできません。

静的を使用する場合、より高いレベルのコンポーネントをテストするためにクラスの実装を交換することはできません。たとえば、データベースからロードするCustomerオブジェクトを返す静的CustomerDAOを想像してください。これで、CustomerFilterクラスができました。これは、いくつかのCustomerオブジェクトにアクセスする必要があります。CustomerDAOが静的な場合、最初にデータベースを初期化して有用な情報を入力しないと、CustomerFilterのテストを作成できません。

また、データベースの作成と初期化には長い時間がかかります。私の経験では、DB初期化フレームワークは時間とともに変化します。つまり、データが変化し、テストが失敗する可能性があります。IEは、顧客1が以前はVIPでしたが、DB初期化フレームワークが変更され、顧客1がVIPでなくなったとしますが、テストは顧客1をロードするようにハードコーディングされました…

より良いアプローチは、CustomerDAOをインスタンス化し、構築時にCustomerFilterに渡すことです。(さらに優れたアプローチは、Springまたは別のInversion of Controlフレームワークを使用することです。

これを行うと、CustomerFilterTestで代替DAOをすばやくモックまたはスタブ化できるため、テストをより詳細に制御できます。

静的DAOがない場合、テストはより高速になり(db初期化なし)、信頼性が高くなります(db初期化コードが変更されても失敗しないため)。たとえば、この場合、テストに関する限り、顧客1がVIPであり、常にVIPになります。

テストの実行

一連の単体テストを一緒に実行すると(たとえば、継続的インテグレーションサーバーで)、静力学は実際の問題を引き起こします。あるテストから別のテストまで開いたままのネットワークソケットオブジェクトの静的マップを想像してください。最初のテストではポート8080でソケットを開く可能性がありますが、テストが終了したときにマップをクリアするのを忘れていました。これで、2番目のテストが起動したときに、ポート8080がまだ占有されているため、ポート8080の新しいソケットを作成しようとするとクラッシュする可能性があります。また、静的コレクション内のソケット参照は削除されず、(WeakHashMapを除いて)ガベージコレクションの対象とはならず、メモリリークが発生することも想像してください。

これは過度に一般化された例ですが、大規模なシステムでは、この問題は常に発生します。同じJVMで繰り返しソフトウェアの開始と停止を行う単体テストについては考えられませんが、これはソフトウェア設計の優れたテストであり、高可用性への願望がある場合は、注意が必要です。

これらの問題は、多くの場合、フレームワークオブジェクト(DBアクセス、キャッシング、メッセージング、ロギングレイヤーなど)で発生します。Java EEまたはいくつかの最高のフレームワークを使用している場合、おそらくこれらの多くを管理しますが、私のようにレガシーシステムを扱っている場合、これらのレイヤーにアクセスするためのカスタムフレームワークがたくさんあるかもしれません。

これらのフレームワークコンポーネントに適用されるシステム構成がユニットテスト間で変更され、ユニットテストフレームワークがコンポーネントを破棄して再構築しない場合、これらの変更は有効にならず、テストがそれらの変更に依存していると失敗します。

フレームワーク以外のコンポーネントでもこの問題の影響を受けます。OpenOrdersという静的マップを想像してください。いくつかの未処理注文を作成する1つのテストを記述し、それらがすべて正しい状態にあることを確認してから、テストを終了します。別の開発者は、必要な注文をOpenOrdersマップに入れる2番目のテストを記述し、注文の数が正確であることを表明します。個別に実行すると、これらのテストは両方とも成功しますが、スイートで一緒に実行すると失敗します。

さらに悪いことに、失敗はテストが実行された順序に基づいている可能性があります。

この場合、静的要素を回避することで、テストインスタンス間でデータが保持されるリスクを回避し、テストの信頼性を向上させます。

微妙なバグ

高可用性環境、またはスレッドが開始および停止される可能性のある場所で作業している場合、コードが本番環境で実行されているときにも、上記の単体テストスイートに関する同じ懸念が当てはまります。

静的オブジェクトを使用してデータを格納するのではなく、スレッドを処理する場合は、スレッドの起動フェーズ中に初期化されたオブジェクトを使用することをお勧めします。このようにして、スレッドが開始されるたびに、オブジェクトの新しいインスタンス(新しい構成となる可能性があります)が作成され、スレッドの1つのインスタンスから次のインスタンスへのデータの流出を防ぎます。

スレッドが停止しても、静的オブジェクトはリセットされず、ガベージコレクションも行われません。「EmailCustomers」というスレッドがあり、それが開始すると、静的なStringコレクションに電子メールアドレスのリストが入力され、各アドレスへの電子メールの送信を開始するとします。スレッドが何らかの理由で中断またはキャンセルされたため、高可用性フレームワークがスレッドを再起動するとします。次に、スレッドが起動すると、顧客のリストが再ロードされます。ただし、コレクションは静的であるため、前のコレクションのメールアドレスのリストを保持する場合があります。現在、一部のお客様は重複し​​たメールを受け取る可能性があります。

余談:静的ファイナル

「static final」の使用は、技術的な実装の違いはありますが、Cの#defineと実質的にJavaで同等です。AC / C ++ #defineは、コンパイル前にプリプロセッサによってコードからスワップアウトされます。Javaの「静的ファイナル」は、スタックに常駐するメモリになります。このように、それは#defineよりもC ++の「静的const」変数に似ています。

概要

これが静力学が問題になるいくつかの基本的な理由の説明に役立つことを願っています。Java EEやSpringなどの最新のJavaフレームワークを使用している場合は、これらの状況の多くは発生しない可能性がありますが、レガシーコードの大きな本体で作業している場合は、はるかに頻繁になる可能性があります。


15

誰もそれについて言及していないので*:同時実行性。静的変数を読み書きする複数のスレッドがある場合、静的変数は驚くかもしれません。これはWebアプリケーション(ASP.NETなど)で一般的であり、かなり厄介なバグを引き起こす可能性があります。たとえば、ページによって更新される静的変数があり、そのページが「ほぼ同時に」2人から要求された場合、一方のユーザーが他方のユーザーが期待する結果を得るか、それより悪い結果になることがあります。

staticは、コードの他の部分への相互依存を減らします。彼らは完璧な国家の保持者として行動することができます

ロックを使用して競合に対処する準備ができていることを願っています。

*実際には、プリートサンガはそれについて言及しました。


5
インスタンス変数には、静的変数よりスレッドセーフな利点はなく、すべて保護されていない変数です。代わりに、これらの変数にアクセスするコードを保護する方法がすべてです。
studgeek 2011

2
私はその主張を完全にはしませんでしたが、議論のために:分離は保護の一形態です。スレッドの状態は分離されています。グローバルな状態ではありません。インスタンス変数は、スレッド間で明示的に共有されない限り、保護を必要としません。静的変数は常にプロセス内のすべてのスレッドで共有されます。
ジャスティンM.キーズ

スレッドスタティック変数がファーストクラスのコンセプトであるといいのですが、ラッピングのすべての層に情報を渡さなくても、ラップされたサブルーチン呼び出しで情報を安全に利用できるようにするのに非常に役立ちます。たとえば、オブジェクトにスレッドの現在のグラフィックスコンテキストにレンダリングするメソッドがあり、現在のグラフィックスコンテキストを保存/復元するメソッドがある場合、それらを使用すると、すべてのメソッド呼び出しでグラフィックスコンテキストを渡さなければならないよりもクリーンなことがよくあります。
スーパーキャット2014年

15

Javaで静的メソッドを使用することのいくつかの基本的な長所と短所を要約すると、次のようになります。

利点:

  1. グローバルにアクセス可能、つまり特定のオブジェクトインスタンスに関連付けられていません。
  2. JVMごとに1つのインスタンス。
  3. クラス名を使用してアクセスできます(オブジェクトは必要ありません)。
  4. すべてのインスタンスに適用可能な単一の値が含まれています。
  5. JVMの起動時にロードされ、JVMがシャットダウンすると終了します。
  6. オブジェクトの状態は変更されません。

短所:

  1. 静的メンバーは、使用中かどうかにかかわらず、常にメモリの一部です。
  2. 静的変数の作成と破棄を制御することはできません。便利なことに、それらはプログラムのロード時に作成され、プログラムのアンロード時に(またはJVMがシャットダウンしたときに)破棄されます。
  3. 同期を使用して静的スレッドをスレッドセーフにすることができますが、追加の作業が必要です。
  4. 1つのスレッドが他のスレッドの機能を破壊する可能性のある静的変数の値を変更した場合。
  5. 使用する前に「静的」を知っておく必要があります。
  6. 静的メソッドをオーバーライドすることはできません。
  7. シリアライゼーションはそれらではうまく機能しません。
  8. 彼らは実行時のポリモーフィズムには参加しません。
  9. 多数の静的変数/メソッドが使用されている場合、メモリの問題があります(ある程度ではあるが、私はそうは思わない)。プログラムが終了するまでガベージコレクションされないためです。
  10. 静的メソッドもテストするのは難しいです。

欠点6、7、8、10は、使用される言語/フレームワークの欠点であり、一般に静的変数の欠点ではありません。フレームワークによって提供されるシングルトンパターンなど、他のソリューションにも欠点1、4、5が存在します。(私は残りに同意し、それが素晴らしいコレクションであるため、私は回答に投票しませんでした。)
peterh-Reinstate Monica

13

クラス内の関数に10,000回の呼び出しを行う必要がある場合、メソッドを静的にして、クラスの10,000個のインスタンスでメモリを乱雑にするのではなく、単純なclass.methodCall()を使用してよろしいですか?

データをオブジェクトの状態にカプセル化する必要性と、一部のデータの関数の結果を単純に計算する必要性のバランスをとる必要があります。

さらに、静力学はコードの他の部分への相互依存を減らします。

カプセル化もそうです。大規模なアプリケーションでは、静的関数はスパゲッティコードを生成する傾向があり、リファクタリングやテストを簡単に行うことができません。

他の回答も、静態の過度の使用に対する正当な理由を提供します。


13

静的変数はグローバルな状態を表すため、一般に悪いと考えられており、そのため推論がはるかに困難です。特に、それらはオブジェクト指向プログラミングの前提を破ります。オブジェクト指向プログラミングでは、各オブジェクトにはインスタンス(非静的)変数で表される独自の状態があります。静的変数は、インスタンス全体の状態を表し、単体テストがはるかに困難になる可能性があります。これは主に、静的変数への変更を単一のテストに分離することがより難しいためです。

そうは言っても、通常の静的変数(一般的には悪いと考えられています)と最終的な静的変数(AKA定数、それほど悪くはない)を区別することが重要です。


4
「静的変数はクラス全体の状態を表す」...「静的変数はインスタンス全体の状態を表す」という意味ですか?「最後の静的AKA定数、それほど悪くない」の場合は+1。値は変更できないため、ある時点でそれに依存するものは、後で暗黙的にその動作を変更することはできません。値は同じです。
Jared Updike

「静的変数はインスタンス全体の状態を表します」は、それを述べるより良い方法です。回答を編集しました。
Jack Edmonds

9

私の意見では、パフォーマンスについてはほとんど問題ではなく、デザインについてです。静的メソッドの使用は、静的変数の使用のせいで間違っているとは考えていません(ただし、実際にメソッド呼び出しについて話していると思います)。

それは単にロジックを分離してそれを良い場所にする方法についてです。静的なメソッドを使用することが正当化される場合もありjava.lang.Mathます。ほとんどのクラスに名前を付けるときXxxUtilXxxhelper、デザインを再考した方がいいと思います。


3
純粋な副作用のない静的メソッドは完全に良いIMOです。しかし、グローバルな可変状態はめったにありません。私はOPをグローバル状態について話していると解釈します。
CodesInChaos

1
@CodeInChaosは完全に同意します。OPは静的メソッドと変数の違いについて完全に明確ではないことがわかりました。
M Platvoet 2011

8

私は答えでなされたポイントのいくつかを要約しました。何か問題を見つけた場合は、遠慮なく修正してください。

スケーリング: JVMごとに静的変数のインスタンスが1つだけあります。ライブラリ管理システムを開発していて、1冊の本に1つしかないため、bookの名前を静的変数にすることにしたとします。しかし、システムが成長して複数のJVMを使用している場合、どの本を扱っているのかを把握する方法がありません。

スレッドセーフ:マルチスレッド環境で使用する場合、インスタンス変数と静的変数の両方を制御する必要があります。ただし、インスタンス変数の場合、スレッド間で明示的に共有されない限り、保護は必要ありませんが、静的変数の場合は、プロセス内のすべてのスレッドで常に共有されます。

テスト:テスト可能なデザインは優れたデザインとは異なりますが、テストできない優れたデザインはほとんど見られません。静的変数はグローバル状態を表すため、それらをテストすることは非常に困難になります。

状態についての推論:クラスの新しいインスタンスを作成した場合、このインスタンスの状態について推論できますが、静的変数がある場合は、どのような状態でもかまいません。どうして?静的変数はインスタンス間で共有されるため、静的変数が別のインスタンスによって変更された可能性があるためです。

シリアライゼーション:シリアライゼーションもそれらではうまく機能しません。

作成と破棄:静的変数の作成と破棄は制御できません。通常、これらはプログラムのロードおよびアンロード時に作成および破棄されます。これは、それらがメモリ管理に悪いことを意味し、起動時の初期化時間も加算します。

しかし、本当に必要な場合はどうでしょうか。

しかし、時にはそれらを本当に必要とするかもしれません。アプリケーション全体で共有される多くの静的変数の必要性を本当に感じている場合、1つのオプションは、これらすべての変数を持つシングルトン設計パターンを利用することです。または、これらの静的変数を持ち、渡すことができるオブジェクトを作成することもできます。

また、静的変数がfinalとマークされている場合、それは定数になり、一度割り当てられた値は変更できません。それは、その可変性によって私たちが直面するすべての問題から私たちを救うことを意味します。


7

あなたは静的変数について質問しているように見えますが、例では静的メソッドも指摘しています。

静的変数は悪ではありません-ほとんどの場合、定数のようなグローバル変数としてfinal修飾子と組み合わせて採用されていますが、それを過度に使用しないでください。

静的メソッド、つまりユーティリティメソッド。それらを使用することは一般的に悪い習慣ではありませんが、大きな懸念はそれらがテストを妨害するかもしれないということです。

多くの静力学を使用して正しい方法で実行する優れたJavaプロジェクトの例として、Playをご覧くださいフレームワーク。SO でもそれについての議論があります。

静的変数と静的インポートを組み合わせたメソッドは、Javaでの宣言型プログラミングを容易にするライブラリで広く使用されています:make it easyまたはHamcrest。多くの静的変数とメソッドがなければ、それは不可能です。

したがって、静的変数(およびメソッド)は適切ですが、賢く使用してください!


6

静的変数は最も重要なことに、データのセキュリティに関する問題を引き起こします(いつでも変更され、誰でも変更できる、オブジェクトなしの直接アクセスなど)。

詳細については、この 感謝をお読みください。


6

静的変数を使用するほとんどの場合、本当にシングルトンパターンを使用することをお勧めします。

グローバルステートの問題は、単純なコンテキストではグローバルとして意味をなすものが、実際のコンテキストではもう少し柔軟である必要がある場合があることです。この場合、シングルトンパターンが役立ちます。


5

さらに別の理由:もろさ。

クラスがある場合、ほとんどの人はそれを作成して自由に使用できると期待しています。

あなたはそれが事実ではないことを文書化するか、またはそれから保護することができます(シングルトン/工場パターン)-しかし、それは追加の作業であり、したがって追加のコストです。それでも、大企業では、誰かがいつかあなたのクラスを使用しようとする可能性があります。

静的変数をたくさん使用している場合、それはうまくいきません。バグは高価です。

.0001%のパフォーマンス改善と、潜在的に無知な開発者による変更に対する堅牢性の間で、多くの場合、堅牢性が良い選択です。


4

静的変数の方が使いやすいと思います。そして、私もそれらが効率的であると思います(間違っている場合は修正してください)。クラス内の関数を10,000回呼び出す必要がある場合は、メソッドを静的にして、単純なclass.methodCall()を使用するのがうれしいです。クラスの10,000インスタンスでメモリを乱雑にする代わりに、そうですか?

私はあなたの考えを理解していますが、単純なシングルトンパターンは、10000個のオブジェクトをインスタンス化する必要なく同じことを行います。

静的メソッドを使用できますが、これはオブジェクトドメインに関連し、オブジェクトの内部プロパティを必要としない、または使用しない関数に対してのみです。

例:

public class WaterContainer {
    private int size;
    private int brand;
    ...etc

    public static int convertToGallon(int liters)...

    public static int convertToLiters(int gallon)...

}

従来のシングルトン(つまり、によってアクセスされるものClass.Instance)は、静的変数よりもかろうじて優れています。これは少しテストしやすいですが、コードが1つしかないという前提でコードを作成するのではなく、たまたま単一のインスタンスを作成する設計よりもはるかに悪いです。
CodesInChaos

私はあなたのコメントを理解しているかわかりません!私はOPに、10,000個のオブジェクトのインスタンス化についてイタリック体で彼が述べたことについて応答していました。シングルトンと静的変数を比較する理由がわかりませんか?あなたが書いたことから私が理解することは、シングルトンは悪いデザインだということです...!Spring FrameworkはデフォルトですべてのBeanをシングルトンにするので、私はあなたを誤解していると思います;-)
Cygnusx1

Class.Instance変更可能な状態を運ぶクラシックシングルトン(を持つ)は、設計が悪いIMOです。その場合、使用する必要のあるシングルトンを、それらを使用するクラスに(通常はDIを使用して)渡されるように設計することを強く推奨します。論理的に不変のクラシックシングルトンは、すばらしいIMOです。
CodesInChaos

@ Cygnusx1クラスシングルトン(クラスが単一のコピーであることを保証するシングルトン)が簡単にテストできなかった理由が明確でない場合は、クラスの存在をプログラムのライフサイクルに密接に結び付けます。これをテストするには、プログラムの起動とシャットダウンを厳守する必要があります。これは、多くの場合、クラスのテストには重要でない副作用があります。事実上シングルトン(プログラムでは1つのコピーですが、それ以外の場合は適用されません)の場合は、プログラムなしでテスト時に複数のコピーを作成し、クラス全体の動作が各テストシナリオで想定どおりであることを確認できます。
エドウィンバック

4

「Statistics be evil」の問題は、グローバルステートについての問題です。変数が静的であるのに適切なタイミングは、変数に複数の状態がない場合です。フレームワーク全体からアクセス可能で、同じメソッド呼び出しに対して常に同じ結果を返すIEツールは、静的データとして「悪」になることはありません。あなたのコメントについて:

静的変数の方が使いやすいと思います。そして、私もそれらが効率的であると思います

Staticsは、変化しない変数/クラスにとって理想的で効率的な選択肢です

グローバル状態の問題は、それが作り出すことができる固有の不整合です。ユニットテストに関するドキュメントでは、この問題に対処することがよくあります。関係のない複数のオブジェクトからアクセスできるグローバルな状態が存在する場合は常に、ユニットテストは不完全であり、「ユニット」の粒度ではありません。このグローバルステートとシングルトンに関する記事で述べたように、オブジェクトAとBが関連していない場合(あるオブジェクトが別のオブジェクトに明示的に参照されていない場合など)、AはBの状態に影響を与えることができません。

クロックなど、適切なコードでのグローバルな禁止状態にはいくつかの例外があります。時間はグローバルであり、ある意味で、コード化された関係なしにオブジェクトの状態を変更します。


「時間はグローバル」-コンピューティングシステムで時間をモデル化する方法は、それ自体が変化する暗黙的でグローバルなものにする方法以外にもあります。cf. この調査:「コンピューティングにおけるモデリング時間:分類と比較調査」@ arxiv.org/abs/0807.4132
Jared Updike

4

私の$ .02は、これらの回答のいくつかが問題を混乱させているということです。「静的は悪い」と言うのではなく、スコープとインスタンスについて話す方が良いと思います。

静的とは「クラス」変数であり、そのクラスのすべてのインスタンスで共有される値を表すということです。通常は、そのようにスコープを設定する必要があります(クラスとそのインスタンスに対して保護またはプライベート)。

クラスレベルの振る舞いを取り囲んで他のコードに公開する場合は、シングルトンが将来の変更をサポートするためのより良い解決策になる可能性があります(@Jessicaが提案したとおり)。これは、クラスレベルでは使用できない方法、特に継承では、インスタンス/シングルトンレベルでインターフェイスを使用できるためです。

他の回答の一部の側面が質問の中心になっていないと思う理由についてのいくつかの考え...

静力学は「グローバル」ではありません。Javaでは、スコープは静的/インスタンスとは別に制御されます。

同時実行性は、インスタンスメソッドよりも静的に対して危険ではありません。まだ保護する必要がある状態です。確かに、インスタンス変数が1つだけあり、静的変数が1つしかないインスタンスが1000ある可能性がありますが、どちらかにアクセスするコードがスレッドセーフな方法で記述されていない場合は、まだねじ込まれています。 。

ライフサイクルの管理は興味深い議論ですが、それほど重要ではないと思います。シングルトンインスタンスの作成と破棄よりもinit()/ clear()のようなクラスメソッドのペアの管理が難しい理由はわかりません。実際、GCのためにシングルトンはもう少し複雑だと言う人もいます。

PS、Smalltalkの観点では、その方言の多くはクラス変数を持っていますが、Smalltalkのクラスは実際にはメタクラスのインスタンスであるため、実際にはメタクラスインスタンスの変数です。それでも、同じ経験則を適用します。インスタンス間で状態を共有するために使用されている場合は問題ありません。それらがパブリック機能をサポートしている場合は、シングルトンを確認する必要があります。ため息、私はSmalltalkを見逃している...


4

投稿には2つの主な質問があります。

まず、静的変数について。静的変数は完全に不要であり、その使用は簡単に回避できます。一般的にOOP言語、特にJavaでは、関数パラメーターは参照によってパスされます。つまり、オブジェクトを関数に渡す場合、オブジェクトへのポインターを渡すため、静的変数を定義する必要はありません。この情報を必要とする任意のスコープにオブジェクトへのポインターを渡すことができます。これがあなたのメモリをポインタで満たすことを示唆しているとしても、実際のメモリページングシステムはこれを処理するように最適化されているため、パフォーマンスの低下を意味するわけではなく、新しいポインタに渡されたポインタによって参照されるページをメモリに保持します範囲; 静的変数を使用すると、システムは、アクセスする必要があるときに、それらが格納されているメモリページをロードする可能性があります(これは、ページに長時間アクセスしていない場合に発生します)。すべての静的なスタフをいくつかの小さな「構成クラス」にまとめることをお勧めします。これにより、システムがすべてのメモリを同じメモリページに配置できるようになります。

第二に、静的メソッドについて。静的メソッドはそれほど悪くはありませんが、すぐにパフォーマンスが低下する可能性があります。たとえば、クラスの2つのオブジェクトを比較し、どちらのオブジェクトが大きいかを示す値(通常の比較メソッド)を考えるメソッドについて考えます。このメソッドは静的である場合とそうでない場合がありますが、呼び出すと非静的なフォームの方が効率的です。同じメソッドの静的バージョンを解決する必要がある3つの参照(クラスに1つと2つ、各オブジェクトに1つ)に直面する2つの参照(各オブジェクトに1つ)のみを解決する必要があるためです。しかし、私が言うように、これはそれほど悪くはありません。Mathクラスを見ると、静的メソッドとして定義された多くの数学関数を見つけることができます。これは、数値を定義するクラスにこれらのすべてのメソッドを配置するよりも効率的です。

結論:静的変数の使用を避け、静的または非静的メソッドを処理するときに正しいパフォーマンスの均衡を見つけます。

PS:私の英語でごめんなさい。


4

静的変数自体には何も問題はありません。壊れているのは、Java構文だけです。各Javaクラスは、実際には2つの構造を定義します。静的変数をカプセル化するシングルトンオブジェクトとインスタンスです。両方を同じソースブロックで定義するのは悪であり、コードが読みにくくなります。Scalaはそれを正しく行いました。


3

a)プログラムに関する理由。

静的変数Global.fooにアクセスする小規模から中規模のプログラムがある場合、通常、その呼び出しはどこからも行われません-パスがないため、タイムラインもありません。使用されている。次に、それを実際の値に設定した人を知るにはどうすればよいですか?今すぐ変更するとどうなりますか?すべてのアクセスを収集し、何が起こっているのかを知るために、ソース全体をgrepしました。

使い方を知っていれば、コードを書いただけなので問題は見えませんが、外国のコードを理解しようとすれば理解できます。

b)本当に1つだけ必要ですか?

多くの場合、静的変数は、同じ種類の複数のプログラムが異なる値で同じJVMで実行されるのを防ぎます。多くの場合、プログラムの複数のインスタンスが有用である使用法を予測しませんが、それが進化した場合、または他の人に有用である場合、プログラムの複数のインスタンスを開始したいという状況を経験する可能性があります。

多くの人が長期間にわたって集中的に使用することのない、多かれ少なかれ役に立たないコードだけが、静的変数に適しています。


3

すべて(できる:)には目的があります。データを共有/キャッシュする必要のあるスレッドと、すべてのアクセス可能なメモリ(1つのJVM内のコンテキストに分割しないでください)がある場合は、静的な方法が最適

です->もちろん、強制することもできます1つのインスタンスだけですが、なぜですか?
私はこのスレッドでいくつかのコメントを見つけました、静的ではありません;)


3

静的変数は良くも悪くもありません。これらは、特定のインスタンスではなく、クラス全体を表す属性を表します。特定のクラスのすべてのインスタンスのカウンターが必要な場合は、静的変数が値を保持する適切な場所になります。

インスタンス関連の値を保持するために静的変数を使用しようとすると、問題が発生します。


2

上記のすべての回答は、静力学が悪い理由を示しています。それらが悪である理由は、実際にはそうではないのに、オブジェクト指向のコードを書いているという誤った印象を与えるためです。それは単なる悪です。


1
しかし、コードを任意の標準パラダイムに従うように厳密に検討することで、実際にコードが改善されるのでしょうか、それとも、機能するコードを書くことだけを避けるように文句を言うのですか?
Zoey

はい、それはそれをよりよくします、なぜならそれは将来それをより扱いやすくし、より理解しやすく、そしてより明確にするからです。
ブロックヘッド、2011

1
OOコードを書かないのはなぜ悪いのですか?そしてなぜBjarne Stroustrupはあなたに同意しないのですか?1つだけ名前を付けます...
ローンヌ侯爵、2013年

2
私はオブジェクト指向のコードを書かないのは悪いことだとは言いませんでした。静的メソッドとプロパティの背後にあるグローバルを偽装しているだけで、オブジェクト指向のコードを書いていると考えるのは悪いことだと私は言いました。私が書いたことをもう一度読んでください。
blockhead 2013年

2

ここに良い答えがたくさんあります、それに加えて、

メモリ:静的変数は、クラスローダーが存続する限り存続します(通常、VMが終了するまで)。

モジュール化:IOC、dependencyInjection、プロキシなどの概念を検討してください。すべては完全に密結合/静的実装に反対しています。

その他の短所:スレッドセーフ、テスト容易性


0

多くのユーザーを持つアプリケーションがあり、静的フォームを定義している場合、すべてのユーザーが他のユーザーの他のすべてのフォームも変更すると考えてください。


0

staticキーワードでグローバル変数を過度に使用すると、アプリケーションのある時点でメモリリークが発生すると思います


0

私の観点から見ると、static変数は、慣例によって作成されたデータまたは変数のみを読み取る必要があります

たとえば、あるプロジェクトのUIがあり、国、言語、ユーザーの役割などのリストがあります。また、このデータを整理するクラスがあります。このリストがないとアプリは動作しないことを確信しています。したがって、アプリの初期化で最初に行うのは、このリストの更新を確認し、このリストをapiから取得することです(必要な場合)。したがって、このデータは「常に」アプリに存在することに同意します。これは実質的に読み取り専用のデータなので、状態を管理する必要はありません。この場合を考えると、これらのデータのインスタンスを多くしたくないので、静的な候補として最適です。


0

私は静力学をたくさん使ってきましたが、少し違う答えを出すことができますか、それとも少し異なる方法でそれを見ることができますか?

クラス(メンバーとメソッドの両方)で静的を使用したとき、最終的に自分のクラスが実際には責任を共有する2つのクラスであることに気付き始めました。シングルトンのように機能する「静的」部分があり、非-静的部分(通常のクラス)。私が知る限り、1つのクラスのすべての静的クラスと、他のクラスの非静的クラスを選択するだけで、常にこれら2つのクラスを完全に分離できます。

これは、クラスのインスタンスを保持するクラス内に静的コレクションがあり、コレクションを管理するためのいくつかの静的メソッドがあるときによく発生していました。考えてみると、クラスが「ただ1つのこと」を行っていないことは明らかです。それは、コレクションであり、まったく異なることを行っているということです。

ここで、問題を少しリファクタリングしてみましょう:クラスをすべてが静的であるクラスと「通常のクラス」である別のクラスに分割し、「通常のクラス」を忘れた場合、質問は純粋に静的クラスvsシングルトンになりますここでは詳細に説明します(おそらく他の12の質問)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.