BEYOND SW 캠프 21기 6회차 회고

컨테이너·2025년 11월 2일
post-thumbnail

📝6회차 회고록ㅣBEYOND SW캠프


🖥️ 6회차 학습 (25.10.27 ~ 25.10.31)

지난주에 이어 JAVA 후반부의 내용을 배웠다. 중요한 내용들인 만큼 하루하루 소화해 내는데 바빴던 한 주였던 것 같다. 배운 내용은 아래와 같다.

Chap09. 다형성
Chap10. API
Chap11. 예외처리
Chap12. 입출력
Chap13. 제네릭스
Chap14. 컬렉션-List

내용 하나하나 하루만에 다 습득하기란 물론 불가능한 내용들이지만, 이번 회고도 새롭게 배우고 기억해야 하는 내용들 위주로 작성해 보겠다.

👍 KEEP


  • 위 내용들을 접하기 전에 Playdata에서 제공한 Java 프리코스 강의를 주었었는데, 미리 들었던 것이 도움이 많이 되었다.

✏️ NEW


1. 다형성

다형성이란 상속이라는 개념을 통해 하나의 인스턴스가 여러 타입을 가질 수 있음을 이야기한다. 이를 통해 인터페이스와 구현을 통해서 프레임워크를 제공하는 등 이는 자바에서 매우 핵심적인 내용이라고 배웠다.

먼저 상속에 대한 내용을 복기해 보자. 부모로부터 자식이 상속을 받는 이미지를 확인해보면
위와 같이 자식 클래스가 부모 클래스를 extend받고 인스턴스를 생성한다면 부모 클래스를 가진채 Heap 영역에 주소값을 가지고 생성된다. 그렇다면 다형성을 이미지로 표현하면 어떨까

부모 타입의 자식 인스턴스 주소값에 접속하여 자신의 클래스에 접속하는 이미지를 확인해볼 수 있다. 이는 그림에도 나와있듯이 타입을 지정해주는 클래스는 자기 자신에게 밖에 들어갈 수 밖에 없기 때문에 그러하다는 것이다.
Parent par = new Child(); 로 표현은 가능하지만, 사실 자식 클래스에는 직접 접근을 할 수 없는 상태이다. 이 때, 자식 클래스에 접근할 수 있도록 부모클래스를 Child로 형변환 시켜줄 수 있다. 이를 다운캐스팅 이라고 한다.

1.1 동적 바인딩과 업/다운 캐스팅

1.1.1 동적 바인딩

다형성은 먼저 동적 바인딩을 이해해야 할 것 같다. 부모 클래스와, 부모 클래스를 상속하는 자식 클래스가 있다고 해보자. 부모 클래스의 메소드를 자식 클래스에서 오버라이딩한 상태이다. 그리고 다형성을 적용하여 아래 코드와 같이 부모 타입으로 자식 인스턴스를 만들었다.

Parent p = new Child();

그러면 아래 이미지와 같이 된다.

위 그림 처럼, 컴파일하여 인스턴스 pmethodA() 에 접근한다면, 오버라이딩 된 메소드에 접근한다. 이 것이 동적 바인딩이다.

즉, 하위 클래스가 상위 클래스의 메소드를 상속받아 오버라이딩을 하면 우선권이 하위 클래스에 있다는 것이다.

1.1.2 업캐스팅과 다운 캐스팅

동적 바인딩으로 인해 접근 못하는 영역을 접근할 수 있게 하는 것이 캐스팅, 즉 형변환이라는 것을 배웠다.

업케스팅 : 자식이 부모로 형변환
다운케스팅 : 부모가 자식으로 형변환

아래는 형변환의 명시적, 묵시적 표현 방법을 나타낸다.

  • 명시적/묵시적 형변환

1.2 다형성과 메소드 오버로딩

메소드 오버로딩을 진행할 때, 각각의 하위 클래스의 메소드를 하나하나 작성하려면 하위 클래스의 갯수대로 작업 해야 하지만, 상위 클래스를 사용하여 오버롣딩을 하면 효율적으로 작업할 수 있음을 배웠다.

