저를 포함한 초보자분들에게 도움이 됐으면 좋겠습니다.
초보자분들은 수박 겉핥기 식으로 알고 넘어가지 마시고, 조금이라도 왜 이렇게 프로그램이 돌아가는지 알고 가시는 시간이 됐으면 좋겠습니다.

자 오늘은 쓰레드(Thread)에 대해서 알아봅시다. 일반적으로 자바 프로그램은 코딩대로 순차적으로 수행하게 됩니다. 평소에는 프로그램에 하나의 쓰레드만 사용을 하고 있죠.
사용하기에는 문제없습니다. 하지만 여러 개의 쓰레드를 사용한다면 어떤 이점이 있을까요?
여러 개의 쓰레드가 해야 하는 일을 나누어 작업을 진행한다면, 하나의 쓰레드가 일하는 것보다 훨씬 빠르지 않을까요? 당연한 이야기입니다. 이처럼 프로세스가 병렬로 작업을 처리할 수 있기에 훨씬 좋은 성능을 낼 수 있는 거죠. 

자바에서 쓰레드 문법은 굉장히 쉽습니다. 어떻게 선언하는지부터 설명해보죠.
자바에서 쓰레드를 선언하는 방법은 2가지입니다. Thread를 상속 받든지, Runnable 인터페이스를 상속받기 2가지가 존재하죠.

(1)public class mythread extends Thread{  //Thread 상속
	public void run() { } //실질적으로 쓰레드가 할 일 구현
	public static void main(String args[]) {
		mythread test = new mythread();         
		test.start();
	}
}
(2)public class mythread implements Runnable{ //Runnable 인터페이스 
	public void run() { }
	public static void main(String args[]) {
		mythread test = new mythread();       //객체를 생성하여
		Thread myThread = new Thread(test);   // 쓰레드의 타겟에 넣어 사용
		myThread.start(); 
	}
}

(1) 코드는 Thread 클래스를 상속받아 사용합니다. 간단하게 Thread 클래스를 상속받는 자손 클래스의 객체를 만들어. start()로 쓰레드를 시작하면 됩니다. 
(2) 코드는 살짝 다릅니다. Thread 클래스를 상속받는 것이 아니기에, mythread 객체를 생성하여 
Thread 클래스 객체를 만들어 target으로 넘겨줘야 합니다. 실행 방법은 start()로 같습니다. 
대개 (1) 코드는 상속받는 클래스가 없을 때 사용하겠죠? 그럼 (2) 코드는 반대로 다른 클래스를 상속받지만, 쓰레드를 써야 하는 경우 사용할 수 있겠죠? 둘의 차이와 사용방법 차이를 기억해 두시면 좋겠네요.
실실 적으로 쓰레드가 해야 하는 일은 run(){ } 메소드를 구현해 주시면 됩니다. 여기서 의문이 하나 생길 수 있습니다. 우린 run()을 안 하고 start()를 했는데, 왜 run()이 되는 걸까요?
start() 메소드를 한번 보고 갈 필요가 있습니다. 쓰레드는 .start()를 하게 되면, run(){}메소드가 자동으로 실행되게 되는데요. 왜 그런지 알아보고 갑시다.

    public synchronized void start() { //start()메소드
        group.add(this);
        boolean started = false;
        try {
            start0();  <--------------------이 부분
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
            }
        }
    }

    private native void start0();
    @Override
    public void run() {
        if (target != null) {
            target.run(); <-----------------------실질적으로 run()메소드실행
        }
    }

위 코드는 start()메소드의 구현내용입니다. 간단하게 되어있죠? 여기서 start0()이라는 메소드를 수행하면 start0에서 target.run();을 수행하게 됩니다. target은 우리가 만들어준 객체입니다.
고로 우리가 선언한 run()메소드가 실행되는 것이죠. 혹여나 start()메소드로 쓰레드를 수행하지 않고
run()으로 한다면... 쓰레드는 생각하는 대로 수행하지 않을 것입니다. 주의하세요!

자 그럼 이제 쓰레드 예제를 하나 살펴보죠.

(3)public class mythread extends Thread{
	String name = null;
	mythread(String k){
		name =k;
	}
	public void run() {
		for(int i = 0; i <10;i++) {
				System.out.println(name + "실행중..");
		}
		
	}
	public static void main(String args[]) {
		mythread test = new mythread("A");
		mythread test2 = new mythread("B");
		test.start();
		test2.start();
	}
}

(3) 코드는 두 개의 쓰레드를 만들고, 실행시켜 보았습니다. 일반적으로 한 개의 쓰레드를 사용하게 된다면, "A 실행 중.." 10번 출력 "B 실행 중.." 10번 출력 순차대로 나올 것입니다.
하지만 쓰레드를 사용하면 어떻게 나올지 예상할 수 없습니다. 직접 해보신다면, 실행할 때마다 결과가 뒤죽박죽 섞여서 나오게 됩니다. 동시에 수행하고 있다고 봐도 무방하죠.

하나만 더 보고 마무리하겠습니다. 사무실에서는 한 대의 프린터를 여러 개의 컴퓨터에 연결하여 사용하는 경우가 많습니다. 대개 이런 경우 쓰레드를 사용하여 처리한다고 볼 수 있는데요. 하지만, 위에서 알아본 쓰레드는 뒤죽박죽 섞이기 때문에 '순서'란 개념이 없습니다. 이렇게 된다면, 프린트 물이 뒤죽박죽 섞여 출력되지 않을까요?


(4)class printer{
	private int count = 0;
	synchronized void count_inc() {
		count++;
		System.out.println(count + "번 출력중..");
	}
}
===============================================================
public class mythread extends Thread{
	printer k = null;
	mythread(printer a) {
		this.k = a;
	}
	public void run() {
		for(int i = 0; i <10;i++) {
				k.count_inc();
		}
		
	}
	public static void main(String args[]) {
		printer temp = new printer();
		mythread test = new mythread(temp);
		mythread test2 = new mythread(temp);
		test.start();
		test2.start();
	}
}

(4) 코드를 봅시다. printer라는 클래스를 만들어서 다른 쓰레드(컴퓨터)에서 접근하여 출력한다고 생각해보죠. 일반적으로 쓰레드를 사용하여 구현한다면, 순서가 뒤죽박죽 섞여서 나오게 됩니다. 생각한 대로 온전하게 출력을 하지 못합니다. 이걸 막기 위해 'synchronized'라는 키워드가 있습니다.
synchronized 키워드를 메소드에 써준다면, A쓰레드가 해당 메소드를 수행할 때 다른 B쓰레드에서 
해당 메소드에 접근하면 A쓰레드가 해당 메소드를 끝낼 때까지 B쓰레드를 기다리게 만듭니다. 
synchronized 키워드를 사용해 줌으로써 순차대로 프린트는 출력을 해 줄 수 있습니다.

이외에도 notify(), sleep() ... 등등 쓰레드 관련 메소드가 많습니다. 궁금하신 분은 찾아보시는 것도 도움이 될 것 같네요. 오늘은 여기까지입니다! 주의하실 점은 start()로 쓰레드를 실행한다는 점, 또 쓰레드 선언 방법 2가지를 구분하여 알고 계시면 될 것 같습니다.

지금까지 java 이론적인 설명을 올려왔는데요. 기본적인 내용이었습니다. 이 정도 배우면 개인적인 생각으론 기본적인 내용은 다 했다고 생각 듭니다. 애플릿, 스윙.. 이런 부분은 눈에 보이는 것을 만드는 것이니까 제외하고.. 어느 정도 자바를 한번 훑어보았습니다.
하지만, 그래도 아직 알아야 할 것이 많습니다. 지금까지 전체적인 내용만 본 것이고, 이제 상세하게 살펴봐야 하겠죠. 그래서, 이제부터는 사용할 만한 함수를 정말 '뜯어보기'말에 맞게 뜯어볼 계획입니다.
물론 추가적으로 올릴 내용이 있다면 올리겠습니다. 
감사합니다.

저를 포함한 초보자분들에게 도움이 됐으면 좋겠습니다.
초보자분들은 수박 겉핥기 식으로 알고 넘어가지 마시고, 조금이라도 왜 이렇게 프로그램이 돌아가는지 알고 가시는 시간이 됐으면 좋겠습니다.

