1. 운영체제(OS)에 독립적. JVM에 의해 실행되기 때문!
- (OS 관계없이 동일 코드로 동작. 이식성이 높다고도 표현한다.)
2. 객체 지향 프로그래밍(OOP)언어
- 절차 지향 언어와 다르게 하나의 기능을 객체 형태로 만들어
객체들을 결합하여 하나의 프로그램을 만드는 것.
3. 사용하기 쉬운 언어
- 능률적이고 명확한 코드 작성 가능
- 다른 언어의 단점 보완(포인터, 메모리 관리)
4. 자동 메모리 관리(Garbage Collection)
- 자바는 개발자가 직접 메모리에 접근할 수 없으며, 자바가 직접 관리한다.
5. 동적 로딩 지원
- 객체간의 상호작용을 정의하기 때문에 필요하지 않는 객체는 생성되지 않고,
필요한 객체만 생성하여 사용한다.
- 오류가 발생하면 발생한 오류의 클래스만 수정하면 되므로 전체를 수정할 필요가 없다.
즉, 유지보수를 쉽고 빠르게 진행할 수 있다.
6. 멀티쓰레드 지원
- 하나의 프로그램 단위가 동일한 쓰레드를 동시에 수행할 수 있다.
7. 네트워크와 분산환경 지원
1. 느린 실행 속도
- 자바는 JVM 위에서 동작하기 때문에 오버헤드가 발생하고, 다른 언어들에 비해 속도가 느리다.
JIT(Just In Time) 컴파일러의 성능 문제가 존재한다.
2. 높은 메모리 사용량
- Garbage Collection이 자동으로 메모리를 관리하는 과정에서
CPU와 메모리를 많이 사용해 성능 저하가 발생할 수 있다.
- 객체 지향 언어이기 때문에 객체를 생성하고 관리하는 과정에서
불필요한 객체 생성이 발생하는 경우도 있다.
3. 학습의 어려움
- 문법이 길고 복잡함
자바는 C 계열 언어라서 기본적인 문법이 어렵지는 않지만,
다른 언어(Python, JavaScript 등)에 비해 코드가 장황하다.
- 객체 지향 개념 (OOP)이 필수적
자바는 객체 지향 프로그래밍(OOP)이 강제되는 언어이기 때문에,
클래스, 객체, 상속, 다형성 등의 개념을 반드시 이해해야 한다.
- 많은 라이브러리와 프레임워크
✅ 1. [Write] Java 코드 작성 (.java 파일)
Java 소스 코드작성
✅ 2. [Compile] 컴파일 (javac → .class 생성)
javac 명령어를 사용하여 Java 소스 코드를 바이트코드(.class)로 변환
* 바이트코드는 JVM에서 실행할 수 있는 중간 코드 형태이다.
✅ 3. [Class Loading] 클래스 로딩 (JVM이 .class 파일 로드)
JVM(Java Virtual Machine)이 클래스 로더(Class Loader)를 통해
.class 파일을 메모리에 로드
(이 단계에서 여러 검사(Verification), 준비(Preparation), 해석(Resolution) 과정이 수행)
✅ 4. [Execution] 실행 (JVM이 바이트코드 실행)
JVM의 인터프리터 또는 JIT 컴파일러가 바이트코드를 실행
✅ 5. [Termination] JVM 종료
프로그램 실행이 끝나면 JVM이 메모리를 정리하고 종료
* JVM은 Garbage Collector를 사용하여 사용하지 않는 객체를 자동으로 제거한다.
개발자가 작성한 .java 파일을 JVM이 이해할 수 있도록 하는
Bytecode 로 변환하고 .class 파일을 만드는 것을 의미
(고급언어로 작성된 소스코드를 가상 머신이 이해할 수 있는 중간 코드로 컴파일한 것)
→ .class 파일에 존재하는 데이터가 바로 자바 바이트코드, Java Bytecode 인 것!
이러한 과정을 거치는 이유는 어떠한 플랫폼에도 종속되지 않고
JVM에 의해 실행 될수 있도록 하기 위함이다.
플랫폼: 개발환경 실행환경 등 어떠한 목적을 수행할 수 있는 환경
ex) 프로그램이 실행되는 환경인 운영체제의 종류(Window, Mac, Linux 등)
개발이 수행되는 환경의 종류(visual studio 등)
Class Loader 를 통해 JVM 내부로 넘어와 Runtime Data Area(JVM 메모리)에 배치된 ByteCode들을 기계어로 변경하게 되는데 이때 두가지 방식을 사용하게 된다.
- 인터프리터 방식
바이트코드를 한 줄씩 해석하여 실행 (속도가 느림)
- JIT(Just-In-Time) 컴파일러 방식
바이트코드를 네이티브 코드(기계어)로 변환하여 실행 (속도 향상)
Class Loader: 변환된 ByteCode(.class)파일을
JVM이 운영체제로부터 할당 받은 메모리 영역인
Runtime Data Area로 적재하는 역할을 한다.
사용해 본 버전 : Java 17
Java 17을 사용한 이유
장기 지원 버전(LTS, Long-Term Support)
Java 17은 LTS 버전으로, 장기간(최소 8년) 지원이 보장된다.
기업 환경에서 안정적으로 사용할 수 있으며, 보안 및 성능 패치가 지속적으로 제공된다.
성능 개선 및 최적화
이전 버전(Java 11, 8) 대비 Garbage Collector(GC) 성능 향상
메모리 관리 최적화로 애플리케이션 성능 개선
이전 버전 대비 현대적인 문법 지원
Java 8, 11 대비 더 간결하고 효율적인 코드 작성 가능
보안 및 유지보수 측면에서 유리
최신 보안 패치 적용이 가능하며, 기업 환경에서도 신뢰할 수 있음
Java는 장기 지원 버전(LTS: Long Term Support) 을 중심으로 발전해왔습니다.
Java 8 (2014, LTS) → 가장 널리 사용됨 (Stream, 람다식 도입)
Java 11 (2018, LTS) → 상용 JDK 변경, 모듈화 지원 개선
Java 17 (2021, LTS) → 패턴 매칭, 성능 개선, 새로운 기능 추가
📌 한눈에 보는 Java 주요 버전
| 버전 | 출시년도 | LTS 여부 | 주요 기능 |
|---|---|---|---|
| Java 8 | 2014 | ✅ LTS | 람다식, 스트림 API, 인터페이스 기본 메서드, optional |
| Java 11 | 2018 | ✅ LTS | var 지역 변수, HTTP Client API, GC 개선 |
| Java 17 | 2021 | ✅ LTS | 패턴 매칭, sealed 클래스, 새로운 스위치 문법 |
JDK (Java Development Kit)
JDK는 Java 애플리케이션을 개발하고 실행할 수 있는 환경이다.
개발자가 Java 프로그램을 작성하고, 컴파일하고, 디버깅할 수 있도록 여러 도구가 포함되어 있다.
🔹 구성 요소
JRE(Java Runtime Environment) → 실행 환경
Javac(Java Compiler) → Java 소스 코드를 바이트코드(.class)로 변환
디버거(Debugger) → 코드 오류를 찾고 수정
Java API 라이브러리 → Java에서 제공하는 표준 라이브러리
JVM(Java Virtual Machine) → 바이트코드를 실행하는 가상 머신
📌 JDK가 필요한 경우
Java 애플리케이션을 개발할 때
Java 소스 코드(.java)를 컴파일하여 바이트코드(.class)로 변환할 때
JRE (Java Runtime Environment)
JRE는 Java 애플리케이션을 실행하기 위한 환경이다.
Java 개발자가 아닌 일반 사용자는 JRE만 설치해도 Java 프로그램을 실행할 수 있다.
🔹 구성 요소
JVM(Java Virtual Machine) → 바이트코드를 실행
코어 라이브러리(Core Libraries) → Java 프로그램 실행에 필요한 기본 API
클래스 로더(Class Loader) → 필요한 클래스 파일을 메모리에 로드
📌 JRE가 필요한 경우
Java 프로그램을 실행할 때
Java 개발자가 아닌 일반 사용자(예: 게임, 비즈니스 애플리케이션 실행)
JDK와 JRE의 차이점

