본문 바로가기
Study/Study Alone

[STUDY] 2주차

by 햄리뮤 2024. 12. 16.
반응형

 

얏호! 공부조아!

 

 

[Q]

  1. 조건문은 기계어로 번역했을때 어떻게 생겼을까요?
  2. 메소드 오버로딩을 자바에서 내부 구현을 어떻게 할까요?
  3. 다형성이 코드를 잘 짜는데 어떻게 도움이 될까요?
  4. 추상 클래스는 어떤 상황에서 사용하게 될까요?

[A]

조건문은 기계어로 번역했을때 어떻게 생겼을까요?

자바의 조건문(if, switch)은 기계어로 변환할 때 주로 비교 연산과 분기 명령으로 나타난다.

예를 들어, 자바코드

if (a > b) {
    doSomething();
} else {
    doSomethingElse();
}

컴파일 후 저수준 언어로 변환된 모습 (어셈블리 수준)

CMP a, b       ; a와 b를 비교
JLE ELSE_LABEL ; a <= b이면 ELSE_LABEL로 점프
CALL doSomething
JMP END_LABEL  ; doSomething 실행 후 END_LABEL로 점프
ELSE_LABEL:
CALL doSomethingElse
END_LABEL:

 

여기서 CMP는 비교 명령어, JLE는 조건에 따라 실행 흐름을 변경하는 분기 명령어이다.

 

깊게 생각해보기

1. 조건문이 기계어로 번역되었을 때의 모습에 대해서 알면 개발할때 어떤 부분에 대해서 장점이 있을까?

개발 시 성능 최적화와 디버깅에서 중요한 장점을 얻을 수 있다.

  • 성능 최적화
    • 코드 작성 시 불필요한 연산이나 분기를 줄이는 방식으로 성능을 개선할 수 있다.
    • 기계어에서는 각 조건문이 분기 명령어(JMP, JE, JNE)로 변환되기 때문에, 중복 계산을 최소화하면 실행 속도가 빨라질 수 있다.
    • // 나쁜 예: 중복 조건 계산
      if (a > 0 && b > 0) {
          if (a > 10) {
              doSomething();
          }
      }
      
      // 좋은 예: 조건 계산 최소화
      if (a > 0) {
          if (b > 0 && a > 10) {
              doSomething();
          }
      }
    • switch문은 컴파일러가 점프 테이블을 생성해, 기계여 수준에서 고정된 메모리 주소로 빠르게 이동할 수 있다. 따라서 숫자 또는 상수 비교가 많은 경우 if-else 보다 switch를 사용하는 것이 효율적이다.
  • 불필요한 연산 피하기
    • 조건문 작성 시 무거운 연산이 들어가면, 컴파일된 코드는 더 많은 기계어 명령어를 포함하게된다. 조건 평가 순서를 잘 설계해, 필요하지 않은 계산을 줄일 수 있다.
// 나쁜 예: 항상 복잡한 계산 수행
if (complexCalculation() && isReady) {
    doSomething();
}

// 좋은 예: 간단한 조건 먼저 확인
if (isReady && complexCalculation()) {
    doSomething();
}
  • 브랜치 예측과 성능
    • 조건문 작성 시 예측이 쉬운 조건을 우선적으로 배치하면, CPU가 예측을 더 잘 수행할 수 있다.
    • 자주 발생하는 조건을 먼저 처리하면 브랜치 예측의 효율성이 높아져 실행 속도가 빨라진다.
// 나쁜 예: 조건 순서가 비효율적
if (rareCondition) {
    handleRareCase();
} else {
    handleCommonCase();
}

// 좋은 예: 일반적인 경우를 먼저 처리
if (commonCondition) {
    handleCommonCase();
} else {
    handleRareCase();
}
  • 디버깅과 이해
    • 디버깅시 예상치 못한 오류나 비정상 동작의 원인을 쉽게 파악할 수 있다.
    • 무한 루프: 잘못된 조건식이 반복적으로 참으로 평가되어, 분기 명령어가 올바르게 작동하지 않을 경우를 이해 가능하다.
    • 코드 분석: 디컴파일된 바이트코드나 어셈블리 수준에서 조건문의 동작을 추적할 수 있다.
  • 저수준 시스템 개발에 유리
    • 임베디드 시스템이나 성능이 중요한 시스템에 죄적화된 조건문을 설계할 수 있다.
    • JVM 내부 동작이나 컴파일러 최적화가 코드를 어떻게 변환하는지 분석해 최적의 코드 작성이 가능하다.

