벡터(Vector)

벡터는 ArrayList와 같이 선형적으로 자료를 담을 수 있는 List 컬렉션입니다. ArrayList와는 다르게 동기화 처리를 하기 때문에 ArrayList보다는 속도가 느리다는 단점이 있습니다. 하지만 여러 쓰레드에서 같은 List를 써야할 필요가 있다면 Vector를 사용하시는 것이 동기화 오류를 만들지 않는 방법이겠죠. 

동기화에 대한 예는 맨 아래에 설명하도록 하겠습니다. 지금부터 사용법에 대해서 알아보도록 합시다.

사용법

벡터는 Generic을 사용합니다. 꺽쇠 '<', '>' 에 자료 타입을 지정해주고 수행합니다. 꺽쇠 안에는 원소로 사용할 객체의 클래스를 명시해주면 됩니다. 아래는 Integer 형의 자료를 Vector에서 다룬다는 선언을 보여줍니다.

 

 

Vector<Integer> v=new Vector();

 

1 - 원소 추가와 삭제, 읽기(add, remove, get)

선형으로 자료를 담고 있습니다. 그래서 데이터를 넣으면 차례차례 뒤에 데이터들이 붙어서 쌓이게 됩니다. 특정 위치의 데이터도 삭제할 수 있습니다. 그리고 정수형 index로 원소를 가져올 수 있습니다. 아래가 그 예제를 보여줍니다. 

import java.util.Vector;	//클래스 import
public class Main {
	
	public static void main(String[] ar){
		Vector<Integer> v=new Vector();
		v.add(20);	//0
		v.add(50);	//1
		v.add(70);	//2
		v.add(100);	//3
		
		for(int i=0;i<v.size();i++) {
			System.out.println(i+"번째 원소:"+v.get(i));
		}
		
		System.out.println("\n index 1 원소 삭제");
		v.remove(1);
		
		for(int i=0;i<v.size();i++) {
			System.out.println(i+"번째 원소:"+v.get(i));
		}
	}
}

 

결과

0번째 원소:20
1번째 원소:50
2번째 원소:70
3번째 원소:100

 index 1 원소 삭제
0번째 원소:20
1번째 원소:70
2번째 원소:100

 

예제를 보면 차례차례 20, 50, 70, 100의 데이터를 넣어주고 있습니다. 그리고 for문을 돌면서 출력해주고 있습니다. 차례대로 출력이 되는 것을 확인할 수 있죠? 그런데 index가 1인 50을 삭제하게 되면 삭제된 뒤의 원소들이 전부 앞으로 당겨지게 됩니다.

 

 

remove

만약 size()보다 큰 index를 갖는 원소를 삭제하거나 get()으로 읽어온다면 ArrayIndexOutOfBoundsException이 발생하게 되므로 size() 체크를 항상 해주셔야합니다. 맨 아래 라인에 v.remove(10); 코드를 추가해서 확인해보도록 하세요.

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 10
	at java.base/java.util.Vector.remove(Vector.java:844)
	at aa.Main.main(Main.java:23)

 

원소를 모두 삭제하고 싶다면 clear(), removeAllElements()를 사용하면 모두 지워집니다.

 

2. Vector를 반복할 수 있는 여러 방법

위의 예제는 단순 for문으로 i를 인덱스 삼아서 get()으로 원소를 읽어왔습니다. 이밖에도 여러 방법이 있는데, 그 방법을 소개합니다. 

2-1 forEach 메소드 사용

	Vector<String> v=new Vector();
	v.add("korea");
	v.add("england");
	v.add("rusia");
		
	v.forEach((item)->{
		System.out.println(item);
	});

람다식을 이용해서 순회할 수 있습니다. 함수 안 괄호에는 원소가 들어갑니다. 그 변수명이 item이지요. 그리고 중괄호('{}')에는 동작부를 구현하면 됩니다. 람다식을 몰라도 사용하는 데에는 문제가 없습니다.

