'전체 글'에 해당되는 글 203건

  1. 2015.07.01 우선순위 큐 - 힙(Heap)
  2. 2015.07.01 트리(Tree)
  3. 2015.07.01 위상정렬
  4. 2015.06.30 자바가 확장한 객체지향
  5. 2015.06.30 자바와 객체지향

스택이나 큐는 시간에 우선순위를 두어, 후입선출(Last-In-First-Out), 선입선출(First-In-First-Out)의 방법을 사용한다.


하지만, 예를 들어, 응급실에 실려오는 환자들을 생각해보자. 단순히 머리가 아픈 사람들보다는 손가락이 절단된 환자가 더 우선적으로 치료받아야 될 사람이다. 이러한 우선순위를 두는 자료구조를 우선순위 큐(Priority Queue)라고 한다. 


우선순위를 매기는 기준은 당연히 여러가지가 있지만, 

학습의 의미로써 우선순위 큐를 표현 할 때는 보통 숫자 혹은 알파벳으로 표현한다. 


우선순위 큐(Priority Queue)를 배열로써 표현하거나, 연결 리스트로도 표현 할 수 있다. 

둘 다 구현 효율은 O(N)이다. 


또한, 이진탐색트리(Binary Search Tree, BST)로 구현할 때는 O(NlogN)이지만 

이 것은 균형잡혀있는 이진탐색트리라는 전제조건이 있을 때이다. 


하지만, 이제 자세히 알아 볼 우선순위 큐(Priority Queue)의 자료구조는 

힙(Heap)이다. 


힙은 완전이진트리로 표현되어야 하는데, 그래서 반드시 트리가 균형잡혀 있을 수 밖에 없다는 특징 때문에

시간효율을 O(NlogN)을 보장한다!!


힙의 정의

1) 항상 완전 이진트리의 모습을 지닌다. 

2) A. 빈 트리거나,

    B. 루트의 키가 왼쪽 자식, 오른쪽 자식의 키보다 크거나 같다. 

        왼쪽 자식과 오른쪽 자식 자시에는 어느 쪽 키가 크든지 상관없다.

        단, 왼쪽 자식, 오른쪽 자식을 루트로 하는 하위트리는 힙이어야 한다.


다음은 맥스 힙(Max Heap)을 나타낸 것이다. 

 



200은 왼쪽 자식인 100보다 크고, 오른쪽 자식인 80보다 크다. 

200의 왼쪽 서브 트리 역시 힙의 형태를 띈다. 200의 오른쪽 서브 트리 역시 힙의 형태를 띈다. 

- 대상 노드가 자식보다 크기만 하면 된다. 

- 약한 정렬 형태를 가진 것을 알 수 있다.

- 완전 이진 트리 형태를 반드시 띄어야 하므로 배열로 표현하는 것이 유리하다.


우선순위의 기준?

맥스 힙(Max Heap), 민 힙(Min Heap)으로 나눌 수 있다. 

루트노드가 자식노드들보다 큰 형태로 힙이 만들어지는 것을 맥스 힙(Max Heap)이라고 한다.

루트노드가 자식노드들보다 작은 형태로 힙이 만들어지는 것을 민 힙(Min Heap)이라고 한다.


힙의 특징?

힙의 가장 큰 특징은 완전이진트리로 표현해야 된다는 것이다. 그러므로 배열로써 자료구조를 표현한다.


위와 같은 힙이라는 트리를 배열로 표현하기에 

1) 인덱스 K에 있는 모든 왼쪽 자식은 (2K+1), 오른쪽 자식은 (2K+2)에 있다.

2) 인덱스 K에 있는 모든 부모 노드는 (K-1) / 2 에 있다.


힙의 삭제?

힙의 삭제는 우선 순위가 가장 높은 루트 노드(Root Node)가 삭제되어야 한다. 







배열로 구현하므로 24라는 노드를 할당해제 하는 식으로 삭제하는 것이 아니라 그 값을 지우고 

힙의 마지막 값인 7로 대체한다.

7을 루트노드로 옮기면 자식노드보다 커야하는 힙의 성질이 깨지게 된다. 

따라서 왼쪽 자식과

오른쪽 자식을 모두 비교해 가장 큰 것과 루트를 스와핑(Swap)해야한다.

7과 19, 7과 12를 비교  => 19가 가장 크므로 19와 7을 스왑

7과 17, 7과 10을 비교  => 17이 가장 크므로 17과 7을 스왑

7과 9를 비교 => 9가 더 크므로 9와 7을 스왑


이 처럼 힙 모습을 되찾으려면 루트로부터 시작해서 제자리 찾기까지 굴러 떨어져야 한다. => Down Heap

 과정을 거치면 힙의 형태가 완성된다. => Heap Rebuilding

힙의 삭제 과정은 O(logN)이다. 배열의 마지막 레코드를 복사하는 것이 O(1)이다. 

최악의 경우 리프노드까지 굴러 떨어져야 한다. 이 것은 logN이다. 그런데 비교때마다 왼쪽 오른쪽을 다 비교해야하므로

=> 2logN 이다. 


힙이 이진탐색트리에 비해 유리한 이유는?

트리의 높이 때문이다. 

이진탐색트리는 균형트리를 유지한다는 보장이 없지만, 힙은 반드시 균형트리를 이뤄야한다.

완전 이진트리이기 때문이다.


힙의 삽입?