동일성(Identity)
두 객체가 완전히 같은 메모리 주소를 가리키는지를 비교하는 것
즉, 동일한 객체인지 여부를 판별한다.
Java에서는 == 연산자를 사용하여 동일성을 비교
두 객체의 참조(reference) 가 같은 경우에만 true를 반환
String str1 = new String("Hello");
String str2 = new String("Hello");
System.out.println(str1 == str2); // false (서로 다른 객체)
→ str1과 str2는 동일한 문자열 값을 가지고 있지만, 메모리 주소가 다르므로 동일하지 않음
동등성(Equality)
두 객체의 내용(값)이 같은지를 비교하는 것
즉, 값이 같은지를 판별하는 개념이다.
Java에서는 equals() 메서드를 사용하여 동등성을 비교
equals() 메서드를 적절히 오버라이드하면 원하는 방식으로 값 비교가 가능
String str1 = new String("Hello");
String str2 = new String("Hello");
System.out.println(str1.equals(str2)); // true (내용이 같음)
→ equals()를 사용하면 두 객체의 값이 같기 때문에 true가 출력
동일성과 동등성의 차이

== 연산자 (주소값 비교)
==는 메모리 주소(참조값)를 비교하는 연산자이다.
Primitive Type(기본 자료형): 값 자체를 비교
Reference Type(참조 자료형): 객체의 주소값(메모리 위치)을 비교
equals() 메서드 (값 비교)
equals()는 객체의 값(내용)을 비교하는 메서드이다.
기본적으로 Object 클래스에 정의된 메서드로, 모든 클래스가 상속받아 사용할 수 있음
하지만, Object의 기본 equals()는 ==와 동일하게 주소값을 비교하므로, 오버라이딩하여 값 비교가 가능함
== vs equals() 차이점