오늘은 예외 처리에 대해서 알아봅시다. 프로그래밍을 하다 보면 다양한 오류가 발생하기 쉽습니다.
가장 대표적인 예로는 file을 읽으려고 했는데 해당 file이 없는 경우, 0으로 나누는 경우 등등.. 쉽게 접할 수 있는 오류들이 많이 있죠. 이러한 오류를 처리하는 것을 '예외 처리'라 볼 수 있습니다.
그럼 어떻게 예외 처리를 할 수 있는지 살펴보도록 합시다.

자, 일단 오류를 분류해 봅시다. 위에서 간단하게 2가지 오류를 말씀드렸습니다.
첫 번째는 파일이 존재하지 않는 경우, 두 번째는 0으로 나누었을 때 오류입니다. 둘 다 오류지만 자세히 살펴보면 특징이 조금 다릅니다. 첫 번째 파일이 존재하지 않는 경우의 오류는 프로그래머가 예상을 하고 예외 처리를 해줘야 하는 경우입니다. 즉, 파일이 존재하지 않을 경우를 대비해서 예외 처리를 해줘야 한다는 것이죠. 예측 가능합니다. 
두 번째, 0으로 나누었을 때 오류입니다. 첫 번째처럼 예외 처리를 해 줄 수 있습니다. 하지만 '0으로 나눈다'를 어떤 식으로 예외 처리를 해줄 건가요? 단적인 예를 들어보죠.

(1)	public void Exam() {
		for(int i = 9; i >=0 ;i++)
			System.out.println(10 % i);
	}

(1) 코드와 같이  10을 9~0까지 나눈 나머지 값을 출력한다고 칩니다. 그럼 예외 처리를 어떻게 해야 할까요? 예외 처리되는 부분에 다시 'for(int i = 9; i >0 ;i++)'를 수행하여 출력해야 합니다. 생각해보세요. 예외 처리를 하는 것이 좋을까요? 아니면 (1) 코드를 수정하는 것이 좋을까요? 당연히 (1) 코드를 i>0으로 수정하는 게 무조건 좋습니다. 두 번째 케이스는 '버그'에 가깝다고 보시는 것이 맞죠.
그래서 첫 번째 케이스는 '검사 예외'(- 예외 처리를 해줘야 하는 것)라 하고 두 번째 케이스는 '비 검사 예외(- 예외 처리보단 버그에 가까워 코드를 고치는 것)'이라고 부릅니다. (물론 비검사 예외도 예외 처리를 해 줄 수 있습니다.)

자 이제 본격적으로 예외 처리를 할 수 있는 try/catch 구문에 대해서 알아봅시다.
try는 예외를 감지하는 곳이고 catch는 try에서 감지한 예외를 처리하는 부분입니다.
예를 보시는 게 더 빠를 것 같습니다. 간단한 예를 위해서 '비검사 예외'를 사용하겠습니다.


(2) public void A() {
		try {
		System.out.println( 10 / 0 );
        System.out.println( "끝" );
		}
		catch(Exception e) {  //catch(어떤 예외?) -Exception은 모든 예외
			System.out.println("예외 처리"); //출력
		}
	} //최종 출력 : 예외 처리

(2)와 같은 구문이 있습니다. 10을 0으로 나누고 있어서 예외가 발생하게 되죠.
이처럼 예외가 발생할 수 있는 부분을 try{ } 안에다 감싸줍니다. 그리고, 예외가 발생하면 즉시 catch 구문으로 넘어가 예외 처리를 수행하게 됩니다. 굉장히 간단합니다. try 구문에서 예외가 발생하면 catch 문으로 가서 해당 예외 처리를 하는 거죠. 하지만 주의해야 할 점이 있습니다. 
(2)의 최종 출력은 "예외 처리"입니다. try 안에 "끝"이라는 출력이 있음에도 출력하고 있지 않습니다.
눈치채셨겠지만, 예외가 발생한 지점에서 바로 catch 부분으로 넘어가는 것이죠. 즉, 10/0에서 예외가 발생해 catch 구문으로 넘어가 예외 처리를 하므로 "끝"이라는 글자가 출력이 되지 않은 것이죠.
또 (2) 코드의 catch는 Exception을 매개변수로 사용하고 있습니다. Exception은 모든 예외를 나타내는 것입니다. 하지만 각각의 예외마다 처리하는 방식이 다를 수 있으므로 예상되는 예외를 매개변수에 넣어서 각각 알맞게 처리해 주시는 게 좋습니다.
(3) 코드와 같이 말이죠.

(3)public static void main(String[] args) {
		int x,sum;
		try {
			x = Integer.parseInt(args[0]);
			sum = x / 0;
		}catch(ArithmeticException e) { // 첫번째 예외처리
			System.out.println("0으로 나누어서 예외발생");
		}catch(Exception e ) { // 두번째 예외처리
			System.out.println("args[0]를 주지않아 예외발생");
		}
	}

(3) 코드와 같이 여러 가지 예외 처리를 선언할 수도 있으니, 자신이 처리해야 하는 예외들을 찾아보시고 알맞게 처리해 주는 게 필요합니다. 하지만 주의하실 점은 catch 구문도 순서가 있기에, 맨 처음부터 
exception(모든 예외)로 선언해 준다면, 두 번째, 세 번째... 선언한 catch 문은 수행할 수가 없습니다.
주의하세요.

자, 그럼 예외 처리를 살펴보았으니, 예외를 인위적으로 발생시켜 보죠.
예외를 인위적으로 발생시키는 방법은 throw라는 키워드가 있습니다.
throw는 '던진다' 즉 예외를 발생시키는 것이라 생각하시면 됩니다. 이것도 예시로 살펴보죠.


(4)	public void A() {
		try {
		 throw new Exception();
		}
		catch(Exception e) {
			System.out.println("A에서 예외 처리");
		}
	} //최종 출력 : A에서 예외 처리

간단합니다. new 예와 명();를 하여 객체를 생성하여 <throw 객체>로 예외를 발생시킵니다. 
이렇게 발생한 예외는 알맞은 catch 문에 들어가 예외 처리를 할 수 있죠. throw는 사람이 '인위적'으로 발생시키는 예외입니다. 반드시 처리해줘야 할 예외를 명시적으로 예외를 발생시켜, 유지 보수에 도움을 주기도 합니다.

마지막으로 throws라는 키워드입니다. throws는 < 메소드명() throws 예외명 > 형태로 사용하고, 자신을 불러온 메소드에서 예외를 던진다 정도로 생각하시면 됩니다. 예를 봅시다.


(5)	public void A() {
		try {
			B();
		}
		catch(Exception e) {
			System.out.println("A에서 예외 처리");
		}
	}
	
	public void B() throws Exception{
		throw new Exception();
	}

A메소드에서 B메소드를 호출하고 B메소드는 예외를 throw 하고 있습니다. 하지만 B에서는 try/catch 구문이 없어서 예외를 발생시켜도 처리해줄 곳이 없습니다. 이런 경우는 대게 '호출한 곳에서 처리해주겠다'라고 생각하시면 됩니다. 즉, 발생한 예외를 호출한 곳으로 넘겨주는 것이 throws입니다.
(5) 코드와 같이 throws를 해주게 되면, 인위적으로 생성한 예외가 A메소드로 넘어가 catch 부분에서 예외 처리가 되는 것이죠. throws 키워드는 메소드에서 발생할 수 있는 예외들을 나열하여 적어줌으로써 호출한 메소드에서 적절하게 대비를 하도록 유도하는 역할도 합니다.
throw, throws를 잘 구분하여 기억하시고 마지막으로 finally를 살펴보고 마무리 짓겠습니다.

finlly는 try/catch 구문을 수행하고 '반드시'수행되는 구문입니다. 예를 보시죠.


(6)	public void A() {
		try {
			System.out.println("1");
			System.out.println( 10 / 0); // 예상치못한 예외발생
			System.out.println("2");
		}
		catch(Exception e) {
			System.out.println("3");
		}
		finally {
			System.out.println("4");
		}
	} //최종 출력 : 1 3 4