힙의 삽입은 "새로 들어 온 직원을 말단의 자리에 앉히고 어디까지 올라가나 해봐라" 이렇게 말할 수 있다. 

삽입 할 값을 마지막 노드에 삽입 한 후에 아래에서부터 부모와 비교하여 제 자리를 찾아간다.





22를 마지막에 삽입한다.

부모노드(10)과 비교한다. => 22가 더 크므로 스왑한다.

부모노드(19)와 비교한다. => 22가 더 크므로 스왑한다.

힙이 리빌딩됐다!


힙의 삽입 작업은 O(logN)이다. 

최악의 경우 루트 노드까지 타고 올라가야 하므로 비교 횟수는 logN이다.

(삭제 노드는 자식 2개와 비교해야 되는데, 삽입 작업은 부모노드와 한 번만 비교하면 된다)

역시 이진탐색에 비한 장점은 균형트리를 보장하므로 logN을 보장한다는 것이다.




힙 정렬?

힙정렬은 힙의 형태를 이용해서 정렬을 하는 것이다. 

최대 힙의 부모를 삭제하고 가져 온다. => 힙 리빌딩한다.

....(반복)

그러면 내림차순으로 값이 정렬된다.


반대로 

최소 힙으로 하면 오름차순으로 값이 정렬된다. 


힙 구성(Heap Building)?

힙 정렬을 위해서는 다음과 같은 두 가지 방법이 있다.

(1) 하향식 힙 구성(Top-Down)

반복적으로 힙에 삽입 작업을 가하는 것이다.

 



하향식 힙 구성의 시간 효율은

삽입 될 때마다 logN의 시간이 걸린다. 

N개가 삽입 되므로 NlogN이 된다. 

또한 정렬을 위한 힙의 삭제 단계에서 NlogN이 걸린다.

그러므로 O(NlogN) + O(NlogN) = O(NlogN) 이 된다. 


(2) 상향식 힙 구성(Bottom-Up)

들어오는 키 값 순서대로 일단 트리를 구성한 후에 힙 구조가 되도록 재조정하는 방식이다. 

그러한 후에 가장 아래서부터 오른쪽에서 왼쪽 순으로 힙의 구조인지 검사한다. 

검사 할 때는 해당 노드와 해당 노드의 아래 쪽만 검사한다. 




** 하향식과 상향식의 비교

- 둘 다 힙의 형태를 띄지만 그 트리가 완전히 똑같지는 않다.

- 완전 이진트리의 특성상 리프노드가 전체 노드의 반 또는 반이상을 차지하게 된다. (균형되게 유지되므로)

(1) 하향식 힙 구성 방법은

노드를 아래에 삽입 한 후에 위로 올라가면서 비교하여 리빌딩한다.

그러므로 가장 많은 노드를 차지하는 리프노드들이 logN의 시간을 타고 위로 올라가게 된다.

(2) 상향식 힙 구성 방법은 

위에서 아래로 내리는 방법으로 리빌딩한다. 

가장 많은 수를 차지하는 리프노드는 그 아래 노드가 없으므로 아예 비교조차 하지 않는다. 

그 바로 위 레벨의 노드 역시 바로 아래와만 비교한다. 

결국 logN의 장거리 여행을 하는 것은 루트노드와 같이 최상단에 있는 것만 한다.


하향식 힙 구성 방법이 O(NlogN)인데 반해

상향식 힙 구성 방법은 O(N)에 가까운 효율을 보인다. 

'자료구조' 카테고리의 다른 글

해시(Hash)  (0) 2015.07.01
B-Tree  (0) 2015.07.01
트리(Tree)  (0) 2015.07.01
위상정렬  (0) 2015.07.01
자료구조 - 리스트(List)  (0) 2015.06.16
Posted by slender ankles
,

트리(Tree)

자료구조 2015. 7. 1. 01:57

트리(Tree)의 정의

: 트리는 계층적인 형태를 띄는 구조를 표현 할 때 쓰이는 자료구조이다.


트리의 용어

부모노드(Parent Node): 계층적구조에서 어느 한 노드의 바로 위의 노드를 부모노드라고 한다. 

자매,형제노드자매,형제노드(Sibling Node): 계층적구조에서 어느 한 노드의 아래로 연결된 노드를 자매,형제노드라고 한다. 


루트노드(Root Node) : 트리의 가장 위에 있는 원조노드를 말한다.

리프노드(Leaf Node) : 자식이 없는 노드를 리프노드라고 한다. 

터미널노드(Terminal Node) : 리프노드와 같다.(끝난다는 의미이다.)


일반트리(General Tree) : 둘 이상의 자식 노드를  가질 수 있는 트리를 말한다.

이진트리(Binary Tree) : 자식이 없거나, 최대 두개인 트리를 말한다.


트리의 레벨(Level) 

: 루트노드를 레벨 0으로 하고 아래로 내려오면서 증가한다. 

트리의 높이(Height)

: 트리의 최대 레벨 = 트리의 높이


포화이진트리(Full Binary Tree)

: 1) 리프노드의 위쪽 노드는 반드시 자식 노드 두개를 거느리고 있어야 한다. 2) 모든 노드의 왼쪽 자식의 높이와 오른쪽 자식의 높이는 같아야 한다.

완전이진트리(Complete Binary Tree)

: 레벨(높이-1)까지는 모든 노드들이 자식을 두 개로 다 채워야하고, 마지막 리프노드는 왼쪽에서 오른쪽으로 채워져 있는 트리를 말한다.


