본문 바로가기
Development (국비 복습 )/자바

[2023-03-30]예외처리,제네릭,

by Puddingforever 2023. 3. 30.

메소드에 마우스를 갖다댓을때 , throws ~~~ 가 나온다면 반드시 예외처리를 해줘야한다 

Class.forName(객체 이름을 문자열로 넣어줌) : 객체가 있는지 확인하는 기능 

해당 객체로 메모리로 생성해서 클래스 타입 객체로 만들어준다. 

 

클래스 타입이 final이라서 상속을 해줄 수는 없다. 

public final class Class<T>
extends Object
implements Serializable, GenericDeclaration, Type, AnnotatedElement

 

forName 메소드 

public static Class<?> forName(String className)
                        throws ClassNotFoundException
Returns the Class object associated with the class or interface with the given string name. Invoking this method is equivalent to:
Class.forName(className, true, currentLoader)
where currentLoader denotes the defining class loader of the current class.

For example, the following code fragment returns the runtime Class descriptor for the class named java.lang.Thread:

Class t = Class.forName("java.lang.Thread")

A call to forName("X") causes the class named X to be initialized.

즉 클래스 타입의 인스턴스를 만들어줌 

 

 

Class myClass = null;
		try {
			myClass = Class.forName("java.lang.String");
			System.out.println("String 객체가 생성됨 . ");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		
		System.out.println("main 메소드 종료 ");

 

 

try{} 블록에서 예외(오류)가 발생되면 해당 try{} 이후의 실행문들을 실행하지 않고 catch{} 블록으로 이동하여 catch{} 블록에 있는 실행문들이 실행된다. 

 

만약 try{} 블록 실행문들이 정상적으로 실행된다면 try-catch를 종료하고 그 이후에 명시된 실행문들이 실행된다. 

 

catch()안의 파라메터 타입인 ClassNotFoundException 은 내가 마음대로 준 것이 아니라 , 아까 메소드를 클릭했을 때 throws  ClassNotFoundException 이렇게 나왔기 때문에 파라메터 타입을  ClassNotFoundException로 주는 것이다. 

 

모든 예외 클래스들은 printStackTrace()라는 메소드를 가지고 있다. 이 메소드의 기능은 오류 내용을 알려준다. 

 

 

 

Throwable이 가장 상위클래스이며 , 밑의 예외 클래스나 인터페이스들은 Throwable 클래스를 상속받는다. 

 

 

 
오류 콘솔 보는 법
원인이 가장 위에 나오고.
중간은 처리과정
가장 밑 라인에 원인의 코드라인이 보인다. 

java.lang.ClassNotFoundException: java.lang.String2

at java.net.URLClassLoader.findClass(URLClassLoader.java:387)

at java.lang.ClassLoader.loadClass(ClassLoader.java:418)

at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)

at java.lang.ClassLoader.loadClass(ClassLoader.java:351)

at java.lang.Class.forName0(Native Method)

at java.lang.Class.forName(Class.java:264)

at basic02_try_catch.TryCatchExample.main(TryCatchExample.java:10)

 

위에서 URLClassLoader.findClass 를 먼저 보면 클래스가 확인되지 않았다고 나옴

그 다음, 맨 밑줄 (TryCatchExample.java:10)를  찾아본다. 

 

 

try-catch 쓰는 이유 : 오류가 발생해도 다른 실행문을 실행할 수 있도록 만들어 주기 위함이다. 

하지만 catch문에 로직을 구현하지는 않고 , 오류가 발생해도 프로그램이 정상적으로 끝날 수 있도록 만들어 주기 위함이다. 

 

 

 

 

출처 : programiz.com

 

에러나 예외처리의 경우 모두 Throwable 클래스를 상속받고 있다.

 

Error : JVM에 메모리의 문제, 라이브러리 부족 문제 등으로 일어나며 개발자가 컨트롤 할 수 없는 분야이다,

 

