왜 Java를 쓰는가
Java가 지금도 널리 쓰이는 이유와, 문법보다 플랫폼과 생태계 관점에서 어떤 강점을 가지는지 정리합니다.
List<String> names = rawNames.stream()
.filter(n -> n.length() >= 3)
.map(String::toUpperCase)
.toList();Category
Preparing references and filters for this topic. 이 주제의 레퍼런스와 필터를 준비하고 있습니다.
Category Reference
기본문법, 객체지향, 컬렉션, NIO, 스트림, 동시성, 테스트, 빌드 도구까지 Java의 핵심 흐름을 카드형 레퍼런스로 정리합니다.
Search titles, summaries, tags, and subcategories.
Showing 36 cards.
Subcategory
1 cards
Java가 지금도 널리 쓰이는 이유와, 문법보다 플랫폼과 생태계 관점에서 어떤 강점을 가지는지 정리합니다.
List<String> names = rawNames.stream()
.filter(n -> n.length() >= 3)
.map(String::toUpperCase)
.toList();9 cards
Java 프로그램의 기본 단위인 package, import, 클래스 파일 구조, `main` 메서드 의미를 정리합니다.
package com.example.app;
import java.util.List;
public class Main {
public static void main(String[] args) {
System.out.println("Hello, Java");
}
}Java의 primitive type, 참조 타입, 변수 선언과 초기화 규칙, 자동 형변환 감각을 정리합니다.
// 정수
byte b = 127; // 1바이트, -128 ~ 127
short s = 32_000; // 2바이트
int i = 2_147_483_647; // 4바이트 — 기본 정수 타입
long l = 10_000_000_000L; // 8바이트, 리터럴에 L 필요
// 실수
float f = 3.14f; // 4바이트, 리터럴에 f 필요
double d = 3.14; // 8바이트 — 기본 실수 타입
// 기타
char c = 'A'; // 2바이트, 유니코드 문자
boolean active = true; // true / false
// 참조 타입
String name = "RefDock"; // 객체, null 가능
// var — 타입 추론 (Java 10+)
var count = 42; // int로 추론
var items = new ArrayList<String>(); // ArrayList<String>으로 추론
// 상수 — final
final int MAX_RETRY = 3;`if`와 `switch`를 언제 선택하는지, 현대 Java의 switch expression이 어떻게 fall-through 버그와 보일러플레이트를 없애는지 정리합니다.
// 단순 조건
if (score >= 60) {
System.out.println("합격");
}
// if-else if-else — 범위 조건
if (score >= 90) {
grade = "A";
} else if (score >= 80) {
grade = "B";
} else {
grade = "C";
}
// 삼항 연산자 — 한 줄 조건 대입
String result = score >= 60 ? "합격" : "불합격";
// switch expression — 값 종류가 명확히 갈릴 때
String label = switch (status) {
case ACTIVE -> "활성";
case INACTIVE -> "비활성";
case PENDING -> "대기";
};`for`, `for-each`, `while`을 어떤 기준으로 고르는지, `break`와 `continue`가 실제로 어떻게 흐름을 바꾸는지 정리합니다.
// 인덱스가 필요한 반복
for (int i = 0; i < names.length; i++) {
System.out.println(i + ": " + names[i]);
}
// 컬렉션 순회 — 인덱스 불필요
for (String name : names) {
System.out.println(name);
}
// 종료 시점이 동적
while (!queue.isEmpty()) {
process(queue.poll());
}Java 배열이 고정 길이인 이유, `length` 필드와 `Arrays` 유틸리티 사용법, 배열 대신 컬렉션을 써야 하는 시점을 정리합니다.
// 배열 선언과 초기화
int[] numbers = {10, 20, 30, 40, 50};
String[] names = new String[3]; // 기본값 null로 초기화
// 길이 확인 — length 필드 (메서드 아님)
System.out.println(numbers.length); // 5
// 인덱스로 접근
System.out.println(numbers[0]); // 10
numbers[1] = 99; // 수정 가능`String`이 불변 객체인 이유와 그 결과로 `==`가 아닌 `equals()`를 써야 하는 이유, 반복 연결에서 `StringBuilder`가 필요한 시점을 정리합니다.
String name = "Kim";
// 값 비교는 반드시 equals()
String a = new String("hello");
String b = new String("hello");
System.out.println(a.equals(b)); // true
System.out.println(a == b); // false — 참조가 다름
// 자주 쓰는 메서드
System.out.println(name.length()); // 3
System.out.println(name.toUpperCase()); // "KIM"
System.out.println(" hello ".strip()); // "hello"
System.out.println(name.contains("im")); // true`String.format`, `formatted()`, `printf`의 차이, 텍스트 블록으로 여러 줄 문자열을 다루는 방법, 포맷 지정자 선택 기준을 정리합니다.
String name = "Kim";
int score = 95;
// String.format — 새 문자열 반환
String msg = String.format("이름: %s, 점수: %d", name, score);
// formatted() — Java 15+, 동일한 결과
String msg2 = "이름: %s, 점수: %d".formatted(name, score);
// 문자열 보간처럼 읽히는 방식 (Java 21+ 미리보기는 아직 없음)
System.out.printf("이름: %s, 점수: %d%n", name, score);기본 타입이 컬렉션에 들어갈 수 없는 이유, 오토박싱이 자동으로 일어나는 시점, 예상치 못한 `NullPointerException`과 `==` 비교 함정을 정리합니다.
// 기본 타입 ↔ 래퍼 클래스
int primitive = 42;
Integer boxed = primitive; // 오토박싱 — 컴파일러가 Integer.valueOf(42)로 변환
int unboxed = boxed; // 언박싱 — 컴파일러가 boxed.intValue()로 변환
// 컬렉션에는 래퍼 클래스 필요
List<Integer> numbers = new ArrayList<>();
numbers.add(10); // 오토박싱
int n = numbers.get(0); // 언박싱`java.time`의 `LocalDate`, `LocalDateTime`, `ZonedDateTime`을 어떤 기준으로 고르는지, 불변 설계가 날짜 연산을 어떻게 안전하게 만드는지 정리합니다.
// 오늘 날짜
LocalDate today = LocalDate.now();
LocalDate birthday = LocalDate.of(1990, 3, 15);
// 날짜 연산 — 새 객체 반환 (불변)
LocalDate nextWeek = today.plusDays(7);
LocalDate lastMonth = today.minusMonths(1);
// 포매팅
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String formatted = today.format(fmt); // "2026-03-27"6 cards
Java 객체지향의 출발점인 class, field, method, constructor, instance 개념을 한 흐름으로 정리합니다.
public class User {
private String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
User user = new User("Kim");
System.out.println(user.getName());Java의 네 가지 접근 제한자가 어떤 범위를 열고 닫는지, `package-private`이 왜 중요한지, 캡슐화 설계 기준을 정리합니다.
public class BankAccount {
private double balance; // 외부 직접 접근 차단
private String ownerId;
public void deposit(double amount) { // 공개 API
if (amount > 0) balance += amount;
}
public double getBalance() {
return balance;
}
private boolean isValid(double amount) { // 내부 구현
return amount > 0 && amount <= balance;
}
}상속이 코드 재사용을 넘어서 타입 관계를 어떻게 만들고, 다형성이 왜 중요한지 정리합니다.
class Animal {
void speak() {
System.out.println("...");
}
}
class Dog extends Animal {
@Override
void speak() {
System.out.println("woof");
}
}공통 계약을 표현하는 interface와 부분 구현을 제공하는 abstract class를 어떻게 구분할지 정리합니다.
interface Payable {
int pay();
}
abstract class Employee implements Payable {
protected String name;
}객체 비교에서 참조 비교와 값 비교가 어떻게 다르고, equals/hashCode/Comparable 계약을 어떻게 맞춰야 하는지 정리합니다.
public final class User implements Comparable<User> {
private final long id;
private final String email;
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof User other)) return false;
return id == other.id && Objects.equals(email, other.email);
}
@Override
public int hashCode() {
return Objects.hash(id, email);
}
@Override
public int compareTo(User other) {
return Long.compare(this.id, other.id);
}
}`enum`이 문자열 상수보다 안전한 이유, 메서드와 필드를 추가하는 방법, switch expression과 함께 모든 케이스를 컴파일 타임에 강제하는 패턴을 정리합니다.
enum Status {
ACTIVE, INACTIVE, PENDING
}
Status s = Status.ACTIVE;
// switch expression과 함께 — 모든 케이스 처리 강제
String label = switch (s) {
case ACTIVE -> "활성";
case INACTIVE -> "비활성";
case PENDING -> "대기";
};5 cards
Java 제네릭이 왜 필요한지, `List<String>` 같은 타입 매개변수가 컴파일 타임 안전성을 어떻게 높이는지 정리합니다.
List<String> names = new ArrayList<>();
names.add("Kim");
String first = names.get(0);`<T extends Comparable<T>>`로 타입에 제약을 거는 방법, `? extends`와 `? super`가 어떤 문제를 해결하는지, 타입 소거가 런타임에 미치는 영향을 정리합니다.
// bounded type — T에 제약 추가
public static <T extends Comparable<T>> T max(List<T> list) {
return list.stream().max(Comparator.naturalOrder()).orElseThrow();
}
// wildcard — 읽기 전용 컬렉션 수용
public static double sum(List<? extends Number> numbers) {
return numbers.stream().mapToDouble(Number::doubleValue).sum();
}
max(List.of(3, 1, 4)); // T = Integer
sum(List.of(1, 2.5, 3L)); // Integer, Double, Long 모두 수용Java Collections Framework의 핵심인 `List`, `Set`, `Map`을 어떤 상황에 고르는지 정리합니다.
List<String> list = new ArrayList<>();
Set<String> set = new HashSet<>();
Map<String, Integer> map = new HashMap<>();Java에서 수정 가능한 컬렉션과 수정 불가능한 컬렉션이 어떻게 다르고, `List.of`, `Set.of`, `Map.of`를 어떤 관점으로 써야 하는지 정리합니다.
List<String> roles = List.of("ADMIN", "EDITOR", "VIEWER");
Set<Integer> levels = Set.of(1, 2, 3);List와 Map을 어떻게 순회하고, 정렬 기준을 Comparable과 Comparator 중 어디에 둘지 정리합니다.
users.sort(Comparator
.comparing(User::getAge)
.thenComparing(User::getName));
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}3 cards
Java 예외 모델의 핵심인 checked exception과 unchecked exception 차이, `try-catch-throws` 흐름을 정리합니다.
try {
Files.readString(path);
} catch (IOException e) {
System.out.println("읽기 실패: " + e.getMessage());
}`try-with-resources`가 왜 중요한지와 `Files`, `Path`를 이용한 기본 파일 처리 흐름을 정리합니다.
Path path = Path.of("notes.txt");
try (BufferedReader reader = Files.newBufferedReader(path)) {
System.out.println(reader.readLine());
}현대 Java 파일 I/O의 핵심인 `Path`와 `Files`를 사용해 경로 조합, 파일 읽기/쓰기, 디렉터리 작업을 어떻게 읽어야 하는지 정리합니다.
Path path = Path.of("data", "users.txt");
if (Files.exists(path)) {
List<String> lines = Files.readAllLines(path);
}5 cards
Java의 lambda expression과 method reference가 어떤 문제를 줄이고, 함수형 스타일과 어떻게 연결되는지 정리합니다.
names.sort((a, b) -> a.compareToIgnoreCase(b));
names.forEach(System.out::println);Java Stream이 컬렉션과 어떻게 다르고, pipeline, intermediate, terminal operation을 어떻게 읽어야 하는지 정리합니다.
List<String> result = names.stream()
.filter(name -> name.length() >= 3)
.map(String::toUpperCase)
.toList();Stream의 마지막 단계에서 결과를 어떤 컬렉션 모양으로 모을지 결정하는 `Collectors`와 `groupingBy`, `toMap` 패턴을 정리합니다.
Map<String, List<Student>> byDepartment =
students.stream()
.collect(Collectors.groupingBy(Student::getDepartment));`Optional`이 null 자체를 없애는 도구가 아니라, 값이 없을 수 있음을 더 명시적으로 표현하는 방법이라는 점을 정리합니다.
Optional<User> user = findUser(id);
String name = user.map(User::getName)
.orElse("anonymous");`record`가 어떻게 값 객체 보일러플레이트를 없애는지, `instanceof`의 패턴 매칭이 cast 코드를 어떻게 줄이는지 정리합니다.
// record — 한 줄로 불변 값 객체
record Point(int x, int y) {}
Point p = new Point(3, 4);
System.out.println(p.x()); // 3 — 접근자 자동 생성
System.out.println(p); // Point[x=3, y=4] — toString 자동
// instanceof 패턴 매칭 — 타입 검사 + 변수 선언 한 번에
Object obj = "hello";
if (obj instanceof String s) {
System.out.println(s.toUpperCase()); // cast 없이 바로 사용
}4 cards
Java 스레드가 무엇인지, Thread와 Runnable의 역할이 어떻게 다르고 lifecycle을 어떻게 읽어야 하는지 정리합니다.
Runnable task = () -> {
System.out.println("work on " + Thread.currentThread().getName());
};
Thread thread = new Thread(task, "worker-1");
thread.start();
thread.join();직접 Thread를 만드는 방식과 thread pool 기반 실행 방식이 어떻게 다르고, Callable/Future를 왜 함께 쓰는지 정리합니다.
ExecutorService pool = Executors.newFixedThreadPool(4);
Future<Integer> future = pool.submit(() -> 40 + 2);
int value = future.get();
pool.shutdown();비동기 작업의 결과를 기다리고 이어 붙이는 `CompletableFuture`를 `Future`보다 어떤 관점에서 이해하면 좋은지 정리합니다.
CompletableFuture<String> future =
CompletableFuture.supplyAsync(() -> "hello")
.thenApply(String::toUpperCase);
String result = future.join();여러 thread가 같은 상태를 건드릴 때 왜 문제가 생기고, synchronized와 lock을 어떤 기준으로 고를지 정리합니다.
public class Counter {
private int value;
public synchronized void increment() {
value++;
}
public synchronized int getValue() {
return value;
}
}3 cards
Java 테스트에서 JUnit이 어떤 역할을 하고, 테스트 메서드, assertion, fixture를 어떻게 조직하는지 정리합니다.
class CalculatorTest {
@Test
void add_returnsSum() {
Calculator calculator = new Calculator();
int result = calculator.add(2, 3);
assertEquals(5, result);
}
}입력 케이스를 여러 번 돌리는 parameterized test와 테스트 준비/정리 단계의 lifecycle을 JUnit 5 기준으로 정리합니다.
@ParameterizedTest
@ValueSource(ints = {0, 1, 2, 10})
void isNonNegative_returnsTrue(int value) {
assertTrue(value >= 0);
}Java 프로젝트에서 Maven과 Gradle이 무엇을 해 주는지, dependency 관리와 build 흐름을 어떤 관점으로 이해하면 좋은지 정리합니다.
src/
main/
java/
test/
java/
pom.xml # Maven
build.gradle # Gradle