Javaコーディング勉強会

6,931 views
6,816 views

Published on

バグを埋め込みを防ぎ、読みやすいコードを書くために気をつけるべきことを解説する

0 Comments
7 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
6,931
On SlideShare
0
From Embeds
0
Number of Embeds
25
Actions
Shares
0
Downloads
50
Comments
0
Likes
7
Embeds 0
No embeds

No notes for slide

Javaコーディング勉強会

  1. 1. Javaコーディング勉強会 Hiroki Inagaki @inatus http://github.com/inatus 1
  2. 2. はじめに 2
  3. 3. はじめに 3  バグの埋め込みを防ぎ、読みやすいコードを 書くために気をつけるべきことを解説する  以下の条件のJava初心者を対象とする  Javaの文法がわかる  オブジェクト指向の基本がわかる  クラス  カプセル化  継承  ポリモーフィズム
  4. 4. はじめに 4  第1部ではバグの埋め込みを防ぐための基礎 知識を解説する  第2部ではEclipseを用いて読みやすいコード を書くための方法を解説する  第3部では第1部・第2部で解説した内容を実 践する演習をおこなう  説明・演習に用いるコードは以下からダウン ロード可能 https://github.com/inatus/javacodingstudy
  5. 5. 目次 第1部 基本編 第1章 本当は怖いString 鉄則1 文字列の連結にはStringBuilderを使う 鉄則2 文字列比較にはequals()メソッドを使う 第2章 知らなきゃ損!ビット演算 鉄則3 整数型の取りうる値の範囲を把握する 鉄則4 浮動小数点数のしくみをだいたい理解する 第3章 今さら聞けないプリミティブ型と参照型 鉄則5 プリミティブ型と参照型の違いを理解する 鉄則6 ボクシング・アンボクシングは最少にする 第4章 正しく使おう型のいろいろ 鉄則7 正しいコレクションの型を利用する 鉄則8 equals()、hashCode()メソッドを実装する 鉄則9 ストリームにはバッファを利用する 鉄則10 ストリームは必ずclose()する 鉄則11 例外を具体的な型でキャッチし処理する 第2部 応用編 第5章 あなたの書いたそのコード、読めますか? 鉄則12 メソッドの抽出で重複コードをまとめる 鉄則13 変数のインライン化で不要な変数を削除 鉄則14 説明変数を抽出して可読性を向上させる 鉄則15 変数名を変更して可読性を向上させる 鉄則16 マジックナンバーを定数に置き換える 鉄則17 ハードコード文字列を定数に置き換える 鉄則18 Javadocを記述する まとめ リファクタリングショートカットキー 第3部 演習 総合演習 5
  6. 6. 第1章 本当は怖いString 第1部 基本編 6
  7. 7. 鉄則1 文字列の連結にはStringBuilderを使う  だめな例:+で文字列を結合 public String connectSentence1(String[] wordArray) { String sentence = ""; for (String word : wordArray) { sentence += word + " "; } return sentence.trim(); } public String connectSentence2(String[] wordArray) { StringBuilder sentence = new StringBuilder(); for (String word : wordArray) { sentence.append(word); sentence.append(" "); } return sentence.toString().trim(); }  よい例:StringBuilder#appendメソッドで結合 7
  8. 8. 鉄則1 文字列の連結にはStringBuilderを使う  解説 String型オブジェクトは不変 String str = "a"; str += "b"; 1003 “a” 1000 1001 1002 1003 1004 1005 1006 アドレス メモリ str イメージ 1003 “ab” 1000 1001 1002 1003 1004 1005 1006 アドレス メモリ str 1003 “a” 1000 1001 1002 1003 1004 1005 1006 アドレス メモリ str 辛い現実 1005 “a” “ab” “b” 1000 1001 1002 1003 1004 1005 1006 アドレス メモリ str 多少事実と異なりますが、分かりやすくするために簡略化しています 連結するたびに新しいオブジェクトを生成 8
  9. 9. 鉄則1 文字列の連結にはStringBuilderを使う  連結を繰り返す場合にはStringBuilderによりメモリを節約する String str = "a"; str += "b"; str += "c"; 多少事実と異なりますが、分かりやすくするために簡略化しています9 StringBuilder str = new StringBuilder("a"); str.append("b"); str.append("c"); 1003 “a” 1000 1001 1002 1003 1004 1005 1006 アドレス メモリ str 辛い現実 1005 “a” “ab” “b” メモリ str 連結するたびに新しいオブジェクトを生成 1002 “abc” “a” “c” “ab” “b” メモリ str 1002 “a” “a” 1000 1001 1002 1003 1004 1005 1006 アドレス メモリ str ちょっとましな現実 1002 “ab” “a” “b” メモリ str 同じオブジェクトに文字列が連結される 1002 “abc” “a” “c” “b” メモリ str
  10. 10. 鉄則1 文字列の連結にはStringBuilderを使う  結論 forループやwhileループの中で繰り返し文字列を結合するような場合に はStringBuilderを使用する  同じ用途のクラスが2種類存在  同期処理が不要な場合:StringBuilderクラス  同期処理が必要な場合:StringBufferクラス  1行の中で複数の文字列を+でつなぐ場合は問題ない  例:以下の2つの場合のメモリ消費はほとんど同じ 10 String str1 = "a" + word + "b"; StringBuilder str2 = new StringBuilder(); str2.append("a"); str2.append(word); str2.append("b");
  11. 11. 鉄則2 文字列比較にはequals()メソッドを使う  だめな例:文字列を==で比較 String str1 = "123"; if (str1 == "123") { System.out.println("だめな例: true"); } else { System.out.println("だめな例: false"); } String str2 = "123"; if ("123".equals(str2)) { System.out.println("よい例: true"); } else { System.out.println("よい例: false"); }  よい例:文字列をequalsメソッドで比較 11
  12. 12. 鉄則2 文字列比較にはequals()メソッドを使う  解説 equals()は参照先のオブジェクトの中身が同値かを検査 ==は同じオブジェクトを参照しているかを検査 1003 “a” “a” 1005 1000 1001 1002 1003 1004 1005 1006 アドレス メモリ str1 if (str1 == str2) {}if (str1.equals(str2)) {} 多少事実と異なりますが、分かりやすくするために簡略化しています str2 false 1003 “a” “a” 1005 1000 1001 1002 1003 1004 1005 1006 アドレス メモリ str1 str2 true 間違って使っても falseになるから すぐに気づく? 期待通りの結果! 12
  13. 13. 鉄則2 文字列比較にはequals()メソッドを使う  辛い現実 一度生成したStringオブジェクトは、再び明示的または 暗黙的にnewされない限りは使いまわされる String定数プール String str1 = "aa"; String str2 = "bb"; String str3 = "cc”; String str4 = "aa"; String str5 = new String("aa"); “aa” “bb” “cc” “aa” 生成&参照 生成&参照 生成&参照 参照 生成&参照 13
  14. 14. 鉄則2 文字列比較にはequals()メソッドを使う  とても辛い現実 Stringオブジェクトは至るところで暗黙的にnewされる String定数プール String str1 = "aa"; StringBuilder sb = new StringBuilder(str1); String str2 = sb.toString(); String str3 = "aaaa"; String str4 = str3.substring(2); “aa” “aa” “aaaa” “aa” 生成&参照 生成&参照 生成&参照 生成&参照 14
  15. 15. 鉄則2 文字列比較にはequals()メソッドを使う  結論 細かいことはさておき、 Stringオブジェクトの比較には必ずequals()を用いれば 大丈夫! 15
  16. 16. 第2章 知らなきゃ損!ビット演算 第1部 基本編 16
  17. 17. 鉄則3 整数型の取りうる値の範囲を把握する  ビックリする例 int num1 = 2147483647; System.out.println(num1); System.out.println(num1 + 1); int型の数直線イメージ 辛い現実 最大値 最小値 2147483647 0 -2147483648 最大値 最小値 -1 2147483647 -2147483648 0 17 いずれにせよ、最大値を超えると最小値に、最小値を下回ると最大値になる
  18. 18. 鉄則3 整数型の取りうる値の範囲を把握する  その他の型 byte型の辛い現実 最大値 最小値 -1 127 -128 0 char型の普通な現実 最大値 最小値 65535 0 short型の辛い現実 最大値 最小値 -1 32767 -32768 0 long型の辛い現実 最大値 最小値 -1 922337 203685 477580 7 -92233 720368 547758 08 0 整数型の選択方針:long型の範囲が必要でない限りは 常にint型を使う 18
  19. 19. 鉄則4 浮動小数点数のしくみをだいたい理解する  おかしな例 double num1 = 1.7976931348623155; System.out.println(num1); System.out.println(num1 + 0.0000000000000001); // 最後の桁に1を足す 符号 指数 仮数 正負 小数点の位置 実際の数値の整数表現 この例では… + 17桁目 17976931348623155 (最大の桁数を超えている部分は計算できない!) float型、double型は内部で3つの部分に分かれている 実際は指数部は2進数の桁数ですが、分かりやすくするために10進数として説明しています 実数型の選択方針:精度の問題を回避するため、 制約がなければ常にdouble型を使う 19
  20. 20. 第3章 今さら聞けないプリミティ ブ型と参照型 第1部 基本編 20
  21. 21. 鉄則5 プリミティブ型と参照型の違いを理解する  プリミティブ型を引数としたメソッド呼び出しの例 int var1 = 1; obj.execute(var1); System.out.println(var1); public void execute(int par1) { par1++; } int[] var2 = {1}; obj.execute(var2); System.out.println(var2[0]); public void execute(int[] par2) { par2[0]++; }  参照型を引数としたメソッド呼び出しの例 21
  22. 22. 鉄則5 プリミティブ型と参照型の違いを理解する  解説 プリミティブ型は直接値を保持する 参照型はオブジェクト参照を保持する 1 1005 {1} 1000 1001 1002 1003 1004 1005 1006 アドレス メモリ var1 辛い現実 var2 1 1005 1 {1} 1005 1000 1001 1002 1003 1004 1005 1006 アドレス メモリ var1 var2 par1 par2 1 1005 2 {2} 1005 1000 1001 1002 1003 1004 1005 1006 アドレス メモリ var1 var2 par1 par2 多少事実と異なりますが、分かりやすくするために簡略化しています 22
  23. 23. 鉄則6 ボクシング・アンボクシングは最少にする  だめな例 public void execute(List<Integer> integerList) { Integer val1 = integerList.get(0); val1++; System.out.println(val1); } public void execute(List<Integer> integerList) { int val2 = integerList.get(0); val2++; System.out.println(val2); }  よい例 23
  24. 24. 鉄則6 ボクシング・アンボクシングは最少にする  解説 プリミティブ型のラッパークラスの演算では ボクシング・アンボクシングが自動的におこなわれ、 その際、オブジェクトが暗黙的に生成される 1003 1 1 1003 1000 1001 1002 1003 1004 1005 1006 アドレス メモリ get(0) Integer型の例int型の例 多少事実と異なりますが、分かりやすくするために簡略化しています val1 1003 1 1 1000 1001 1002 1003 1004 1005 1006 アドレス メモリ get(0) val2 tmp 1003 1 2 1000 1001 1002 1003 1004 1005 1006 アドレス メモリ get(0) val2 1003 2 1 2 1002 1000 1001 1002 1003 1004 1005 1006 アドレス メモリ get(0) val1 tmp 24
  25. 25. 第4章 正しく使おう型のいろいろ 第1部 基本編 25
  26. 26. 鉄則7 正しいコレクションの型を利用する  だめな例:HashMapの値に常にnullを挿入 Map<Profile, Object> profileHashMap = new HashMap<Profile, Object>(); private void execute(Profile profile) { profileHashMap.put(profile, null); } Set<Profile> profileHashSet = new HashSet<Profile>(); private void execute(Profile profile) { profileHashSet.add(profile); }  よい例:HashSetを使用 26
  27. 27. 鉄則7 正しいコレクションの型を利用する  だめな例:要素が重複しないようにArrayListに挿入 List<Profile> profileArrayList = new ArrayList<Profile>(); private void execute(Profile profile) { if (!profileArrayList.contains(profile)) { profileArrayList.add(profile); } } Set<Profile> profileLinkedHashSet = new LinkedHashSet<Profile>(); private void execute(Profile profile) { profileLinkedHashSet.add(profile); }  よい例:LinkedHashSetを使用 27
  28. 28. 鉄則7 正しいコレクションの型を利用する  Listのイメージ(重複可) 0 “Inatus” インデックス 値 1 “John” 2 “Ashok” 3 “John” 4 “Tom” 5 “Ashok”  Setのイメージ(重複不可) “Inatus” “John” “Ashok” “Tom”  Mapのイメージ(キーの重複不可) 100 “Inatus” 121 “John” 231 “Ashok” 103 “Tom” 255 “Tom” キー 値 28
  29. 29. 鉄則7 正しいコレクションの型を利用する  正しいコレクションを選択する List Set Map 順序が不要 - HashSet HashMap 順序が必要 ArrayList LinkedList LinkedHashSet LinkedHashMap ソートが必要 - TreeSet TreeMap  ArrayListとLinkedListの選択方針 読込(ランダムアクセス)が中心 ArrayList 挿入や削除が多い LinkedList 書換えが多い ArrayList 29 ※これらのクラスはすべて スレッドアンセーフである
  30. 30. 鉄則8 equals()、hashCode()メソッドを実装する  例:同じ値を持つオブジェクトを2つ生成し挿入する Set<Profile> profileHashSet = new HashSet<Profile>(); Profile profile1 = new Profile("inatus", 27, "Kawasaki"); profileHashSet.add(profile1); Profile profile2 = new Profile("inatus", 27, "Kawasaki"); profileHashSet.add(profile2); System.out.println(profileHashSet.size()); 30
  31. 31. 鉄則8 equals()、hashCode()メソッドを実装する  作成したクラスのequals()、hashCode()メソッドが 呼び出される場合には必ずオーバーライドが必要  たとえば…  equals()でオブジェクトの同値性比較をする場合  コレクションに格納する場合(内部でequals()、 hashCode()メソッドを呼び出し、同じ要素が格納されて いるか判定)  モックオブジェクトの引数になる場合(equals()メソッ ドでモックで設定した引数で呼び出されているか判定) 31
  32. 32. 鉄則8 equals()、hashCode()メソッドを実装する  equals() 、hashCode()メソッドの実装方法 Eclipseで自動生成が可能 32 ソースコード上で右クリック →ソース →hashCode()およびequals()の生成 比較するフィールド にチェック
  33. 33. 鉄則9 ストリームにはバッファを利用する  だめな例:1バイトずつ読み書きする OutputStream output1 = null; output1 = new FileOutputStream("output/output1.txt"); output1.write(text.getBytes()); InputStream input1 = null; input1 = new FileInputStream("output/output1.txt"); byte[] buf = new byte[4096]; int len; while ((len = input1.read(buf)) != -1) { System.out.write(buf, 0, len); } 33
  34. 34. 鉄則9 ストリームにはバッファを利用する  よい例:バッファを利用して数バイトずつ読み書きする BufferedOutputStream output2 = null; output2 = new BufferedOutputStream( new FileOutputStream("output/output2.txt")); output2.write(text.getBytes()); output2.flush(); BufferedInputStream input2 = null; input2 = new BufferedInputStream( new FileInputStream("output/output2.txt")); byte[] buf = new byte[4096]; int len; while ((len = input2.read(buf)) != -1) { System.out.write(buf, 0, len); } 34
  35. 35. 鉄則9 ストリームにはバッファを利用する  だめな例:1文字ずつ読み書きする Writer output3 = null; output3 = new FileWriter("output/output3.txt"); output3.write(text); Reader input3 = null; input3 = new FileReader("output/output3.txt"); char[] cbuf = new char[4096]; int len; while ((len = input3.read(cbuf)) != -1) { System.out.write(String.valueOf(cbuf).getBytes(), 0, len); } 35
  36. 36. 鉄則9 ストリームにはバッファを利用する  よい例:バッファを利用して数文字ずつ読み書きする BufferedWriter output4 = null; output4 = new BufferedWriter( new FileWriter("output/output4.txt")); output4.write(text); output4.flush(); BufferedReader input4 = null; input4 = new BufferedReader( new FileReader("output/example/output4.txt")); String line; while ((line = input4.readLine()) != null) { System.out.println(line); } 36
  37. 37. 鉄則9 ストリームにはバッファを利用する  1バイト・1文字ずつ読み書きする基本ストリームクラス 読み込み 書き込み バイトストリーム InputStream OutputStream 文字ストリーム Reader Writer  バッファに溜めてから読み書きするラッパストリームクラス 読み込み 書き込み バイトストリーム BufferedInputStream BufferedOutputStream 文字ストリーム BufferedReader BufferedWriter ディスクアクセスが頻繁に起こるため低速 一旦バッファに溜めることでディスクアクセスの頻度を減らす 37
  38. 38. 鉄則9 ストリームにはバッファを利用する  補足:ファイルの書き込みにはPrintWriterを使うとよい 38 PrintWriter output5 = null; output5 = new PrintWriter(new BufferedWriter( new FileWriter("output/example/output5.txt"))); output5.println(text); output5.flush();
  39. 39. 鉄則10 ストリームは必ずclose()する  だめな例:ストリームをクローズしない BufferedWriter output = null; try { output = new BufferedWriter( new FileWriter("output/output4.txt")); output.write(text); output.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } 39
  40. 40. 鉄則10 ストリームは必ずclose()する  よい例:ストリームをクローズする BufferedWriter output = null; try { output = new BufferedWriter( new FileWriter("output/output4.txt")); output.write(text); output.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (output != null) { output.close(); } } catch (IOException e) { e.printStackTrace(); } } 40
  41. 41. 鉄則10 ストリームは必ずclose()する  よい例:Java 7のtry-with-resources文を用いる try (BufferedWriter output = new BufferedWriter(new FileWriter( "output/output4.txt"))) { output3.write(text); output3.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } 41
  42. 42. 鉄則10 ストリームは必ずclose()する  解説 ストリームはclose()しなければメモリに残ったまま となる メモリ不足の原因となるので必ずfinallyブロックで close()するか、Java 7で新たに追加されたtry-with- resources文を使う 42
  43. 43. 鉄則11 例外を具体的な型でキャッチし処理する  だめな例1:例外をどこでもキャッチしない public void execute(String path) throws Exception { BufferedReader input = new BufferedReader( new FileReader(path)); String line; while ((line = input.readLine()) != null) { System.out.println(line); } if (input != null) { input.close(); } } 43
  44. 44. 鉄則11 例外を具体的な型でキャッチし処理する  だめな例2:Exceptionをキャッチ、なにも処理しない public void execute(String path) { try (BufferedReader input = new BufferedReader( new FileReader(path))) { String line; while ((line = input.readLine()) != null) { System.out.println(line); } } catch (Exception e) { // なにもしない } } 44
  45. 45. 鉄則11 例外を具体的な型でキャッチし処理する  よい例:例外を具体的な型でキャッチし適切に処理する public void execute(String path) { try (BufferedReader input = new BufferedReader( new FileReader(path))) { String line; while ((line = input.readLine()) != null) { System.out.println(line); } } catch (FileNotFoundException e) { e.printStackTrace(); System.err.println("execute1メソッドで例外が発生:" + path + "が見つかりません"); } catch (IOException e) { e.printStackTrace(); System.err.println("execute1メソッドで例外が発生:" + path + "の読み込みに失敗しました"); } } 45
  46. 46. 鉄則11 例外を具体的な型でキャッチし処理する  解説 例外はキャッチされなければそのメソッドを呼んだ メソッドにスローされる →最後までキャッチされなければJVMが停止する Exception型でキャッチせず、具体的な型でキャッ チし、適切に処理をおこなう 46
  47. 47. 第5章 あなたの書いたそのコード、 読めますか? 第2部 応用編 47 Eclipseを用いたリファクタリング
  48. 48. 鉄則12 メソッドの抽出で重複コードをまとめる メソッドに抽出する部分を選択 右クリック→リファクタリング→メソッドの抽出 メソッド名・アクセス修飾子の指定 48
  49. 49. 鉄則12 メソッドの抽出で重複コードをまとめる メソッドの抽出・共通化が完了 プレビューを表示 49
  50. 50. 鉄則13 変数のインライン化で不要な変数を削除 インライン化する変数にカーソルを合わせる 右クリック→リファクタリング→インライン化 確認ダイアログを表示 50
  51. 51. 鉄則13 変数のインライン化で不要な変数を削除 一時変数のインライン化が完了 プレビューを表示 51
  52. 52. 鉄則14 説明変数を抽出して可読性を向上させる 説明変数を抽出する部分を選択する 右クリック→リファクタリング→ローカル変数の抽出 変数名を入力 52
  53. 53. 鉄則14 説明変数を抽出して可読性を向上させる 説明変数の抽出が完了 プレビューを表示 53
  54. 54. 鉄則15 変数名を変更して可読性を向上させる 名前を変更する変数にカーソルを合わせる 右クリック→リファクタリング→名前変更 変数名を入力 54
  55. 55. 鉄則15 変数名を変更して可読性を向上させる 変数名の変更が完了 55
  56. 56. 鉄則16 マジックナンバーを定数に置き換える マジックナンバー部分を選択する 右クリック→リファクタリング→定数の抽出 定数名、アクセス修飾子を入力 56 マジックナンバーとは… 一見しただけでは何のことか わからない数字 定数に置き換え、定数名に意味を 記述することで可読性を向上させる
  57. 57. 鉄則16 マジックナンバーを定数に置き換える マジックナンバーの定数化が完了 プレビューを表示 57
  58. 58. 鉄則17 ハードコード文字列を定数に置き換える ハードコード文字列部分を選択する 定数名、アクセス修飾子を入力 右クリック→リファクタリング→定数の抽出 58
  59. 59. 鉄則17 ハードコード文字列を定数に置き換える マジックナンバーの定数化が完了 プレビューを表示 59
  60. 60. 鉄則18 Javadocを記述する クラス名・メソッド名にカーソルを合わせる Javadocを生成 右クリック→ソース→要素コメントの生成 60
  61. 61. 鉄則18 Javadocを記述する Javadocを記入する  クラスの場合  メソッドの場合  フィールドの場合 61
  62. 62. まとめ リファクタリングショートカットキー 62 コマンド Mac Windows メソッドの抽出 ⌘+Option+M Alt+Shift+M インライン化 ⌘+Option+I Alt+Shift+I ローカル変数の抽出 ⌘+Option+L Alt+Shift+L 名前変更 ⌘+Option+R Alt+Shift+R 定数の抽出 なし なし メソッドシグニチャの変更 ⌘+Option+C Alt+Shift+C 移動 ⌘+Option+V Alt+Shift+V 要素コメントの生成 ⌘+Option+J Alt+Shift+J
  63. 63. 総合演習 第3部 演習 63
  64. 64. 総合演習  鉄則1-18を用いてExerciseクラスをリファクタリ ングしてください  随時、テストクラスExerciseTestを実行して振る 舞いが変わっていないことを確認してください  Exerciseクラスの仕様  createFileメソッド  引数で渡したファイル名・内容のファイルをoutput/exerciseディレク トリに作成  同じファイル名が既に存在する場合は、ファイル名に「_連番」を付 加したファイル名として作成する  mainメソッド  createFileメソッド実行用のドライバメソッド  演習では修正不要 64
  65. 65. 参考文献 65  パーフェクトJava 技術評論社 アリエル・ネットワーク株式会社 (著), 井上 誠一郎 (著), 永井 雅人 (著), 松山 智大 (著)  Sun SJC-P認定ガイド 日経BP社 Kathy Sierra (著), Bert Bates (著), トップスタジオ (著)  WEB+DB PRESS Vol.37 技術評論社  WEB+DB PRESS Vol.52 技術評論社 内容に関するご指摘等は@inatusまでお願いいたします

×