[Java] Java의 정석 | Chapter 06 객체지향 프로그래밍Ⅰ

숙취엔 꿀물.·2023년 12월 3일

Java

목록 보기
6/13
post-thumbnail

👉 1. 객체지향언어

1.1 객체지향언어의 역사

  • 객체지향이론의 기본 개념은 실제 세계는 사물(객체)로 이루어져 있으며, 발생하는 모든 사건들은 사물간의 상호작용이다. 라는 것이다.
  • ~~
  • 자바가 1995년에 발표되고 1990년대 말에 인터넷의 발전과 함께 크게 유행하면서 객체 지향언어는 이제 프로그래밍언어의 주류로 자리 잡았다.

1.2 객체지향언어

  • 객체지향언어는 기존의 프로그래밍언어와 다른 전혀 새로운 것이 아니라, 기존의 프로그래밍 언어에 몇 가지 새로운 규칙을 추가한 보다 발전된 형태의 것이다.

  • 객체지향언어의 주요특징은 다음과 같다.

    	1. 코드의 재사용성이 높다.
    		새로운 코드를 작성할 때 기존의 코드를 이용하여 쉽게 작성할 수 있다.
    	2. 코드의 관리가 용이하다.
    		코드간의 관계를 이용해서 적은 노력으로 쉽게 코드를 변경할 수 있다.
    	3. 신뢰성이 높은 프로그래밍을 가능하게 한다.
    		제어자와 메서드를 이용해서 데이터를 보호하고 올바른 값을 유지하도록 하며, 코드의 중복을 제거하여 코드의 불일치로 인한 오동작을 방지할 수 있다.

-> 앞으로 상속, 다형성과 같은 객체지향개념을 학습할 때 재사용성유지보수 그리고 중복된 코드의 제거 , 이 세 가지 관점에서 보면 보다 쉽게 이해할 수 있을 것이다.



👉 2. 클래스와 객체

2.1 클래스와 객체의 정의와 용도

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

객체의 정의 : 실제로 존재하는 것, 사물 또는 개념
객체의 용도 : 객체가 가지고 있는 기능과 속성에 따라 다름

유형의 객체 : 책상, 의자, 자동차, TV와 같은 사물
무형의 객체 : 수학공식, 프로그램 에러와 같은 논리나 개념


2.2 객체와 인스턴스

  • 클래스로부터 객체를 만드는 과정을 클래스의 인스턴스화(instantiate) 라고 하며,
  • 어떤 클래스로부터 만들어진 객체를 그 클래스의 인스턴스(instance) 라고 한다.

2.3 객체의 구성요소 - 속성과 기능

속성(property) - 멤버변수(member variable), 특성(attribute), 필드(field), 상태(state)
기능(function) - 메서드(method), 함수(function), 행위(behavior)

class Tv{
	String color;	// 색깔
    boolean power;	// 전원상태
    int channel;	// 채널
    
      void power()		{ power = !power; }
      void channelUp()	{ channel++; }
      void channelDown()	{ channel--; }
}

예를 들어, 위 코드에서 위 변수 부분이 속성 , 아래 메서드 부분이 기능 이다.


2.4 인스턴스의 생성과 사용

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

Tv t;					// Tv클래스 타입의 참조변수 t를 선언
t = new Tv();			// Tv인스턴스를 생선한 후, 생성된 Tv인스턴스의 주소를 t에 저장
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의 채널을 낮추는 기능을 하는 메서드
}
public class TvTest {
    public static void main(String[] args) {
        Tv t;               // Tv인스턴스를 참조하기 위한 변수 t를 선언
        t = new Tv();       // Tv인스턴스를 생성한다.
        t.channel = 7;      // Tv인스턴스의 멤버변수 channel의 값을 7로 한다.
        t.channelDown();    // Tv인스턴스의 메서드 channelDown()을 호출한다.
        System.out.println("현재 채널은 "+t.channel+" 입니다.");
    }
}
  • 인스턴스는 참조변수를 통해서만 다룰 수 있으며, 참조변수의 타입은 인스턴스의 타입과 일치해야한다. (중요!!!!)
