일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- Java IO
- 리스트
- 동시성
- LIST
- Collection
- 김영한
- 도커 엔진
- Kubernetes
- 스레드
- 멀티 쓰레드
- 도커
- 컨테이너
- 쿠버네티스
- 자바 입출력 스트림
- java socket
- 실전 자바 고급 1편
- 자바
- Thread
- java
- Docker
- 시작하세요 도커 & 쿠버네티스
- 인프런
- 자바 io 보조스트림
- 스레드 제어와 생명 주기
- 알고리즘
- 자료구조
- filewriter filereader
- 쓰레드
- java network
- container
- Today
- Total
쌩로그
인프런 - 김영한 - 실전 자바 기본편 요약 및 정리 본문
목록
- 개요
- 본론
2-1. 캡슐화
2-2. 패키지
2-3. 자바 메모리 구조
2-4. 코드는 읽는 사람을 위해 써야한다.
2-5. 상속과 메모리 구조
2-6. 다형성 - 다형적 참조 부모와 자식의 인스턴스 관계
2-7. 다형성 - 메서드 오버라이딩 - 요약
1. 개요
해당 포스팅은 김영한의 실전 자바 기본편
을 학습하고 정리한 포스팅이다.
참고로 정리만 필요한 부분만 정리했다.
2. 본론
2-1. 캡슐화
캡슐화 정의
- 데이터와 해당 데이터르 처리하는 메서드를 하나로 묶어서 외부에서의 접근을 제한하는 것
- 데이터의 직접적인 변경을 방지하거나 제한할 수 있다.
- 속성과 기능을 하나로 묶고(클래스로), 외부에 꼭 필요한 기능만 노출하고 나머지는 내부로 모두 숨긴다.
캡슐화를 코드로 배웠다.
객체 지향 프로그램의 특징이 뭐냐고 하면 캡상다추를 떠올리는데, 캡이 캡슐화다.
정의는 다음과 같다.
속성과 기능을 하나로 묶어서 필요한 기능을 메서드를 통해 외부에 제공하는 것
그냥 클래스를 만들면 해결된다.
public class MusicPlayerMain3 {
public static void main(String[] args) {
MusicPlayer musicPlayer = new MusicPlayer();
// 볼륨 켜기
musicPlayer.on();
// 볼륨 증가
musicPlayer.volumeUp();
// 볼륨 증가
musicPlayer.volumeUp();
// 볼륨 감소
musicPlayer.volumeDown();
// 상태 확인
musicPlayer.showStatus();
// 플레이어 끄기
musicPlayer.off();
}
}
예를 들어 위와 같이 MusicPlayer를 사용하는 코드가 있다고 하자.
그냥 객체를 하나 생성해서 기능을 사용하면 된다.
해당 객체를 사용하는 클라이언트(=외부)는 안에 구현이 어떻게 되어있는지 몰라도 된다.
그냥 기능을 가져다 쓰면 된다.
이처럼 클래스를 하나 잘 만드는 것만으로도 이미 캡슐화가 되는 것이다.
MusicPlayer 클래스는 다음과 같다.
public class MusicPlayer {
int volume = 0;
boolean isOn = false;
void on() {
isOn = true;
System.out.println("음악 플레이어를 시작합니다");
}
void off() {
isOn = false;
System.out.println("음악 플레이어를 종료합니다.");
}
void volumeUp() {
volume++;
System.out.println("음악 플레이어 볼륨:" + volume);
}
void volumeDown() {
volume--;
System.out.println("음악 플레이어 볼륨:" + volume);
}
void showStatus() {
System.out.println("음악 플레이어 상태 확인");
if (isOn) {
System.out.println("음악 플레이어 ON, 볼륨:" + volume);
} else {
System.out.println("음악 플레이어 OFF");
}
}
}
위와 같은데 volume
변수명을 volume2
로 하든, v
로 하든 외부에서는 알 필요가 없다.
그냥 MusicPlayer 에서만 알면 되는 것이다.
이게 캡슐화다...
이걸 코드로 배웠다..
막연하게 알던 걸 알게 되어서 정리해봤다.
2-2. 패키지

