본문 바로가기
혼자 공부하는 자바

혼자 공부하는 자바 (51강 ~ 55강)

by moca7 2024. 6. 14.

51강. 익명 객체(1) - 익명 자식 객체 생성

52강. 익명 객체(2) - 익명 구현 객체 생성, 익명 객체의 로컬 변수 사용

53강. 예외 클래스

54강. 예외 처리(1) - 예외 처리 코드

55강. 예외 처리(2) - 예외 종류에 따른 처리 코드, 예외 떠넘기기

 

 

 

 

51강. 익명 객체(1) - 익명 자식 객체 생성

 

 

ㅁ 익명 자식 객체는 상속과 관련, 익명 구현 객체는 인터페이스와 관련이 있다.

 

 

ㅁ 익명 anonymous 객체

- (클래스)이름이 없는 객체

- 어떤 클래스를 상속하거나 인터페이스를 구현하여야 익명 객체를 만들 수 있다.

 

 

 

(1) 익명 자식 객체

- 클래스이름1을 가지고 new해서 생성자 호출할 때 씀.

그리고 부모클래스 타입으로 변수를 선언해서 자식 객체를 대입(타입변환)하는 것이 가능. 

이렇게 상속할 때 명시적으로 클래스를 선언하고, 이 클래스 이름을 가지고 객체를 만들어서 부모 타입 변수에 대입하는 것이 일반적.

 

- 그런데 아래와 같이 new 다음에 부모클래스(). 이건 부모클래스의 생성자를 호출하는 꼴. 

근데 이 뒤에 중괄호가 붙어 있음. 중괄호는 클래스 선언부.

 

위에서 처럼 class키워드, 클래스이름, 그 다음 중괄호가 오는게 일반적인데

아래 같은 경우는 앞에 class 키워드와 클래스이름이 없음. 이름이 없는 클래스가 선언이 된다는 것.

 

앞에 부모 클래스 생성자꼴이 왔어요. 이건 부모클래스를 상속해서 중괄호와 같이 자식 클래스 내용을 작성하고, 이것을 가지고 객체를 생성한 다음에 부모 타입 변수에 대입을 한다는 뜻.  

 

객체가 생성되는 것은 자식 객체가 생성되는 거. 중괄호와 같이 선언된 자식 객체를 생성하는데, 부모클래스() 얘가 부모가 되어서(상속되서) 중괄호와 같이 선언된 자식클래스를 만들어서 부모클래스에 대입을 하는거.

 

자식 클래스의 이름이 없어서 익명 자식 객체, 줄여서 익명 객체라고 이야기 함. 

 

 

(2) 익명 구현 객체

- 우리가 어떤 클래스를 선언할 때, 이 클래스가 인터페이스를 구현해야 한다면 implements 하고 인터페이스를 위와 같이 명시적으로 언급하고 중괄호 안에 클래스 내용을 작성함. 

클래스이름2를 가지고 객체를 생성할 때, new 클래스이름2(); 이렇게 생성자를 호출해서 객체를 생성한 다음에 인터페이스 타입 변수에 대입(타입변환)을 한다.

구현 객체를 인터페이스 타입 변수에 대입한다.

- 근데 아래를 보면, new 다음에 인터페이스() 가 왔음.

인터페이스는 객체를 생성할 수 없는데 어떻게 이런 표현이 가능한가. 

만약 이 뒤의 중괄호를 생략을 한다면 자바 문법에 어긋남. 

 

인터페이스()는 생성자라는 이야기인데, 인터페이스는 생성자를 가지고 있지 않음.

이 인터페이스를 구현한 클래스를 만드는데, 이름이 없음. class 클래스이름2가 없음.

그렇지만 중괄호와 같이 선언을 하고, 인터페이스를 구현해서 만들라는 뜻.

 

내가 새로운 객체를 만드는데 그 객체는 중괄호와 같이 선언되고, 이 인터페이스를 구현해서 선언한 것이다라는 뜻.

이것을 가지고 객체를 만들어서 인터페이스 타입의 변수에 대입하겠다는 뜻.

 

이것을 익명 구현 객체, 줄여서 익명 객체라고 함. 

 

 

ㅁ 익명 자식 객체를 생성해서 이용하는 곳은 3군데가 있다.

 

(1) 필드를 선언할 때 초기값으로 익명 자식 객체를 생성하여 대입.

 

 

- Parent를 상속해서 이름없는 자식 클래스를 중괄호와 같이 만들고 객체를 만든 거.

 

 

