プログラミング自主学習 DAY62 Exception/Coding Test
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") ;
}
①よりは②パターンの方が多い。①は自分で例外を発生させて、例外を処理する形で、まるで自分が自分の家を防火して自分で火を消すことと同じだ。
それでは、実習に入ってみる。
public class InsufficientException extends Exception{
public InsufficientException(){}
public InsufficientException(String message){super(message);}
//Exceptionにはmessage fieldがあるため(getMessageメソッドから推理できる。)
}
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;
}
}
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