<Java> 재귀호출, 메서드 오버로딩, 생성자(Constructor), 변수의 초기화

·2023년 6월 8일
0

Java

목록 보기
6/7

재귀호출(Recursive call)

자기가 자기 자신을 호출하는 것 ➡️ 무한반복의 로직

재귀호출은 반복문으로 바꿀 수 있으며 반복문보다 성능이 나쁘다.
이해하기 쉽고, 간결한 코드를 작성할 수 있다. (분할 정복 알고리즘 한정)
분할 정복 알고리즘이란?

하지만 실제로 사용을 잘 하진 않음.
왜냐? 컴퓨터 자원을 많이 잡아먹는다.

재귀호출 예시

  • 팩토리얼, 제곱, 트리운행, 폴더목록표시 등이 있다.

팩토리얼(Factorial)
5! = 5 x 4 x 3 x 2 x 1
f(n) = n
f(n-1) 단, f(1) = 1이다.

예시 코드

// Factorial.java

package chap06;
import java.util.*;

public class Factorial {
    public static void main(String[] args) {
        System.out.print("임의의 자연수를 입력하세요.");

        Scanner sc = new Scanner(System.in);

        int num = sc.nextInt();
        int result;

        FactorialObj ex = new FactorialObj();

        System.out.println(ex.factorial(num));
    }
}


// FactorialObjClass.java

package chap06;

public class FactorialObj {
    int factorial(int n) {
        int result = 0;
        if(n == 1) {
            result = 1;
        } else {
            result = n * factorial(n - 1);
        } 
        return result;
    }
}

클래스, 인스턴스 예시 코드

package chap06;
import  java.util.*;

// 사용자에게 하나의 실수를 입력받아 다음의 결과를 출력하는 코드를 작성하세요.
/*
* 단, 객체지향적으로 코드를 작성하세요.
*
* 실행결과
*
* 하나의 실수를 입력하세요 : 3.64
* 버림 : 3
* 반올림 : 4
* 올림 : 4
* */
public class Exam1 {
    public static void main(String[] args) {
        System.out.print("0부터 100 사이의 실수를 입력해주세요.");

        Scanner sc = new Scanner(System.in);
        double num = sc.nextDouble();

        Exam1Test testInstance = new Exam1Test();

        System.out.println("버림 : " + testInstance.numDown(num));
        System.out.println("반올림 : " + testInstance.roundUp(num));
        System.out.println("올림 : " + testInstance.numUp(num));
    }
} // main
package chap06;

import java.util.Scanner;

// 사용자에게 하나의 실수를 입력받아 다음의 결과를 출력하는 코드를 작성하세요.
/*
* 단, 객체지향적으로 코드를 작성하세요.
* */
public class Exam1Test {
    int numDown(double num) {
        int floorNum = (int)Math.floor(num);
        return floorNum;
    }

    int numUp(double num) {
        int ceilNum = (int)Math.ceil(num);
        return ceilNum;
    }

    int roundUp(double num) {
        int roundNum = (int)Math.round(num);
        return roundNum;
    }
} // class

메서드 오버로딩

하나의 클래스에 같은 이름의 메서드를 여러 개 정의하는 것메서드 오버로딩(=== 오버로딩) 이라고 한다.

다형성polymorph을 구현하는 방법 중 하나인 오버로딩
객체지향을 개표하는 기능.

오버로딩의 조건

  1. 메서드의 이름이 같아야 한다.
  2. 매개변수 개수 또는 타입달라야 한다.
  3. 매개변수는 같고, Return Type다른 경우는 오버로딩이 성립되지 않는다.
    (Return Type은 오버로딩을 구현하는데 아무런 영향을 주지 못한다.)

➡️ 하나의 메서드를 완전 동일하게 만들면 안된다. 그것을 구분하기 위해 매개변수와 Type이 달라야 함.

오버로딩 예시

  1. System.out.println() 메서드
    • 다양하게 오버로딩된 메서드를 제공함으로써 모든 변수를 출력할 수 있도록 설계됨
  2. 매개변수의 이름이 다른 것은 오버로딩이 아니다.
