[Java] 주요 개념 리뷰

이지연·2026년 1월 13일

자바 총정리 리뷰 (클래스/객체부터 스레드·네트워킹까지)

이번 글은 자바를 학습하면서 직접 정리했던 내용을 “흐름이 끊기지 않게” 다듬은 리뷰다.
기본 문법 수준을 넘어서, 객체지향 설계의 이유(캡슐화/상속/다형성), 추상화(추상클래스·인터페이스), 함수형 스타일(Stream/Lambda/Optional), 예외·파일·JSON 처리, 그리고 멀티스레드·네트워킹 관점까지 한 번에 연결해보는 것이 목표다.


C02MethodClass 패키지

C03Class & C05Scope

클래스는 한마디로 “설계도”다. 동일한 구조의 데이터와 기능을 재사용/확장 가능하게 만들어주며, 큰 시스템일수록 클래스 설계의 품질이 유지보수성과 직결된다.

여기서 가장 먼저 정리해야 하는 포인트가 static이다.

  • 클래스 생성 시 static을 붙이면 “클래스 소속(공유)”이 된다.
  • static을 빼면 “객체 소속(개별)”이 된다.
  • 클래스를 통해 복제본을 만든 것이 객체(인스턴스)이며, 객체의 핵심은 “각 인스턴스가 고유한 메모리/상태를 가진다”는 점이다.
  • static이 붙으면 객체마다 고유 상태를 갖는 것이 아니라 클래스 단위로 값/로직을 공유하게 된다. 즉, 객체지향 설계의 목적과는 방향이 달라질 수 있다.

객체를 생성하기 위한 설계는 결국 “인스턴스 중심”이어야 한다.
static을 제거하면 메서드/변수는 각각 “객체의 메서드/객체의 변수(필드)”가 된다. 그래서 자바는 기본적으로 객체 기반 설계를 권장하고, 이를 객체지향 언어라고 부른다.

객체를 만들기 위해서는 new로 메모리를 할당한다.
즉, 객체는 고유 메모리를 가지고 그 내부의 값을 할당/변경하며 상태를 유지한다.

그렇다면 클래스 메서드(static)는 언제 쓰는가?

  • 객체의 고유 상태를 변경/누적하는 목적이 아니라
  • 매개변수에 따라 계산하고 결과를 리턴하는 “상태 비의존적(독립적) 로직”일 때 사용 가능하다.

C04PersonMain

객체를 사용하는 이유는 크게 두 가지로 정리할 수 있다.

1) 고유의 메모리: 인스턴스마다 독립적인 상태를 가진다.
2) 데이터의 그룹핑: 관련 데이터를 하나로 묶어 관리가 쉬워진다.

예를 들어 사람을 표현할 때 name, email 등을 사람마다 따로 변수로 만들면 관리가 어려워진다.
Person 클래스를 만들고 객체로 생성하면 “한 사람의 데이터를 하나의 덩어리”로 묶어서 관리할 수 있다.

그리고 실무 관점에서 안정성을 높이려면 필드를 private로 선언하는 것이 거의 표준이다.

  • private이면 외부에서 p1.name처럼 직접 접근이 불가해진다.
  • 다른 클래스가 임의로 값을 바꾸는 구조를 막아 시스템 안정성이 올라간다(캡슐화).

객체 생성 직후 필드 초기값도 알아두면 좋다.

  • 참조형(String 등) = null
  • 원시형(int 등) = 0 같은 기본값

값을 세팅하는 방법은 보통 두 가지다.

  • setter: 값을 “변경”하려는 의도를 메서드로 드러낸다(일반적으로 getter도 함께 필요).
  • 생성자: 객체 생성 시점(new)에 필드를 초기화하는 방식으로 초기 세팅에 더 적합하다.

그리고 toString()이 없으면 객체 출력 시 기본적으로 클래스명@해시 형태만 출력된다.


C06MethodOverloading

