본문 바로가기
Java

[Java] 클래스

by Hindsight.. 2021. 1. 4.

 

백기선님이 진행하시는 Live Study 5주차 과제입니다.

github.com/whiteship/live-study/issues/5

 

5주차 과제: 클래스 · Issue #5 · whiteship/live-study

목표 자바의 Class에 대해 학습하세요. 학습할 것 (필수) 클래스 정의하는 방법 객체 만드는 방법 (new 키워드 이해하기) 메소드 정의하는 방법 생성자 정의하는 방법 this 키워드 이해하기 마감일시

github.com

 

 

1. 클래스 정의하는 방법

class NeNeOrder{ //네네 치킨 주문서
	private int chicken; // 치킨 마리수
    private int ddongZip; // 똥집 개수
    private int liveBeer;// 생맥주 개수
    
    public NeNeOrder(){} // 디폴트 생성자, 생성자를 오버로드 해서 정의했을 경우 디폴트 생성자를 명시적으로 표시해주어야한다.
    
    public NeNeOrder(int chicken, int ddongZip, int liveBeer){ // 생성자
    	this.chicken = chicken;
        this.ddongZip = ddongZip;
        this.liveBeer = liveBeer;
    }
    
    @Override// 객체변수를 출력해주는 toString함수를 오버라이드해 커스텀
    public void toString(int chicken, int ddongZip, int liveBeer){
    	return "치킨 : " + chicken + " 똥집 : " + ddongZip + " 생맥주 : " + liveBeer;
    }
    
    public void AddMenu(int chicken, int ddongZip, int liveBeer){ // 클래스함수 1
    	this.chicken += chicken;
        this.ddongZip += ddongZip;
        this.liveBeer += liveBeer;
    }
    
    public void CancelMenu(int chicken, int ddongZip, int liveBeer){ // 클래스함수 2
    	int chickenTmp = this.chicken - chicken;
        int ddongZipTmp = this.ddongZip - ddongZip;
        int liveBeer = this.liveBeer - liveBeer;
        
        if(chickenTmp < 0 || ddongZipTmp < 0 || liveBeer < 0){
        	System.out.println("취소하려는 개수가 주문 개수를 초과했습니다!");
            return;
        }
    	this.chicken = chickenTmp;
        this.ddongZip = ddongZipTmp;
        this.liveBeer =  liveBeerTmp;
    }
    
    public int CalcOrder(){ // 클래스 함수 3
    	int price;
        price += chicken * 17000;
        price += ddonZip * 10000;
        price += livebeer * 3000;
        
    	return price; 
    }
    
}

네네치킨집의 가상 주문서를 클래스로 표현해보았다. 이 주문서는 정해진 틀은 같지만, 각 테이블의 주문에 따라 치킨, 똥집, 생맥주 수가 달라진다. 하지만, 주문서를 통해 할수 있는 행동들 즉, 함수들은 동일하다. 메뉴 추가, 메뉴 취소, 메뉴 출력, 메뉴 계산 이 함수들은 모든 주문서에서 실행할 수 있지만, 각 주문서의 변수에 따라 결과값이 달라진다. ( 내가 가장 와닿게 이해할 수 있던 클래스의 예.. )

 

주문서 틀 == 클래스 , 테이블에 배포된 주문서 == 객체

 

클래스 내에는 크게 멤버변수, 생성자, 메소드(사용자 정의함수)가 있다.

 

멤버변수 : 클래스 내에서 사용되는 변수로, 생성되는 객체 별로 독립된 값을 가진다. 테이블의 주문서마다 주문하는 치킨의 수가 다른 것으로 이해한다.

생성자 : 클래스를 기반으로 객체를 생성하기위해 초기 변수 등을 초기화하는 함수이다. 주문을 받는 서버(서빙 하시는분 ..)가 주문을 받아 가장 처음 테이블에 배포되는 주문서를 작성하는 함수라고 생각하면 편하다. ( 번외로 생성자는 함수와 별개로 구분된다 )

메소드(사용자 정의 함수) : 사장님이 주문서에서 파생되는 행동들을 미리 정의해둔 것이다. 메뉴 추가, 취소, 출력, 계산 등이 있다. 사장님에 따라 미리 적절한 함수들을 정의해놓을 수 있다. 

 

접근지정자

  클래스 내부 동일 패키지 하위 클래스 그 외
public O O O O
private O O O X
default (생략 가능) O O X X
protected O X X Xp

클래스와 메소드 앞에 붙일 수 있는 접근 지정자이다. 각각 접근 여부를 나타내고, 설계 및 기능에 따라 적절한 접근 지정자를 지정한다.

 

예를 들어, 클래스의 필드 변수는 해당 클래스와 관련없는 패키지 또는 클래스에서 내부 변수에 접근해 임의로 조작할 수 없도록 private을 지정한다.

 

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

 

NeNeOrder table1 = new NeNeOrder(1, 1, 4);
// 치킨 1, 똥집 1, 생맥 4
NeNeOrder table2 = new NeNeOrder(2, 0, 2);
// 치킨 2, 똥집 0, 생맥 2
NeNeOrder table3 = new NeNeOrder();
// 후에 일행이 도착하면 주문하는 테이블이다. 여기서 default 생성자를 코드에 작성하지 않았을 경우 오류가 출력된다.
// 생성자는 오버로딩 시 디폴트 생성자가 사라지기 때문이다.

 

