🍎 Backend/JAVA

[JAVA] CS면접대비 자바와 객체지향 정리

밈98 2025. 1. 2. 13:14

💡 자바언어의 특징

  • 객체지향 언어
  • GC존재하므로 불필요한 메모리 정리
  • JVM으로 독립적인 운영체제 환경에서 실행이 가능하다
  • 멀티스레딩: 내장된 멀티스레딩 기능으로 동시 작업 수행 지원이 가능하다

💡 자바언어의 단점

  •  수행속도가 느리다
    • C나 C++의 네이티브 언어에 비해 많은 메모리를 소비한다
    • JVM은 프로그램 속도를 감소시키는 기능을 수행함
  • 많은 메모리 공간을 필요로 함
    • JVM은 자바 애플리케이션 실행을 위해 힙 메모리와 추가적인 메모리 공간을 필요로 한다.
    • 객체지향언어이므로 객체를 생성하는 과정에서 불필요한 객체의 생성으로 메모리 낭비가 발생한다.

💡 자바의 실행 과정

 

  • 작성: Java 소스 파일(.java)을 작성.
  • 컴파일: Java 컴파일러(javac)가 소스 코드를 바이트코드(.class)로 변환.
  • 실행: JVM이 바이트코드를 읽어 실행.
    • 이 과정에서 바이트코드는 JIT 컴파일러를 통해 네이티브 코드로 변환될 수 있음.

 

💡Java Bytecode란?

 

  • 바이트코드는 JVM에서 실행되는 중간 코드로, 플랫폼 독립성을 제공.
  • 사람이 읽기 어려운 이진 코드 형태.
  • 소스 코드를 javac로 컴파일하면 생성됨.
  • JVM은 바이트코드를 해석하거나 JIT 컴파일러를 사용해 네이티브 코드로 변환.

💡자바 인터프리터와 JIT 컴파일(just-in-time compilation)

 

  • 인터프리터 방식: JVM이 바이트코드를 한 줄씩 해석하면서 실행. 빠른 시작이 가능하지만 실행 속도가 느림.
  • JIT 컴파일: 실행 중 자주 사용되는 바이트코드를 네이티브 코드로 변환하여 실행 속도를 향상. 초기에는 느릴 수 있지만, 반복 실행 시 성능이 개선.

📚 JVM에서 인터프리터를 사용하는 이유

  1. 플랫폼 독립성
    • 인터프리터는 특정 플랫폼에 종속되지 않는다. 자바의 철학인 WORA(Write Once, Run Anywhere)를 구현하기 위해 바이트코드를 한 줄씩 읽고 실행하여 다양한 플랫폼에서 동작할 수 있도록 함
    • 반면, 네이티브 컴파일러는 특정 플랫폼에 최적화된 기계어로 변환하므로 플랫폼 종속성이 생긴다.
  2. 빠른 초기 실행
    • 프로그램 전체를 실행 전에 컴파일하는 방식은 초기 실행 속도가 느림
    • 인터프리터를 사용하면 바이트코드를 바로 실행하여 초기 실행 속도를 높일 수 있음
  3. 보안적 장점
    • 자바 바이트코드는 프로그램과 하드웨어 사이에서 버퍼 역할을 하며, 인터프리터가 보안 계층 역할을 수행합니다.
    • 이를 통해 신뢰할 수 없는 소프트웨어 실행 시 바이러스 및 악성 프로그램으로부터 보호받을 수 있습니다.

JIT(Just-In-Time) 컴파일러의 필요성과 동작 방식

- 필요성

  • 인터프리터는 바이트코드를 한 줄씩 읽고 실행하기 때문에 컴파일러 기반 언어에 비해 실행 속도가 느립니다.
  • JVM의 성능을 개선하기 위해 JIT 컴파일러가 추가되었습니다.