(2) 메서드 내에서 로컬 변수 선언 시 초기값으로 대입.

 

 

 

(3) 매개 변수의 매개값으로 익명 자식 객체를 생성하여 대입.

 

 

 

 

ㅁ 익명 자식 객체의 멤버 접근 제한

 

 

- 익명 자식 객체를 만들게 되면 그 익명 객체를 대입할 수 있는 변수는 부모 타입이어야 한다.

그래서 부모 타입에 선언된 내용만 익명 자식 객체의 내용에 넣을 수 있다.

(변수의 유효 범위) (메서드는 ~ )

- 오버라이딩된 메서드는 항상 자식거가 호출되기 때문에 parentMethod()는 부모 쪽에도 있지만 자식 쪽의 것이 호출됨.

- 익명 자식 객체는 항상 부모 타입에 대입되기 때문에 부모에 선언된 것만 사용 가능.

 

- 그렇다면 익명 자식 객체 중괄호 내부에서는 자신만의 필드와 자신만의 메서드를 선언하는 의미가 없나?

있다. childField와 childMethod()는 외부에선 사용 못하지만 그 객체 내에서는 사용 가능. 

바깥쪽에선 불가능. 

 

 

 

 

52강. 익명 객체(2) - 익명 구현 객체 생성, 익명 객체의 로컬 변수 사용

 

 

ㅁ 익명 구현 객체 생성

 

(1) 필드 선언 시 초기값으로 익명 구현 객체 생성하여 대입하는 경우

 

 

(2) 메서드 내에서 로컬 변수 선언 시 초기값으로 익명 구현 객체 생성하여 대입하는 경우

 

 

 

(3) 매개 변수의 매개값으로 익명 구현 객체 생성하여 대입하는 경우

 

 

 

 

ㅁ 메서드의 매개 변수나 로컬 변수를 익명 객체 내부에서 사용할 때의 제한

 

- 컴파일 시 익명 객체에서 사용하는 매개 변수나 로컬 변수는 final 특성을 가짐. (값 변경 불가)

 

이어서 작성된 소스코드

 

- 익명 객체 내에서 arg1, arg2, var1, var2를 사용할 때 제한이 있다. 

- method 메서드가 종료되면 로컬 변수들은 다 사라지지만 익명 구현 객체는 힙 영역에서 계속 실행 상태로 존재할 수 있음. (쓰레드 관련)

 

이런 경우 메서드가 종료되어도 익명 구현 객체에선 저 변수(arg1, arg2, var1, var2)들을 계속 써야 함.

그래서 자바는 로컬 변수를 익명 객체 내에서 사용할 때는 로컬 변수의 값을 익명 객체 내부에 저장을 함.

 

변수가 근데 바깥의 값과 익명 객체 내부의 값이 똑같아야 하므로 final을 붙이지 않아도 final로 선언됨.

(자바 7까지는 반드시 final을 붙여야 하고, 자바 8부터는 안붙여도 됨.)

 

arg1, arg2, var1, var2의 값을 변경하려 하면 오류 발생. field는 바꿔도 오류 발생하지 않음.

 

 

 

 

53강. 예외 클래스

 

 

ㅁ 에러와 예외

- 에러: 컴퓨터 하드웨어 관련 고장으로 응용프로그램 실행 오류가 발생하는 것.

- 예외: 그 외 프로그램 자체에서 발생하는 오류.

 


ㅁ 자바는 예외를 두 가지로 구분함. 일반 예외와 실행 예외.

 

ㅁ if로 배열의 길이를 미리 확인하는 등의 코드는 예외 처리 코드는 아님. 예외가 발생하지 않도록 하는 코드다.

예외 처리 코드는 예외가 발생하고 나서 실행하는 코드.

 

 

 

ㅁ 예외 Exception

- 사용자의 잘못된 조작(ex. 숫자를 입력하는 곳에 문자 입력) 또는 개발자의 잘못된 코딩으로 인해 발생하는 프로그램 오류.

- 예외가 발생하면 프로그램은 그 즉시 종료되고 예외 내용을 출력함.

- 예외가 아예 발생하지 못하게 할 순 없지만, 예외가 발생했을 때 어떻게 처리를 해서 최소한 프로그램이 종료되지 않도록 하는 방법이 예외 처리 프로그램.

예외 처리 프로그램을 통해 프로그램이 (종료되지 않고)정상 실행상태 유지 가능

- 자바는 예외 발생 가능성이 높은 코드를 컴파일할 때 컴파일러에서 예외 처리 유무를 확인하고 알려주는 기능이 있다. 

 

 