오버로딩과 오버라이딩은 이름이 비슷하지만 완전히 다르다.

  • 메서드 오버로딩(Overloading)

    • 메서드명은 같지만 매개변수의 개수 또는 타입이 다르다.
    • 예: println, sum 같은 형태가 대표적이다.
  • 메서드 오버라이딩(Overriding)

    • 상속/인터페이스 관계에서 부모의 메서드를 자식이 재정의하는 것(시그니처 동일).

C07Constructor

생성자(Constructor)는 객체가 만들어지는 시점(new)에 자동 호출되는 “초기화용 메서드”다.

  • 반환타입이 없다.
  • 생성자명은 클래스명과 동일하다.
  • 오버로딩한 생성자를 개발자가 만들면 기본생성자는 자동 제공되지 않으므로, 기본생성자가 필요하면 직접 작성해야 한다.

생성자의 사용 목적은 명확하다.

  • 객체 사용 시점에 필드를 안정적으로 초기화하기 위함
  • setter는 변경 가능성을 열어두므로 초기 세팅은 보통 생성자를 선호한다.

setter가 꼭 필요하다면 “의도”를 드러내는 이름을 권장한다.

  • setBalance(x) 보다는
  • transfer / deposit / withdraw 처럼 “업무 행위(서비스 의도)”를 담는 방식이 더 좋다.

제네릭(Generic)

제네릭은 타입을 일반화해서 재사용성을 높이는 문법이다.

  • 제네릭에는 원시자료형이 직접 들어갈 수 없고(Wrapper 사용), 참조형은 모두 가능하다.
  • 내가 만든 클래스도 제네릭 타입으로 사용할 수 있다.
  • List<Account>, List<Post>처럼 도메인 객체를 컬렉션으로 관리하는 경우가 매우 흔하다.

C09BankService: 계좌(Account) 생성·이체 실습 포인트

실습에서 자주 만나는 포인트는 “타입 선택의 의도”다.

  • private Long id;
    • 초기값이 null인 것이 더 자연스러운 경우(“아직 부여되지 않음”)가 많아 Wrapper(Long) 사용.
  • long balance;
    • 초기값이 0인 것이 더 자연스러운 경우가 많아 원시형(long) 사용.

staticId 같은 방식은 객체가 생성될 때마다 클래스 내부에서 증가시키는 형태로 구현할 수 있다.
다만 실무에서는 보통 DB의 auto increment(또는 시퀀스 등)로 대체되고, Spring/DB 단계에서 처리되는 경우가 많다.

setter가 필요한 경우도 있다.

  • 이름 변경, 잔고 변경 등 “값 변경 요구”가 실제 존재한다면 setter가 필요할 수 있다.
  • 하지만 setBalance 같은 일반 setter보다 transfer/deposit/withdraw 같은 도메인 행위 메서드가 더 권장된다.

객체 메서드 지향 관점도 중요하다.

  • a.transfer(b, 50000);
    • 객체가 자기 상태/행위를 책임지는 형태(권장)
  • Account.transfer(a, b, 50000);
    • 클래스 메서드로 처리(지양)

C10BoardService

서비스를 설계할 때 가장 먼저 해야 할 일은 “핵심 엔티티”를 정의하는 것이다.

  • 자바에서는 엔티티가 클래스/객체로 표현된다.
  • DB에서는 테이블로 표현된다.
  • 예: 게시판 서비스 핵심 엔티티 = 회원(Author/User), 게시글(Post)

관계 표현 예시는 다음과 같다.

  • Post post = new Post(title, contents, author);
    • 게시글이 작성자(Author) 객체를 직접 참조하는 아키텍처(관계 표현)

또한 AuthorList<Post> postListByAuthor를 두면 작성자가 작성 글 목록을 “소유/조회”할 수 있는 구조가 된다(양방향 관계 모델링 예시).

  • DB 관계에서는 1:N
  • Spring/JPA 관점에서는
    • 회원의 List<Post> 때문에 one-to-many
    • 게시글의 회원 객체 때문에 many-to-one

C03Inheritance 패키지

C01Inheritance