public void feed(Rabbit rabbit) {} -> 하위 클래스 ❌
public void feed(Tiger tiger) {}   -> 하위클래스  ❌
// 위 작업을 상위클래스에 작성하면 한번에 끝난다.
public void feed(Animal animal) {  -> 상위클래스 오버로딩! ⭕
        animal.eat();
  • 이 때, 상위 클래스 오버로딩 메소드에 하위 클래스가 인자로 들어갈 때는 업캐스팅이 일어난다.
public void feed(Animal타입..) 
feed(Tiger타입...) => 업케스팅

이렇게 업케스팅되어 feed()가 동작한다면 결과는 아래와 같이 나올 것이다.

animal이 울음소리를 냅니다.

1.3 메소드 Return과 다형성

다형성을 사용하지 않는다면 리턴 메소드를 하위 클래스만큼 메소드를 정의해야 할 것이다. 리턴은 한 가지의 타입만 가능하기 때문이다.

다형성을 사용하여 상위 클래스만으로 메소드를 작성하여 리턴 받을 때 효율적으로 코드를 작성할 수 있다.

public class Application4 {

    public static void main(String[] args) {

        Application4 application4 = new Application4();

        Animal randomAnimal = application4.getRandomAnimal();
        randomAnimal.cry();
    }

    // 메소드
    public Animal getRandomAnimal() {
        int random = (int) (Math.random() * 2); //0, 1
        
        return random==0?new Rabbit():new Tiger();//삼항 연산자 사용.
    }
}

Tiger 타입이 나오든 Rabbit 타입이 나오든 Animal을 통해 리턴을 넘겨줄 수 있다.

1.4 Instanceof 연산자

instanceof 연산자는 레퍼런스 변수가 실제로 어떤 클래스 타입의 인스턴스인지 확인하여 true or false를 반환한다.

2. 추상 클래스와 인터페이스

2.1 추상 클래스란

추상 클래스를 정의하는 몇 가지 규칙이 아래와 같이 있다.

  • 추상메소드 0개 이상 포함하는 클래스

  • 추상 클래스는 클래스 선언부에 abstract 키워드를 명시

  • 인스턴스 생성 불가
    (추상 클래스를 상속받는 하위 클래스를 이용해서 인스턴스를 생성해야한다. 이 때 추상 클래스는 상위 타입으로 사용될 수 있으며, 다형성을 이용할 수 있다.)

추상 메소드란??

⇒ 메소드의 선언부만 있고 구현부가 없는 메소드를 추상메소드라고 한다. 추상메소드의 경우 반드시 abstract키워드를 메소드 헤드에 작성해야 한다.

ex) public abstract void method();

추상메소드 작성

추상클래스에 작성한 추상메소드는 반드시 후손이 오버라이딩해서 작성해야하며, 후손 클래스들의 메소드들의 공통 인터페이스로의 역할을 수행할 수 있다. 추상클래스에 작성한 메소드를 호출하게 되면 실제 후손 타입의 인스턴스가 가지는 메소드로는 다형성이 적용되어 동적바인딩에 의해 다양한 응답을 할 수 있게 된다.

추상 클래스 상속

추상클래스를 상속받아 구현할 때는 extends 키워드를 사용하며 → 자바에서는 extends로 클래스를 상속받을 시 하나의 부모 클래스만 가질 수 있다(단일상속).

부모에 선언된 추상 메소드는 자식클래스에서 반드시 오버라이딩 해야 한다.

메소드를 개발하여 사용하려 하는데 메소드가 사용자마다 그떄그때 달라지는 경우가 있다. 하지만 명칭은 통일된 경우, 일반 클래스를 추상 클래스로 만들어서, “같은 이름”으로 다르게 작동하는 오버라이딩을 강제성으로 부여한다. “같은 이름” 이란, 동일한 이름과 타입도 포함된다.

2.2 추상 클래스의 특징

  • Field와 생성자를 가질 수 있다. 단, 인스턴스는 생성할 수 있다.
  • 추상 클래스는 일반적인 메소드를 가지지 못한다.
  • 일반 클래스는 추상 메소드를 가지지 못한다.

2.3 추상 클래스의 다형성

추상클래스의 자식의 메소드로 오버라이딩하여 동적 바인딩이 일어난다.
정적 바인딩 → Product, 동적 바인딩 → SmartPhone

