백기선 자바 스터디 1기 5주차

2022. 5. 11. 15:43Java/java-live-study

728x90
반응형

목표

자바의 Class에 대해 학습하세요.

학습할 것

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

과제

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

클래스 정의하는 방법

객체 지향 프로그래밍(OOP, Object-Oriented Programming)에서는 모든 데이터를 객체로 취급한다.

 

객체란 간단히 이야기하면 실생활에서 우리가 인식할 수 있는 사물로 설명할 수 있다.

이러한 객체의 상태(state)와 행동(behavior)을 구체화하는 형태의 프로그래밍이 객체 지향 프로그래밍이다.

 

이때 객체를 만들어 내기 위한 틀과 같은 개념을 클래스(class)라고 한다. 클래스에서 객체의 상태를 나타내는 변수를 필드(field)라고 하고 행동을 나타내는 함수를 메소드(method)라고 부른다.

public class Person {
    
    //필드
    int age;
    String name;

    //메소드
    public String getName(){
        return name;
    }
}

자바에서는 클래스를 정의하기 class 키워드를 사용하며, 외부 클래스가 해당 클래스에 접근하는 범위를 접근 지정자를 통해 제한할 수 있다.

접근 지정자는 public, protected, default, private 네 가지가 있다.

접근 지정자 클래스 내부 동일 패키지 하위 클래스 그 외 영역
public o o o o
protected o o o x
default(기본, 생략가능) o o x x
private o x x x

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

클래스를 정의했다면 클래스로부터 객체를 만들어 사용한다.

 

객체는 new 키워드를 이용해 생성이 가능하다. new는 세가지 일을 하는데 

  1. Heap 영역에 메모리 할당
  2. 생성자 호출
  3. 타입 캐스팅 후 주소값 반환

이렇게 세가지 과정을 거쳐 객체가 생성된다.

public class Person {

    int age;
    String name;

    public String getName(){
        return name;
    }

    public static void main(String[] args) {
        Person person = new Person(); //객체 생성
    }
}

메소드 정의하는 방법

클래스 내부의 메소드는 접근지정자, 리턴 타입, 메소드명, 파라미터로 구성된 정의부와 메소드의 기능을 호출하는 호출부로 구성된다.

public class Person {

    int age;
    String name;

    //정의부
    public String getName(){
        //구현부
        return name;
    }
}

메소드명이 같은 메소드가 여러개일 수 있는데, 메소드는 파라미터가 다르다면 같은 메소드명으로도 정의가 가능하다. 이를 오버로딩(Overloading)이라고 한다.

public class OverloadingEX {
    
    public int plus(int num1, int num2) {
        return num1 + num2;
    }
    
    //파라미터가 다르다면 같은 메소드명으로 선언가능하다.
    public double plus(double num1, double num2) {
        return num1 + num2;
    }
}

또한, 자식 클래스는 부모 클래스의 메소드를 재정의할 수 있는데 상속받은 부모 클래스가 정의한 메소드를 자식 클래스가 가져와 변경하거나 확장할 수 있다. 이를 오버라이딩(Overriding)이라고 한다.

public class Person {
    
    public void talk() {
        System.out.println("사람입니다.");
    }
}

class Man extends Person {
    
    @Override
    public void talk() {
        System.out.println("남자입니다.");;
    }
}

class Woman extends Person {
    
    @Override
    public void talk() {
        System.out.println("여자입니다.");;
    }
}

메소드 오버라이딩은 부모 클래스의 메소드를 자식 클래스에서 메소드를 재정의하기 때문에 확장과 변경에 용이하다는 장점이 있다.(다형성)


생성자 정의하는 방법

생성자는 new 연산자를 통해서 객체를 생성할 때 반드시 호출 되고 실행된다. 생성자는 필드 값을 초기화하는 역할을 한다.

 

생성자 접근지정자, 클래스명, 파라미터로 정의 가능하다.

public class ConstructorEX {
    
    int field;
    
    //기본 생성자 - 정의된 생성자가 없다면 자동으로 생성된다.
    public ConstructorEX () {
        
    }
   
    //파라미터로 필드 값을 초기화 할 수 있다.
    public ConstructorEX (int field) {
        this.field = field;
    }
}

this 키워드 이해하기

this 키워드는 클래스가 인스턴스화 되었을때 자기 자신의 메모리 주소를 가지고 있다.

 

생성자나 메소드를 통해 넘어온 파라미터의 변수명이 필드 변수명과 동일한 경우 this 키워드를 사용해 필드 변수와 매개변수를 구분해준다.

public class ThisEX {
    
    int field;
    
    public ThisEX() {
    }
    
    public ThisEX(int field) {
        this.field = field;
    }
    
    public void setField(int field) {
        this.field = field;
    }
}

 

this()는 클래스 내부에서 생성자를 호출한다.

 

this()는 "생성자"의 첫 번째 문장에서 호출되어야한다.

public class ThisEX {

    int field;

    public ThisEX() {
    }

    public ThisEX(int field) {
        this();
        this.field = field;
    }
}

과제

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

Node 클래스

public class Node {

    int value;
    Node left, right;

    public Node(int value) {
        this.value = value;
    }

    public void addLeft(Node node) {
        left = node;
    }

    public void addRight(Node node) {
        right = node;
    }
}

 

BinaryTree 클래스

import java.util.LinkedList;
import java.util.Queue;

public class BinaryTree {

    public static void bfs(Node node) {
        Queue<Node> q = new LinkedList<>();
        q.add(node);

        while (!q.isEmpty()) {
            Node cur = q.poll();
            System.out.print(cur.value);

            if (cur.left != null)
                q.add(cur.left);

            if (cur.right != null)
                q.add(cur.right);
        }
    }

    public static void dfs(Node node) {

        if (node.left != null) {
            dfs(node.left);
        }

        System.out.print(node.value);

        if (node.right != null) {
            dfs(node.right);
        }
    }
}

 

테스트

import org.junit.jupiter.api.*;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;

import static org.junit.jupiter.api.Assertions.assertEquals;


class BinaryTreeTest {

    final ByteArrayOutputStream outputStreamCaptor = new ByteArrayOutputStream();
    final PrintStream standardOut = System.out;

    @BeforeEach
    void setUpStream() {
        System.setOut(new PrintStream(outputStreamCaptor));
    }

    @AfterEach
    void restoreStreams() {
        System.setOut(standardOut);
    }

    @Test
    void bfs() {
        Node node1 = new Node(1);
        Node node2 = new Node(2);
        Node node3 = new Node(3);
        Node node4 = new Node(4);

        node1.addLeft(node2);
        node1.addRight(node3);
        node2.addRight(node4);

        BinaryTree.bfs(node1);

        assertEquals("1234", outputStreamCaptor.toString());
    }

    @Test
    void dfs() {
        Node node1 = new Node(1);
        Node node2 = new Node(2);
        Node node3 = new Node(3);
        Node node4 = new Node(4);

        node1.addLeft(node2);
        node1.addRight(node3);
        node2.addRight(node4);

        BinaryTree.dfs(node1);

        assertEquals("2413", outputStreamCaptor.toString());
    }
}
728x90
반응형