🔥

プログラミング自主学習 DAY62 Exception/Coding Test

2023/07/28に公開

Exception

RuntimeException

public class ExceptionHandlingExample1 {
	public static void printLength(String data) {
		int result = data.length();
		System.out.println("문자 수: " + result);
	}
	
	public static void main(String[] args) {
		System.out.println("[프로그램 시작]\n");
		printLength("ThisIsJava");
		printLength(null);
		System.out.println("프로그램 종료");
	}

}
Exception in thread "main" [프로그램 시작]

문자 수: 10
java.lang.NullPointerException: Cannot invoke "String.length()" because "data" is null
	at ch11.sec02.exam01.ExceptionHandlingExample1.printLength(ExceptionHandlingExample1.java:5)
	at ch11.sec02.exam01.ExceptionHandlingExample1.main(ExceptionHandlingExample1.java:12)

printLength(null); dateが参照しているオブジェクトがないため、例外が発生した。
結果をみれば、lengthの値はあるため、コンパイルの後、実行されたことがわかる。
コンパイラがチェックできないRuntime Exceptionである。

public class ExceptionHandlingExample2 {
	public static void printLength(String data) {
	try {
		int result = data.length();
		System.out.println("문자 수: " + result);
	}catch(NullPointerException e){
		//System.out.println(e.getMessage());
		//System.out.println(e.toString());
		e.printStackTrace();
	}finally {
		System.out.println("[마무리 실행]\n");
	}
	
	}
	
	public static void main(String[] args) {
		System.out.println("[프로그램 시작]\n");
		printLength("ThisIsJava");
		printLength(null);
		System.out.println("프로그램 종료");
	}

}

例外情報を詳しく教えてくれるprintStackTrace()をcatchに追加することで、
どのラインで例外が発生したかを確認できる。
catchのパラメータがNullPointerException eになっている理由は、javaは例外が発生した場合、その例外を処理する(終了)際にその例外に該当するクラスのオブジェクトを生成するためだ。

プログラム実行時、起きる可能性がある例外の種類を予想し、その例外クラスのオブジェクトが終了せずに、他の対応をするためにパラメータにはNullException eをパラメータに設定し、eが持っている他のメソッドでプログラムを強制的に実行させる。
まるで狙った獲物をわなをかけて狩りをするイメージだ。

狙った獲物NullPointerException eをtryでチェックしてcatchする。


Exception

コンパイラがプログラムを作成する際に教えてくれるExceptionだ。例外を処理するか、他のロジックを組まない限り、コンパイル自体ができない。

public class ExceptionHandlingExample {

	public static void main(String[] args) {
     
		Class.forName("java.lang.String");
		
	}
}

このように、コードを作成する場合、Class.forNameのラインから、コンパイルエラーが表示される。Class.forName()は、パラメータの中にあるクラスを動的にローディングし、メモリに割り当てるstaticメソッドだ。

しかし、現在はClassNotFoundExceptionという一般例外が起きる可能性があり、

パラメーターに入る実引数は人により、いつでも変わる可能性があるためかなと個人的には思う。

事前に例外が発生してプログラムが終了する可能性を防ぐため、コンパイル前に強制的に
try-catchを作成するよう、コンパイルをストップするのではないかと思う。

public class ExceptionHandlingExample {

	public static void main(String[] args) {
     
		try {
			Class.forName("java.lang.String");
		    System.out.println("String is Exist");
	    } catch (ClassNotFoundException e) {
	    	System.out.println("String is not Exist");
	    	e.printStackTrace();
	    }
		
		System.out.println();
		
		try {
			Class.forName("java.lang.String2");
			System.out.println("String2 is Exist");
		} catch (ClassNotFoundException e) {
			System.out.println("String2 is not exist");
			e.printStackTrace();
		}		
		
	}
}
String is Exist

String2 is not exist
java.lang.ClassNotFoundException: java.lang.String2
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
	at java.base/java.lang.Class.forName0(Native Method)
	at java.base/java.lang.Class.forName(Class.java:375)
	at ch11.sec02.exam02.ExceptionHandlingExample.main(ExceptionHandlingExample.java:18)


多重Catch

tryの中で、2種類以上の例外が起きる可能性がある場合、例外別にcatchを作成することで、対応ができる。

public class ExceptionHandlingExample {

	public static void main(String[] args) {
		
		String[] array = {"100" , "1oo"};
		
		for(int i=0; i<=array.length; i++) {
			try {
			System.out.println(array[i]);
			int value = Integer.parseInt(array[i]);
		}catch(ArrayIndexOutOfBoundsException e) {
			System.out.println(e.toString());
			System.out.println("please check Array's length again");
		}catch(NumberFormatException e) {
			System.out.println(e.toString());
			System.out.println("Some index can't be converted to int data type. please check again");	
		}
			System.out.println("finish");
			System.out.println();
		}
		

	}

}
100
finish