class TvTest3 {
	public static void main(String args[]) {
    	Tv t1 = new Tv();
        Tv t2 = new Tv();
        System.out.println("t1의 channel값은 " + t1.channel + "입니다.");
        System.out.println("t2의 channel값은 " + t2.channel + "입니다.");
        
        t2 = t1;		// t1이 저장하고 있는 값(주소)을 t2에 저장한다.
        t1.channel = 7; // channel 값을 7로 한다.
        System.out.println("t1의 channel값을 7로 변경하였습니다.");
        
        System.out.println("t1의 channel값은 " + t1.channel + "입니다.");
        System.out.println("t2의 channel값은 " + t2.channel + "입니다.");
    }
}

t2 = t1; 부분을 보자

t1은 참조변수이므로, 인스턴스의 주소를 저장하고 있다. 이 문장이 수행되면, t2가 가지고 있던 값은 잃어버리게 되고 t1에 저장되어 있던 값이 t2에 저장되게 된다. 그렇게 되면 t2 역시 t1이 참조하고 있던 인스턴스를 같이 참조하게 되고, t2가 원래 참조하고 있던 인스턴스는 더 이상 사용할 수 없게 된다.

  • 참조변수에는 하나의 값(주소)만이 저장될 수 있으므로 둘 이상의 참조변수가 하나의 인스턴스를 가리키는(참조하는) 것은 가능

  • 그러나, 하나의 참조변수로 여러 개의 인스턴스를 가리키는 것은 가능하지 않다..!


2.5 객체 배열

많은 객체를 다뤄야할 때, 객체 또한 배열로 다루면 편리할 것.

// Tv 클래스는 위의 코드와 동일
public class TvTest4 {
    public static void main(String[] args) {
        Tv[] tvArr = new Tv[3]; // 길이가 3인 Tv객체 배열

        // Tv객체를 생성해서 Tv객체 배열의 각 요소에 저장
        for (int i = 0; i < tvArr.length; i++) {
            tvArr[i] = new Tv();
            tvArr[i].channel = i + 10; // tvArr[i]의 channel에 i+10을 저장
        }

        for (int i = 0; i < tvArr.length; i++) {
            tvArr[i].channelUp();   // tvArr[i]의 메서드를 호출. 채널이 1증가
            System.out.printf("tvArr[%d].channel = %d\n", i, tvArr[i].channel);
        }
    }
}

2.6 클래스의 또 다른 정의

클래스는 객체를 생성하기 위한 틀 이며 클래스는 속성과 기능으로 정의되어있다 고 했다. 이것은 객체지향이론의 관점에서 내린 정의이고, 이번엔 프로그래밍적인 관점에서 클래스의 정의와 의미를 보자.

1. 변수 - 하나의 데이터를 저장할 수 있는 공간
2. 배열 - 같은 종류의 여러 데이터를 하나의 집합으로 저장할 수 있는 공간
3. 구조체 - 서로 관련된 여러 데이터를 종류에 관계없이 하나의 집합으로 저장할 수 있는 공간
4. 클래스 - 데이터와 함수의 결합(구조체 + 함수)
  • 클래스 - 사용자정의 타입(user-defined type)

프로그래밍언어에서 제공하는 자료형(primitive type)외에 프로그래머가 서로 관련된 변수들을 묶어서 하나의 타입으로 새로 추가하는 것을 사용자정의 타입(user-defined type)이라고 한다.

시, 분, 초를 위한 변수를 hour1, hour2, hour3 이런식으로 사용하거나 int[] hour = new int[3]; 이렇게 사용하다보면 프로그램 수행과정에서 시, 분, 초가 따로 뒤섞여서 올바르지 않은 데이터가 될 가능성이 있다. 이런 경우 이를 하나로 묶는 사용자정의 타입, 즉 클래스를 정의하여 사용해야한다.

class Time {
	int hour;
    int minute;
    float second;
}


👉 3. 변수와 메서드

