【初心者向け】プログラミングの「例外処理」基本の基本

プログラミング初心者向けに例外処理の基本概念から実践的な使い方まで分かりやすく解説。エラーに強いプログラムの作り方を学びましょう。

Learning Next 運営
22 分で読めます

みなさん、プログラミングでエラーが発生したとき、プログラムが突然停止してしまった経験はありませんか?

「なぜプログラムが止まってしまうの?」「エラーが起きても動き続けるプログラムを作りたい」 多くの初心者がこのような悩みを抱えています。

この記事では、プログラミングの「例外処理」について、基本概念から実践的な使い方まで初心者向けに分かりやすく解説します。 例外処理を理解することで、エラーに強く、ユーザーフレンドリーなプログラムを作れるようになります。

例外処理とは何か?

例外処理の基本概念

例外処理とは、プログラム実行中に発生する予期しない問題(例外)を適切に処理する仕組みです。

日常生活で例えると、以下のような状況と似ています:

  • 料理中: 材料が足りなくなったときの代替案
  • 電車通勤: 電車が遅延したときの別ルート
  • ATM操作: 残高不足のときの適切な案内

プログラムでも同様に、問題が発生したときの「対処法」を事前に用意しておくのが例外処理です。

例外が発生する典型例

プログラミングでよく発生する例外の例:

ファイル操作での例外

// ファイルが存在しない場合
const fs = require('fs');
// この処理でエラーが発生する可能性がある
const data = fs.readFileSync('存在しないファイル.txt');
console.log(data); // ここまで到達しない

数値計算での例外

// ゼロで割る計算
function divide(a, b) {
return a / b; // b が 0 の場合、Infinity が返される
}
// 配列の範囲外アクセス
const numbers = [1, 2, 3];
console.log(numbers[10]); // undefined が返される

ネットワーク通信での例外

// サーバーへの接続エラー
fetch('https://存在しないサーバー.com/api')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => {
// ここで例外を処理
console.log('通信エラーが発生しました');
});

try-catch文の基本

基本的な構文

例外処理の最も基本的な形がtry-catch文です:

try {
// 例外が発生する可能性のあるコード
const result = riskyOperation();
console.log('成功:', result);
} catch (error) {
// 例外が発生した場合の処理
console.log('エラーが発生しました:', error.message);
}

try-catch文の動作

try-catch文は以下のように動作します:

正常時の動作

try {
console.log('1. try文の開始');
const result = 10 + 20;
console.log('2. 計算結果:', result);
console.log('3. try文の終了');
} catch (error) {
console.log('4. この部分は実行されない');
}
console.log('5. プログラム続行');
// 出力結果:
// 1. try文の開始
// 2. 計算結果: 30
// 3. try文の終了
// 5. プログラム続行

例外発生時の動作

try {
console.log('1. try文の開始');
throw new Error('意図的なエラー');
console.log('2. この部分は実行されない');
} catch (error) {
console.log('3. エラーをキャッチ:', error.message);
}
console.log('4. プログラム続行');
// 出力結果:
// 1. try文の開始
// 3. エラーをキャッチ: 意図的なエラー
// 4. プログラム続行

実践的な例外処理の例

ユーザー入力の検証

ユーザーからの入力を安全に処理する例:

function validateAge(ageInput) {
try {
// 文字列を数値に変換
const age = parseInt(ageInput);
// 数値でない場合
if (isNaN(age)) {
throw new Error('年齢は数値で入力してください');
}
// 範囲外の場合
if (age < 0 || age > 150) {
throw new Error('年齢は0歳から150歳の間で入力してください');
}
return age;
} catch (error) {
console.log('入力エラー:', error.message);
return null; // エラー時はnullを返す
}
}
// 使用例
console.log(validateAge('25')); // 25
console.log(validateAge('abc')); // null(エラーメッセージ出力)
console.log(validateAge('-5')); // null(エラーメッセージ出力)

API通信の例外処理

外部APIとの通信で発生する例外の処理:

async function fetchUserData(userId) {
try {
// API呼び出し
const response = await fetch(`/api/users/${userId}`);
// HTTPステータスをチェック
if (!response.ok) {
throw new Error(`HTTPエラー: ${response.status}`);
}
// JSONデータを取得
const userData = await response.json();
return userData;
} catch (error) {
// 具体的なエラー処理
if (error.name === 'TypeError') {
console.log('ネットワークエラーが発生しました');
} else {
console.log('ユーザー情報の取得に失敗:', error.message);
}
// デフォルト値を返す
return {
id: userId,
name: '不明',
email: '不明'
};
}
}