메소드 오버로딩을 자바에서 내부 구현을 어떻게 할까요?

  • 자바에서 메서드 오버로딩은 컴파일 시점에 메서드 호출을 구분한다.
    내부적으로는 메서드 시그니처(이름 + 매개변수 타입과 개수)를 기준으로 오버로딩된 메서드를 구분한다.

예를 들어

public void print(String s) { ... }
public void print(int n) { ... }

 

컴파일러는 각 메서드를 다음과 같이 구분한다.

  • print(Ljava/lang/String;)V -> 문자열을 인자로 받는 메서드
    • java/lang/String: 패키지와 클래스 이름이다. (JVM 표현: Ljava/lang/String;)
    • L: 참조 타입(객체 타입)을 나타낸다. 자바 클래스 이름 앞에 붙음
    • JVM에서는 패키지 구분자는 슬래시(/)를 사용한다. (자바 소스 코드에서는 점(.)사용)
    • V: 반환 타입이 void임을 의미한다.
  • print(I)V -> 정수를 인자로 받는 메서드

바이트코드에서 다른 이름으로 구분된 메서드를 호출하기 때문에 런타임에는 혼란이 없다.

 

다시 정리하면

  • 컴파일러가 오버로딩된 메서드를 구분하는 방식: 메서드 이름 + 매게변수 타입/개수를 기준으로 한다.
  • 컴파일 결과: 바이트코드에 서로 다른 시그니처로 변한되어 저장된다.
    • add(II)I: 두 int를 받아 int를 반환
    • add(DD)D: 두 double을 받아 double을 반환
  • JVM 호출 방식: 런타임에는 정확한 시그니처를 기반으로 메서드를 호출하므로 혼동이 없다.

즉, 메서드 이름은 같더라도 내부적으로는 매개변수의 차이를 통해 서로 다른 메서드처럼 동작하게 된다.

깊게 생각해보기

1. 메서드 오버로딩에서 여러 후보 메서드가 있을 경우, 어떤 규칙으로 메서드를 선택할까?

  • 정확히 일치하는 시그니처를 가진 메서드.
  • 타입 승격(Primitive Type Promotion): 더 큰 데이터 타입으로 변환 가능하면 선택.
  • 박싱/언박싱(Auto-boxing/Unboxing): 기본형과 래퍼 클래스 간 변환 가능 여부.
  • 가변 인자(Varargs): 위 조건에 맞는 메서드가 없을 경우 선택.
public void test(int a) { }
public void test(long a) { }
public void test(Integer a) { }
public void test(int... a) { }

test(10);  // int → int (정확히 일치) → 첫 번째 메서드 호출
test(10L); // long → 두 번째 메서드 호출
test(new Integer(10)); // Integer → 세 번째 메서드 호출
test(); // 가변 인자 호출

2. 메서드 오버로딩은 자바 8의 함수형 프로그래밍(람다, 메서드 참조)과 어떻게 연결될까?

  • 함수형 인터페이스를 사용할 때 오버로딩된 메서드 참조는 모호성을 초개할 수 있다.
  • 이 문제는 명시적인 캐스팅으로 해결해야 한다.
public void doSomething(String s) { }
public void doSomething(Integer i) { }

// 람다에서 모호한 호출:
MyInterface myInterface = MyClass::doSomething;

다형성이 코드를 잘 짜는 데 도움이 되는 이유

다형성은 유연하고 확장 가능한 코드를 작성할 수 있게 한다.

  • 추상화된 접근: 부모 클래스의 참조를 통해 여러 자식 클래스의 객체를 다룰 수 있다.
    • 부모 클래스 타입의 변수를 사용해서 다양한 자식 객체를 한꺼번에 관리하거나 사용할 수 있다.
// 부모 클래스
abstract class Animal {
    abstract void sound(); // 동물마다 다른 소리를 정의
}

// 자식 클래스
class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("멍멍");
    }
}

class Cat extends Animal {
    @Override
    void sound() {
        System.out.println("야옹");
    }
}