3.1 선언위치에 따른 변수의 종류

  • 변수는 클래스변수, 인스턴스변수, 지역변수 모두 세 종류가 있다.

  • 변수의 중요한 요소는 변수의 선언된 위치 이므로 변수의 종류를 파악하기 위해서는 변수가 어느 영역에 선언되었는지를 확인하는 것이 중요하다.

  • 멤버변수를 제외한 나머지 변수들은 모두 지역변수, 멤버변수 중 static이 붙은 것은 클래스변수, 붙지 않은 것은 인스턴스변수

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

3.2 클래스변수와 인스턴스변수

public class CardTest {
    public static void main(String[] args) {
        System.out.println("Card.width = " + Card.width);
        System.out.println("Card.height = " + Card.height);
        // 클래스변수(static변수)는 객체생성없이 '클래스이름.클래스변수'로 직접 사용 가능하다.
        
        Card c1 = new Card();
        c1.kind = "Heart";
        c1.number = 7;      // 인스턴스변수의 값을 변경한다.

        Card c2 = new Card();
        c2.kind = "Spade";
        c2.number = 4;

        // c1, c2 kind number 크기 출력

        c1.width = 50;
        c1.height = 80; // 클래스변수의 값을 변경한다.

        // 변경된 크기 출력
    }
}

class Card{
    // 인스턴스변수
    String kind;
    int number;

    // 클래스변수
    static int width = 100;
    static int height = 250;
}

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


3.3 메서드

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

기본적으로 수학의 함수와 유사하며, 어떤 값을 입력하면 이 값으로 작업을 수행해서 결과를 반환한다.

메서드를 사용하는 이유

  1. 높은 재사용성(reusability)
  2. 중복된 코드의 제거
  3. 프로그램의 구조화

3.4 메서드의 선언과 구현

메서드는 크게 두 부분, 선언부(header, 머리)구현부(body, 몸통) 로 이루어져 있다.

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

int add(int a, int b)									// 선언부
{														// 구
	int result = a + b;									// 현
    return result;		// 호출한 메서드로 결과를 반환한다.	//
}														// 부

메서드 선언부

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

매개변수 선언(parameter declaration)

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

메서드의 이름(method name)

이름만으로도 메서드의 기능을 쉽게 알 수 있도록 함축적이면서도 의미있는 이름을 짓도록 노력해야 한다.

반환타입(return type)

  • 메서드의 작업수행 결과(출력)인 반환값(return value) 의 타입을 적는다.
  • 반환값이 없는 경우 반환타입으로 void 를 적어야한다.

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

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

return문

  • 메서드의 반환타입이 void 가 아닌 경우, 구현부{} 안에 return 반환값; 이 반드시 포함되어 있어야 한다.
  • 이 값의 타입은 반환타입과 일치하거나 적어도 자동 형변환이 가능한 것 이어야 한다.

지역변수(local variable)

메서드 내에 선언된 변수


3.5 메서드의 호출

메서드를 정의해도 호출되지 않으면 아무 일도 일어나지 않는다.

메서드이름(값1, 값2, ...);		// 메서드를 호출하는 방법

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

  • 메서드를 호출할 때 괄호() 안에 지정해준 값들을 인자(argument) 또는 인수 라고 하고,
  • 인자의 개수와 순서는 호출된 메서드에 선언된 매개변수와 일치해야 한다.

3.6 return문


3.7 JVM의 메모리 구조

응용프로그램이 실행되면, JVM은 시스템으로부터 프로그램을 수행하는데 필요한 메모리를 할당받고 JVM은 이 메모리를 용도에 따라 여러 영역으로 나누어 관리한다.

  1. 메서드 영역(method area)
  • 프로그램 실행 중 어떤 클래스가 사용되면, JVM은 해당 클래스의 클래스파일(*.class)을 읽어서 분석하여 클래스에 대한 정보(클래스 데이터)를 이곳에 저장한다. 이 때, 그 클래스의 클래스변수(class variable)도 이 영역에 함께 생성된다.
  1. 힙(heap)
  • 인스턴스가 생성되는 공간. 프로그램 실행 중 생성되는 인스턴스는 모두 이곳에 생성된다. 즉, 인스턴스변수(instance variable)들이 생성되는 공간이다.
  1. 호출스택(call stack 또는 execution stack)
  • 호출스택은 메서드의 작업에 필요한 메모리 공간을 제공한다. 메서드가 호출되면, 호출스택에 호출된 메서드를 위한 메모리가 할당되며, 이 메모리는 메서드가 작업을 수행하는 동안 지역변수(매개변수 포함)들과 연산의 중간결과 등을 저장하는데 사용된다. 그리고 메서드가 작업을 마치면 할당되었던 메모리공간은 반환되어 비워진다.

