ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Study] 자바 5주차 과제
    JAVA 2021. 1. 10. 20:06
    반응형

    자바 5주차 과제입니다.

     


     

    클래스 정의하는 방법

     

    객체 지향 프로그래밍(OOP, Object-Oriented Programming)

    객체 지향 프로그래밍에서는 모든 데이터를 객체(object)로 취급하며, 이러한 객체가 바로 프로그래밍의 중심이 됩니다.

    객체란 소프트웨어 세계에서 구현할 대상이며, 이 때 객체를 만들어 내기 위한 설계도와 같은 개념을 클래스(class)라고 합니다.

     

    클래스(class)

    자바에서 클래스(class)란 객체를 정의하는 틀 또는 설계도와 같은 의미로 사용됩니다.

    자바에서는 이러한 설계도인 클래스를 가지고, 여러 객체를 생성하여 사용하게 됩니다.

    그렇다면 클래스는 어떻게 정의할까요? 먼저, 클래스의 구조를 살펴봅시다.

     

    • 필드(field) - 필드는 해당 클래스 객체의 상태 속성을 나타내며, 멤버 변수라고 불립니다. 여기서 초기화하는 것을 필드 초기화 또는 명시적 초기화라고 합니다.

      인스턴스 변수 - 인스턴스가 갖는 변수로 인스턴스를 생성할 때 만들어집니다. heap 영역에 할당되고 garbage collector에 의해 관리됩니다.

      클래스 변수 - static 키워드가 앞에 붙은 인스턴스 변수로 해당 클래스에서 파생된 모든 인스턴스가 이 변수를 공유합니다. 그렇기에 heap 영역이 아닌 static 영역(class 영역)에 할당되며 garbage collector의 관리를 받지 않습니다.

    • 메소드(method) - 메소드는 해당 객체의 동작, 행위를 나타내며, 보통 필드의 값을 조정하는데 쓰입니다.

      인스턴스 메소드 - 인스턴스 변수와 연관된 작업을 하는 메소드입니다. 인스턴스를 통해 호출할 수 있으므로 반드시 먼저 인스턴스가 생성이 되어야 합니다.

      클래스 메소드 - 정적 메소드라고 부르며 static이 앞에 붙어있는 메소드입니다. 인스턴스를 생성하지 않아도 클래스 이름을 통해 호출이 가능합니다. 그렇기에 일반적으로 인스턴스와 관계없는 메소드를 클래스 메소드로 정의합니다.


    • 생성자(constructor) - 생성자는 객체가 생성된 직후에 객체 내 변수를 초기화하는데 사용되는 코드 블록입니다.

    • 초기화 블록(initializer) - 초기화 블록 내에서는 조건문, 반복문 등을 사용하여 명시적 초기화에서 불가능한 초기화를 수행할 수 있습니다.

      클래스 초기화 블록 - 클래스 변수 초기화에 쓰입니다.
      인스턴스 초기화 블록 - 인스턴스 변수 초기화에 쓰입니다.

      클래스 변수 초기화: 기본값 -> 명시적 초기화 -> 클래스 초기화 블록
      인스턴스 변수 초기화: 기본값 -> 명시적 초기화 -> 인스턴스 초기화 블록 -> 생성자 
    public class MyClass {
        static String classVariable; //클래스 변수
        String instanceVariable; //인스턴스 변수
    
        static {
            System.out.println("클래스 초기화 블록");
        }
    
        {
            System.out.println("인스턴스 초기화 블록");
        }
        
        MyClass() {
            System.out.println("생성자");
        }
    
        static void classMethod() {
            //클래스 메소드
            // ...
        }
    
        void instanceMethod() {
            //인스턴스 메소드
            // ...
        }
    }
    

     

     

    제어자(modifier)

    제어자(modifier)란 클래스와 클래스 멤버의 선언 시 사용하여 부가적인 의미를 부여하는 키워드를 의미합니다.

    자바에서 제어자는 접근 제어자(access modifier)와 기타 제어자로 구분할 수 있습니다.

     

    기타 제어자는 경우에 따라 여러 개를 함께 사용할 수도 있지만, 접근 제어자를 두 개 이상 같이 사용할 수는 없습니다.

    이러한 접근 제어자와 기타 제어자는 조합에 따라 함께 사용할 수 있습니다.

     

    • 접근 제어자 - 접근 제어자는 해당 클래스 또는 멤버를 정해진 범위에서만 접근할 수 있도록 통제하는 역할을 합니다. 클래스는 public과 default 밖에 쓸 수 없습니다. 범위는 다음과 같습니다. 참고로 default는 아무것도 덧붙이지 않았을 때를 의미합니다.

    출처 : http://www.tcpschool.com/java/java_modifier_accessModifier

    • final - 자바에서 final 제어자는 '변경할 수 없다'는 의미로 사용됩니다. 즉, 필드나 지역 변수에 사용하면 값을 변경할 수 없는 상수(constant)가 됩니다. 또한, 클래스에 사용하면 해당 클래스는 다른 클래스가 상속받을 수 없게 됩니다. 메소드에 사용하면 해당 메소드는 오버라이딩을 통한 재정의를 할 수 없게 됩니다.
    • static - 자바에서 static 제어자는 '공통적인'이라는 의미로 사용됩니다. 즉, 변수에 사용하면 해당 변수를 클래스 변수로 만들어 줍니다. 또한, 메소드에 사용하면 해당 메소드를 클래스 메소드로 만들어 줍니다. 초기화 블록에서도 사용할 수 있습니다.

    • abstract - 자바에서 abstract 제어자는 '추상적인'이라는 의미로 사용됩니다. 선언부만 있고 구현부가 없는 메소드를 추상 메소드라 하며, 반드시 abstract 제어자를 붙여야 합니다. 또한, 하나 이상의 추상 메소드가 포함하고 있는 추상 클래스도 반드시 abstract 제어자를 붙여야 합니다.

     

     

     

     

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

    public class Main {
        public static void main(String[] args) {
            MyClass myClass1; //클래스의 객체를 참조하기 위한 참조변수를 선언
            myClass1 = new MyClass(); //클래스의 객체를 생성 후, 객체의 주소를 참조변수로 저장
            myClass1.instanceVariable = "인스턴스 변수1"; //MyClass 인스턴스의 인스턴스 변수 instaceVariable의 값을 "인스턴스 변수"로 한다.
            myClass1.instanceMethod(); //MyClass 인스턴스의 메소드 instanceMethod()를 호출한다.
            
            MyClass myClass2 = new MyClass(); //한 문장으로 생성도 가능
            myClass2.instanceVariable = "인스턴스 변수2";
        }
    }
    

     

    위의 클래스를 이용해 객체를 만들어보았습니다. 

    연산자 new 키워드는 새 객체에 메모리를 할당하고 해당 메모리에 대한 참조값을 반환하여 클래스를 인스턴스화합니다. 일반적으로 객체가 메모리에 할당되면 인스턴스라고 부릅니다.

    같은 클래스로부터 생성되었을지라도 각 인스턴스의 속성(멤버변수)은 서로 다른 값을 유지할 수 있으며, 메소드의 내용은 모든 인스턴스에 대해 동일합니다.

     

     

     

     

     

    메소드 정의하는 방법

    public int add(int x, int y) {
    	int result = x + y;
    	return result;
    }
    
    public void printInstanceVariable() {
    	System.out.println(instanceVariable);
    }

    출처 : http://www.tcpschool.com/java/java_methodConstructor_method

     

    1. 접근 제어자: 해당 메소드에 접근할 수 있는 범위를 명시합니다.

    2. 반환 타입(return type): 메소드가 모든 작업을 마치고 반환하는 데이터의 타입을 명시합니다. 단, 반환값이 없는 경우 반환타입으로 'void'를 적어야합니다.

    3. 메소드 이름: 메소드를 호출하기 위한 이름을 명시합니다.

    4. 매개변수 목록(parameters): 메소드 호출 시에 전달되는 인수의 값을 저장할 변수들을 명시합니다.

    5. 구현부: 메소드의 고유 기능을 수행하는 명령문의 집합입니다.

     

    • 메소드 시그니처(method signature)란 메소드의 선언부에 명시되는 메소드 이름과 매개변수의 리스트를 가리킵니다.
      만약 두 메소드가 매개변수의 개수와 타입, 그 순서까지 모두 같다면, 이 두 메소드의 시그니처는 같다고 할 수 있습니다.

     

     

     

     

     

    생성자 정의하는 방법

    MyClass() {}
    
    MyClass(String instanceVariable) {
    	this.instanceVariable = instanceVariable;
    }

    출처 : http://www.tcpschool.com/java/java_methodConstructor_constructor

     

    위에서 말한대로 자바에서는 객체의 생성과 동시에 인스턴스 변수를 초기화할 수 있는 생성자(constructor)라는 메소드를 제공합니다. 자바에서 생성자의 이름은 해당 클래스의 이름과 같아야 합니다.

     

    1. 생성자는 반환값이 없지만, 반환타입을 void형으로 선언하지 않습니다.

    2. 생성자는 초기화를 위한 데이터를 인수로 전달받을 수 있습니다.

    3. 객체를 초기화하는 방법이 여러 개 존재할 경우에는 하나의 클래스가 여러 개의 생성자를 가질 수 있습니다.

    즉, 생성자도 하나의 메소드이므로, 메소드 오버로딩이 가능하다는 의미입니다.

     

     

     

     

     

    this 키워드 이해하기

    MyClass(String instanceVariable) {
    	this.instanceVariable = instanceVariable; //this 참조 변수
    }
    
    MyClass() {
    	this("인스턴스 변수"); //this() 메소드
    }

     

     

    this 참조 변수

    this 참조 변수는 인스턴스가 바로 자기 자신을 참조하는 데 사용하는 변수입니다.

    이러한 this 참조 변수는 해당 인스턴스의 주소를 가리키고 있습니다.

    위의 예제처럼 생성자의 매개변수 이름과 인스턴스 변수의 이름이 같을 경우에는 인스턴스 변수 앞에 this 키워드를 붙여 구분해야만 합니다.

    당연한 말로 this 참조 변수를 사용할 수 있는 영역은 인스턴스 메소드뿐이며, 클래스 메소드에서는 사용할 수 없습니다.

     

    this() 메소드

    this() 메소드는 생성자 내부에서만 사용할 수 있으며, 같은 클래스의 다른 생성자를 호출할 때 사용합니다.

    this() 메소드에 인수를 전달하면, 생성자 중에서 메소드 시그니처가 일치하는 다른 생성자를 찾아 호출해 줍니다.

    위의 예제에서 매개변수를 가지는 첫 번째 생성자는 this 참조 변수를 사용하여 인스턴스 변수에 접근하고 있습니다.

    또한, 매개변수를 가지지 않는 두 번째 생성자는 내부에서 this() 메소드를 이용하여 첫 번째 생성자를 호출합니다.

    이렇게 내부적으로 다른 생성자를 호출하여 인스턴스 변수를 초기화할 수 있습니다.

    단, 한 생성자에서 다른 생성자를 호출할 때에는 반드시 해당 생성자의 첫 줄에서만 호출할 수 있습니다.

     

     

     

    이진 트리

     

    출처 : https://wonit.tistory.com/199

     

     

    이진 트리(Binary tree)란 자식노드가 최대 두 개인 노드들로 구성된 트리를 말합니다.

    이진 트리에는 루트 이진 트리, 정 이진 트리, 포화 이진 트리 등이 있습니다.

     

    이진 트리에 있는 데이터를 사용하기 위해서는 노드들을 탐색하는 방법이 필요합니다. 이렇게 노드를 방문하고 어떤 작업을 하는 것을 이진 트리 탐색이라고 합니다.

     

    • in-order(중위 순회)

      왼쪽 자식노드 -> 내 노드 -> 오른쪽 자식노드
    • pre-order(전위 순회)

      내 노드 -> 왼쪽 자식노드 -> 오른쪽 자식노드

    • post-order(후위 순회)

      왼쪽 자식노드 -> 오른쪽 자식노드 -> 내 노드
    • level-order(레벨 순회)

      내 노드, 내 노드로부터 깊이 1인 노드들, 내 노드로부터 깊이 2인 노드들, ... , 내 노드로부터 깊이 N인 노드들 (N: 나(트리)의 깊이)

     

    이진 탐색 트리에는 다음과 같은 조건이 있습니다.

     

    1. 이진 탐색 트리의 자식 노드 개수는 2개 이하이다.

    2. 모든 원소는 중복된 값을 가져서는 안된다.

    3. 왼쪽 서브트리에 존재하는 노드들의 값은 그 트리의 루트 값보다 반드시 작다.

    4. 오른쪽 서브트리에 존재하는 노드들의 값은 그 트리의 루트 값보다 반드시 크다.

     

     

    Node 클래스

     

    package binarytree;
    
    public class Node {
        private int value;
        private Node left;
        private Node right;
    
        public Node(int value) {
            this.value = value;
            this.left = null;
            this.right = null;
        }
    
        public int getValue() {
            return value;
        }
    
        public void setValue(int value) {
            this.value = value;
        }
    
        public Node getLeft() {
            return left;
        }
    
        public void setLeft(Node left) {
            this.left = left;
        }
    
        public Node getRight() {
            return right;
        }
    
        public void setRight(Node right) {
            this.right = right;
        }
    }
    

     

    BinaryTree 클래스

     

    package binarytree;
    
    import java.util.*;
    
    public class BinaryTree {
        public List<Integer> dfsList; //dfs로 탐색할 때 노드 값 저장
        public List<Integer> bfsList; //bfs로 탐색할 때 노드 값 저장
        private Node root;
    
        public BinaryTree() {
            this.dfsList = new ArrayList<>();
            this.bfsList = new ArrayList<>();
            this.root = null;
        }
    
        public Node getRoot() {
            return root;
        }
    
        public void setRoot(Node root) {
            this.root = root;
        }
    
        public void insertNode(int value) {
            if (root == null) {
                setRoot(new Node(value));
            }else {
                Node tmpNode = root;
                while (true) {
                    if (tmpNode.getValue() == value) {
                        throw new IllegalArgumentException("중복된 값은 삽입할 수 없습니다.");
                    }else if (tmpNode.getValue() > value) {
                        //루트값보다 작으므로 좌측으로
                        if (tmpNode.getLeft() == null) {
                            tmpNode.setLeft(new Node(value));
                            break;
                        }else {
                            tmpNode = tmpNode.getLeft();
                        }
                    }else {
                        //루트값보다 크므로 우측으로
                        if (tmpNode.getRight() == null) {
                            tmpNode.setRight(new Node(value));
                            break;
                        }else {
                            tmpNode = tmpNode.getRight();
                        }
                    }
                }
            }
        }
    
        public void deleteNode(int value) {
            if (root == null) {
                throw new IllegalArgumentException("이진트리가 존재하지 않습니다.");
            }else {
                Node tmpNode = root; //삭제할 노드
                Node preNode = null; //삭제할 노드의 부모
                while (true) {
                    if (tmpNode.getValue() == value) {
                        break;
                    }else if(tmpNode.getValue() > value) {
                        //루트값보다 작으므로 좌측으로
                        if (tmpNode.getLeft() == null) {
                            throw new IllegalArgumentException("삭제할 값이 존재하지 않습니다.");
                        }else {
                            preNode = tmpNode;
                            tmpNode = tmpNode.getLeft();
                        }
                    }else {
                        //루트값보다 크므로 우측으로
                        if (tmpNode.getRight() == null) {
                            throw new IllegalArgumentException("삭제할 값이 존재하지 않습니다.");
                        }else {
                            preNode = tmpNode;
                            tmpNode = tmpNode.getRight();
                        }
                    }
                }
    
                if (tmpNode.getLeft() == null && tmpNode.getRight() == null) {
                    // 1) 삭제할 노드의 자식이 없는 경우
                    if (preNode.getValue() > tmpNode.getValue()) {
                        //좌측 노드 삭제
                        preNode.setLeft(null);
                    }else {
                        //우측 노드 삭제
                        preNode.setRight(null);
                    }
                }else if (tmpNode.getRight() == null) {
                    // 2) 삭제할 노드의 자식이 하나인 경우 (우측 노드가 없을 때)
                    tmpNode = tmpNode.getLeft();
                }else if (tmpNode.getLeft() == null) {
                    // 2) 삭제할 노드의 자식이 하나인 경우 (좌측 노드가 없을 때)
                    tmpNode = tmpNode.getRight();
                }else {
                    // 3) 삭제할 노드의 자식이 둘인 경우
                    //좌측 최댓값 또는 우측 최솟값을 현재 노드로 올린다.
                    //여기선 우측 최솟값으로 한다.
                    Node rightNode = tmpNode.getRight();
                    preNode = tmpNode;
                    while (true) {
                        if (rightNode.getLeft() == null) {
                            break;
                        }
                        preNode = rightNode;
                        rightNode = tmpNode.getLeft();
                    }
                }
            }
        }
    
        public void dfs(Node node) {
        	//DFS는 Depth-First-Search의 준말로 깊이를 먼저 탐색하는 방법을 뜻함.
            //in, pre, post-order 순회 방식을 선택하여 구현 가능하며 여기서는 in-order 순회 방식으로 구현
            if (node == null) {
                throw new NoSuchElementException("노드가 존재하지 않습니다.");
            }
            //좌측 노드 순회
            Node left = node.getLeft();
            if (left != null) {
                dfs(left);
            }
            dfsList.add(node.getValue());
            //우측 노드 순회
            Node right = node.getRight();
            if (right != null) {
                dfs(right);
            }
        }
    
        public void bfs(Node node) {
        	//BFS는 Breadth-First-Search의 준말로 너비를 우선적으로 탐색하는 방법을 뜻함.
            //level-order와 같은 순회 방식이며 Queue를 이용하여 구현
            if (node == null) {
                throw new NoSuchElementException("노드가 존재하지 않습니다.");
            }
            Queue<Node> queue = new LinkedList<>();
            queue.add(node);
            while (!queue.isEmpty()) {
                Node currentNode = queue.poll();
                Node left = currentNode.getLeft();
                Node right = currentNode.getRight();
                bfsList.add(currentNode.getValue());
                if (left != null) {
                    queue.add(left);
                }
                if (right != null) {
                    queue.add(right);
                }
            }
        }
    }
    

     

     

    Test

     

    import binarytree.BinaryTree;
    
    import java.util.HashMap;
    import java.util.LinkedList;
    import java.util.Queue;
    import org.junit.jupiter.api.*;
    import static org.junit.jupiter.api.Assertions.*;
    import java.util.*;
    
    public class BinaryTree_Test {
    	static BinaryTree binaryTree;
        
        @BeforeAll
        static void createBinaryTree() {
            binaryTree = new BinaryTree();
            binaryTree.insertNode(7);
            binaryTree.insertNode(2);
            binaryTree.insertNode(9);
            binaryTree.insertNode(1);
            binaryTree.insertNode(5);
            binaryTree.insertNode(14);
        }
        
        @Test
        void dfs() {
            binaryTree.dfs(binaryTree.getRoot());
            List<Integer> answer = Arrays.asList(1, 2, 5, 7, 9, 14);
            assertArrayEquals(answer.toArray(), binaryTree.dfsList.toArray());
        }
    
        @Test
        void bfs() {
            binaryTree.bfs(binaryTree.getRoot());
            List<Integer> answer = Arrays.asList(7, 2, 9, 1, 5, 14);
            assertArrayEquals(answer.toArray(), binaryTree.bfsList.toArray());
        }
    }
    

     

     

    References

     

    - jeeneee.dev/java-live-study/week5-class/

    - www.tcpschool.com/java/java_class_intro

    - www.tcpschool.com/java/java_modifier_ectModifier

    - github.com/jongnan/Java_Study_With_Whiteship/blob/master/week5/week5_1.md

    - blog.naver.com/swoh1227/222175350122

    - http://www.tcpschool.com/java/java_methodConstructor_method

    - http://www.tcpschool.com/java/java_methodConstructor_constructor

    - 자바의 정석 (남궁 성 지음)

    728x90

    'JAVA' 카테고리의 다른 글

    [Study] 자바 7주차 과제  (0) 2021.01.18
    [Study] 자바 6주차 과제  (0) 2021.01.15
    [Study] 자바 4주차 과제  (1) 2020.12.08
    [Study] 자바 3주차 과제  (0) 2020.11.28
    [Study] 자바 2주차 과제  (0) 2020.11.21

    댓글

Designed by Tistory.