객체지향 프로그래밍 1

LeeKyoungChang·2022년 2월 12일
0
post-thumbnail

Java의 정석 의 책을 읽고 정리한 내용입니다.

 

📚 1. 객체지향언어

객체지향언어의 주요 특징
(1) 코드의 재사용성이 높다.

  • 새로운 코드를 작성할 때 기존의 코드를 이용하여 쉽게 작성할 수 있다.

(2) 코드의 관리가 용이하다.

  • 코드간의 관계를 이용해서 적은 노력으로 쉽게 코드를 변경할 수 있다.

(3) 신뢰성이 높은 프로그래밍을 가능하게 한다.

  • 제어자와 메서드를 이용해서 데이터를 보호하고 올바른 값을 유지하도록 하며, 코드의 중복을 제거하여 코드의 불일치로 인한 오동작을 방지할 수 있다.

 

  • 객체지향개념을 학습할 때 재사용성과 유지보수 그리고 중복된 코드의 제거, 이 세 가지 관점에서 보면 보다 쉽게 이해할 수 있을 것이다.
  • 너무 객체지향개념에 얽매여서 고민하기 보다는 일단 프로그램을 기능적으로 완성한 다음 어떻게 하면 보다 객체지향적으로 코드를 개선할 수 있을지를 고민하여 점차 개선해 나가는 것이 좋다.

 

📚 2. 클래스와 객체

📢 클래스?
클래스 : 객체의 설계도 또는 틀
클래스 정의 : 클래스란 객체를 정의해 놓은 것이다.
클래스의 용도 : 클래스는 객체를 생성하는데 사용된다.

 

프로그래밍에서의 객체는 클래스의 정의된 내용대로 메모리에 생성된 것을 뜻한다.

  • 객체의 정의 : 실제로 존재하는 것. 사물 또는 개념
  • 객체의 용도 : 객체가 가지고 있는 기능과 속성에 따라 다르다.
    • 유형의 객체 : 책상, 의자, 자동차, TV와 같은 사물
    • 무형의 객체 : 수학공식, 프로그램 에러와 같은 논리나 개념

 

✔ 클래스와 객체의 예

클래스객체
제품 설계도제품
TV 설계도TV
붕어빵 기계붕어빵

➡ 프로그래밍에서는 먼저 클래스를 작성한 다음, 클래스로부터 객체를 생성하여 사용한다.

 

🔔 참고
JDK(Java Development Kit)에서는 프로그래밍을 위해 많은 수의 유용한 클래스(Java API)를 기본적으로 제공하고 있으며, 우리는 이 클래스들을 이용해서 원하는 기능의 프로그램을 보다 쉽게 작성할 수 있다.

 

📖 A. 객체와 인스턴스

  • 클래스로부터 객체를 만드는 과정을 클래스의 인스턴스화(instantiate)
  • 어떤 클래스로부터 만들어진 객체를 그 클래스의 인스턴스(instance)

ex) '책상은 인스턴스다.'라고 하기 보다는 '책상은 객체다.'라는 쪽이, '책상은 책상 클래스의 객체이다.'라고 하기 보다는 '책상은 책상 클래스의 인스턴스다.'라고 하는 것이 더 자연스럽다.

클래스를 인스턴스로 인스턴스화 시키는 모습
: 클래스 ➡ 인스턴스(객체)

 

📖 B. 객체의 구성요소 - 속성과 기능

객체는 속성과 기능의 집합이다.
객체가 가지고 있는 속성과 기능을 그 객체의 멤버(구성원, member)라 한다.

 

📢 속성과 기능
속성(property) : 멤버변수(member variable), 특성(attribute), 필드(field), 상태(state)

  • ex) 크기, 길이, 높이, 색상, 볼륨, 채널 등

     

기능(function) : 메서드(method), 함수(function), 행위(behavior)

  • ex) 켜기, 끄기, 볼륨 높이기, 볼륨 낮추기, 채널 변경하기 등

 

📖 C. 인스턴스의 생성과 사용

클래스명 변수명;    ➡ 클래스의 객체를 참조하기 위한 참조변수를 선언 
변수명 = new 클래스명(); ➡ 클래스의 객체를 생성 후, 객체의 주소를 참조변수에 저장

 

TvTest.java


class Tv{
    // TV의 속성 (멤버 변수)
    String color;   // 색상
    boolean power;  // 전원상태(on/off)
    int channel;    // 채널
    
    // Tv의 기능 (메서드)
    void power() {power = !power;}   // TV의 켜거나 끄는 기능을 하는 메서드
    void channelUp(){++channel;}    // TV의 채널을 높이는 기능을 하는 메서드
    void channelDown(){--channel;}  // TV의 채널을 낮추는 기능을 하는 메서드
}


class TvTest {

    public static void main(String[] args){
        Tv t;   // Tv 인스턴스를 참조하기 위한 변수 t를 선언 (t:참조변수)
        t = new Tv();   // Tv인스턴스를 생성한다.
        t.channel = 7;  // Tv인스턴스의 멤버변수 channel의 값을 7로 한다.
        t.channelDown();    // Tv인스턴스의 메서드 channelDown()을 호출한다.
        System.out.println("현재 채널은 " + t.channel + " 입니다.");
    }
}

 

  • 인스턴스는 참조변수를 통해서만 다룰 수 있으며, 참조변수의 타입은 인스턴스의 타입과 일치해야 한다.
  • 하나의 인스턴스를 여러 개의 참조변수가 가리키는 경우 가능하다.
  • 여러 인스턴스를 하나의 참조변수가 가리키는 경우는 불가능하다.

 

🔔 참고
자신을 참조하고 있는 참조변수가 하나도 없는 인스턴스는 더 이상 사용되어질 수 없으므로 '가비지 컬렉터(Garbage Collector)'에 의해서 자동적으로 메모리에서 제거된다.

 

📖 D. 객체 배열

  • 객체 역시 배열로 다루는 것이 가능하며, 이를 '객체 배열'이라고 한다.
  • 객체 배열 안에 객체가 저장되는 것은 아니고, 객체의 주소가 저장된다.
  • 객체 배열은 참조변수들을 하나로 묶은 참조 변수 배열인 것이다.

 

Tv[] tvArr = new Tv[3]; // 길이가 3인 Tv타입의 참조변수 배열 (3개의 객체의 주소를 저장할 수 있다.)
  • 객체 배열을 생성하는 것은 그저 객체를 다루기 위한 참조 변수들이 만들어진 것일 뿐, 아직 객체가 저장되지 않았다.
  • 객체를 생성해서 객체 배열의 각 요소에 저장하는 것을 잊으면 안된다!