Exceptions : Error와는 다르게 프로그램 내부에서 핸들할 수 있는 것을 말한다.

메소드에서 예외가 발생하면 , 아까  ClassNotFoundException처럼 Exception 이라는 객체를 상속받은 

어떠한 예외 객체를 만들어낸다.

Exception객체는 예외원인 이름이나 설명같은 것들을 개발자에게 알려준다. 

 

 

Exception객체는 RuntimeException과 IOException을 가지고 있다. 

 

1. RuntimeException

 

프로그래밍 에러로 인해 생기는 에러이다. unchecked exceptions 라고도 불리우며,

컴파일 시가 아닌 런타임시에 생기는 에러이다.

 

원인

-Improper use of an API (IllegalArgumentException) - API를 잘못 썻을 때

-Null pointer acess ( missing the initializing of a variable ) - NullPointerException - 객체 초기화를 하지 않았을 때

-Out-of-bounds array access - ArrayindexOutOfBoundsException - 배열길이를 어겼을 때

-Dividing a number by 0 - ArithmeticException -  int 정수형의 경우 0으로 나누려고 하면 에러가 난다 (double이나 float은 에러 안남) 

 

즉 RuntimeException 이 일어난것은 ,,,, 프로그램을 짠 개발자의 잘못이다.

 

NullPointerException같은 경우도 , 변수가 초기화가 되어있었다면 일어나지 않을 일이고, 

ArrayIndexOutOFBoundsException도 배열 길이를 잘 확인했다면 일어나지 않았을 예외이다.

 

 

2.IOException 

IOException 은 checked exception이라고도 불리는데 컴파일시에 생기는 에러이다. 

 

원인

-자바 파일이 존재하지 않는데 쓰려고 하는 것 (import 안해서 생기는 에러 ) FileNotFoundException

- 자바 객체를 끝내고 나서  읽으려고 할 때(sc.close()를 했는데 읽으려고 할 때) 

 

 

IOException이나 RuntimeException은 정상적으로 프로그램이 종료되지 않는다는 것을 뜻해서 

프로그램을 정상적으로 끝내기 위해 try-catch, final block, throw 같은 걸 사용해서 예외의 경우를 만들어줘서 프로그램이 정상적으로 끝날 수 있도록 하는 것이 좋다. 

 


예외처리 핸들링 방법 

 

1.try catch

 

		Class myClass = null;
		try {
			myClass = Class.forName("java.lang.String2");
			System.out.println("String 객체가 생성됨 . ");
		} catch (ClassNotFoundException e) {
			System.out.println("클래스가 없습니다.");
           // e.printStackTrace();
		}
	
		System.out.println("main 메소드 종료 ");

클래스가 없습니다.

main 메소드 종료   < ---- 에러 나든 안나든 실행됨 

 

2.try-catch-finally

try {
			myClass = Class.forName("java.lang.String2");
			System.out.println("String 객체가 생성됨 . ");
		} catch (ClassNotFoundException e) {
			System.out.println("클래스가 없습니다.");
			return; 
		}finally {
		System.out.println("예외발생 상관없이 반드시 실행 ");
		}
		
		System.out.println("메소드 종료"); < --- finally 있어서 에러나면 실행안됨 ,

클래스가 없습니다.

예외발생 상관없이 반드시 실행

 

 

오류가 났기 때문에 메소드 종료는 출력되지 않았지만 , finally{} 문은 실행되었다. 

예외발생 유무와 상관없이 항상 실행하고 싶을 때 finally{} 블록안에 넣어준다.

주의!! 정상적으로 처리가 되도 finally안의 코드는 실행된다. 

 

주의!!! finally가 써잇는 경우 , 오류가 났을 때 try-catch-finally 뒤의 코드는 실행되지 않는다. 

System.out.println("메소드 종료"); <---- 오류가 나면 안나옴 

 

 

 

3.try - catch