상속은 “기존 클래스를 확장해서 재사용”하는 핵심 도구다.

  • A 클래스를 extendsB 클래스는 A의 필드와 메서드를 상속받을 수 있다.

상속의 핵심 특징 2가지:

1) 오버라이딩 가능

  • 부모 메서드를 자식에서 다시 구현할 수 있고, 호출 시 자식 메서드가 우선된다.

2) 다형성

  • 부모 타입 변수로 자식 객체를 참조할 수 있다.
  • 단, “참조 변수 타입” 기준으로 사용 가능한 메서드가 제한된다(컴파일 타임 기준).
  • 즉, A 타입으로 B를 담으면 A에 선언된 메서드만 호출 가능하고, B 고유 메서드는 직접 호출 불가(다운캐스팅 필요).

다형성 예시(인터페이스/상속 모두 같은 관점):

  • Queue<Integer> myQ = new LinkedList<>();
    • myQQueue 타입이므로 Queue에 정의된 메서드만 사용할 수 있다(LinkedList 고유 기능은 제한).

그리고 @Override는 “재정의 의도”를 명시한다.

  • 시그니처 불일치 같은 실수를 컴파일 단계에서 잡아준다.

C02SuperKeyword

super는 부모를 가리키는 키워드다.

  • super() : 부모 클래스 생성자 호출(생성자 첫 줄에서 호출됨)
  • super.필드 / super.메서드() : 부모 멤버에 접근(자식에서 재정의된 메서드와 구분할 때 사용)

C03AnimalMain 실습

다형성을 체감하기 좋은 예시:

  • Animal dog2 = new Dog();
    • Animal 타입 하나로 여러 자식 객체(Dog, Cat 등)를 받아 공통 로직으로 처리할 수 있다.

C04ProtectedClass

접근 제어자의 범위는 다음 순서로 넓어진다.

  • public > protected > default(패키지) > private

실무 관례로는 다음 패턴이 일반적이다.

  • 클래스는 public
  • 필드(변수)는 private
  • 메서드를 통해 제어(캡슐화)

학습/시험에서는 각각의 범위를 정확히 구분해 묻는 경우가 많다.


C06AbstractMain: 추상클래스와 인터페이스

추상클래스(abstract class)

  • 추상메서드 + 일반메서드 공존 가능
  • 추상메서드는 구현체(바디)가 없다.
  • 추상메서드가 하나라도 있으면 클래스에도 abstract를 붙여야 한다.
  • 자식 클래스가 extends로 상속받아 추상메서드를 오버라이딩하여 구현해야 한다.

인터페이스(interface)

  • 기본 관점에서 메서드가 전부 추상메서드인 “규격(구현 강제)” 역할
  • 구현 클래스는 implements로 구현하고 모든 추상메서드를 오버라이딩해야 한다.
  • 참고로 자바에서는 default/static 메서드로 구현을 포함할 수도 있지만, 학습 단계에서는 “규격” 이해가 핵심이다.

상속/구현 형태 요약

  • 자식클래스 extends 추상클래스
  • 자식클래스 implements 인터페이스

final 키워드

  • 클래스에 붙이면 상속 불가(사용 빈도는 상대적으로 낮음)
  • 변수에 붙이면 상수(재할당 불가)로 사용되어 실무에서 자주 사용됨

C04Interface 패키지

C01AnimalInterface1

인터페이스 간에는 extends가 가능하다.

  • 예: interface A extends B, C
    • 인터페이스는 다중 상속이 가능하다.

클래스 관련 정리

  • 클래스는 다중 상속 불가능
  • 클래스는 다중 구현 가능(implements 여러 개 가능, 오버라이딩 구현 강제)

C05AnonymousLambda

익명 클래스/람다는 “이름 없는 구현(일회성 구현)”을 빠르게 만들기 위한 문법이다.
특히 함수형 인터페이스 구현을 간단히 표현할 때 자주 사용된다.


C04StreamApi

