Dipa's document :: 'SH.Language/Java' 카테고리의 글 목록

안뇽하세요 ㅎ 이번시간에는 Map 컬렉션에 대해서 알아보도록 하겠습니다.

Map 컬렉션은 앞서 소개한 List 컬렉션 Set 컬렉션과는 또 다른 기능이 숨겨져 있습니다.

Map 컬렉션은 키(Key)와 값(Value)로 구성된 Entry객체를 저장하는 구조로서 하나의 키에 값이 저장이 되어 있습니다. 또한 Map 컬렉션의 특징으로는 키의 값이 중복저장이 안된다는 특징이 있습니다.

Map 컬렉션의 대표적인 클레스로는 HashMap, HashTable, TreeMap, Property가 있지만, TreeMap같은경우는 다음 포스팅떄 따로 TreeSet과 함께 포스팅을 하도록 하겠습니다.

* HashMap

HashMap의 특직으로는 앞서 포스팅한 HashSet의 특징과 같습니다. 다만 HashMap에서 equals()와 hashCode()를 Override를 할 경우에는 HashMap의 Key값을 가지고 비교를 하게 됩니다.

그렇다면 HashMap을 통한 소스코드를 보겠습니다.

public class HashMapExample {
	public static void main(String[] args) {
		Map<String,Integer> map = new HashMap<String,Integer>();
		
		map.put("지승훈", 90);
		map.put("지자바", 85);
		map.put("지가나", 70);
		map.put("지코인", 90);
		map.put("지이더", 80);
		map.put("지하라", 65);
		System.out.println("총 Entry 개수 : " + map.size());
		
		//1번쨰 방법
		Set<String> set = map.keySet();
		Iterator<String> iterator = set.iterator();
		while(iterator.hasNext()){
			String key = iterator.next();
			int value = map.get(key);
			System.out.println("key : " + key + ", value : " + value);
		}
		System.out.println();
		
		//2번쨰 방법
		Set<Map.Entry<String, Integer>> set2 = map.entrySet();
		Iterator<Map.Entry<String, Integer>> iterator2 = set2.iterator();
		while(iterator2.hasNext()){
			Map.Entry<String, Integer> entry = iterator2.next();
			String key = entry.getKey();
			int value = entry.getValue();
			System.out.println("key : " + key + ", value : " + value);
		}
	}
}

HashMap에서도 마찬가지로 객체를 가져와야 할경우에는 Iterator를 씁니다. 따라서 Iterator를 통하여 값을 처리를 하는 경우가 2가지가 있습니다. 첫번째로는 HashMap의 key값을 Set타입으로 받은 다음, 그 Set을 이용을 하여서 Iterator에 접목을 시키고 나머지는 Set에 저장되어 있는 Key값을 이용해 mapping을 하여 Key값에 대한 데이터를 처리를 합니다. 

두번째 방법으로는 첫번째 방법과 비슷한데, Map(Key,value) 전체 (즉, Entry)를 가져와서 Iterator를 처리를 하는 방법입니다.

* HashTable

HashTable 같은경우는 HashMap과 동일한 구조를 가지고 있습니다. 다만 차이점이 있다면 HashTable은 동기화(synchronized) 메소드로 구성되어 있기 떄문에, 멀티스레드 환경에서 데이터 처리를 안전하게 할수 있습니다.(Thread Safe)

 

Posted by SH후니
,

이번시간은 Set 컬렉션에 대해서 다뤄보도록 하겠습니다.ㅎㅎ

Set 컬렉션은 앞서 List 컬렉션과는 다르게 index로 관리되어지는 것이 아니라서 저장순서가 유지되어 지지 않습니다. 마치 하나의 주머니안에 데이터(객체)를 담는거라고 생각하시면 좋을꺼 같습니다 ㅎ

Set 컬렉션의 특징은 앞서 소개한 바로 순서가 보장되지 않고, 중복 저장이 되지 않는 것이 특징입니다.

따라서 Set 인터페이스의 메소드들은 기존의 List 인터페이스를 구현한 메소드들 중에서 인덱스와 관련된것은 뺴고 모두 동일하다고 생각하시면 됩니다.

Set 인터페이스를 구현한 대표적인 클레스로는 HashSet, TreeSet이 있습니다. 먼저 이 포스팅에서는 HashSet에서만 다루겠습니다. TreeSet같은경우에는 다음 포스팅에 나올 TreeMap과 함꼐 다루도록 하겠습니다. 

* HashSet

HashSet클레스 같은경우는 객체를 순서없이 저장을하고, 동일한 객체는 저장을 하지 않는 부분에 있어서 Set 인터페이스를 구현한 클레스 입니다.