try {	
		String data1 = args[0];
		String data2 = args[1]; //runtimeException , java.lang.ArrayIndexOutOfBoundsException	
		int value1 = Integer.parseInt(data1);
		int value2 = Integer.parseInt(data2);
		int result = value1 + value2;	
		System.out.println(value1+ "+" + value2 + "=" +result);
		System.out.println();	
		} catch(ArrayIndexOutOfBoundsException e) {
			System.out.println("2개의 값을 입력하세요. ");
		} catch(NumberFormatException e) {
			System.out.println("2개의 숫자 값을 입력하세요. ");
		}
        
        		System.out.println("main 실행 종료");

2개의 숫자 값을 입력하세요.

main 실행 종료

 

args[] 를 한개를 20a를 줘서 숫자값을 처리 못하는 경우였다. 따라서 NumberFormatException이 있는 catch문이 실행되었다. 예외처리 경우를 여러개 만들고 싶을 때 쓴다. 

 

주의점 !! Catch문이 여러개 있어도 가장 첫번째꺼만 실행되고 뒤는 실행되지 않는다.

catch(ArrayIndexOutOfBoundsException e) {
			System.out.println("2개의 값을 입력하세요. ");
			e.getMessage();
		} catch(NumberFormatException e) {
			System.out.println("2개의 숫자 값을 입력하세요. ");
			e.getMessage();
		} catch(RuntimeException e) {
			System.out.println("실행에 문제가 있습니다.");
			e.getMessage();
		}catch(Exception e) {
			System.out.println("실행에 문제가 있습니다. ");
			e.getMessage();
		}
        
        System.out.println("main 실행 종료");

catch가 아무리 많아도 가장 가까운 딱 하나만 실행됨 

 

2개의 숫자 값을 입력하세요.

main 실행 종료

 

 

주의!!

Exception은 범위가 NumberFormatException, RuntimeException보다 범위가 넓기 떄문에 뒤에 catch문 쓸 때 에러가 난다. 

 

따라서 catch문 적을 때 하위클래스부터 상위클래스 순으로 적어야한다. 

범위가 넓은 객체를 위에 적으면 안되고 가장 밑에 적어야한다. 

 

올바른 코드! 

 

 

 

3.다중예외 catch문

catch문 코드를 똑같은걸 쓰고 싶을 때 | 를 써서 파라메터로 가져올 수 있다 

catch(ArrayIndexOutOfBoundsException | NumberFormatException e) {
			System.out.println("2개의 값을 입력하세요. ");
			e.getMessage();
		} catch(RuntimeException e) {
			System.out.println("실행에 문제가 있습니다.");
			e.getMessage();
		}catch(Exception e) {
			System.out.println("실행에 문제가 있습니다. ");
			e.getMessage();
		}

두 개중 하나가 발생하면 코드 실행

 

 

주의!!!! 동일 레벨의 객체끼리만 파라메터 안에 쓸 수 있다. 

catch(ArrayIndexOutOfBoundsException | NumberFormatException | RuntimeException e) {
			System.out.println("2개의 값을 입력하세요. ");
			e.getMessage();
		}

RuntimeException 이 상위클래스 개념이기 때문에 에러가 난다. 

따라서 동일 레벨에 있는 클래스끼리만 | 를 사용할 수 있다. 

 


 

Generic 

 

BoxNonGeneric 

public class BoxNonGeneric {
	
	private Object value;
	
	public void set(Object value) {
		this.value = value;
	}
	
	public Object get() {
		return this.value;
	}
}

 

Apple

public class Apple {
	
	

}

 

 

 

메인메소드

 

		BoxNonGeneric myBox = new BoxNonGeneric();
		
		Object myObj = null;
        
		myObj = new String("홍길동"); // String->Object 자동타입변환
		myBox.set(myObj);
		System.out.println("담긴 값: " + myBox.get());
	
		myObj = new Integer(10); //Integer ->Object 자동타입변환
		myBox.set(myObj);
		System.out.println("담긴 값: " + myBox.get());
		
		
		myObj = new Apple(); 
		myBox.set(myObj); //Apple -> Object 자동타입변환
		System.out.println("담긴 값: " + myBox.get());

 

 