트리를 배열로 구현하는 방법(간략소개)

: 완전이진트리를 배열로써  표현 할 수 있다. 

다음과 같은 규칙으로 노드에 접근 할 수 있다.

루트노드를 기준으로 

n은 부모노드의 인덱스를 말한다.

왼쪽 자식에 접근하는 법 => (2n + 1)

오른쪽 자 식에 접근하는 법 => (2n + 2)





(2 * 0 + 1) => B [1]로써 0번인덱스노드의 왼쪽 자식노드에 접근

(2 * 0 + 2) => C [2]로써 0번인덱스노드의 오른쪽 자식노드에 접근


E에 접근하기 위해서는 ? 

=> (2*2 + 1) = 5인덱스로 접근하면 된다. 

D에 접근하기 위해서는 ?

=> (2*2 + 2) = 6인덱스로 접근하면 된다.



트리를 포인터로 구현하는 방법(간략소개)

: 노드를 데이터값, 다음왼쪽노드주소값, 다음오른쪽노드주소값 으로 구성한 후에 연결하면 된다.




트리를 배열로 표현할때와 포인터로 표현할때의 장단점?

=> 리스트와 비슷하다. 

[배열로 표현]

장점 : 인덱스로 접근하므로 굉장히 빠르게 접근이 가능하다.

단점 : 사용될 메모리를 미리 선언해야한다. 10000개의 노드를 사용하겠다고 선언해놨지만 100개밖에 사용하지 않는다면 낭비가 될 수 있다.

[리스트로 표현]

장점 : 사용될 메모리를 프로그램을 실행하면서 늘릴 수 있다. 메모리를 효율적으로 사용 할 수 있다.

단점 : 주소를 타고 타고 들어가야하므로 탐색성능은 배열에 비해 느리다.


스레드트리(Thread Tree)란?



이진탐색트리(Binary Search Tree)란?

이진탐색트리의 조건

1) 노드 N에 대하여 N의 왼쪽 하위트리는 N보다 작고, N의 오른쪽 하위트리는 N보다 크다.

2) 노드 N에 대해여 N의 왼쪽 하위트리와 오른쪽 하위트리 모두 이진탐색트리이다.

다음과 같은 트리가 이진탐색트리(Binary Search Tree)이다.





이진탐색트리의 탐색, 삽입, 삭제의 방법?



[탐색방법]

* 8을 찾고 싶다?

(1) 루트노드인 7보다 찾는 노드(8)가 크다 => 오른쪽 자식 노드로 진입

(2) 현재노드인 21보다 찾는 노드(8)가 작다. => 왼쪽 노드로 진입

(3) 현재노드인 8과 찾는 노드(8)가 같다! (찾음)


[삽입방법]

* 6을 삽입하고 싶다?

(1) 루트노드인 7보다 찾는 노드(6)가 작다 => 왼쪽 자식 노드로 진입

(2) 현재노드인 3보다 찾는 노드(6)가 크다 => 오른쪽 노드로 진입

(3) 현재노드인 5보다 찾는 노드(6)가 크다 => 오른쪽 노드로 진입

(4) 오른쪽 노드가 NULL이다. => 이 곳에 삽입


[삭제방법]

삭제하는 유형과정에는 크게 세 가지가 있다. 

[1] 삭제하려는 노드가 자식노드가 없다. => 그냥 삭제하면 된다.

[2] 삭제하려는 노드가 자식노드가 1개다. => 삭제하고 자식노드로 대체한다.

[2] 삭제하려는 노드가 자식노드가 2개다. => 삭제하고 왼쪽서브트리 중에서 가장 큰거 OR 오른쪽서브트리 중에서 가장 작은거를 선택해서 대체한다.

* 5를 지우고 싶다(자식노드가 없는 경우)

이진탐색을 통해 5를 찾는다. => 5를 그냥 삭제한다.


* 2를 지우고 싶다(자식노드가 1개인 경우)

이진탐색을 통해 2를 찾는다. => 2를 삭제하고 그 자식노드를 연결한다.


* 3을 지우고 싶다(자식노드가 2개인 경우)

이진탐색을 통해 3을 찾는다 

=> 오른쪽 서브 트리 중에서 가장 작은 수 OR 왼쪽 서브 트리 중에서 가장 큰 수를 선택 찾는다.

=> 3을 지우고 위에 선택된 수로 대체한다. 

=> 가장 작은 수 또는 가장 큰 수의 노드를 제거한다. 


* 6을 지우고 싶다(해당하는 노드가 없는 경우)

6을 탐색한다. 없다. 끝.


==> 이 부분은 실제 소스를 통해서 구현하면서 자잘한 부분에 대해서 설명하는 편이 낳을 것 같다.

 


이진탐색트리의 효율 및 최악의 케이스?

이진탐색트리는 보통 O(logN)의 시간효율을 갖는다. 


하지만 다음과 같은 경우(편향이진트리) 에는 O(N)시간이 걸린다.


편향이진트리 역시 이진트리이다. 자식을 두 개까지 가질 수 있는 트리이기 때문이다. 하지만 이건 트리가 아니라 리스트라고 할 수 있겠다.

편향이진트리에서 리프노드까지 5번이동해야 한다.

보통의 이진트리에서는 리프노드까지 logN의 이동만에 도달할 수 있는데에 비해서 정말 느린것이다.