ㅁ 일반 예외 exception

- 컴파일러가 컴파일을 할 때 일반 예외가 발생할 수 있는 코드를 발견하면, 예외 처리 코드가 있는지 없는지 검사함. 만약 예외처리 코드가 없다면 컴파일 오류를 발생시켜서 개발자가 오류 처리 코드를 작성하도록 함. 

- 자바 소스 컴파일 과정에서 해당 예외 처리 코드 있는지 검사. 

- 그래서 컴파일러 체크 예외라고도 함.

 

- 자바는 예외도 클래스로 관리함. 예외가 발생하면 해당 예외 클래스로 해당 예외 객체를 만들어서, 예외 처리 코드에서 예외 객체를 이용할 수 있도록 해줌.  

 

예외 클래스의 상속 관계

 

- 자바의 모든 예외는 Exception이라고 하는 부모를 가지고 있음. Exception은 모든 예외 클래스의 최상위 부모다.

아래의 예외들이 전부 다 Exception을 상속 받고 있다.

 

- Exception을 상속 받는 예외들 중 RuntimeException이 있는데, RuntimeException의 하위 예외 클래스들을 실행 예외라고 함.

 

 

ㅁ 실행 예외 runtime exception

- 말 그대로 실행할 때 발생할 수 있는 예외.

- 컴파일러는 실행 예외에 대한 부분은 체크하지 않음. 

- 실행 예외는 실행 시 예측할 수 없이 갑자기 발생함. 그래서 컴파일 과정에서 예외처리코드를 검사하지 않음.

- 실행 예외는 개발자의 경험에 의해서 예외 처리 코드를 작성해야 함. (여기서 이런 예외가 발생할 수 있을 거다.)

 

실행 예외의 종류


- RuntimeException을 상속 받아서 만들어진 예외는 모두 실행 예외다.

즉, 어떤 예외가 RuntimeException을 상속받고 있다면 실행 예외이고, 컴파일러가 이 예외들을 검사하지 않는다. 

 

 

ㅁ 실행 예외의 종류

 

(1) NullPointerException

- 가장 빈번하게 발생하는 실행 예외

- 객체 참조가 없는 상태의 참조 변수로 객체 접근 연산자 도트를 사용할 경우 발생.

 

ex) String str = null;             //         str이 어떤 객체를 참조하고 있지 않고 null을 가지고 있음.

str.length();                          //         도트(.)는 어떤 객체에 접근하라는 뜻.

 

 

(2) ArrayIndexOutOfBoundsException

- 배열에서 인덱스 범위를 초과할 경우

 

ex) int[] arr = {1, 2, 3};

int result = arr[0] + arr[3];

 

- 실행 매개값도 마찬가지

 

public static void main(String[] args) {

   String data1 = args[0];

}

 

i. 이클립스에서 실행 버튼 옆 화살표 - Run Configurations - Arguments - Program Arguments에서 실행 매개값 넣으면 예외가 발생하지 않음. 값은 스페이스바로 구분.

 

ii. 커맨드 라인에서 java Main Hello World 123하면 Hello, World, 123이 각각 args 배열의 요소로 전달.

 

 

 

(3) NumberFormatException

- 숫자로 변환할 수 없는 경우에 발생함. 

 

문자열을 숫자로 변환하는 경우

 

- Integer.parseInt(String s)는 매개값으로 받는 String을 int 타입으로 변환하는 코드.

만약 이 String에 숫자가 아닌 문자로 구성된 문자열이 들어오면 발생.

A가 들어오면 정수로 변환이 불가능.

 

 

(4) ClassCastException

- 클래스 강제 타입 변환을 잘못할 때 발생하는 실행 예외.

- 상위 및 하위 클래스 그리고 구현 클래스와 인터페이스 간에는 타입 변환이 가능.

 

 

 

 

 

54강. 예외 처리(1) - 예외 처리 코드

 

 

ㅁ 프로그램에서 예외가 발생했을 경우 반드시 예외 처리를 해야 한다.

- 예외 처리의 목적은 프로그램의 갑작스러운 종료를 막고, 정상 실행을 유지할 수 있도록 하는 것이다.

 

 

ㅁ 예외 처리 코드

- 자바 컴파일러는 일반 예외를 발생시키는 코드를 발견할 경우 예외 처리 코드를 강제로 요구

- 실행 예외는 컴파일러가 체크하지 않으므로 개발자가 경험으로 예외 처리 코드 작성

 

 