이러한 자동타입 변환을 방지하려고 나온 것이 제네릭 표기법이다. 

 

 


제네릭 클래스 만드는 방법

 

실행메소드

public static void main(String[] args){

	//Integer객체를 통해 제네릭 클래스를 만든다 
    GenericsClass<Integer> intObj = new GenericsClass<>(5);
    System.out.println("Generic class returns : " + intObj.getData());
    
    
    //String 객체를 통해 제네릭 클래스를 만든다
    GenericsClass<String> stringObj = new GenericsClass<>("Java Programming");
    System.out.println("Generic class returns : " + stringObj.getData());
}

 

제네릭이 사용된 클래스 

 

class GenericsClass<T>{

	//필드
	private T data;
    
    //생성자
    public GenericsClass(T data){
    	this.data = data;
    }
    
    //메소드
    public T getData(){
    	return this.data;
    }

}

 

 

 

출력결과

 

Generic Class returns: 5
Generic Class returns: Java Programming

 

 

[ 제네릭 클래스 만드는 방법 ]

1.클래스 옆에 제네릭을 쓰겠다고 파라메터 <T>를 선언해준다.

2.필드., 생성자를 파라메터를 그대로 가져와서 선언한다.

3.메소드의 경우 리턴타입은 상관없아 void int 등등 아무거나 해도 됨 ! 

 

이렇게 T로 파라메터 타입을 받을 수 있도록 한다면 어떠한 타입으로도 객체를 생성할 수 있다. 

 

class GenericsClass<T> {....}

 

<>안에 있는 T는 타입 파라메터라고 부른다. 

 

 

    GenericsClass<Integer> intObj = new GenericsClass<>(5);

 

intObj인스턴스를 확인해보자 . GenericsClass를 만들 때 ., 제네릭 인자값으로 Integer를 주었다. 

GenericsClass 내부는 필드와 생성자를 모두 T를 사용하도록 만들었기 때문에 이제 객체는 Integer타입으로 생성이 되는 것이다.

 

    GenericsClass<String> stringObj = new GenericsClass<>("Java Programming");

 

stringObj 인스턴스에서는 제네릭의 인자값으로 String을 주었기 때문에 앞으로 GenericsClass에서 사용되는 생성자와 필드 타입이 String이 된다. 즉 String타입의 객체가 만들어지는 것이다. 


 제네릭 메소드 

 

실행 메소드

public static void main(String[] args){

//Integer 데이터로 인스턴스를 만든다 
DemoClass demo = new DemoClass();


//String값으로 제네릭 메소드가 사용되는 방법
demo.<String>genericsMethod("Java Programming");

//integer값으로 제네릭 메소드가 사용되는 방법
demo.<Integer>genericsMethod(25);

}

 

제네릭 메소드가 있는 클래스

class DemoClass{

	public <T> void genericsMethod(T data){
    
    	System.out.println("Generics Method: ");
        System.out.println("Data Passed : " + data);
       
    }

}

 

 

출력값

Generics Method:
Data Passed: Java Programming
Generics Method:
Data Passed: 25

 

 

선언 순서 

접근제한자 제네릭선언 반환타입 메소드이름(파라메터) 

 

genericsMethod라는 <T> 타입을 파라메터로 받는 제네릭 메소드를 만들었다. 

public <T> void genericsMethod(T data){,,,,}

 

 

이제 실행 메소드에서 제네릭 메소드를 사용해주면 된다. 

demo.<String>genericMethod("Java Programming");

demo.<Integer>genericMethod(25);

 

사실 제네릭 메소드를 쓸 때 굳이 타입을 지정해주지 않아도 괜찮다.

