몇가지 표준 함수들

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()
어떤 값들의 집합에서 가장 큰 값 또는 가장 작은 값을 알아내야 하는 경우 사용하는 함수


:
1. 단일 인수
하나의 매개변수를 받는 함수

< ch501.lua 코드 >



--> 하나의 매개변수를 받는 함수 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이 나온 것처럼 말이다. 그리고 이후에 하나를 뒤에 더 추가할 수가 있다.

6) type
  (1) 변수의 저장된 값이 타입을 알려주는 함수


-> 타입의 사용법은 이런식이다.

:
FormComp.java

import org.kwis.msp.lcdui.Jlet;
import org.kwis.msp.lcdui.JletStateChangeException;
import org.kwis.msp.lwc.FormComponent;
import org.kwis.msp.lwc.LabelComponent;
import org.kwis.msp.lwc.ShellComponent;

public class FormComp extends Jlet {
    protected void startApp(String[] arg0) {

        ShellComponent shell = new ShellComponent();
        FormComponent form = new FormComponent();  

        form.addComponent(new LabelComponent("위피 프로젝트"));
        form.addComponent(new LabelComponent("라벨컴포넌트"));  
        shell.addComponent(form);
        shell.show();
    }
   
    protected void pauseApp(){}

    protected void resumeApp(){}

    protected void destroyApp(boolean arg0) throws JletStateChangeException {}
}

:
ShellCompo.java

import org.kwis.msp.lcdui.*;
import org.kwis.msp.lwc.*;

public class ShellCompo extends Jlet {   
    protected void startApp(String args[]) {
        ShellComponent test = new ShellComponent();
   
        test.setTitle(new LabelComponent("제목 컴포넌트"));
        test.addComponent(new ButtonComponent("작업 컴포넌트", null));
        test.setCommand(new ButtonComponent("커맨드 컴포넌트", null), false);
       
        test.show();
    }   
    protected void pauseApp(){}
   
    protected void resumeApp(){}
   
    protected void destroyApp(boolean b){}
}
:
HelloWipi.java

import org.kwis.msp.lcdui.*;

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);
    }
}

결과화면
사용자 삽입 이미지

:
1. 메소드의 정의 및 용법
: 동일한 내용의 코드가 여러차례 반복하여 쓰여질 경우, 그러한 코드를 하나의 이름으로 묶어서 관리할수 있도록 해주는 그룹이라고 정의할수 있겠다. 다음의 예제를 보자.
1//    public class Exam07_01 {
2//        public static void main(String[] args) {
3//           System.out.println("*****");
4//           System.out.println("연습삼아 쳐봤어요.");
5//           System.out.println("*****");
6//           System.out.println("*****");
7//           System.out.println("그만 할께요");
8//           System.out.println("*****");
9//        }
10//    }
위에서 3, 5, 6, 8번째 줄은 반복되는 코드들이다. 이러한 코드들이 간단한 줄일 경우에는 문제가 되지는 않으나, 만약 코드가 10줄, 100줄이 넘어갈 경우에는, 이러한 코드들을 일일이 다 쳐줄수는 없는 일이다.

그렇기 때문에 동일한 소스코드를 하나의 그룹으로 묶어서, 필요할 때마다 그 그룹을 불러오게 되는데, 그 그룹을 바로 메소드라고 할수가 있다.

메소드는 다음과 같은 형식으로 작성을 한다.
접근제한자 지정예약어 결과형리턴값 메소드명 (매개변수들) throws 예외클래스들 {
    내용정의부;
}
이렇게 풀어적어서 적어 놓으니까, 뭐라고 하는지 하나도 모르겠다. 다음과 같은 형식으로 바꿔서 설명하겠다.
1//    public static void view_star() {
2//        System.out.println('*****");
3//    }
1번째 라인에 있는 구문중 public 이라고 적혀 있다. 이것이 바로 접근 제한자라고 부르는데, 여기에는 private, protected, public, package 라는 제한자가 올수가 있다.

그다음에 오는 static 이라는 항목이 지정예약어라고 불리우는 예약어인데, 여기에는 static, final, abstract, synchronized 등이 온다.

이제 다음에 나오는 것이 void 인데, 이게 바로 결과형 리턴값이며, void 는 일반적으로 아무것도 돌려줄 값이 없는 경우에 사용되는 값인데, 메소드에서 어떠한 동작을 수핸한 후에, 되돌려줄 값이 있을 경우, 그 값의 형태를 알려주는 부분이다.

view_star 는 당연히 메소드 명이며, 지금 1번째 라인의 () 안에는 아무것도 없지만, 매개변수들이 들어가는 경우가 생길수가 있다.

그리고 마지막 throws 예외클래스들은 사용이 할때도 있고 안할때도 있으니, 그건 나중에 설명하기로 한다.

그럼 위의 첫번째 예제를 메소드를 사용하여 고쳐 보도록 하자.
1//    public class Exam07_01 {
2//        public static void view_star() {
3//            System.out.println("*****");
4//        }

