[Java] 자바 제네릭 (Generic) 문법


제네릭 (Generic) 문법

- 제네릭을 사용하기 전엔 값을 꺼낼 때마다 일일이 본래 타입으로 형변환(캐스팅)을 해야 했다.

- 그러나 제네릭을 사용하면 값을 꺼낼 때마다 형변환을 할 필요가 없고 특정 타입의

  값만 다루도록 제한할 수 있다.


- 문법 : 

  클래스 선언부에 해당 클래스가 다룰 타입(클래스 또는 인터페이스)이 무엇인지

  타입 정보를 받을 변수를 선언한다.




제네릭을 사용하기 전 Bucket class와 호출 코드


public class Bucket {
  Object value;
 
  public void setValue(Object value) {
    this.value = value;
  }
 
  public Object getValue() {
    return value;
  }
}


Bucket b= new Bucket();

   
String str = new String("Hello");
System.out.println(str.length());
   

b.setValue(str);
// String str2 = b.getValue(); // getValue()가 리턴하는 건 Object 객체이다.
                                     // Object 객체를 String 타입에 담을 수 없기 때문에 오류!
String str2 = (String)b.getValue();

System.out.println(str2.length());






제네릭을 사용한 후 Bucket class


public class Bucket2 <T> {
  T value;
 
  public void setValue(T value) {
    this.value = value;
  }
 
  public T getValue() {
    return value;
  }
}




제네릭을 사용한 후 main() 메서드


import java.util.Date;

public class Test03 {
  public static void main(String[] args) {
    // 타입을 지정해보자!
    // Bucket2<String> b= new Bucket2<String>();
   
    // new 명령 다음에는 타입 지정을 생략할 수 있다.
    Bucket2<String> b= new Bucket2<>();

//    Bucket2<String> b= new Bucket2<Object>();
  
  b.setValue(new Date()); // 컴파일 오류!
  b.setValue(new Integer(10)); // 컴파일 오류!
   
  }
}





블로그 이미지

필로그래머

,

[Java] 자바 컬렉션 - [Map] HashMap(java.util.HashMap)


컬렉션 클래스 : Map

- 값을 저장할 때 key 개체의 해시 값(hash value)을 사용하여 저장한다.

- 값을 꺼낼 때도 key 객체의 해시 값을 사용하여 꺼낸다.

- 값을 중복 저장할 수 없다.

- 순서대로 저장하고 순서대로 꺼낼 수 없다.

- 보통 문자열 라벨을 사용하여 값을 저장하고 꺼낼 때 주로 사용한다.

- 예)

   HashMap :

    - key나 value로 null을 허용한다.

    - 동기화를 지원하지 않는다.

       => 여러 스레드가 동시에 접근하여 값을 변경하는 것을 막지 않는다.

       => 데이터의 입출력 속도가 빠르다.

       => 대신 여러 스레드가 동시에 값을 바꿀 수 있기 때문에 

               멀티 스레드 상황에서 사용할 때 주의해야 한다.

    Hashtable : 

     - key나 value로 null을 허용하지 않는다.

     - 동기화를 지원한다.

       => 여러 스레드가 동시에 접근하더라도 그 중 한개의 스레드만이 접근할 수 있다.

        => 데이터의 입출력 속도가 느리다.

        => 대신 여러 스레드가 동시에 값을 바꿀 수 없기 때문에

             멀티 스레드 상황에서 안전하게 사용할 수 있다.


put(key, value)

- key 객체 : 

  1) key 객체의 hashCode() 리턴 값을 사용하여

      value를 저장할 때 사용할 위치를 계산한다.

  2) 그래서 key 객체는 같은 값을 가진 객체인 경우

      같은 hashCode()를 리턴하는 클래스를 사용한다.

      예) String, 랩퍼 클래스(Byte, Short, Integer, Long, Float, Double, Boolean 등)

- value 객체: Map 컬렉션에 보관될 값



Map에서 값을 꺼내는 방법 I

- 값을 저장할 때 사용한 key를 통해 꺼낸다.

- 반드시 같은 인스턴스일 필요는 없다.

  저장할 때 사용한 key의 hashCode() 리턴 값과 

  같은 hash value를 가진다면 값을 꺼내는 key로 사용할 수 있다.

  물론 equals()의 결과도 true여야 한다.



값을 저장할 BooK class 예제


static class Book {
    String title;
    String press;
    int page;
   
    public Book(String title, String press, int page) {
      this.title = title;
      this.press = press;
      this.page = page;
    }
   