1oo
java.lang.NumberFormatException: For input string: "1oo"
Some index can't be converted to int data type. please check again
finish

java.lang.ArrayIndexOutOfBoundsException: Index 2 out of bounds for length 2
please check Array's length again
finish

finishで回転数別のログをチェックしてみた。3回強制的に回ることはでき(実際は2回しか回れない)回転ごとに起きた例外の情報も把握した。
また、様々な例外を以下のように処理することもできる。

catch(ArrayIndexOutOfBoundsException e){
...
}

catch(Exception e){
...
}

このように、ArrayIndexOutOfBoundsExceptionはその例外の対処方法を処理し、
他の例外は継承とポリモーフィズムを利用して、一括で処理することもできる。

また、論理演算子で処理することもできる。

catch(ArrayIndexOutOfBoundsException e){
...
}

catch(NumberFormatException | NullPointer Exception e){
...
}


try-with-resource

resource(リソース) はデーターを提供するオブジェクトだ。ファイル、イメージ、テキスト、オーディオなどが典型的なリソースで、我々はリソースを生成し、(create)、読み取り(read)、アップデート(update)し、消すこと(delete)もできる。
読みてるためにはファイルをオープンして、修正し、最後にはクローズする必要がある。
クローズをしないと、他のプログラムから使用できないためだ。
そのため、リソースを活用する場合は、例外が起きようが、起きないようが、リソースを閉めるのは必ず必修だ。
そのため、finallyを活用することが重要だ。

FileInputStream fis = null;
try{
 fis = new FileInputStream("file.txt");
 ....
}catch(IOException e){
....
}finally{
 fis.close();
}

このような方法おあるが、javaはリソースを扱う場合、try-with-resourceブロックで自動的にclose()してくれる機能がある。

try(FileInputStream fis = new FileInputStream("file.txt")){
 ....
} catch(IOException e){
 ....
} 

ただし、この場合はAutoClosableというインタフェースを具象し、close()というメソッドをオーバーライドすることが必要だ。

public class FileInputStream implements AutoCloseable{
  ....
  @Override
  public void close() throws Exception{...}
  }


https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/AutoCloseable.html


<複数の場合> -> ;で区切る。

try(
  FileInputStream fis1 = new FileInputStream("file1.txt");
  FileInputStream fis2 = new FileInputStream("file2.txt");
  ){
   ...
  }catch(IOException e){
   ...
  } 
 FileInputStream fis1 = new FileInputStream("file1.txt");
 FileInputStream fis2 = new FileInputStream("file2.txt");
 try(fis1;fis2){
   ...
 }catch(IOException e){
  ...
 }

Autoclosable 活用

public class MyResource implements AutoCloseable{
	private String name;
	
	public MyResource(String name) {
		this.name = name;
		System.out.println("[MyResource( " +name+   " ) open]");
	}
	
	
	public String read1() {
		System.out.println("[MyResource( " +name+   " ) open]");
	    return "100";
	}
	
	public String read2() {
		System.out.println("[MyResource( " +name+   " ) open]");
	    return "abc";
	}
	
	@Override
	public void close() throws Exception {
		System.out.println("[MyResource( " +name+   " ) close]");
	}
}

public class TryWithResourceExample {

	public static void main(String[] args) {
		try(MyResource res = new MyResource("A")) {
			String data = res.read1();
			int value = Integer.parseInt(data);
		} catch (Exception e) {
			System.out.println("例外処理: " + e.getMessage());
		}
		
		System.out.println();
		
		try(MyResource res = new MyResource("A")) {
			String data = res.read2();
			int value = Integer.parseInt(data);
		} catch (Exception e) {
			System.out.println("例外処理: " + e.getMessage());
		}
		
		System.out.println();
		
		MyResource res1 = new MyResource("A");
		MyResource res2 = new MyResource("B");
		try(res1; res2){
			String data1 = res1.read1();
			String data2 = res1.read1();
		}catch(Exception e) {
			System.out.println("例外処理: " + e.getMessage());
		}

	}

}

[MyResource( A ) open]
[MyResource( A ) open]
[MyResource( A ) close]

[MyResource( A ) open]
[MyResource( A ) open]
[MyResource( A ) close]
例外処理: For input string: "abc"

[MyResource( A ) open]
[MyResource( B ) open]
[MyResource( A ) open]
[MyResource( B ) open]
[MyResource( B ) close]
[MyResource( A ) close]

throws

基本的にメソッド内部で起きた例外は、try-catchで処理することが基本だが、Callerに例外を押し付けることもできる。

public class ThrowsExample1 {

	public static void main(String[] args) throws ClassNotFoundException {
	
			findClass();
		
		
	}//main() method -> Caller JVM
		
		public static void findClass() throws ClassNotFoundException{
			Class.forName("java.lang.String2");
		}//findClass() method -> Caller main() method

}//class