호출스택의 특징 정리

  • 메서드가 호출되면 수행에 필요한 만큼의 메모리를 스택에 할당받는다.
  • 메서드가 수행을 마치고나면 사용했던 메모리를 반환하고 스택에서 제거된다.
  • 호출스택의 제일 위에 있는 메서드가 현재 실행 중인 메서드이다.
  • 아래에 있는 메서드가 바로 위의 메서드를 호출한 메서드이다.

3.8 기본형 매개변수와 참조형 매개변수

기본형 매개변수 - 변수의 값을 읽기만 할 수 있다.(read only)
참조형 매개변수 - 변수의 값을 읽고 변경할 수 있다.(read & write)

3.9 참조형 반환타입

반환하는 값의 타입이 참조형이라는 얘기.

static Data copy(Data d){
	Data tmp = new Data();
    tmp.x = d.x;
    
    return tmp;
}

3.10 재귀호출(recursive call)

메서드의 내부에서 메서드 자신을 다시 호출하는 것.

void method(){
	method();		// 재귀호출, 메서드 자신을 호출한다.
}

굳이 반복문대신 재귀호출을 사용하는 이유 ?

  • 재귀호출이 주는 논리적 간결함 때문이다.

  • 몇 겹의 반복문과 조건문으로 복잡하게 작성된 코드를 단순한 구조로 바꿀 수 있다.

  • 다소 비효율적이더라도 알아보기 쉽게 작성하는 것이 논리적 오류가 발생할 확률도 줄어들고 나중에 수정하기도 좋다.

  • 다만, 재귀호출에 드는 비용 < 재귀호출의 간결함이 주는 이득 인 경우에만 사용하는게 좋다.

예시 코드) 팩토리얼(factorial) 구하기

public class FactorialTest {
    public static void main(String[] args) {
        int result = factorial(4);

        System.out.println(result);
    }

    static int factorial(int n) {
        int result = 0;

        if (n == 1)
            result = 1;
        else
            result = n * factorial(n - 1); // 다시 메서드 자신을 호출한다.

        return result;
    }
}

3.11 클래스 메서드(static메서드)와 인스턴스 메서드

클래스를 정의할 떄, 어느 경우에 static을 사용해서 클래스 메서드로 정의해야 하는가?

  1. 클래스를 설계할 때, 멤버변수 중 모든 인스턴스에 공통으로 사용하는 것에 static을 붙인다.
  2. 클래스 변수 (static변수)는 인스턴스를 생성하지 않아도 사용할 수 있다.
  3. 클래스 메서드(static메서드)는 인스턴스 변수를 사용할 수 없다.
  4. 메서드 내에서 인스턴스 변수를 사용하지 않는다면, static을 붙이는 것을 고려한다.

3.12 클래스 멤버와 인스턴스 멤버간의 참조와 호출

  • 같은 클래스에 속한 멤버들 간에는 인스턴스를 생성하지 않고도 서로 참조 또는 호출이 가능하다.

  • 단, 클래스멤버가 인스턴스 멤버를 참조 또는 호출하고자 하는 경우에는 인스턴스를 생성해야 한다.

-> 인스턴스 멤버가 존재하는 시점에 클래스 멤버는 항상 존재하지만, 클래스멤버가 존재하는 시점에 인스턴스 멤버가 존재하지 않을 수도 있기 때문이다.