    public String toString() {
      return String.format("%s, %s, %d", title, press, page);
    }

  }




Map에서 값을 꺼내는 호출 코드


public static void main(String[] args) {

    HashMap map = new HashMap();
    
    map.put(new String("key1"), new Book("aaaa", "유리출판사", 100));
    map.put("key2", new Book("bbbb", "유리출판사", 200));
    map.put(3, new Book("cccc", "유리출판사", 300));
    
    System.out.println(map.get(new String("key1")));
    System.out.println(map.get(3));
  }

}

- 실행 결과 : 

aaaa, 유리출판사, 100

cccc, 유리출판사, 300



* 참고 :

  key 값에 올 수 있는 건 인스턴스 뿐인데 어째서 key 값으로 숫자 1을 넣어줄 수 있는 것인가? 

  => 랩퍼 클래스(wrapper class)는 오토 박싱 (auto-boxing) 기능을 가지고

     있어서 인스턴스를 요구하는 명령어가 있을 경우, key 값으로 써준 숫자 1을

     자동적으로 new Integer(1)로 만들어 준다.

      

** 참고 :

   map.get(key)메서드가 리턴하는 것은 key 값에 해당되는 인스턴스의

   toString() 메서드 리턴 값이다.




Map에서 값을 꺼내는 방법 II


- 값 목록을 리턴 받아 꺼낸다.


 Collection valueList map.values();
    // 값이 들어 있는 컬렉션에서 값을 한 개씩 꺼내줄 도구를 얻는다.
 Iterator iterator = valueList.iterator();
   
 while(iterator.hasNext()) {
   System.out.println(iterator.next());

 }





Map에서 값을 꺼내는 방법 III


- key 목록을 리턴 받아 꺼낸다


Set KeySet = map.KeySet();
Object[] keyArray = keySet.toArray();
 for (int i = 0; i < keyArray.length; i++) {
   System.out.println(map.get(keyArray[i]));

 }





Map에서 값을 꺼내는 방법 V


- key와 value를 한쌍으로 하는 목록을 리턴 받는다.


Set entrySet = map.entrySet();
    for (Object object : entrySet) {
      Entry entry = (Entry)object;
      System.out.printf("%s=%s\n", entry.getKey(), entry.getValue());

    }











블로그 이미지

필로그래머

,

[Java] 자바 컬렉션 클래스 - [Set] HashSet (java.util.Hashset)


컬렉션 클래스 : HashSet (java.util.HashSet)

 - 저장하려는 객체에 대해 hashCode()를 호출하여

   그 리턴 값을 가지고 저장할 위치를 계산한다.

 - null 값을 저장할 수 있다.

 - 값을 중복 저장할 수 없다.

   이유 -> '집합'이기 때문

 - 값을 넣을 때 인스턴스의 해시값을 기준으로 저장하기 때문에

   순서대로 저장되지 않는다.

   그래서 값을 꺼낼 때도 순서대로 꺼낼 수 없다.

 - 값을 꺼낼 때 숫자 인덱스로 꺼낼 수 없다.

 - 예) HashSet



Set 컬렉션에서 값을 꺼내는 방법


 1) 배열을 리턴 받는다. (입력 순서대로 리턴 받을 수 없다.)

     - toArray()


Object[] valueList = dateSet.toArray();


for (int i = 0; i < valueList.length; i++) {
  System.out.println(valueList[i]);

}


 2) 값을 꺼내주는 메서드를 이용한다.

     - iterator()


Iterator iterator = dateSet.iterator();
 
while (iterator.hasNext()) { // hasNext() -> 꺼낼 데이터가 있는가?
  System.out.println(iterator.next());

}






* 주의!
  HashSet 컬렉션에 값을 저장할 때,
  인스턴스의 주소가 영향을 끼치는 것이 아니라

  hashCode()의 리턴 값이 영향을 끼친다.



HashSet과 hashCode() 메서드 예제