Tv[] tvArr = new Tv[3];  // 참조변수 배열(객체 배열)생성

// 객체를 생성해서 배열의 각 요소에 저장
tvArr[0] = new Tv();
tvArr[1] = new Tv();
tvArr[2] = new Tv();

 

배열의 초기화 블럭을 사용하면, 다음과 같이 한 줄로 간단히 할 수 있다.

Tv[] tvArr = {new Tv(), new Tv(), new Tv()};

 

다뤄야할 객체의 수가 많을 때는 for문을 사용하면 된다.

Tv[] tvArr = new Tv[100];

for(int i=0; i < tvArr.length; i++){
	tvArr[i] = new Tv();
}

객체 배열도 같은 타입의 객체만 저장할 수 있다.

 

📖 E. 클래스의 또 다른 정의

클래스는 '객체를 생성하기 위한 틀'이며 '클래스는 속성과 기능으로 정의되어 있다.'

 

프로그래밍적인 관점
(1) 클래스 - 데이터와 함수의 결합

  • 변수 : 하나의 데이터를 저장할 수 있는 공간
  • 배열 : 같은 종류의 여러 데이터를 하나의 집합으로 저장할 수 있는 공간
  • 구조체 : 서로 관련된 여러 데이터를 종류에 관계없이 하나의 집합으로 저장할 수 있는 공간
  • 클래스 : 데이터와 함수의 결합(구조체 + 함수)

 

(2) 클래스 - 사용자정의 타입(user-defined type)

  • 사용자정의 타입 : 프로그래머가 서로 관련된 변수들을 묶어서 하나의 타입으로 새로 추가하는 것
  • 자바와 같은 객체지향언어에서는 클래스가 사용자 정의 타입이다.

 

비객체지향적 코드객체지향적 코드
int hou1, hour2, hour3Time t1 = new Time();
int[] hour = new int[3];Time[] t = new Time[3]; t[0] = new Time();

 

객체지향언어에서는 제어자와 메서드를 이용할시 여러가지 조건들을 손 쉽게 반영할 수 있다.

 

public class Time {
    private int hour;
    private int minute;
    private float second;

    public int getHour(){return hour;}
    public int getMinute(){return minute;}
    public float getSecond(){return second;}

    public void setHour(int h){
        if (h < 0 || h > 23) return;
        hour = h;
    }

    public void setMinute(int m){
        if (m < 0 || m > 59) return;
        minute = m;
    }

    public void setSecond(float s){
        if (s < 0.0f || s > 59.99f) return;
        second = s;
    }
}
  • 제어자를 이용해서 변수의 값을 직접 변경하지 못하고 대신 메서드를 통해서 값을 변경하도록 작성하였다.
  • 값을 변경할 때 지정된 값의 유효성을 조건문으로 점검한 다음에 유효한 값일 경우에만 변경한다.

 

📚 3. 변수와 메서드

📖 A. 선언위치에 따른 변수의 종류

  • 멤버변수를 제외한 나머지 변수들은 모두 지역변수
  • 멤버변수 중 static이 붙은 것은 클래스 변수
  • 멤버변수 중 static이 붙지 않은 것은 인스턴스변수
public class Variables {
    // 밖은 클래스영역
    int iv;  // 인스턴스 변수
    static int cv; // 클래스변수 (static변수, 공유변수)

    void method() {
        // 안은 메서드영역
        int lv = 0; // 지역변수
    }
}
변수의 종류선언위치생성시기
클래스변수(class variable)클래스 영역클래스가 메모리에 올라갈 때
인스턴스변수(instance variable)클래스 영역인스턴스가 생성되었을 때
지역변수(local variable)클래스 영역 이외의 영역(메서드, 생성자, 초기화 블럭 내부)변수 선언문이 수행되었을 때

 

1. 인스턴스변수(instance variable)

  • 클래스 영역에 선언되며, 클래스의 인스턴스를 생성할 때 만들어진다.
  • 인스턴스 변수의 값을 읽어 오거나 저장하기 위해서는 먼저 인스턴스를 생성해야한다.
  • 인스턴스는 독립적인 저장공간을 가지므로 서로 다른 값을 가질 수 있다.
  • 인스턴스마다 고유한 상태를 유지해야하는 속성의 경우 사용한다.

2. 클래스변수(class variable)

  • 클래스 변수를 선언하는 방법은 인스턴스변수 앞에 static을 붙이기만 하면 된다.
  • 클래스변수는 모든 인스턴스가 공통된 저장공간(변수)을 공유하게 된다.
  • 한 클래스의 모든 인스턴스들이 공통적인 값을 유지해야하는 속성의 경우, 클래스변수로 선언해야 한다.
  • 클래스가 메모리에 '로딩(loading)'될 때 생성되어 프로그램이 종료될 때까지 유지되며, public을 앞에 붙이면 같은 프로그램 내에서 어디서나 접근할 수 있는 전역변수(global variable)의 성격을 갖는다.

3. 지역변수(local variable)

  • 메서드 내에 선언되어 메서드 내에서만 사용 가능하며, 메서드가 종료되면 소멸되어 사용할 수 없게 된다.

 

📖 B. 클래스변수와 인스턴스변수

카드 속성 : 무늬, 숫자, 폭, 높이
각 Card인스턴스는 자신만의 무늬와 숫자를 유지하고 있어야 하므로 이들을 인스턴스변수로 선언
각 Card의 폭과 높이는 모든 인스턴스가 공통적으로 같은 값을 유지해야하므로 클래스변수로 선언

  • 클래스변수를 사용할 때는 클래스이름.클래스변수의 형태로 하는 것이 좋다.

 

📢 그래서?
인스턴스변수는 인스턴스가 생성될 때 마다 생성되므로 인스턴스마다 각기 다른 값을 유지할 수 있지만, 클래스 변수는 모든 인스턴스가 하나의 저장공간을 공유하므로, 항상 공통된 값을 갖는다.

 

📖 C. 메서드

  • 메서드(method)는 특정 작업을 수행하는 일련의 문장들을 하나로 묶은 것이다.

 

🔔 메서드를 사용하는 이유
1. 높은 재사용성(reusability)

  • Java API에서 제공하는 메서드들을 사용하면서 경험한 것처럼 한번 만들어 놓은 메서드는 몇 번이고 호출할 수 있으며, 다른 프로그램에서도 사용이 가능하다.

2. 중복된 코드의 제거
3. 프로그램의 구조화

  • 큰 규모의 프로그램에서는 문장들을 작업단위로 나눠서 여러 개의 메서드에 담아 프로그램의 구조를 단순화시키는 것이 필수적이다.

 