HashSet 클레스는 객체를 저장을 하기전에 먼저 HashCode()를 통해서 해시코드를 얻어내고, 그 해시코드의 값을 가지고 동일한 객체가 저장이 되어 있는지 없는지에 대해서 조사를 하게 됩니다. 만약 동일한 해쉬코드가 나오면 다시 equals()를 통해서 한번더 점검을 한 후, 저장을 할지 말지에 대한 결정을 하게 됩니다.

HashCode()와, equals()메소드의 부분은 제 블로그에 나와 있으니 한번 참고를 해보시면 좋을꺼 같네요^^

그렇다면, 순서도 없이 저장된 객체를 어떻게 꺼내서 처리를 할 수 있을까요? 

결론부터 말하면 Set 인터페이스의 iterator() 라는 메소드 즉 반복자를 통해서 이용을 할 수 있습니다.  그럼 Iterrator 인터페이스를 보도록 하겠습니다.


public interface Iterator<E> { /** * Returns {@code true} if the iteration has more elements. * (In other words, returns {@code true} if {@link #next} would * return an element rather than throwing an exception.) * * @return {@code true} if the iteration has more elements */ boolean hasNext(); /** * Returns the next element in the iteration. * * @return the next element in the iteration * @throws NoSuchElementException if the iteration has no more elements */ E next(); /** * Removes from the underlying collection the last element returned * by this iterator (optional operation). This method can be called * only once per call to {@link #next}. The behavior of an iterator * is unspecified if the underlying collection is modified while the * iteration is in progress in any way other than by calling this * method. * * @implSpec * The default implementation throws an instance of * {@link UnsupportedOperationException} and performs no other action. * * @throws UnsupportedOperationException if the {@code remove} * operation is not supported by this iterator * * @throws IllegalStateException if the {@code next} method has not * yet been called, or the {@code remove} method has already * been called after the last call to the {@code next} * method */ default void remove() { throw new UnsupportedOperationException("remove"); } /** * Performs the given action for each remaining element until all elements * have been processed or the action throws an exception. Actions are * performed in the order of iteration, if that order is specified. * Exceptions thrown by the action are relayed to the caller. * * @implSpec * <p>The default implementation behaves as if: * <pre>{@code * while (hasNext()) * action.accept(next()); * }</pre> * * @param action The action to be performed for each element * @throws NullPointerException if the specified action is null * @since 1.8 */ default void forEachRemaining(Consumer<? super E> action) { Objects.requireNonNull(action); while (hasNext()) action.accept(next()); } }

이제 Iterator를 통해서 HashSet 클레스의 객체를 처리해보도록 하겠습니다.


public class Member {
	private String name;
	private int age;
	
	public Member(String name, int age){
		this.name = name;
		this.age = age;
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	
	@Override
	public int hashCode() {
		return name.hashCode() + age;
	}
	
	@Override
	public boolean equals(Object obj) {
		if(obj instanceof Member){
			Member member = (Member)obj;
			return (member.name.equals(this.name))&&(member.age==this.age);		
		}else{
			return false;
		}
	}
	
	@Override
	public String toString() {
		return name + " : " + age;
	}
}


public class HashSetExample {

	public static void main(String[] args) {
	
		Set<String> StringSet = new HashSet<String>();
		
		StringSet.add("지승훈");
		StringSet.add("홍길동");
		StringSet.add("지자바");
		StringSet.add("지후니");
			
		
		Iterator<String> iterator = StringSet.iterator();
		
		System.out.println("Set Size : " + StringSet.size());
		while(iterator.hasNext()){
			System.out.println(iterator.next().toString());
		}
		
		Set<Member> member = new HashSet<Member>();
		
		
		member.add(new Member("지승훈",27));
		member.add(new Member("홍길동",30));
		
		Iterator<Member> iterator2 = member.iterator();
		
		while(iterator2.hasNext()){
			System.out.println(iterator2.next().toString());
		}
	}

}


Posted by SH후니
,

오늘은 컬렉션 프레임워크 중에서 List 컬렉션에 대해서 알아보도록 하겠습니다.

List 컬렉션은 객체를 일렬로 늘어 놓은 구조로써, 객체를 인덱스로 관리를 하기 떄문에, 객체를 저장하면 자동으로 인덱스를 부여 하게 됩니다. 또한 리스트 컬렉션은 객체 자체를 저장하는 것이 아니라 객체의 참조 번지를 저장함으로써 힙영역에 객체를 가르키게 됩니다.

List 컬렉션의 특징으로는 앞서 컬렉션 프레임워크를 설명했을 때 처럼 순서를 유지하고 저장, 중복저장 가능하기 떄문에 같은 객체를 저장하게 되면 인덱스의 참조 번지는 같은 객체를 가르키게 되어 있고, null을 저장을 할 경우에는 인덱스에 있는 객체 참조 번지가 아무것도 가르키지 않게 됩니다.

* ArrayList

ArrayList 클레스에 객체를 저장을 하게 되면, 인덱스로 관리를 하게 됩니다. 일반 배열과 공통점은 인덱스로 관리한다는 점이지만, 배열과 ArrayList는 아주 큰 차이점이 있습니다.

바로 객체를 담을 크기가 유동적이라는 것입니다.!!

public class ArrayListExample {
   