2-2 Iterator객체로 순회

	Iterator<String> it=v.iterator();
	while(it.hasNext()) {
		String item=it.next();
		System.out.println(item);
	}

 

사이즈를 체크하지 않으면서 순회하고 싶다면 Iterator클래스로 객체를 만들어 사용하는 방법이 있습니다. Iterator클래스는 Java.util 패키지에 존재하니 import하여 사용해보세요. 순회에 필요한 메소드는 hasNext()와 원소를 가져오는 next()메소드만 알고 있으면 됩니다.

메소드 설명
hasNext() 이 다음에 원소가 있는지 확인합니다. 있으면 true, 없으면 false를 반환하지요. 대체로 while안의 조건문에 사용합니다.
next() 다음 원소를 가져옵니다. 반환되는 객체는 Generic으로 넘겨준 원소의 자료형입니다.

위 코드의 결과는 그 이전의 결과와 같습니다.

 

3. Vector와 Vector를 합치기

 

 

public static void main(String[] ar){
		Vector<String> v1=new Vector();
		v1.add("r");
		v1.add("e");
		v1.add("a");
		v1.add("k");
		
		Vector<String> v2=new Vector();
		v2.add("w");
		v2.add("o");
		v2.add("n");
		
		v1.addAll(v2);
		v1.forEach((item)->{
			System.out.print(item);
		});
		System.out.println();
	}

 

결과

reakwon

 

addAll() 메소드로 벡터와 벡터를 합칠 수 있습니다. 단, 두 벡터는 같은 Generic 형식을 사용해야하고 합칠 벡터 뒤에 그 벡터가 붙습니다. 또는 생성자를 이용해서 객체 생성시에 벡터를 합칠 수 있습니다. 아래와 같은 형식으로 사용할 수 있습니다. 아래 예는 v2에 v1을 객체 생성시에 합칩니다.

Vector<String> v2=new Vector(v1);

 

4. 원소가 존재하는 지 확인

	public static void main(String[] ar){
		Vector<String> v=new Vector();
		v.add("r");	v.add("e");
		v.add("a"); v.add("kwon");
		
		System.out.println(v.contains("r"));
		System.out.println(v.contains("z"));
	}

 

결과

true
false

 

contains() 메소드로 Vector에 원소가 있는지 확인할 수 있습니다. 있으면 true, 없으면 false를 반환합니다.

 

5. Vector 정렬

public static void main(String[] ar){
	Vector<String> strV=new Vector();
	strV.add("reakwon");
	strV.add("hello");
	strV.add("world");
		
	//알파벳 순으로 정렬
	Collections.sort(strV);
	strV.forEach((item)->{
		System.out.println(item);
	});
		
	System.out.println();
		
	Vector<Integer> intV=new Vector();
	intV.add(5);
	intV.add(1);
	intV.add(3);
		
	//오름차순 정렬
	Collections.sort(intV);
	intV.forEach((item)->{
		System.out.println(item);
	});
}

Collections.sort() 메소드를 이용해서 Vector의 데이터를 정렬할 수 있습니다. 기본적으로 숫자는 오름차순, 문자열은 사전순으로 정렬됩니다.

결과

hello
reakwon
world

1
3
5

혹은 내가 만든 객체를 정렬하려면 어떻게할까요? 사람 객체를 키순으로 정렬하고 싶다면, 또는 이름 순으로 정렬, 나이순으로 정렬하고 싶다면 어떻게할까요? 우리가 직접 비교해서 Collections에게 알려줘야합니다. 그것에 대한 설명은 아래의 링크를 참고해주세요. 사용법은 동일합니다.

reakwon.tistory.com/91

 

[자바/JAVA] Collections와 ArrayList를 이용한 객체 정렬

