Javaプログラミングでは、言語仕様または標準的なAPIドキュメントを通じてのみ知識を学ぶことはできません。この記事では、最も一般的に使用されているイディオム、特に推測が困難なイディオムのいくつかを収集しようとします。
この記事のすべてのコードを公共の場に入れました。資格情報なしで、好みに応じてコードスニペットをコピーして変更できます。
equals()を実装する
class person {string name; Int Birthndyyear; byte [] raw; public boolean equals(object obj){if(!obj instance of person)return false;人other =(person)obj; return name.equals(other.name)&& Birthndyear == other.birthyear && arrays.equals(raw、other.raw); } public int hashcode(){...}}パラメーターは、周辺クラスではなく、タイプのオブジェクトである必要があります。
foo.equals(null)はfalseを返す必要があり、nullpointerexceptionを投げることはできません。 (クラスのnullインスタンスは常にfalseを返すため、上記のコードを実行できることに注意してください。)
基本型ドメインの比較(たとえば、int)が使用され、基本型アレイドメインの比較はarray.equals()によって使用されます。
equals()を上書きする場合、それに応じてHashCode()を上書きすることを忘れないでください。
参照:java.lang.object.equals(object)。
hashcode()を実装する
クラスパーソン{文字列A;オブジェクトB;バイトc; int [] d; public int hashcode(){return a.hashcode() + b.hashcode() + c + arrays.hashcode(d); } public boolean equals(object o){...}} xおよびyオブジェクトにx.equals(y)== trueがある場合、x.hashcode()== y.hashcode()を確認する必要があります。
逆命題によれば、x.hashcode()!= y.hashcode()の場合、x.equals(y)== falseが真でなければなりません。
x.hashcode()!= y.hashcode()がx.equals(y)== falseを保証する必要はありません。ただし、これにより、ハッシュテーブルのパフォーマンスが可能な限り改善されます。
HashCode()の最も単純な法的実装は、単に0を返すことです。この実装は正しいですが、これにより、ハッシュマップなどのデータ構造が非常にゆっくりと実行されます。
compareto()を実装する
クラスの人は、比較可能な<パーソン> {string firstName;文字列lastName; int Birthday; // firstNameで比較し、lastNameでネクタイを破り、最終的に生年月日パブリックint compareto(firstname.compareto(other.firstname)!= 0)return firstname.compareto(other.firstname); else if(lastname.compareto(other.lastname)!= 0)return lastname.compareto(other.lastname); else if(birthdate <other.birthdate)return -1; else if(birthdate> other.birthdate)return 1;それ以外の場合は0を返します。 }}プリミティブタイプの代わりに、常に同等の汎用バージョンを実装してください。これにより、コードボリュームを節約し、不必要な手間を減らすことができるためです。
結果を返すサイン(負/ゼロ/ポジティブ)を気にするだけで、そのサイズは重要ではありません。
Comparator.compare()実装はこれに似ています。
clone()を実装する
クラス値はクローン可能{文字列ABC; double foo; int [] bars;雇われた日付; public values clone(){try {values result =(values)super.clone(); result.bars = result.bars.clone(); result.hired = result.hired.clone();返品結果; } catch(clonenotsupportedexception e){//不可能なスローnew assertionerror(e); }}} super.clone()を使用して、オブジェクトクラスに新しいオブジェクトの作成を担当します。
基本型ドメインは正しくコピーされています。繰り返しますが、StringやBigintegerなどの不変のタイプをクローンする必要はありません。
すべての非プリミティブタイプフィールド(オブジェクトと配列)のディープコピーを手動で実行します。
クローン可能なクラスが実装されており、clone()メソッドはcloneNotsupportedexceptionを投げてはいけません。したがって、この例外をキャッチして無視するか、チェックされていない例外でラップする必要があります。
Object.clone()メソッドを使用せずにClone()メソッドを手動で実装することは問題ありません。
StringBuilderまたはStringBufferを使用します
// join(["a"、 "b"、 "c"]) - > "aおよびb and c" string join(list <string> strs){stringbuilder sb = new StringBuilder(); boolean first = true; for(string s:strs){if(first)first = false; else sb.append( "and"); sb.append(s); } sb.toString();}を返しますこのような重複した文字列連結を使用しないでください:s += itemは、その時間効率がO(n^2)であるためです。
stringbuilderまたはstringbufferを使用する場合、append()メソッドを使用してテキストを追加し、toString()メソッドを使用して、接続されたテキスト全体を取得できます。
StringBuilderがより速いため、優先度が与えられます。 StringBufferのすべての方法は同期されており、通常、同期された方法は必要ありません。
範囲でランダムな整数を生成します
ランダムrand = new Random(); // 1〜6の間、diceroll(){return rand.nextint(6) + 1;}を含むJava APIメソッドを常に使用して、整数の範囲で乱数を生成します。
これらの不確実な用途には、Math.Abs(rand.nextint())%nを使用しようとしないでください。結果はバイアスされているためです。さらに、rand.nextint()== integer.min_valueの場合など、結果値は負になる場合があります。
iterator.remove()を使用します
voidフィルター(list <string> list){for(iterator <string> iter = list.iterator(); iter.hasnext();){string item = iter.next(); if(...)iter.remove(); }}remove()メソッドは、次の()メソッドの最近返されたエントリに作用します。各エントリは、remove()メソッドを1回だけ使用できます。
文字列を返します
String Reverse(string s){return new StringBuilder(s).Reverse()。toString();}この方法は、おそらくJava標準ライブラリに追加する必要があります。
スレッドを開始します
次の3つの例は、異なる方法で同じことを行います。
runnanableを実装する方法:
void startathRead0(){newスレッド(new myRunnable())。start();} class myrunnable runnable {public void run(){...}}スレッドを継承する方法:
void startathread1(){new mythread()。start();} class mythread extends thread {public void run(){...}}匿名でスレッドを継承する方法:
void startathRead2(){new Thread(){public void run(){...}} .start();}run()メソッドを直接呼び出さないでください。 thread.start()メソッドは常に呼び出され、新しいスレッドが作成され、新しく作成されたスレッドがrun()を呼び出します。
finallyを使用してください
I/Oストリーム例:
void writestuff()throws ioexception {outputstream out = new fileoutputStream(...); try {out.write(...); }最後に{out.close(); }}ロック例:
void dowithlock(lock lock){lock.acquire(); {...}最後に{lock.release(); }}試行前のステートメントが実行されず、例外がスローされた場合、最終的なステートメントブロックは実行されません。しかし、この例では、リソースのリリースについて心配する必要はありません。
TRYステートメントブロックのステートメントが例外をスローする場合、プログラムの実行は最終的なステートメントブロックにジャンプして、できるだけ多くのステートメントを実行し、この方法からジャンプします(この方法に別の周辺ステートメントブロックがない限り)。
入力ストリームからバイトデータを読み取ります
inputstream in =(...); try {while(true){int b = in.read(); if(b == -1)break; (...プロセスB ...)}}最後に{in.close();}read()メソッドは、ストリームから読み取りの次のバイト数(0〜255を含む0〜255)を返し、ストリームの終了に到達したときに-1を返します。
入力ストリームからブロックデータを読み取ります
inputstream in =(...); try {byte [] buf = new byte [100]; while(true){int n = in.read(buf); if(n == -1)break; (... Offset = 0およびlength = n ...)with Bufを処理します}}最後に{in.close();}read()メソッドは必ずしもBUF全体を埋めるわけではないため、処理ロジックのリターンの長さを考慮する必要があることを忘れないでください。
ファイルからテキストを読んでください
BufferedReader in = new BufferedReader(new inputStreamReader(new FileInputStream(...)、 "utf-8")); try {while(true){string line = in.readline(); if(line == null)break; (...プロセスライン...)}}最後に{in.close();} BufferedReaderオブジェクトの作成は非常に冗長であるように見えます。これは、Javaがバイトと文字を2つの異なる概念として扱うためです(これはCとは異なります)。
ソケットなどのFileInputStreamの代わりに、あらゆるタイプの入力ストリームを使用できます。
bufferedreader.readline()は、ストリームの終了に到達するとnullを返します。
一度に1つの文字を読むには、reader.read()メソッドを使用します。
UTF-8なしで他のキャラクターエンコーディングを使用できますが、そうしない方が良いです。
ファイルにテキストを書きます
printwriter out = new PrintWriter(new outputStreamWriter(new FileOutputStream(...)、 "UTF-8")); try {out.print( "hello"); out.print(42); out.println( "world!");}最後に{out.close();} Printwriterオブジェクトの作成は非常に冗長であるように見えます。これは、Javaがバイトと文字を2つの異なる概念として扱うためです(これはCとは異なります)。
System.outと同じように、print()とprintln()を使用して、複数のタイプの値を印刷できます。
UTF-8なしで他のキャラクターエンコーディングを使用できますが、そうしない方が良いです。
防御的なチェック値
int factorial(int n){if(n <0)新しいIllegalargumentException( "未定義"); else if(n> = 13)新しいarithmeticexception( "result overflow"); else if(n == 0)return 1;それ以外の場合はn *因子(n -1);}を返しますこれらの条件を明示的に検出するために、入力値が正で、十分に小さいなどとは思わないでください。
適切に設計された関数は、可能なすべての入力値に対して正しく実行できる必要があります。すべての状況が考慮され、間違った出力(オーバーフローなど)がないことを確認してください。
予防テストオブジェクト
int findIndex(list <string> list、string target){if(list == null ||ターゲット== null)新しいnullpointerexception(); ...}オブジェクトパラメーターがヌルにならないとは思わないでください。この状態を明示的に検出します。
予防検出アレイインデックス
void frob(byte [] b、int index){if(b == null)throw new nullpointerexception(); if(index <0 || index> = b.length)new indexOutofBoundsexception(); ...}指定された配列インデックスが境界を越えないとは思わないでください。明示的に検出します。
予防的検出アレイ間隔
void frob(byte [] b、int off、int len){if(b == null)throw new nullpointerexception(); if(off <0 || off> b.length || len <0 || b.length -off <len)新しいindexOutofboundsexception()をスローします。 ...}指定された配列間隔(たとえば、オフから、レン要素の読み取り)が境界を越えないとは思わないでください。明示的に検出します。
配列要素を入力します
ループの使用:
//配列の各要素「a」を123byte [] a =(...); for(int i = 0; i <a.length; i ++)a [i] = 123;
(優先的)標準ライブラリを使用する方法:
arrays.fill(a、(byte)123);
配列要素を範囲内にコピーします
ループの使用:
//アレイから8つの要素をコピーして、オフセット3 //アレイb 'からオフセット6から始まる' a 'から始まります。
(優先的)標準ライブラリを使用する方法:
System.ArrayCopy(A、3、B、6、8);
サイズアレイ
ループを使用(スケールアップ):
// array 'をnewlenbyte [] a =(...); byte [] b = new byte [newlen]; for(int i = 0; i <a.length; i ++)// a b [i] = a [i]; a = b;
ループを使用します(サイズを減らします):
// array 'a'をnewlenbyte [] a =(...); byte [] b = new byte [newlen]; for(int i = 0; i <b.length; i ++)// b [i] = a [i]; a = b;
(優先的)標準ライブラリを使用する方法:
a = arrays.copyof(a、newlen);
4バイトをintに詰めます
int packbigendian(byte [] b){return(b [0]&0xff)<< 24 | (b [1]&0xff)<< 16 | (b [2]&0xff)<< 8 | (b [3]&0xff)<< 0;} int packlittleendian(byte [] b){return(b [0]&0xff)<< 0 | (b [1]&0xff)<< 8 | (b [2]&0xff)<< 16 | (b [3]&0xff)<< 24;}INTを4バイトに分解します
byte [] unpackbigendian(int x){return new byte [] {(byte)(x >>> 24)、(byte)(x >>> 16)、(byte)(x >>> 8)、(byte)(x >>> 0)};} byte [] unpacklendian(int x){x>> >>> 8)、(byte)(x >>> 16)、(byte)(x >>> 24)};}常に署名のない右シフト演算子(>>>)を使用してビットをラップし、算術右シフト演算子(>>)を使用しないでください。