✅【保存版】Java面接対策:よく聞かれる質問と回答まとめ
勉強ちゃん
いろいろ勉強
前回は、商品名と数量を入力し、ファイルから単価を読み込んで合計金額を計算・保存するプログラムを作りました。
ただし、すべての処理を1つのmain
に詰め込みだったので、読みづらく、拡張もしにくい状態でした。
今回はその続き、Javaの「クラス」とオブジェクトをわかりやすく解説しつつ、コードをクラス設計 → リファクタリングしていきます。
クラス(class)は、アプリで扱うデータ(フィールド)と振る舞い(メソッド)をひとまとめにした設計図です。
設計図から実体化したものがオブジェクト(インスタンス)です。
例)「商品」を表すクラス = 商品名 と 単価(データ)+ 合計を計算(振る舞い)
前回の処理は1つのmain
にまとまっていました。実は次の4つの役割が混ざっています。
Map<String, Integer>
)→ それぞれを専用クラスに分けると、役割が明確になり保守が楽になります。
// 商品単価クラス
public class Item {
private final String name;
private final int unitPrice;
public Item(String name, int unitPrice) {
this.name = name;
this.unitPrice = unitPrice;
}
public String getName() { return name; }
public int getUnitPrice() { return unitPrice; }
}
// 購入明細クラス(商品+数量+小計)
public class Purchase {
private final Item item;
private final int quantity;
public Purchase(Item item, int quantity) {
this.item = item;
this.quantity = quantity;
}
public Item getItem() { return item; }
public int getQuantity() { return quantity; }
public int getSubtotal() { return item.getUnitPrice() * quantity; }
// ファイル出力向けの1行表現
public String toLine() {
return item.getName() + "\t" + quantity + "\t" + getSubtotal() + "円";
}
}
ポイント
リファクタリングとは
外側の動作(入出力の結果)は変えず、内部構造を整理・改善することです。
目的は可読性・保守性・拡張性の向上です。
productUnitPrice
)productInfo
)import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;
public class ProductLoader {
// ファイル形式例: リンゴ,100 のような「商品名,単価」
public Map<String, Integer> loadPrices(String fileName) throws IOException {
Map<String, Integer> priceMap = new HashMap<>();
try (BufferedReader br = new BufferedReader(new FileReader(fileName))) {
String line;
int row = 0;
while ((line = br.readLine()) != null) {
row++;
if (line.isBlank()) continue;
String[] data = line.split(",");
if (data.length != 2) {
throw new IOException("価格ファイルの形式エラー(" + row + "行目): " + line);
}
String name = data[0].trim();
int price = Integer.parseInt(data[1].trim());
priceMap.put(name, price);
}
}
return priceMap;
}
}
import java.util.*;
public class ShoppingCalculator {
private final List<Purchase> lines = new ArrayList<>();
private int total = 0;
public void inputAndCalculate(Map<String, Integer> priceMap) {
try (Scanner sc = new Scanner(System.in)) {
while (true) {
System.out.print("商品名を入力してください(終了は end):");
String productName = sc.nextLine().trim();
if ("end".equalsIgnoreCase(productName)) break;
Integer unitPrice = priceMap.get(productName);
if (unitPrice == null) {
System.out.println("指定された商品は存在しません。");
continue;
}
System.out.print("数量を入力してください: ");
while (!sc.hasNextInt()) {
System.out.print("整数で入力してください: ");
sc.next();
}
int quantity = sc.nextInt();
sc.nextLine(); // 改行の読み飛ばし
Item item = new Item(productName, unitPrice);
Purchase line = new Purchase(item, quantity);
lines.add(line);
total += line.getSubtotal();
System.out.println("追加: " + line.toLine());
}
}
}
public List<Purchase> getLines() { return Collections.unmodifiableList(lines); }
public int getTotal() { return total; }
}
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
public class ResultWriter {
public void write(String fileName, List<Purchase> lines, int total) throws IOException {
try (BufferedWriter bw = new BufferedWriter(new FileWriter(fileName, true))) {
for (Purchase line : lines) {
bw.write(line.toLine());
bw.newLine();
}
bw.write("合計金額:" + total);
bw.newLine();
}
}
}
public class Main {
public static void main(String[] args) {
try {
// 1) 単価表を読み込む
ProductLoader loader = new ProductLoader();
var priceMap = loader.loadPrices("productUnitPrice"); // 例: Apple,120
// 2) 入力と計算
ShoppingCalculator calculator = new ShoppingCalculator();
calculator.inputAndCalculate(priceMap);
// 3) 結果を保存
ResultWriter writer = new ResultWriter();
writer.write("productInfo", calculator.getLines(), calculator.getTotal());
System.out.println("計算結果を productInfo に保存しました。");
} catch (Exception e) {
System.out.println("エラーが発生しました: " + e.getMessage());
}
}
}
ProductLoader
だけ、ResultWriter
だけ、と単体テストがしやすいです。「クラスとオブジェクト」をさらに進めて、スレッド(並行処理)入門への橋渡し
を予定しています。