int add(int a, int b) {return a + b};
int add(int x, int y) {return x + y};

Return Type은 오버로딩의 성립조건이 아니다.

int add(int a, int b) {return a + b};
long add(int a, int b) {return (long)(a + b)};
  1. 매개변수의 타입이 다르므로 오버로딩이 성립하는 예시
long add(int a, long b) {return a + b};
long add(long x, int y) {return x + y};

오버로딩의 올바른 예시 => 매개변수는 다르지만, 같은 의미의 기능을 수행.

생성자 (Constructor)

인스턴스가 생성될 때마다 호출되는 '인스턴스 초기화 메서드'

Card c = new Card(); // 인스턴스 생성과 동시에 초기화

모든 클래스는 반드시 하나 이상의 생성자를 가진다.

  • 인스턴스 변수의 초기화 또는 인스턴스 생성시 수행할 작업에 사용
  • 몇가지 조건을 제외하고는 메서드와 같다.
    - 클래스로부터 인스턴스가 만들어질 때, 자동으로 호출되는 특별한 메서드라고도 할 수 있다.

✅ 해당 클래스 생성자가 4개 === 해당 클래스로부터 생성할 수 있는 인스턴스가 4개

생성자 예시 코드

package chap06;

public class Car {
    String color;
    String gearType;
    int door;

    Car() {
        this.color = "black";
        this.gearType = "auto";
        this.door = 4;
    }

    Car(String color) {
        this.color = color;
        this.gearType = "auto";
        this.door = 4;
    }

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

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

특징

  1. 생성자의 이름은 반드시 클래스의 이름과 같아야한다.
  2. 반환형 자체가 없다. (=== 반환값이 없다. void 표기하면 에러가 난다.)
  3. 생성자는 매개변수를 가질 수 있다.
  4. 생성자 오버로딩 가능. 유일하게 식별할 수 있기 위해서 생성자의 매개변수와 타입이 다름.
  5. 각 인스턴스 객체는 중복될 수 없다. 생성자를 통해 각각의 구별된 인스턴스들이 생성가능하다.

기본 생성자 (Default Constructor)

  • 매개변수가 없는 생성자
  • 클래스에 생성자가 하나도 없으면 컴파일러가 기본 생성자를 추가한다.
    - 생성자가 하나라도 있으면 컴파일러는 기본 생성자를 추가하지 않는다.

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

JVM에서 자동적으로 default Constructor 하나를 만들어준다.

클래스이름() {}
Card() {} // 컴파일러에 의해 추가된 Card 클래스의 기본 생성자. 내용 없음

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

자기 클래스 안에 있는 다른 생성자를 호출하는 문장이다.

this() - 생성자, 같은 클래스의 다른 생성자를 호출할 때 사용. 다른 생성자 호출은 생성자의 첫 문장에서만 가능하다.

this()를 사용해서 다른 생성자를 끌어다 사용함으로써 중복되는 코드를 제거할 수 있다.

// 예시코드

package chap06;

public class Car2 {
    String color;
    String gearType;
    int door;

    Car2() {
        this("black", "Auto", 4);
    }

    Car2(String color) {
    this(color, "Auto", 4);
    }

    Car2(String color, String gearType) {
        this(color, gearType, 4);
    }
    
//✅ 위에 this()들은 아래 Car2의 메소드를 호출하고 있음.
    Car2(String color, String gearType, int door) {
        this.color = color;
        this.gearType = gearType;
        this.door = door;
    }
}

반드시 첫 번째 문장으로 this()를 시작해야 한다.
그렇지 않으면 구문 오류를 초래한다.

왜❓ ➡️ 먼저 this()로 초기화 하고, 다른 작업을 이어가게 하도록 하기 위해서.

Car2() {
        System.out.println("첫번째");
        this("black", "Auto", 4);
    } // 구문 오류 발생

this() 출력순서로_더 이해해보기

//Class
package chap06;

public class Car2 {
    String color;
    String gearType;
    int door;