3. 인터페이스

3.1 인터페이스의 특징

클래스 종류ClassAbstract ClassInterface
구성 요소필드/메소드필드/메소드/추상메소드상수필드/추상메소드
상속을 했을 경우 부모 메소드의 강제성없음추상메소드 0개 이상정해진 규칙
  • 인터페이스 상수

    인터페이스에는 상수와 추상 메소드만 작성 가능하다. 변수는 상수만 작성 가능하기에, 인터페이스에 작성되는 모든 변수는 상수이다. 인터페이스 필드에 작성되는 모든 변수는 public final static을 모두 포함한다.

  • 인터페이스 생성자

    인터페이스는 생성자를 가지지 않는다.

  • 인터페이스는 구현부가 있는 non-static 메소드를 가질 수 없다.

    public void nonStaticMethod() {} ⇒ ❌

  • 인터페이스 메소드

    인터페이스 안에 작성한 메소드는 묵시적으로 public static abstract 이라는 의미를 가진다. 다른 접근제한자 사용 불가하다.

    따라서 인터페이스의 메소드를 오버라이딩하는 경우 반드시 접근제한자를 public으로 해야 오버라이딩 가능하다.

3.1.1 JDK 1.8에 추가된 인터페이스 기능

  • Static 메소드에 대해 사용할 수 있도록 해 두었다.
    public static void staticMethod() {
        System.out.println("InterProduct 클래스의 staticMethod 호출됨..");
    }
  • Default 를 추가하여 메소드를 동작하도록 해 두었다.
    public default void defaultMethod() {
        System.out.println("InterProduct 클래스의 defaultMethod 호출됨..");
    }

3.2 인터페이스 구현

  • 인터페이스를 구현하기 위해서는 implements 키워드를 사용한다.
class Person extends SuperClass implements Instinct, Serializable {

		@Override
		public void eating(String food) {
				System.out.println("사람은 " + food + "를 요리하고 식기를 활용해 음식을 먹는다.");
    }
}

3.3 인터페이스 사용 목적

인터페이스는 클래스들의 상위 부모 역할을 주어 상수와 추상 메소드 등 공유 목적 필드와 메소드를 작성하여 공통성을 강제로 가지게 만들어졌다.

인터페이스는 특수적으로 하나의 클래스가 부모 클래스를 상속하는 것 외에 여러 개의 인터페이스를 구현(implement)할 수 있다.

4. API-wrapper

wrapper 클래스는 기본 타입의 데이터를 인스턴스화 해야 하는 경우에 사용한다.

기본 타입래퍼 클래스
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

4.1 Wrapper Class

4.1.1 Boxing

박싱이란, 리터럴을 오브젝트화 시켜, 클래스에서 사용할 수 있는 메소드 들을 사용할 수 있게 만들어주었다.

boxing의 예시

int intVal = 20;
  Integer boxingNumber1 = new Integer(intVal);
Integer boxingNumber1 = Integer.valueOf(intVal); //static메소드로 int -> Integer
int junBoxingNumber = boxingNumber1.intValue(); // Integer -> int
//오토박싱
Integer boxingNumber2 = intVal; //알아서 바뀜.
//오토 언박싱
int unBoxingNumber2 = boxingNumber2;

값을 비교해보자

int inum = 20;
Integer integerNum1 = Integer.valueOf(20);
Integer integerNum2 = Integer.valueOf(20);
Integer integerNum3 = 20;
Integer integerNum4 = 20;
/*값 비교*/
System.out.println("int와 Integer비교 : " + (inum == integerNum1));
System.out.println("(inum == integerNum3) = " + (inum == integerNum3));
/*주소값 비교*/
System.out.println("(integerNum1 == integerNum2) = " + (integerNum1 == integerNum2));
System.out.println("(integerNum1 == integerNum3) = " + (integerNum1 == integerNum3));

4.2 Parcing & wrapper

4.2.1 parcing

문자열(String) 값을 기본자료형 값으로 변경하는 것을 parsing이라고 한다.