public class MemberCall {
    int iv = 10;
    static int cv = 20;
    
    int iv2 = cv;
    // 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();
    }
}


👉 4. 오버로딩(overloading)

4.1 오버로딩이란?

  • 한 클래스 내에 이미 사용하려는 이름과 같은 이름을 가진 메서드가 있더라도 매개변수의 개수 또는 타입이 다르면, 같은 이름을 사용해서 메서드를 정의할 수 있다.

  • 메서드 오버로딩(method overloading) 또는 오버로딩(overloading) 이라 한다.


4.2 오버로딩의 조건

  1. 메서드 이름이 같아야 한다.
  2. 매개변수의 개수 또는 타입이 달라야 한다.

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


4.3 오버로딩의 예

void println()
void println(boolean x)
void println(cahr x)
void println(char[] x)
void println(double x)
void println(float x)
void println(int x)
...

4.4 오버로딩의 장점

  1. 기억하기도 쉽고 이름도 짧게 할 수 있어서 오류의 가능성을 많이 줄일 수 있다.
  2. 메서드의 이름을 절약할 수 있다.

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

  • JDK1.5부터 메서드의 매개변수 개수를 동적으로 지정해 줄 수 있게 되었으며, 이를 가변인자(variable arguments) 라고 한다.

  • 가변인자는 타입... 변수명 과 같은 형식으로 선언하며, PrintStream클래스의 printf()가 대표적인 예이다.

    	public PrintStream printf(String format, Object... args) {...}

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

가변인자는 내부적으로 배열을 이용하기도 한다. 그래서 가변인자가 선언된 메서드를 호출할 때마다 배열이 새로 생성된다.
-> 편리하지만, 이런 비효율이 숨어있으므로 꼭 필요한 경우에만 가변인자를 사용하자 !
-> 그리고 추가로 가능하면 가변인자를 사용한 메서드는 오버로딩하지 않는 것이 좋다.



👉 5. 생성자(Constructor)

5.1 생성자란?

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

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

클래스이름(타입 변수명, 타입 변수명, ...) {
	// 인스턴스 생성 시 수행될 코드,
    // 주로 인스턴스 변수의 초기화 코드를 적는다.
}

class Card {
	Card() {		// 매개변수가 없는 생성자
    	...
    }
    
    Card(String k, int num) {	// 매개변수가 있는 생성자
    	...
    }
    ...
}

연산자 new가 인스턴스를 생성하는 것이지 생성자가 인스턴스를 생성하는 것은 아니다.

Card 클래스의 인스턴스를 생성하는 코드를 예로 들어, 수행되는 과정을 나누면

Card c = new Card();

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

5.2 기본 생성자(default constructor)

본래 모든 클래스에는 반드시 하나 이상의 생성자가 정의되어 있어야 한다.

  • 지금까지 클래스에 생성자를 정의하지 않고도 인스턴스를 생성했던건 컴파일러가 제공하는 기본 생성자(default constructor) 덕분이었다.

  • 특별히 인스턴스 초기화 작업이 요구되어지지 않는다면 생성자를 정의하지 않고 컴파일러가 제공하는 기본 생성자를 사용하는 것도 좋다.


5.3 매개변수가 있는 생성자


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

같은 클래스의 멤버들 간에 서로 호출할 수 있는 것처럼 생성자 간에도 서로 호출이 가능하다.
단! 다음의 두 조건을 만족해야함

- 생성자의 이름으로 클래스이름 대신 this를 사용한다.
- 한 생성자에서 다른 생성자를 호출할 때는 반드시 첫 줄에서만 호출이 가능하다.
class Car{
    String color;
    String gearType;
    int door;
    
    Car(){
        this("white", "auto",4); // Car(String color, String gearType, int door)를 호출
    }
    
    Car(String color){
        this(color, "auto", 4);
    }

    public Car(String color, String gearType, int door) {
        this.color = color;
        this.gearType = gearType;
        this.door = door;
    }
}