- 동작 방식

  1. 실시간 컴파일
    • 코드 실행 중 인터프리터처럼 기계어를 생성하면서, 특정 코드가 자주 실행되면 해당 부분을 컴파일함
  2. 컴파일 조건
    • 특정 코드가 자주 실행되면 JIT 컴파일러가 이를 인식하고 컴파일 대상에 포함합니다.
    • 임계치(메서드 호출 횟수, 루프 반복 횟수 등)에 도달한 코드가 컴파일됩니다.
  3. 최적화된 실행
    • JIT는 반복적인 코드(예: 루프)를 컴파일하여 실행 속도를 높인다.
    • "스택 상의 교체(On-Stack Replacement, OSR)"를 통해 루프 실행 중 컴파일된 코드를 대체하여 빠르게 실행합니다.

Java 버전과 특징

    • Java 8 : 람다 표현식, 스트림 API, 새로운 날짜 및 시간 API.
    • Java 11 : LTS, var를 지역 변수에 사용 가능, HTTP 클라이언트 API.
    • Java 17 : 또 다른 LTS, sealed 클래스, 텍스트 블록, 레코드 클래스.

JDK와 JRE

  • JDK (Java Development Kit): 개발 도구 세트. 컴파일러(javac), 디버거 등 포함.
  • JRE (Java Runtime Environment): Java 프로그램 실행 환경. JVM과 표준 라이브러리 포함.
  • 차이점: JDK는 개발과 실행 모두 가능하지만, JRE는 실행만 가능.

동일성과 동등성

  • 동일성 (Identity): 두 객체의 메모리 주소가 동일한 경우.
  • 동등성 (Equality): 두 객체의 내용이나 값이 같은 경우.

equals()와 ==의 차이

  • ==: 메모리 주소 비교. 객체의 동일성을 검사.
  • equals(): 객체의 내용을 비교. 기본 구현은 ==와 동일하지만, 재정의 가능. (e.g., String 클래스는 내용 비교로 재정의됨).

HashCode와 equals()와의 차이

  • hashCode()
    • 객체를 해시코드(정수)로 변환하는 메서드.
    • 해시 기반 자료구조(e.g., HashMap, HashSet)에서 사용.
    • 동일한 객체(equals()가 true)는 반드시 동일한 해시코드를 가져야 함.
    • 구현 시 equals()와 일관성을 유지해야 함.
  • 차이점
    • equals(): 두 객체의 논리적 동등성을 비교.
    • hashCode(): 객체를 식별하기 위한 정수 값 반환.

 

toString() 메서드

  • 객체의 문자열 표현을 반환.
  • 기본적으로 클래스 이름과 해시코드 출력.
  • 재정의하면 객체의 내용을 의미 있는 형태로 출력 가능.

자바에서 메인 메서드가 static인 이유

  1. 클래스의 인스턴스 없이도 호출 가능:
    • 프로그램 시작 시 JVM이 호출.
  2. 객체 생성 전에 실행:
    • 객체 생성은 메인 메서드 이후에 가능.
  3. 실행 진입점 역할:
    • 정적 메서드로 독립적인 호출을 보장.

상수(Constant)와 리터럴(Literal)

  • 상수 (Constant)
    • 변경할 수 없는 값을 저장하는 변수.
    • Java에서는 final 키워드를 사용.
  • 리터럴 (Literal)
    • 코드에 직접 쓰인 고정된 값.

Primitive Type과 Reference Type

  • Primitive Type
    • 데이터 자체를 저장.
    • 8가지: byte, short, int, long, float, double, char, boolean.
  • Reference Type
    • 객체의 주소를 저장.
    • 클래스, 배열, 인터페이스 등 포함.
  • 차이점
    • Primitive: 값 저장.
    • Reference: 객체 참조 주소 저장.

Java는 Call by Value일까, Call by Reference일까?

  • Java는 Call by Value.
    • 메서드 호출 시 인수의 복사본이 전달.
    • Primitive Type: 값 자체가 복사.
    • Reference Type: 객체 주소 값이 복사.