5//        public static void main(String[] args) {
6//            view_star();
7//            System.out.println("연습삼아 쳐봤어요.");
8//            view_star();
9//            view_star();
10//           System.out.println("그만 할께요");
11//           view_star();
12//        }
13//    }
여기서 중요할 점은, 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//        }    // 행렬 형식의 출력 메소드

11//        public static void main(String[] ar) {
12//           view_star(2, 5);
13//           System.out.println("출력하자");
14//           view_star(3, 3);
15//           view_star(2, 10);
16//           System.out.println("출력된다");
17//           System.out.println("출력되었다");
18//           view_star(4, 7);
19//        }
20//    }
이렇게 매겨변수를 받아, 그 매개변수로 동적으로 실행시킬때 사용할수 있는 방식이 바로 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. 메서드도 상위 클래스의 메서드만 사용할 수 있는데 오버라이딩이 되었다면 하위 클래스의 메서드가 실행된다.

다형성을 사용하는 이유는??? 확실하지는 않지만...



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. 추상 메서드를 포함하고 있기 때문에 객체를 발생시킬수도 없다.


이상, 다시 공부해 나갈때 다시 정리하겠음... -0-
:
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번째 라인까지), 문제는, 어떻게 하면 입력받은 두수안에서, 입력한 배수를 구하고, 그것의 합을 구하느냐였다.

오랜만에 수학적인 문제를 생각하려니, 머리가 회전되지 않는게 가장 큰 문제였으나, 가장 무식하게, 연습장에다가 써보면서 문제를 차근차근 풀어가기로 마음먹었다.

예를 들어, 10부터 20까지의 수중, 4의 배수를 구하는 문제를 대입해 보았다.

4의 배수를 처음부터, second 에 입력될 20까지 차례 대로 나열하면 다음과 같다.
4 * 1 = 4
4 * 2 = 8
4 * 3 = 12
4 * 4 = 16
4 * 5 = 20
4 * 6 = 24
이에 관한 for 문이 바로 17번째 라인에 있는 for문이다.

이제 문제는 여기서, 어떻게 하면, 범위내의 배수를 빼내느냐, 즉, 10부터 20이라는 숫자 사이의 있는 4의 배수를 어떻게 빼내오느냐의 문제가 발생한다.
4 * 1 = 4 (X)
4 * 2 = 8 (X)
4 * 3 = 12 (O)
4 * 4 = 16 (O)
4 * 5 = 20 (O)
4 * 6 = 24 (X)
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번째 라인이 실행되어, 윤년이 아니라는 메세지가 출력된다.

4로 나누어지는 지를 통과하면, 100으로 나누어지는 지를 확인한다. 만약, 나누어지지 않는다면, 16번째 라인이 실행, 윤년이라는 메시지가 출력된다.

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;

       System.out.println();
       System.out.println("========= " + gugu + " =========");

       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 : 달력 출력시, 시작되는 날짜와 요일에 따른 선행 빈 공간 출력

        int yr = 0, mth = 0, mth_day = 0, day = 0, date = 0;
        String ch_mon = "";
        String blank1 = "", blank2 = "\t", blank3 = "\t\t", blank4 = "\t\t\t", blank5 = "\t\t\t\t", blank6 = "\t\t\t\t\t", blank7 = "\t\t\t\t\t\t";

        // 년도 입력, 저장
        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());

            switch(mth) {
                case 1 : ch_mon = "January"; mth_day = 31; break;
                case 2 : ch_mon = "February";
                    if(yr % 4 == 0 && yr % 100 != 0 || yr % 400 == 0) {
                        mth_day = 29;            // 윤년
                    } else {
                        mth_day = 28;            // 평년
                    }
                    date = 31; break;           
                    // 전월까지의 일수를 합해서 저장. 1월까지의 일수를 저장
                case 3 : ch_mon = "March"; mth_day = 31;
                    if(yr % 4 == 0 && yr % 100 != 0 || yr % 400 == 0) {
                        date = 31 + 29; break;   
                // 윤년시, 전월까지의 일수를 합해서 저장, 1월과 2월까지의 일수를 합산, 저장.
                    } else {
                        date = 31 + 28; break;   
                // 평년시, 전월까지의 일수를 합해서 저장, 1월과 2월까지의 일수를 합산, 저장.
                    }
                case 4 : ch_mon = "April"; mth_day = 30;
                    if(yr % 4 == 0 && yr % 100 != 0 || yr % 400 == 0) {
                        date = 31 + 29 + 31; break;
                    } else {
                        date = 31 + 28 + 31; break;
                    }
                case 5 : ch_mon = "May"; mth_day = 31;
                    if(yr % 4 == 0 && yr % 100 != 0 || yr % 400 == 0) {
                        date = 31 + 29 + 31 + 30; break;
                    } else {
                        date = 31 + 28 + 31 + 30; break;
                    }
                case 6 : ch_mon = "June"; mth_day = 30;
                    if(yr % 4 == 0 && yr % 100 != 0 || yr % 400 == 0) {
                        date = 31 + 29 + 31 + 30 + 31; break;
                    } else {
                        date = 31 + 28 + 31 + 30 + 31; break;
                    }
                case 7 : ch_mon = "July"; mth_day = 31;
                    if(yr % 4 == 0 && yr % 100 != 0 || yr % 400 == 0) {
                        date = 31 + 29 + 31 + 30 + 31 + 30; break;
                    } else {
                        date = 31 + 28 + 31 + 30 + 31 + 30; break;
                    }
                case 8 : ch_mon = "August"; mth_day = 31;
                    if(yr % 4 == 0 && yr % 100 != 0 || yr % 400 == 0) {
                        date = 31 + 29 + 31 + 30 + 31 + 30 + 31; break;
                    } else {
                        date = 31 + 28 + 31 + 30 + 31 + 30 + 31; break;
                    }
                case 9 : ch_mon = "September"; mth_day = 30;
                    if(yr % 4 == 0 && yr % 100 != 0 || yr % 400 == 0) {
                        date = 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31; break;
                    } else {
                        date = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31; break;
                    }
                case 10 : ch_mon = "October"; mth_day = 31;
                    if(yr % 4 == 0 && yr % 100 != 0 || yr % 400 == 0) {
                        date = 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30; break;
                    } else {
                        date = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30; break;
                    }
                case 11 : ch_mon = "November"; mth_day = 30;
                    if(yr % 4 == 0 && yr % 100 != 0 || yr % 400 == 0) {
                        date = 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31; break;
                    } else {
                        date = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31; break;
                    }
                case 12 : ch_mon = "December"; mth_day = 31;
                    if(yr % 4 == 0 && yr % 100 != 0 || yr % 400 == 0) {
                        date = 31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30; break;
                    } else {
                        date = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30; break;
                    }

                default : System.out.println("Please Input Again.");
            }


        }while (mth != 1 && mth != 2 && mth != 3 && mth != 4 && mth != 5 && mth != 6 && mth != 7 && mth != 8 && mth != 9 && mth != 10 && mth != 11 && mth != 12);

        System.out.println(" ============== " + "<< " + yr + ", " + ch_mon + " >>" + " ============== ");
        System.out.println();
        System.out.println(" Sun\tMon\tTue\tWed\tThu\tFri\tSat ");