byte b = Byte.parseByte("1");
short s = Short.parseShort("2");
int i = Integer.parseInt("4"); // 중요
long l = Long.parseLong("8"); //8L 8l 안됨
float f = Float.parseFloat("3.14f");
double d = Double.parseDouble("3.14");
boolean bool = Boolean.parseBoolean("true");

4.2.2 Parcing 반대로 문자열로 변환.

  • valueOf()

    기본 자료형 값을 wrapper 클래스 타입으로 변환시키는 메소드

  • toString()

    필드값을 문자열로 반환하는 메소드

     /*parsing*/
    byte b = Byte.parseByte("1");
    short s = Short.parseShort("2");
    int i = Integer.parseInt("4"); // 중요
    long l = Long.parseLong("8"); //8L 8l 안됨
    float f = Float.parseFloat("3.14f");
    double d = Double.parseDouble("3.14");
    boolean bool = Boolean.parseBoolean("true");
    String c = Character.valueOf('a').toString(); // 'a' => "a"
  • String의 valueOf

    오버라이딩 되어 있으므로 원하는 타입을 작성하면 문자열로 변경해준다.

5. 예외처리

5.1 예외처리

강제로 예외를 발생
throw new 예외클래스명();

예외처리방법

  1. throws로 위임
  2. try-catch로 처리

1. Throws

Throws는 예외가 발생했을 때, 메소드를 통하여 예외를 넘기는 것이다. 최종 목적지는 main이며, main에 예외가 넘어왔을 땐 프로그램이 예외를 발생시키고 중단시킨다.