    Car2() {
        this("black", "Auto", 4);
        System.out.println("첫 번째 생성자");
    }

    Car2(String color) {
    this(color, "Auto", 4);
        System.out.println("두 번째 생성자");
    }

    Car2(String color, String gearType) {
        this(color, gearType, 4);
        System.out.println("세 번째 생성자");
    }

    Car2(String color, String gearType, int door) {
        this.color = color;
        this.gearType = gearType;
        this.door = door;
        System.out.println("네 번째 생성자");
    }
}


// main
package chap06;

public class CarTest2 {
    public static void main(String[] args) {
        Car2 car1, car2, car3, car4;

        car1 = new Car2();
        car2 = new Car2("White");
        car3 = new Car2("White","Manual");
        car4 = new Car2("White","Manual",3);
        
/* 출력값은 네 번째 생성자
첫 번째 생성자

네 번째 생성자
두 번째 생성자

네 번째 생성자
세 번째 생성자

네 번째 생성자 */ // 그 이유는 네 번째 생성자를 this()로 참조하고 있고, 네 번째 생성자로 초기화를 하고 있기 때문이다.

//        System.out.println("car1 : " + car1.color + "   " + car1.gearType + "   " + car1.door);
//        System.out.println("car2 : " + car2.color + "   " + car2.gearType + "   " + car2.door);
//        System.out.println("car3 : " + car3.color + "   " + car3.gearType + "   " + car3.door);
//        System.out.println("car4 : " + car4.color + "   " + car4.gearType + "   " + car4.door);
    }
}

참조변수 this.

인스턴스 자기 자신을 가리키는 키워드이다.
this를 통해 클래스 메소드 및 생성자에서 자기 자신의 데이터를 업데이트하거나 조작할 수 있다.

this.는 클래스를 기반으로 생성된 인스턴스를 가리키는 참조다.

🔥 주의 : 인스턴스를 가르키는 참조와 인스턴스 자체는 다르다.

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

변수의 초기화

  1. 메서드 안에서 선언되는 메서드 변수 (자동으로 초기화 X)
  2. 인스턴스 변수 (자동으로 초기화 O) But, 자동으로 초기화되면 고유한 인스턴스가 아니므로 생성자로 인스턴스 변수를 초기화 해줘야 한다.
  3. 클래스 변수 (자동으로 초기화 O), 직접 값을 지정하여 초기화

인스턴스 변수와 클래스 변수 ➡️ 변수 선언과 초기화를 꼭 분리해야 한다.

  • 인스턴스 변수
    - 생성자로 초기화
  • 클래스(static) 변수
    - 생성자로 초기화 가능하지만, 지양해야 한다.
    • 초기화 블럭{}이 존재하는데, 해당 블럭에서 초기화를 진행해야 한다.

왜? 클래스 변수는 하나만 존재.
클래스로부터 인스턴스는 제약없이 생성 가능.

클래스 변수는 인스턴스 생성 하지 않아도 메모리로 끌어올릴 수 있다.

// main
package chap07;

public class Test4 {
    public static void main(String[] args) {
//     Class2 class2 = new Class2();

        System.out.println("classVar : " + Class2.classVar); // class가 최초로 참조되어져서 메모리에 올라옴. 인스턴스 생성을 하지 않아도 실행되는 이유이다.
        Class2.classVar = 1234;
        System.out.println("classVar : " + Class2.classVar);
    }
}

// class
package chap07;

public class Class2 {

    static int classVar; // 0으로 초기화
    int instanceVar; // 0으로 초기화

    void methodA() {
//        int methodVar; // 초기화가 안된 메소드 변수는 사용할 수 없다.
        int methodVar = 10; // 메소드 변수는 반드시 초기화해야 사용할 수 있다.
        System.out.println("methodVar : " + methodVar);
    }


}

...이어서 9일에 내용 업데이트하기

profile
- 배움에는 끝이 없다.

0개의 댓글