	public static void main(String[] args) throws Exception {
		List<String> lists = new ArrayList<String>();
		
		
		//String 객체를 저장
		lists.add("java");
		lists.add("JDBC");
		lists.add("servlet/jsp");
		lists.add(2,"database");
		lists.add("ibaties");
		lists.set(4, "MyBatis");
		
		for(String list : lists){
			System.out.println(list.toString());
		}
		System.out.println();
		
		System.out.println("java라는 단어 있나요? " + lists.contains("java"));
		for(int i = 0; i<lists.size();i++){
			System.out.println(lists.get(i));
		}
		System.out.println();
		
		lists.remove(0);
		lists.remove("MyBatis");
		if(!lists.isEmpty()){
			lists.clear();
		}
		
		System.out.println("size : " + lists.size());
		
	}
}


ArrayList는 객체의 저장용량(capacity)을 default로는 10으로 설정이 되어 있다. 하지만 이 저장 용량을 초과하여 객체를 추가로 저장하게되면 자동적으로 값이 증가를 한다. 그렇다고 해서 다시 객체를 삭제를 한다고 해서 저장용량(capacity)이 줄어들지는 않는다.


public class ArrayListExample {
	 
    static int findCapacity(List<String> al) throws Exception {
        Field field = ArrayList.class.getDeclaredField("elementData");
        field.setAccessible(true);
        return ((Object[]) field.get(al)).length;
    }
   
	public static void main(String[] args) throws Exception {
		List<String> list = new ArrayList<String>();
		
		for(int i=0;i<100;i++){
			list.add(i+"");
			System.out.println("list_size : " + list.size() + ", list_ capacity : " + findCapacity(list));
		}
		
		System.out.println();
		
		for(int i=0;i<100;i++){
			list.remove(0);
			System.out.println("list_size : " + list.size() + ", list_ capacity : " + findCapacity(list));
		}
		
	}
}

* Vector

Vector클레스는 ArrayList 클레스의 구조와 비슷하지만 이 둘의 차이점은 멀티스레드의 환경에서 데이터를 처리를 하는지 입니다.. Vector클레스는 동기화된(synchronized)  메소드로 구성이 되어 있습니다. 따라서 멀티 스레드 환경에서 동시에 이 메소드를 접근을 할 수 없도록 구성 되어 있으며, 이를 스레드 안전 (Thread safe)라고 합니다


* LinkedList

linkedList 같은경우는 ArrayList와 사용 방법이 같습니다. 하지만 안의 내부구조가 다릅니다. ArrayList같은경우는 index에 의해서 관리가 되어 진다고 하면, LinkedList 같은경우는 인접해있는 노드를 연결해서 체인처럼 연결이 되어 있습니다. 따라서 중간의 데이터를 제거 하고싶다면 중간의 양쪽노드를 서로 이어주면 자동적으로 중간의 데이터는 없어지게 되는 것입니다.

예를 들어서 A->B->C 이렇게 연결이 되어 있을떄, B를 제거 하고싶으면 A에 연결되어있는 참조를 B가아닌 C로 이동을하여 A->C로 만들어주면 되겠죠? 


* ArrayList vs LinkedList

그렇다면 ArrayList와 LinkedList의 차이점은 무엇일까요? 둘은 쓰이는 곳이 다릅니다. 예를들어 수만개의 데이터가 있는데, 중간의 데이터를 추가하고 삭제하게되면 ArrayList값은 찾아서 데이터를 추가 삭제하고 그 다음칸에 있는 데이터는 한칸씩 뒤로 물러나게(index증가)됩니다. 그런데 이런 작업을 많이 하게되면 성능 저하의 원인이 되겠죠? 따라서 이런 작업이 많을 경우에는 LinkedList를 이용하는것이 좋습니다. 또한 검색/순차적으로 삭제&추가를 할 경우에는 ArrayList가 성능이 좋습니다.

정리 하자면, 

구분 

순차적으로 추가/삭제 

중간에 추가/삭제 

검색 

ArrayList 

빠르다

느리다 

빠르다 

