💡 자바언어의 특징
- 객체지향 언어
- 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에서 인터프리터를 사용하는 이유
- 플랫폼 독립성
- 인터프리터는 특정 플랫폼에 종속되지 않는다. 자바의 철학인 WORA(Write Once, Run Anywhere)를 구현하기 위해 바이트코드를 한 줄씩 읽고 실행하여 다양한 플랫폼에서 동작할 수 있도록 함
- 반면, 네이티브 컴파일러는 특정 플랫폼에 최적화된 기계어로 변환하므로 플랫폼 종속성이 생긴다.
- 빠른 초기 실행
- 프로그램 전체를 실행 전에 컴파일하는 방식은 초기 실행 속도가 느림
- 인터프리터를 사용하면 바이트코드를 바로 실행하여 초기 실행 속도를 높일 수 있음
- 보안적 장점
- 자바 바이트코드는 프로그램과 하드웨어 사이에서 버퍼 역할을 하며, 인터프리터가 보안 계층 역할을 수행합니다.
- 이를 통해 신뢰할 수 없는 소프트웨어 실행 시 바이러스 및 악성 프로그램으로부터 보호받을 수 있습니다.
JIT(Just-In-Time) 컴파일러의 필요성과 동작 방식
- 필요성
- 인터프리터는 바이트코드를 한 줄씩 읽고 실행하기 때문에 컴파일러 기반 언어에 비해 실행 속도가 느립니다.
- JVM의 성능을 개선하기 위해 JIT 컴파일러가 추가되었습니다.
- 동작 방식
- 실시간 컴파일
- 코드 실행 중 인터프리터처럼 기계어를 생성하면서, 특정 코드가 자주 실행되면 해당 부분을 컴파일함
- 컴파일 조건
- 특정 코드가 자주 실행되면 JIT 컴파일러가 이를 인식하고 컴파일 대상에 포함합니다.
- 임계치(메서드 호출 횟수, 루프 반복 횟수 등)에 도달한 코드가 컴파일됩니다.
- 최적화된 실행
- 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인 이유
- 클래스의 인스턴스 없이도 호출 가능:
- 프로그램 시작 시 JVM이 호출.
- 객체 생성 전에 실행:
- 객체 생성은 메인 메서드 이후에 가능.
- 실행 진입점 역할:
- 정적 메서드로 독립적인 호출을 보장.
상수(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 키워드로 제외 가능
오버로딩과 오버라이딩의 차이
- 오버로딩 (Overloading)
- 동일한 이름의 메서드를 다른 매개변수로 정의.
- 컴파일 시간에 결정 (Compile-Time Polymorphism).
- 오버라이딩 (Overriding)
- 부모 클래스의 메서드를 재정의.
- 런타임에 결정 (Run-Time Polymorphism).
다형성 (Polymorphism)
- 정의: 동일한 메서드가 다른 동작을 하도록 구현.
- 종류:
- 컴파일타임 다형성: 메서드 오버로딩.
- 런타임 다형성: 메서드 오버라이딩.
- 필요성:
- 코드의 유연성과 확장성 향상.
- 다양한 객체를 일관된 방식으로 처리 가능.
상속 (Inheritance)
- 정의: 부모 클래스의 필드와 메서드를 자식 클래스에서 물려받는 것.
- 목적:
- 코드 재사용.
- 클래스 계층 구조 형성.
상속의 단점
- 캡슐화 약화: 자식 클래스가 부모 클래스의 구현 세부사항에 의존.
- 유연성 부족: 부모-자식 간 강한 결합이 발생.
- 다중 상속 불가: Java는 다중 상속을 지원하지 않음(복잡성 문제).
- 추가적인 유지보수 부담: 부모 클래스 변경 시 자식 클래스에 영향을 미침.
상속과 조합의 차이
- 상속 (Inheritance):
- "is-a" 관계.
- 부모 클래스를 확장.
- 조합 (Composition):
- 클래스가 다른 객체를 포함한다, "has -a" 관계이다
instanceof 키워드
- 객체가 특정 클래스나 인터페이스의 인스턴스인지 확인.
instanceof 키워드 사용 시 문제점
- 의존성 증가: 특정 클래스에 강하게 의존.
- 다형성 위배: 다형성을 통해 해결 가능한 문제를 하드코딩으로 처리.
- 코드 복잡성 증가: 다수의 타입 검사가 가독성을 저하시킬 수 있음.
interface란?
- 정의: 클래스가 구현해야 할 메서드의 집합만 정의.
- 특징:
- 메서드는 기본적으로 public이고, 구현부가 없음 (Java 8부터 디폴트 메서드 가능).
- 다중 구현 가능.
interface와 abstract class의 차이
- 용도
- 인터페이스: 동작(Behavior)의 정의.
- 추상 클래스: 공통 속성과 메서드의 정의.
- 구현 여부
- 인터페이스: 구현부 없음 (Java 8부터 디폴트 메서드 지원).
- 추상 클래스: 일반 메서드와 추상 메서드 혼합 가능.
- 상속
- 인터페이스: 다중 구현 가능.
- 추상 클래스: 단일 상속만 가능.
interface와 abstract class 사용 시점
- interface
- 여러 클래스에 공통된 동작을 정의해야 할 때.
- 다중 상속이 필요한 경우.
- abstract class
- 공통된 상태와 동작을 공유해야 할 때.
- 부모-자식 관계가 강하게 결합된 경우.
final 키워드
final키워드는 "변경 금지"라는 의미를 내포하고 있다!
- 클래스: 상속 금지.
- 메서드: 오버라이딩 금지. (서브클래스에서 변경되지 않을때!)
- 변수: 값 변경 금지(상수).
'🍎 Backend > JAVA' 카테고리의 다른 글
[JAVA] 어노테이션(Annotation) (0) | 2025.01.09 |
---|---|
[JAVA] 람다(Lambda)와 스트림(Stream) (0) | 2025.01.09 |
[java] abstract(추상) vs implements의 비교 (1) | 2024.12.20 |
[JAVA] 해시 맵 (0) | 2023.12.11 |
[JAVA] 메모리 관리 & call by value (0) | 2023.12.08 |