⚠️ ==와 equals()를 사용할 때 주의할 점 ⚠️
1. String을 비교할 때 ==를 사용하면 안 됨
2. null을 비교할 때 equals() 사용하면 NullPointerException 발생 가능
hashCode()란?
객체를 식별하는 정수(해시 코드)를 반환하는 메서드이다.
Java의 Object 클래스에서 기본 제공 (public int hashCode())
hashCode()의 목적: 객체를 빠르게 찾기 위해 사용 (예: HashMap, HashSet 등)
같은 객체(equals() == true)라면 같은 hashCode() 값을 가져야 함!
equals() vs hashCode() 차이점

hashCode()와 equals()의 관계
📌 규칙
- equals()가 true를 반환하는 두 객체는 hashCode() 값이 동일해야 함
- 하지만 hashCode() 값이 같다고 해서 equals()가 반드시 true를 반환하는 것은 아님.
(서로 다른 객체가 같은 해시 값을 가질 수도 있음 → "해시 충돌")
Java에서 equals()를 오버라이딩하면 객체의 값(내용)을 비교할 수 있다.
그런데 hashCode()를 같이 오버라이딩하지 않으면
HashSet, HashMap, HashTable 같은 컬렉션에서 문제가 발생할 수 있다.
📌 한마디로 정리하면?
📌 결론
✅ equals()만 재정의하면 HashSet, HashMap에서 중복이 제대로 처리되지 않음!
✅ 따라서 equals()를 재정의할 때 hashCode()도 반드시 재정의해야 한다!
Java의 모든 객체(Object)가 기본적으로 가지고 있는 메서드로,
객체를 문자열(String)로 변환할 때 사용됩니다.
📌 특징
- 모든 클래스는 기본적으로 toString() 메서드를 상속받음
(Object 클래스에 정의되어 있음)
- 객체의 정보를 문자열로 반환하는 역할
- 기본적으로는 클래스 이름 + 해시코드(16진수)를 반환하지만,
toString()을 오버라이딩(재정의) 하면 원하는 정보를 출력할 수 있음.
1. 객체를 생성하지 않고 실행하기 위해
static이 없으면 main()을 실행하려면 객체가 필요
Java는 객체 지향 언어이므로 일반적으로 클래스의 메서드를 사용하려면
객체를 먼저 생성해야 하지만, 프로그램 실행을 위해 매번 객체를 생성하는 것은 비효율적임
But! static을 사용하면 객체 생성 없이도 바로 실행 가능하다
2. JVM이 자동으로 main()을 호출하기 위해
JVM이 main()을 자동으로 실행해야 함
Java 프로그램을 실행하면 JVM은 프로그램의 진입점인 main() 메서드를 찾아 실행하는데
이때 main()이 static이 아니면 객체를 먼저 생성해야 하지만
JVM이 어떤 객체를 만들어야 하는지 알 수 없음
따라서 static을 사용하여 JVM이 바로 main()을 호출할 수 있도록 한다
3. 메모리 사용 효율을 높이기 위해
static 메서드는 메모리에 한 번만 로드
Java에서 static 메서드는 프로그램 실행 시 메모리에 단 한 번 로드됨
따라서, main()이 여러 개의 객체에 의해 중복 생성되는 것을 방지할 수 있다
4. Java의 설계 철학 (객체 생성을 최소화)
Java는 객체 지향이지만, 필요하지 않으면 객체 생성을 피함
Java는 객체 지향 언어이지만, 꼭 필요하지 않으면 객체 생성을 최소화하는 방식으로 설계됨
프로그램을 실행할 때 굳이 객체를 생성하지 않아도 된다면, static을 사용하는 것이 더 적절함
main()은 프로그램 시작을 위한 진입점일 뿐, 객체의 동작과 관련이 없으므로
static으로 선언된다.
📌 결론
| 이유 | 설명 |
|---|---|
| 1. 객체 없이 실행 가능 | 객체를 만들지 않고 바로 main() 실행 |
| 2. JVM이 직접 호출 가능 | JVM이 프로그램 시작 시 main()을 자동 호출 |
| 3. 메모리 효율성 증가 | static이므로 메모리에 한 번만 로드됨 |
| 4. Java 설계 철학 반영 | 객체 생성을 최소화하여 실행을 단순화 |
- 리터럴(Literal)
변수에 저장되는 고정된 값 자체를 의미한다.
예를 들어, int a = 10;에서 10이 리터럴이다.
리터럴은 데이터 타입(정수, 실수, 문자, 문자열 등)에 따라 분류할 수 있다.
| 리터럴 유형 | 예제 | 설명 |
|---|---|---|
| 정수 리터럴 | 10, -5, 1000 | int, long 타입의 숫자 |
| 실수 리터럴 | 3.14, -0.5, 2.71f | float, double 타입의 소수 |
| 문자 리터럴 | A, 9, @ | char 타입의 단일 문자 |
| 문자열 리터럴 | "Hello", "Java" | String 타입의 문자열 |
| 논리 리터럴 | true, false | boolean 타입의 참/거짓 값 |
➡ 모든 리터럴은 특정한 데이터 타입을 가짐!
- 상수(Constant)
상수(Constant)는 한 번 값을 지정하면 변경할 수 없는 변수이다.
Java에서 상수를 만들려면 final 키워드를 사용한다.
- 상수의 특징
final 키워드를 사용해야 함
초기화 이후 값을 변경할 수 없음
일반적으로 대문자 + 언더스코어(_) 를 사용하여 이름을 짓는 것이 관례(PI, MAX_VALUE)
반드시 초기화해야 함 (초기화하지 않으면 컴파일 오류 발생)
| 비교 항목 | 상수(Constant) | 리터럴(Literal) |
|---|---|---|
| 정의 | 변하지 않는 변수 | 값 자체 |
| 선언 방식 | final 키워드 사용 | 코드에 직접 입력 |
| 값 변경 가능 여부 | 변경 불가 | 변경 가능(변수에 저장하면) |
| 예제 | final int MAX = 100; | 100(변수 없이 직접 사용) |
Primitive Type (기본 자료형)
메모리에 직접 값을 저장하는 데이터 타입
Java에서 성능 최적화를 위해 미리 정의됨
객체가 아니므로 Heap 메모리를 사용하지 않고, Stack 메모리에 저장됨
Primitive Type의 종류 (8가지)
Reference Type (참조 자료형)
Heap 메모리에 저장된 객체를 가리키는 "주소값"을 저장하는 타입
객체, 배열, 클래스, 인터페이스 등이 Reference Type에 해당됨
기본값은 null (값이 없음을 의미)
Primitive Type과 Reference Type 차이점

