Javaの例外処理とコンパイルエラーについて

Javaでは例外(Exception)が発生するので、その例外を適切に処理する記載をしておかないとコンパイルエラーになります。例外の処理方法は2つあります。

  • try 〜 catch文により例外をキャッチする。
  • メソッドの宣言にthrows句を記載して、呼び出し元に例外を引き渡す。

Javaでは例外処理を記載することが原則ですが、当てはまらないケースが1つあります。それは RuntimeException を継承したクラスの例外です。

RuntimeException を継承したクラスには ArrayIndexOutOfBoundsException(配列の添字が不正の時に発生する)や ArithmeticException(ゼロで除算した場合に発生する)などがあります。

例外処理の記載がない場合、RuntimeException はJavaの実行環境までスローされます。

以下の青色の例外は例外処理を記載しなくてもコンパイルエラーにはなりません。

実際にサンプルプログラムで動きを確かめてみました。

通常の例外の場合です(RuntimeExceptionでない例外のことです)。独自の例外クラスを作成し、例外処理を記載しました。

ThrowException1の中でSampleExceptionという例外を発生させ、MainSample1で例外をキャッチします。

ソースコードは以下です(テキストエディタの画像を貼り付けたのでコードは最後に記載しておきます)。

SampleException.java
– – – – –

ThrowException1.java
– – – – –

ThrowException2.java
– – – – –

MainSample1.java
– – – – –

実行結果です。


$ java sample4.MainSample1
この例外は発生しません
例外をcatchしました
sample4.SampleException: SampleExceptionが発生しました

MainSample1の e2.exampleException(1); を実行したときは例外が発生しないためThrowException1の中で「この例外は発生しません」の文言を出力しています。e2.exampleException(-1); を実行したときにSampleExceptionの例外が発生したためMainSample1で例外をキャッチして処理を終わらせています。

なお、MainSample1で try〜catch 文を使わなかった場合、次のようなコンパイルエラーになります。


$ javac sample4/MainSample1.java 
sample4/MainSample1.java:10: エラー: 例外SampleExceptionは報告されません。スローするには、捕捉または宣言する必要があります
			e2.exampleException(1);
			                   ^
sample4/MainSample1.java:11: エラー: 例外SampleExceptionは報告されません。スローするには、捕捉または宣言する必要があります
			e2.exampleException(-1);
			                   ^
sample4/MainSample1.java:12: エラー: 例外SampleExceptionは報告されません。スローするには、捕捉または宣言する必要があります
			e2.exampleException(2);
			                   ^
エラー3個

ThrowException2のexampleExceptionメソッドに throws SampleException を付けなかった場合は、次のようなコンパイルエラーになります。


$ javac sample4/ThrowException2.java 
sample4/ThrowException2.java:8: エラー: 例外SampleExceptionは報告されません。スローするには、捕捉または宣言する必要があります
		e1.exampleException(i);
		                   ^
エラー1個

RuntimeException が発生する場合です。

メソッドにthrows句を記載しなくても例外は呼び出し元に返されます。MainSample2では例外をキャッチする記載(try〜catch文)がないため、例外はJavaの実行環境まで返されます。

ソースコードは以下です。

ThrowRuntime1.java
– – – – –

ThrowRuntime2.java
– – – – –

MainSample2.java
– – – – –

実行結果です。


$ java sample5.MainSample2
この例外は発生しません
配列に値を格納しました
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
	at sample5.ThrowRuntime1.exampleException(ThrowRuntime1.java:12)
	at sample5.ThrowRuntime2.exampleException(ThrowRuntime2.java:8)
	at sample5.MainSample2.main(MainSample2.java:10)

MainSample2の e2.exampleException(1); を実行したときは例外が発生しないためThrowRuntime1の中で「この例外は発生しません」の文言を出力しています。e2.exampleException(-1); を実行したときにArrayIndexOutOfBoundsExceptionの例外が発生してJava実行環境まで渡されます。Java実行環境は例外の内容を標準出力しています。

ちなみにこのケースですが、ThrowRuntime1で例外を発生させたあとに「配列に値を格納しました」の出力をするようにしました。例外が発生しているため処理が中断されるのかと思ったのですが、そうでもないようです。実行結果として「配列に値を格納しました」の文言が出力されているので、そのあとに例外をスローしているようです。