(6) 코드를 봅시다. finally 구문은 catch 다음에 구현하여 사용합니다. finally 구문은 말씀드렸듯 반드시 수행되는 구문입니다. 최종 출력을 보시면 1 3 4 가 나옵니다. 즉, 0으로 나누는 것에 예외가 발생하고
finally 구문을 수행했다는 것이죠. 정말 간단하고 알아 두시기만 하면 됩니다.
그럼 finally 구문은 왜 사용할까요? 이유는 간단합니다. 1. 반드시 수행되야 하는 구문이 있을 경우,
2. try 구문, catch 구문의 중복되는 코드를 finally 구문에 넣음으로써 코드 중복을 없앨 수 있습니다. 예를 들어볼까요? Scanner가 대표적입니다.


(7)	public void A() {
		Scanner test = new Scanner(System.in);
		try {
			//~~~~~~~ 기타 구문 수행
			test.close();
		}
		catch(Exception e) {
			test.close();
		}
	}

(7) 코드를 보시면 test로 Scanner가 선언되어있습니다. 만약 try 구문에서 예외 없이 마친다면 test.close로 Scanner를 끝내야겠죠. 하지만 try 구문 안에 ~~~~구문에서 예외가 발생한다면 어떨까요? 그럼 try 구문에서 close를 해주지 못했으니 catch 구문에 가서 똑같은 코드인 test.close를 해 줘야 합니다.
하지만 finally 구문을 사용하게 된다면, 예외가 발생하든 안 하든 반드시 수행해야 하므로 test.close의 코드 중복을 막고 보다 보기 편하게 코딩할 수 있습니다.

자, 지금까지 try/catch 예외 처리에 대해서 알아보았습니다. 어렵지 않은 내용입니다. 어떻게 사용하는지만 알고 있으면 충분히 응용하여 사용하실 수 있다고 생각 듭니다. thow, thows는 헷갈리지 않게 다시 한번 정리해 두시는 것이 좋겠습니다.

자. 다음 편은 쓰레드에 대해서 적어보도록 하겠습니다.

저를 포함한 초보자분들에게 도움이 됐으면 좋겠습니다.
초보자분들은 수박 겉핥기 식으로 알고 넘어가지 마시고, 조금이라도 왜 이렇게 프로그램이 돌아가는지 알고 가시는 시간이 됐으면 좋겠습니다.

오늘은 내재 클래스(nested class)를 살펴보도록 하죠.
내재 클래스는 간단합니다. Class 안에 또 다른 Class가 들어가 있는 것이죠.
(1) 코드를 보고 설명해보죠!

(1)public class outerClass {
	class innerClass{ // outerClass의 멤버 -----InnerClass
	}
	static class nestedClass{  ----------Nested Class
	}	
}

보시다시피, outerClass 안에  nestedClass,innerClass가 존재합니다. 이게 내재/내부 클래스의 전부입니다. 간단하죠? 그럼 왜 내재/내부 클래스가 나뉘는지 간단히 이야기하고 넘어갑시다.
원래는 static nested Class 와 non-static nested Class로 나누는 것이 맞습니다. 보시다시피 static의 여부에 따라 나눠 놓았죠. 하지만.. 영어 이름이 상당히 깁니다..
이제부턴 non-static nested Class은 내부 클래스, static nested Class는 내재 클래스로 부릅시다.

내재/내부 클래스는 클래스와 동일합니다. 객체를 생성할 수도 있고 멤버로 변수, 메소드를 가질 수 있죠. 별로 다른 것이 없죠. 하지만 왜 클래스 안에 클래스를 넣어 사용을 할까요?
몇 가지 이유가 있습니다. 하나의 예를 들어보죠. A 클래스와 A 클래스만 도와주는 B 클래스가 있다고 합시다. 물론 클래스 A, B를 각각 생성하여 사용하여도 문제는 없습니다. 하지만, 여기서 'B 클래스는 A 클래스만 도와준다'라고 한다면, 굳이 B 클래스. java를 생성할 필요가 있을까요? 하지만 만약  B 클래스를 A 클래스의 내재/내부 클래스로 만들어 사용한다면, 장점이 몇 가지 있습니다. 
1. 클래스를 논리적으로 그룹화할 수 있다는 것입니다. B 클래스는 A 클래스를 도와주는 역할만 하므로 A와 B를 논리적 그룹을 만들 수 있다는 소리죠. 
2. 캡슐화의 향상을 도모할 수 있습니다. B 클래스는 A 클래스의 멤버가 되므로 A 클래스에서만 접근 가능하기에(private로 선언시) 외부에 은닉이 될 수 있는 것이죠. 
3. 판독, 유지 보수에 도움이 됩니다.
논리적으로 그룹화한다는 소리는 코드를 필요한 곳에 가깝게 위치시킬 수 있다는 소리이기에  추후 오류가 있더라도 유지 보수, 판독에 도움이 되죠.

자. 이제 그럼 어떻게 내재/내부 클래스를 접근하고, 객체 생성을 하는지 알아보도록 하죠.
일단은 내부 클래스부터 보시죠!

(2)public class outerClass {
	int k = 1;
	class innerClass{ // outerClass의 멤버
		int k = 3;
		public void say() {
			System.out.println("innerClass");
		}
	}

	public static void main(String args[]) {
		outerClass outerObject = new outerClass(); // outerClass객체 생성 
		innerClass temp = outerObject.new innerClass(); // innerClass객체 생성
		System.out.println(temp.k); // 3
		temp.say(); // innerClass
		
	}
}

클래스 객체를 만들기 위해 new를 사용하는 것을 같습니다. 하지만 다른 점이 눈에 띄네요.
바로 ' outerClass outerObject = new outerClass(); '입니다. 반드시 외부 클래스 객체를 생성한 후 
.new~~로 객체를 생성해야 합니다. 이유는 간단하죠.
innerClass는 outerClass(외부 클래스)의 멤버이기 때문입니다. 자 (2) 코드에서 outerClass에 있는 int k =1;에 접근하기 위해 어떻게 하나요? 객체.k로 접근을 합니다. k가 outerClass의 멤버이기 때문이죠.
이와 마찬가지고 innerClass도 멤버이기에 접근을 하려면 일단 outerClass의 객체가 필요합니다.
그리고 나선 innerClass를 생성하는 것이므로 ' innerClass 변수명'를 선언해주시고 
객체(outerClass 객체).new innerClass()를 해줘야 innerClass가 생기는 것이죠.
처음엔 조금 헷갈릴 수 있습니다. 클래스에 멤버에 접근하기 위해서 어떻게 해야 하는지 다시 한번 잘 생각해보시는 게 좋을 것 같습니다.

마지막으로 내재 클래스입니다.
코드 먼저 보시죠.

(3)public class outerClass {
	int k = 1;

	static class static_innerClass{
		int a = 10;
		public void say() {
			System.out.println("static_innerClass");
		}
	}
	
	public static void main(String args[]) {
//		outerClass.static_innerClass.say(); // X 오류 say()메소드는 static이 아니여서
		static_innerClass temp = new static_innerClass();
		System.out.println(temp.a); // 10
		temp.say(); //static_innerClass
	}
}

(3) 코드는 더 간단합니다. static의 특성을 다시 한번 기억해 보세요. static은 어디서든 접근이 가능합니다. 그래서 내재 클래스는 일반적인 클래스를 선언하는 것처럼 선언해주고 사용하면 끝납니다.
하지만, static은 클래스에만 붙어있습니다. static_innerClass의 멤버는 반드시 객체를 생성해야 접근이 가능하다는 것만 주의해 주시면 될 것 같네요.

자 지금까지 내재(nested)/내부(inner) 클래스에서 대해서 살펴보았습니다.
내재/내부 클래스를 사용함으로써 얻을 수 있는 장점을 다시 한번 되새겨 보시고, 선언하여 사용하는 것은 크게 어렵지 않으니 쉽게 배워 가실 수 있으실 겁니다.

자, 다음번에는 예외 처리에 대해서 살펴보겠습니다.

저를 포함한 초보자분들에게 도움이 됐으면 좋겠습니다.
초보자분들은 수박 겉핥기 식으로 알고 넘어가지 마시고, 조금이라도 왜 이렇게 프로그램이 돌아가는지 알고 가시는 시간이 됐으면 좋겠습니다.

오늘은 추상 클래스가 좀 더 '추상화'된 인터페이스에 대해서 살펴보겠습니다.
추상 클래스를 잘 모르시겠다면 다시 한번 보고 오시면 더욱 도움이 될 것입니다.
http://sims-solve.tistory.com/10?category=747624