Exception in thread "main" java.lang.ClassNotFoundException: java.lang.String2
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
	at java.base/java.lang.Class.forName0(Native Method)
	at java.base/java.lang.Class.forName(Class.java:375)
	at ch11.sec05.ThrowsExample1.findClass(ThrowsExample1.java:13)
	at ch11.sec05.ThrowsExample1.main(ThrowsExample1.java:7)

JVMは例外を処理せずに、出力のみする。
そのため、最悪な状況であってもmainでは例外を処理しなければならない。

public class ThrowsExample1 {

	public static void main(String[] args)  {
	    try {
			findClass();
	    }catch(ClassNotFoundException e) {
	    	System.out.println("例外処理:" + e.toString());
	    }
		
	}//main method -> Caller JVM(X)
		
		public static void findClass() throws ClassNotFoundException{
			Class.forName("java.lang.String2");
		}//find method -> Caller main method

}//class

例外処理:java.lang.ClassNotFoundException: java.lang.String2

public static void method() throws Exception(or Throwable)

様々な例外処理を押し付けたい場合は、最上位例外クラスを記入することで、throwsはできるが、おすすめはしない。

カスタム例外

ライブライから必要な例外クラスを提供していない場合、直接例外クラスを作成しなければならない。例外クラスをカスタムする場合は、一般例外もしくはランタイム例外を継承する。

public class XXXException extends [Exception | RuntimeException]{
   public XXXException(){
   }
   
   public XXXException(String message){
       super(message);
  }
}

例外クラスの作成が終わったため、次はどの条件で例外が発生し、インスタん化をして処理するか決めなければならない。

throw

throw(throwsと異なる)を通して例外を発生させることができる。

void method(){
   try{
      throw new Exception("message"); 
   }
   catch(Exception e){
     String message = e.getMessage;
   }
}

void method() throws Exception{
  
    throw new Exception("message") ;
}


①よりは②パターンの方が多い。①は自分で例外を発生させて、例外を処理する形で、まるで自分が自分の家を防火して自分で火を消すことと同じだ。

それでは、実習に入ってみる。

InsufficientException
public class InsufficientException extends Exception{

    public InsufficientException(){}
    public InsufficientException(String message){super(message);}
    //Exceptionにはmessage fieldがあるため(getMessageメソッドから推理できる。)
}
Account
public class Account{
   
    private long balance;
    
    public Account(){}
    
    public long getBalance(){
     return balance;
    }
    
    public void deposit(long money){
       this balance += money;
    }
    
    //withdrawはExceptionオブジェクトを生成するため、必ずtry-catchが必要!
     public void withdraw(long money) throws InsufficientException{
       if(money > balance) {
         throw InsufficientException("残高" + (money - balance) + "円不足");
       }
       this balance -= money;
    }
  }  
    
AccountExample
public class AccountExample{
  public static void main(String[] args){
 
  Account account = new Account();
  
  account.deposit(10000);
  System.out.println(account.getBalance());
 
 try {
   account.withdraw(30000);
 }catch(InsufficientException e) {
   System.out.println(e.getMessage());
 }		
   System.out.println("残高: " + account.getBalance());
 
 }
} 
残高: 10000
残高が20000円不足
残高: 10000

Coding Test

配列の中でNの倍数のみもっている配列をリターン

また、Collectionsは勉強したことがないが、初めて、ArrayListをチャレンジしてみた。
しかし、作ったリストを配列に変換することに苦戦した。
下が問題のコードだ。

import java.util.ArrayList;

class Solution {
    public int[] solution(int n, int[] numlist) {
        
       ArrayList<Integer> arr = new ArrayList<Integer>();
        
       for(int i= 0; i<numlist.length; i++){
           if(numlist[i]%n==0) arr.add(numlist[i]);
       } 
    
       int answer[] = arr.toArray(new int[arr.size()]);  
       return answer;
    }
}
int answer[] = arr.toArray(new int[arr.size()]);  
       return answer;

この、toArrayが問題らしい。今、Arraylistの中にある要素は、全部Integerクラスのオブジェクトのようだが、この場合は、簡単に変換ができないようだ。

import java.util.ArrayList;

class Solution {
    public int[] solution(int n, int[] numlist) {
        
       ArrayList<Integer> arr = new ArrayList<Integer>();
        
       for(int i= 0; i<numlist.length; i++){
           if(numlist[i]%n==0) arr.add(numlist[i]);
       } 
    
       int[] answer = new int[arr.size()];
       
       for(int i=0; i<answer.length; i++){
           answer[i] = arr.get(i);
       }
    
       return answer;
    }
}

結局、Brutal Forceで、int[] answerを生成し、いちいち入れて終わった。
さぞStreamを勉強したら、より洗練されたコードが作成できるでしょう。
悔しい!!!!!!

Discussion