정렬 우리는 정렬에 관해서 배우기도 하였고 구현도 해보았습니다. 그래서 어떻게 정렬이 되는지 알고 있죠. 하지만 실제 프로그래밍하는 상황에서 이 정렬을 직접구현해서 프로그램을 만들지

reakwon.tistory.com

6. Thread-Safe 예

아까 이야기했던 동기화에 대한 부분을 확인해보도록 합시다. 우선 ArrayList로 아래의 코드를 짜면서 관찰해보죠. 

 

 

 

public class Main {
	
	static ArrayList<String> list=new ArrayList();
	public static void main(String[] ar){
		list.add("reakwon");
		list.add("hello");
		list.add("world");
		
		Thread thread=new Thread(new Runnable() {
			@Override
			public void run() {
				list.forEach((item)->{
					//1초마다 원소를 출력
					try {
					Thread.sleep(1000);
					}catch(InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(item);
				});
			}
		});
		
		thread.start();	//thread 시작
		
		//thread가 forEach문을 먼저 수행할 여유를 주기 위해 1초 기다림
		try {
			Thread.sleep(1000);
		}catch(InterruptedException e) {
			e.printStackTrace();
		}
		//thread가 forEach() 하는 중에 원소추가
		list.add("thread-unsafe");
	}
}

 

결과

reakwon
Exception in thread "Thread-0" java.util.ConcurrentModificationException
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1513)
	at aa.Main$1.run(Main.java:19)
	at java.base/java.lang.Thread.run(Thread.java:831)

 

위의 코드는 아주 간단한 코드입니다. thread라는 쓰레드와 메인 스레드는 list라는 ArrayList객체를 공유합니다. thread는 그 list를 forEach()로 1초마다 그 원소를 출력하는 역할을 하고, 메인 스레드는 스레드 생성하고 1초가 지난 다음 list에 원소를 추가하는 상황입니다. 이때 메인스레드가 thread가 forEach()를 수행하는 중에 list에 원소를 집어넣는 접근을 하게 되는데, 이때 두 스레드 동시에 list에 접근하게 됩니다. 그렇게 되면 위처럼 ConcurrentModificationException이 발생하게 되는 상황이 되죠. 어떻게 고칠까요?

ArrayList를 Vector로만 바꿔주면 이 문제는 해결됩니다.

static Vector<String> list=new Vector();

MainThread는 thread가 forEach()를 종료할때까지 Vector객체에 접근할 수 없고, 반대로 메인스레드의 add()가 끝날때까지 thread는 Vector객체에 접근할 수 없습니다.

Vector는 동작마다 동기화를 걸어줍니다. 이런 일은 속도롤 떨어지게 만드는 작업이지만 멀티 스레드 환경에서는 안전한 작업이죠. 그래서 여러 스레드가 있는 환경에서 개발한다면 Vector를 사용하고 단일 스레드 환경에서는 ArrayList를 활용하시면 되겠습니다.

이상으로 Vector에 대한 포스팅을 마칩니다.

 

 

반응형
블로그 이미지

REAKWON

와나진짜

,

String을 왜 인코딩하고 디코딩할까요? 인코딩과 디코딩을 해야하는 상황이 있습니다. 만일 DB가 한글을 지원하지 않는 경우 한글로 된 문자를 숫자로 encoding해서 DB에 저장하면 되고, 사용자에게 보여줄때는 다시 decoding해서 보여주면 됩니다.

또는 암호화할때 문자열을 encoding한 후에 암호화하게 됩니다.

이 밖에도 문자열을 encoding과 decoding을 해야할 상황이 있겠죠? 그러므로 문자열을 어떻게 encoding할지 decoding할지 알아보도록 하겠습니다.

 

byte[] getBytes()

byte[] getBytes(Charset charset)

byte[] getBytes(String charsetName)

문자열을 인코딩된 byte형태로 넘겨줍니다. 매개변수없이 그냥 getBytes()메소드를 사용하면 플랫폼에 따른 default charset을 사용합니다.