추상 클래스 편에서도 '추상'이라는 단어를 살펴보았습니다. 위에서 언급했지만, 인터페이스(interface)는 추상 클래스보다 한 단계 더 '추상화' 된 것입니다.
잠깐 추상 클래스에 대해서 떠올려봅시다. 추상 클래스는 하나 이상의 추상메소드로 구성되어있으며, 상속받는 클래스에서 반드시 추상메소드를 구상메소드로 구현해야 했습니다.(구상메소드 => 구현된 메소드) 그럼! 추상 클래스보다 더 추상화된 인터페이스는 어떨까요? 
'추상화' 한다는 건 추상메소드처럼 공통되는 특성, 속성을 추출하여 선언만 하고 다른 곳에서 알맞게 구현하는 것이었습니다.
인터페이스는 추상 클래스에서 더 나아가 모든 메소드를 abstract 한 것입니다. 변수는 상수만 정의할 수 있습니다.
예시를 보면서 이야기해 봅시다.

 (1)public interface shapeInterface {
	public int k = 3;
    public void area();
}
=========================================
public class rectangle implements shapeInterface {
	public static void main(String args[]) {
		rectangle test = new rectangle();
		
		//test.k=5;  ------> shapeInterface의 k를 변경하려고 할 때 error
		System.out.println(shapeInterface.k); // 3
		System.out.println(k); // 3 
		
	}
	@Override
	public void area() {
		System.out.println("넓이구하기");
	}
}

(1)처럼 간단하게 예시를 보도록 하겠습니다.
일단 Class를 만드는 것처럼 만들되, class가 아닌 interface 키워드를 사용하여 Interface로 만들게 됩니다. 이렇게 인터페이스(interface)로 만들게 되면 자동으로 결정되는 것이 있습니다. 변수는 static final, 메소드는 abstract 키워드를 써주지 않아도 변수는 static final, 메소드는 abstract 키워드를 사용 한 것처럼 됩니다. 증명해봅시다.
rectangle에서 객체 test를 만들어 test.k를 통하여 interface의 k 값을 5로 변경하려고 하면
"The final field shapeInterface.k cannot be assigned"라는 에러를 볼 수 있습니다. final 필드를 바꿔줄 수 없다는 소리죠. interface에서 변수를 선언하면 자동으로 상수(final)이 된다는 소리죠.
한 가지 더 있습니다."System.out.println(shapeInterface.k);" 이 부분입니다. 이 부분을 보시고 한 가지 떠올라야 하는 키워드가 있습니다. 바로 static이죠. 하지만 우리는 interface에 static을 지정하지 않았죠. 여기서 알 수 있듯, interface에서 선언한 변수는 반드시 static이 된다는 것 또한 알 수 있습니다.
정리해보자면, 따로 명시해주지 않아도 자동으로 final static 변수가 되는 것이죠.

이번엔 메소드 입니다. 메소드는 추상메소드와 같습니다. interface 안에서도 구상메소드(구체적으로 구현된 메소드)를 선언하지 못합니다. 하지만 추상메소드처럼 abstract 키워드를 명시적으로 사용하지 않아도 자동으로 abstract 됩니다. 즉. "public void area();"만 하더라도 자동으로 abstract 된다는 것이죠.
이제 상속받는 클래스에선 반드시 interface의 메소드들을 구상메소드로 구현해 줘야 하죠.
내용은 추상메소드와 같습니다. 그럼 interface를 사용하는 이유는 뭘까요? 무슨 이점이 있는 걸까요?
객체지향 프로그래밍은 클래스 간에 결합도가 낮을수록 좋습니다. 이유는 A 클래스가 바뀌면 B 클래스까지 수정해야 한다면, 개발자 입장에선 좋을까요? 싫습니다. 이러한 결합도를 낮추기 위해 '추상화'를추구하는 것입니다.

자 그럼 추상 클래스 vs 인터페이스를 정리하고 마무리해보겠습니다.
추상 클래스 =  추상메소드(1개 이상) + 구상메소드 / 인터페이스 = 추상메소드(only)
추상 클래스 변수 = 일반 변수 / 인터페이스 변수 = static final 변수
추상 클래스 상속 = extends / 인터페이스 상속 = implements 
클래스 상속 개수 =  1개(추상 클래스 or 클래스) / 인터페이스 상속 개수 = 무한대

이렇게 간단하게 정리해 보았습니다. '추상'이라는 말이 계속 나오고 있습니다. 한번 다시 곰곰이 생각해보시고 정리하시길 권합니다. 오늘은 여기까지입니다!

다음 편은 내재 클래스(Nested class)를 살펴보겠습니다.

저를 포함한 초보자분들에게 도움이 됐으면 좋겠습니다.
초보자분들은 수박 겉핥기 식으로 알고 넘어가지 마시고, 조금이라도 왜 이렇게 프로그램이 돌아가는지 알고 가시는 시간이 됐으면 좋겠습니다.

오늘은 클래스 중 추상 클래스(abstract)를 살펴보죠.
'추상'이라는 단어의 뜻을 알고 계시나요?
국어사전에는 '여러 가지 사물이나 개념에서 공통되는 특성이나 속성 따위를 추출하여 파악하는 작용'
이라고 적혀있습니다.
자바에서도 추상 클래스(abstract)는 국어사전의 의미와 동일합니다.
자바에서는 공통이 되는 메소드를 파악해 추상메소드를 만드는 것이 추상 클래스에서 가장 중요합니다.
간단한 예를 들어보죠. 넓이를 구한다고 생각해봅시다. 그럼 네모의 넓이는 어떻게 구하나요? x, y 값을 곱하여 구합니다. 그럼 세모는요? x * y / 2를 하면 됩니다. 누구나 알고 있는 것이죠.
하지만 이걸 이제 클래스로 표현해야 한다면 어떻게 하실 건가요? 혹시 세모 클래스, 네모 클래스를 각각 만들어 사용하실 건가요? 그러기엔 x, y좌표가 있어야 하고 넓이를 구하는 메소드가 있다는 공통점이 눈에 띄지 않나요? 

위 국어사전의 '정의처럼 공통되는 특성이나 속성 따위를 추출'하여 새로운 클래스를 구성해 보면 어떨까요? 소스코드를 보시죠.

(1)public class shape {
	  int x;
	  int y;
	  public void area() { //??????
	  }
}
==================================
public class rectangle extends shape { }
==================================
public class triangle extends shape{ }

(1) 소스코드와 같이 shape란 클래스를 만들어서 네모, 세모에 공통이 되는 부분을 모두 넣은 클래스를 만들었습니다. 이와 같이 shape란 클래스를 만들어서 상속 형태로 사용하는 것이 이상적이라 볼 수 있습니다. 하지만 문제가 있습니다. 네모, 세모의 넓이를 구하는 메소드(area)는 각자 넓이를 구하는 공식이 다르다는 것입니다. 하지만 '넓이'를 구해야 한다는 속성은 변함이 없어 shape에 공통된 메소드로 들어가게 되는 것이죠. 이 문제를 어떻게 해결할 수 있을까요? '오버라이딩'이 단숨에 생각이 나셔야 합니다.
오버라이딩을 사용하여 rectangle, triangle 각각 클래스에서 재지정하여 넓이를 구해야 하죠.
하지만 우리가 배운 '오버라이딩'은 부모 클래스에 메소드가 온전히 정의가 되어있을 때 사용했었습니다. 여기서 생각해 봐야 하는 부분은 shape에 넓이를 구하는 메소드가 완전히'정의'될 필요가 있는가입니다. 이 뜻은 shape 안에 area는 반드시 자손 클래스에 가서 서로 다른 형태로 사용된다고 우리는 이미 알고 있습니다. 그럼 shape 안에서 완전하게 정의가 될 필요는 없는 거죠. (완전히 정의된다는 것은 메소드가 수행하는 부분 { }이 들어가야 한다는 뜻)
그래서 사용하는 게 abstract 키워드입니다. abstract 키워드는 간단하게 말해서 상속받는 부분에서 '반드시' abstract로 된 메소드를 반드시 정의해주겠다는 소리입니다!
예제를 보시죠!

