assert()
컴파일된 스크립트 명령들의 묶음을 가리키는 변수를 인수로 사용하여 assert 를 호출하면 그 명령들이 즉시 실행된다. 루아 스크립트 명령 묶음들은 loadstring 이나 loadfile 함수로 적재, 컴파일 할 수가 있다.
다만, 게임 개발에서 loadfile 은 그리 유용하지 않다는 데에 있는데, 그 이유는 더 편한 dofile 이 있기 때문이다. 하지만 loadstring 과 assert 함수가 크로스 하면, 루아 스크립트 안에서 문자열 형태의 루아 명령 묶음들을 만들고 그것을 즉시 실행한다는 유용한 능력을 얻을 수 있다.
'>
-> a 에다가 문자열을 저장하고, b 에다가 실행하는 함수 명령을 저장한다. 이후 assert() 를 사용하여 실행
dofile(파일이름)
루아 스크립트 파일을 적재하고 즉시 처리하는 함수
수학함수들
math.floor()
주어진 수의 소수부를 버리고 정수로 변환하는 함수. 반올림이 필요하면 0.5를 더한 값을 이 함수에 넣으면 된다.
'>
math.random()
난수를 사용할 수 있게 만드는 함수. 아무 인수도 지정하지 않은 경우 0에서 1사이의 의사 난수를 돌려준다. 생성할 난수의 최대, 최소값을 지정할 수 있다.
'>-> math.random() 을 이용한 8면체 주사위를 보여준 화면
이 난수 함수로 좀 더 무작위해 보이는 값들을 얻으려면, 프로그램을 시작할 때 난수 발생기에 고유한 값을 Seed 값으로 제공할 필요가 있다. 매번 고유한 Seed 값을 얻는 용도로는 날짜를 출력해주는 os.date 함수가 적합하다.
※ Game Development with LUA 책에서는 이 부분에 math.randomseed 라는 함수를 사용한 예시를 적어주었다.
math.randomseed(os.date("%d%H%M%S"))
그런데 이 부분을 고스란히 변수에 넣어서 출력해보면 nil 값이 출력된다.
조금은 더 공부하고 찾아봐야 할 부분. 이해가 되지 않는다.
math.min(), math.max() 어떤 값들의 집합에서 가장 큰 값 또는 가장 작은 값을 알아내야 하는 경우 사용하는 함수
--> 하나의 매개변수를 받는 함수 SetName() 을 실행시키는 화면, SetName에 매개변수 Young 을 집어넣었다. 한편, 문자열(String)을 이용하려면 항상 "" 가 있어야 한다는 것을 확인 할 수 있다. 만약 "" 가 없으면 문자열을 인식하지 못한다.
2. 다중 인수 여러개의 매개변수를 받는 함수
< ch502.lua 코드 >
--> 두개의 매개변수를 받는 함수 MyInfo() 를 실행시킨 화면. MyInfo에 두개의 매개변수를 집어넣어 실행시켰다.
다음은 가변적인 갯수, 즉 정해지지 않은 갯수의 매개변수를 받는 함수다.
< ch 503.lua 코드 >
--> 가변적인 갯수, 즉 정해지지 않은 객수의 매개변수를 받는 함수다. 여기서는 무작위로 4개의 매개변수를 받아 실행시키는 화면이다.
가변적인 갯수를 받으려면, 함수이름 옆에 매개변수 이름을 ...(마침표 세개)를 써주면 된다.
그렇게 한후, 함수를 실행시킬 때 입력되는 매개변수는 지역 테이블 arg(지역변수처럼 if문 안에서만 사용) 안에 차례대로 저장이 된다. 그리고 테이블 arg에 저장된 인수의 갯수는 arg.n 으로 알아낼 수가 있다.
(※ 무조건 arg.n 만 되는지는 아직 확인이 안됨. 분명한 것은 arg.n 이외에 것을 집어넣으면 오류가 발생, arg.a 나, name.n 등, 이름을 변경하려했지만 모두 에러가 발생함)
한편, string.format 함수는 뒤에 따라 나오는 매개변수들을 모두 나열하라는 의미를 지닌 함수이다. 사용법은 나중에 보기로 한다.
다음은 필수적으로 입력해야 하는 매개변수와, 추가도 가능한 형식의 매개변수를 받는 함수다.
< ch 503.lua 코드 > 안보이시면 클릭!
--> 위의 함수형식과 마찬가지다. 단, 여기서는 처음 두 매개변수, val1 과 val2 는 생략하면 안된다. 매개변수가 딱 2개일 경우에는 그냥 그 두 매개변수를 곱한다. 만일 세번째 매개변수가 존재하면 함수는 그것을 인수와 함께 곱해버린다. 그러나 그 이상의 추가되는 매개변수들은 무시가 된다.
3. 반환값
함수에서 자신의 처리 결과를 호출한 곳에게 돌려주는 역할을 하는 것이 있는데, 이때는 return 문을 사용한다.
사용용법은 다른 언어와 마찬가지인데, 다만, return 문에서 다른 함수를 호출하는 경우 루아는 그런 return 문을 그 함수 처리의 물리적인 끝으로 보고, 그 함수를 스택에서 제거한 후 즉시 다음 함수로 건너뛴다.
--> 위에서 함수 ExploreMaze() 가 실행이 되면, RoomA() 라는 함수로 넘어가게 된다. RoomA 라는 함수에서는 또, east 라는 입력을 받지 않으면, 여전히 RoomA() 함수에 머무르게 되지만, east를 입력하게 되면, HallA() 라는 함수로 넘어가게 된다. 이런식으로 쭈욱 이어지게 된다.
기본적으로 모든 변수는 전역변수이지만, 필요에 따라 지역변수를 설정해야 하는 경우가 있는데, 이때 지역변수는 local 이라는 예약어를 사용하여 지정한다.
function MyFunction()
local myX = 7 -- 이 변수는 함수 실행이 끝나면 사라진다.
if myX < 10 then
local myY = "Hello World" -- 이 변수는 if문이 끝나면 사라진다.
print(myY) -- 출력
end
print(myY) -- 위의 if문이 끝났으므로 nil 이 출력
end
한편 local 을 붙이지 않아도 자동적으로 지역변수가 되는 경우가 있는데,
1) for i=1, 10 do.... end 의 i는 for문 안에서만 유효한 값이다.
2) function MyFunction(param) 에서 param 에 들어가는 값 역시 자동으로 지역변수가 되며, 함수 MyFunction 이 끝나면 자동으로 사라진다.
1. 루아의 변수 형식
1) nil
(1) 다른 언어로 치자면 null 에 해당되는 형식
(2) 변수 myValue 에 값이 저장 되어있을때, nil 을 다시 배정하면,
변수 myValue 에 저장된 변수는 삭제됨
-> myValue 에는 처음에 아무 값도 저장되어 있지 않다. 그러다 7을 저장했고, 이후 nil 을 이용해 다시 변수 myValue 를 초기화 시켜버렸다. 그래서 지금은 아무것도 없는 상태가 되어 버렸다.
2) 부울 - boolean
(1) 조건문에서 논리값을 계산하여 true 혹은 false 값을 반환시킨다.
3) 문자열 - string
(1) 문자열의 값은 단문이든 수백만개의 문자열이든 모두 가능
(2) 숫자와 문자열을 자동적으로 상호 변환
< 클릭하면 확대 가능 >
-> ""안에 쌓여있는 문자열 "8"과 숫자 8 을 더하면, 루아에서는 알아서 자동으로 둘다 숫자로 변환해서 8+8 의 결과 16을 출력해준다. 하지만 "" 안에 쌓인 문자열은 그대로 그냥 문자열일 뿐이고 그래서 문자열로써 출력할 뿐이다. 하지만 여기서 문자열 "Hello World"와 숫자 8을 더한다면? 바로 에러가 뜬다. 문자열 "Hello World" 를 숫자로 변환할 수는 없으니까.
4) 수치 - number
-> 여러가지 수치 출력 방식이다. 7.65e8 은 7.65 x 10 8승을 표현, 7.65e-2 는 7.65 x 10 -2승을 표현한다.
5) 테이블 - table
(1) 일종의 배열 역할이지만 완벽한 배열의 느낌은 아님
(2) 배열에서는 0번째 속성이 있지만, 테이블에서는 0번째가 아니라 1번째로 바로 저장이 됨
-> 테이블은 다른 언어에서의 배열과 비슷한 역할을 한다. 하지만 0번째가 없이 바로 1번째로 저장이 된다. 3번째 요소를 출력하니 6이 나온 것처럼 말이다. 그리고 이후에 하나를 뒤에 더 추가할 수가 있다.
public class HelloWipi extends Jlet { // 프로그램 시작 public void startApp(String[] arg0) { // 기본적인 Display 생성 Display test = Display.getDefaultDisplay();
// card 객체를 생성 CardTest card = new CardTest(); // card 객체를 pushCard 를 사용하면 아래의 CardTest.java // paint 메소드를 한번 호출. test.pushCard(card); }
public void pauseApp(){} // 프로그램을 일시정지시 호출 public void resumeApp(){} // 프로그램을 다시 실행시 호출 // 프로그램을 종료시 호출 public void destroyApp(boolean arg0){ } }
CardTest.java
import org.kwis.msp.lcdui.*;
public class CardTest extends Card { // 위의 HelloWipi.java 에서 사용된 pushCard 에 의해서 호출되어짐 public void paint(Graphics a){ // 시작점 (10, 10), 위치(a.TOP | a.LEFT) 설정 및 화면 출력 a.drawString("Hello~ This is Young.", 10, 10, a.TOP | a.LEFT); } }
여기서 중요할 점은, class 밖에다가는, import, package, 또다른 class 이외에는 다른 코드를 사용할수가 없다는 점이다.
즉, 메소드가 단독적으로 class 밖에서 사용할수는 없다는 이야기이다.
2번째 라인 ~ 4번째 라인에서 사용된 메소드를 이용하여, 6, 8, 9, 11번째 라인에서, 메소드명으로 해당 메소드를 호출하여 결과값이 수정전 코드와 똑같게 출력되게 만들어 주었다.
2. 메소드의 종류 메소드의 종류에는 총 3가지가 있으나, 이 파트에서는 2가지만 다루도록 한다. (1) call by name : 메소드의 이름에 의해 호출되는 메소드로 특정 매개변수 없이 실행가능하다. 하지만, 다른 방식의 메소드 호출에 비해 기능이 상당히 제한적이다. 이 방식은 이미 결정된 결과를 출력하거나, 고정된 실행을 요구하는 것외에는 사용 할수가 없다.
위에 예제를 든 메소드가 이에 해당한다.
(2) call by value : 메소드를 이름으로 호출할 때 특정 매개변수를 전달하여 그 값을 기초로 실행하는 방식인데 각 매개변수는 그 개수에 있어서 제한도 없고 자료형에도 제약이 없다. 클래스 형이 사용될수도 있다. 이 경우에는 상당히 동적이라고 말할 수가 있다.
예를 들어 보자.
1// public class Exam07_02 { 2// public static void view_star(int y, int x) { 3// for(int j = 0; j < y; ++j) { 4// for(int i = 0; i < x; ++i) { 5// System.out.print("*"); 6// } 7// System.out.println(); 8// } 9// // x = 75; 10// } // 행렬 형식의 출력 메소드
이렇게 매겨변수를 받아, 그 매개변수로 동적으로 실행시킬때 사용할수 있는 방식이 바로 call by value 방식이다.
여기서 생각해야 할 것 하나. 만약, 9번째 라인에 있는 주석을 제거하여 x = 75 라는 코드를 살려두었다고 하여도, 12, 14, 15, 18번째 라인에 있는 매개변수 x 에 해당되는 값들에 영향을 미치지는 않는다.
위의 메소드 영역과, 아래 main 영역은 전혀 차별화된 공간이며 서로 다른 영역이기 때문에 서로간의 값이 영향을 받지 않는다. 이렇게 원본 값이 변하지 않는 것이 call by value 이며, 훗날 배우게 될 call by reference 에서는 원본 값이 변하게 되는데, 이것은 나중에 설명하기로 한다.
(3) call by reference : 메소드 호출시 매개변수로 사용되는 값이 특정 위치를 참조하는 reference 변수, 차후 설명
3. 결과형 리턴값 메소드를 실행한 후 결과로 되돌려 주는 값을 지칭하며, 이때는 return 예약어를 사용해야 한다.
하지만, 지금까지의 코드들은 대부분 되돌려주는 값이 없었다. 그렇기에 return 이라는 예약어가 생략 가능했었다.
이러한 것에는 여러 종류가 있는데, 이를 살펴보면...
void : 실행 후 돌려줄 결과가 없을 때 원시 타입 자료형 : 8대 자료형 클래스형 : 모든 클래스나 인터페이스 형
위의 예문을 본다면, 2번째 라인에서 void 가 사용되었는데, 이는 되돌려주는 값이 없다는 뜻이며, 이로 인해 return 이 생략이 되었던 것이다. 하지만, 9번째 라인의 주석을 치우고, return; 라는 구문을 추가할수도 있으며, return 이후에 나오는 값을, 되돌려 준다.
public class Exam07_03 { public static boolean aaa() { return false; }
public static int bbb() { return 34; }
public static int ccc(int x, int y) { int z = x + y; return z; }
public static void main(String[] ar) { boolean a = aaa(); System.out.println("a = " + a); int b = bbb(); System.out.println("b = " + b); inc c = ccc(10, 20); System.out.println("c = " + c); } }
여기에서는 3가지의 메소드가 사용되어졌다. 첫번째 메소드 aaa() 는 논리형인 boolean 값을 되돌려 주었고, 두번째 메소드 bbb() 에서는 정수형인 int 값을 되돌려 주었다. 세번째 메소드 ccc() 에서는 매개변수 int x, int y 를 받아서, 덧셈을 한후, 그 결과값을 되돌려 주는 기능을 수행한다.
이렇게, 메소드 안에서 어떠한 기능을 수행한후 되돌려 주고픈 값이 있을때, 그때 사용되는게 바로 이 return 이라는 예약어이다.
p.s 위의 글들은, 김승현 강사님의 열혈강의 Java Programming 의 Round 7-1, 2, 3, 동영상 강좌에 기초한 내용입니다.
상속 재사용 즉, 효율적인 코드를 반복 사용하기 위해 등장한 개념으로, 보통 상속의 개념을 생각하면 쉽게 이해가 되는 의미이다.
자바에서는 순수하게 하나의 클래스에게서만 상속받을 수 있으며, 이를 단일 상속이라고 하며, 예약어로 extends 를 사용한다.
다음과 같은 class가 있다고 가정을 하자.
class A { int a = 100; }
누군가가 다음과 같은 class A가 마음에 들어 그것을 가져다 쓰고 싶다면, 다음과 같은 식으로 사용하면 된다.
class B { A ap = new A(); int b = 200; }
이렇듯 하나의 클래스 내부에 다른 클래스의 객체를 포함하고 있는 형태를 포함 Object 라고 한다. 굳이 상속을 사용하지 않더라도 자연스럽게 다른 클래스를 재사용할 수 있다. 하지만 문제는 다음과 같은 상황에서 발생한다.
또다른 누군가가 class A를 재사용한다고 한다면 이렇게 된다.
class C { B bp = new B(); int c = 300; }
또다른 누군가가 재사용한다면..
class D { C cp = new C(); int d = 400; }
이렇게 된다면, 다음과 같은 경우, 문제가 발생한다. a라는 멤버의 값을 출력하라고 한다면??
D dp = new new D(); System.out.println("a = " + dp.cp.bp.ap.a);
원칙대로라면 정확한 사용법이지만, 과연 이런식의 작성이 효율적일지는 생각해 봐야 한다. 만약 이보다 더 늘어날 경우, 프로그램은 대책없이 커지게 될테니까.
이러한 문제점을 해결하기 위해 등장한 개념이 바로 상속이다.
자바의 API 를 살펴본다면, java.lang 이라는 패키지 안에 Object 라는 클래스가 있는데, Object 클래스는 어느 누구에게도 상속을 받지 않고 있으며, 이는 바로 Object 클래스가 최상위, 부모 클래스라는 것을 의미한다. 이 의미는 즉, Object 를 제외한 모든 클래스(interface 제외)가 Object 클래스에게서 상속을 받는 다는 의미가 된다.
이 Object 클래스는 상속관계가 적혀 있지 않은 모든 클래스를 자동적으로 java.lang.Object 클래스가 상속되었다고 JVM 이 인식하고 있으므로 생략이 가능하다.
다음과 같은 소스를 살펴 보자
1// class Inherit extends Object {
2// }
3// public class Round13_Ex01 extends Object { 4// public static void main(String[] ar) { 5// Inherit in = new Inherit(); 6// System.out.println("두 객체가 같니? " + in.equals(in)); 7// } 8// }
Inherit 클래스는 아무것도 내용이 적혀 있지 않은 클래스다. 5번째 라인에서는 객체를 생성시 아무런 생성자도 없다면 default 생성자가 있는 것으로 JVM 이 인식하기 때문에 아무런 에러 메시지도 없으면서 잘 실행 된다.
다음 라인인 6번째 라인에서의 in.equals(in) 이 문제인데, 객체 메서드를 보면 먼저 in을 발생시킨 클래스 내에서 equals() 메소드를 찾아야 한다. 그러나 in 이라는 객체를 발생시킨 Inherit 클래스 내부에는 아무것도 없다. 그렇다면 당연히 에러메세지가 출력이 되어야 하지만, 상위 클래스로부터 물려받았을 수도 있기 때문에 그 클래스에게 상속을 준 상위 클래스로 거슬러 올라가야 한다.
Inherit 에게 상속한 상위클래스는 생략되어 있는 Object 클래스이며, 이 클래스 안에는 equals() 라는 메소드가 있기에, 에러가 발생하지 않는 것이다.
이렇듯 상속이란, 이미 작성되어 있던 내용들을 마치 내가 만든 것처럼 다시 사용할수 있게 만드는 개념이다.
상속에서는 이렇게 메소드를 찾아 나가는데, 먼저 해당 객체의 클래스부터 먼저 뒤져보고, 없으면 그 다음의 상위 클래스로 찾아 올라가고, 그렇게 찾아 올라가다, 끝까지 가서도 못찾으면 그때서야 에러가 발생하게 된다.
상속받은 하위 클래스에서는 상위 클래스의 멤버를 마치 자신의 멤버인 양 사용할수 있으며, 모든 클래스의 최상위에는 Object 클래스가 있다는 것을 염두해 두어야 한다.
또 한가지, super 라는 예약어는 상위클래스의 멤버를 지칭할때 사용하는 예약어이며, this 는 현재 클래스의 자기 자신을 의미하는 예약어이다.
상속에서의 몇가지 특징은
1. 상속받는 class 는 상속해 주는 class 의 private 와 생성자를 제외한 모든 것을 상속받는다. 2. 단일 상속만 가능 3. protected 는 상속받는 클래스에서 접근이 가능 4. 상속받는 클래스에서 객체를 생성하면, 상위클래스와 자신의 생성자를 모두 호출
오버라이드 메서드의 상속에서 동일한 이름의 메서드가 상위 클래스와 하위 클래스에 동시에 존재하는 것을 보고 오버라이딩이라고 한다.
class A2 { public void disp() { System.out.println("A2 클래스"); } }
class B2 extends A2 { public void disp() { super.disp(); // A2 클래스의 disp() 메서드를 호출 System.out.println("B2 클래스"); } }
여기에서 A2 클래스와 B2 클래스안에는 동일한 이름의 메서드가 있다. 이때 B2 클래스의 disp() 메소드를 보고 오버라이딩되었다라고 한다.
이렇듯 오버라이딩은 상속관계에서 상위클래스와 하위클래스에서 동일한 이름의 메서드가 있는 것을 보고 이야기 하는 것이다.
여기에는 두가지 중요한 규칙이 있다.
1. 오버라이딩을 할 때에는 접근 제한자의 범위가 같거나 커야 한다.
private < package < protected < public
2. 오버라이딩된 하위 클래스의 메서드에 throws 구문이 있으면 상위 클래스의 오버라이딩을 준 메서드도 반드시 해당 예외 클래스에 준하는 throws 구문이 있어야 한다.
이 두가지 규칙을 위반하면 에러가 발생한다.
class A { private void aaa() {...} }
class B extends A { private(package, protected, public) void aaa() {...} }
상위 클래스의 메소드를 오버라이딩 하려고 할 때 그 메소드가 private 이면 하위 클래스에 오버라이딩 되는 메서드의 접근 제한자로는 private, package, protected, public 네가지 모두 된다는 뜻이다. 만약 상위 클래스의 메소드가 package 라면 하위 클래스의 메소드는 package, protected, public 세가지가 될수 있고, protected 라면 protected 와 public 두가지만 된다. 이렇게 되는 것이 첫번째 규칙이다.
class A { public void aaa() throws Exception{...} // 상위 클래스의 메소드에 예외 구문이 있다면) }
class B extends A { public void aaa() {...} // throws 구문이 있어도 되고 없어도 된다. }
반면
class A { public void aaa() {...} // 아래와 같다면, 상위 클래스에서 throws 가 반드시 있어야 한다. }
class B extends A { public void aaa() throws Exception {...} // 하위 클래스의 메서드에 예외구문이 있다면.. }
이것이 두번째 규칙이다.
오버라이딩이 되면, 하위클래스의 오버라이딩 된 메소드가 실행이 된다.
Polymorphism(다형성) 1. 여러개의 개별적인 클래스를 하나의 부모 클래스 객체로 통합 관리하여 그 효율성을 높인 것. 2. 필드는 무조건 상위 클래스의 필드만 사용할 수 있다. 3. 메서드도 상위 클래스의 메서드만 사용할 수 있는데 오버라이딩이 되었다면 하위 클래스의 메서드가 실행된다.
다형성을 사용하는 이유는??? 확실하지는 않지만...
다형성이요?
'여러 개의 클래스가 같은 메시지에 대해서 각자의 방법으로 작용할 수 있는 능력'이라고 나와 있네요
객체지향프로그램의 특징중의 하나인 다형성을 얻기위해서 그런식으로 사용을 하지여... 저도 짧은 지식으로 설명하려니.. 어렵네여...
다
형성이란 같은 메시지에 대해서 각각의 객체들이 다르게 반응할 수 있는 능력이라고 할 수 있습니다. 말이 어렵네여.. 한마디로
이런거지여.... 만약.. 내가 상대방에게 "cut"이라고 말했다면.. 미용사라면... 사람의 머리카락을 자를것이고...
의사라면.. 수술을 할 것이고, 배우라면 연기를 멈추겠지여.. 미용사, 의사, 배우 모두 "cut"이라는 동일한 메시지를
받았지만.. 다르게 반응하잖아여.. 이게 다형성이란 넘입니다.. 죄성.. 저도 무지 헷갈리네여.. 공부를 더 많이 해야할듯..
-_-;;
메시지가 뭐냐면여.. aa.d(); 이런게 메시지예여.. 이런걸 점표기법이라고 하데여.. aa는 메시지를
받을 객체를 나타내는거구여.. d()는 그 객체가 수행할 메소드(행위)를 나타냅니다. 한마디로 말하면.. 객체 aa에게 "d()
메소드를 수행해 주세요"라고 말하는것과 같습니다..
자바에서는 부모 클래스의 참조변수(님이 쓰신 내용중의 aa)를
이용해서 자식 클래스의 객체를 참조할 수 있기 때문에 다형성이 가능한것입니다. A aa = new B(); 하셨지여..
그니까.. 부모 클래스의 참조변수 aa가 자식 클래스인 B의 객체를 참조하고 있는것입니다.(에구.. 단어선택이 점 어렵네여..
적절치못한것도 같고..)
프린터의 경우를 예를 들어보죠. 프린터에는 도트프린터, 잉크젯프린터, 레이저프린터 등
여러가지 종류의 프린터가 있습니다. 모든 프린터에 적용되는 종이에 인쇄하는 메소드인 print()를 정의한다고 가정해보죠.
그런데 print() 메소드의 세부적인 구현들은 프린터마다 다 달라야 합니다. 왜냐하면.. 각 프린터마다 동작방법이
다르거든요.. 그래서 여기에서는 모든 프린터에 공통된 특징을 가지고 있는 Printer라는 부모 클래스를 먼저 만들고.. 각각의
프린터 클래스들은 이 부모 클래스를 상속 받아서 세부내용들을 구현하도록 할 겁니다..
부모 클래스: class Printer { public void print() {} }
부
모 클래스가 정의되었고 모든 프린터에 공통된 메소드인 print() 메소드가 정의되어있지여? 근데 내용은 비어있군여.
print() 메소드는 종이에 인쇄하는 메소드인데 각각의 프린터가 인쇄하는 방법이 다르기 때문에 각각의 프린터 클래스들이 이
메소드를 재정의해서 인쇄하는 프로그램을 구현할 겁니다.
class LaserPrinter extends Printer { public void print() { 레이저프린터에 맞는 세부내용 구현 } }
class InkjetPrinter extends Printer { public void print() { 잉크젯프린터에 맞는 세부내용 구현 } }
class DotmatrixPrinter extends Printer { public void print() { 도트프린터에 맞는 세부내용 구현 } }
에고.. 이제 각각의 프린터들을 위한 클래스들이 만들어졌군여... 이제 프로그램에서.. 객체를 생성해보겠습니다.
Printer p = new InkjetPrinter();
흠.. 잉크젯프린터를 위한 객체가 생성이 되었군여.. 그럼..
p.print();
라고 메시지를 보내면 어케될까여.. 당근 잉크젯프린터 객체가 잉크젯 프린터의 특성에 맞게 인쇄를 하겠지여. 그럼 이렇게 하면 어케될까여..
Printer p = new LaserPrinter(); p.print();
당
연히 레이저프린터의 특성에 맞게 인쇄가 되겠지여.. 똑같이 p.print();라는 메시지를 보냈는데.. p가 어떤 객체냐에
따라서 다르게 반응할 수 있게 된거지여.. 이게 다형성입니다. 위에서도 얘기했지만.. 이것이 가능한 이유는 부모클래스의
참조변수가 자식 클래스의 객체를 참조할 수 있기 때문입니다. 단 이때 부모클래스의 참조변수가 접근할 수 있는것은 부모가 물려준
변수와 메소드 뿐입니다.
위의 소스에서 부모 클래스의 참조변수인 p가 LaserPrinter클래스, InkjetPrinter클래스의 객체를 참조할 수 있었던것은 두 클래스 모두 부모 클래스인 Printer 클래스의 자식이기 때문입니다.
Abstract Class 1. 다형성에서 특별한 할일이 없이, 단지 클래스들을 하나로 묶어주는 역할만 할때 사용 2. 일반 클래스의 모든 기능을 할수 있으나, 자신의 객체만큼은 발생시키지 못한다. 3. 만약 충상 클래스가 추상 메서드를 가지고 있다면 그것은 반드시 오버라이딩이 되어야 한다.
Abstract Method 1. 추상 메소드는 ; 로 끝이남 2. 반드시 오버라이딩이 되어야만 하며, 그렇지 않을 경우, 이 메소드를 포함하는 클래스 역시, 추상 클래스를 상속 받아, 추상 클래스가 되어, 객체를 발생시키지 못한다. 3. 직접 객체를 발생시킬 필요가 없고 단지 상속에 의해서 하위 클래스만 제어할 목적일때 사용한다.
Interface 1. 자바에서는 단일 상속만 가능하며, 이로 인해 불편함을 개선하고자 등장한 다중 상속의 개념이 바로 인터페이스 2. 인터페이스 혹은 클래스등, 같은 유형간의 상속에는 extends 라는 예약어를, 인터페이스가 클래스에게 상속할 때는 implements 라는 예약어를 사용하나, 클래스가 인터페이스에게 상속할 수는 없다. 3. 필드는 무조건 public static final 로 인식 4. 메소드는 무조건 public abstract 멤버 메소드로 인식. 5. 중첩 클래스에는 자동적은 static 이 붙는다. 6. 인터페이스 내에 있을 수 있는 멤버는 위에 3가지를 제외하고는 없다. 7. 생성자도 없다. 8. 추상 메서드를 포함하고 있기 때문에 객체를 발생시킬수도 없다.
1. 두개의 수와 배수 n을 입력받아 두 수 사이의 n 배수의 합을 구하는 프로그램을 작성해보자.
1// import java.io.*;
2// public class SumTwoMulti { 3// public static void main(String[] args) throws IOException { 4// BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
5// int first = 0, second = 0, mul = 0, sum = 0, temp = 0;
6// System.out.print("Input the Frist Number : "); 7// first = Integer.parseInt(in.readLine());
8// Ststem.out.print("Input the Second Number : "); 9// second = Integer.parseInt(in.readLine());
10// System.out.print("Input the Multiple : "); 11// mul = Integer.parseInt(in.readLIne());
12// if(first >= second) { 13// temp = second; 14// second = first; 15// first = temp; 16// }
17// for(int i = 1; mul * i <= second; ++i) { 18// if(mul * i < first) continue;
19// sum += mul * i; 20// }
21// // for(int i = first; i <= second; ++i) { 22// // if(i % mul == 0) { 23// // sum += i; 24// // } 25// // }
26// System.out.println(); 27// System.out.println("The Sum of Multiple from " + first + " to " + second + " is " + sum); 28// } 29// }
첫
번째로 할일은 규칙을 찾는 일일 것이다. 얼마전 해봤던 두수의 크기 비교를 약간 응용한 문제였기 때문에, 그 부분까지는 문제가
되지 않았으나(16번째 라인까지), 문제는, 어떻게 하면 입력받은 두수안에서, 입력한 배수를 구하고, 그것의 합을 구하느냐였다.
오랜만에 수학적인 문제를 생각하려니, 머리가 회전되지 않는게 가장 큰 문제였으나, 가장 무식하게, 연습장에다가 써보면서 문제를 차근차근 풀어가기로 마음먹었다.
20 이후의 문제는, 17번째 라인의 for 문에서 20이 될때까지 반복 실행하라고 하였으니, 20 이후의 문제는 없지만, 10 이전의 문제는, 어떻게 하면 풀수 있을까?
그때 생각난 것이 continue 예약어였다. 만약 4의 배수가 10보다 작으면, 아무 수행도 하지 않고, 다음 반복할 작업으로 바로 넘어가도록 만드는 것인데, 거기에 continue 가 가장 제격이었다.
그렇게, 하고, 마지막에는, 배수들의 합을 sum 에 저장하는 것으로 마무리 했다.
이렇게 다 풀고 나서, 풀이를 보니....
이...이렇게 간단하게 풀줄이야..... -0- 여전히... 돌머리임에는.. 틀림없다....
2. 문자 하나를 입력받아 대문자인지 소문자인지 구별하는 프로그램을 작성.
import java.io.*;
public class Capital { public static void main(String[] args) throws IOException { char ch;
System.out.print("Input the One Character : "); ch = (char)System.in.read();
if(ch >= 'a' && ch <= 'z') { System.out.println(ch + " is a small letter."); } else if(ch >= 'A' && ch <= 'Z') { System.out.println(ch + " is a capital letter."); } else System.out.println(ch + " is not the Alphabet."); } }
이 문제는 굳이 설명할 필요가 있을까... 그래서 패스..
3. 연도를 입력받아 윤년인지 아닌지를 구별하는 프로그램.(윤년은 일반적으로 4년에 한번씩 돌아오는데, 그중 100으로 나누어 떨어지는 해는 윤년이 아니다. 그러나 100으로 나누어 떨어지는 해 중에서도 400으로 나누어 떨어지는 해는 윤년이다.)
1// import java.io.*;
2// public class LeapYear { 3// public static void main(String[] args) throws IOException { 4// BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
5// int ly = 0;
6// System.out.print("Input the year : "); 7// ly = Integer.parseInt(in.readLine());
8// if(ly % 4 == 0) { 9// if(ly % 100 == 0) { 10// if(ly % 400 == 0) { 11// System.out.println(ly + " is a leap year."); 12// } else { 13// System.out.println(ly + " is not a leap year."); 14// } 15// } else { 16// System.out.println(ly + " is a leap year."); 17// } 18// } else { 19// System.out.println(ly + " is not a leap year."); 20// } 21// } 22// }
if
~ else 문이라는 구문에 대해서 약간 헷걸렸던 문제다. 가끔가다, 수박을 보고, 수박이라는 단어가 문득 생소하다는 것을
느끼듯, 이 문제도 if ~ else 문에서, 잠시 사용법에서 약간의 혼돈이 와서.... 역시, 프로그래밍은 자주 사용해야
한다니까.... -0-
총 3단계의 검색을 거친다고 볼수가 있는데, 처음, 입력한 년도가 4로 나누어지는지를 검사한다. 만약, 나누어 지지 않는다면, 19번째 라인이 실행되어, 윤년이 아니라는 메세지가 출력된다.
100으로 나누어지면, 400으로 나누어지는 지를 마지막으로 검사한다. 만약, 400으로 나누어지면 윤년이라는 메시지를, 아니라면 윤년이 아니라는 메시지를 출력하게 되어있다.
문제는 예상외로 쉬우나, 나같이 돌머리를 소유한 자라면, 예상외로 힘들게 풀지도... 아무튼, if ~ else 문에 대해서 착각하지 않는다면, 쉽게 풀릴 문제.
4. 주민등록번호 8번째 자리를 입력받아 출생지를 알아보는 프로그램을 작성.(인터넷 검색을 이용하여 각 숫자가 나타내는 지역을 알아보자. 예를 들면 123456-7891234에서 8이라는 숫자가 적혀있는 자리는 출생지를 나타낸다. 그 숫자가 0이면 '서울' 이런 식인데 인터넷에서 검색하여 알아보자.)
import java.io.*;
public class ID8 { public static void main(String[] args) { int id8 = 0;
System.out.print("Input the 8th number of your ID : "); id8 = System.in.read() - 48;
switch(id8) { case 0 : System.out.println("You were born in Seoul."); break; case 1 : System.out.println("You were born in Kyoung Gi, In Cheon."); break; case 2 : System.out.println("You were born in Kang Won."); break; case 3 : System.out.println("You were born in Chung Book."); break; case 4 : System.out.println("You were born in Cung Nam, Dae Jeon."); break; case 5 : System.out.println("You were born in Jeon Book."); break; case 6 : System.out.println("You were born in Jeon Nam, Gwang Ju."); break; case 7 : System.out.println("You were born in Kyoung Book, Dae Gu."); break; case 8 : System.out.println("You were born in Kyoung Nam, Pusan."); break; case 9 : System.out.println("You were born in Jeju."); break;
default : System.out.println("Who are you...???"); } } }
설명...필요한가????
5. 구구단을 출력하는 프로그램을 작성해 보자.
import java.io.*;
public class Gugu { public static void main(String[] args) throws IOException { int gugu = 0;
System.out.print("Input the Multiplication number : "); gugu = System.in.read() - 48;
for(int i = 1; i <= 9; ++i) { System.out.println(gugu + " * " + i + " = " + (gugu * i); } } }
이것역시..설명이 필요한가???
6. 앞서 배운 계산기 프로그램을 만들어서 한번 계산을 한 후 다시 게산을 할 것인지 물어 보고 수행을 계속할 것인지를 결정하는 프로그램을 만들어 보자.
import java.io.*;
public class Calc { public static void main(String[] args) throws IOException { BufferedReader in = new BufferdReader(new InputStreamReader(System.in));
int x = 0, y = 0; char op = 0, yn = 0;
while(true) { // 첫번째 숫자를 입력 받아서 변수 x에 저장 System.out.print("Input the First Number : "); x = Integer.parseInt(in.readLIne());
// 연산자를 입력하는 부분. 출력된 +, -, *, /, % 외에 문자를 입력하면, 다시 입력할수 있도록 // 반복실행 시켜준다. do{ System.out.print("Pick the Operators(+, -, *, /, %) : "); op = (char)System.in.read(); System.in.read(); System.in.read(); } while(op != '+' && op != '-' && op != '*' && op != '/' && op != '%');
// 두번째 숫자를 입력받아 변수 y에 저장 System.out.print("Input the Second Number : "); y = Integer.parseInt(in.readLine());
// 입력받은 연산자에 따라, 해당 위치로, 이동, 출력. switch(op) { case '+' : System.out.println(x + " " + op + " " + y + " = " + (x+y)); break; case '-' : System.out.println(x + " " + op + " " + y + " = " + (x-y)); break; case '*' : System.out.println(x + " " + op + " " + y + " = " + (x*y)); break; case '/' : System.out.println(x + " " + op + " " + y + " = " + (x/y)); break; case '%' : System.out.println(x + " " + op + " " + y + " = " + (x%y)); break; }
// 다시 한번 계산을 할것인지를 물음. System.out.print("One more? "); yn = (char)System.in.read(); System.in.read(); System.in.read();
// 위의 질문에서 n이나 N 을 입력시 while 문을 탈출. if(yn == 'N' || yn == 'n') break; } } }
설명은 주석문으로 대체.
7. 연도와 월을 입력받아 달력을 출력하는 프로그램을 작성(윤년도 확인해야 하고 요일도 나타나야 한다.) 참고 : 배열 없이 프로그램 작성
import java.io.*;
class Cal { public static void main(String[] args) throws IOException { BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
// yr : 입력 받을 년도를 저장, mth = 입력 받을 월을 저장, // mth_day = 입력 받은 월에 해당하는 일수를 저장, day : 출력하게 될 날짜들을 저장, // date : 입력받은 월의 전월까지의 총 일수를 저장 // ch_mon : 입력 받은 월에 해당되는 영어를 저장, // blank : 달력 출력시, 시작되는 날짜와 요일에 따른 선행 빈 공간 출력
// 년도 입력, 저장 System.out.print("Input the year by 4 units(EX : 2004) => "); yr = Integer.parseInt(in.readLine());
// 월을 입력, 저장 // 입력받은 월에 해당되는 영어를 변수에 저장하며, // 입력받은 월에 해당되는 일수를 저장한다. // 또한 전월까지의 총 일수를 저장하며, // 이 모든 과정을 평년과 윤년에 따라 수행되도록 한다. do { System.out.print("Input the month(EX : 6) =? "); mth = Integer.parseInt(in.readLine());