만일 특정 charset을 지정할 경우 인자를 받는 getBytes메소드를 사용하면 됩니다. ISO-8859-1, euc-kr, utf-8 등의 charset이 존재하는데 encoding과 decoding할때 이 chatset을 맞춰서 해야합니다. 그러지 않을 경우 문자가 깨지는 현상이 발생하게 됩니다. 

 

자, 그러면 이제 실제 프로그램을 짜면서 사용법을 알아보도록 합시다. 여기서는 가장 많이 사용하는 UTF-8로 charset을 지정했습니다.

public static void main(String[] args){
	String str="reakwon의 블로그";
	//default charset으로 인코딩된 바이트 배열	
	byte[] bytes=str.getBytes();
    //인코딩된 바이트 출력
	System.out.print("Default charset encoding: ");
	for(int i=0;i<bytes.length;i++)
		System.out.print(bytes[i]+" ");
	System.out.println();
	//default charset으로 디코딩된 문자열 출력
	String decoded=new String(bytes);
	System.out.println(decoded);
		
	System.out.println();
	try {
    	//UTF-8로 인코딩된 바이트 배열
		bytes=str.getBytes("UTF-8");
		System.out.print("UTF-8 charset encoding: ");
		for(int i=0;i<bytes.length;i++)
			System.out.print(bytes[i]+" ");
		System.out.println();
        //이 바이트 배열을 default charset으로 디코딩된 문자열 출력 : charset이 다르므로 한글이 깨짐.
		decoded=new String(bytes);
		System.out.println(decoded);
		//인코딩된 UTF-8로 디코딩되어 한글이 깨지지 않음.
		decoded=new String(bytes,"UTF-8");
		System.out.println(decoded);
			
	} catch (UnsupportedEncodingException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
}

 

문자열은 한글이 섞여있는 문자열입니다. 우선 default charset으로 바이트 배열을 얻어오고 출력해줍니다. 인코딩된 바이트 배열을 다시 디코딩하여 출력해줍니다. String으로 decoding하는 방법은 무척 간단합니다. 아래의 String 클래스의 생성자를 사용하면 됩니다.

 

 

String(byte[] bytes) : default charset으로 decoding한 문자열

String(byte[] bytes, String charsetName) : 특정 charset으로 decoding한 문자열

 

try...catch 안에서는 UTF-8로 인코딩하고 인코딩된 값을 출력해줍니다. 그 이후 String 인코딩 된 값을 출력해주는데요. 만약 default charset으로 디코딩한 후 문자열을 출력하면 어떻게 될까요? 예상하셨겠지만 영어를 제외한 한글을 깨져서 나옵니다.

그래서 인코딩된 방식과 같이 UTF-8로 디코딩해야합니다. 그래야만 한글이 깨지지 않지요. 그래서 바로 위의 String의 2번째 생성자를 사용해서 charset을 지정합니다. 그 후 출력하면 정상적으로 한글이 출력됩니다.

결과를 확인해보세요.

 

결과

Default charset encoding: 114 101 97 107 119 111 110 -64 -57 32 -70 -19 -73 -50 -79 -41 
reakwon의 블로그

UTF-8 charset encoding: 114 101 97 107 119 111 110 -20 -99 -104 32 -21 -72 -108 -21 -95 -100 -22 -73 -72 
reakwon?쓽 釉붾줈洹?
reakwon의 블로그

 

결과를 들여다보면 영어는 ASCII, 그리고 한글은 2바이트를 사용하는 것을 알 수 있네요.

반응형
블로그 이미지

REAKWON

와나진짜

,

다형성(Polymorphism)


다형성이라는 개념은 OOP에서 아주 중요한 개념이므로 모르면 OOP에 대해서 제대로 안다고 할 수 없는 개념입니다.


각 요소들이 여러 가지 자료형으로 표현될 수 있다는 것을 말하게 되는데, 반댓말로는 단형성이 있습니다. 한가지의 요소는 한가지의 형태로만 매칭된다는 것을 의미합니다.


음... 일단 모르겠어요.. 다형성이 정확히 무엇인지.


암튼 뭐.. 앞에 '다'라는 의미는 '많은 다(多)' 자가 아니겠어요?? 뭔가 여러가지 (자료)형을 말하는 것 같은데 클래스를 만들어가면서 알아보도록 하지요.


여기 People이라는 클래스가 있습니다. 아주 간단하게 정의한 클래스죠. 그 안에는 printInfo라는 멤버메소드가 있군요.




class People{
	
	public void printInfo() {
		System.out.println("나는 사람입니다.");
	}
}



People 클래스에서 printInfo를 호출하게 되면 지가 사람이라는 군요.


그 밑에 Man과 Woman 클래스는 People클래스를 상속합니다.


class Man extends People{}
class Woman extends People{}


이후 메인에서는 이 두 클래스를 객체로 만들어 printInfo를 호출합니다. 


public class Test {

	public static void main(String[] args) {
		Man man=new Man();
		Woman woman=new Woman();
		
		man.printInfo();
		System.out.println();
		woman.printInfo();
	}
}


이후 실행을 하게 되면 아래의 결과가 나오게 되겠죠.


나는 사람입니다.


나는 사람입니다.


두 클래스 Man과 Woman은 People이라는 클래스를 상속받았으므로 printInfo 호출시 People의 printInfo를 호출할 수 있다는 것, 뭐 놀랍지 않군요.


이제 이것을 토대로 다형성을 세세하게 알아보도록 합시다.



Woman, Man은 People이다





UML 다이어그램으로 본다면 위의 그림과 같을 겁니다.

Man과 Woman은 People이라는 클래스를 상속하기 때문이에요.


우리는 이 다이어그램을 이런 관점으로 한번 바라볼 수 있을까요?


Man은 People이다. (남자는 사람이다.)

Woman은 People이다. (여자는 사람이다.)


현실 세계에서 이 다이어그램을 말로 풀어보아도 그 의미가 맞습니다.

그 반대는 어떨까요?


People은 Man이다. (사람은 남자이다.)

People은 Woman이다. (사람은 여자이다.)


사람은 남자인가요? 아니면 사람은 여자인가요?

그렇지 않습니다. 사람은 남자인지, 여자인지 알 수가 없습니다.


그렇기 때문에 반대로 표현하면 모호해진다는 것을 알 수 있지요.


여기서 중요한 점은 Man은 People로 표현할 수 있고, Woman도 People로 표현할 수 있다는 것입니다.


이것이 다형성의 개념이 나오게 됩니다. Man과 Woman은 People이기 때문에 People이라는 자료형으로 받을 수 있습니다.




한번 확인해볼까요?

public class Test {

	public static void main(String[] args) {
		People people=new Man();
		
		people.printInfo();
		System.out.println();
		
		people=new Woman();
		people.printInfo();
		
	}
}


실행을 시켜서 확인해보면 위의 결과와 동일한 것을 알 수 있습니다. 

Man과 Woman은 People(부모클래스)로 받을 수 있다는 점을 기억하세요!



다형성과 오버라이딩

여기서 Man과 Woman은 printInfo를 물려받았고 오버라이딩(Overriding)할 수 있다는 것을 알고 있습니다.


그래서 저는 Man과 Woman의 printInfo를 그 클래스에 맞도록 오버라이딩하고 싶습니다. Man과 Woman 클래스를 다음과 같이 수정해보도록 하지요.



class Man extends People{
	@Override
	public void printInfo() {
		super.printInfo();
		System.out.println("그리고 나는 남자입니다.");
	}
	
}
class Woman extends People{
	@Override
	public void printInfo() {
		super.printInfo();
		System.out.println("그리고 나는 여자입니다.");
	}
}

그리고 실행해본다면 


나는 사람입니다.

그리고 나는 남자입니다.


나는 사람입니다.

그리고 나는 여자입니다.


오버라이딩된 printInfo를 호출한다는 것을 알 수 있습니다. 오...

다형성에서 People은 자식클래스에서 재정의된 메소드를 호출할 수 있다는 것입니다.


그렇다면 Woman과 Man에서 단독으로 정의한 메소드는 어떻게 될까요?

Man과 Woman클래스에서 다음과 같이 메소드를 추가해보도록 합시다.



class Man extends People{
	@Override
	public void printInfo() {
		super.printInfo();
		System.out.println("그리고 나는 남자입니다.");
	}
	
	public void enlist() {
                System.out.println("내일 군대를 갑니다.");
		System.out.println("충성!");
	}
	
}
class Woman extends People{
	@Override
	public void printInfo() {
		super.printInfo();
		System.out.println("그리고 나는 여자입니다.");
	}
	
	public void makeUp() {
                System.out.println("예뻐질 거랍니다.");
		System.out.println("톡톡 촵촵!");
	}
}


그리고 people.enlist를 호출하려한다면 호출이 되지 않습니다. 왜냐하면 People 형이기 때문이죠. People 클래스는 enlist라는 메소드를 갖지 않기 때문에 호출할 수 없습니다.


이런 경우에는 데이터 형에 맞게 캐스팅해주어서 사용해야합니다.

바로 아래처럼요.


public class Test {

	public static void main(String[] args) {
		People people=new Man();
		people.printInfo();
		((Man)people).enlist();
		
		System.out.println();
		
		people=new Woman();
		people.printInfo();
		((Woman)people).makeUp();
		
	}
}


왜 그런걸까요?

People은 자신을 상속한 클래스 중에서 어떤 매소드를 만들지, 어떤 멤버 변수를 만들어낼지 미리 알아낼 수 없기 때문이죠.


때문에 그 메소드가 있는 객체로 직접 캐스팅해주어서 매소드를 사용해야합니다.


이해를 돕기 위한 그림이 아래에 있습니다. People이라는 자료형이 사용가능한 메소드는 printInfo밖에 없습니다. 그래서 Man의 printInfo메소드를 사용할 수 있습니다. 


또한 new가 동적 메모리를 할당하는 역할을 하므로 Man이 실제 메모리에 잡히게 됩니다. 따라서 형변환을 Man으로 하는 것이 가능한 것이죠.






이와 같은 다형성은 어디에서 쓰일까요?


대표적으로 메소드에서 매개변수로 People을 상속하는 클래스를 받을때 사용할 수 있습니다. 


        public static void func(People people) {
		people.printInfo();
	}
	public static void main(String[] args) {
		Man man=new Man();
		Woman woman=new Woman();
		func(man);
		
		System.out.println();
		func(woman);
	}

func의 매개변수 people은 People의 객체이기 때문에 그것을 상속하는 모든 클래스를 받아 낼 수 있어요.


그래서 Object 객체로 모든 객체를 받을 수 있는 것도 바로 이러한 다형성의 속성때문입니다. 




또한 필요에 의해서는 instanceof 연산자를 사용해서 캐스팅할 수 있습니다.




        public static void func(People people) {
		people.printInfo();
		if(people instanceof Man) 
			((Man)people).enlist();
		if(people instanceof Woman)
			((Woman)people).makeUp();
		
	}
	public static void main(String[] args) {
		Man man=new Man();
		Woman woman=new Woman();
		func(man);
		
		System.out.println();
		func(woman);
	}


OOP의 가장 중요한 특징 중 하나 다형성에 대해서 알아보았습니다. 도움이 되었스면 좋겠습니다. 화이팅!


반응형
블로그 이미지

REAKWON

와나진짜

,

클래스(Class)


객체지향언어에서는 현실세계를 반영하기 위해 객체(Object)라는 개념을 도입하게 됩니다. 현실세계에서 보는 사람들, 자동차, TV 등이 객체지향언어에서는 객체로 표현이 됩니다. 


클래스란 객체를 생성하기 위해 그 객체가 어떤 데이터를 갖고 어떤 연산을 하는지에 대해 정의합니다.




클래스 정의

사람(Human)이라는 클래스가 있다고 칩시다. 사람은 눈, 코, 입이 있고, 손과 다리가 있겠죠. 이런 것이 클래스에서는 데이터입니다. 그리고 눈으로는 사물을 보고, 코로는 냄새를 맏고, 입으로는 말을 하거나 음식을 먹겠죠. 이것이 클래스 관점으로 보면 연산입니다.


간단히 이야기하면 멤버 변수와 멤버 메소드라고 기억하시기 바랍니다. 


코드로 정의를 해본다면 이렇게 될겁니다.


class Human{ String eyes="눈"; String ears="귀"; String nose="코"; String mouth="입"; void useEyes(){ System.out.println(eyes+"으로 봄"); } void useEars(){ System.out.println(ears+"로 소리를 들음"); } void useNose(){ System.out.println(nose+"로 냄새를 맡음"); } void useMouth(){ System.out.println(mouth+"으로 욕을 함"); } }


class라는 키워드로 Human이라는 클래스를 정의하고 있습니다. 클래스 안에는 데이터가 있고, 이 데이터들을 활용하여 행동을 정의하고 있습니다. 하지만 이렇게 클래스를 정의했다고 해서 바로 써먹을 수 있는 것은 아닙니다.




객체생성

자바에서는 모든 데이터를 객체로 취급합니다. 클래스를 정의한 이후에 클래스를 통해서 객체를 생성해야합니다. 객체를 생성하는 방법은 new라는 키워드로 메모리에 할당을 해야하는데요. 간단합니다.


Human human=new Human();


클래스를 통해서 메모리에 생성을 했습니다. 이제 human이라는 객체!가 생성이 된것이죠. 

C++을 배웠다면 new라는 키워드가 익숙할 겁니다. 네, C++에서의 new와 거의 동일한 역할을 합니다. 하지만 우리는 객체를 쓰고 닫을때 C++에서와 같이 메모리를 해제하지 않아도 됩니다. 왜냐하면 Garbage Collector가 알아서 메모리를 해제해 주거든요. C++에 대해 배우지 않았어도 상관없습니다. 


우리는 앞서 클래스를 정의할때 메소드 들을 정의했었습니다. 그렇기 때문에 이 메소드들을 사용할 수 있는 것입니다. 물론 그 객체의 변수들도 바꾸어 줄 수 있습니다. 


아래처럼요.


public class Main { public static void main(String[] args){ Human human=new Human(); human.eyes="쌍꺼풀 수술한 눈"; human.useEyes(); human.useEars(); human.useNose(); } }


물론 데이터가 바뀌었으니, 메소드의 연산도 바뀔 수 있습니다.




생성자

우리는 new라는 키워드를 통해서 객체를 생성할 때 


Human human=new Human();


라는 표현으로 객체를 생성했습니다. 하지만 눈 여겨 보면 괄호가 있는 것을 확인 할 수 있지요.

분명 괄호를 쓰는 이유가 있을 겁니다. 메소드처럼 그 안에 데이터를 매개변수로 전달하거나 하는 이유가 있지 않을까요?


우리는 클래스로부터 객체를 생성할때 초기 데이터를 전달해줄 수 있습니다. 그것이 바로 생성자라고 합니다.


생성자는 객체가 생성될때 가장 처음 호출하는 메소드라고 보면 됩니다. 이 생성자는 꼭 호출이 됩니다. 그리고 객체가 생성되지요. 하지만 우리는 생성자를 선언하지 않았습니다. 하지만 객체를 만들 수는 있었죠.


왜 일까요? 우리가 명시적으로 생성자를 선언하지 않아도 자동으로 default 생성자를 알아서 자바가 정의해줍니다. 물론 아무 기능을 하지 않는 생성자로요.


생성자의 정의는 일반 메소드를 정의하는 것과 같지만


1. 반환값이 없습니다.

2. 생성자의 이름은 클래스의 이름과 정확히 같아야합니다.

3. 생성자는 매개변수에 따라 여러개가 정의 될 수 있습니다.

4. public이라는 접근제어자여야 합니다.


위의 Human은 아무 매개변수가 없는 Default 생성자가 자동으로 호출이 된 것입니다. 




아래 코드는 생성자를 통해서 객체를 초기화 시키는 방법을 보여줍니다. 

class Human{
	String eyes="눈";
	String ears="귀";
	String nose="코";
	String mouth="입";
	public Human(){ 
		//default 생성자
	}
	public Human(String _eyes, String _ears, String _nose, String _mouth){
		eyes=_eyes;
		ears=_ears;
		nose=_nose;
		mouth=_mouth;
	}
	public void useEyes(){
		System.out.println(eyes+"으로 봄");
	}
	public void useEars(){
		System.out.println(ears+"로 소리를 들음");
	}
	public void useNose(){
		System.out.println(nose+"로 냄새를 맡음");
	}
	public void useMouth(){
		System.out.println(mouth+"로 욕을 함");
	}
}


public class Main {

	public static void main(String[] args){
		Human human=new Human("라식한 눈","귀","코털 삐져나온 코","험악한 입");
		human.useEars();
		human.useEyes();
		human.useMouth();
		human.useNose();
	}
}


위에서 생성자를 정의한 규칙대로 생성자를 정의해주었습니다. 그리고 default 생성자를 명시적으로 정의해주었습니다. 


그래서 생성자가 2개인 것이군요.


객체를 생성할때 2번째있는 생성자로 객체를 초기화시켰습니다. 그렇다면 초기화된 데이터로 객체가 생성됐을까요?

결과를 보고 확인해보죠.

 


제가 설명한 대로 군요.

생성자 어렵지 않지요?


Object 클래스

우리는 단지 위 4개의 메소드만을 정의했습니다. 하지만 우리가 정의하지 않은 메소드들도 리스트에 나옵니다. 우리는 분명이 아래와 같은 hashCode, notify, wait 과 같은 메소드들은 정의하지 않았습니다. 이것들은 무엇일까요? 우리는 이 정체를 밝혀내기 위해서 우선 Object 클래스를 알아야합니다.



사실 위의 메소드들은 Object클래스의 메소드들입니다. 위의 메소드들이 Object클래스에 그대로 존재하고 있다는 것을 확인하고 있지요.





허나 왜 Object클래스의 메소드가 왜 내가 만든 클래스에 딸려 나오는 것이냐?

모든 클래스는 Object클래스를 자동으로 상속받게 됩니다.




상속(Inheritance)?

아직 이야기하지는 않았습니다. 간단히 말해서 어떤 클래스의 변수와 메소드들을 물려받는다고 지금은 그렇게 이해하시기 바랍니다. 상속을 쓰게 되면 코드의 재활용이나 유지보수가 더욱 더 쉬워집니다. 상속을 사용하는 방법은 클래스를 정의하기 전 중괄호( { )에서 extends 클래스명 을 통해 이루어 집니다.


그렇기 때문에 extends Object를 명시해주지 않아도, 우리는 Object클래스의 메소드를 사용할 수 있는 겁니다.


모든 클래스입니다. 모든 클래스는 Object 클래스를 상속받는다는 것을 기억하세요.


아직은 Object 클래스의 메소드들을 일일이 설명하는 것은 좀 오바인것 같습니다.

포스팅을 하면서 중간중간에 설명하는 것으로 하거나, 정 궁금하다면 자바 Doc를 보아주세요.



반응형
블로그 이미지

REAKWON

와나진짜

,