(2)public abstract class shape { //하나라도 추상메소드가 있으면 추상클래스가 되야함!
	int x=2;
	int y=2;
	
	public abstract void area(); // abstract 메소드! 
}
======================================
public class rectangle extends shape {
	@Override //오버라이드!
	public void area() {
		System.out.println(this.x*this.y);
	}
}
=======================================
public class triangle extends shape{
	@Override
	public void area() {
		System.out.println(this.x*this.y/2);
	} 
	public static void main(String args[]) {
		new rectangle().area();
		new triangle().area();
	}
}

(2)와 같이 (1)의 코드를 고쳐보았습니다. 가장 눈에 띄는 건 shape의 area를 abstract 키워드를 사용하여 추상메소드로 선언했다는 것입니다. 이렇게 되면, shape 자체 area메소드는 실질적으로 구하는 공식이 없어 훨씬 추상적이게 되는 것이죠.(공통적이 부분을 잘 뽑아내게 되는 것이죠)
하지만 반드시 추상메소드를 선언하게 되면 추상메소드가 속한 클래스는 반드시 추상 클래스가 돼야 합니다. 추상메소드가 하나라도 있게 된다면 추상 클래스가 되는 것이죠. 추상 클래스가 된다고 해서 클래스와 크게 바뀌는 것은 없습니다. 단, 추상 클래스를 상속받는 클래스는 추상메소드를 '반드시' 클래스 안에서 재정의 해야 합니다.(오버라이딩과 같이 말이죠)

하지만 의문점이 하나 생길 수 있습니다. 그럼 rectangle, triangle 따로따로 area 메소드를 사용하면 되는 거 아니냐?라는 점입니다. 물론 맞는 말입니다. 상관없습니다. 하지만.. 지금처럼 추상화를 잘 해놓게 된다면 이점이 있습니다. '형식'을 가지게 되는 것입니다. 살면서 한 번씩은 보고서, 자기소개서, 리포트 등등.. 을 써보셨을 겁니다. 혹시 백지인 A4용지에 구구절절 써 내려가는 것과 '형식'이 적혀있는 A4 종이에 맞춰 적어 내려가는 것 중 어느 것을 선택할 건가요? 대개 '형식'이 있는 것을 선택하지 않을까요? 
이 '형식'을 통하여 작성자는 어떤 것을 써 내려가야 하는지 방향성을 알 수 있지 않을까요?
자바에서도 마찬가지라고 생각합니다. '형식'을 만들어 방향성을 제시하고 추후에도 재사용 하겠다는 의미입니다. 추상화 클래스를 잘 구성하게 된다면, 보다 짜임세있는 '형식'을 가지는 코드를 작성할 수 있지 않을까요? 오늘은 여기까지입니다!

오늘은 추상클래스(메소드)편을 살펴보았습니다. 어려운 것은 없었습니다. 다시 한번 잘 생각해보시고 '추상화'가 줄 수 있는 이점에 대해서 살펴보시는 것도 좋을 것 같습니다. 

다음 편은 Interface(인터페이스)를 살펴보고 추상 클래스와 비교까지 해보도록 하겠습니다.

저를 포함한 초보자분들에게 도움이 됐으면 좋겠습니다.
초보자분들은 수박 겉핥기 식으로 알고 넘어가지 마시고, 조금이라도 왜 이렇게 프로그램이 돌아가는지 알고 가시는 시간이 됐으면 좋겠습니다.

오늘은 자바 프로그램에 있어서 유용하게 사용할 수 있는 키워드를 몇 가지 살펴보고자 합니다.
상속, 객체를 배우셨으면 당연히 알아야 하는 this 와 super를 알아보겠습니다.

첫 번째로 this - 이것을 뜻하는 영어인데요.
자바에서 '이것' this는 < 메소드를 부른 객체 > 정도로 생각하시면 됩니다.
메소드를 수행할 때, 우리는 객체를 생성하여 객체. 메소드로 메소드를 수행하게 되죠.
this는 메소드 안에서만 사용할 수 있습니다. 이유는 간단합니다. 위에서 말했듯 A.메소드 형태로 메소드를 부르면, A 객체 안에 있는 메소드를 수행하는 거죠. 이렇게 A.메소드 형태는 현재 어떤 객체를 이용하고 있는지 알 수 있는 징표가 됩니다. 이렇기 때문에 this는 A를 나타낼 수 있는 것이죠.
만약 메소드가 아닌 곳에서 선언하게 된다면, 객체명.~~~로 접근을 하는 것이 아니기에 this는 어떤 객체를 가리키는지 알 수가 없죠. 결국 this는 사용 못 하게 되는 것이죠. (변수는 객체명.변수 로 접근하지만 이것이 끝이기에 this를 사용하여 할 수 있는 것이 없습니다.)
이와 같은 이유로 static 메소드도 동일합니다. static메소드는 객체와 상관없이 하나를 공유하는 메소드라고 잠깐 설명드렸는데요.
static메소드는 객체를 생성하지 않아도 클래스명.static메소드명으로 호출이 가능합니다. 이와 같이 객체가 없어도 호출이 가능하기에 this의 사용을 허용하지 않습니다.

다음은 super입니다. 현재 객체를 나타내는 this가 있다면, super는 상위 클래스 객체를 나타내는 겁니다. super는 상속받은 자손 객체에서 사용할 수 있습니다. 예를 보시죠.


(1)public class parent {
	public void say() {
		System.out.println("부모");
	}
}
(2)public class son extends parent{
	public void say() {
		super.say();
		System.out.println("자손");
	}
	public static void main(String[] args) {
		son myson = new son(); // son 객체생성
		myson.say(); // 최종 출력 : 부모 자손
	}
}

parent를 상속받은 son 클래스가 있습니다. 저번 시간에 형 변환을 해서 parent에 있는 메소드를 수행하고자 했으나 실패를 했죠. 하지만 parent의 메소드를 수행하는 방법이 있다고 말씀드렸었습니다.
바로 super 키워드죠. (2) say() 메서드 안에 super.say()라고 되어있습니다. super 키워드는 자식-부모 관계(상속)에서 부모 객체라 생각하시면 되고, 부모객체.메소드를 사용하여 부모의 메소드를 수행할 수 있습니다. 조금 더 자세히 말하자면, 상속을 받은 son 클래스를 객체를 만들게 되면, son, parent 객체가 만들어져. myson이라는 객체에 들어 있다고 생각하시면 됩니다. 이 때문에 다형성, 형 변환이 가능한 것이기도 하고요. 어쨌든 myson은 son, parent 객체를 가지고 있기에 super라는 키워드를 사용하면, 현재 객체(son)의 부모(parent) 객체에 접근하여 부모의 메소드를 사용할 수 있는 것이죠.

마지막으로 final입니다.
final은 변수를 지정할 때 변하지 않는 '상수'로 많이 사용합니다.( 물론 다른 곳에서도 사용합니다.)
final 키워드를 가지게 되면, 바꿀 수 없습니다. final 변수명 = ~~~;로 선언하게 된다면, 선언한 곳에서만 내용을 바꿀 수 있습니다.


(3)public class Solution {
	
	public final int k = 3; //선언 한 곳 
	
	public static void main(String[] args) {
		Solution a = new Solution();
		a.k=6; // X 오류발생
	}
}

(3) 코드는 fianl로 int를 선언해보았습니다. 객체 a를 만들어 a.k로 접근하여 값을 변경하려니 오류가 발생합니다. 이처럼 final은 선언한 곳 에서만 값을 바꿀 수 있지 다른 곳에선 바꿀 수 없는 '상수'가 됩니다. 그럼 final 메소드를 알아보죠.


(4)public class parent {
	  public final void say() {
	  	System.out.println("부모");
	}
}
---------------------------------------------------------------------
public class son extends parent{

	public void say() {   // X 오류
		System.out.println("자손");
	}
	public static void main(String[] args) {
		son myson = new son(); 
		myson.say(); 
	}
}

(4) 코드에서 보듯이 상속 관계에서 parent의 say를 final로 지정하였습니다. 다시 말씀드리지만 final은 변할 수 없는 상태가 되는 것과 같습니다. 그래서, 자손 son에서 say를 오버라이딩을 시도하니 오류가 나오는 것을 알 수 있습니다.(오버라이딩은 메소드를 재정의하는 것이기 때문에)

다음은 final 클래스입니다.