ここで使用したソースコードを記載しておきます。

SampleException.java


package sample4;

public class SampleException extends Exception {
	//コンストラクタ(同じパッケージにのみ公開)
	SampleException(String s) {
		//スーパークラスのコンストラクタを呼び出す
		super(s);
	}
}

ThrowException1.java


package sample4;

public class ThrowException1 {

	void exampleException(int i) throws SampleException {
	
		if ( i > 0 )
			System.out.println("この例外は発生しません");
		else
			throw new SampleException("SampleExceptionが発生しました");

	}
}

ThrowException2.java


package sample4;

public class ThrowException2 {

	void exampleException(int i) throws SampleException {
	
		ThrowException1 e1 = new ThrowException1();
		e1.exampleException(i);

	}
}

MainSample1.java


package sample4;

public class MainSample1 {

	public static void main( String[] args ) {
	
		ThrowException2 e2 = new ThrowException2();
		
		try {
			e2.exampleException(1);
			e2.exampleException(-1);
			e2.exampleException(2);
		} catch ( SampleException e ) {
			System.out.println("例外をcatchしました");
			System.out.println(e);
		}
	}
}

ThrowRuntime1.java


package sample5;

public class ThrowRuntime1 {

	void exampleException(int i) {
	
		int[] a = new int[3];
	
		if ( i > 0 )
			System.out.println("この例外は発生しません");
		else
			a[3] = 1;
			System.out.println("配列に値を格納しました");
	}
}

ThrowRuntime2.java


package sample5;

public class ThrowRuntime2 {

	void exampleException(int i) {
	
		ThrowRuntime1 e1 = new ThrowRuntime1();
		e1.exampleException(i);

	}
}

MainSample2.java


package sample5;

public class MainSample2 {

	public static void main( String[] args ) {
	
		ThrowRuntime2 e2 = new ThrowRuntime2();
		
		e2.exampleException(1);
		e2.exampleException(-1);
		e2.exampleException(2);
	}
}

ServletからJSPへデータを引き渡すサンプルプログラム

ServletからJSPにデータを引き渡すサンプルプログラムを作りました。プログラムは Servlet、JSP、および、データの入れ物となるBeanで構成します(サンプルソースは最後に記載しておきます)。引き渡すデータは商品番号と商品名と値段です。

実行環境
・ubuntu 20.04 LTS
・Tomcat 9.0.65
・OpenJDK 1.8
・JSTL 1.2.5

まずはBeanです。BeanはJavaBeansの仕様に沿って作成します。

  • publicで引数なしのコンストラクタがある(コンストラクタを定義しない場合はコンパイラが引数なしのコンストラクタを自動で生成します)。
  • privateなフィールドと、そのフィールドに値を設定するメソッド(セッター)と取得するメソッド(ゲッター)がある。
  • シリアライズが可能である(Serializableインターフェースを実装している)。

Shohin というクラス名でBeanを作成しました。フィールドに number(商品番号)、product(商品名)、price(値段)を持っています。

Servletです。Fwsample1 というクラス名で作成しました。Servletの中で、商品番号「1」、商品名「apple」、値段「200(円)」を設定して fwsample1.jsp にデータを引き渡しています。

JSPです。fwsample1.jsp ではBeanから number(商品番号)、product(商品名)、price(値段) を取得して表示します。

JSPでBeanからの値の取得にはEL式(Expression Language)を使っています。EL式は ${}で記載したものです。

実行結果です。

ServletからJSPに引き渡すデータ(Bean)は1つとは限らないので、Beanが複数あった場合のサンプルも載せておきます。Servletでデータベースから複数行を取得して、JSPに引き渡す場合に利用できます。

Servletです。Fwsample2 というクラス名で作成しました。Beanをリストに格納してから fwsample2.jsp にデータを引き渡しています。

JSPです。fwsample2.jsp ではJSTL(JSP Standard Tag Library)の繰り返し処理(<c:forEach>タグ)を使ってリストからBeanを取り出して表示しています。JSPで繰り返し処理や条件分岐を行う場合は、<% Javaの処理 %> のように記述する必要がありますが、JSTLを使うことによりタグでその処理ができるようになります。