public static void main(String[] args) throws Exception {

2. Try-catch

예외 발생 가능성이 있는 코드를 작성하는 곳이다.

public static void main(String[] args) {
    ExceptionTest exceptionTest = new ExceptionTest();
    try {
        /*try : 예외 발생 가능성이 있는 코드를 작성 */
        exceptionTest.checkEnoughMoney(10000,50000);
        exceptionTest.checkEnoughMoney(10000,5000);
        System.out.println("=========== 상품 구입 완료 ============");
    } catch (Exception e) {
        /*catch : try 블럭 안에서 예외가 발생할 경우 catch블럭의 코드가 실행된다.*/
        System.out.println("=========== 상품 구입 불가 ============");
        e.printStackTrace(); // 콘솔창에 에러 내용 확인할 수 있음
    }
    System.out.println("프로그램을 종료합니다.");
}

Exception e : catch문내에 발생한 모든 예외가 e 로 들어가게 된다. 이 e를 이용해서 코드를 작성하거나 예외를 저장할 수 있다.

e.printStackTrace : 예외가 발생한 에러 메시지를 콘솔창에다가 띄어준다.

예외가 발생한 상황이다.

에러가 발생한 시점에서는 예외처리로 넘어가기 때문에 try 문에서 중단된다.

예외가 발생하더라도 try-catch문 이후로는 순차적으로 흘러간다.

5.2 사용자 정의 예외처리

UserException

사용자 정의 예외처리

사용자 정의 예외 클래스를 만들기 위해서는 Exception 클래스를
상속받으면 된다.

경우에 따라서는 더 상위 타입인 Throwable 클래스나 하위 타입의 클래스를 상속받기도 한다.

  • 사용자 정의 시 Exception 클래스를 상속받는다.

상속받으면 위처럼 exception의 것들을 상속받을 수 있다.

  • 예외 메시지를 넘겨받아야 한다. 생성자를 만들어서 super() 생성자를 통해 타고 올라가면 사용자 정의 → Exception → Throwable 까지 올라가게 된다. Throwable에서는 생긴 예외를 stack으로 쌓아서 사용자 콘솔에 출력해준다.

5.3 예외처리 알아야 할 것들

5.3.1 try-with-resource

JDK 1.7에 추가된 문법이다.

close 해야 하는 인스턴스의 경우 try앞에[ 괄호 안에서 생성하면 해당 try-catch 블럭이 완료될 때 자동으로 close 처리해준다.

try (BufferedReader in = new BufferedReader(new FileReader("test.dat"));){
	//  위 try() 안에 있는 내용은 사용되고 알아서 닫힌다.
	String s;
	
	while((s = in.readLine()) != null){
		System.out.println(s);
	}

} catch (FileNotFoundException/* | EOFException*/ e) {
	e.printStackTrace();

} catch (IOException e) {
	e.printStackTrace();
}

5.3.2 Try-catch Exception 클래스(상위VS하위)

예외 상황별로 catch블럭을 따로 작성해서 처리할 수도 있고, 상위 타입의 Exception을 이용해서 통합적으로 처리할 수도 있다.

단, 상위 타입의 Exception 블럭이 먼저 기술되면 아래로 코드가 도달할 수 없으므로 컴파일 에러가 발생한다.

서술 순서는 하위 타입 ⇒ 상위 타입으로 기재한다

5.3.3 Catch 블록 정리하는 방법

catch (PriceNegativeException | MoneyNegativeException | NotEnoughMoneyException e)

예외처리 Negative Money 예시

아래 코드들은 Negative Money 예외처리 코드들을 모두 정리해 놓은 글로, 사용자 정의 예외처리를 어떻게 정의하는지 나타낸다.

Application1

package com.lhw.section02.userexception;

import com.lhw.section02.userexception.exception.MoneyNegativeException;
import com.lhw.section02.userexception.exception.NegativeException;
import com.lhw.section02.userexception.exception.NotEnoughMoneyException;
import com.lhw.section02.userexception.exception.PriceNegativeException;

public class Application {

    public static void main(String[] args) {

        ExceptionTest exceptionTest = new ExceptionTest();

        try {
            /*상품 가격이 음수일 경우*/
            exceptionTest.checkEnoughMoney(-1000, 1000);
            /*잔액이 음수일 경우*/
            exceptionTest.checkEnoughMoney(1000, -1000);
            /*(잔액 < 가격) 인 경우*/
            exceptionTest.checkEnoughMoney(1000, 500);
            /*예외 상황별로 catch블럭을 따로 작성해서 처리할 수도 있고, 상위 타입의 Exception*/
/*        } catch (Exception ex) { //아래의 내용을 상위 클래스로 받아줄 수 있다.
            ex.printStackTrace();
        }*/
        // 아래 내용은 좀 더 상세하게 나누어서 작업해주는 부분이다.
        /* 예외 클래스를 catch하는 순서 : 더 하위 클래스를 먼저 작성
         * 상위가 먼저 올라오면, 어떤 하위 클래스에서 예외가 발생했는지
         * 확인할 수 없다.*/
        } catch (PriceNegativeException e) {
            e.printStackTrace();
        } catch (MoneyNegativeException e) {
            System.out.println(e.getMessage());
        } catch (NotEnoughMoneyException e) {
            System.out.println(e.getMessage());;
        } catch (NegativeException e) {
            System.out.println(e.getMessage());
        }

    }
}

ExceptionTest

package com.lhw.section02.userexception;

import com.lhw.section02.userexception.exception.MoneyNegativeException;
import com.lhw.section02.userexception.exception.NegativeException;
import com.lhw.section02.userexception.exception.NotEnoughMoneyException;
import com.lhw.section02.userexception.exception.PriceNegativeException;

/*
 * ExceptionTest 클래스는 예외를 직접적으로 발견하여 throw하고, 메소드를 throws
 * 를 하는 공간이다. 이 곳에서부터 예외가 시작된다.
 */
public class ExceptionTest {
/* Throw 와 Throws
 * throw는 예외 클래스 객체(인스턴스)를 생성해 주는 것이고
 * throws는 해당 메소드가 감지할 예외 클래스를 읽어오는 것이다.
 */
 public void checkEnoughMoney(int price, int money) throws
            PriceNegativeException, MoneyNegativeException, NotEnoughMoneyException {

        if (price < 0) { 
            throw new PriceNegativeException("상품 가격이 음수일 수 없습니다..");
        }

        if (money < 0) {
            throw new MoneyNegativeException("잔액은 음수일 수 없습니다.");
        }

        if (money < price) {
            throw new NotEnoughMoneyException("가진 돈 보다 상품 가격이 더 높습니다.");
        }

    }
}

NotEnoughMoneyException

package com.lhw.section02.userexception.exception;
/* 예외 클래스이다.
 * 사용자 지정 예외를 지명해주고, 어떤 역할을 할지만 기술한다.
 * 사용자 지정 예외 클래스라면 `Exception`을 상속받아야 한다.
 * 하지만 때에 따라 더 상위인 Throwable이나 하위 클래스를 상속 받아도 된다.*/
public class NotEnoughMoneyException extends Exception {
		/* 아래 생성자는 매개변수 생성자로, NotEnoughMoney예외를 생성할 때
		 * 반드시 해당 매개변수를 받아오게 강제화 하는 것이다.*/
    public NotEnoughMoneyException(String s) {
        super(s);
    }
}

NegativeException

package com.lhw.section02.userexception.exception;

/*
* 사용자 정의 예외 클래스를 만들기 위해서는 Exception 클래스를
* 상속받으면 된다.경우에 따라서는 더 상위 타입인 Throwable
* 클래스나 하위 타입의 클래스를 상속받기도 한다.
* */
public class NegativeException extends Exception {

    public NegativeException(String message) {
        super(message);
    }
}

PriceNegativeException

package com.lhw.section02.userexception.exception;
/* PriceNegativeException 또한 예외 클래스이다.
 * 이 클래스는 사용자 지정 예외인 NegativeException을 상속받는다.
 * 음수가 들어왔을 때의 예외 처리하는 경우 중, 가격이 음수인 경우만
 * 발생하는 예외를 처리해주는 클래스이기에, Exception이 아니라
 * 훨씬 하위 클래스, 그리고 사용자 지정 예외 클래스를 가져왔다.*/
public class PriceNegativeException extends NegativeException {

    /*
    상속구조를 통해서 나중에 필요로 할 수 있는 다형성을
    염두에 두고 세부로 나눈 것이다. 
    원래 상속을 하면 부모의 것을 따라가야한다. 기존에는
    부모에 기본 생성자가 있기 때문에 아무것도 안해도 된다.
    하지만 얘는 지금 부모가 매개변수 생성자를 가지고 있으니
    얘도 매개변수 생성자를 만들어 줘야한다.
    */
    public PriceNegativeException(String message) {
        super(message);
    }
}

MoneyNegativeException

package com.lhw.section02.userexception.exception;
/* MoneyNegative도 PriceNegative와 마찬가지로 NegativeException을 
 * 상속받으며, 매개변수 생성자가 있다.*/
public class MoneyNegativeException extends NegativeException {
    
    public MoneyNegativeException(String message) {
        super(message);
    }
}

6. 입출력

내용이 많아 아래 링크에 따로 정리해 두었다.
입출력 링크

7. 제네릭

내용이 많아 아래 링크에 따로 정리해 두었다.
제네릭 링크

8. 컬렉션-List

내용이 많이 아래 링크에 정리한다.
컬렉션 링크

💡 PROBLEM


  • 제공된 프리코스에 비해 강의자료의 디테일이나 양이 다소 부족했으며, 강사님의 진도가 좀 빠르다고 느꼈다.
  • 점점 코드가 길어지고, 강의 자료도 이것저것 많이 다루다 보니 강의 노트를 작성하는데 한계를 느꼈다.
  • 겨울이 되어 강의실이 다소 답답했다.
  • BufferedRead와 파일 읽어올 때의 예외처리를 잘 이해하지 못했다. 버퍼리더를 안전하게 닫아주는 것의 의미를 공부해 보아야 겠다.
  • 사용자 지정 예외 말고 java.lang에 있는 예외들을 어떻게 처리하는지 어떤 상황에 사용하는지 자세히 배울 수 있을줄 알았는데 배우지 못했다. 프리코스로 예습할 때 다양한 예외 클래스들을 사용해서 기대했지만 아무래도 좀 기본적인 부분이라 다루지 않은 듯 하니, 직접 찾아 보아야겠다.
  • I/O 스트림 종류가 좀 많아서 헷갈리니 정리를 해보아야 겠다.

🪛 TRY


  • 강의 자료 복잡하니 노트 최소화하고 줌 사용 시도 해 보자.
  • 허리가 아프고 아침에 일어나기 힘드니, 컨디션 조절해야겠다.
profile
백엔드

0개의 댓글