public class Main {
    public static void main(String[] args) {
        // 부모 클래스 타입으로 자식 객체들을 다룸
        Animal dog = new Dog();
        Animal cat = new Cat();
        
        dog.sound(); // 멍멍
        cat.sound(); // 야옹
    }
}
  • 확장성: 새로운 클래스가 추가되더라도 기존 코드를 수정하지 않고 새로운 동작을 추가할 수 있다.
    • 기존 코드를 건드리지 않고 새로운 클래스만 추가하면 된다.
  • 중복 제거: 공통된 로직은 상위 클래스에서 구현하고, 개별 동작만 하위 클래스에서 정의하면 된다.
    • 반복되는 코드는 부모클래스에 한 번만 작성하고, 각 자식 클래스는 자기 고유 동작만 추가하면된다.
public abstract class Animal {
    public abstract void sound();
}
public class Dog extends Animal {
    public void sound() { System.out.println("Bark"); }
}
public class Cat extends Animal {
    public void sound() { System.out.println("Meow"); }
}
public void playWithAnimal(Animal animal) {
    animal.sound(); // 다형성으로 다양한 객체에 대응
}

추상 클래스는 어떤 상황에서 사용하게 될까요?

추상 클래스는 구현해야 할 공통적인 기능이 있으면서도, 세부 구현이 다를 경우 사용한다.

  • 상속을 통한 코드 재사용: 중복된 코드를 추상 클래스에서 작성하고, 자식 클래스에서 구체화 한다.
  • 기본 동작 제공: 일부 메서드는 구현을 제공하고, 일부 메서드는 추상 메서드로 정의해 자식 클래스에서 구현하도록 강제한다.

추상 클래스 대신 인터페이스를 사용하는 경우: 다중 상속이나 공통 구현이 필요하지 않을 경우 사용한다.

깊게 생각해보기

추상 클래스와 인터페이스의 차이점는 정확히 뭘까?

특징 추상 클래스 인터페이스
상속(extends cs implements) 하나의 부모 클래스만 상속 가능(extends) 여러 개의 인터페이스를 구현 가능(implements)
상태(필드) 인스턴스 변수와 상수를 포함 가능 상수만 포함 가능(Java 8 이후 default 메서드로 일부 구현 가능)
공유 코드 공통 동작(메서드)를 구현할 수 있어 코드 재사용성이 높음 공통 구현이 제한적(Java 8이후 default메서드와 static 메서드로 일부 가능)
사용 목적 같은 종류의 객체들에 대해 공통된 동작과 상태를 정의 객체가 특정 동작을 제공할 것을 보장
계층 구조 객체의 'is-a' 관계를 표현 객체의 'can-do' 관계를 표현

 

  • is-a 관계
    • 객체가 특정 클래스의 하위 클래스로, 상위 클래스의 한 종류임을 의미 한다. (A는 B다. 라고 표현할 수 있는 관계)
    • 상속을 통해 표현된다.
    • 상위 클래스의 속성과 행동을 그대로 물려 받는다.
    • 상위 클래스에서 정의된 기본 동작을 재사용하거나, 필요에 따라 오버라이딩할 수 있다.
  • can-do 관계
    • 객체가 특정 기능이나 행동을 수행할 수 있음을 나타내는 관계이다. (A는 B를 할 수 있다. 라고 표현할 수 있는 관계)
    • 인터페이스를 통해 표현된다.
    • 특정 행동을 구현하도록 강제한다.
    • 다중 구현이 가능하므로, 객체가 여러 가지 기능을 가질 수 있다.

스터디 후 느낀점 및 반성

대답을 하면서 느낀건데 개발자 처럼 말하는 것을 연습해야할꺼같다.

정확한 개념을 기반으로 구조적인거 등등을 좀더 추가해서 말을 하는 연습을 해야겠다...

일단 알고있는 개념이 별로 없어서 그런게 제일 큰거같다...

반응형

'Study > Study Alone' 카테고리의 다른 글

[나혼자공부] 4주차 복습-2  (0) 2024.12.26
[나혼자공부] 4주차 복습-1  (2) 2024.12.25
[나혼자공부] 3주차 복습-2  (1) 2024.12.20
[나혼자공부] 3주차 복습-1  (0) 2024.12.19
[STUDY] 1주차  (0) 2024.12.06

댓글