 LinkedList

느리다 

빠르다 

느리다 



Posted by SH후니
,

오늘은 컬렉션의 List 컬렉션 에 대해서 알아보도록 하겠습니다.

먼저 Collection이란?

많은 응용프로그램을 개발을 하다보면, 수많은 데이터를 처리(추가, 삭제 검색..등등) 하게 됩니다.

하지만 이러한 처리에 대해서 어떻게 하면 가장 효율적으로 할수 있을까? 라는 생각으로 접근을 할 수 있습니다. 데이터를 가장 간단하게 처리하는 방법이 무엇이 있을까여? 바로 "배열" 입니다. 데이터를 담을 공간을 어느정도 담을지 선언을 하고, 처리를한다. 무척 간단하죠? ㅋㅋㅋ

근데 배열은 많은 문제점이 있습니다. 일단 데이터를 얼마나 처리를 할지에 대한 불문명함과 또한 그런거를 모르고 아주 크게 데이터 공간을 선언을하면, 불필요한 공간이 남게 됩니다. 그리고 어떠한 인덱스 자리에 데이터를 삭제를 하게되면..그 공간은 비어있게 되는 것이지요.. 이러한 많은 문제점등이 있기 떄문에.. JAVA에서는 배열의 문제점을 해결하고 자료구조(Data Structure)를 바탕으로 컬렉션 프레임워크(Collection FrameWork)를 만들었습니다.

그렇다면,, JAVA 컬렉션에는 어떠한 것들이 있는지 살펴보도록 하겠습니다.

 * JAVA Collection FrameWork 주요 인터페이스 및 클레스

LinkedList, Stack, Vector, ArrayList 는 List인터페이스를 구현한 클레스들이고, HashSet, TreeSet은 Set인터페이스를, 그리고 HashTable, HashMap, TreeMap, Property 는 Map 인터페이스를 구현한 클레스 입니다.


 * JAVA Collection FrameWork 주요 인터페이스의 특징

 인터페이스

구현클레스 

특징 

List 

ArrayList

Vector

LinkedList 

- 순서를 유지하고 저장

-중복 저장 가능 

Set 

HashSet

TreeSet 

- 순서를 유지하지 않고 저장

- 중복 저장 불가능 

Map 

HashMap

HashTable

TreeMap

Property 

- 키와 값의 쌍으로 저장

- 키는 중복 저장 안 됨 





Posted by SH후니
,

오늘은 wrapper(포장)클레스에 대해서 살펴보도록 하자.

Wrapper클레스란 기본타임(byte,char,short,int,long,float,double,boolean)을 객체로 생성하는 클레스를 말한다. 그리고 이러한 wrapper클레스는 불변객체로써 값을 변경할 수가 없다.

예를 들어, 즉 포장이란 무엇인가? 어떠한 내용물을 담는 상자? 라고 생각하면 쉬울 것이다.

그럼 안의 내용물을 변경하려면 그 포장지를 뜯어야하지 않는가? 포장을 한다는 거 자체가 선물의 의미인데..이것을 바꾸려고 하는것은..(약간..억지의 감이 있지만)

따라서 값을 변경을 하지못하고, 값을 변경하고 싶으면 포장 객체를 생성을 하여야 한다.

 기본타입

포장 클레스

byte

Byte

char

Character

short

Short

int

Integer

long 

Long

float

Float

double

Double

boolean

Boolean

기본 타입 -> 포장 객체 : Boxint(박싱)

포장객체 -> 기본 타입 : Unboxing(언박싱)

package wrapper;

public class BoxingUnboxing {