Java에서 메서드 호출 방식은 항상 "Call by Value(값에 의한 호출)" 이다.
📌 한마디로 정리하면?
즉, Java는 Call by Value 방식만 지원하며, Call by Reference 방식은 존재하지 않음!!
Java 직렬화(Serialization)란?
객체를 바이트 스트림(Byte Stream)으로 변환하여 저장하거나 전송할 수 있도록 만드는 과정
파일 저장, 네트워크 전송, 데이터베이스 저장 등에 사용됨
ObjectOutputStream, ObjectInputStream 을 사용하여 직렬화/역직렬화 가능
📌 직렬화 과정 (Serialization Process)
객체 → 바이트 스트림 변환 → 파일 저장 / 네트워크 전송
📌 역직렬화 과정 (Deserialization Process)
바이트 스트림 → 원래 객체로 변환
직렬화의 필요성
객체를 파일이나 데이터베이스에 저장하고 나중에 다시 사용할 수 있음
네트워크를 통해 객체를 주고받을 때 필요 (예: RMI, 분산 시스템)
JVM 외부에서도 데이터를 유지 (프로그램 종료 후에도 객체를 복원 가능)

하나의 객체가 여러 가지 형태를 가질 수 있는 성질
Java에서는 상속과 인터페이스를 활용하여 다형성을 구현한다.
왜 필요할까?
1. 부모 클래스나 인터페이스로 다양한 자식 객체를 처리할 수 있어 코드의 재사용성 증가
2. 새로운 기능, 클래스를 추가해도 기존 코드를 수정하지 않아 확장성 증가
3. 코드 변경 시 하나의 부모 타입만 수정하면 되므로 유지보수성 향상
4. 실행 시 객체의 타입에 따라 동작이 달라질 수 있어 유연한 프로그램 설계 가능
부모 클래스의 필드와 메서드를 자식 클래스에서 물려받아 사용하는 것
→ 상속을 통해 기존 클래스를 확장하여 새로운 클래스를 만들 수 있다.
1. 부모클래스를 변경하면 자식 클래스에도 영향을 미쳐 의존성이 증가
2. 상속 구조가 깊어질수록 코드의 복잡성이 증가해 문제를 추적하기 어려워질 수 있음
3. 부모 클래스의 필드와 메서드가 자식 클래스에서 노출되어 캡슐화 원칙이 깨질 수 있음
4. "is-a"관계로 설계되어, 잘못 설계된 상속 구조는 확장 및 수정이 어려움
| 특징 | 상속 | 조합 |
|---|---|---|
| 관계 | "is-a" 관계 | "has-a" 관계 |
| 유연성 | 부모 클래스에 강하게 의존하여 변경이 어려움 | 독립적인 구성 요소로 유연하게 확장 가능 |
| 코드 재사용 | 부모 클래스의 기능을 상속 받아 재사용 | 포함된 객체의 기능을 호출하여 재사용 |
| 캡슐화 | 부모 클래스의 내부 구현이 자식 클래스에 노출 | 캡슐화가 유지되며 수정 시 영향이 적음 |
| 구조 복잡성 | 상속 구조가 깊어질수록 복잡성 증가 | 독립적인 관계로 상대적으로 간단 |
현재 참조형 변수가 어떤 클래스 타입의 객체 주소를 참조하고 있는지
확인하고자 할 때 사용한다.
이 연산자의 결과는 boolean타입으로, 해당 객체가 지정된 타입의 인스턴스이면
true를 반환하고, 그렇지 않으면 false를 반환한다.
String message = "Hello, World!";
boolean result = message instanceof String; // true
"Hello, World!"는 String 클래스의 인스턴스이므로, instanceof 연산자는 true를 반환한다.
• 캡슐화 위배
캡슐화는 클래스 안에 속성과 기능을 하나의 캡슐로 만들어
데이터를 외부로부터 보호하는 것을 말한다.
하지만 instanceof를 사용하면 각 객체가 무엇인지, 어떤 행동을 반환해야 하는지
외부의 객체에 노출되게 된다. 캡슐화를 위반하면 객체의 상태에 직접 접근이 가능해지므로
외부에서 무분별한 수정이 가능해지고 즉, 이는 보안 상의 문제로 이어질 수 있다.
• 단일책임원칙(SRP) 위배
SRP는 클래스나 메서드가 단일의 책임만을 가져야 한다는 원칙이다.
하지만 instanceof를 사용해 한 곳에서 여러 타입에 대한 처리를 구현하면,
해당 클래스나 메서드가 여러 역할을 통시에 담당하게 되어 SPR을 위반하게 된다.
if (animal instanceof Dog) {
//Dog의 로직
} else if (animal instanceof Cat) {
//Cat의 로직
} else if (animal instanceof Bird) { // 새로운 타입 추가
//Bird의 로직
}
출처: https://javacatcher.tistory.com/160 [현주먹의 개발로그:티스토리]
- instanceof를 사용해 캡슐화 위반, 계속 분기 코드 추가
- 다양한 타입의 동물을 처리하고 있으므로 하나의 책임이 아닌
Animal의 각 하위객체에 대한 역할을 다 수행
• 개방-폐쇄원칙(OCP) 위배
OCP는 확장에는 열려있고 변경에는 닫혀 있어야 한다는 원칙이다.
하지만instanceof를 사용하여 타입별로 동작을 정의하면 새로운 타입이 추가될 때마다
기존 코드를 수정해야 한다.
if (animal instanceof Dog) {
//Dog의 로직
} else if (animal instanceof Cat) {
//Cat의 로직
} else if (animal instanceof Bird) { // 새로운 타입 추가
//Bird의 로직
}
출처: https://javacatcher.tistory.com/160 [현주먹의 개발로그:티스토리]
- Animal의 새로운 하위 객체가 추가될 때마다 기존 코드에 분기문을 추가해야 한다.
때문에 instanceof 대신 다형성을 사용하면 좋다!!!!!!
• 인터페이스(interface)
interface 키워드를 사용하여 정의
오직 추상 메서드와 상수(static final)만을 작성할 수 있는 추상 클래스의 변형체 (기초 설계도)
메서드의 통일성을 부여하기 위해 추상 메서드만 따로 모아놓은 것으로,
상속 시 인터페이스 내에 정의된 모든 추상 메서드를 구현해야 함
→ 모든 메서드가 추상적, 구현 강제
• 인터페이스의 특징
- 내부의 모든 메서드는 public abstract 로 정의해야 하며, 이를 생략할 수 있다.
(default 메소드 제외)
- 내부의 모든 필드(변수)는 public static final 이어야 하며, 이를 생략할 수 있다.
- 클래스에 다중 구현이 가능하다.
다중 구현이 된다는 점을 이용해, 내부 멤버가 없는 빈 껍데기 인터페이스를 선언하여
마커 인터페이스로서 이용 가능!
- 인터페이스 끼리는 다중 상속이 가능하다. (상속 키워드로 implements 사용)
- 객체 생성은 불가하지만 부모 타입 참조형 변수로 사용 가능하다.
- 클래스 간의 다중 상속은 불가능하지만 인터페이스 간의 다중 상속,
인터페이스 - 클래스 간의 다중 상속은 가능하다.
extends : 클래스 간의 상속, 인터페이스 간의 상속 관계
implements : 인터페이스 - 클래스 상속 관계
- 보통 xxxable 이런 형식으로 인터페이스 네이밍 규칙을 따른다.
⭐ 인터페이스는 Object 클래스를 상속 받지 못하기 때문에 클래스가 아님 !
같은 추상화인 인터페이스와 다른 점
: 존재 목적이 다르다!
• 추상 클래스는 그 추상 클래스를 상속 받아서 기능을 이용하고 확장시키는 게 목적
즉, 클래스간의 연관 관계를 구축하는 것에 초점을 둔다.
• 인터페이스는 클래스와 별도로
구현 객체가 같은 동작을 한다는 것을 보장하기 위해 사용하는 것에 초점을 둔다.
• 추상클래스(abstract class)
abstract키워드를 사용하여 정의
하나 이상의 추상 메서드를 가지고 있거나 abstract로 정의가 된 클래스
추상메서드를 선언하여 상속을 통해 하위 클래스에서 반드시 구현하도록 강제하는 클래스
• 추상클래스의 특징
- 인스턴스, 즉 객체를 만들 수 없는 클래스로 new 키워드로 객체 생성이 불가능하다.
- 일반 클래스 상속과 동일하게 상속 키워드로 extends를 사용한다.
- 하위 클래스에서 추상 메서드의 구현을 강제해야 한다.(Override)
- 추상 메서드를 포함하는 클래스는 반드시 추상 클래스여야 한다.
- 다중 상속이 불가능하여 단일 상속만 허용한다.