ファイル操作の例外処理

ファイルの読み書きでの例外処理:

const fs = require('fs').promises;
async function readConfigFile(filename) {
try {
// ファイルを読み込み
const data = await fs.readFile(filename, 'utf8');
// JSONとして解析
const config = JSON.parse(data);
return config;
} catch (error) {
// エラーの種類に応じた処理
if (error.code === 'ENOENT') {
console.log(`設定ファイル ${filename} が見つかりません`);
// デフォルト設定を返す
return {
debug: false,
port: 3000
};
} else if (error instanceof SyntaxError) {
console.log('設定ファイルのJSON形式が正しくありません');
return null;
} else {
console.log('設定ファイルの読み込みでエラー:', error.message);
return null;
}
}
}

finally文の使い方

finally文の基本

finally文は、例外の発生に関係なく必ず実行される処理を書きます:

function processFile(filename) {
let file = null;
try {
console.log('1. ファイルを開いています...');
file = openFile(filename);
console.log('2. ファイルを処理中...');
const result = processData(file);
return result;
} catch (error) {
console.log('3. エラーが発生しました:', error.message);
return null;
} finally {
// 例外の有無に関係なく実行される
if (file) {
console.log('4. ファイルを閉じています...');
closeFile(file);
}
}
}

リソースの適切な解放

finally文は、リソースの解放によく使用されます:

async function databaseOperation() {
let connection = null;
try {
// データベース接続
connection = await connectToDatabase();
// データベース操作
const result = await connection.query('SELECT * FROM users');
return result;
} catch (error) {
console.log('データベースエラー:', error.message);
throw error; // エラーを再発生させる
} finally {
// 接続を必ず閉じる
if (connection) {
await connection.close();
console.log('データベース接続を閉じました');
}
}
}

例外の種類と使い分け

組み込み例外の種類

JavaScriptの主要な組み込み例外:

// TypeError: 型に関するエラー
try {
const number = null;
number.toFixed(2); // nullに対してメソッド呼び出し
} catch (error) {
if (error instanceof TypeError) {
console.log('型エラーです:', error.message);
}
}
// ReferenceError: 参照エラー
try {
console.log(undefinedVariable); // 定義されていない変数
} catch (error) {
if (error instanceof ReferenceError) {
console.log('参照エラーです:', error.message);
}
}
// SyntaxError: 構文エラー
try {
JSON.parse('不正なJSON文字列{');
} catch (error) {
if (error instanceof SyntaxError) {
console.log('構文エラーです:', error.message);
}
}

カスタム例外の作成

独自の例外クラスを作成することもできます:

// カスタム例外クラス
class ValidationError extends Error {
constructor(message, field) {
super(message);
this.name = 'ValidationError';
this.field = field;
}
}
// 使用例
function validateUser(userData) {
try {
if (!userData.email) {
throw new ValidationError('メールアドレスが必要です', 'email');
}
if (!userData.email.includes('@')) {
throw new ValidationError('有効なメールアドレスを入力してください', 'email');
}
return true;
} catch (error) {
if (error instanceof ValidationError) {
console.log(`入力エラー(${error.field}): ${error.message}`);
return false;
} else {
// 予期しないエラー
console.log('予期しないエラー:', error.message);
throw error;
}
}
}

例外処理のベストプラクティス

適切な例外処理の原則

効果的な例外処理のための重要な原則:

1. 具体的な例外を捕捉する

// 悪い例:すべての例外を同じように処理
try {
riskyOperation();
} catch (error) {
console.log('何らかのエラーが発生しました');
}
// 良い例:例外の種類に応じた処理
try {
riskyOperation();
} catch (error) {
if (error instanceof NetworkError) {
console.log('ネットワークエラー: 接続を確認してください');
} else if (error instanceof ValidationError) {
console.log('入力エラー:', error.message);
} else {
console.log('予期しないエラー:', error.message);
// ログ出力やエラー報告
}
}

2. 例外の再発生を適切に使用

async function processUserData(userData) {
try {
// データの検証
validateUserData(userData);
// データベースに保存
await saveToDatabase(userData);
return { success: true };
} catch (error) {
// ログを出力
logger.error('ユーザーデータの処理でエラー:', error);
// 呼び出し元に例外を伝える
throw new Error('ユーザーデータの処理に失敗しました');
}
}