(5)public final class parent { }
-----------------------------
   public class son extends parent{ }  //오류 

(5) 번 코드는 fianl로 선언된 parent를 son에 상속해주려고 했더니 오류가 발생합니다. 이유는 '상속을 위해 디자인된 클래스를 제외한 클래스를 상속하여 프로그램의 디자인을 깨고, 예상치 못한 일이 발생하는 것을 막아주기 위한 수단'이라는 설명이 있습니다. 개인적으로 맞다고는 공감하지만 말이 어렵죠.
저는 이렇게 생각합니다.'오버라이딩이 될 수도 있으니까.' final 클래스는 메소드를 선언하면 final을 붙이지 않아도 final메소드가 됩니다. 위에서 보셨듯 final메소드는 자손에서 오버라이딩 할 수 없습니다.
이러한 '위험요소' 가 있기에 처음부터 제한했다고 생각합니다.
구구절절 길어졌지만, final을 사용하면 원치 않은 상속을 피할 수 있습니다.
또 final로 선언된 메소드는 실행 속도가 빠르다고 합니다. 

오늘은 여기까지입니다! 자바 프로그램에서 없어도 되지만 있으면 훨씬 도움이 되는 3가지 키워드를 살펴보았습니다. 단순하게 이 3가지 키워드가 어떤 역할을 하는지 기억하셔야겠지만, 왜 사용 가능한지도 한번 곰곰이 생각해 보시면 더욱더 프로그래밍 하는데 도움이 되지 않을까 생각합니다.

다음 편은 추상 클래스 편을 써보겠습니다.

저를 포함한 초보자분들에게 도움이 됐으면 좋겠습니다.
초보자분들은 수박 겉핥기 식으로 알고 넘어가지 마시고, 조금이라도 왜 이렇게 프로그램이 돌아가는지 알고 가시는 시간이 됐으면 좋겠습니다.

오늘은 상속 편에서 잠깐 언급했던 오버 라디오에 관련한 내용을 적어볼까 합니다.
오버라이딩, 오버라이드, 하이딩은 서로서로 관련이 많기에 같이 설명해 보고자 합니다.
자 그럼 오버라이딩(Overriding)부터 설명해 보겠습니다.

오버라이딩(Overriding)이란 부모 클래스에서 정의한 메소드를 자손 클래스에서 재정의하여 사용하는 것입니다. 재정의하여 사용하니 메소드의 return, 이름, 매개변수는 같아야 합니다.
예시를 봅시다.

(1) public class parent {
	  public void say() {
		  System.out.println("부모");
	  }
  }
(2) public class son extends parent{
	  public void say() {
		  System.out.println("자손");
	  }
	  public static void main(String[] args) {
		  son myson = new son(); // 객체생성
		  myson.say(); //자손 출력
	  }
  }

(2) 코드를 보시면, son 클래스는 (1) 클래스인 parent를 상속받고 있습니다. 
그럼 생성한 myson 객체는 son, parent의 변수,메소드를 사용할 수 있죠.
하지만 son과 parent 클래스에 이름, 매개변수가 같은 메소드인 say을 두 클래스 모두 가지고 있습니다. 이러한 경우에 오버라이딩(Overriding)이 적용이 되는 것이죠.
이와 같은 경우에 say 메소드를 수행 시켜보면, son에 있는 say가 실행되는 것을 볼 수 있죠.
오버라이딩은 이와 같이 이름,매개변수가 같은 메소드가 부모, 자손 클래스에 모두 있으면 상속을 받는 자손메소드로 사용하는 것이죠.
이유는 간단합니다. 저번에도 말씀드렸듯 상속은 자손 클래스 - 부모 클래스 순서로 엮어주는 것입니다.
say라는 메소드를 실행하면, 자손 클래스 - 부모 클래스 순으로 처음 발견한 곳의 메소드를 수행합니다.
부모 클래스에만 있는 메소드를 사용할 수 있는 이유도 이와 같습니다. 자손 클래스에서 찾고 없으니 부모 클래스로 넘어가 찾아 사용하기 때문이죠.

그다음은 하이딩(hidding / 은닉)입니다.
하이딩은 static으로 정의된 메소드에 수행이 됩니다. 오버라이딩과 하는 일은 같습니다. 
하지만 다른 점이 하나 있습니다. 오버라이딩은 한번 되는 순간 형 변환을 통하여 부모메소드를 실행시켜도 오버라이딩된 메소드가 실행되는 것을 볼 수 있습니다. 예를 보시죠.

(3)public class son extends parent{
	public void say() {
        super.say(); //부모
		System.out.println("자손");
	}

	public static void main(String[] args) {
		son myson = new son(); // son 객체생성
		parent myparent = (parent)myson; // parent객체 생성  --- myson의 형변환
		myparent.say(); //자손
	}
}  최종출력: 부모 자손

(3) 코드와 같이 son 객체를 생성하여 부모의 형태로 변환하여 myparent에 저장을 해주었습니다.
이 상태에서 say()를 실행해 보면, 접근은 parent에 있는 메소드에 접근을 하지만, 결과는 "자손"이라는 출력이 나옵니다. myson.say()와 같죠. 이처럼 오버라이딩이 되면 형 변환으론 부모 메소드에 접근할 수 없습니다. 그럼 어떻게 접근할까요? 3번째 줄에 적혀있는 super라는 키워드가 부모의 재산(변수/메소드)에 접근 가능하도록 해줍니다. 
결국 (3) 코드의 결과는 "부모 자손"이라는 결과를 뽑게 되죠.

하이딩은 다릅니다! 형 변환으로 접근이 가능하죠. 이유는 단순히 숨기기 때문입니다.
예를 보시죠. 달라진 것은 say()메소드 앞에 static 키워드가 붙은 것뿐입니다.(부모/자손 say 둘 다)