// 출력시, 시작일수와 요일에 따른 빈 공간을 출력하기 위한 부분, 하지만 이해안되는 식
// yr-1은 무얼까??

        int extra = ((yr-1) + (yr-1)/4 - (yr-1)/100 + (yr-1)/400 + date + 1 ) % 7;

        switch(extra) {
            case 0 : System.out.print(blank1); break;
            case 1 : System.out.print(blank2); break;
            case 2 : System.out.print(blank3); break;
            case 3 : System.out.print(blank4); break;
            case 4 : System.out.print(blank5); break;
            case 5 : System.out.print(blank6); break;
            case 6 : System.out.print(blank7); break;
        }

        for(int i = 1; i <= mth_day; i++) {
            day = i;

//    이해안되는 식
//    (yr-1)/4 - (yr-1)/100 + (yr-1)/400 : 윤년횟수
//    date : 전월까지의 일수를 총 합산한 일수
//    day : 해당 날짜, 1일, 2일, 3일, ....
//    yr-1 은 무얼까... -0-

            int total = ((yr-1) + (yr-1)/4 - (yr-1)/100 + (yr-1)/400 + date + day ) % 7;

//    6보다 작으면 그대로 출력, 그렇지 않으면, 한줄 내려 출력
            if(total % 7 < 6) {
// 10보다 작으면, 한자릿수라는 이야기 이므로, 한자릿수 앞에 0을 붙여 출력, 그렇지 않으면,
// 두자릿수라는 이야기로 0을 제외한 채 출력
                if(day < 10) {
                    System.out.print(" 0" + day + "\t");
                } else {
                    System.out.print(" " + day + "\t");
                }
            } else {
                if(day < 10) {
                    System.out.println(" 0" + day + "\t");
                } else {
                    System.out.println(" " + day + "\t");
                }
            }
        }
    }
}
솔직히 풀다가 막힌 부분이 생겨, 다른 소스를 보고 약간의 편집을 통해 완성시킨 소스.

그래서 이해가 안가는 부분이 몇가지가 있었는데, total 이라는 변수와, 달력을 출력시 틀에 맞춰 출력시키기 위한 코딩부분에서 이해가 안가는 부분이 몇가지 있었다.

특히 위의 yr-1 부분은 여전히 이해가 안가는 부분이다.

이 문제는 일단, 여기서 종료.
:

BLOG main image
아무거나 공부하자!!! by Young79

공지사항

카테고리

분류 전체보기 (79)
Programing (29)
English (31)
Graphic (4)
Saying on T"We"tter (15)

최근에 올라온 글

최근에 달린 댓글

글 보관함

달력

«   2025/07   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Total :
Today : Yesterday :