클래스
- Java
- 2021년 2월 1일
객체 지향의 중심 객체를 정의하는 일종의 틀 같은 것으로 상태(변수) / 행동(메서드)이 있을 수 있다.
이로 인해 추상화, 캡슐화, 상속, 다형성 등을 가능하게 하여 개발 / 유지보수를 쉽게 해준다.
1. 클래스 정의하는 방법
class키워드를 통해 클래스를 새로 정의할 수 있다.
class Fruit{
private String name;
Fruit(String name){this.name = name;}
public String getName(){
return name;
}
}
1) 필드
변수를 뜻하며 두가지 종류가 존재한다.
- 인스턴스 변수 : Fruit이라는 객체를 새로 생성할때마다 각각 독립적인 값을 갖는 변수로 heap 영역에 저장 된다.
- 클래스 변수 : 인스턴스 변수 앞에 static키워드를 붙여 선언한 변수이다. 딱 한번만 선언되는 변수로 static 영역에 할당되어 Fruit이라는 객체는 이 변수를 모두 공유한다.
2) 메서드
어떠한 행동을 정의한 형태로, 필드들을 수정하거나 참조, 반환을 할 수 있다.
3) 생성자
객체를 생성했을때 사용되는 일종의 메서드로 필드들을 초기화하는데 사용된다.
4) 접근 제어자
객체 내 정의된 필드, 메서드, 생성자들을 외부에서 접근을 제어하기 위한 키워드로 아래와 같이 4가지가 존재한다.
default는 생략이 가능하다.
접근 제어자 | 동일 클래스 | 동일 패키지 | 자손 클래스 | 그 외 |
---|---|---|---|---|
public | 접근 가능 | 접근가능 | 접근가능 | 접근가능 |
protected | 접근가능 | 접근가능 | 접근가능 | X |
default | 접근가능 | 접근가능 | X | X |
private | 접근가능 | X | X | X |
5) 키워드 종류
필드, 메서드, 클래스 앞에 붙을수 있는 키워드들로 해당 키워드의 종류에 따라 기능이나 권한이 달라진다.
키워드 | 목적 |
---|---|
static | 메모리 할당을 한번만 한다. (클래스에 속한다.) |
final | 클래스에 붙을 경우 상속이 불가능하고, 필드/메서드에 붙을 경우 상수로써 수정이 불가능하다. |
abstract | 클래스 붙을 경우 객체 생성이 불가능하고, 이를 상속받아서만 이용이 가능하다. ( 메서드도 추상 클래스에서만 붙을 수 있고 선언만 가능하다.) |
synchronized | 한개의 스레드만 접근이 가능하다. |
volatile | 변수 접근을 캐시가 아닌 메모리로 접근한다. |
transient | 객체 직렬화시 무시한다. |
2. 객체 만드는 방법
public class ClassExample{
public static void main(String[] args){
Fruit apple = new Fruit("apple");
}
}
new
키워드를 이용하여 클래스의 생성자중 적절한 생성자로 객체를 만들어(heap 영역
에 할당) 그 주소를 클래스 변수에 저장하는 방식으로 생성이 가능하다.
이 과정을 인스턴스화
한다고 하고 이 클래스 변수(객체)를 인스턴스
라고 한다.
3. 메소드 정의하는 방법
public String getName(){
return name;
}
위 와 같이 접근 제어자 / (키워드) / 반환 타입 / 메서드 이름 / 매개변수로 선언 할 수 있고 코드 블럭( {} )를 통해 행동을 정의, 구현 할 수 있다.
- 접근 제어자 : 위에서 설명한 접근제어자 4가지 중 한개가 올 수 있다.
- 키워드 : 이도 마찬가지로 위에서 설명한 키워드중 여러개가 올 수 있으며 없을 수도 있다.
- 반환 타입 : 함수의 구현부가 종료될때 반환하는 값의 타입을 지정해야 한다.
- 메서드 이름 : 메서드를 구분하는 이름
- 매개변수 : 함수에 사용할 매개변수를 정의 할 수 있으며 없을 수도 있고 여러개가 올수도 있다.
4. 생성자 정의하는 방법
class Fruit{
private String name;
//Fruit(){}
Fruit(String name){this.name = name;}
}
반환타입을 빼고 클래스명을 이름으로한 메서드같이 정의할 수 있다.
이를 정의 하지 않는다면 주석과 같은 기본생성자를 컴파일러가 자동으로 생성해주지만, 매개변수가 있는 타입만 정의시 매개변수가 없는 기본생성자를 자동으로 생성해주지 않으니 기본생성자도 직접 생성해주어야한다.
5. this 키워드 이해하기
class Fruit{
private String name;
private int price;
Fruit(){}
Fruit(String name){this.name = name;}
Fruit(String name, int price){
this(name);
this.price = price;
}
public String getName(){
return name;
}
public static exStaticMethod(){
System.out.println(this.name); //error
}
}
this는 인스턴스된 객체 자기 자신을 가르키는 키워드로 Fruit이라는 생성자를 보면 name으로 변수명이 중복되지만 this키워드를 통해 객체내의 인스턴스 변수와 지역변수를 구분이 가능하게 한다.
이런 이유로 static 메서드에서 this를 통한 접근은 불가능하다.
static으로 선언하면 클래스에 종속되기 때문에 인스턴스를 갖지 않기 때문이다.
this()를 통해 클래스내의 생성자를 호출할 수도 있다.
6. 과제
Binary Tree와 BFS, DFS 구현
- int value, Node left, right를 가지고 있어야 합니다.
- BinrayTree라는 클래스를 정의하고 주어진 노드를 기준으로 출력하는 bfs(Node node)와 dfs(Node node) 메소드를 구현하세요.
- DFS는 왼쪽, 루트, 오른쪽 순으로 순회하세요.
Node.java
package javaStudy.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.java
package javaStudy.BinaryTree;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
public class BinaryTree{
private Node root;
private List<Integer> bfsList;
private List<Integer> dfsList;
public BinaryTree(Node root) {
this.root = root;
this.bfsList = new ArrayList<>();
this.dfsList = new ArrayList<>();
}
public void bfs(Node node){
Queue<Node> q = new LinkedList<>();
q.offer(node);
while(!q.isEmpty()){
Node n = q.poll();
bfsList.add(n.getValue());
if(n.getLeft() != null){
q.offer(n.getLeft());
}
if(n.getRight() != null){
q.offer(n.getRight());
}
}
}
public void dfs(Node node){
if(node == null) return;
dfsList.add(node.getValue());
dfs(node.getLeft());
dfs(node.getRight());
}
public void addNode(int value){
Node node = root;
Node newNode = new Node(value);
if(node == null){
root = newNode;
return;
}
Node p = node;
while(node != null){
p = node;
node = (node.getValue() > value) ? node.getLeft() : node.getRight();
}
if(p.getValue() > value){
p.setLeft(newNode);
}else{
p.setRight(newNode);
}
}
public List<Integer> getBfsList() {
return bfsList;
}
public List<Integer> getDfsList() {
return dfsList;
}
}
BinaryTreeTest.java
package test.BinaryTree;
import javaStudy.BinaryTree.BinaryTree;
import javaStudy.BinaryTree.Node;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.List;
public class BinaryTreeTest {
BinaryTree binaryTree;
Node root;
@BeforeEach
void init(){
root = new Node(2);
binaryTree = new BinaryTree(root);
binaryTree.addNode(1);
binaryTree.addNode(22);
binaryTree.addNode(12);
binaryTree.addNode(23);
binaryTree.addNode(24);
binaryTree.addNode(13);
binaryTree.addNode(5);
}
@Test
void bfsTest(){
binaryTree.bfs(root);
List<Integer> arrayList = Arrays.asList(2,1,22,12,23,5,13,24);
Assertions.assertEquals(arrayList,binaryTree.getBfsList());
}
@Test
void dfsTest(){
binaryTree.dfs(root);
List<Integer> arrayList = Arrays.asList(2,1,22,12,5,13,23,24);
Assertions.assertEquals(arrayList,binaryTree.getDfsList());
}
}