(4)public class son extends parent{
	public static void say() {
		System.out.println("자손");
	}

	public static void main(String[] args) {
		son myson = new son(); // son 객체생성
		parent myparent = (parent)myson; // parent객체 생성
		myparent.say(); //부모
	}

(4) 번 코드를 수행하면 어떻게 될까요? 설명드렸듯 static으로 설정된 메소드는 오버라이딩이 되는 것이 아닌 하이딩이 되는 것이기에 "부모"라는 출력이 나옵니다. 부모 메소드 say()에 접근했단 것이죠.
그럼 왜 하이딩(hidding)을 하는 것일까요?? 이 부분은 static에 대해서 알아야 합니다. 여기선 간단하게 설명한 후 끝내도록 하죠. 
static 키워드는 하나만 쓰겠다는 것입니다. 무슨 말인고 하니..
A 클래스 안에 static int a =3; 이 되어있다고 합시다.
A newA = new A(); ,  A newA2 = new A();를 이용해 2개의 객체를 만들었다고 했을 때, 이 객체 안에 들어있는 int a는  A, A2 객체가 공유하고 있는 겁니다. 그래서 A.a=5로 변경을 하면, A2.a는 5가 되듯 공유하고 있는 것이죠. 이렇게 static 키워드는 하나를 공유하여 사용한다는 개념이기에 객체가 생성되기도 전에 미리 만들어져 있죠. 이와 같은 특성 때문에 객체를 만들지 않아도 static은 클래스명.~~로 접근이 가능합니다. 오늘은 이 정도만 설명하겠습니다.

언제든지 접근 가능해야 하는 static의 특성 때문에 hidding(은닉)으로써 super 키워드를 사용하지 않고 접근이 가능하도록 해준 것이죠. 하지만 static은 C의 전역변수 역할을 하기에 객체지향 언어인 java에선 사용을 자제하는 편입니다.

마지막으로 오버 로딩(Overloading)입니다. 오버라이딩은 상속관계 메소드의 이름, 매개변수가 같을 때 일어나지만, 오버 로딩은 메소드 이름이 같고 매개변수가 '다를 때' 일어납니다. 예제를 보시면 명확히 이해하실 수 있습니다.

(5)public class son {	
	public void say() {
		System.out.println("void say");
	}
	public void say(int k) {
		System.out.println("int say");
	}
	public void say(char a) {
		System.out.println("char say");
	}

	public static void main(String[] args) {
		son myson = new son(); // son 객체생성
		myson.say(); //void say
		myson.say('d'); //char say
		myson.say(4); //int say
	}
}

(5) 코드를 보시면 say메소드가 3개 존재합니다. 하지만 각각 매개변수 받는 형태가 다르죠.
이렇게 메소드 이름이 같고 매개변수를 받는 형태가 다른 것을 오버 로딩이라고 합니다.
오버 로딩을 하여 메소드를 수행하게 되면, 매개변수에 맞게 알맞은 메소드를 수행시켜 줍니다.
say()를 했을 땐 알아서 void say를 출력해주고, say('d')를 했을 땐 char say를 출력해 주듯 말이죠.

오늘도 코드적으로 뜯어보기보단 이론적인 설명이었습니다. 개념들 잘 익히시고 static 메소드는 왜 hiding이 되는지 정도만 알아두시면 될 것 같습니다.

다음은.. this, super 와 같은 키워드에 대해서 이야기해 보겠습니다.

저를 포함한 초보자분들에게 도움이 됐으면 좋겠습니다.
초보자분들은 수박 겉핥기 식으로 알고 넘어가지 마시고, 조금이라도 왜 이렇게 프로그램이 돌아가는지 알고 가시는 시간이 됐으면 좋겠습니다.

자바 뜯어보기 - 클래스 편 보셨는지요? 상속은 클래스 편을 반드시 보고 오시는 게 좋습니다.
http://sims-solve.tistory.com/5?category=747624


오늘은 클래스 편에 이어 '상속'에 대해서 이야기해보도록 하겠습니다.
대개 일상생활에서는 '상속' 하면 '재산 상속'이 생각나네요.
'재산 상속'이라 하면 가지고 있는 재산을 물려준다.라는 의미가 되는데요.
클래스에서도 이 의미가 통하게 됩니다.

일단 자바에서 어떻게 상속을 받는지 살펴보도록 합시다. 

(1)public class parent {
	public void say() {
		System.out.println("부모의 유산");
	}
}
(2)public class son extends parent{  }

(1)은 parent(부모)가 되는 클래스를 정의해 주었습니다. 
(2)는 son(자손)이 parent를 상속받은 클래스입니다.
(2)에서 보듯 상속은 extends를 사용합니다. 이렇게 parent를 상속해주면 어떻게 될까요?
상속은 '재산 상속'과 같다고 말씀드렸습니다. parent의 재산은 무엇일까요?
재산은 parent가 가지고 있는 변수 / 메소드라고 생각하시면 됩니다.
즉 parent가 가지고 있는 모든 것을 son에게 물려줍니다.

이렇게 되면 son은 무슨 이득이 있을까요? (3) 코드를 보시죠.

(3)public class son extends parent{
	 public static void main(String[] args) {
	 	 son myson = new son(); // 객체생성
	 	 myson.say(); // "부모의 유산" 출력
	 }
 }

say()라는 메소드는 (1) 소스에서 보듯이 parent에서 선언한 메소드입니다.
하지만 son은 parent를 상속받고 있으므로 son 형태로 만든 myson 객체가 say 메소드를 사용할 수 있습니다. 마치 myson 객체 안에 선언된 메소드처럼 사용할 수 있게 되는 것이죠.
이게 바로 상속입니다. 다시 정의를 하지 않아도 부모에게서 물려받은 변수 / 메소드를 재사용 할 수 있습니다.
물론 myson 안에 say가 생성되는 것은 아닙니다. 상속을 받게 되면 son - parent 순으로 연결이 된다고 생각하시면 됩니다. 그래서 myson.say()를 하게 되면, 처음은 son 클래스에 say()메소드가 있는지 찾습니다. 없으면 다음 순서인 parent로 넘어가 say()가 있나 찾습니다. 이렇게 상속받은 쪽으로 올라가면서 해당 메소드,변수가 있는지 찾아 찾으면 실행시켜 주는 것이죠.

증명을 하고 넘어가 보죠. 만약 son에서도 say()가 있다면 어떻게 될까요?


(4)public class son extends parent{
	
	public void say() {
		System.out.println("자손의 유산");
	}

	public static void main(String[] args) {
		son myson = new son(); // 객체생성
		myson.say(); // "자손의 유산" 출력
	}
}

(4) 코드를 해보시면, son에 있는 say 메소드가 실행되는 것을 보실 수 있을 겁니다.
이처럼 생성한 자손의 클래스에서 먼저 say()가 있는지 찾기 때문에 son에 있는 say()가 출력되는 것을 볼 수 있습니다. 이것을 바로 '오버라이딩'이라고 합니다.
지금은 간단히 설명하고 넘어가겠습니다. '오버라이딩'은 부모가 물려준 재산 중 중복된 재산이 있다면 
부모의 재산을 사용하지 않고 내가 가지고 있는 재산을 사용하겠다는 겁니다.
즉, 부모 건 필요 없다는 소리죠. '오버라이딩'에 관한 내용은 추후에 쓰겠습니다.

오늘도 코드적으로 뜯어보기보단 이론적인 설명이었습니다. 자바에서 Class를 사용하는데 상속은  약방에 감초같이 빠질 수 없는 역할을 하죠. 상속은 그렇게 어려운 개념은 아니므로 잘 이해하시고 넘어가시길 바랍니다.

다음은 오버라이딩,은닉 등등.. 추가적인 부분을 올리도록 하겠습니다.

저를 포함한 초보자 분들에게 도움이 됐으면 좋겠습니다.
초보자 분들은 수박 겉핥기 식으로 알고 넘어가지 마시고, 조금이라도 왜 이렇게 프로그램이 돌아가는지알고 가시는 시간이 됐으면 좋겠습니다.

자바 뜯어보기 - 입력편(BufferedReader) 보셨는지요?
안보고 오셨다면 한번 보고 오시는 것도 좋습니다.  
http://sims-solve.tistory.com/3?category=747624

블로그 키워드를 보니 '입력'으로 유입하시는 분들이 많더라구요. 
입력방법을 크게 Scanner, BufferedReader 두가지 뜯어봤는데요. 대게 BufferReader를 많이 사용해요. 왜냐면 입력편에서 말했듯 속도를 무시 할 수 없습니다.
그래서 이번편은! 입력을 받은 걸 어떻게 처리 할 수 있는지 알아보도록 합시다.

1. 혹시 1993-05-02를 입력으로 받아 년/월/일로 나눈다면 어떻게 하실 건가요? 

(1)	public static void main(String[] args) throws IOException {
		String k = "1993-05-02";
		System.out.println(k.substring(0,4)+" "+k.substring(5,7)+" "+k.substring(8,10));
	}

혹시나 (1)코드처럼 처리 하실껀가요?
그럼 만약에 1993-5-2로 입력이 들어오면 어떻게 할껀가요?
또 다시 다르게 처리를 해줘야 합니다. 이렇게 특정 문자로 구분되어있는 문자열을 아주 쉽게 처리할 수 있는 방법을 제시하는 클래스가 있습니다. StringTokenizer라는 아주 유용할 클래스가 존재합니다.
String은 문자열을 나타내며 Tokenizer는 낱말분석이라는 뜻입니다. 즉 문자열을 분석한다는 소리죠.

StringTokenizer는 사용자가 설정한 구분문자를 기준으로 나누어 출력을 할 수 있도록 도와줍니다.
예시를 봅시다.


(2)	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer st = new StringTokenizer(br.readLine(),"-");
		while(st.hasMoreTokens()) {
			System.out.print(st.nextToken());
		}
	}//최종출력 : 19930502

(2)코드와같이 사용 가능합니다. 일단 입력 BufferedReader는 우리가 일반적으로 사용하듯 만들어 주면 됩니다. new StringTokenizer(String , delim(구분문자))로 구성이 되는데 우리는 입력을 받은 것을 처리 하므로 br.readLine()을 넣어 주고 "1993-05-02"에서 "-"구분문자를 기준으로 자르고 싶으니 
구분문자를 "-"로 설정해 주면 됩니다.
자 설정은 끝났습니다. 이제 문자열을 분석해 보죠.


 (3)  public boolean hasMoreTokens() {
        newPosition = skipDelimiters(currentPosition);
        return (newPosition < maxPosition);
    }