📖 D. 메서드의 선언과 구현

선언부: 반환타입 메서드이름 (타입 변수명, 타입 변수명, ...)
구현부: { //메서드 호출시 수행될 코드 }

 ;

✔ 메서드 선언부(method declaration, method header)

  • 메서드 선언부는 '메서드의 이름'과 '매개변수 선언', 그리고 '반환타입'으로 구성되어 있으며, 메서드가 작업을 수행하기 위해 어떤 값들을 필요로 하고 작업의 결과로 어떤 타입의 값을 반환하는지에 대한 정보를 제공한다.

 

매개변수 선언(parameter declaration)

  • 매개변수는 메서드가 작업을 수행하는데 필요한 값들(입력)을 제공받기 위한 것이며, 필요한 값의 개수만큼 변수를 선언하며 각 변수 간의 구분은 쉼표','를 사용한다.

 

✔ 메서드의 구현부(method body, 메서드 몸통)

  • 메서드를 호출했을 때 수행될 문장들을 넣는다.

 

📖 E. 메서드의 호출

✔ 인자(argument)와 매개변수(parameter)

  • 메서드를 호출할 때 괄호()안에 지정해준 값들을 '인자(argument)' 또는 '인수'라고 한다.
  • 인자의 개수와 순서는 호출된 메서드에 선언된 매개변수와 일치해야 한다.
  • 인자의 타입은 매개변수의 타입과 일치하거나 자동 형변환이 가능한 것이어야 한다.

 

✔ 메서드의 실행흐름

  • static메서드는 같은 클래스 내의 인스턴스 메서드를 호출할 수 없다.

 

✔ 매개변수의 유효성 검사

  • 메서드의 구현부{}를 작성할 때, 제일 먼저 해야 하는 일이 매개변수의 값이 적절한 것인지 확인하는 것이다.
  • 가능한 모든 경우의 수에 대해 고민하고 그에 대비한 코드를 작성해야한다.
  • 메서드를 작성할 때는 매개변수의 유효성 검사하는 코드를 반드시 넣어야 한다.

 

📖 F. JVM의 메모리 구조

123

cv : 클래스변수
lv : 지역변수
iv : 인스턴스변수

 

1. 메서드 영역(method area)

  • 프로그램 실행 중 어떤 클래스가 사용되면, JVM은 해당 클래스의 클래스파일(*.class)을 읽어서 분석하여 클래스에 대한 정보(클래스 데이터)를 이곳에 저장한다.
  • 이 때, 그 클래스의 클래스변수(class variable)도 이 영역에 함께 생성된다.

2. 힙(heap)

  • 인스턴스가 생성되는 공간. 프로그램 실행 중 생성되는 인스턴스는 모두 이곳에 생성된다.
  • 인스턴스변수(instance variable)들이 생성되는 공간이다.

3. 호출스택(call stack 또는 execution stack)

  • 호출스택은 메서드의 작업에 필요한 메모리 공간을 제공한다.
  • 메서드가 호출되면, 호출스택에 호출된 메서드를 위한 메모리가 할당되며, 이 메모리는 메서드가 작업을 수행하는 동안 지역변수(매개변수 포함)들과 연산의 중간결과 등을 저장하는데 사용된다.
  • 메서드가 작업을 마치면 할당되었던 메모리공간은 반환되어 비워진다.

 

호출스택의 특징

  • 메서드가 호출되면 수행에 필요한 만큼의 메모리를 스택에 할당받는다.
  • 메서드가 수행을 마치고나면 사용했던 메모리를 반환하고 스택에서 제거된다.
  • 호출스택의 제일 위에 있는 메서드가 현재 실행 중인 메서드이다.
  • 아래에 있는 메서드가 바로 위의 메서드를 호출한 메서드이다.
class CallStackTest{
    public static void main(String[] args) {
        firstMethod(); // static메서드는 객체 생성없이 호출가능하다.
    }

    static void firstMethod() {
        secondMethod();
    }

    static void secondMethod() {
        System.out.println("secondMethod()");
    }
}
  • 객체를 생성하지 않고도 메서드를 호출할 수 있으려면, 메서드 앞에 static을 붙여야 한다.

1234

 

📖 G. 기본형 매개변수와 참조형 매개변수

  • 자바에서는 메서드를 호출할 때 매개변수로 지정한 값을 메서드의 매개변수에 복사해서 넘겨준다.
  • 매개변수의 타입이 기본형(primitive type)일 때는 기본형 값이 복사되겠지만, 참조형(reference type)이면 인스턴스의 주소가 복사된다.

 

기본형 매개변수

  • 변수의 값을 읽기만 할 수 있다.
class Data {int x;}

class PrimitiveParamEx{
    public static void main(String[] args) {
        Data d = new Data();
        d.x = 10;
        System.out.println("main(): x = "+d.x);

        change(d.x);
        System.out.println("After change (d.x)");
        System.out.println("main(): x = "+d.x);
    }

    static void change(int x) {  // 기본형 매개변수
        x = 1000;
        System.out.println("change() : x = " + x);
    }
}

3
(1) change메서드가 호출되면서 'd.x'가 change메서드의 매개변수 x에 복사된다.
(2) change메서드에서 x의 값을 1000으로 변경
(3) change메서드가 종료되면서 매개변수 x는 스택에서 제거된다.

 

참조형 매개변수

  • 변수의 값을 읽고 변경할 수 있다.
class Data {int x;}

class ReferenceParamEx{
    public static void main(String[] args) {
        Data d = new Data();
        d.x = 10;
        System.out.println("main(): x = "+d.x);

        change(d);
        System.out.println("After change (d.x)");
        System.out.println("main(): x = "+d.x);
    }

    static void change(Data d) {  // 참조형 매개변수
        d.x = 1000;
        System.out.println("change() : x = " + d.x);
    }
}

2

(1) change메서드가 호출되면서 참조변수 d의 값(주소)이 매개변수 d에 복사된다. 이제 매개변수 d에 저장된 주소값으로 x에 접근이 가능하다.
(2) change메서드에서 매개변수 d로 x의 값을 1000으로 변경
(3) change메서드가 종료되면서 매개변수 d는 스택에서 제거된다.

 

배열일 때도 객체와 같이 참조변수를 통해 데이터가 저장된 공간에 접근한다. (참조형 매개변수 결과와 유사하다.)

class RunTest{
    public static void main(String[] args) {
        RunTest r = new RunTest();

        int[] result = {0,0};

        r.change(3, result);

        System.out.println(result[0]);
    }

    void change(int x, int[] result) { 
        result[0] = x;
    }
}

// 결과 : 3
  • 매개변수로 넘겨받은 배열에 값을 저장한다.

 

📖 H. 참조형 반환타입

class Data {int x;}

class ReferenceParamEx{
    public static void main(String[] args) {
        Data d = new Data();
        d.x = 10;

        Data d2 = copy(d);
        System.out.println("d.x =" + d.x);
        System.out.println("d2.x="+d2.x);
    }

    static Data copy(Data d) {  // 기본형 매개변수
        Data tmp = new Data();	// 새로운 객체 tmp를 생성한다.
        tmp.x = d.x;			// d.x의 값을 tmp.x에 복사한다.

        return tmp;				// 복사한 객체의 주소를 반환한다.
    }
}
  • copy 메서드 내에서 생성한 객체를 main메서드에서 사용할 수 있으려면, 새로운 객체의 주소를 반환해줘야 한다.

1

(1) copy메서드를 호출하면서 참조변수 d의 값이 매개변수 d에 복사된다.
(2) 새로운 객체를 생성한 다음, d.x에 저장된 값을 tmp.x에 복사한다.
(3) copy메서드가 종료되면서 반환한 tmp의 값은 참조변수 d2에 저장된다.
(4) copy메서드가 종료되어 tmp가 사라졌지만, d2로 새로운 객체를 다룰 수 있다.

반환타입이 '참조형'이라는 것은 메서드가 '객체의 주소'를 반환한다는 것을 의미한다.

 

📖 I. 재귀호출(recursive call)

메서드의 내부에서 메서드 자신을 다시 호출하는 것을 '재귀호출(recursive calss)'이라 하고, 재귀호출을 하는 메서드를 '재귀 메서드'라 한다.

 

🔔 반복문 vs 재귀호출

  • 반복문은 그저 같은 문장을 반복해서 수행하는 것이다.
  • 재귀함수는 매개변수 복사와 종료 후 복귀할 주소저장 등, 추가로 필요하기 때문에 반복문보다 수행시간이 더 오래 걸린다.

 

✔ 재귀호출을 사용하는 이유

  • 재귀호출을 사용시 논리적 간결하다.

 

✔ 재귀호출은 언제 사용해야할까?

  • 어떤 작업을 반복적으로 처리해야한다면, 먼저 반복문을 작성해보고 너무 복잡하면 재귀호출로 간단히 할 수 없는지 고민해볼 필요가 있다.
  • 재귀호출을 비효율적이므로 재귀호출에 드는 비용보다 재귀호출의 간결함이 주는 이득이 충분히 큰 경우에만 사용해야 한다!

 

🔔 참고

  • static 메서드를 호출할 때 클래스이름을 생략하는 것이 가능하다.
  • 스택의 저장한계를 넘어섰을 때, 스택오버플로우 에러(Stack Overflow Error)가 발생한다.

 

📖 J. 클래스 메서드(static메서드)와 인스턴스 메서드

✔ 클래스 메서드

  • 메서드 앞에 static이 붙어 있으면 클래스메서드
  • 객체를 생성하지 않고도 클래스이름.메서드이름(매개변수)와 같은 식으로 호출이 가능하다.
  • 인스턴스와 관계없는(인스턴스 변수나 인스턴스 메서드를 사용하지 않는) 메서드를 클래스 메서드(static 메서드)로 정의한다.

 

✔ 인스턴스 메서드

  • 메서드 앞에 static이 붙어 있지 않으면 인스턴스 메서드
  • 반드시 객체를 생성해야만 호출할 수 있다.
  • 인스턴스 메서드는 인스턴스 변수와 관련된 작업을 하는, 즉 메서드의 작업을 수행하는데 인스턴스 변수를 필요로 하는 메서드이다.

 

🔔 참고

  • 클래스 영역에 선언된 변수를 멤버 변수
  • 멤버변수 중에 static이 붙은 것은 클래스변수(static변수)
  • 멤버변수 중에 static이 붙지않은 것을 인스턴스변수

멤버변수는 인스턴스변수와 static변수를 모두 통칭하는 말이다.

 

📢 static 붙여야하는 시기
1. 클래스를 설계할 때, 멤버변수 중 모든 인스턴스에 공통으로 사용하는 것에 static을 붙이다.

  • 생성된 각 인스턴스는 서로 독립적이기 때문에 각 인스턴스의 변수(iv)는 서로 다른 값을 유지한다.
  • 그러나 모든 인스턴스에서 같은 값이 유지되어야 하는 변수는 static을 붙여서 클래스 변수로 정의해야 한다.

2. 클래스 변수(static변수)는 인스턴스를 생성하지 않아도 사용할 수 있다.

  • static이 붙은 변수(클래스변수)는 클래스가 메모리에 올라갈 때 이미 자동적으로 생성되어 있기 때문이다.

3. 클래스 메서드(static메서드)는 인스턴스 변수를 사용할 수 없다.

  • 클래스 메서드(static이 붙은 메서드)는 인스턴스 생성 없이 호출가능하므로 클래스 메서드가 호출되었을 때 인스턴스가 존재하지 않을 수도 있다.
  • 그래서 클래스 메서드에서 인스턴스변수의 사용을 금지한다.
  • 반면에 인스턴스 변수나 인스턴스 메서드에서는 static이 붙은 멤버들을 사용하는 것이 언제나 가능하다.
  • 인스턴스 변수가 존재한다는 것은 static변수가 이미 메모리에 존재한다는 것을 의미하기 때문이다.

4. 메서드 내에서 인스턴스 변수를 사용하지 않는다면, static을 붙이는 것을 고려한다.

  • 인스턴스변수를 필요로 한다면, static을 붙일 수 없다. 인스턴스변수를 필요로 하지 않는다면 static을 붙이자. 메서드 호출시간이 짧아지므로 성능이 향상된다.
  • static을 안 붙인 메서드(인스턴스메서드)는 실행 시 호출되어야할 메서드를 찾는 과정이 추가적으로 필요하기 때문에 시간이 더 걸린다.

 

📢 정리

  • 클래스의 멤버변수 중 모든 인스턴스에 공통된 값을 유지해야하는 것이 있는지 살펴보고 있으면, static을 붙여준다.
  • 작성한 메서드 중에서 인스턴스 변수나 인스턴스 메서드를 사용하지 않는 메서드에 static을 붙일 것을 고려한다.

 

🔔 참고

  • random()과 같은 Math클래스의 메서드는 모두 클래스 메서드이다.

 

class MyMath2 {
    long a, b;

    // 인스턴스변수 a, b만을 이용해서 작업하므로 매개변수가 필요없다.
    long add()		{ return a + b;	}  // a, b는 인스턴스변수
    long subtract()	{ return a - b;	}
    long multiply()	{ return a * b;	}
    double divide()	{ return a / b;	}

    // 인스턴스변수와 관계없이 매개변수만으로 작업이 가능하다.
    static long		add(long a, long b)			{ return a + b;	}  // a, b는 지역변수
    static long		subtract(long a, long b)	{ return a - b;	}
    static long		multiply(long a, long b)	{ return a * b;	}
    static double	divide(double a, double b)	{ return a / b;	}
}

class MyMathTest2 {
    public static void main(String[] args) {
        // 클래스메서드 호출. 인스턴스 생성없이 호출가능
        System.out.println(MyMath2.add(200L, 100L));
        System.out.println(MyMath2.subtract(200L, 100L));
        System.out.println(MyMath2.multiply(200L, 100L));
        System.out.println(MyMath2.divide(200.0, 100.0));

        MyMath2 mm = new MyMath2(); // 인스턴스를 생성
        mm.a = 200L;
        mm.b = 100L;

        // 인스턴스메서드는 객체생성 후에만 호출이 가능하다.
        System.out.println(mm.add());
        System.out.println(mm.subtract());
        System.out.println(mm.multiply());
        System.out.println(mm.divide());
    }
}

300
100
20000
2.0
300
100
20000
2.0
  • 인스턴스메서드인 add(), subtract(), multiply(), divide()는 인스턴스 변수인 ab만으로도 충분히 작업이 가능하기 때문에, 매개변수를 필요하지 않으므로 괄호()에 매개변수를 선언하지 않았다.
  • 반면에 add(long a, long b), subtract(long a, long b)등은 인스턴스변수 없이 매개변수만으로 작업을 수행하기 때문에 static을 붙여서 클래스메서드로 선언하였다.
  • 그래서 MyMath2main메서드에서 보면, 클래스메서드는 객체 생성 없이 바로 호출이 가능했고, 인스턴스메서드는 MyMath클래스의 인스턴스를 생성한 후에야 호출이 가능했다.

 

📖 K. 클래스 멤버와 인스턴스 멤버간의 참조와 호출

  • 같은 클래스에 속한 멤버들 간에는 별도의 인스턴스를 생성하지 않고도 서로 참조 또는 호출이 가능하다.
    • 단, 클래스 멤버가 인스턴스 멤버를 참조 또는 호출하고자 하는 경우에는 인스턴스를 생성해야 한다.
  • 이유 : 인스턴스 멤버가 존재하는 시점에 클래스 멤버는 항상 존재하지만, 클래스 멤버가 존재하는 시점에 인스턴스 멤버가 존재하지 않을 수도 있기 때문이다.

&nbps;

🔔 참고
인스턴스 멤버란 인스턴스 변수와 인스턴스 메서드를 의미한다.

  • static메서드는 인스턴스 메서드를 호출할 수 없다.
  • 인스턴스메서드는 인스턴스변수를 사용할 수 있지만, static메서드는 인스턴스변수를 사용할 수 없다.

 

class MemberCall {

    int iv = 10;
    static int cv = 20;

    int iv2 = iv;
//	static int cv2 = iv; // 에러! 클래스변수는 인스턴스 변수를 사용할 수 없다.
    static int cv2 = new MemberCall().iv;  // 이와 같이 객체를 생성해야 사용가능하다.



    static void staticMethod1() {

        System.out.println(cv);
//		System.out.println(iv);  에러 - 클래스메소드에서 인스턴스변수를 사용 불가.
        MemberCall c = new MemberCall();
        System.out.println(c.iv); // 객체를 생성한 후에야 인스턴스변수의 참조가능.

    }

    void instanceMethod1() {
        System.out.println(cv); // 클래스변수는 어디서든 사용 가능.
        System.out.println(iv); // 인스턴스 변수는 인스턴스 메소드에서 사용 가능
    }

    static void staticMethod2() {
        staticMethod1();
//		instanceMethod1();    // 에러. 클래스메서드에서는 인스턴스메서드를 호출할 수 없다.
        MemberCall c = new MemberCall();
        c.instanceMethod1();  // 인스턴스를 생성한 후에야 호출할 수 있다.
    }

    void instanceMethod2() {    // 인스턴스메서드에서는 인스턴스메서드와 클래스메서드
        staticMethod1();        // 모두 인스턴스 생성없이 바로 호출이 가능하다.
        instanceMethod1();
    }

}
  • 클래스멤버(클래스변수와 클래스메서드)는 언제나 참조 또는 호출이 가능하기 때문에 인스턴스멤버가 클래스멤버를 사용하는 것은 아무런 문제가 안된다.
  • 그러나, 인스턴스멤버(인스턴스변수와 인스턴스 메서드)는 반드시 객체를 생성한 후에만 참조 또는 호출이 가능하기 때문에 클래스멤버가 인스턴스멤버를 참조, 호출하기 위해서는 객체를 생성하여야 한다.
  • 하지만, 인스턴스멤버간의 호출에는 아무런 문제가 없다. 하나의 인스턴스 멤버가 존재한다는 것은 인스턴스가 이미 생성되어있다는 것을 의미하며, 즉 다른 인스턴스 멤버들도 모두 존재하기 때문이다.

 

🔔 참고

  • 수학에서의 대입법처럼, c = new MemberCall()이므로 c.instanceMethod1();에서 c대신 new MemberCall()을 대입하여 사용할 수 있다.
    int result = new MemberCall().instanceMetohd1();
  • 이렇게 사용시, 참조변수를 선언하지 않았기 때문에 생성된 MemberCall인스턴스는 더 이상 사용할 수 없다.

 

📚 4. 오버로딩(overloading)

한 클래스 내에서 같은 이름의 메서드를 여러 개 정의하는 것을 메서드 오버로딩(method overloading)또는 간단히 오버로딩(overloading)이라 한다.

 

📌 오버로딩의 조건
1. 메서드 이름이 같아야 한다.
2. 매개변수의 개수 또는 타입이 달라야 한다.

➡️ 오버로딩된 메서드들은 매개변수에 의해서만 구별될 수 있으므로 반환 타입은 오버로딩을 구현하는데 아무런 영향을 주지 못한다.

 

✔️ 오버로딩 예시

	long add(int a, long b) { return a+b; }
	long add(long a, int b)	{ return a+b; }
  • 두 메서드 모두 int형과 long형 매개변수가 하나씩 선언되어 있지만, 서로 순서가 다른 경우이다.
  • 이 경우에 호출 시 매개변수의 값에 의해 호출될 메서드가 구분될 수 있으므로 중복된 메서드 정의가 아닌, 오버로딩으로 간주한다.

 

✏️ 그래서?

  • 같은 일을 하지만 매개변수를 달리해야하는 경우에, 이와 같이 이름은 같고 매개변수를 다르게 하여 오버로딩을 구현한다.

 

✔️ 가변인자(varargs)와 오버로딩

  • 메서드의 매개변수의 개수를 동적으로 지정하는것을 가변인자(variable arguments)라고한다.
  • 가변인자는 타입... 변수명 과 같은 형식으로 선언한다.
  • 가변인자를 매개변수 중에서 제일 마지막에 선언해야 한다.
public PrintStream printf(String format, Object... args) {...}

 

가변인자를 사용하면 메서드 하나로 간단히 대체할 수 있다.

String concatenate(String s1, String s2){...}
String concatenate(String s1, String s2, String s3){...}
String concatenate(String s1, String s2, String s3, String s4){...}

➡️

String concatenate(String... str){...}
  • 인자의 개수를 가변적으로 할 수 있다.
  • 인자가 아예 없어도 되고 배열도 인자가 될 수 있다.
  • 가변인자는 내부적으로 배열을 이용하는 것이다.
  • 가변인자가 선언된 메서드를 호출할 때 마다 배열이 새로 생성된다.
  • 꼭 필요한 경우에만 가변인자를 사용하자!

 

public class VarArgsEx {
	public static void main(String[] args) {
		String[] strArr = { "100", "200", "300"	};
		
		System.out.println(concatenate("", "100", "200", "300"));
		System.out.println(concatenate("-", strArr));
		System.out.println(concatenate(",", new String[] {"1", "2", "3"}));
		System.out.println("["+concatenate(",", new String[0])+"]");
		System.out.println("["+concatenate(",")+"]");
	}
	
	static	String concatenate(String delim, String... args) {
		String result = "";
		
		for(String str : args) {
			result += str + delim;
		}
		
		return result;
	}
	
//	
//	static String concatenate(String... args) {
//		return concatenate("", args);
//	}
	
}
100200300
100-200-300-
1,2,3,
[]
[]
  • concatenate메서드는 매개변수로 입력된 문자열에 구분자를 사이에 포함시켜 결합해서 반환한다.
System.out.println(concatenate("-", new String[]{"100", "200", "300"}));  // 성공
System.out.println(concatenate("-", {"100", "200", "300"}));  // 오류 발생

 

	static	String concatenate(String delim, String... args) {
		String result = "";
		
		for(String str : args) {
			result += str + delim;
		}
		
		return result;
	}


	static String concatenate(String... args) {
			return concatenate("", args);
	}

위 두 메서드를 있는 상태에서 System.out.println(concatenate("-", "100", "200", "300"));를 실행시 오류가 발생한다.

  • 두 오버로딩된 메서드가 구분되지 않아서 발생하는 것이다.
  • 가변인자를 선언한 메서드를 오버로딩하면, 메서드를 호출했을 때 이와 같이 구별 되지 못하는 경우가 발생하기 쉽기 때문에 주의해야 한다.
  • 가능하면 가변인자를 사용한 메서드는 오버로딩하지 않는 것이 좋다.

 

📚 5. 생성자(Constructor)

  • 생성자 : 인스턴스가 생성될 때 호출되는 인스턴스 초기화 메서드이다.
  • 인스턴스 변수의 초기화 작업에 주로 사용되며, 인스턴스 생성 시에 실행되어야 할 작업을 위해서도 사용된다.
  • 인스턴스 초기화 : 인스턴스 변수들을 초기화하는 것을 뜻한다

 

📌 생성자의 조건
1. 생성자의 이름은 클래스의 이름과 같아야 한다.
2. 생성자는 리턴값이 없다.

 

생성자도 오버로딩이 가능하므로 하나의 클래스에 여러 생성자가 존재할 수 있다.

클래스이름(타입 변수명, 타입 변수명, ...){
	// 인스턴스 생성 시 수행될 코드,
	// 주로 인스턴스 변수의 초기화 코드를 적는다.
}
class Card{
	Card(){  // 매개변수가 없는 생성자
		...
	}
	
	Card(String k, int num){  // 매개변수가 있는 생성자
		...
	}
	...
}
  • 연산자 new가 인스턴스를 생성하는 것이지 생성자가 인스턴스를 생성하는 것이 아니다.
  • 생성자는 단순히 인스턴스변수들의 초기화에 사용되는 조금 특별한 메서드일 뿐이다.

 

🔔 수행되는 과정
Card c = new Card();
1. 연산자 new에 의해서 메모리(heap)에 Card클래스의 인스턴스가 생성된다.
2. 생성자 Card()가 호출되어 수행된다.
3. 연산자 new의 결과로, 생성된 Card인스턴스의 주소가 반환되어 참조변수 c에 저장된다.

➡️ 인스턴스를 생성할 때는 반드시 클래스 내에 정의된 생성자 중의 하나를 선택하여 지정해주어야 한다.

 

✔️ 기본 생성자(default constructor)

  • 모든 클래스에는 반드시 하나 이상의 생성자가 정의되어 있어야 한다.
  • 클래스에 생성자를 정의하지 않고도 인스턴스를 생성할 수 있었던 이유는 컴파일러가 제공하는 '기본 생성자(default constructor)' 덕분이었다.
  • 컴파일할 때, 소스파일(*. java)의 클래스에 생성자가 하나도 정의되지 않은 경우 컴파일러는 자동적으로 아래와 같은 내용의 기본 생성자를 추가하여 컴파일한다.
클래스이름() { }
Card() { }

 

class Data1 {
	int value;
}
class Data2 {
	int value;
	
	Data2(int x) {
		value = x;
	}
}
public class ConstructorTest {
	public static void main(String[] args) {
		Data1 d1 = new Data1();
		Data2 d2 = new Data2();   // compile error
	}
}
  • 에러 메시지가 나타난다.
  • 이것은 Data2에서 Data2()라는 생성자를 찾을 수 없다는 내용의 에러 메시지인데, Data2에 생성자 Data2()가 정의되어 있지 않기 때문에 에러가 발생한 것이다.
  • Data1에는 정의되어 있는 생성자가 하나도 없으므로 컴파일러가 기본 생성자를 추가해주었지만, Data2에는 이미 생성자 Data(int x)가 정의되어 있으므로 기본 생성자가 추가되지 않았기 때문이다.

➡️ 기본 생성자가 컴파일러에 의해 추가되는 경우는 클래스에 정의된 생성자가 하나도 없을 때 뿐이다.

 

✔️ 생성자에서 다른 생성자 호출하기 - this(), this

🔔 생성자에서 다른 생성자 호출

  • 생성자의 이름으로 클래스이름 대신 this를 사용한다.
  • 한 생성자에서 다른 생성자를 호출할 때는 반드시 첫 줄에서만 호출이 가능하다.

 

Car(String color){
	door = 5;  // 첫 번째 줄
	Car(color, "auto", 4);  
	// 에러1. 생성자의 두 번째 줄에서 다른 생성자 호출
	// 에러2. this(color, "auto", 4);로 해야한다.
}
  • 생성자 내에서 다른 생성자를 호출할 때는 클래스이름 대신에 this를 사용해야 한다.

 

Car(Stromg c, String g, int d) {
	color = c;
	gearType = g;
	door = d;
}
Car(String color, String gearType, int door) {
	this.color = color;
	this.gearType = gearType;
	this.door = door;
}
  • 생성자의 매개변수로 선언된 변수의 이름이 color로 인스턴스변수 color와 같을 경우에는 이름만으로는 두 변수가 서로 구별이 안 된다. 이런 경우에는 인스턴스변수 앞에 this를 사용하면 된다.
  • this.colorcolor는 지역변수로 서로 구별이 가능하다.
  • color = color로 사용시 둘 다 지역변수로 간주된다.
  • this는 참조변수로 인스턴스 자신을 가리킨다.
  • this를 사용할 수 있는 것은 인스턴스 멤버뿐이다. static메서드(클래스 메서드)에서는 this를 사용할 수 없다.

 

🔔 this, this()?

  • this : 인스턴스 자신을 가리키는 참조변수, 인스턴스의 주소가 저장되어 있다. 모든 인스턴스메서드에 지역변수로 숨겨진 채로 존재한다.
  • this(), this(매개변수) : 생성자, 같은 클래스의 다른 생성자를 호출할 때 사용한다.

thisthis()는 비슷하게 생겼을 뿐 완전히 다른 것이다. this는 참조 변수고 this()는 생성자다.

 

✔️ 생성자를 이용한 인스턴스의 복사

Car(Car c){
	color = c.color;
	gearType = c.gearType;
	door = c.door;
}
  • Car클래스의 참조변수를 매개변수로 선언한 생성자이다.
  • 매개변수로 넘겨진 참조변수가 가리키는 Car인 인스턴스의 인스턴스변수인 color, gearType, door의 값을 인스턴스 자신으로 복사하는 것이다.

 

💡 참고

  • Object클래스에 정의된 clone메서드를 이용하면 간단히 인스턴스를 복사할 수 있다.

 

class Car{
	~
}

class CarTest3{
	public static void main(String[] args){
		Car c1 = new Car();
		Car c2 = new Car(c1); // c1의 복사본 c2를 생성
	}
}
  • 인스턴스 c2는 c1을 복사하여 생성된 것이므로 서로 같은 상태를 갖지만, 서로 독립적으로 메모리공간에 존재하는 별도의 인스턴스이므로 c1의 값들이 변경되어도 c2는 영향을 받지 않는다.
  • 무작정 새로 코드를 작성하는 것보다 기존의 코드를 활용할 수 없는지 고민해봐야한다.
Car(Car c){
	color = c.color;
	gearType = c.gearType;
	door = c.door;
}
Car(Car c){
	// Car(String color, String gearType, int door)
	this(c.color, c.gearType, c.door);
}

 

📌 정리
인스턴스를 생성할 때는 다음의 2가지 사항을 결정해야 한다.
1. 클래스 - 어떤 클래스의 인스턴스를 생성할 것인가?
2. 생성자 - 선택한 클래스의 어떤 생성자로 인스턴스를 생성할 것인가?

 

📚 6. 변수의 초기화

변수의 초기화 : 변수를 선언하고 처음으로 값을 저장하는 것

  • 가능하면 선언과 동시에 적절한 값으로 초기화 하는것이 바람직하다.
  • 지역변수는 사용하기 전에 반드시 초기화해야 한다.
class InitTest {
	int x;	//인스턴스변수
	int y = x;	//인스턴스변수

void method1() {
		int i;		//지역변수
		int j =i;	//에러 지역변수를 초기화하지 않고 사용
	}
}

✏️ 변수의 초기화?
멤버변수(클래스변수와 인스턴스변수)와 배열의 초기화는 선택적이지만, 지역변수의 초기화는 필수적이다.

 

✔️ 각 타입의 기본값(default value)

자료형기본값
booleanfalse
char'\u0000'
byte, short, int0
long0L
float0.0f
double0.0d 또는 0.0
참조형변수null

 

✔️ 변수의 초기화 예

선언예설 명
int i=10;int형 변수 i를 선언하고 10으로 초기화 된다.
int j=10;int형 변수 j를 선언하고 10으로 초기화 된다.
int i=10, j=10;같은 타입의 변수는 콤마(,)를 사용해서 함께 선언하거나 초기화 할 수 있다.
int i=10, long j=0;에러. 타입이 다른 변수는 함께 선언하거나 초기화할 수 없다.
int i=10;   int j=i;변수 i에 저장된 값으로 변수 j를 초기화 한다.   변수 j는 i의 값인 10으로 초기화 된다.
int j=i; int i=10;에러. 변수 i가 선언되기 전에 i를 사용할 수 없다.

 

✏️ 멤버변수의 초기화 방법
1. 명시적 초기화(explicit initialization)
2. 생성자(constructor)
3. 초기화 블럭(initialization block)

  • 인스턴스 초기화 블럭 : 인스턴스변수를 초기화 하는데 사용.
  • 클래스 초기화 블럭 : 클래스변수를 초기화 하는데 사용.

 

✔️ 명시적 초기화(explicit initialization)

  • 변수를 선언과 동시에 초기화하는 것을 명시적 초기화라고 한다.
  • 가장 기본적이면서도 간단한 초기화 방법이므로 여러 초기화 방법 중에서 가장 우선적으로 고려되어야 한다.
  • 복잡한 초기화 작업이 필요할 때는 '초기화 블럭(initialization block)' 또는 생성자를 사용해야 한다.
class Car{
	int door = 4;	// 기본형(primitive type) 변수의 초기화
	Engine e = new Engine();	// 참조형(reference type) 변수의 초기화

	// ...
}

 

✔️ 초기화 블럭(initialization block)

✏️ 초기화 블럭의 종류

  • 클래스 초기화 블럭 클래스변수의 복잡한 초기화에 사용된다.
  • 인스턴스 초기화 블럭 인스턴스변수의 복잡한 초기화에 사용된다.

 

명시적 초기화만으로는 부족한 경우 초기화 블럭을 사용한다.

class InitBlock {
	static {/* 클래스 초기화블럭 입니다. */}
	
	{/* 인스턴스 초기화블럭 입니다. */ }
	
	// ...
}
  • 클래스 초기화 블럭은 클래스가 메모리에 처음 로딩될 때 한번만 수행되며, 인스턴스 초기화블럭은 생성자와 같이 인스턴스를 생성할 때 마다 수행된다.
  • 생성자보다 인스턴스 초기화 블럭이 먼저 수행된다는 것도 기억해두자!

 

💡 참고

  • 클래스가 처음 로딩될 때 클래스변수들이 자동적으로 메모리에 만들어지고, 곧바로 클래스 초기화블럭이 클래스변수들을 초기화하게 되는 것이다.

 

인스턴스 변수의 초기화는 주로 생성자를 사용하고, 인스턴스 초기화 블럭은 모든 생성자에서 공통으로 수행돼야 하는 코드를 넣는데 사용한다.

Car() {
	count++;
	serialNo = count;
	color="White";
	gearType ="Auto";
}
Car(String color, String gearType) {
	count++;
	serialNo = count;
	this.color=color;
	this.gearType = gearType;
}

➡️

{
	count++;
	serialNo = count;
}
Car() {
	clor = "White";
	gearType="Auto";
}

Car(String color, String gearType) {
	this.color=color;
	this.gearType = gearType;
}
  • 인스턴스 블럭에 넣어줌으로써 중복을 제거
  • 재사용성을 높이고 중복을 제거하는 것이 바로 객체지향프로그래밍이 추구하는 궁극적인 목표이다.

 

public class BlockTest {
	static {
		System.out.println("static { }");
	}
	
	{
		System.out.println("{ }");
	}
	
	public BlockTest() {
		System.out.println("생성자");
	}
	
	public static void main(String args[]) {
		System.out.println("BlockTest bt = new BlockTest(); ");
		BlockTest bt = new BlockTest();
		
		System.out.println("BlockTest bt2 = new BlockTest(); ");
		BlockTest bt2 = new BlockTest();
	}
}
static { }
BlockTest bt = new BlockTest();
{ }
생성자
BlockTest bt2 = new BlockTest();
{ }
생성자
  • 클래스 초기화 블럭은 처음 메모리에 로딩될 때 한번만 수행되었지만, 인스턴스 초기화 블럭은 인스턴스가 생성될 때 마다 수행되었다.

 

public class StaticBlockTest {
	static int[] arr = new int[10];

	static {
		for(int i=0;i<arr.length;i++) {
			// 1과 10사이의 임의이 값을 배열 arr에 저장한다.
			arr[i] = (int)(Math.random()*10) + 1;
		}
	}
	public static void main(String args[]) {
		for(int i=0; i<arr.length;i++)
			System.out.println("arr["+i+"] :" + arr[i]);
	}
}
arr[0] :4
arr[1] :1
arr[2] :8
arr[3] :5
arr[4] :4
arr[5] :6
arr[6] :7
arr[7] :5
arr[8] :1
arr[9] :7
  • 명시적 초기화를 통해 배열 arr을 생성하고, 클래스 초기화 블럭을 이용해서 배열의 각 요소들을 random()을 사용해서 임의의 값으로 채우도록 했다.
  • 이처럼 배열이나 예외처리가 필요한 초기화에서는 명시적 초기화만으로는 복잡한 초기화작업을 할 수 없다.
  • 이런 경우에 추가적으로 클래스 초기화 블럭을 사용하도록 한다.

 

💡 참고

  • 인스턴스변수의 복잡한 초기화는 생성자 또는 인스턴스 초기화 블럭을 사용한다.

 

✔️ 멤버변수의 초기화 시기와 순서

✏️ 초기화가 수행되는 시기와 순서 정리

  • 클래스변수의 초기화시점 : 클래스가 처음 로딩될 때 단 한번 초기화 된다.
  • 인스턴스변수의 초기화시점 : 인스턴스가 생성될 때마다 각 인스턴스별로 초기화가 이루어진다.
  • 클래스변수의 초기화순서 : 기본값 → 명시적초기화 → 클래스 초기화 블럭
  • 인스턴스변수의 초기화순서 : 기본값 → 명시적초기화 → 인스턴스 초기화 블럭 → 생성자

 

class InitTest {
	static int cv = 1;   // 명시적 초기화
	int iv = 1;		 // 명시적 초기화
	
	static { 	cv = 2;  }	// 클래스 초기화 블럭
	{ iv = 2; 	}		// 인스턴스 초기화 블럭
	InitTest () {		// 생성자
		iv = 3;
	}
}


/*
	1. cv가 메모리(method area)에 생성되고, cv에는 int형의 기본값인 0이 저장된다.
    2. 그 다음에는 명시적 초기화(int cv = 1)에 의해 cv에 1이 저장된다.
    3. 마지막으로 클래스 초기화 블럭(cv = 2)이 수행되어 cv에 2가 저장된다.
    4. InitTest클래스의 인스턴스가 생성되면서 iv가 메모리(heap)에 존재하게 된다. iv는 int형 변수이므로 기본값 0이 저장된다.
    5. 명시적 초기화(int iv = 1)에 의해 iv에 1이 저장된다.
    6. 인스턴스 초기화 블럭(iv = 2)이 수행되어 iv에 2가 저장된다.
    7. 생성자가 수행되어 iv에 3이 저장된다.

*/

 

new InitTest(); 와 같이 하여 인스턴스를 생성했을 때, cv와 iv가 초기화되어가는 과정

클래스 초기화클래스 초기화클래스 초기화인스턴스 초기화인스턴스 초기화인스턴스 초기화인스턴스 초기화
기본값명시적초기화클래스초기화블럭기본값명시적 초기화인스턴스초기화블럭생성자
cv : 012cv : 2, iv : 0cv : 2, iv : 1cv : 2, iv : 2cv : 2, iv : 3
1234567
  • 클래스변수 초기화 (1~3) : 클래스가 처음 메모리에 로딩될 때 차례대로 수행됨
  • 인스턴스변수 초기화(4~7) : 인스턴스를 생성할 때 차례대로 수행됨

(클래스변수는 항상 인스턴스변수보다 항상 먼저 생성되고 초기화 된다.)

 

profile
"야, (오류 만났어?) 너두 (해결) 할 수 있어"

0개의 댓글

관련 채용 정보