	public static void main(String[] args) {
		Integer obj1 = new Integer(100);
		Integer obj2 = new Integer("100");
		Integer obj3 = Integer.valueOf(100);
		
		int value1 = obj1.intValue();
		int value2 = obj2.intValue();
		int value3 = obj3.intValue();
		
		System.out.println(value1);
		System.out.println(value2);
		System.out.println(value3);

//결과값 //100 //100 //100 } }

* 자동 박싱, 언박싱

박싱에도 자동 형변환처럼 자동적으로 변환을 하는 기능이 있다. 이 기능은 자바5부터 추가된 기능이다.

package wrapper;

public class AutoBoxingAutoUnboxing {

	public static void main(String[] args) {
		Integer obj1 = 100; //자동 박싱

int value = obj1; //자동 언박싱 System.out.println(obj1.intValue());

System.out.println(value);

//결과값 //100 //100 } }

마지막으로 ==,!=의 연산자이다. Wrapper클레스는 객체이기 떄문에 결국 ==,!=연산자는 포장객체의 내부의 값을 비교를 하는것이 아닌 객체의 참조를 비교를 하는것이다. 내부의 값을 비교를 하기위해서는 언박싱을 하여 비교를 하여야 하는데, 예외 적으로 내부의 값을 비교를 하는 짓도 한다..^^

타입 

값의 범위 

boolean 

true,false 

char 

\u0000 ~ \u0071 

 byte,short,int

-128~127 

왜 이런 현상이 나오는 것일까? 바로 위에 3가지의 기본 타입의 포장클레스의 캐쉬 떄문이다.

각각의 캐쉬를 소스코드로 확인을 해보자.

 private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }
private static class CharacterCache {
        private CharacterCache(){}

        static final Character cache[] = new Character[127 + 1];

        static {
            for (int i = 0; i < cache.length; i++)
                cache[i] = new Character((char)i);
        }
    }


Posted by SH후니
,

오늘은 String클레스에 대해서 다뤄볼까 한다.

전에 Object클레스의 equals()를 설명을 하면서 String클레스에 대해서 다시 한번 언급을 하려고 했던 부분이 있다. 바로 객체를 선언을 할떄의 차이다.

public class internMethod {

	public static void main(String[] args) {
		String input01 = "홍길동";
		String input02 = new String("홍길동");
		
		System.out.println(input01.equals(input02));
	}
}

이것의 결과는 당연히 true가 나온다. 객체안에 있는 데이터를 비교를 하기 때문이다.

하지만.. 이렇게 되면 어떻게 될까?

public class internMethod {

	public static void main(String[] args) {
		String input01 = new String("홍길동");
		String input02 = new String("홍길동");
		
		System.out.println(input01.equals(input02));
	}
}

당연히 객체의 번지 주소를 비교를 하기 떄문에 false가 나오게 된다.

이러한 이유는 과연 무엇일까?? 

첫번쨰 소스코드의 차이의 이유는 바로 JVM에서 String 객체를 생성을 하였을 경우, 어떤 메모리 영역에 저장이 되는지에 대한 이유이다.

new 생성자를 이용해서 인스턴스를 만들고, heap 메모리에서 관리한다는 사실은 공통적이지만, String 객체는 불변객체로 한번 생성이 되면 값은 변하지가 않는다. 이런식으로 다른객체지만 객체 안의 데이터가 같은것을 무한히 생성을 하다보면 결국 메모리 관리 측명에서 비효율적이라는 판단을 한다.

이래서 만들어진 메모리 영역이 String Constant Pool이다.

또한 문자열을 변경하는 작업을 많이 할경우에는 StringBuffer와 StringBuilder클레스를 사용하면된다.

이 두 클레스는 내부 버퍼에 문자열을 저장해 두고 그안에서 추가, 수정 삭제 작업을 할 수 있도록 설계 되어 있다. 두가지를 사용하는 방법은 동일하나.. 하나의 차이가 있다면,

StringBuffer는 멀티 스레드 환경에서 사용할 수 있도록 동기화가 적용되어 있어 스레드에 안전하고,

StringBuilder는 단일 스레드 환경에서만 사용하도록 설계 되어 있다.


Posted by SH후니
,

Class클레스란?

자바의 클레스와 인터페이스의 메타데이터(클레스의 이름, 생성자의 정보, 필드 정보, 메소드 정보)등등을 관리 하는 클레스이다.

즉, 클레스 안의 멤버 변수와 메소드 등등의 리턴 타입이라던지...파라메터값 등등을 알 수 있다는 뜻이다.

그 중에서 오늘은 getClass(), forName()를 살펴보고자 한다. 


저 두개의 메소드의 공통점은 무엇일까? 일단 힌트는 둘다 Class의 정보를 받아볼수 있는 메소드이다.

코드를 통해 살펴보자..

 
  
public final native Class<?> getClass();
 
public static Class<?> forName(String className)
                throws ClassNotFoundException {
        Class<?> caller = Reflection.getCallerClass();
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }

일단 둘다 공통점은 Class를 return한다는 것이다. 

차이점은? 

getClass() 같은경우는 Object클레스에 있는 메소드로써 객체를 생성을 한뒤 호출 할 수 있는 메소드이다.

forName() 같은경우는 객체를 생성하기 전에 직접 객체를 얻을 수 있다.

package api_class._class;
 
public class Car {
	private String model;
	private String owner;
	
	public Car(){
		
	}
	
	public Car(String model){
		this.model = model;
	}
	public String getModel() {
		return model;
	}
	public void setModel(String model) {
		this.model = model;
	}
	public String getOwner() {
		return owner;
	}
	private void setOwner(String owner) {
		this.owner = owner;
	}
}
package api_class._class;
 
public class ClassExample {

	public static void main(String[] args) {
		Car car = new Car();
		Class clazz1 = car.getClass();
		System.out.println(clazz1.getName());
		System.out.println(clazz1.getSimpleName());
		System.out.println(clazz1.getPackage().getName());
		
		System.out.println();
		
		try {
			Class clazz2 = Class.forName("api_class._class.Car");
			System.out.println(clazz2.getName());
			System.out.println(clazz2.getSimpleName());
			System.out.println(clazz2.getPackage().getName());
		} catch (ClassNotFoundException e) {
			System.out.println("존재하지 않은 클레스");
		}
		
	}

}

위의 소스는 단지 클레스의 이름에 대한 정보만 나와있다.. 하지만 생성자, 필드 메소드의 정보도 얻을수 있다고 하였는데?

당연 정보를 얻어 올수 있다!! 이것이 바로 Reflection(리플렉션)이다.

각각의 정보를 얻어 올수 있는 메소드 (생성자 - getDeclaredConstructors(), 필드 - getDeclareFields(), 메소드 - getDeclaredMethods() ) 이다. 

이들의 리턴값은 각각의 타입에 대한 배열이다. (왜냐하면,, 여러개 있을수 있으니까?)

하지만 저들의 메소드는 그 클레스에 선언된 것만 가져온다. 만약 상속된 멤버 까지 가져오고 싶다면.. 중간에 Declare를 뺴면된다..? 쉽죠? ㅋㅋ

이제 각각의 정보를 가져오는 소스를 보도록 하자

package api_class._class;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectionExample {

	public static void main(String[] args) throws ClassNotFoundException {
		try {
			Class clazz = Class.forName("_class.Car");
			System.out.println("[생성자 정보]");
			Constructor[] constructors = clazz.getDeclaredConstructors();
			for(Constructor constructor : constructors){
				System.out.print(constructor.getName() + "(");
				Class[] parameters = constructor.getParameterTypes();
				printParameters(parameters);
				System.out.println(")");
			}
			
			System.out.println();
			
			System.out.println("[필드 정보]");
			Field[] fields = clazz.getDeclaredFields();
			
			for(Field field : fields){
				System.out.println(field.getType().getSimpleName() + " " + field.getName());
			}
			System.out.println();

			System.out.println("[메소드 정보]");
			Method[] methods = clazz.getDeclaredMethods();
			for(Method method : methods){
				System.out.print(method.getReturnType().getSimpleName() + " " + method.getName() + "(");
				Class[] parameters = method.getParameterTypes();
				printParameters(parameters);
				System.out.println(")");
			}
			
		} catch (ClassNotFoundException e) {
			System.out.println("클레스가 없음");
		}
		
	}
	
	static void printParameters(Class[] parameters){
		for(int i=0;i<parameters.length;i++){
			System.out.print(parameters[i].getName());
			if(i<(parameters.length-1)){
				System.out.print(",");
			}
		}
	}

}

이제 마지막으로 그렇다면.. 이들을 정보만 알지말고 객체를 new연산자를 이용해서 생성하지 않고, 메소드를 호출하여 사용할 수 있을까?

그것이 바로 newInstance()를 이용하여 사용할 수있다. newInstance()는 new연산자를 이용하여 객체를 생성을 하지않고, 동적으로 객체를 생성하여, runtime시에 발생을 하게 된다.

package api_class._class;

public interface Action {
	public void execute();
}


package api_class._class;

public class ReceiveAction implements Action{

	@Override
	public void execute() {
		System.out.println("데이터를 받습니다.");
	}
	
}


package api_class._class;

public class SendAction implements Action{

	@Override
	public void execute() {
		System.out.println("데이터를 보냅니다.");
	}
	
}


package api_class._class;

public class NewInstanceExample {

	public static void main(String[] args) {
		try {
			Class clazz = Class.forName("_class.ReceiveAction");
			Action action = (Action) clazz.newInstance();
			action.execute();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
	}

}


'SH.Language > Java' 카테고리의 다른 글

[JAVA] Wrapper 클레스  (0) 2018.01.04
[JAVA] String, StringBuffer, StringBuilder 클레스  (0) 2018.01.02
[JAVA] Object클레스 - clone()  (0) 2017.12.14
[JAVA] Object클레스 - toString()  (0) 2017.12.11
[JAVA] Object클레스 - hashCode()  (0) 2017.12.06
Posted by SH후니
,

Object클레스의 clone()메소드는 복제를 의미한다. 따로 설명을 하기가 어려운데..그냥 정말 복제다.

복제를 하는 이유는 무엇일까? 원본 객체를 유지를 하기위한 방패막이라고나 할까?ㅋㅋㅋ

하지만 복재는 두가지 분류로 나뉜다. 얕은복제, 깊은복제

1) 얕은 복제 : 객체의 기본 타입만 복제(참조 타입의경우 객체의 번지가 복사 -> 참조를 바꾸면? 같이 바뀐다..)

2) 깊은 복제 : 객체의 기본 타입, 참조 타입 모두가 바뀐다.

그럼, 이제 코드를 한번 확인을 해보자.

package object;

public class Member implements Cloneable{
	public String id;
	public String name;
	public String password;
	public int age;
	public boolean adult;
	
	public Member(String id, String name, String password, int age, boolean adult){
		this.id = id;
		this.name = name;
		this.password = password;
		this.age = age;
		this.adult = adult;
	}
	
	public Member getMember(){
		Member cloned = null;
		try {
			cloned = (Member)this.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return cloned;
	}
}

Clone()메소드를 작성시에 유의사항은 복사할 객체를 Cloneable 인터페이스를 구현을 해야하고, Clone()메소드에 대한 예외 처리를 해야 한다는 것이다.

getMember()드에서 object클레스의 clone()을 override를 해야하지만 default로는 얕은복제를 하기떄문에 얕은 복제를 먼저 구현을 하고 실험을 해보자.


package object;

public class MemberExample {
	public static void main(String[] args){
		Member original = new Member("blue","홍길동","12345",25,true);
		
		Member cloned = original.getMember();
		original.age = 30;
		
		System.out.println("[원본 객체의 필드값]");
		System.out.println("id: " + original.id);
		System.out.println("name: " + original.name);
		System.out.println("password: " + original.password);
		System.out.println("age: " + original.age);
		System.out.println("adult: " + original.adult);
		
		System.out.println();
		
		System.out.println("[복제 객체의 필드값]");
		System.out.println("id: " + cloned.id);
		System.out.println("name: " + cloned.name);
		System.out.println("password: " + cloned.password);
		System.out.println("age: " + cloned.age);
		System.out.println("adult: " + cloned.adult);
	}
}

결과에서 나왔듯이 원본객체를 바꿔도 복제한 객체에는 바뀌지 않는것을 볼수가 있다.

그런데 여기서 의문을 가지는 개발자가 있을것이다. 처음에는 얕은 복제는 기본타입만 복제가 된다고 했는데 String을 바꿨는데 원본과 복제된 객체의 값이 다르다? 그러면 참조타입도 복제가 되었다는 말아닌가???? ㅡㅡ 여기서.. 여기서 나의대답은!! String은 참조 타입이다. 하지만 String같은경우는 값이 바뀌는 것이 아니라 오히려 그 데이터의 객체를 하나 생성을 한다...

말이 어려울수 있으니..이부분에 대해서는 String 클레스에서 다시한번 설명하도록 하겠다.


package object;

import java.util.Arrays;

public class Member2 implements Cloneable{
	public String name;
	public int age;
	public int[] scores;
	public Car car;
	
	public Member2(String name, int age, int[] scores, Car car) {
		this.name = name;
		this.age = age;
		this.scores = scores;
		this.car = car;
	}
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		Member2 cloned = (Member2)super.clone();
		cloned.scores = Arrays.copyOf(this.scores, this.scores.length);
		cloned.car = new Car(this.car.model);
		
		return cloned;
	}
	
	public Member2 getMember(){
		Member2 cloned = null;
		try {
			cloned = (Member2)this.clone();
		} catch (CloneNotSupportedException e) {}
		
		return cloned;
	}
}

깊은 복제를 하기위해서는 override를 해야하는데, 일단 기본타입을 위해서 먼저 object클레스의 clone()을 한번 호출을한뒤, 남은 클레스타입과 배열타입을 복사를 한다.

package object;

public class Member2Example {
	public static void main(String[] args){
		Member2 original = new Member2("홍길동",25,new int[]{90,90},new Car("소나타"));
		
		Member2 cloned = original.getMember();
		cloned.scores[0] = 50;
		cloned.scores[1] = 50;
		
		System.out.println("[원본 객체의 필드값]");
		System.out.println("name : " + original.name );
		System.out.println("age : " + original.age );
		for(int i =0;i<original.scores.length;i++){
			System.out.print(original.scores[i] + " ");
		}
		
		System.out.println();
		
		System.out.println("[복제 객체의 필드값]");
		System.out.println("name : " + cloned.name );
		System.out.println("age : " + cloned.age );
		for(int i =0;i<cloned.scores.length;i++){
			System.out.print(cloned.scores[i] + " ");
		}
	}
}


Posted by SH후니
,

Object클레스의 toString()메소드느 객체의 문자 정보를 리턴을 한다. 그리하여 Object 클레스의 toString()메소드를 Override를 하여 사용자의 입맛에 맡게 사용하면된다.


먼저, Object클레스의 toString()메소드이다.

public class ObjectToStringMethod {

	public static void main(String[] args) {
		Object tmp = new Object();
		String output = tmp.toString();
		System.out.println(output);

	}
}

Object클레스의 toString()"클레스명@16진수헤쉬코드" 를 반환을 한다.


다음은 toString()를 Override를 한 클레스와 실행부분이다.



class SmartPhone{
	private String company;
	private String os;
	
	public SmartPhone(String company, String os){
		this.company = company;
		this.os = os;
	}
	
	@Override
	public String toString() {
		return company + " - " + os;
	}
}
public class OverrideToStringMethod {
	public static void main(String[] args) {
		SmartPhone smartPhone = new SmartPhone("구글","안드로이드");
		String output = smartPhone.toString();
		System.out.println(output);
		System.out.println(smartPhone);
	}
}

여기서 System.out.println()메소드의 경우에 객체를 집어 넣으면 어떻게 될까? 매개값으로 객체를 넣을경우,

Override를 통해 구현한 toString()를 호출해서 리턴값을 받아 출력하도록 되어 있다.

Posted by SH후니
,


이 메소드는 해싱(hashing)기법에 사용되는 '해쉬함수(hash function)'를 구현 한 것이다. 해싱은 데이터 관리기법 중의 하나인데 다량의 데이터를 저장하고 검색하는데 유용하다. (자료구조 - 해쉬참고바람)


일반적으로 해시코드가 같은 두 객체가 존재하는 것이 가능하지만, Object클레스의 hashCode() 메소드는 객체의 메모리 번지를 이용해서 해시코드를 만들어 리턴하기 떄문에 객체마다 다른 값을 가지고 있다.


컬렉션부분에서의 HashSet, HashMap, Hashtable과 같은경우에서 논리적 동등 비교(데이터 비교)시 hashCode()를 overriding을 할 필요성이 있다. 

Key 객체의 주소가 달라도 같은 내용의 객체이면, hashCode()는 같은 hashCode를 리턴 해야 할 것이다. 

이유는 Key객체의 내용이 같을 떄, 같은 값의 hashCode를 리턴해야 HashTable, HashMap과 같은 것을 사용할 떄, 제대로 된 key로써의 역할을 하기 떄문이다.


먼저 equals()를 호출하기전에 hashCode()를 통한 해쉬코드의 값을 호출을 하고 비교를 하고 같으면, 다시 한번 equals()메소드를 통해 두 객체의 내부 데이터가 동등한지 확인 작업을 하고 동등객체 인지의 유무를 결정을 한다.

따라서, 사용자가 직접 만든 클래스를 Key로 사용하기 싶으면, 반드시 hashCode()를 overridung를 해야한다.


아래의 소스 코드를 보면

public class ObjectEqualsMethod {

public class Key {
	private int number;
	
	public Key(int number) {
		this.number = number;
	}

	public int getNumber() {
		return number;
	}

	public void setNumber(int number) {
		this.number = number;
	}
	
	@Override
	public boolean equals(Object obj) {
		if(obj instanceof Key) {
			Key compareKey = (Key)obj;
			if(this.number == compareKey.number) {
				return true;
			}
		}
		return false;
	}
	
    }
}
import java.util.HashMap;

public class KeyExample {
	public static void main(String[] args) {
		
		//Key객체를 식별키로 사용해서 String 값을 저장하는 HashMap 객체를 생성 
		HashMap<Key,String> hashMap = new HashMap<Key,String>();
		
		hashMap.put(new Key(1), "홍길동");

		System.out.println((hashMap.get(new Key(1))));
	}
}

Key값으로 Key객체를 사용하였는데도, 결과는 null이라는 충격적인 결말이 나온다. 이는 hashCode()를 재정의를 하지를 않아, Object클래스의 

hashCode()를 사용을 하기 떄문에 저장하고, 불러오는 과정의 Key객체는 서로 다른 객체로 판별이 되어 null값으로 처리가 된다. 


결국 Object클레스의 hashCode()를 재정의를 하여여 한다는 결론이 나온다.


HashSet, HashMap, Hashtable은 해시코드 값이 다르면 다른 객체로 판단하고, 해시코드 값이 같으면 equals()메소드를 다시 비교한다. 그렇기 떄문에 hashCode() 메소드가 true가 나와도 equals()의 리턴값이 다르면 다른 객체가 된다. 

Posted by SH후니
,