경주장

5주차 과제: 클래스 #5 나머지! 본문

JAVA/whiteship_live-study

5주차 과제: 클래스 #5 나머지!

달리는치타 2022. 2. 21. 21:54

분량이 긴 것같아 글을 분리하여 작성하겠습니다.

학습할 것 (필수)

  • 클래스 정의하는 방법
  • 객체 만드는 방법 (new 키워드 이해하기)
  • 메소드 정의하는 방법
  • 생성자 정의하는 방법
  • this 키워드 이해하기

생성자(Constructor)는 new 연산자와 같이 사용되어 클래스로부터 객체를 생성할 때 호출되어 객체의 초기화를 담당합니다. 객체 초기화란 필드를 초기화하거나, 메소드를 호출하여 객체를 사용할 준비를 하는 것을 의미합니다. new 연산자에 의해 생성자가 성공적으로 실행되면 힙(heap)영역에 객체가 생성되고 객체의 주소가 리턴됩니다. 리턴된 객체의 주소는 클래스 타입 변수에 저장되어 객체에 접근할 때 이용됩니다.

모든 클래스는 생성자가 반드시 존재하며 하나 이상을 가질 수 있습니다. 클래스 내부에 생성자 선언을 생량했다면 컴파일러는 다음과 같이 기본 생성자(Default Constructor)를 바이트코드에 자동 추가시킵니다.

public class Car{
}

//=>

public class Car{
    public Car(){}    // 자동 추가, 기본 생성자
}

때문에 클래스에 생성자를 선언하지 않아도 다음과 같이 기본 생성자를 호출하여 객체를 생성시킬 수 있습니다.

Car car = new Car();

클래스에 명시적으로 선언한 생성자가 한 개라도 있으면, 컴파일러는 기본 생성자를 추가하지 않습니다.


생성자를 명시적으로 선언하는 방식에 대해 알아보겠습니다.

클래스(매개변수 선언, ...){
 //객체의 초기화 코드
}
public class Car{
...
    Car(String model, String color, int maxSpeed){
        this.model = model;
        this.color = color;
        this.maxSpeed = maxSpeed;
    }
}

3개의 필드를 매게변수로 받아 초기화하는 생성자입니다.

이때 this라는 키워드를 활용하였는데

this는 객체가 자기자신을 참조하는 주소를 의미합니다. (자기자신의 Instance를 참조하는 방법) this.maxSpeed= maxSpeed; 이라는 코드는 매개 변수로 입력받은 maxSpeed의 값을 객체가 참조하는 maxSpeed의 값에 대입하라는 의미입니다.


생성자 역시 매서드와 마찬가지로 오버로딩을 지원합니다. 생성자의 경우 매개 변수를 달리하는 생성자를 여러개 선언하여 오버로딩을 할 수 있습니다.

public class Car{
...

    Car(){
    }

    Car(String model){
        this.model=model;
    }

    Car(String model, String color, int maxSpeed){
        this.model = model;
        this.color = color;
        this.maxSpeed = maxSpeed;
    }
}

인자가 없는 생성자, model만을 초기화 하는 생성자, model과 color, maxSpeed를 초기화 하는 생성자 총 세가지의 생성자를 선언하였습니다. 이제 Car 객체를 생성할 때 상황에 따라 세가지 생성자를 활용 할 수 있습니다.


생성자 오버로딩이 많아질 경우 생성자 간의 중복된 코드가 발생할 수 있습니다. this()의 호출로 이를 개선 할 수 있습니다.

public class Car{
...

Car(){
    }

Car(String model){
	this(model, "red", 100);
}

Car(String model, String color, int maxSpeed){
	this.model = model;
	this.color = color;
    this.maxSpeed = maxSpeed;
    }
}

this()는 자신의 다른 생성자를 호출하는 코드로 반드시 생성자의 첫줄에서만 허용됩니다.

즉, 부모의 생성자를 호출하는 super()의 경우에도 마찬가지입니다.super(...)는 반드시 자식 생성자 첫 줄에 위치해야 하며 부모클래스에 기본 생성자만 있는 경우 자동으로 super()이 자식 생성자의 첫 줄에 삽입됩니다. 부모클래스에 명시적인 생성자가 있지만 자식 클래스의 생성자에서 super(...)를 호출하지 않는 경우 컴파일 에러가 발생합니다.


메소드 정의하는 법!

메소드는 객체의 동작(행위)에 해당하는 중괄호 { } 불록을 말합니다. 메소드는 자신의 필드를 읽고 수정하는 역할 뿐만 아니라, 다른 객체를 생성해서 다양한 기능을 수행하기도 합니다.


메소드 선언은 아래와 같이 이루어집니다.

리턴 타입 메소드이름([매개변수선언, ...] ) {

    //메소드 실행 - 실행할 코드 작성

}

매서드의 선언부인 첫번째 라인 (메소드이름, 매개변수 선언)을 메소드 시그니처라고도 합니다. 메소드 시그니처는 인터페이스의 선언시 활용되며 단일 메소드를 구분하는 기준이 될 수 있습니다.

메소드 시그니처 

  • 메소드 이름
    메소드 이름은 다음과 같은 제한이 있습니다.
    • 숫자로 시작하면 안 되고, $와 _를 제외한 특수문자를 사용하면 안됩니다.
    • 관례적으로 메소드면은 소문자, 카멜케이스로 작성합니다.
    • 메소드의 이름이 너무 길거나 끊어서 작성이 필요한 경우에는 (e.g. 테스트 코드 작성) 언더바를 사용하지만 잘 활용하지 않습니다.
  • 매개변수 선언
    • 매개 변수는 메소드가 실행할 때 필요한 데이터를 외부로 부터 받기 위해 사용됩니다.
    • 여기서는 자바의 매개변수 선언 중 특별한 경우는 '...' 문법에 대해서 다루어 보겠습니다.

매개 변수의 수를 모를 경우 매개 변수를 배열 타입으로 선언하는 일명 '...'문법으로 해결 할 수 있습니다.
나열된 모든 숫자를 더하는 sum 메소드를 아래와 같이 선언 할 수 있습니다.

 

int ...으로 선언된 메서드의 values 변수는 int[] 타입과 같습니다. '...'문법을 활용하면 간단하게 ,로 배열을 전달 할 수 있습니다.

 

public class MethodStudy {

    public static void main(String[] args){
        int s = sum(1,2,3,4);
        System.out.println("s = " + s);
    }
    
    static int sum(int... values){
        int[] inValues = values;
        return Arrays.stream(inValues).sum();
    }
}
s = 10

 

또한 중요한 헷갈리기 쉬운 개념으로 리턴타입과 예외(throws ...)는 메소드 시그니처에 포함되지 않습니다.
따라서 컴파일러는 메소드를 구분할 때 리턴 타입을 고려하지 않기 때문에 서로 다른 리턴 타입을 가져도 동일한 시그니처를 가진 2개의 메소드를 선언할 수 없습니다.