Gurumee 2020. 12. 19. 12:52
반응형

백기선님의 온라인 스터디 "스터디 할래" 5주차 정리 문서입니다.

목표

자바가 제공하는 제어문을 학습하세요.

학습할 것

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

과제 (선택)

  • int 값을 가지고 있는 이진 트리를 나타내는 Node 라는 클래스를 정의하세요.
  • int value, Node left, right를 가지고 있어야 합니다.
  • BinrayTree라는 클래스를 정의하고 주어진 노드를 기준으로 출력하는 bfs(Node node)와 dfs(Node node) 메소드를 구현하세요.
  • DFS는 왼쪽, 루트, 오른쪽 순으로 순회하세요.

클래스 정의하는 방법

클래스를 정의하는 방법은 다음과 같다. 먼저 클래스_이름.java라는 파일을 만들고 아래와 같은 방법으로 작성한다.

public class 클래스_이름 {
    접근_제한자 타입 필드_이름;

    // ...

    접근_제한자 클래스_이름() {

    }

    // ...

    접근_제한자 타입 메소드_이름(파라미터..) {
        // ...
    }
}

클래스는 보통 필드, 메소드, 생성자로 이루어져 있다. 생성자는 클래스 이름으로 정의하고, 필드들을 초기화하는 역할을 한다.

예를 들어 사람은 먼저 다음과 같은 속성이 있다.

  • 나이
  • 이름

이를 나타내는 Person 클래스는 다음과 같이 정의할 수 있다. (먼저 Person.java를 만들어둔다.)

public class Person{
    int age;
    String name;
}

age는 나이를, name은 이름을 나타낸다. 이들을 필드라고 한다. 그리고 클래스에 필드/메소드/생성자를 선언할 때, "접근 제한자"라는 것을 붙인다. 접근 제한자란 외부에서 클래스의 내부 값들을 접근할 때 허용 범위를 나타낸다. 기본적으로 자바에서 제공하는 접근 제한자는 다음과 같다.

  • private : 해당 클래스에서만 접근 가능하다.
  • protected : 자식을 상속하는 클래스들까지 접근이 가능하다.
  • public : 모든 곳에서 접근이 가능하다.
  • default : 같은 "패키지"에서 접근이 가능하다.

만약 Person을 다음과 같이 정의했다고 해보자.

public class Person{
    private int age;
    private String name;
}

이제 age, namePerson객체만 접근이 가능하다. 이런 느낌이다.

객체 만드는 방법 (new 키워드 이해하기)

다시 private을 제거한다. 객체를 만들려면 new 연산자를 사용해야 한다. 다음과 같이 쓸 수 있다.

Person p = new Person();

자바에서는 생성자를 선언하지 않으면, 기본 생성자를 바이트 코드로 변환하기 전에 미리 만들어준다. 만들어진 기본 생성자는 클래스 내부의 필드를 기본 값으로 초기화한다.

  • 기본 타입
    • char - \u0000
    • byte - 0
    • short - 0
    • int - 0
    • long - 0L
    • float - 0.0f
    • double - 0.0
    • boolean - false
  • 참조 타입 - null

즉, page는 0, namenull값으로 초기화된다.

System.out.println(p.age);  // 0 출력
System.out.println(p.name); // null 출력

메소드 정의하는 방법

메소드는 객체의 동작이라고 말할 수 있다. Person은 말할 수 있어야 한다. 그냥 "안녕하세요 나는 누구이고 몇살입니다"라고 말하는 동작을 정의해보자. 다음과 같이 정의할 수 있다.

public class Person{
    int age;
    String name;

    public void greeting() {
        System.out.println("안녕하세요 나는 " + name + "이고 " + age + "살입니다");    
    }
}

그럼 "객체를 생성한 후" greeting 메소드를 호출하면 된다.

Person p = new Person();
p.age = 30;
p.name = "구르미"
p.greeting(); // "안녕하세요 나는 구르미이고 30살입니다." 출력

this 키워드 이해하기

음 순서를 바꾸겠다. 먼저 this라는 것을 이해하기 위해서 다시 필드들을 private으로 바꾼다.

public class Person{
    private int age;
    private String name;
}

그 후 이전에 실행했던 코드를 실행해보라.

Person p = new Person();
p.age = 30;  // 컴파일 에러
p.name = "구르미";
p.greeting(); 

실행조차 안된다. private 접근 제한자가 적용되었기 때문에 말 그대로 그 "객체"만 접근이 가능하다. 그래서 컴파일 오류가 발생하는 것이다. private으로 접근이 막힌 필드들을 접근하기 위해서는 this 키워드를 사용해야 한다.

public class Person{
    private int age;
    private String name;