이런 식으로 있다고 해보자.
package pack;
import pack.a.User;
public class PackageMain3 {
public static void main(String[] args) {
User userA = new User();
pack.b.User userB = new pack.b.User();
}
}
다른 패키지에 똑같은 이름의 타입이 있을 때
- 둘 다 import 하지 못 한다.
- 하나는 import를 하고, 하나는 풀경로를 적어줘야 한다.
내 생각에는 "이런 경우 어차피 잘 없을건데.. 하고 생각중이었는데,,"
영한님이 이런 경우가 있다고 하더라;;;
그래서 당황했다.
그럴 때는 잘 사용하는 것은 import를 하고, 잘 사용하지 않는 것은 풀 경로를 적어주는 방식으로 사용한다고 하신다.
모든 패키지는 다 다른 패키지다.
- 위의 경우 처럼
pack
,pack.a
,pack.b
가 있는데,pack
패키지 아래 있다고 해서 공통적으로나 겹치는 패키지가 아니라, 이 세 패키지는 모두 각각 다른 패키지이다..!!
패키지 관례
- 소문자로 함
- 패키지 이름의 앞 부분에는 일반적으로 회사의 도메인 이름을 거꾸로 사용한다고 한다.
- 예를 들면
myapp.company.com
과 같은 애플리케이션이 있을 때큰 개념
->세부 개념
으로 가는 방식으로com.company.myapp
과 같은 방식으로 패키지를 사용한다.
- 예를 들면
2-3. 자바 메모리 구조
자바는 실행하면 클래스 정보를 읽는다.
클래스 정보를 읽고 메서드 영역에 올린다.
메서드 영역(Method Area)
- 클래스 정보 : 클래스의 실행 코드(바이트 코드), 필드, 메서드와 생성자 코드등 모든 실행 코드가 존재한다.
- static 영역 :
static
변수들을 보관한다. - runtime 상수 풀 : 프로그램을 실행하는데 필요한 공통 리터럴 상수를 보관한다.
- 프로그램에
hello
라는 리터럴 문자가 있으면 문자를 공통으로 묶어서 관리한다.
- 프로그램에
- 객체가 생성될 때 인스턴스 변수에는 메모리가 할당되지만, 메서드는 새로운 할당이 없다. 메서드는 메서드 영역에서 공통으로 관리되고 실행된다. 즉 인스턴스의 메서드를 호출하면 실제로는 메서드 영역에 있는 코드를 불러서 실행한다.
스택 영역(Stack Area)
- 자바 실행시, 하나의 실행 스택이 생성된다. 각 스택 프레임은 지역 변수, 중간 연산 결과, 메서드 호출 정보 등을 포함한다.
- 스택 프레임: 스택 영역에 쌓이는 네모 박스가 하나의 스택 프레임이다.
- 메서드를 호출할 때마다 하나의 스택 프레임이 쌓이고, 메서드가 종료되면 해당 스택 프레임이 제거된다.
- 참고로 쓰레드별로 하나의 스택이 생성된다.
힙 영역(Heap Area)
- 객체(인스턴스)와 배열이 생성되는 영역이다.
- 가비지 컬렉션(GC)이 이루어지는 주요 영역이며, 더 이상 참조되지 않는 객체는 GC에 의해 제거된다.
2-4. 코드는 읽는 사람을 위해 써야한다.
코드는 작성하는 사람을 위해서 쓰기보다, 읽는 사람을 위해 작성된 코드가 좋은 코드다.
2-5. 상속과 메모리 구조
상속 관계의 객체를 생성하면 그 내부에는 부모와 자식이 모두 생성된다.
예를 들어 다음과 같이 Car 클래스를 상속한 ElectricCar(전기차)의 코드가 다음과 같다고 가정할 때
public class ElectricCar extends Car {
...
}
ElectricCar
인스턴스를 생성하면 메모리 구조는 다음과 같이 적재된다.

상속 관계의 객체를 호출할 때, 대상 타입을 정해야 한다. 이때 호출자의 타입을 통해 대상 타입을 찾는다.
예를 들어서 위와 같이 메서드가 부모 클래스에는 move()
, 자식 클래스에는 charge()
가 있을 때charge()
메서드를 호출하려면 어디 타입의 charge()
메서드를 호출할지 결정해야 하는데, 호출자의 타입을 통해 대상 타입을 찾는다.
즉 다음과 같은 코드가 있다고 하자.
public class Main {
public static void main(String[] args) {
ElectricCar electricCar = new ElectricCar();
electricCar.charge();
}
}
참조형 변수 electricCar
은 ElectricCar
타입으로 선언되었다.
따라서 ElectricCar
의 charge()
를 호출한다.

반대로 move()
를 호출할 때 ElectricCar
에서 찾지 못하면 부모 클래스에서 해당 메서드를 찾는다.

