선장님과 함께하는 자바 스터디입니다.
자바 스터디 Github
github.com/whiteship/live-study
나의 Github
github.com/cmg1411/whiteShip_live_study
- 클래스 정의하는 방법
- 객체 만드는 방법 (new 키워드 이해하기)
- 메소드 정의하는 방법
- 생성자 정의하는 방법
- this 키워드 이해하기
클래스
클래스란 인스턴스를 만들기 위한 틀 같은 존재다. 인스턴스를 만들기 위한 설계도이다.
OOP 에서 객체를 만들어 쓰기 위해 객체를 어떻게 만들 것인지를 정의한다.
생성자, 필드, 메서드를 포함하고 이것들이 객체의 존재를 정의한다.
인스턴스 vs 객체
- 객체 : 클래스라는 틀로 만들 대상.
- 인스턴스 : 소프트웨어 상에서 실제로 메모리에 올려져 만들어진 객체
클래스의 구성요소
- 생성자
- 필드
- 메서드
- 초기화블럭
이번 글에서 하나하나 다뤄본다.
클래스 정의 방법
(클래스 접근지정자) class (클래스이름) {
...
}
public class Car {
}
- 접근 지정자는 public, default(package-private) 만 가능하다.
- implements, extends 키워드를 이용하여 상속, 구현으로 확장할 수 있다.
- abstract 키워드를 class 앞에 붙이면 추상클래스로 활용할 수 있다. (추상 클래스는 인스턴스 생성이 불가)
- final 키워드를 class 앞에 붙이면 상속불가 클래스로 만들 수 있다. (불변 클래스를 만드는 단계중 하나)
객체 생성 방법 (new)
(클래스이름) (객체이름) = new (클래스이름)();
Car car = new Car();
- 클래스로 객체를 생성할 떄에는 new 키워드를 사용한다.
- new 키워드의 역할은 아래와 같다.
- 힙 영역에 데이터를 저장할 공간을 할당받게 한다.
- 할당받은 공간의 참조값을 객체에게 반환한다.
- 생성자를 호출한다.
객체생성 예제
public class ClassExample {
public static void main(String[] args) {
Car car = new Car("Genesis", 10); <-- lineNumber 5
}
}
public class Car {
private String name;
private int position;
public Car(String name, int position) {
this.name = name;
this.position = position;
}
}
간단한 예제 이다.
이 코드의 바이트 코드를 보자.
인텔리J 를 사용한다면 코드를 컴파일한 후 Shift 를 두번 눌러서 show byteCode 를 하면 class 파일을 볼 수 있다.
(컴파일을 하지 않으면 class 파일이 없으니 당연히 볼 수 없다.)
cmd 환경에서는 javap - c 명령을 사용하면 볼 수 있다.
public <init>()V
L0
LINENUMBER 3 L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
RETURN
L1
LOCALVARIABLE this Lcom/whiteship/white_ship_study/classExample/ClassExample; L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1
// access flags 0x9
public static main([Ljava/lang/String;)V
// parameter args
L0
LINENUMBER 5 L0
NEW com/whiteship/white_ship_study/classExample/Car
DUP
LDC "Genesis"
BIPUSH 10
INVOKESPECIAL com/whiteship/white_ship_study/classExample/Car.<init> (Ljava/lang/String;I)V
ASTORE 1
L1
LINENUMBER 6 L1
RETURN
L2
LOCALVARIABLE args [Ljava/lang/String; L0 L2 0
LOCALVARIABLE car Lcom/whiteship/white_ship_study/classExample/Car; L1 L2 1
MAXSTACK = 4
MAXLOCALS = 2
}
LINENUMBER 5 L0 부터가 객체를 생성한다.
new 키워드가 하는 일이라 볼 수 있다.
1. NEW ~
- 새로운 Car 인스턴스가 car 이라는 이름으로 heap 영역에 생성된다.
- stack 영역의 Operation Stack 에 위에서 생성한 인스턴스의 참조자가 쌓인다.
2. DUP
- Operation Stack 에 방금 쌓은 참조자를 복사해서 쌓는다.
- 이후 생성자를 호출했을 때 객체의 참조자가 스택에서 제거되기 때문이다.
3. LDC "Genesis"
- LoaD Constant 의 약자이다.
- 힙 영역의 상수풀에 "Genesis" 를 생성하고 참조값을 가져온다.
- 참조값을 Operation Stack 에 쌓는다.
alkhwa-113.tistory.com/entry/String%EA%B3%BC-StringBuilder 스트링 상수 풀
4. BIPUSH 10
- 생성자 인수로 사용할 10 을 Operation Stack 에 쌓는다.
5, INVOKESPECIAL
- 직역하면 특별한 것을 호출(?) 한다는 것이다.
- 위에서 쌓인 인스턴스의 참조값, String 의 참조값, 10 을 가지고 일을 할 것이다. 무슨 일을 하느냐 하면
- 생성자를 호출한다. 뒤의 <init> 을 보면 알 수 있다.
- 생성자에서 객체 값들을 초기화시킨다.
- 이후 Operation Stack 에서 생성자 호출에 사용한 값들을 pop() 한다.
6. ASTORE 1
- LVA(Local Variable Array) 에 car 라는 이름의 공간을 할당하고,
- Operation Stack 에 마지막으로 남아있던 car 인스턴스의 참조값을 넣는다.
이렇게 되면 객체를 생성하는 것이다.
참고
생성자
- 생성자는 클래스로 인스턴스를 생성할 때 자동으로 실행되어 객체를 초기화한다.
- 초기화를 위해 사용하므로 별도의 리턴타입이 없고, 선언부에 리턴타입을 명시하지 않는다.
- 생성자를 오버로딩하여 시그니처별로 생성할 수 있다.
- 하지만 같은 시그니쳐로 여러 생성자를 생성할 수 는 없다.
- 생성자를 명시적으로 선언하지 않으면 빈 기본생성자를 컴파일할 때 생성한다.
- 생성자도 내부적으로 메서드처럼 동작하지만 메서드는 아니다.
public Car() {
}
public Car(String name, int position) {
this.name = name;
this.position = position;
}
// 같은 시그니쳐는 두개 생성 불가.
// 오류 발생. (정적 팩토리 메서드 방식을 사용하면 해결 가능)
public Car(String name, int price) {
this.name = name;
this.price = price;
}
하나의 팁으로는, private 접근 지정자를 생성자에 적용할 수 있는데,
모든 생성자가 private 이면 외부에서 접근이 불가하므로,
객체생성이 불가하고 상속을 막을 수 있다. (상속 파트에서 봅시다)
필드
- 필드는 클래스 클래스 내부에 선언된 변수 로 이해하면 쉽다.
- 필드는 클래스의 속성을 나타낸다.
- 인스턴스 변수 : 클래스가 만든 인스턴스의 라이프사이클 동안 사용되는 변수
- 클래스 변수 : static 키워드가 붙어있어 정적으로 생성되어 사용되는 변수
public class Car {
private static final NAME = "Sonata"; // 클래스 변수. 상수
private final int price; // 인스턴스 변수
}
메서드
- 클래스에 포함된 함수를 메서드라한다.
- 클래스함수 라고도 한다.
- 메서드는 클래스의 행위(동작)를 정의한다.
(접근지정자) (반환타입) (메서드이름)((매개변수)) {
~구현~
}
public void print(String name) {
System.out.println(name);
}
초기화 블럭
- 클래스 내부에서 초기화 블럭을 이용하여 인스턴스를 생성할 때 초기화를 할 수 있다.
일반 초기화 블럭 (대괄호)
public class Car {
private String name;
{
name = "RR";
}
public Car() {
}
}
일반 초기화 블럭에서 하는 일은 보통 생성자에서도 할 수 있다.
아직까지는 왜 사용하는지 깨닫지 못했다,
정적 초기화 블럭 (static 대괄호)
public class Car {
private String name;
static {
~
}
public Car() {
}
}
자바 어플리케이션을 실행 시 정적으로 수행된다.
정적 블록이므로 객체가 생성되지 않은 시점에서 실행된다.
따라서 인스턴스 변수에 초기화를 시키는 그런 행동은 못한다.
this()
- this() 는 생성자에서 쓰이는 메서드이다.
- this 와는 다르다. this 는 인스턴스 자신을 가리키는 참조 변수이다.
- this() 는 자기자신의 생성자를 뜻한다.
- 매개변수에 따라 같은 시그니쳐의 생성자를 실행한다.
- 생성자들이 많을 때, 중복코드를 줄일 수 있다.
public class Car {
private String name;
private int price;
public Car() {
}
public Car(String name) { // 밑의 this(name) 에 의해 호출된다.
this.name = name;
}
public Car(String name, int price) {
this(name); // 위의 생성자를 호출한다.
this.price = price;
}
}
과제 (Optional)
- int 값을 가지고 있는 이진 트리를 나타내는 Node 라는 클래스를 정의하세요.
- int value, Node left, right를 가지고 있어야 합니다.
- BinrayTree라는 클래스를 정의하고 주어진 노드를 기준으로 출력하는 bfs(Node node)와 dfs(Node node) 메소드를 구현하세요.
- DFS는 왼쪽, 루트, 오른쪽 순으로 순회하세요.
추후 완성예정
참고
'WhiteShip Java Study : 자바 처음부터 멀리까지' 카테고리의 다른 글
디스패치, 다이나믹 디스패치, 더블 디스패치 (2) | 2021.02.01 |
---|---|
상속 (0) | 2021.02.01 |
4주차) 과제 (0) | 2021.01.30 |
제어문 (0) | 2021.01.28 |
연산자 (0) | 2021.01.28 |