public static void main(String[] args) {
    HashSet set = new HashSet();
    
    set.add(new String("홍길동"));
    set.add(new String("임꺽정"));
    set.add(new String("유관순"));
    set.add(new String("윤봉길"));
    set.add(new String("안중근"));
    set.add(new String("김구"));
    set.add(new String("김구"));
    /* "김구" 문자열인 경우엔 서로 다른 인스턴스 임에도 불구하고 중복되지 않는다.
     *  왜?
     *  => Set은 객체(의 주소)를 저장할 때 그 객체에 대해 hashCode() 메소드를 호출한 후
     *     그 리턴 값을 위치를 계산한다.
     *  => String 클래스는 같은 값을 갖는 경우 같은 hash value를 리턴하도록
     *     hashCode()를 오버라이딩 하였다.
     *     즉 위의 두 개의 "김구" String 객체는 비록 인스턴스는 다르지만
     *     값이 같기 때문에 hashCode()의 리턴값도 같다.
     *     그래서 위치를 계산한 값도 같다.
     *     위치 계산 값이 같아서 같은 값으로 간주하기 때문에 중복 저장되지 않는다.
     */
   
    // 증명! -> 값이 다 똑같이 출력된다.
    System.out.println(new String("김구").hashCode());
    System.out.println(new String("김구").hashCode());
    System.out.println(new String("김구").hashCode());
     
   
    /* 결론!
     * HashSet 컬렉션에 값을 저장할 때,
     * 인스턴스의 주소가 영향을 끼치는 것이 아니라
     * hashCode()의 리턴 값이 영향을 끼친다.
     */
   
    Iterator iterator = set.iterator();
    while (iterator.hasNext()) { // 꺼낼 데이터가 있는가?
      System.out.println(iterator.next());
    }
}


블로그 이미지

필로그래머

,

[Java] 자바 캡슐화(encapsulation), 접근 제어자(private, default, protected, public)


캡슐화(encapsulation)

- 어쩌다 유지보수를 하는 다른 개발자가 인스턴스 변수나 메서드, 

  이너 클래스에 접근해서 값을 마음대로 저장시킨다면 클래스를 

  정의(추상화)한 보람이 없어진다.

- 캡슐화는 이런 경우를 막고자 고안된 방법으로, 추상화를 무너뜨리지 않게 

  만드는 문법이다. 즉 클래스가 원래 목적에 맞게 동작할 수 있도록 클래스 멤버

  (변수, 메서드, 이너 클래스)의 접근을 제한하는 것이다.

- 추상화 : 어떤 목적에 맞게 동작하도록 클래스 안에 변수나 메서드를 정의한 것을 의미



I 캡슐화(encapsulation) : modifier 종류

- private : 

  클래스 멤버만이 접근 가능


- (default) : 아무것도 붙이지 않는 것.

  클래스 멤버 + 같은 패키지의 클래스


- protected : 

  클래스 멤버 + 같은 패키지의 클래스 + 자식 클래스 


- public :

  모든 접근 허용




modifier 예제

package step11.ex1;

public class A {
  private int  v1;
  int v2;
  protected int v3;
  public int v4;
}



다른 패키지에서의 변수 선언 코드

import step11.ex1.*;


public class Test04 extends A{
  public void test() {
//    this.v1 = 1; // private
//    this.v2 = 2; // (default)
    this.v3 = 3; // protected. 상속 받아서 만든 변수이기 때문에 가능
    this.v4 = 4; // public 
   
  }
 
  public static void main(String[] args) {
    A obj = new A();
//    obj.v1 = 1; // private
//    obj.v2 = 2; // (default)
//    obj.v3 = 3; // protected. 남의 것은 안된다. 상속 받아서 만든 변수에 대해서만 가능.
    obj.v4 = 4; // public
  }
}





II 캡슐화(encapsulation) : 패키지 멤버 클래스(일반 클래스)에 붙일 수 있는 modifier 종류 II

- 패키지 멤버 클래스 : 

  소스 파일 가장 밖에 선언한 클래스이다.

  일반적으로 작성하는 클래스이다.

- (default) : 아무것도 붙이지 않는 것.

  같은 패키지의 클래스

- public :

  모든 접근 허용




III 캡슐화(encapsulation) : 중첩 클래스(nested class) / 내부 클래스(inner class)

클래스 안에 선언된 클래스를 "중첩 클래스(nested class)" 또는 "내부 클래스(inner class)"라고 부른다.

inner 클래스는 클래스 안에 선언되기 때문에 바깥 클래스의 멤버이다.

따라서 클래스의 멤버인 변수나 메서드와 똑같이 modifier를 사용할 수 있다.










블로그 이미지

필로그래머

,

[Java] 자바 super 키워드의 사용


super 키워드

- 재정의 하기 전(부모)의 메서드를 호출할 때 사용한다.

- 많은 신입 분들이 super가 수퍼 클래스를 가리키는 것이라고 오해를 한다.

- 정의

   1) 자신을 뺀 조상 클래스에서 메서드를 찾는다.

   2) 그 중 가장 가까이에 있는 메서드를 가리킨다.



m1(), m2(), m3() 메서드를 가진 A class 예제


public class A {

  int a;
 