일단은 .hasMoreTokens()입니다. (3)코드에서 보듯이 아주 간단합니다. 여기서 newPositio은 뽑아야될 문자의 위치입니다. maxPosition은 문자의 길이 - 1(0부터시작)를 뜻하구요.
즉, 뽑아야 할 문자가 있다면 true를 반환하게 됩니다. 예시로 설명드리죠.
1993-05-02가 있다면 처음 newPosition은 0입니다. nextToken()을 하게되면 구분문자"-"의 다음을 가리키게 되죠.
그럼 두번째 newPosition은 5, 같은 방법으로 하면 세번째 newPosition은 7, 네번째는 9가 됩니다.
9가되면 maxPosition과 같기에 false를 리턴하여 마무리합니다.

그 다음은 nextToken()메소드 입니다.
nextToken은 설정된 구분문자의 위치를 알아내어 문자을 처리하는 역할을 하죠.
조금 더 자세히 StringTokenizer를 뜯어볼까요?


(4)class StringTokenizer implements Enumeration

기본적으로 StringTokenizer클래스 안에는 (4)코드처럼  문자들을 쪼개기위해 여러가지 변수들이 있습니다. 이 변수들을 적절하게 사용하여 문자를 출력해주는 것이죠.
변수가 어떤 역할을 하는지는 주석으로 처리해 놓았으니 참고하시길 바랍니다.

(5)  public String nextToken() {

        currentPosition = (newPosition >= 0 && !delimsChanged) ?
            newPosition : skipDelimiters(currentPosition);

        delimsChanged = false;
        newPosition = -1;

        if (currentPosition >= maxPosition)
            throw new NoSuchElementException();
        int start = currentPosition;
        currentPosition = scanToken(currentPosition);
        return str.substring(start, currentPosition);
    }

(5)코드가 nextToken 메소드인데요. scanToken을 통하여 설정한 구분자 위치를 찾아내고 
String.substring를 사용하여 구분자 전까지 문자열을 리턴해 주네요. 

이처럼 StringTokenizer를 사용하게 된다면 손쉽게 구분자가 있는 문자열들은 처리 할 수 있습니다.
필자가 가장 많이 사용하는 경우는 생각보다 " "(띄어쓰기)로 문자열을 구분하여 처리해야 하는 경우가 많은데 이 경우 StringTokenizer로 많이 처리합니다.
여러분들도 StringTokenizer로 문자열을 현명하게 처리 할 수 있길 바랍니다.

저를 포함한 초보자 분들에게 도움이 됐으면 좋겠습니다.
초보자 분들은 수박 겉핥기 식으로 알고 넘어가지 마시고, 조금이라도 왜 이렇게 프로그램이 돌아가는지알고 가시는 시간이 됐으면 좋겠습니다.

오늘은 java에 없어서는 안될 Class를 이야기해 보겠습니다.
일단 Class가 왜 자바에서 중요할까요?
Java는 OOP(객체지향 프로그래밍)을 지향합니다. C는 절차지향 프로그래밍이죠.
여기서 '객체'라는 것이 나오는데 이 '객체'를 중심으로 프로그래밍을 하는거죠.
그럼 객체는  뭘까요? Class를 이용하여 만들어진 것이라고 보시면 됩니다.
그럼 Class는? 객체를 생성하기 위한 틀 이라고 보시면 됩니다.
말이 어렵습니다.. 간단하게 가보죠.

게임을 시작하면 캐릭터를 만드는 창이 나옵니다.
캐릭터를 만드는 화면이 클래스 입니다. 이 화면을 통하여 캐릭터(객체)를 생성할 수 있죠.
우리는 이 클래스(캐릭터 생성화면)를 이용하여 캐릭터(객체)의 머리모양,눈모양,..... 등등 많은 것을 바꿀 수 있죠. 그리고 캐릭터를 생성을 하게됩니다. 이 생성된 캐릭터가 하나의 '객체'가 되는 것이죠.
다른 사람이 캐릭터를 만들게 된다면 어떨까요.
같은 캐릭터 생성화면(Class)을 사용하기 때문에 캐릭터(객체)는 전체적인 외형은 다를지라도, 머리모양,눈모양...등등 같은 속성을 가지게 되죠. 마찬가지로 '객체'가 생성되는 것이죠.

그럼 자바에선 어떻게 Class를 정의하고 객체를 만들까요? 이미 우리는 자바를 시작할 때 Class를 보았습니다.


(1)public class first{
	public static void main(String[] args) {
		System.out.println("Hello World!");
		
	}
}

처음 자바를 실행 해보면 'Hello World!'를 출력하는 코드가 나올 것입니다. 
이것도 하나의 class로 구성돼있습니다. (1)코드의 첫줄 public class first{ ~~ }에서 보듯 우리는 처음부터  Class를 사용하고 있는 것이죠.
Class는 간단하게 정의 할 수 있습니다.


 (2)public class second {  }

이런식으로 말이죠. 물론 second Class파일을 새로 만들어 주어야 합니다. 즉, first,second 2개의 자바 파일이 있는 것이죠. first는 class로 구성이 되어있지만 실질적으론 second의 객체를 만드는 역할을 할 것이고 second는 객체를 찍어내는 class(틀)역할을 할 것 입니다.

클래스는 만들었으니 이제 '객체'를 만들어 봅시다.
first에 second객체를 만들어 보겠습니다.


 
(3)public class first {	
        public static void main(String[] args) {
		    second create = new second();
	     }
}이와같이 < Class명 변수명 = new Class명(); > 이런식으로 객체를 생성 할 수 있습니다. 
객체를 생성할 때는 new키워드를 이용하여 만들어주고, new를 이용하게 되면 완전히 새로운 객체를 만들게 됩니다.
하지만, 이런 객체를 만들어도 (2)코드 second Class에서는 어떤 내용도 들어있지 않기에 깡통을 만드는 것과 같습니다. 그럼 일단 Class(틀)에 속성들을 넣어봅시다.



 (4)public class second {
	 String name = "Sims";
	 String head_Color = "black";
	
	 public void set_name(String change_name) {
	 	name = change_name;
	 }
	
	 public void set_headColor(String change_Color) {
	 	head_Color = change_Color;
	 }
	
	 public void walk() {
	 	System.out.println("걷는중");
	 }
	
	 public void attack() {
	 	System.out.println("공격");
	 }
}

(4)와같이 second에 머리색,아이디를 지정하는 변수를 만들고 이름,머리색을 바꿀 수 있는 메소드 또한 정의해 주면, 객체의 머리색을 바꿀수도 있고 아이디또한 바꿀 수 있습니다. 
그럼 어디서 머리색을 바꿀까요? 우리가 생성해준 first파일의 create객체를 이용하여 바꿀 수 있습니다.


(5)public class first {	
	public static void main(String[] args) {
		second create = new second(); (객체생성 = 캐릭터생성)
		create.set_headColor("red");
		System.out.println(create.head_Color); //red
		create.walk(); //걷는중
	}
}
create는 second틀을 이용하여 만들었기에 second안에 있는 변수(name,head_Color),메소드(walk,run,set_name,set_headColor)를 따로 생성하여 가지고 있는 것입니다. 그럼 create안에 있는 것들에 접근해보죠.
create. 를 이용하여 접근 할 수 있습니다. < . >이 키워드가 바로 내부에 있는 속성들을 접근 하는 키워드 입니다. 예를 들어 create.set_headColor(~~);를 한다면 create안에 있는 set_headColor 메소드에 접근하여 일을 수행하는 것이죠.

우리가 출력을 하기위해 사용했던 System.out.println()도 System이라는 객체안에 있는 out에 접근하여 out안에 있는 println메소드를 접근하여 출력을 해주었던 것입니다!
(여기서 System이라는 객체를 만들지 않았는데 어떻게 접근하냐! 라는 질문이 나올 수 있습니다.
나중에 static 키워드를 배우게 되는데 이놈은 객체를 만들지 않고 어디서든 접근이 가능합니다. System안에 있는 out이 static변수로 선언되어있기에 객체를 생성을 하지 않아도 접근이 가능합니다.)

지금까지 Class에 대해서 설명을 해보았습니다. 자바는 Class와 객체의 개념만 어느정도 습득한다면 쉬운 언어이므로 Class,객체에 대해서 좀 더 자세히 알아 보시는 것도 좋을 것 같습니다. 코드적으론 뜯어볼게 없었네요. 

다음은 상속에 대해서 올리도록 하겠습니다.


+ Recent posts