JSTLを利用するにはJSTLのJARファイルを入手する必要があります。Tomcatのタグライブラリのダウンロードページから取得します。JARファイル(Standard-1.2.5の場合)には Impl、Spec、EL、Compat があるので、これらのJARファイルをダウンロードします。

ダウンロードしたJARファイルは Tomcatのwebapps配下のアプリケーションフォルダにある WEB-INF/lib フォルダに格納します(libフォルダがない場合は作成します)。

実行結果です。

ここで使用したサンプルプログラムのソースを記載しておきます。

Shohin.java


package bean;

public class Shohin implements java.io.Serializable {

	private int number;
	private String product;
	private int price;

	public int getNumber() {
		return number;
	}
	public String getProduct() {
		return product;
	}
	public int getPrice() {
		return price;
	}

	public void setNumber(int number) {
		this.number=number;
	}
	public void setProduct(String product) {
		this.product=product;
	}
	public void setPrice(int price) {
		this.price=price;
	}
}

Fwsample1.java


package sample3;

import bean.Shohin;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import javax.servlet.annotation.WebServlet;

@WebServlet(urlPatterns={"/sample3/fwsample1"})
public class Fwsample1 extends HttpServlet
{
	public void doGet (
		HttpServletRequest request, HttpServletResponse response
		) throws ServletException, IOException
		{
			response.setContentType("text/html; charset=UTF-8");
			PrintWriter out = response.getWriter();

			try	{
				//beanに格納
				Shohin s = new Shohin();
				s.setNumber(1);
				s.setProduct("apple");
				s.setPrice(200);

				//JSPに引き渡し
				request.setAttribute("shohin", s);
				request.getRequestDispatcher("fwsample1.jsp")
					.forward(request, response);
			}
			catch ( Exception e ) {
				out.println("エラーが発生しました<br>");
				e.printStackTrace( out );
			}
		}
}

fwsample1.jsp


<%@page contentType="text/html; charset=UTF-8" %>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>fwsample1.jsp</title>
</head>
<body>

${shohin.number}:${shohin.product}:${shohin.price}

</body>
</html>

Fwsample2.java


package sample3;

import bean.Shohin;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import javax.servlet.annotation.WebServlet;
import java.util.*;

@WebServlet(urlPatterns={"/sample3/fwsample2"})
public class Fwsample2 extends HttpServlet
{
	public void doGet (
		HttpServletRequest request, HttpServletResponse response
		) throws ServletException, IOException
		{
			response.setContentType("text/html; charset=UTF-8");
			PrintWriter out = response.getWriter();

			try	{
				List<Shohin> list = new ArrayList<>();

				//リストに格納
				Shohin s1 = new Shohin();
				s1.setNumber(1);
				s1.setProduct("apple");
				s1.setPrice(200);
				list.add(s1);

				Shohin s2 = new Shohin();
				s2.setNumber(2);
				s2.setProduct("orange");
				s2.setPrice(150);
				list.add(s2);

				//JSPに引き渡し
				request.setAttribute("list", list);
				request.getRequestDispatcher("fwsample2.jsp")
					.forward(request, response);
			}
			catch ( Exception e ) {
				out.println("エラーが発生しました<br>");
				e.printStackTrace( out );
			}
		}
}

fwsample2.jsp


<%@page contentType="text/html; charset=UTF-8" %>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>fwsample2.jsp</title>
</head>
<body>

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<c:forEach var="p" items="${list}">
	${p.number}:${p.product}:${p.price}<br>
</c:forEach>

</body>
</html>

なお、ここで記載した内容は以下の書籍を参考にさせてもらいました。ServletとJSPの基礎が学べます。

この書籍の良い所(個人的な感想ではあります)は、Eclipseを使わずに説明をしてくれているためJavaEE仕様が理解できることです。Eclipseは便利なのですがその便利さゆえにJavaEEの仕様をEclipse側で吸収してしまい、本質的なところが省略されてしまっているように思えます。

この書籍ではEclipseなしでJavaをコンパイル、そしてTomcatに配置して、Servlet、および、JSPを動かしています。基礎的な内容であるため実際の現場ですぐ役立つわけではないですが、初心者で本質を知りたい方にはとても良い書籍だと思います。書籍の付録にはEclipseを使った場合も書かれているので、Eclipseを否定しているわけではありません。