3. 메소드 정의하는 방법

class NeNeOrder{
	
    public int AddOrder(int Chicken){}
    //접근 지정자 , 출력 타입, 메소드 이름, 파라미터, 메소드 내용
    private void PrintOrder(int chicken){}
    
}

 

4. 생성자 정의하는 방법

class NeNeOrder {} // 기본 묵시적 생성자가 컴파일러에 의해 실행됨


class NeNeOrder{

	public NeNeOrder(){} // 파라미터가 없는 묵시적 생성자

}

class NeNeOrder {
	private int chicken;
    
	public NeNeOrder(int chicken){ // 파라미터가 있는 명시적 생성자
    	this.chicken = chicken;
    }
}
//명시적 생성자만이 구현되어있는데, 묵시적 생성자를 사용할 경우 Error

 

가장 하단의 명시적 생성자만이 생성되어 있는 클래스의 경우, 명시적 생성자가 생성되어있으면 묵시적 생성자는 컴파일러에 의해 추가되지 않음으로 코드 작성자가 직접 작성해 주어야 한다.

 

class NeNeOrder {
	private int chicken;
    
    public NeNeOrder() {} // Good
    
	public NeNeOrder(int chicken){ // 파라미터가 있는 명시적 생성자
    	this.chicken = chicken;
    }
}

 

생성자와 함수와 다르다고 한 점은 

리턴 타입을 갖지 않음

클래스 이름과 동일

생성자를 항상 갖고 있음 ( 명시하지 않을시 컴파일러가 묵시적 생성자 삽입 )

등 이 있다.

 

5. this 키워드 이해하기

class NeNeOrder {
	private int chicken;
    
    public NeNeOrder() {} // Good
    
	public NeNeOrder(int chicken){ // 파라미터가 있는 명시적 생성자
    	this.chicken = chicken;
    }
}

 

위의 많은 예제들에서 this 키워드를 사용해 변수를 할당많았다.

this는 객체화된 클래스에서의 자신의 메모리 주소를 가르키기 때문에 위의 this.chicken이 객체화된 NeNeOrder 클래스의 chicken 변수를 가르키는 것이다.

또한 상속화된 클래스에서 부모와 자식클래스간의 함수사용에서 this를 혼동할 수 있으므로 실행하는 주체 자기 자신을 가르킨다는 것을 주의하자.

 

과제 (Optional)

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

Node.java

public class Node {
    int value;
    Node left;
    Node right;

    public Node(){}

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

BinaryTree.java

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

public class BinaryTree {
    Node root;

    BinaryTree(Node root){
        this.root = root;
    }

    public void dfs(Node node){
        if(node == null) return;
        dfs(node.left);
        System.out.print(node.value + " ");
        dfs(node.right);
    }

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

        while(!q.isEmpty()){
            Node n = q.poll();
            if(n == null) continue;
            System.out.print(n.value + " ");
            q.add(n.left);
            q.add(n.right);
        }

    }
}

 

BinaryTreeTest.java ( 테스트 코드 참고 www.notion.so/Live-Study-5-75f857b63e524d33914a8b3ec6e1e894 )

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class BinaryTreeTest {

    static Node root;
    static BinaryTree binaryTree;

    @BeforeEach
    void setUp() {
        Node node10 = new Node(10,null,null);
        Node node9 = new Node(9,null,null);
        Node node8 = new Node(8,node10, null);
        Node node7 = new Node(7,null, node9);
        Node node6 = new Node(6,node8, null);
        Node node5 = new Node(5,null,null);
        Node node4= new Node(4, node7, null);
        Node node3 = new Node(3,node5,node6);
        Node node2 = new Node(2,node4, null);
        Node node1 = new Node(1,node2,node3);

        binaryTree = new BinaryTree(node1);
        root = binaryTree.root;
    }

    @Test
    @DisplayName("root 체크")
    void getRoot() {
        Assertions.assertEquals(1,binaryTree.root.value);
    }

    @Test
    @DisplayName("dfs,  좌 -> 루트 -> 우")
   void dfs() {
        System.out.println(7+" "+9+" "+4+" "+2+" "+1+" "+5+" "+3+" "+10+" "+8+" "+6);
        binaryTree.dfs(root);
    }

    @Test
    @DisplayName("bfs 왼쪽부터 순회")
    void bfs() {
        System.out.println(1+" "+2+" "+3+" "+4+" "+5+" "+6+" "+7+" "+8+" "+9+" "+10);
        binaryTree.bfs(root);
    }
}

 

'Java' 카테고리의 다른 글

[Java] 패키지  (0) 2021.02.27
[Java] 상속  (0) 2021.02.21
[Java] 제어문, Queue, Stack, LinkedList  (0) 2020.12.23
[Java] 연산자  (0) 2020.12.20
[Java] 자바 데이터 타입, 변수 그리고 배열  (0) 2020.12.19