그렇다면 O(logN)과 O(N)의 시간 차이가 그렇게 큰 것인가?

예를 들어 트리노드의 개수가 100,000개라고 쳐보자!

O(log100000) => 5

O(100000) => 100000

이렇게나 엄청난 차이가 난다.!!!

'자료구조' 카테고리의 다른 글

B-Tree  (0) 2015.07.01
우선순위 큐 - 힙(Heap)  (0) 2015.07.01
위상정렬  (0) 2015.07.01
자료구조 - 리스트(List)  (0) 2015.06.16
정렬에 관해서  (0) 2015.04.03
Posted by slender ankles
,

위상정렬

자료구조 2015. 7. 1. 01:46

위상정렬을 왜 쓰는가? 

쉬운 예로 설명할 수 있겠다. 

1) 스타크래프트를 할 때 고급 건물을 짓기 위해서 먼저 지어야 되는 건물들이 있다.

이러한 순서를 나타낸 것을 위상정렬이라고 할 수 있겠다. 

이러한 순서와 관계를 정리해주는 것을 위상정렬이라고 하겠다. 


2) 학교 수강 과목 중에 반드시 선수되어야 할 과목들이 있을 것이다. 이렇게 어떠한 과목을 듣기 전에 듣고 가는 과정을 나타내는 것을

위상정렬이라고 할 수 있겠다. 



위상정렬를 구하는 방법에 대해서 간단히 설명하겠습니다.

우선 위상정렬은 방향이 있는 비순환 그래프에서 가능한 것입니다. 





위와 같은 그래프가 있습니다. 

1) 우선 위상정렬을 구하기 위해서는 진입하는 간선이 없는 노드를 찾습니다.

2) 찾은 노드를 저장합니다.

3) 이 노드에서 빠져나가는 간선들을 다 지워줍니다.

4) 1)의 과정으로 돌아갑니다. 


자신으로 들어오는 간선이 없는 노드를 indegree가 0인 노드라고 표현합니다.

이러한 과정을 반복해서 나온 노드들의 번호들의 순서가 위상정렬이 됩니다. 


다음과 같은 반복이 I까지 가게됩니다. 












 

'자료구조' 카테고리의 다른 글

우선순위 큐 - 힙(Heap)  (0) 2015.07.01
트리(Tree)  (0) 2015.07.01
자료구조 - 리스트(List)  (0) 2015.06.16
정렬에 관해서  (0) 2015.04.03
트리 순회_동적할당소스코드  (0) 2015.03.31
Posted by slender ankles
,

abstract키워드?

추상 메서드(Abstract Method)를 간단하게 설명하면 "선언부는 있는데 구현부가 없는 메서드"를 말한다.

추상 클래스(Abstract Class)는 "추상 메서드가 하나라도 있는 클래스는 추상클래스로 선언해야 한다"를 말한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// Driver.java
package abstractMethod01;
 