  public void m1() {
    System.out.println("A.m1()");
  }
 
  public void m2() {
    System.out.println("A.m2()");
  }
  public void m3() {
    System.out.println("A.m3()");
  }
}


m1(), m2() 메서드를 재정의 하고 m4() 메서드를 추가한 B class 예제

public class B extends A{

  int b;
 
  public void m1() { // A클래스의 m1() 재정의
    System.out.println("B.m1()");
  }
 
  public void m2() { // A클래스의 m2() 재정의
    System.out.println("B.m2()");
  }
  public void m4() { // 새로 추가
    System.out.println("B.m4()");
  }
}


B의 m2() 메서드와 A의 m3() 메서드를 재정의한 C class 예제

public class C extends B{

  int c;
 
  public void m2() { // B클래스의 m2() 재정의
    System.out.println("C.m2()");
  }
 
  public void m3() { // A클래스의 m3() 재정의
    System.out.println("C.m3()");
  }
 
  public void m5() { // 새로 추가
    System.out.println("C.m4()");
  }
  public void test1() {
    m1(); // B.m1()
    m2(); // C.m2()
    m3(); // C.m3()
  }
  public void test2() {
    super.m1(); // B.m1() <-- 재정의 하지 않았으면 상속 받은 메서드를 가리킨다.
                //            super는 무시된다.
    super.m2(); // B.m2()
    super.m3(); // A.m3()
    m4();       // B.m4()
    super.m4(); // B.m4(); <-- 재정의 하지 않았으면 super는 무시된다.
    m5();       // C.m5();
//    super.m5(); // 재정의하거나 상속 받은 메서드가 아니기 때문에, 컴파일 오류!
                // 조상 중에 m5() 메서드가 없다.
  }
}



인스턴스 생성 및 메서드 호출 코드


public static void main(String[] args) {

    C obj = new C();
    obj.test1();
    System.out.println("------------------------------");
    obj.test2();
    System.out.println("------------------------------");
}

- 실행 결과 :

B.m1()

C.m2()

C.m3()

------------------------------

B.m1()

B.m2()

A.m3()

B.m4()

B.m4()

C.m4()

------------------------------



블로그 이미지

필로그래머

,

[Java] 자바 상속과 super() 메서드


2017/05/12 - [java] - [Java] 상속 (inheritance)과 Object 클래스


상속과 super() 메서드

- 상속 받은 클래스의 모든 생성자는 수퍼 클래스의 기본 생성자를 호출한다.

   super();

- 만약 수퍼 클래스의 기본 생성자를 호출하는 코드를 작성하지 않는다면,

  컴파일러가 생성자 블록의 첫 줄에 이 코드를 자동으로 삽입한다.

- 수퍼 클래스의 생성자를 호출하는 코드는 반드시 첫 명령어로 작성해야 한다.

  그 앞에 주석은 



수퍼 클래스의 기본생성자를 호출하는 super() 메서드 예제


public class A {
  int a;
 
  public A() {
//    int a = 20; // 만약 수퍼 클래스의 생성자를 호출하는 코드 앞에 다른 명령이 있다면,
                //   컴파일 오류이다.
    // 반드시 첫 명령어로 이 클래스의 수퍼 클래스 생성자를 호출하는 코드를 작성해야 한다.
    super(); // Object 클래스 생성자 호출
    System.out.println("A()");
  }

}


public class B extends A {
  int b;
 
  public B() {
    super(); // A 클래스 생성자 호출
    System.out.println("B()");
  }
}

public class C extends B {
  int c;
 
  public C() {
    super(); // B 클래스 생성자 호출
    System.out.println("C()");
  }

}




인스턴스 생성 코드


 public static void main(String[] args) {
    new C();   

  }


- 실행결과 :

A()

B()

C()


*참고 : 

 수퍼 클래스(부모 클래스)의 기본 생성자를 호출하는 super() 메서드를 쓰지 않으면

 컴파일러는 자동적으로 추가시킨다.




블로그 이미지

필로그래머

,

[Java] 자바 final 명령어 (public static final ...)


final 명령어

- 값을 바꿀 수 없게 조회하는 용도로만 사용하고 싶은 변수가 있다면, 읽기 전용으로 만들어야 한다.

- final 명령을 변수 선언 앞에 붙인다.

- 조회용임을 직관적으로 알 수 있도록 보통 대문자로 변수명을 시작한다.

  그리고 단어와 단어 사이에는 '_' 문자를 사용하여 구분한다.

- 이렇게 조회용으로만 사용하는 변수를 상수 변수라고 부른다. 보통 "상수"라고 한다.