현재 타입에서 기능을 찾지 못하면 상위 부모 타입으로 기능을 찾아서 실행한다. 기능을 찾지 못하면 컴파일 오류가 발생한다.
제목 그대로이다.
예를 들어 위의 Main
코드에서 생성된 인스턴스인 electircCar
에서 fly()
메서드를 호출한다고 가정하자. 자식 클래스에도, 부모 클래스에도 없다.
따라서 찾지 못하므로 컴파일 에러가 발생한다.
2-6. 다형성 - 다형적 참조 부모와 자식의 인스턴스 관계
다형성의 특징은 다음과 같다.
- 부모 타입의 변수에 자식 타입의 인스턴스를 담을 수 있다.
- 예를 들어 자동차가 부모이고 전기차가 자식이라면 다음과 같은 코드를 사용할 수 있다.
Car car = new ElectricCar();
- 자식 타입의 변수에 부모 타입의 인스턴스를 담을 수 없다.
- 전기차 타입의 변수에 자동차 클래스 인스턴스를 담을 수 없다.
ElectricCar electricCar = new Car();
- 위와 같은 경우 컴파일 오류가 발생한다.
- 부모 타입의 변수로 자식 타입의 인스턴스를 담는 경우 부모 타입의 메서드를 호출할 수 있지만, 자식 타입의 메서드를 호출할 수는 없다.
Car car = new ElectricCar();
- 위와 같은 경우,
car.move();
는 호출할 수 있지만, 전기차를 충전하는fillElectric()
메서드가 있다고 했을 때,car.fillElectric();
을 호출하면 컴파일 오류가 발생한다.
위의 내용은 다형성의 기본적인 내용이다.
나는 위의 내용을 코드로 확인하고, 텍스트로만 받아들여서 알고 있었고, 사실 지금도 원리에 대해서서 이해하지 못하는 부분도 있었다.
그런데 위에 정리한 상속과 메모리 구조를 보고 이번 강의에서 의문점을 풀수도 있겠다 싶었는데, 아니나 다를까 역시 이 부분도 메모리 구조로 풀어 설명하셨다.

Parent
타입으로 선언된 poly
변수에 자식 인스턴스를 담았다.
그리고 자식 타입의 메서드인 childMethod()
를 호출하려고 한다.
먼저는 poly
가 부모타입이기 때문에 부모 클래스에서 childMethod()
를 찾는다.
하지만 찾을 수 없다.
그래서 컴파일 오류가 발생한다.
부모 클래스는 자식 클래스의 정보를 알 수가 없다.
자식은 부모 클래스를 알 수 있지만, 부모 클래스는 자식에 대한 정보를 알 수 없기 때문에 자식 메서드를 호출하려고 해도 할 수가 없는 것이다.
따라서 컴파일 오류가 발생한다.
상속 관계는 부모 방향으로 찾아 올라갈 수는 있지만 자식 방향으로 찾아 내려갈 수는 없다.
Parent 는 부모 타입이고 상위에 부모가 없다. 따라서 childMethod()
를 찾을 수 없으므로 컴파일 오류가 발생한다
2-7. 다형성 - 메서드 오버라이딩
메서드를 오버라이딩하면 부모에서 메서드를 호출하더라도 오버라이딩 된 메서드가 우선권을 가지게 되어서 서브클래스(자식 클래스)의 메서드를 호출한다.
//부모
public class Parent {
public String value = "parent";
public void method() {
System.out.println("Parent.method");
}
}
//자식
public class Child extends Parent {
public String value = "child";
@Override
public void method() {
System.out.println("Child.method");
}
}
//실행 코드
public class OverridingMain {
public static void main(String[] args) {
//자식 변수가 자식 인스턴스 참조
Child child = new Child();
System.out.println("Child -> Child");
System.out.println("value = " + child.value);
child.method();
//부모 변수가 부모 인스턴스 참조
Parent parent = new Parent();
System.out.println("Parent -> Parent");
System.out.println("value = " + parent.value);
parent.method();
//부모 변수가 자식 인스턴스 참조(다형적 참조)
Parent poly = new Child();
System.out.println("Parent -> Child");
System.out.println("value = " + poly.value); //변수는 오버라이딩 X poly.method();
}
}
//실행 결과
Child -> Child
value = child
Child.method
Parent -> Parent
value = parent
Parent.method
Parent -> Child
value = parent
Child.method
마지막에 부모에서 method()
를 호출하면 자식 타입의 method()
가 호출된다.
즉 오버라이딩된 메서드를 호출한다.
(물론 오버라이딩 된 메서드가 있어야한다.)
3. 요약
그냥 내가 모르는 내용을 요약한 것이다.
개인적으로 영한님 자바 강의를 슬쩍 추천한다.
'Language > JAVA' 카테고리의 다른 글
김영한의 실전 자바 - 중급 2편 - Sec 02. 제네릭 - Generic1 (4) | 2025.01.01 |
---|---|
인프런 - 김영한 - 실전 자바 중급 1편 요약 및 정리 (3) | 2024.12.08 |
Java - Scope (2) | 2023.12.11 |
TIL - 자바의정석 1~9장까지 흩어보기 (1) | 2023.08.14 |
자바 시간 관련 클래스는 가급적 LocalXX 타입을 사용하자 (0) | 2023.07.24 |