• 추상클래스를 사용하는 경우
- 상속 받을 클래스들이 공통으로 가지는 메서드와 필드가 많아 중복 멤버 통합을 할 때
- 멤버에 public 이외의 접근자(protected, private) 선언이 필요한 경우
- non-static, non-final 필드 선언이 필요한 경우
(각 인스턴스에서 상태 변경을 위한 메서드가 필요한 경우)
- 요구사항과 함께 구현 세부 정보의 일부 기능만 지정했을 때
- 하위 클래스가 오버라이드하여 재정의하는 기능들을 공유하기 위한 상속 개념을 사용할 때
→ 추상 클래스는 이를 상속할 각 객체들의 공통점을 찾아 추상화시켜 놓은 것으로,
상속 관계를 타고 올라갔을 때 같은 부모 클래스를 상속하며 부모 클래스가 가진 기능들을
구현해야 할 경우 사용한다.
• 인터페이스를 사용하는 경우
- 일관된 인터페이스(작업환경)를 제공하기 위해
- 클래스 간의 접점을 만들어 강제적으로 부모, 자식 관계를 형성하기 위해
- 어플리케이션의 기능을 정의해야 하지만 그 구현 방식이나 대상에 대해 추상화 할 때
- 서로 관련성이 없는 클래스들을 묶어 주고 싶을 때 (형제 관계)
- 다중 상속(구현)을 통한 추상화 설계를 해야할 때
- 특정 데이터 타입의 행동을 명시하고 싶은데,
어디서 그 행동이 구현되는지는 신경쓰지 않는 경우
- 클래스와 별도로 구현 객체가 같은 동작을 한다는 것을 보장하기 위해
final : 변경할 수 없도록 제한하는 키워드
변수(variable), 메서드(method), 또는 클래스(class)에 사용될 수 있으며,
어떤 곳에 사용되냐에 따라 다른 의미를 가진다.
final 변수(variable) - 값 변경 불가능
- 변수에 final을 붙이면 이 변수는 수정할 수 없다는 의미를 가진다.
- 선언과 동시에 초기화 하거나, 생성자에서 한 번만 초기화가 가능하다.
* 생성자를 이용하면 객체마다 다른 값을 설정할 수 있다.
하지만 객체가 생성된 후에는 값을 변경할 수 없음
final 메서드(method) - 오버라이딩 불가
- 메서드에 final을 붙이면 override를 제한하게 된다.
- 부모클래스의 동작을 변경하지 않도록 보호할 때 사용한다.
- final 메서드는 자식 클래스에서 수정할 수 없다.
- 보안이 중요한 로직이나 변경하면 안 되는 동작을 보호할 때 사용한다.
final 클래스(Class) - 상속 불가
- final 키워드를 클래스에 붙이면 상속 불가능 클래스가 된다.
즉, 다른 클래스에서 상속하여 재정의를 할 수 없다.
- 다른 클래스가 이 클래스를 확장(extends)하지 못하도록 제한, 보안이 필요한 클래스에서 사용한다.