3. 適切なエラーメッセージ

function withdrawMoney(account, amount) {
try {
if (amount <= 0) {
throw new Error('出金額は0より大きい値を指定してください');
}
if (account.balance < amount) {
throw new Error(`残高不足です。現在の残高: ${account.balance}`);
}
account.balance -= amount;
return { success: true, newBalance: account.balance };
} catch (error) {
return {
success: false,
message: error.message
};
}
}

よくある間違いと対策

間違い1: 例外を無視する

// 悪い例:例外を無視
try {
riskyOperation();
} catch (error) {
// 何もしない(エラーを隠してしまう)
}
// 良い例:最低限のログ出力
try {
riskyOperation();
} catch (error) {
console.error('操作でエラーが発生:', error.message);
// 必要に応じて代替処理
}

間違い2: 過度な例外処理

// 悪い例:不要な例外処理
try {
const result = 1 + 1; // 例外が発生しない処理
console.log(result);
} catch (error) {
// 決して実行されない
}
// 良い例:例外が発生する可能性がある処理のみ
const result = 1 + 1; // そのまま実行
console.log(result);
try {
const data = JSON.parse(userInput); // 例外の可能性あり
processData(data);
} catch (error) {
console.log('JSONの解析エラー:', error.message);
}

実践演習

練習問題1: 基本的な例外処理

以下の要件を満たす関数を作成してください:

// 要件:
// - 文字列を受け取り、数値に変換する
// - 変換できない場合は適切なエラーメッセージを表示
// - 負の数の場合は警告を表示
// - 正常な場合は数値を返す
function parsePositiveNumber(input) {
try {
// ここに実装を追加
const number = parseFloat(input);
if (isNaN(number)) {
throw new Error('数値に変換できません');
}
if (number < 0) {
console.warn('警告: 負の数が入力されました');
}
return number;
} catch (error) {
console.error('エラー:', error.message);
return null;
}
}
// テスト
console.log(parsePositiveNumber('123')); // 123
console.log(parsePositiveNumber('abc')); // null
console.log(parsePositiveNumber('-5')); // -5(警告付き)

練習問題2: 非同期処理の例外処理

非同期処理での例外処理を練習してください:

// 模擬的なAPI呼び出し関数
function mockApiCall(shouldFail) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (shouldFail) {
reject(new Error('API呼び出しに失敗しました'));
} else {
resolve({ data: 'APIからのデータ' });
}
}, 1000);
});
}
// 要件:
// - API呼び出しの成功・失敗を適切に処理
// - エラーの場合はデフォルト値を返す
// - 処理時間を計測して表示
async function fetchDataSafely(shouldFail = false) {
const startTime = Date.now();
try {
console.log('API呼び出し開始...');
const result = await mockApiCall(shouldFail);
const elapsed = Date.now() - startTime;
console.log(`成功(${elapsed}ms):`, result.data);
return result.data;
} catch (error) {
const elapsed = Date.now() - startTime;
console.log(`エラー(${elapsed}ms):`, error.message);
return 'デフォルトデータ';
}
}
// テスト
fetchDataSafely(false); // 成功パターン
fetchDataSafely(true); // 失敗パターン

まとめ

プログラミングの例外処理について重要なポイントをまとめます:

例外処理の基本概念

  • 目的: プログラムの予期しないエラーを適切に処理
  • 基本構文: try-catch-finally文の活用
  • リソース管理: finallyでのリソース解放
  • カスタム例外: 独自の例外クラスの作成

実践的なポイント

  • 具体的な処理: 例外の種類に応じた適切な対応
  • ユーザビリティ: 分かりやすいエラーメッセージ
  • ログ出力: デバッグのための適切な記録
  • 代替処理: エラー時のフォールバック機能

避けるべき間違い

  • 例外の無視: エラーを隠してしまう危険性
  • 過度な例外処理: 不要な場所での例外処理
  • 曖昧なエラーメッセージ: ユーザーが理解できない内容

例外処理は、エラーに強い安定したプログラムを作るための重要な技術です。

最初は複雑に感じるかもしれませんが、基本的なパターンを覚えて実践を重ねることで、必ず身につけることができます。 今日から例外処理を意識して、ユーザーフレンドリーなプログラムを作ってみませんか?

関連記事