public class Driver{
    public static void main(String[] args){
        동물[] 동물들 = new 동물[5];
        
        동물들[0= new 쥐();
        동물들[1= new 고양이();
        동물들[2= new 강아지();
        동물들[3= new 송아지();
        동물들[4= new 병아리();
        
        for(int i  = 0;i<동물들.length;i++){
            동물들[i].울어보세요();  
        }
    } 
}
 
// 쥐.java
package abstractMethod01;
public class 쥐 extends 동물{
    void 울어보세요(){
        System.out.println("찍!찍!");
    }  
}
 
// 고양이.java
package abstractMethod01;
public class 고양이 extends 동물{
    void 울어보세요(){
        System.out.println("야옹!");
    }  
}
 
// 병아리.java                           
package abstractMethod01;            
public class 병아리 extends 동물{        
    void 울어보세요(){               
        System.out.println("삐약");
    }                                
}
 
// 동물.java                                                               
package abstractMethod01;            
public class 동물{
    void 울어보세요(){               
        System.out.println("나는 동물 어떻게 울어야 하나요?");
    }                                
}
 
cs



동물타입의 참조 변수를 통해 하위 클래스의 인스턴스를 호출하고 있기 때문에 반드시 상위 클래스의

울어보세요()라는 메서드는 존재해야 한다. 

그런데, 동물클래스의 울어보세요()라는 메서드를 호출하게 되면 난감해진다.

동물이라는 객체를 울어보세요를 구현하기가 난감해진다. 


이럴 때 abstract 메서드를 사용하여 선언은 해놓되 구현부는 없는 형태로 구현한다.


1
2
3
4
5
// 동물.java
package abstractMethod01;
public abstract class 동물{
    abstract void 울어보세요();
}
cs


이렇게 되면 동물 클래스(최상위 추상 클래스)를 호출 할 때 에러가 난다. 

한마디로 추상클래스의 인스턴스 메서드를 호출하면 에러가 나므로 이러한 문제를 원천봉쇄 할 수 있다. 

- 추상클래스는 인스턴스, 즉 객체를 만들 수 없는 클래스가 된다.


- 추상클래스를 상속한 하위 클래스는 추상메서드의 구현부를 반드시 구현해야 한다.

그렇지 않은 경우에 "The type 고양이 must implement the inherited abstract method 동물.울어보세요()"라는 에러가 난다.


<<정리>>

- 추상 클래스는 인스턴스, 즉 객체를 만들 수 없다. 즉, new를 사용 할 수 없다.

- 추상 메서드는 하위 클래스에게 메서드의 구현을 강제한다. 오버라이딩 강제

- 추상 메서드를 포함하는 클래스는 반드시 추상 클래스여야 한다.


생성자란?

클래스의 인스턴스를 생성하는 메소드를 말한다.


클래스명 클래스참조변수 = new 클래스명();


생성자에 인자를 만들어도 되나?

된다. 

알아두어야 할 것이 있다.

- 개발자가 아무런 생성자도 만들지 않으면 자바는 인자가 없는 기본 생성자를 자동으로 만들어준다.

- 인자가 있는 생성자를 하나라도 만든다면 자바는 기본 생성자를 만들어 주지 않는다.


static 블록이란?

클래스가 스태틱 영역에 배치될 때 실행되는 코드 블록이다.

클래스의 생성자와 같은 기능이다. 

여러 개의 객체를 생성 할 때는 당연히 처음 클래스가 불려질 때 띄워지는 부분이라고 보면 된다.

코드를 보면

1
2
3
4
5
6
7
package staticblock;
public class 동물{
    static 동물{
        System.out.println("동물 클래스 레디 온");  
    }
}
 
cs



그렇다면 메모리 구조를 설명 할 때보면 처음에 클래스가 가장 먼저 올라간다고 했으니까 스태틱 블록이

main메서드보다 먼저 호출될까?

=> 아니다.

실제로는 해당 패키지 또는 클래스가 처음으로 사용 될 때 로딩되는 것이 맞다.


static블록이 실행 될 때

- 클래스의 정적 속성을 사용할 때

- 클래스의 정적 메서드를 사용할 때

- 클래스의 인스턴스를 만들 때


왜 구조를 설명 할 때보면 자바 어플리케이션 실행 가장 맨 처음에 스태틱영역에 클래스들이 자리 잡는다고 했는데 그렇지 않은가?

스태틱영역도 메모리이기 때문이다. 메모리는 최대한 늦게 시작하여 최대한 빠르게 반환하는 것이 효율적이다.

물론 자바의 구조상 클래스와 패키지들은 스태틱영역에 자리잡은채 반환되지는 않는다. 

하지만, 최대한 늦게 로딩함으로써 메모리 사용을 최대한 늦추기 위해서 이와 같이 동작한다.



final키워드란?(클래스, 변수, 메소드에서)

클래스에 final 키워드가 붙으면 어떤 의미인가?

=> 상속을 허락하지 않는다는 의미이다.

1
2
3
4
5
// 클래스에서의 final 키워드
package finalclass;
 
public final class 고양이{}
 
cs



변수에 final 키워드가 붙으면 어떤 의미인가?

=> 바로 변경 불가능한 상수가 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 변수에서의 final 키워드
 
package finalValuable;
 
public class 고양이{
    final static int 정적상수1 = 1;
    final static int 정적상수2;
    
    final int 객체상수1 = 1;
    final int 객체상수2;
    
    static{
        정적상수2 = 2;
        
        // 상수는 한 번 초기화되면 값을 변경할 수 없다.
        // 정적상수2 = 4;  
    }
    
    고양이(){
        객체상수2 = 2;
        // 상수는 한 번 초기화하면 값을 변경 할 수 없다.
        // 객체상수  2 = 4;
        
        final int 지역상수1 = 1;
        final int 지역상수2;
        
        지역상수2 = 2// 최초 한 번만 가능  
    }
}
 
cs


최초에 값을 할당하지 않은 변수인 경우에 최초 1회만 값을 할당 가능하다.


메소드에 final키워드가 붙으면 어떤 의미인가?

=> 최종이라는 의미로, 재정의, 즉 오버라이딩이 금지된다.


instanceof 연산자란?

인스턴스는 클래스를 통해 만들어진 객체이다. 

instanceof연산자는 만들어진 객체가 특정 클래스의 인스턴스인지 물어보는 연산자이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// Driver.java
package instanceof01;
 
class 동물{
      
}
 
class 조류 extends 동물{
  
}
 
class 펭귄 extends 조류{
  
}
 
public class Driver{
    public static void main(String[] args){
        동물 동물객체 = new 동물();
        조류 조류객체 = new 조류();
        펭귄 펭귄객체 = new 펭귄();  
        
        System.out.println(동물객체 instanceof 동물); // true
        
        System.out.println(조류객체 instanceof 동물); // true
        System.out.println(조류객체 instanceof 조류); // true
        
        System.out.println(펭귄객체 instanceof 동물); // true
        System.out.println(펭귄객체 instanceof 조류); // true
        System.out.println(펭귄객체 instanceof 펭귄); // true
        
        System.out.println(펭귄객체 instanceof Object); // true
    }  
}
 
cs



- instanceof키워드는 실제 객체타입을 보고 판단한다.

- instanceof연산자는 클래스의 상속관계 뿐만 아니라 interface의 상속관계에서도 동일하게 적용된다.


package키워드란?

package키워드는 네임스페이스(이름공간)을 만들어주는 역할을 한다.

개발팀의 user클래스과 홍부팀의 user클래스는 분명 다르지만 같은 이름을 가진다. 

이러한 충돌을 막기 위하여 package키워드를 이용해  네임스페이스를 만들어주는 역할을 하는 것이다.


쉬운 예로)

A라는 사람의 스마트폰과 B라는 사람의 스마트폰은

스마트폰에는 동일하다. 하지만 A라는 사람과 B라는 사람의 이름이 다르기 때문에 

두 스마트폰을 구별할 수 있는 것이다.


interface키워드와 implements키워드 에서의 부가설명

앞써 설명했다. 접근제어지시자에 대해서 이야기하고자 한다.

- 인터페이스는 public 추상 메서드와 public 정적 상수만 가질 수 있다.

그렇다면 다음은 잘 못된 것인가?


1
2
3
4
5
6
interface Speakable{
double PI = 3.141592;
final double absoluteZeroPoint = -275.15;
 
void sayYes();
}
cs


=> 아니다. 배려깊게도 interface에 public과 abstract, static이라는 키워드를 사용하지 않아도 자동으로 그렇게 할당한다.

하지만 나는 이러한 특성을 알아도 반드시 명명해주겠다. 정확한 가독성이 중요하다고 생각하기 때문이다.


** 참고사항 **

자바 8에서의 변화 중 람다라는 것은 무엇인가?

좀 더 부가적인 설명이 필요하고 따로 정리해야겠지만, 

"람다는 변수에 저장 할 수 있는 로직이다."라고 설명 할 수 있다.

자바스크립트에서 변수에 함수를 저장하여 인자로 전달하거나 하는 기능을 사용 할 수 있었는데

자바에서도 이 같이 이해하면 될까?

다른 언어와 비교한다면

C++의 함수포인터!

C#의 델리게이트(Delegate)

자바스크립트 : 함수를 저장하는 변수 / 함수 인자로 callback 전달


this키워드?

- 지역 변수와 속성(객체 변수, 정적 변수)의 이름이 같은 경우 지역변수가 우선된다.

- 객체 변수와 이름이 같은 지역 변수가 있는 경우 객체 변수를 사용하려면 this를 접두사로 사용한다.

- 정적 변수와 이름이 같은 지역 변수가 있는 경우 정적 변수를 사용하려면 클래스명을 접두사로 사용한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package This;
 
class 펭귄{
    int var = 10;
    
    void test(){
        int var = 20;
        System.out.println(var);      // 20출력
        System.out.println(this.var); // 10출력
    }  
}
 
public class Driver{
    public static void main(String[] args){
        펭귄 뽀로로 = new 펭귄();
        뽀로로.test();  
    }  
}
 
cs



super키워드?

단일 상속만을 지원하는 자바에서 super는

"바로 위 상위 클래스의 인스턴스를 지칭하는 키워드"이다. 

- super 키워드란 ? => "바로 위 상위 클래스의 인스턴스를 지칭하는 키워드"

- 하지만, 상위의 상위 클래스 super.super로는 접근 할 수 없다.

'JAVA' 카테고리의 다른 글

자바 해시맵(HashMap)  (0) 2015.08.05
자바 문자열  (0) 2015.07.26
자바와 객체지향  (0) 2015.06.30
자바 스레드의 이해  (0) 2015.06.30
자바 메모리 구조 >>> 2  (0) 2015.06.30
Posted by slender ankles
,

자바와 객체지향

JAVA 2015. 6. 30. 15:45

구조적 프로그래밍?

아무리 복잡한 문제라도 작은 문제로 분할해서 하나씩 정복하다 보면 결국 해결된다는 전략입니다.

몇 천, 몇 만 라인을 논리적인 단위로 나누어 블록화해서 작성하자. 이런 논리적인 단위를 함수라고 합니다.


객체지향 프로그래밍?

함수를 이용한 프로그래밍이 좀 더 발전한 것이 객체지향 프로그래밍입니다.

세상 모든 눈으로 보여지는 것, 만져지는 것, 상상하는 것들을 객체로 판단해서 프로그래밍하는데 활용하겠다는 것.

- 세상의 존재하는 모든 것은 사물, 즉 객체(Object)이다.

- 각각의 사물은 고유하다.

- 사물은 속성을 갖는다.

- 사물은 행위를 한다.


사람은 사물을 하나하나 이해하기 보다는 분류(class)하고 싶어한다.

- 직립보행을 하며, 말을 하는 존재를 사람이라고 한다.

- 연미복, 짧은 다리, 날지 못하는 새를 펭귄이라고 한다.

- 밤하늘에 반짝이는 사물들을 별이라고 한다.


더 나아가, 

객체? 속성? 메소드?

김연아(Object), 유재석(Object), 하하(Object), 강호동(Object) 등등

이런 사람이라는 객체들은 나이(Property), 몸무게(Property), 키(Property)와 같은 속성과

싸다(Method), 먹다(Method), 말하다(Method), 일하다(Method)와 같은 메소드를 가지고 있다.


객체 지향 이전 vs 객체 지향

객체 지향 이전에는 객체에 대한 속성과 메소드를 따로 구분하여 구현하였는데 반해,

객체 지향에서는 이런 속성(필드), 메소드(함수)를 합하여 객체화 시켜 구현을 하게 한다.


클래스와 객체와의 관계

클래스

분류이다. 

객체 

실체이다.

객체지향적 관점에서 사람은 클래스이고, 김연아는 객체이다.


클래스와 객체를 구분하는 간단한 방법

나이를 물어보는 것이다.

사람이 몇 살인가? => (X) 

김연아는 몇 살인가? => (O)

펭귄은 몇 살인가? => (X)

뽀로로는 몇 살인가? => (O)

김연아와 뽀로로는 몇 살인지 답변 가능하다. 그러므로 객체이다.

하지만 사람과 펭귄의 나이를 물어보는 것은 무엇인가 이상하다. 이 것은 클래스(분류)이다.


객체지향의 4대 특징? 

추상화, 상속, 캡슐화, 다형성



추상화(Abstraction)란?

객체에 대한 속성이나 특성을 가지고 모델링하는 것을 말한다.

ex) 환자라는 객체에 대한 추상화

환자라는 객체에 대해 추상화하기 위해서는 환자에 대한 속성이나 특성을 가지고 모델링하는 것을 말한다.

환자의 이름, 나이, 몸무게, 기저질환유무라는 속성(Property)와

운동하다, 운전하다, 화장실가다, 밥먹다와 같은 메소드(Method)로 추상화 할 수 있을 것이다.

환자라는 것에 국한한다는 것은 프로그래밍에서는 애플리케이션의 경계로부터 정한다고 한다.


=> 애플리케이션의 경계안에서 객체에 대한 속성이나 특성을 가지고 모델링하는 것을 말한다.

라고 정의할 수 있겠다.




클래스를 통해 객체를 만들어 낼 때의 메모리 구조는 어떻게 변화하나?


Mouse mickey = new Mouse();


3개의 명령문이 녹아있다.

Mouse mickey // Mouse객체에 대한 참조 변수 mickey를 만든다.

new Mouse() // Mouse클래스의 인스턴스를 하나 만들어 힙에 배치한다.

= 대입문 // Mouse객체애 대한 주소(포인터)를 참조 변수 mickey에 할당한다.




Mouse라는 클래스는 스태틱영역에 위치해있다.

new Mouse()라는 명령어를 만나면 인스턴스를 만들어 힙 영역에 배치한다.

또 지역 변수인 mickey라는 변수는 힙영역의 인스턴스를 가리키고 있는 모양이다.





클래스 멤버 vs 객체 멤버 = static 멤버 vs 인스턴스 멤버


static 키워드란 무엇을 말하는가?

미키마우스, 제리마우스, 마이티마우스란 객체에서 공통된 질문인 "꼬리가 몇 개인가?"를 해보자.

무조건 답은 "하나"이다. 마우스라는 클래스의 공통된 성질이다. 꼬리가 한 개 인 것.

그렇다면 이렇게 모든 객체가 가진 공통된 특성을 힙영역에 메모리를 잡고 할 당하고 있는 것은 낭비가 아닌가?

countofTail과 같은 변수를 스태틱 변수로 옮겨 버리면 메모리를 더욱 효율적으로 사용 할 수 있다.

변수 앞에 static이라는 키워드를 붙여서 사용한다.

static 멤버는 클래스의 공통된 특성을 가진 변수를 스태틱 영역에 할당하는 것을 말한다.



static변수에 접근하는 효율적인 방법은?

mickey라는 참조변수는 Mouse라는 객체에 도달한 후에 다시 스택틱 변수가 위치한 스태틱 영역으로 이동한다.

(스택영역 -> 힙 영역 -> 스태틱영역)

하지만 클래스명.정적멤버  형식으로 접근하게 되면 스택에서 바로 스태틱영역으로 접근하여 효율적이다.

(스택영역 -> 스태틱영역)


인스턴스 멤버는

힙 영역에 할당된 객체들의 멤버를 말한다.


클래스 멤버 = static 멤버 = 정적 멤버

객체 멤버 = 인스턴스 멤버

용어에 대해 부르는 것들이 조금 다양할 뿐 같은 말이다. 헷갈리지 말아야 한다.







상속(Inheritance)이란? => 재사용 + 확장

상속에 대한 정의는 상위 클래스를 하위 클래스가 재사용하고 확장할 수 있는 특징을 말한다.

보통 부모클래스, 자식클래스로 나누는데 이러한 용어보다는 

상위 클래스 - 하위 클래스 또는 슈퍼 클래스 - 서브 클래스 라고 하는 것이 맞다.

이러한 관점은 계층적인 관점이 아니라, 분류적인 관점이기 때문이다.


이러한 주장을 뒷받침하듯이 자바의 문법은 Inheritance가 아닌 extends(확장하다)라는 키워드를 사용한다.



toString()이라는 메서드를 어느 클래스에서든지 사용 할 수 있는 이유는?

모든 클래스의 상속 구조에서의 최상위 클래스는 Object클래스이다.

결국 모든 클래스는 Object클래스의 특성을 물려받는다.

상속때문이다. 상속의 강력한 기능을 잘 표현한 예라고 할 수 있다.


상속관계를 맺기 위한 조건?

is a kind of의 조건을 만족해야 한다.

하위클래스 is a kind of 상위클래스 => 하위클래스는 상위 클래스의 한 종류이다?

펭귄 is a kind of 동물 => 펭귄은 동물의 한 종류이다. 

고래 is a kind of 동물 => 고래는 동물의 한 종류이다.

조류 is a kind of 동물 => 조류는 동물의 한 종류이다.


자바가 다중 상속을 막은 이유는?

자바는 C++과 다르게 다중 상속을 포기했다. 

다중 상속의 여러 문제점 때문에 다중 상속을 지원하지 않게 되었다. 

다이아몬드 문제

TopA

MiddleB, middleC

BottomD

B는 A를 상속했다. C도 A를 상속했다.

D는 B와 C를 다중 상속했다. 

=> A가 가지고 있는 공통된 특성이 중복으로 D가 가져야 된다.


그럼 자바는 다중 상속을 어떻게 보완했나? => Interface

다중 상속을 포기하고 인터페이스를 도입한 자바에서 인터페이스는 어떤 역할을 하며 어떤 관계를 나타내는 것인가?

- 인터페이스 : 구현 클래스 is able to 인터페이스

- 해석 : 구현 클래스는 인터페이스 할 수 있다.

- 예제 : 고래는 헤엄 칠 수 있다.




위의 관계도를 보면서 다중상속의 단점을 버리고, 이점을 취한 인터페이스에 대한 이해를 할 수 있겠다.


인터페이스는 be able to, 즉 "무엇을 할 수 있는"이라는 표현 형태로 만드는 것이 좋다.

자바 API에서도 이러한 be able to 형식의 인터페이스를 많이 볼 수 있다.

Serializable 인터페이스(직렬화 할 수 있는)

Cloneable 인터페이스(복제 할 수 있는)

Comparable 인터페이스(비교 할 수 있는)

Runnable 인터페이스(실행 할 수 있는)



// 동물.java
package inheritance02;
 
public class 동물{
    String myClass;
    
    동물(){
        myClass = "동물";  
    }
    void showMe(){
        System.out.println(myClass);  
    }
}
 
// 날수있는.java
package inheritance02;
 
public interface 날수있는{
    void fly();
}
 
// 헤엄칠수있는.java
package inheritance02;
 
public interface 헤엄칠수있는{
    void swim();
}
 
// 포유류.java
package inheritance02;
 
public class 포유류 extends 동물{
    포유류(){
        myClass = "포유류";  
    }
}
 
// 조류.java
package inheritance02;
 
public class 조류 extends 동물{
    조류(){
        myClass = "조류";
    }
}
 
// 고래.java
package inheritance02;
 
public class 고래 extends 포유류 implements 헤엄칠 수 있는{
    고래(){
        myClass = "고래";  
    } 
    
    @Override
    public void swim(){
        System.out.println(myClass + "수영 중 어프!! 어프!!");
    } 
}
 
// 박쥐.java
package inheritance02;
 
public class 박쥐 extends 포유류 implements 날 수 있는{
    박쥐(){
        myClass = "박쥐";  
    } 
    
    @Override
    public void fly(){
        System.out.println(myClass + "날고 있다..슈융");
    } 
}
 
cs


상속될 때의 메모리 구조는 어떻게 되나?

상속 받은 상위 클래스까지 힙 영역에 할당된다.







다형성 : 사용편의성


오버라이딩(Overriding) vs 오버로딩(Overloading)

- 라이딩 : 올라타기

- 로딩 : 적재하기


오버 라이딩 => 재정의 : 상위 클래스의 같은 메서드 이름, 같은 인자 리스트

오버 로딩 => 중복정의 : 상위 클래스의 같은 메서드 이름, 다른 인자 리스트




** 기억해야 되는 부분

상위 클래스의 객체 참조 변수를 사용하면 재정의하지 않은 메서드가 불려지나?

상위 클래스의 객체 참조 변수를 사용하더라도 하위 클래스에서 오버라이딩(재정의)한 메서드가 호출 된다는 점을 기억해야 한다.


다형성이 지원되지 않는다면?

예를 들어 연산하는 부분을 생각해볼 수 있다. 

자바와 같은 경우에 정수 자료형으로 byte, short, int, long, char 부동소수점 수 자료형으로는 float, double이 있다. 

만약 다형성이 지원되지 않는다면 다른 함수 이름을 7 * 7 = 49개 정도는 만들어줘야 한다.

다형성이 지원하기 때문에 하나의 메서드를 가지고 이 같은 필요를 충족시켜줄 수 있게 된다.



캡슐화란 무엇인가? 정보은닉이란 무엇인가?

객체지향의 특징 중 캡슐화란 접근 제어 지시자를 이용해 외부로부터 정보를 마구잡이로 접근 할 수 없게끔 할 수 있는 특징을 말한다.

긴설명은 생략하고 정확하게 이해하고 있고, 실전을 통해서 익혀야 한다고 생각한다.

간략히 표현하자면, 


그 동안 실수로 알았던 것이 

Protected가 상속 받은 클래스에서만 접근 가능한 줄 알았는데 같은 패키지에서도 접근 가능하다는 것이다.



참조변수의 복사?

Call By Value vs Call By Reference

참조 변수를 복사하는 것은 결국 주소를 복사하는 것이다.

String 변수에 대한 CBV, CBR에 대해서 설명해보겠다.


String a = "aaa";


String b = a;


String c = new String("aaa");


a와 b는 같은 주소값을 가진다. 

c는 새로운 주소에 값이 할당된다.

그래서 == 연산자로 비교하면 a와 b는 같다

하지만 a와 c, b와 c를 비교하면 다르다.


== 연산자는 주소 값을 비교하고, equals()라는 함수를 통해 값을 비교 할 수 있다. 

CBV와 CBR의 예라고 할 수 있다. 

'JAVA' 카테고리의 다른 글

자바 문자열  (0) 2015.07.26
자바가 확장한 객체지향  (0) 2015.06.30
자바 스레드의 이해  (0) 2015.06.30
자바 메모리 구조 >>> 2  (0) 2015.06.30
자바 메모리 구조>>1  (0) 2015.06.30
Posted by slender ankles
,