public class CarTest2 {
    public static void main(String[] args) {
        Car c1 = new Car();
        Car c2 = new Car("blue");
        
        // c1의 속성 출력
        // c2의 속성 출력
    }
}
  • this 를 사용할 수 있는 것은 인스턴스멤버뿐이다.

  • static메서드(클래스 메서드)에서는 인스턴스 멤버들을 사용할 수 없는 것처럼, this 역시 사용할 수 없다.

    	this - 인스턴스 자신을 가리키는 참조변수, 인스턴스의 주소가 저장되어 있다. 모든 인스턴스메서드에 지역변수로 숨겨진 채로 존재한다.

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

현재 사용하고 있는 인스턴스와 같은 상태를 갖는 인스턴스를 하나 더 만들고자 할 때 생성자를 이용할 수 있다.

Car(Car c) {
	color = c.color;
    gearType = c.gearType;
    door = c.door;
}

생성자를 잘 활용하면 보다 간결하고 직관적인, 객체지향적인 코드를 작성할 수 있을 것이다.

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


👉 6. 변수의 초기화

6.1 변수의 초기화

  • 멤버변수 는 초기화를 하지 않아도 자동적으로 변수의 자료형에 맞는 기본값으로 초기화가 이루어지므로 초기화하지 않고 사용해도 되지만,
  • 지역변수 는 사용하기 전에 반드시 초기화해야 한다.

멤버변수의 초기화 방법에는 여러 가지가 있는데 다음과 같다.

> 멤버변수의 초기화 방법
1. 명시적 초기화(explicit initialization)
2. 생성자(constructor)
3. 초기화 블럭(initialization block)
	- 인스턴스 초기화 블럭 : 인스턴스변수를 초기화 하는데 사용.
    - 클래스 초기화 블럭 : 클래스변수를 초기화 하는데 사용.

6.2 명시적 초기화(expicit initialization)

  • 변수를 선언과 동시에 초기화하는 것을 명시적 초기화라고 한다.
  • 가장 기본적이면서도 간단한 초기화 방법이므로 여러 초기화 방법 중에서 가장 우선적으로 고려되어야 한다.
clas Car {
	int door = 4;				// 기본형(primitive type) 변수의 초기화
    Engine e = new Engine();	// 참조형(reference type) 변수의 초기화
}

명시적 초기화가 간단하고 명료하긴 하지만, 보다 복잡한 초기화 작업이 필요할 때는 초기화 블럭(initialization block) 또는 생성자 를 사용해야 한다.


6.3 초기화 블럭(initialization block)

클래스 초기화 블럭 : 클래스변수의 복잡한 초기화에 사용된다.
인스턴스 초기화 블럭 : 인스턴스 변수의 복잡한 초기화에 사용된다.
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();
    }
}

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

클래스변수의 초기화시점 : 클래스가 처음 로딩될 때 단 한번 초기화 된다.
인스턴스변수의 초기화시점 : 인스턴스가 생성될 때마다 각 인스턴스별로 초기화가 이루어진다.

클래스변수의 초기화순서 : 기본값 -> 명시적초기화 -> 클래스 초기화 블럭
인스턴스변수의 초기화순서 : 기본값 -> 명시적초기화 -> 인스턴스 초기화 블럭 -> 생성자
  • 프로그램 실행도중 클래스에 대한 정보가 요구될 떄, 클래스는 메모리에 로딩된다. 에를 들면, 클래스 멤버를 사용했을 떄, 인스턴스를 생성할 때 등이 이에 해당된다.

  • 하지만, 해당 클래스가 이미 메모리에 로딩되어 있다면, 또다시 로딩하지 않는다. 물론 초기화도 다시 수행되지 않는다.

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

위의 코드에서 new InitTest(); 와 같이 하여 인스턴스를 생성했을 때, cv와 iv가 초기화되어가는 과정을 단계별로 살펴보면 다음과 같다.

> 클래스 변수 초기화(1~3) : 클래스가 처음 메모리에 로딩될 때 차례대로 수행됨.
> 인스턴스변수 초기화(4~7) : 인스턴스를 생성할 때 차례대로 수행됨.

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

0개의 댓글