    public void setAge(int age){
        this.age = age;
    }

    public int getAge(){
        return age;
    }

    public void setName(String name){
        this.name = name;
    }

    public String getName(){
        return this.name;
    }

    public void greeting() {
        System.out.println("안녕하세요 나는 " + name + "이고 " + age + "살입니다");    
    }
}

getAge를 보자.

return age;

이 때 위 구문은 "return this.age;"가 함축된 것이다. 그래서 해당 객체의 필드의 값이 반환이 된다.

setAge를 보자. 파라미터로 필드와 이름이 같은 age가 선언이 되어 있다. 그러면, 이 때 객체라고 하더라도, 파라미터 이름이 같으면 파라미터의 변수가 우선권을 가진다. 그럼 어떻게 age 필드를 접근해야 할까.

바로 this 키워드이다.

this.age = age;

그럼 해당 객체의 age 필드에 메소드 파라미터로 전달된 age 변수의 값이 할당된다. 이제 실행문을 다음과 같이 바꾸면 된다.

Person p = new Person();
p.setAge(30);  
p.setName("구르미");
p.greeting(); // "안녕하세요 나는 구르미이고 30살입니다." 출력

기억하자! 객체의 내부 필드/메소드/생성자를 접근하는 것이 바로 "this" 키워드이다.

생성자 정의하는 방법

이제 생성자를 알아보자. 아무것도 선언이 안된어있다면, 추후에는 이렇게 코드가 변형된다.

public class Person{
    private int age;
    private String name;

    public Person() {

    }

    // ...
}

이래서 어떤 필드도 초기화가 안되어 있는 것이다. Person을 다음과 같이 변경해보자.

public class Person{
    private int age;
    private String name;

    public Person() {
        this.age = 15;
        this.name = "test";
    }

    // ...
}

그 다음 다음 구문을 실행해보자.

Person p = new Person();
System.out.println(p.getAge());  // 15 출력
System.out.println(p.getName()); // test 출력

기본적으로 생성자는 메소드라고 볼 수 있다. 이 때 "오버로딩"이라는 것을 할 수 있다. 같은 메소드 이름이어도 파라미터가 다르면 여러 개 선언할 수 있다. 이렇게 말이다.

public class Person{
    // ...

    public Person() {
    }

    public Person(int age) {
        this.age = age;
    }

    public Person(String name) {
        this.name = name;
    }

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }

    // ...
}

이렇게 할 수 있다는 것이다. 이번엔 여러 생성자를 한 번에 다 써보자.

Person p1 = new Person();
Person p2 = new Person(15);
Person p3 = new Person("test");
Person p4 = new Person(20, "test2");

p1.greeting(); // "안녕하세요 나는 null이고 0살입니다." 출력
p2.greeting(); // "안녕하세요 나는 null이고 15살입니다." 출력
p3.greeting(); // "안녕하세요 나는 test이고 0살입니다." 출력
p4.greeting(); // "안녕하세요 나는 test2이고 20살입니다." 출력

그리고 한 가지 더! 생성자에서 this를 조금 다르게 쓸 수 있다. 바로 this()로 다른 생성자를 호출하는 것인데, 코드를 다음과 같이 변경해보자.

public class Person{
    // ...

    public Person() {
        this(0);
    }

    public Person(int age) {
        this(age, "test");
    }

    public Person(String name) {
        this(15, "test2")
    }

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }

    // ...
}

그 후 아까 코드를 실행해보자. 한 번 아래 코드의 결과를 예상해보는 것도 좋을 것이다.

Person p1 = new Person();
Person p2 = new Person(15);
Person p3 = new Person("test");
Person p4 = new Person(20, "test2");

p1.greeting(); // "안녕하세요 나는 test이고 0살입니다." 출력
p2.greeting(); // "안녕하세요 나는 test이고 15살입니다." 출력
p3.greeting(); // "안녕하세요 나는 test2이고 15살입니다." 출력
p4.greeting(); // "안녕하세요 나는 test2이고 20살입니다." 출력

무슨 일이 벌어진 것일까? 먼저 2번째 3번째 생성자들은, 4번째 생성자를 호출하게 된다. 두 번째 생성자는 처음에 파라미터 age에 15이 할당되고 4번째 생성자를 age=15, name="test"로 파라미터로 전달하여 객체를 초기화한다. 세번째 생성자도 동작 방식은 비슷하다.

첫 번째 생성자는 먼저 두 번째 생성자를 호출한다. 이 때 age=0으로 전달한다. 그 후 두 번째 생성자에서 age=0, name="test"를 네 번째 생성자로 전달하여 객체를 초기화한다.

과제(추후에..)

728x90
반응형