Java 직렬화(Serialization)

  • 객체의 상태를 바이트 스트림으로 변환.
  • 목적: 객체를 파일에 저장하거나 네트워크를 통해 전송.
  • 요구 사항:
    • 클래스가 Serializable 인터페이스 구현.
  • 역직렬화:
    • 바이트 스트림을 다시 객체로 복원.
  • 주의:
    • 직렬화 대상 필드 중 민감한 데이터는 transient 키워드로 제외 가능
 오버로딩과 오버라이딩의 차이
  1. 오버로딩 (Overloading)
    • 동일한 이름의 메서드를 다른 매개변수로 정의.
    • 컴파일 시간에 결정 (Compile-Time Polymorphism).
  2. 오버라이딩 (Overriding)
    • 부모 클래스의 메서드를 재정의.
    • 런타임에 결정 (Run-Time Polymorphism).

다형성 (Polymorphism)

  • 정의: 동일한 메서드가 다른 동작을 하도록 구현.
  • 종류:
    • 컴파일타임 다형성: 메서드 오버로딩.
    • 런타임 다형성: 메서드 오버라이딩.
  • 필요성:
    • 코드의 유연성과 확장성 향상.
    • 다양한 객체를 일관된 방식으로 처리 가능.

상속 (Inheritance)

  • 정의: 부모 클래스의 필드와 메서드를 자식 클래스에서 물려받는 것.
  • 목적:
    • 코드 재사용.
    • 클래스 계층 구조 형성.

상속의 단점

  1. 캡슐화 약화: 자식 클래스가 부모 클래스의 구현 세부사항에 의존.
  2. 유연성 부족: 부모-자식 간 강한 결합이 발생.
  3. 다중 상속 불가: Java는 다중 상속을 지원하지 않음(복잡성 문제).
  4. 추가적인 유지보수 부담: 부모 클래스 변경 시 자식 클래스에 영향을 미침.

상속과 조합의 차이

  • 상속 (Inheritance):
    • "is-a" 관계.
    • 부모 클래스를 확장.
  • 조합 (Composition):
    • 클래스가 다른 객체를 포함한다, "has -a" 관계이다

instanceof 키워드

  • 객체가 특정 클래스나 인터페이스의 인스턴스인지 확인.

instanceof 키워드 사용 시 문제점

  1. 의존성 증가: 특정 클래스에 강하게 의존.
  2. 다형성 위배: 다형성을 통해 해결 가능한 문제를 하드코딩으로 처리.
  3. 코드 복잡성 증가: 다수의 타입 검사가 가독성을 저하시킬 수 있음.

interface란?

  • 정의: 클래스가 구현해야 할 메서드의 집합만 정의.
  • 특징:
    • 메서드는 기본적으로 public이고, 구현부가 없음 (Java 8부터 디폴트 메서드 가능).
    • 다중 구현 가능.

interface와 abstract class의 차이

  1. 용도
    • 인터페이스: 동작(Behavior)의 정의.
    • 추상 클래스: 공통 속성과 메서드의 정의.
  2. 구현 여부
    • 인터페이스: 구현부 없음 (Java 8부터 디폴트 메서드 지원).
    • 추상 클래스: 일반 메서드와 추상 메서드 혼합 가능.
  3. 상속
    • 인터페이스: 다중 구현 가능.
    • 추상 클래스: 단일 상속만 가능.

interface와 abstract class 사용 시점

  1. interface
    • 여러 클래스에 공통된 동작을 정의해야 할 때.
    • 다중 상속이 필요한 경우.
  2. abstract class
    • 공통된 상태와 동작을 공유해야 할 때.
    • 부모-자식 관계가 강하게 결합된 경우.

final 키워드

final키워드는 "변경 금지"라는 의미를 내포하고 있다!

  1. 클래스: 상속 금지.
  2. 메서드: 오버라이딩 금지. (서브클래스에서 변경되지 않을때!)
  3. 변수: 값 변경 금지(상수).