- 상수는 인스턴스 마다 고유의 값을 갖지 않고, 모든 인스턴스가 같은 값을 공유한다.

- 그래서 모든 인스턴스가 같은 값을 갖는 변수인 경우에는 보통 클래스 변수로 선언한다.

- 클래스 변수로 선언하면 인스턴스 마다 그 변수를 만들 필요가 없어 메모리가 절약된다.

- 상수 변수 중에서 공개하는 변수인 경우 public을 붙인다.



final 상수 예제


static class ArrayList {
   
    public static final int DEFAULT_SIZE = 10;
    public static final int GROW_SIZE = 1000;
   
    Object[] list;
    int length;
   
    public ArrayList() {
      list = new Object[10];
    // DEFAULT_SIZE = 20; => 오류 발생
    }
   
    public ArrayList(int capacity) {
      if (capacity < DEFAULT_SIZE || capacity < GROW_SIZE) {
        list = new Object[DEFAULT_SIZE];
      } else {
        list = new Object[capacity];
      }
  }
}



public static final int DEFAULT_SIZE = 10;

- static을 안 써줘도 되지만 static을 써줘서 클래스 상수로 만들어

  주면 인스턴스 마다 상수를 만들 필요가 없기 때문에 메모리가 절약

  된다.

- 상수 변수 중에서 공개하는 상수인 경우 public을 붙인다.







블로그 이미지

필로그래머

,

[Java] 자바 컬렉션 클래스 - List, Set, Map의 개념 정리


컬렉션 클래스

- 여러 개의 주소를 보관할 때 사용하는 클래스이다.

- 사용 목적 :

  배열의 문제는 인스턴스를 배열 크기 만큼만 저장할 수 있다는 것이다.

  더 추가로 저장하고 싶다면, 새 배열을 만들어야 한다.

  관리하기 너무 번거롭다.


자바에서 제공하는 컬렉션 클래스의 종류

 1) List 계열 

    - null 값을 저장할 수 있다.

    - 값을 중복 저장할 수 없다.

    - 값을 넣는 순서대로 저장한다.

      그래서 값을 꺼낼 때도 넣은 순서대로 꺼낼 수 있다.

    - 값을 꺼낼 때 숫자 인덱스를 사용하여 꺼낸다.

    - 예) ArrayList, LinkedList


2) set계열

    - HashSet

    - null 값을 저장할 수 있다.

    - 값을 중복 저장할 수 없다.

       이유 -> 집합이기 때문

    - 값을 넣을 때 인스턴스의 해시값을 기준으로 저장하기 때문에

      순서대로 저장되지 않는다.

      그래서 값을 꺼낼 때도 순서대로 꺼낼 수 없다.

    - 값을 꺼낼 때 숫자 인덱스로 꺼낼 수 없다.

    - 예) HashSet


3) Map 계열

    - 값을 저장할 때 key 개체의 해시 값(hash value)을 사용하여 저장한다.

    - 값을 꺼낼 때도 key 객체의 해시 값을 사용하여 꺼낸다.

    - 값을 중복 저장할 수 없다.

    - 순서대로 저장하고 순서대로 꺼낼 수 없다.

    - 보통 문자열 라벨을 사용하여 값을 저장하고 꺼낼 때 주로 사용한다.

    - 예)

       HashMap :

       - key나 value로 null을 허용한다.

       - 동기화를 지원하지 않는다.

          => 여러 스레드가 동시에 접근하여 값을 변경하는 것을 막지 않는다.

          => 데이터의 입출력 속도가 빠르다.

          => 대신 여러 스레드가 동시에 값을 바꿀 수 있기 때문에 

               멀티 스레드 상황에서 사용할 때 주의해야 한다.


       Hashtable : 

       - key나 value로 null을 허용하지 않는다.

       - 동기화를 지원한다.

          => 여러 스레드가 동시에 접근하더라도 그 중 한개의 스레드만이 접근할 수 있다.

          => 데이터의 입출력 속도가 느리다.

          => 대신 여러 스레드가 동시에 값을 바꿀 수 없기 때문에

               멀티 스레드 상황에서 안전하게 사용할 수 있다.



아래 참조


2017/05/17 - [java] - [Java] 컬렉션 클래스 - ArrayList를 직접 만들어서 내부 구조를 이해해보자

2017/05/16 - [java] - [Java] 컬렉션 클래스 - HashSet

2017/05/16 - [java] - [Java] 컬렉션 - [Map] HashMap(java.util.HashMap)




블로그 이미지

필로그래머

,