Stream API는 자바의 함수형 프로그래밍 스타일을 지원하는 도구다.

  • 함수형 프로그래밍 관점: input → output 중심
  • 원본 데이터를 직접 바꾸기보다 “흐름(파이프라인)”으로 처리한다.
  • 스트림은 원본 컬렉션/배열을 직접 조작하기보다, 스트림 객체(처리용 뷰/파이프라인)를 생성해 연산을 수행한다.

스트림 종류 예시

  • Stream<T>, IntStream, LongStream

처리 방식

  • 중개연산(intermediate): filter/map/sorted 등(지연 실행, 파이프라인 구성)
  • 최종연산(terminal): forEach/collect/count/reduce 등(실행 트리거, 스트림 소모)

Optional / OptionalInt 등

  • 값이 “있을 수도/없을 수도” 있음을 명시적으로 표현하는 컨테이너다.
  • max/min 결과가 Optional인 이유: 대상이 비어있으면 “값 자체가 없음”을 표현해야 하므로 0 같은 임의값을 리턴할 수 없기 때문이다.
  • 따라서 isPresent 등으로 존재 여부를 확인한 뒤 getAsInt/get 등을 사용한다(또는 더 안전한 꺼내기 방식 사용).

메서드 참조(Method Reference)

  • 하나의 메서드만 호출하는 단순 람다는 더 짧게 줄일 수 있다.
  • 예: System.out::println

Optional 객체 처리(값 꺼내기/대안 처리) 대표 방식 4가지

1) isPresent() 확인 + get()/getAsInt()
2) orElse(기본값 제공)
3) orElseGet(기본값을 공급하는 함수 제공)
4) orElseThrow(없으면 예외 발생)

자주 쓰는 패턴은 isPresent 체크 후 get() 또는 orElseThrow로 “없으면 중단” 처리다.

Optional은 직접 생성하는 경우보다, 라이브러리/프레임워크가 Optional로 리턴해주는 값을 “어떻게 처리할지”가 더 중요하다.

예외 관련 개념

  • 예외 강제 발생(throw): 의도된 오류 상황에서 흐름을 중단시키기 위함(더 진행하면 안 되는 상태).
  • 예외 처리(try-catch): 중단 자체를 막는 목적도 있지만, 실무에서는 사용자에게 적절한 메시지/응답을 전달하기 위해 처리하는 의미가 크다.
  • 예: 회원가입 시 이미 존재하는 이메일 → 강제 예외 발생 → 예외 처리로 사용자에게 안내 메시지 반환.

C06EtcClass 패키지

C03EnumMain

Enum(열거형)은 데이터 값을 통일/표준화하기 위해 사용하는 타입이다.

  • 예: 상태값, 카테고리, 권한 등 “정해진 값 목록”을 안전하게 관리할 때 유용하다.

C06EtcClass 패키지

04GenericMain

제네릭은 타입을 일반화하여 재사용성을 높인다.

  • 자바에서 any처럼 받을 수 있는 형태 예시: <T>, Object, List<?>
  • Optional과 유사하게 “직접 복잡하게 설계”하는 경우는 많지 않고
  • 이미 제네릭으로 제공되는 컬렉션/라이브러리를 “정확히 사용”하는 일이 더 빈번하다.

C07ExceptionFileParsing 패키지

01ExceptionBasic

예외가 발생할 수 있는 코드를 try로 감싸고, 실무(웹)에서는 에러 메시지/상태를 HTTP 응답 문서 형태로 만들어 프론트에 전달해야 한다.
프론트는 이를 받아 alert/toast 등으로 사용자에게 안내한다.


07ExceptionFileParsing 패키지

02ExceptionAdvance

예외 강제 발생 → 적절한 예외 처리 흐름을 연습한다.

  • checked exception: 컴파일 타임에 처리 강제(보통 try-catch 또는 throws 필요)
  • unchecked exception: 런타임 예외(필요 시 throw new로 발생시키고, 상위에서 처리/전파)

C03File

IO vs NIO(Non-blocking 관점)

  • NIO는 non-blocking 방식 등으로 IO 처리 효율을 높일 수 있다.
  • 논블로킹과 비동기는 동일 개념은 아니지만, “다른 작업을 기다리며 멈추지 않는다”는 관점에서 유사하게 이해할 수 있다(성능상 이점).