demo.genericsMethod("Java Programming");

이렇게만 적어줘도 컴파일러에서 자동으로 제네릭을 String으로 지정해준다. 

 

 

제네릭 메소드를 사용하는 이유 !

파라메터 타입을 여러개를 받고 싶기 때문에 사용한다. 


 

 

 

제네릭 메소드 선언 방법

접근제한자(static) 제네릭선언 반환타입 메소드이름(파라메터)

<T extends A>

T는 A를 상속받은 타입만 쓸 수 있다.  즉 A는 항상 부모 클래스만 쓰는 것이 관례이다. 

 

<T extends Number> : T는 Number 타입을 상속받은 타입만 사용할 수 있다

 

public class Util {
	public <T extends Number> int compare(T t1, T t2) {
		System.out.println("t1에 대입된 객체타입 : " + t1.getClass().getName());
		System.out.println("t2에 대입된 객체타입 : " + t2.getClass().getName());
		System.out.println();
		double v1 = t1.doubleValue(); // t1을 double로 바꿔줌 ! 
		System.out.println("v1: " + v1);
		
		double v2 = t2.doubleValue();   //t2를 double로 바꿔줌 ! 
		System.out.println("v1: " + v1);
		
		return Double.compare(v1, v2); // v1 <v2 : -1 반환 
										// v1 = v2 : 0반환
										// v1 > v2 : 1반환 
	}
}

 

public static void main(String[] args) {
Util myUtil = new Util();
// myUtil.compare("홍길동","이순신"); Number를 상속받는 클라스만 사용이 가능해서 string을 쓰면 에러남
myUtil.compare(10,10);
//Integer t1, Integer t2 -> 둘다 원래는 int 타입인데 , 제네릭에서 클래스를
//쓰도록 만들었기 때문에 저절로 Integer로 변환되었다.

}

 

t1에 대입된 객체타입 : java.lang.Integer

t2에 대입된 객체타입 : java.lang.Integer

v1: 2.0

v1: 2.0

 

따라서 코드 안에서 로직이 실행될때는 Integer 값으로 들어가게 되는 것이지만 , 

double로 인해서 자동 타입 변환이 일어났고 

compare()메소드로 인해서 int 타입으로 결과가 나온 것이다. 

 

 


 toString();

 

Object의 메소드임.

문자형으로 값을 반환함

public class Person {
	
	private String name;
    
	public Person(String name) {
		this.name = name;
	}
	
	public String getName() {
		return name;
	}
	
	@Override
	public String toString() {
		return name;
	}
	

}

 

toString으로 인해서 , 굳이 getter를 쓰지 않아도 필드값을 가져올 수 있다. 

 

source -> generate toString() 하면 자동으로 생김

 

@Override

public String toString() {

return "Person [name=" + name + "]";

}

 

필드가 전부 private이라서 디버그하는 경우, getter를 쓰는 경우가 많이 때문에 더 쉽게 하려고 toString()으로 만들어준다. 

 

 

 


 

<? extends B> : B를 포함해서 B를 상속받은 자식 클래스들을 타입으로 쓸 수 있다. 

 

 

//? 는 Student를 포함해서 상속받은 자식 클래스들

public void registerCourse2(Applicant<? extends Student> applicant) {

System.out.println(applicant.kind.getClass().getSimpleName() +

"이(가) Course2를 등록함");

}

 

 

<? super B> : B를 포함해서 B를 기준으로 부모 클래스 (부모클래스가 여러개면 , 다 포함함) 를 타입으로 쓸 수 있다. 

 

//?는 Worker를 포함해서 Worker를 기준으로 부모 클래스 (부모클래스 2개면 그것도 다 받음 , 즉 부모클래스는 갯수 상관없다) 

public void registerCourse3(Applicant<? super Worker> applicant) {

 

}

 

 

<?> : 모든 타입의 클래스 가능

 

 


 

 

댓글