ㅁ try-catch-finally 블록

- 예외 처리 코드라고 하면 try-catch-finally를 말하고, try-catch-finally는 블록을 가질 수 있다.

- 생성자 및 메서드 내부에서 작성되어 일반예외와 실행예외가 발생할 경우 예외 처리를 가능하게 함.

 

 

- finally는 옵션. 있을 수도 있고 없을 수도 있음. 

정상 실행되든 예외가 발생하든 항상 실행하는 코드.

 

 

ㅁ 예제

-

public static void main(String[] args) {

    String data1 = null;
    String data2 = null;

    try {
      data1 = args[0];
      data2 = args[1];
    }
    catch(Exception e) {
      System.out.println("실행 매개값의 수가 부족합니다.");
      return;
    }

    finally{

      System.out.println("가나다라마바사");

    }

 

   System.out.println("ABCDEFG");

 

}

- 실행 매개값을 하나만 준 경우.

- return이 있으면 메인 메서드를 종료시킨다.

"ABCDEFG"는 출력되지 않지만, "가나다라마바사"는 출력된다.

 

- return 문은 프로그램 흐름을 제어하는 중요한 도구입니다. 예외가 발생했을 때 이후의 코드가 실행될 필요가 없거나, 잘못된 데이터로 인해 문제가 발생할 수 있다면 return을 사용하여 메서드를 종료하는 것이 좋습니다. 그러나 예외 발생 후에도 코드가 계속 실행되어야 하거나, 일부 후속 처리가 필요할 경우 return을 사용하지 않아야 합니다. 이를 통해 프로그램의 예외 상황을 어떻게 처리할지 결정할 수 있습니다.

 

 

public class Main {

    public static void main(String[] args) { System.out.println(testMethod()); }

 

    public static String testMethod() {

        try {

             System.out.println("try 블록 시작");

             return "try 블록의 return";

        } catch (Exception e) {

              System.out.println("catch 블록 실행");

              return "catch 블록의 return";

         } finally {

              System.out.println("finally 블록 실행");

         }

      }

}

 

- 실행 결과

try 블록 시작

finally 블록 실행

try 블록의 return

 

- return보다 finally가 먼저 실행된다. (try든 catch든) return 문이 호출되더라도, 그 전에 finally 블록이 반드시 실행됩니다.

- finally 블록은 항상 실행된다. 예외가 발생하든 발생하지 않든, return 문이 있든 없든, finally 블록은 항상 실행됩니다.

 

 

 

 

55강. 예외 처리(2) - 예외 종류에 따른 처리 코드, 예외 떠넘기기

 

 

ㅁ 다중 catch

- 발생하는 예외별로 예외 처리 코드를 다르게 하는 다중 catch 블록을 가질 수 있음.

 

 

- try 첫번째 코드에서 첫번째 예외가 발생한 경우, try 두번째 코드는 실행하지 않고, catch의 해당 예외 블록을 실행하고, try-catch 블록을 빠져나감.

- try 블록에 여러 개의 예외가 발생할 수 있을 경우, 예외의 수 별로 catch를 만들어서 각각 예외처리를 따로 할 수 있다.

- (오른쪽) 이건 catch를 잘못 작성한 것. 모든 예외는 Exception을 상속받기 때문.

두번째 catch는 결코 실행되지 않기 때문에 자바 컴파일러는 컴파일 에러를 발생시킴.

catch 두 개의 순서를 바꾸면 괜찮아짐.

 

 

ㅁ throws 키워드

- 메서드에서 처리하지 않은 예외를 호출한 곳으로 넘기는 역할.

- 메서드를 호출한 곳에서 다양한 방식으로 처리할 수 있도록 함.

(메서드2를 메서드1에서도 호출하고, 다른데서도 호출하고 다 다른방식으로 처리)

- throws 키워드 뒤에는 떠넘길 예외 클래스를 쉼표로 구분하여 나열.

(이 예외 클래스를 내가 처리하지 않고 호출한 곳으로 떠넘기기.)

 

 

 

ㅁ 이클립스에서 Unhandled exception떠서 surround with try/catch해서 나오는 e.printStackTrace();는 현재 발생한 예외 코드의 내용을 추적해서 프린트해줌. 이 예외가 어디서 발생했고 왜 발생했는지. try에 몇십줄, 몇백줄도 들어갈 때.

- 개발 완료된 프로그램에는 빼고 개발 중에 디버깅 용도로 사용.

- main 메서드 throw 붙여서 JVM에 떠넘기는 것과 같음.