synchronized(동기화)

  • 다른 스레드의 동시 실행을 제한해 동시성 문제를 방지한다.
  • 대신 락으로 인해 성능이 느려질 수 있다.
  • 예: StringBuffersynchronized 기반이라 상대적으로 느릴 수 있다.
  • 단, 동기화가 없으면 동시성 문제가 발생할 수 있으므로 트레이드오프를 이해해야 한다.

실무 관점

  • 파일 읽기/쓰기 자체를 애플리케이션 서버에서 직접 많이 하기보다, S3 같은 외부 스토리지를 활용하는 구조가 흔하다.

C04Json

JSON은 전체적으로는 문자열이지만 내부적으로는 데이터 타입(boolean, int 등)을 각각 가진다.
ObjectMapper 클래스를 통해 파싱/직렬화를 진행한다.

  • readValue: 파싱(역직렬화), JSON → 객체
  • writeValueAsString: 직렬화, 객체 → JSON

보통 POST 요청이 들어오면 파싱을 수행하고, GET 요청이 들어오면 직렬화를 수행한다.
이 과정은 스프링에서는 반복이 너무 많아서 자동화되어 있으며, 프론트에서 axios.get으로 받아오면 바로 객체로 쓰는 느낌과 비슷하게 이해할 수 있다.

파싱 시에는 자바의 리플렉션 기술을 통해 객체를 생성한다.
이때 private 설계에도 접근이 가능해지고, 기본생성자와 getter가 JSON 데이터 형식과 동일하게 세팅되어야 하는 이유도 함께 이해할 수 있다.


C05HttpJsonParsing

(이 파트는 제목만 있고 상세가 비어 있으니, 블로그에서는 아래처럼 “다음 학습 예고” 형태로 채우면 자연스럽다.)

  • HTTP 요청/응답에서 JSON이 오갈 때
  • 요청 바디(JSON) → 자바 객체 바인딩
  • 자바 객체 → 응답 JSON 직렬화
  • 이 흐름을 스프링이 어떤 단계에서 자동 처리하는지(컨트롤러/메시지 컨버터 등)를 다음 글에서 정리할 예정

C08Thread 패키지

ThreadMain

자바에서 스레드는 작업을 처리하는 주체(자원)이다.
컴퓨터에서 CPU 코어가 작업을 할당받듯이, 자바 역시 실행 시 main에 스레드가 할당된다.
그리고 웹서비스는 단일 작업이 아니라 여러 요청을 동시에 처리해야 하므로 “스레드/동시성” 개념이 필수로 따라온다.


C09NetWorking 패키지

C01WebServerGet

서버 생성 단계에서 socket을 통해 사용자와 서버 사이의 접점을 만든다(예: 8080이라는 단자를 만든다고 생각).
스프링의 경우 while문이 없어도 서버가 계속 꺼지지 않고 요청을 지속적으로 받는다.

GET 요청에서 데이터를 넘기는 경우도 있는데, 보통 바디에 담기보다는 URL에 함께 담아 넘긴다.

  • 예: http://localhost:8080/post?title=안녕

그리고 순수 자바로 요청/응답 처리에서 어려운 점이 명확하다.

1) 사용자의 요청값 parsing(HTTP request 문서 파싱)
2) 사용자에게 응답값 제공(HTTP response 문서 생성)

이 지점이 바로 스프링(특히 스프링 MVC/스프링부트)이 엄청난 반복 작업을 대신해주는 이유로 이어진다.


C03DBConnect

DB 연결은 JDBC, MyBatis 등을 사용해 진행한다.
처음에는 “연결/쿼리/결과 매핑”의 흐름을 직접 경험해보는 것이 도움이 되고, 이후 스프링 단계에서 이 반복이 어떻게 추상화/자동화되는지로 연결하면 학습 효율이 좋아진다.

profile
Eazy하게

0개의 댓글