- request.getHeader("REFERER");
2.현재페이지 URL 알아내기
- request.getRequestURI()
3.ip알아내기
- request.getRemoteAddr()
(세부출처 : http://www.dbtool.co.kr/) Java Error Message 정리 자료입니다. 1. ERROR Exception in thread "main" java.lang.NoClassDefFoundError: 파일명 |
객체지향 개념을이해하기 위해 기본적으로 객체에대해 알아야합니다. 지금 여러분 주위를 한 번 둘러보세요. 거기에는 실세계라는 것이 있고, 그 실세계에 존재하는 여러 객체들이 보일것입니다. 화장실 다녀오는 김대리, 여러분이 지금 사용하고 있는 컴퓨터, 또는 밖에서 경적을 울리며 지나가는 자동차 등을 모두 객체라고 할 수 있습니다. 이러한 실세계에 존재하는 객체는 다음과 같은 두 가지 구성요소를 갖습니다.
예를 들어, 실생활에 존재하는 자동차 객체는 색, 네 개의 바퀴, 핸들, 배기량, 현재 속도, 현재 기어 위치 등등의 상태를 갖고, 달린다, 멈춘다, 기어를 바꾼다, 속도를 높이거나 낮춘다, 경적을 울린다 등과 같은 행동을 할 수 있습니다.
이러한 실세계의 객체를 소프트웨어적으로 표현하기 위한 방법 중의 하나가 객체지향 방법입니다. 따라서, 소프트웨어 객체는 실세계의 객체가 갖는 구성요소를 모두 표현할 수 있어야 합니다. 이를 위해 객체지향 방법을 이용하여 실세계의 객체가 갖는 상태(state)와 행동(behavior)을 소프트웨어 객체의 변수(variable)와 메소드(method 또는 function)로 모델링하게 됩니다. 자세히 말하자면, 소프트웨어 객체는 실세계의 객체가 갖는 특성이나 상태를 나타내기 위해 변수를 이용하고, 이러한 특성이나 상태를 변경시키는 행동을 표현하기 위해 변수의 값을 변경하거나 다른 객체로부터 온 요청에 대한 서비스를 수행하는 메소드 즉 함수를 구현하는 것입니다. 그리고, 이 상태를 나타내는 변수들과 상태를 변경해 주는 행동을 구현한 메소드(또는 함수)를 하나로 묶어줌으로써 실세계의 객체를 소프트웨어 객체로 모델링하고 구현할 수 있습니다. 이러한 객체지향 방법에서 나타나는 몇 가지 특징들을 살펴보면 다음과 같습니다.
<그림1. 클래스 계층도>
<그림 2. 메소드 다중 정의>
자바 언어는 객체지향 프로그래밍을 할 수 있도록 문법을 제공해 주고 있습니다. 객체지향 프로그래밍이란 기본적으로 클래스를 정의할 수 있고, 객체 또는 인스턴스를 생성할 수 있어야 합니다. 자바 언어를 이용하여 클래스를 정의할 때 다음과 같이 할 수 있습니다.
class 클래스이름 {
// 변수 선언부
…
// 메소드 선언부
…
}
객체가 갖는 상태를 변수로써 정의하고, 행동을 메소드로써 정의할 수 있고, 이를 하나의 묶음으로 캡슐화 하기 위해 “class { … }”와 같이 하면 됩니다. 따라서, 자바에서는 위와 같이 클래스를 정의할 수 있습니다.
이렇게 정의된 클래스를 실제로 인스턴스로 생성할 수 있고, 이렇게 생성된 인스턴스를 객체라고 할 수 있다고 했습니다. 클래스에 대한 객체 또는 인스턴스를 생성하기 위해서는 먼저 클래스에 대한 객체 또는 인스턴스를 선언해야 하며, 객체 또는 인스턴스를 선언하는 방법은 의외로 간단합니다. 기본 자료형에 대한 변수를 선언하는 것과 같습니다. 다시 말해서 클래스형에 대한 변수를 선언한다고 생각할 수 있습니다. C++에서와 마찬가지로 자바에서 클래스에 대한 인스턴스 또는 객체를 선언하고 생성하는 방법은 다음과 같습니다.
클래스이름 클래스인스턴스이름 = new 클래스이름();
또는
클래스이름 클래스인스턴스이름;
클래스인스턴스이름 = new 클래스이름();
자바에서 클래스에 대한 객체(또는 인스턴스)를 선언하고 생성하기 위해 클래스에 대한 변수를 선언함과 동시에 생성하는 방법과, 클래스에 대한 변수를 선언하고 나중에 필요할 때 객체 (또는 인스턴스)를 생성하는 방법 등 두 가지 방법을 모두 제공해 주고 있습니다. 자바에서는 이러한 클래스에 대한 변수를 클래스 참조형 변수라 하고, 클래스 참조형 변수는 참조형이므로 반드시 new 연산자를 이용하여 메모리 공간을 할당해 주어야 합니다.
다음은 자바 언어를 이용하여 클래스를 정의하고, 이 클래스에 대한 객체를 선언하고 생성하는 예를 보여주는 자바 프로그램입니다.
class Point { |
<프로그램1.ClassTest.java>
지금까지 클래스를 정의하고 객체를 선언 및 생성하는 방법에 대하여 살펴보았습니다. 객체지향 개념에서는 객체가 갖는 상태 정보에 대해 접근 및 변경할 수 있는 메소드를 제공해 줌으로써 객체가 갖는 상태 정보에 대한 잘못된 접근 또는 변경을 막을 수 있었지요. 또한, 상태 정보를 나타내는 변수 또는 행동이나 접근 및 변경을 위한 메소드에 대한 접근 권한을 지정해 줌으로써 객체가 갖는 데이터에 대한 정보은닉을 가능하도록 했습니다. 자바에서는 객체가 갖는 변수 또는 메소드에 대한 접근 권한을 지정하도록 하기 위해 다음과 같은 네 가지 종류의 접근지정자를 제공해 주고 있습니다.
접근지정자 변수 선언;
접근지정자 메소드 선언;
private int x, y;
public void setX() { … }
public int getX() { … }
다음에 나오는 예제는 접근지정자를 사용하고 있는 자바 프로그램입니다. 접근지정자 private로 선언된 변수 또는 메소드는 같은 클래스 내에서만 접근할 수 있습니다. 따라서, 다음에 나오는 자바 프로그램에서와 같이 Point 클래스가 아닌 곳에서는 사용할 수 없고, 단지 Point 클래스에서 제공해 주는 접근가능한 메소드를 사용하여 값을 참조해야 합니다.
class Point { |
<프로그램2.ClassTest.java>
C++와 같은 객체지향 언어에서 다형성을 제공해 주기 위한 방법 중의 하나가 다중 정의입니다. 다중 정의란 같은 이름으로 여러 가지 일을 하는 것은 말합니다. C++에서는 함수 다중 정의와 연산자 다중 정의 등 두 가지 다중 정의를 제공해 줍니다. 함수 다중 정의는 같은 이름의 함수가 여러 개 존재하는 것이고, 연산자 다중 정의란 같은 이름의 연사자가 클래스에 따라 다른 연산을 수행할 수 있도록 하는 것입니다. 이러한 다중 정의에 대해 자바에서는 연산자 다중 정의는 제공해주지 않고, 단지 함수 다중 정의 즉 메소드 다중 정의만을 제공해 주고 있습니다. 그렇다면 메소드의 이름이 같다면 어떤 것을 이용하여 같은 이름의 여러 개의 메소드를 구별할 수 있을까요? C++에서와 마찬가지로 자바에서도 같은 이름의 여러 개의 메소드를 구별하기 위하여 메소드가 갖는 매개변수의 개수와 자료형을 이용하여 구분합니다. 예를 들면, 다음과 같은 메소드는 메소드 다중 정의에 의해 서로 구별가능 합니다.
void f(char n) { … }
void f(int n) { … }
void f(int i, int j) { … }
void f(long n) { … }
void f(float n) { … }
이렇게 메소드 다중 정의에 의해 선언된 메소드는 그 메소드를 호출할 때의 매개변수의 개수 또는 매개변수의 자료형에 따라 동적으로 호출이 이루어집니다. 따라서, 메소드를 호출할 때 호출되는 메소드는 실매개변수의 자료형과 가장 가까운 형식매개변수를 갖는 메소드가 호출됩니다. 다음은 메소드 다중 정의에 대한 예를 보여주기 위해 작성한 자바 프로그램입니다.
class MethodOverloadingTest { |
<프로그램 3.MethodOverloadingTest.java>
위의 자바 프로그램에서 char 자료형 매개변수를 갖는 메소드는 주석 처리되었지만, 위에서는 메소드 호출이 성공적으로 이루어진 것을 볼 수 있습니다. 이런 경우가 가능한 것은, 앞에서도 살펴보았듯이 암시적 형변환이 이루어지기 때문에 가능합니다. 다시 말해서, char 자료형 매개변수를 갖는 메소드를 찾고, 이러한 메소드가 없을 경우 char 자료형에 대해 내부적으로 형변환이 가능한 int 자료형 매개변수를 갖는 메소드를 찾게 되고, 이 int 자료형 매개변수를 갖는 메소드가 존재하므로, 이 메소드를 호출하게 되는 것입니다.
자바 언어를 이용하여 객체를 생성하고 필요에 따라 값을 초기화 해 주어야 하는 경우가 있습니다.
Point p1 = new Point();
p1.setX(5); p1.setY(10);
위의 예에서는 Point 클래스 객체 p1을 선언하고, “new Point()”를 통해서 객체를 생성하였습니다. 그리고, 마지막으로 p1 객체가 같은 상태를 저장하기 위한 x와 y 변수에 각각 “p1.setX(5)”와 “p1.setY(10)” 메소드를 이용하여 초기화하고 있습니다. 이러한 작업을 좀 더 간단하게 할 수는 없을까요? 물론, C++를 접해 본 사용자라면 생성자(constructor)라는 것이 있잖아요”라고 생각하겠지요.
자바 프로그램을 처음 시작하거나 자바를 이용하여 많은 프로그램을 작성해 본 프로그래머들이라도 쉽게 지나칠 수 있는 것이 객체 생성자입니다. 이러한 객체 생성자는 new 연산자를 이용하여 객체를 생성할 때 호출됩니다. 자바에서도 C++와 마찬가지로 객체 생성자를 제공해 주고 있고, 이러한 객체 생성자의 특징을 살펴보면 다음과 같습니다.
클래스이름(형식매개변수 리스트) { … }
클래스이름(형식매개변수 리스트) {
다른 객체 생성자 호출; ß 반드시 첫번째 줄에서
이루어져야 함.
…
}
클래스이름 변수이름 = new 클래스이름(실매개변수 리스트);
new 연산자를 이용하여 객체를 생성할 때, 먼저 객체를 위한 메모리 공간을 할당하여 이를 디폴트 값으로 초기화 하며, 객체 생성자의 첫 번째 문장이 다른 생성자를 호출할 경우 이를 먼저 처리하고, 다음으로 객체가 갖는 인스턴스 변수의 초기화 수식, 초기화 블록, 생성자의 몸체를 순서대로 수행합니다. 다음에 나오는 예제 프로그램은 이러한 과정을 보여주기 위한 자바 프로그램입니다.
class Point { |
<프로그램 4. ConstructorTest.java>
이 때, 자바 프로그램에서 Circle 클래스를 정의하고 Circle 클래스를 위한 아무런 객체 생성자도 정의해 주지 않았다면, 자바 컴파일러에 의해 자동으로 삽입되는 Circle 클래스의 객체 생성자는 다음과 같습니다.
public Circle() { } |
<그림 3. 객체 생성자가 없을 경우 자동으로 삽입되는 객체 생성자>
자바에서는 일반적으로 객체의 메소드 내에서 객체가 가지고 있는 변수 또는 메소드를 직접 참조할 수 있습니다. 그런데, 자바에서 클래스를 정의하고 객체를 생성할 때, this라는 키워드를 자주 사용하게 됩니다. 그렇다면 this는 어떤 역할을 하도록 자바에서 제공해 주는 걸까요? this는 다음과 같은 몇 가지 중요한 특징을 갖습니다.
this
this.멤버변수
this(매개변수);
앞에서 살펴보았던 Point 클래스에 비해 훨씬 간결하고 보기 좋은 프로그램이라는 느낌이 들 것입니다. 다음은 this를 사용하여 자바 프로그램을 좀 더 간결하게 하는 예제 프로그램입니다.
class Point { |
<프로그램 5.ThisTest.java>
다음은 this를 사용하여, 자신의 다른 객체 생성자를 호출하였을 경우, 그 생성 과정은 어떻게 되는 지를 보여주기 위한 예입니다.
class Point { |
<프로그램 6.ThisTest2.java>
클래스 내에 정의되어 있는 멤버는 변수와 메소드가 있고, 이는 다시 인스턴스 변수와 인스턴스 메소드 및 클래스 변수와 클래스 메소드로 나뉩니다. 일반적으로 클래스 내에 선언된 변수와 메소드는 대부분이 인스턴스 변수와 인스턴스 메소드입니다. 이는 클래스에 대해 인스턴스를 생성할 때, 각 인스턴스가 독립적으로 이를 위한 메모리 공간을 확보하기 때문입니다. 이와는 달리, 클래스 변수와 클래스 메소드는 클래스의 모든 객체(또는 인스턴스)가 공유하는 전역 변수와 전역 메소드를 말합니다. 클래스 변수 또는 메소드는 기존의 인스턴스 변수와 인스턴스 메소드의 앞에 ‘static’이라는 키워드를 명시해 주면 됩니다. 이러한 이유 때문에 클래스 변수와 클래스 메소드를 각각 static 변수와 static 메소드라고도 합니다.
인스턴스 변수는 각 인스턴스(또는 객체)가 따로 갖는 변수이고 클래스 변수는 메소드 영역에 따로 잡혀 해당 클래스에 대한 모든 인스턴스들이 공유하는 변수입니다. 따라서, 클래스 변수는 객체의 생성과 관련이 없으며, 클래스가 메모리에 로드 될 때 단 한번 클래스 변수를 위한 메모리가 할당되고 초기화됩니다. 따라서, 객체를 생성하지 않더라도 클래스 변수에 대한 접근이 가능합니다.
클래스 변수와 클래스 메소드의 선언)
[접근권한] static 변수 선언;
[접근권한] static 메소드 선언;
클래스 변수와 클래스 메소드의 접근)
클래스이름.클래스메소드()
클래스메소드()
객체참조값.클래스메소드()
인스턴스 메소드는 각 객체에 속하는 메소드이므로 인스턴스 메소드를 호출할 때는 해당 객체에 대한 참조값이 필요하지만, 클래스 메소드는 객체에 속한 것이 아니므로 객체에 대한 참조값이 필요가 없습니다. 또한, 인스턴스 변수 내에서는 자신에 대한 참조값 또는 상위 클래스에 대한 참조값을 각각 나타내는 this와 super와 같은 객체 참조값을 사용할 수 있고, 따라서 변수와 메소드의 사용에 제약이 없으나, 클래스 메소드 내에서는 해당 객체에 대한 참조값을 갖지 않으므로 인스턴스 변수 또는 인스턴스 메소드를 사용할 때는 해당 객체 참조값을 명시해 주어야 하며, 그렇지 않을 경우 클래스 메소드 내에서는 인스턴스 변수 또는 인스턴스 메소드를 참조할 수 없습니다. 다음은 클래스 변수와 클래스 메소드를 사용하는 자바 예제 프로그램입니다.
class Point { |
<프로그램 7.ClassVarMethodTest.java>
다음에 나오는 예제는 클래스 메소드에서 인스턴스 변수에 접근하는 잘못된 경우를 보여 주고 있는 예입니다.
class Point { |
<프로그램 8.ClassVarMethodTest2.java>
자바에서 클래스를 처음으로 메모리에 적재(loading)하고, 클래스에 대한 객체(또는 인스턴스)를 생성할 때 자바에서는 내부적으로 초기화 작업을 수행합니다. 클래스는 클래스 변수 및 클래스 메소드 등과 같은 클래스 멤버와 인스턴스 변수 및 인스턴스 메소드 등과 같은 인스턴스 멤버로 구성되어 있습니다. 이 때, 클래스 메소드와 인스턴스 메소드를 메모리에 적재하고, 클래스 변수와 인스턴스 변수에 대한 메모리를 할당하고 초기화 해야 하는데, 자바에서 언제 어떻게 이러한 작업을 수행하는 지에 대하여 살펴보도록 하겠습니다.
자바에서는 자바 가상머신에 의해 해당 클래스가 메모리에 적재될 때 클래스 초기화를 무엇보다도 먼저 수행합니다. 이 때 수행되는 클래스 초기화는 다음과 같이 이루어집니다.
다음과 같이 클래스 변수의 초기화 수식만으로 클래스 변수 초기화할 수 없을 경우 클래스 초기화 블록을 사용할 수 있습니다.
클래스 초기화는 객체의 생성과는 무관하게 클래스가 메모리에 적재될 때 이루어지는 것과는 달리, new 연산자에 의해 객체가 생성될 때마다 수행되는 작업이 있는데 다음과 같습니다.
클래스 변수의 초기화 및 초기화 블록
static 변수선언=초기값;
또는
static 배열형변수선언=new 배열형;
static {
여러 개의 변수들의 초기화
또는
배열의 초기화;
}
클래스 변수의 초기화 및 초기화 블록의 예
static int countPoint=0;
또는
static int monthDays[] = new int[10];
static {
monthDays[ 0] = 31; monthDays[ 1] = 28;
…
monthDays[10] = 30; monthDays[11] = 31;
}
단, 인스턴스 변수의 초기화 및 초기화 블록은 위에서 static 를 제거하면 됩니다.
new 연산자를 이용하여 객체를 생성할 때, 먼저 객체를 위한 메모리 공간을 할당하여 이를 디폴트 값으로 초기화 하며, 객체 생성자의 첫 번째 문장이 다른 생성자를 호출할 경우 이를 먼저 처리하고, 다음으로 객체가 갖는 인스턴스 변수의 초기화 수식, 초기화 블록, 생성자의 몸체를 순서대로 수행합니다. 다음에 나오는 예제 프로그램은 그러한 과정을 잘 보여주는 자바 프로그램입니다.
class Point { |
<프로그램 9.ConstructionTest2.java>
또한, 객체의 생성과는 무관하게 클래스를 메모리에 적재하면서 클래스 변수의 초기화 클래스 초기화 블록의 실행 등의 초기화를 수행하고, 객체를 생성할 때 인스턴스 변수의 초기화 및 초기화 블록의 실행 등을 하고 객체 생성자를 실행합니다. 다음에 나오는 예제 프로그램은 그러한 과정을 잘 보여주는 자바 프로그램입니다.
class A { |
<프로그램 10.ClassInitTest.java>
객체지향 개념은 클래스를 이용하여 새로운 클래스를 생성 또는 정의할 수 있도록 하고 있습니다. 예를 들어, 자동차가 갖는 일반적인 상태와 행동들을 자동차 클래스로 정의해 놓고, 이 자동차 클래스를 확장하여 버스만이 갖는 상태와 행동을 추가하여 버스 클래스를 정의하고, 트럭이 갖는 상태와 행동을 추가하여 트럭 클래스를 정의하고, 그리고 자가용이 갖는 상태와 행동들을 추가하여 자가용 클래스를 정의 할 수 있겠지요. 이 때, 자동차 클래스를 상위클래스(superclass)라 하고 버스 클래스, 트럭 클래스, 자가용 클래스 등을 하위클래스(subclass)라 하며, 이들 간의 관계에 대해 얘기할 때 “하위클래스는 상위클래스를 상속한다(inherit)”라고 합니다. 다시 말해서, 하위클래스는 상위클래스가 갖고 있는 모든 특성들을 상속하여 사용할 수 있다는 것입니다. 이러한 상속 관계를 트리로 나타낼 수 있고, 이 상속관계 트리를 클래스 계층도(class hierarchy)라 합니다.
자바에서의 모든 클래스들은 반드시 어떤 클래스로부터 파생되어야 하는데, 클래스 계층 구조의 최상위 클래스는 바로 java.lang이라는 패키지에 있는 Object라는 클래스입니다. 따라서, 자바에서 정의된 모든 클래스는 기본적으로 Object클래스로부터 파생된 클래스가 되며, 자바 개발자가 만든 클래스가 그 어떤 클래스도 상속하지 않도록 정의하였다면, 자바는 내부적으로 이 사용자 클래스가 Object 클래스를 상속하도록 코드를 추가하여 줍니다. 그러므로, 자바에서 생성된 모든 클래스는 Object 클래스가 가지고 있는 변수와 메소드를 상속하여 그대로 사용할 수 있습니다.
class A {
…
}
class B extends A {
…
}
위와 같이 함으로써, 클래스 B는 클래스 A를 상속하면서 확장하게 되고, 이 때 클래스 A에 있는 변수와 메소드에 데해 접근 권한에 따라 상속하여 사용할 수 있습니다. 다음은 클래스 B가 클래스 A를 상속하도록 하고 있는 자바 프로그램입니다.
class A { |
<프로그램 11.InheritanceTest.java>
자바에서 “B 클래스가 A 클래스를 상속한다”고 할 때, 상속관계에 있는 두 클래스의 관계를 정의해 보면, A 클래스를 상위클래스(superclass)라 하고, B 클래스를 하위클래스(subclass)라 합니다. 이 때, 하위클래스는 다른 클래스로부터 파생된 클래스를 나타내며, 상위클래스의 모든 상태(변수)와 행동(메소드)을 상속하게 됩니다. 상위클래스란 클래스 계층구조에서 바로 한 단계 위 클래스를 나타냅니다. 하위클래스는 상위클래스의 외부 인터페이스 및 그 구현에 대해 재사용하므로, 상위클래스의 모든 변수와 메소드에 대해 하위클래스에서 접근 가능한 변수와 메소드는 하위클래스의 것으로 생각할 수 있는데, 하위클래스가 상속할 수 있거나 그렇지 못한 상위클래스의 멤버는 다음과 같습니다.
또한, 자바에서는 C++에서와는 달리 클래스는 단 하나의 클래스만을 상속하도록 하고 있습니다. 다시 말해서, 상위클래스와 하위클래스 간의 관계는 항상 1:1이라는 것이지요. 또한, 클래스는 클래스 계층 구조를 최상위 클래스까지 거슬러 올라가면서 만날 수 있는 상위클래스, 상위클래스의 상위클래스 등등 모든 상위클래스의 변수 및 메소드를 상속하는 것입니다. 마지막으로 상위클래스는 상위자료형이라고 말할 수 있고, 하위클래스는 하위자료형이라 말할 수 있습니다. 이 때, 하위자료형은 수식 내에서 상위자료형이 나타날 수 있는 어떤 위치에도 나타날 수 있습니다. 다시 말해서, 하위자료형은 상위자료형을 확장한 자료형이므로, 상위자료형 변수는 하위자료형 값을 가리킬 수 있지만, 하위자료형 변수는 상위자료형 값을 가리킬 수 없습니다.
// 상위클래스 정의
class A {
…
}
// 하위클래스 정의 – A 클래스를 확장
class B extends A {
…
}
다음은 상위클래스와 하위클래스의 관계에 따른 상위자료형과 하위자료형의 관계를 명확하게 보여주기 위한 자바 예제 프로그램입니다.
class A { |
<프로그램12.SuperSubClassTest.java>
다음은 상위클래스와 하위클래스의 관계를 좀 더 명확하게 보여주기 위한 자바 예제 프로그램입니다.
class A { |
<프로그램 13.SuperSubClassTest2.java>
자바에서는 상위클래스에 있는 변수와 메소드에 대해 하위클래스에서 접근 가능한 모든 변수와 클래스를 하위클래스가 상위클래스로부터 상속하여 자신의 것으로 사용합니다. 그러나 상위클래스가 가지고 있는 객체 생성자는 하위클래스가 상속할 수 없고, 대신 하위클래스의 객체 생성자에서 상위클래스의 객체 생성자를 호출할 수 있도록 합니다. 클래스의 객체 생성자에서 자신의 다른 객체 생성자를 호출하기 위해, 키워드 this를 사용하여 객체 생성자의 첫 줄에 나타내 주었습니다. 그렇다면, 하위클래스의 객체 생성자에서 상위클래스의 객체 생성자를 호출하기 위해서는 어떻게 해야 할까요? 하위클래스의 객체 생성자에서 상위클래스의 객체 생성자를 명시적으로 호출하기 위해서는 super라는 키워드를 사용합니다. 그리고 하위클래스의 객체 생성자에서 상위클래스의 객체 생성자를 호출하는 문장을 명시적으로 나타내 주지 않으면, 자바 컴파일러는 자동으로 "super();" 문장을 삽입해 줍니다. 여기서, 객체 생성자에서 다른 객체 생성자를 호출하기 위해 다음과 같은 키워드를 사용할 수 있고, 이러한 문장은 반드시 객체 생성자의 첫번째 문장으로 나타나야 합니다.
이렇게, 자바 프로그램이 상속과 다른 객체 생성자를 호출하게 되는 복잡한 구조를 갖게 될 때, 객체의 생성 순서를 살펴보면 다음과 같습니다.
class A {
A() {
this(…);
…
}
…
}
class B extends A {
B() {
super(…);
…
}
…
}
다음은 상속에 따른 생성자들의 관계를 보여주기 위한 자바 예제 프로그램입니다.
class A { |
<프로그램 14.SuperSubClassTest2.java>
다음은 상속에 따른 생성자들의 관계를 보여주기 위한 자바 예제 프로그램입니다.
class A { |
<프로그램 15.SuperThisTest2.java>
자바에서는 객체지향 개념에서 정의하고 있는 특성 중 하나인 다형성(Polymorphism)을 제공해 주기 위해, 메소드의 재정의(method overriding) 및 메소드의 다중 정의(method overloading)를 할 수 있도록 하고 있습니다. 상속관계에서 나타날 수 있는 다형성의 특징이 메소드 재정의입니다. 이러한 다형성은 보다 더 강력한 소스 코드 및 외부 인터페이스 재사용할 수 있도록 해 주고, 상위클래스의 일부 메소드가 하위클래스에 적합하지 않을 경우에도, 적합하지 않은 메소드만 재정의(overriding) 함으로써 상속이 가능하며 나머지 부분은 재사용 될 수 있습니다. 또한, 프로그램의 다른 부분이 수정 없이 재사용되고, 응용 프로그램의 개발을 돕기 위한 뼈대 구조를 제공하는 강력한 클래스 라이브러리(패키지)를 제공할 수 있습니다.
하위클래스는 상위클래스로부터 상속되는 상태와 행동들을 가질 수 있습니다. 또한, 하위클래스는 자신에게 필요한 변수들과 메소드를 추가적으로 정의할 수 있습니다. 그리고, 하위클래스는 상위클래스에서 정의된 메소드와 같은 이름, 같은 인자들을 갖는 새로운 메소드를 정의하여 상위클래스에서 상속되는 메소드를 재정의(overriding)할 수 있습니다. 이렇게 하위클래스가 상위클래스의 인스턴스 메소드를 새로 구현함으로써 상위클래스에서 제공해주고 있는 메소드를 하위클래스에 맞게 새롭게 구현할 수 있는 것입니다. 하위클래스에서 상위클래스의 메소드를 재정의하기 위해서는 다음과 같은 규약을 지켜주어야 합니다.
매개 변수의 개수나 자료형이 일치하지 않을 경우, 이는 메소드 다중 정의(method overloading)가 됩니다.
class A {
…
int m1(int i) { … }
int m2(float f) { … }
…
}
class B extends A {
…
int m1(int i) { … } // 메소드 재정의
int m2(float f1, float f2) { … }< // 메소드 다중 정의
int m3() { … } // 메소드 추가
…
}
상위자료형을 갖는 변수를 통하여 재정의된 메소드를 호출하면, 참조되고 있는 객체의 실제 클래스에서 마지막으로 재정의된 메소드가 호출됩니다. 그리고, 인스턴스 변수, 클래스 변수, 그리고 클래스 메소드는 은닉될 수는 있어도 재정의 될 수는 없습니다. 마지막으로, 하위클래스에서 재정의되었지만 상위클래스에 있는 메소드를 호출하기 위해서는 super라는 키워드를 사용합니다. 기존의 상위클래스의 메소드 및 그 상위클래스에 접근하는 기존 클래스의 재사용 인스턴스 메소드 내에서의 super 변수는 this 변수와 마찬가지로 현재 객체의 참조값을 갖지만, 그 자료형은 직계 상위클래스의 자료형이 됩니다. 따라서, super 키워드를 통하여 재정의된 메소드를 호출할 경우에는, 현재의 클래스에 재정의된 메소드를 호출하지 않고 상위클래스의 메소드를 호출하는 특별한 의미를 갖습니다.
다음은 메소드 재정의 및 메소드 다중 정의를 보여주기 위한 자바 프로그램입니다.
class SuperClass { |
<프로그램 16. OverridingTest.java>
인스턴스 변수, 클래스 변수, 그리고 클래스 메소드는 은닉될 수는 있어도 재정의 될 수는 없습니다. 또한, 상위클래스에 있는 변수를 참조하거나 또는 메소드를 호출하기 위해서는 super라는 키워드를 사용합니다. 기존의 상위클래스의 변수, 메소드 및 그 상위클래스에 접근하는 기존 클래스의 재사용 인스턴스 메소드 내에서의 super 변수는 this 변수와 마찬가지로 현재 객체의 참조값을 갖지만, 그 자료형은 직계 상위클래스의 자료형이 됩니다. super 키워드를 통하여 재정의된 메소드를 호출할 경우에는, 현재의 클래스에 재정의된 메소드를 호출하지 않고 상위클래스의 메소드를 호출하는 특별한 의미를 갖습니다.
하위클래스는 상위클래스의 외부 인터페이스 및 그 구현에 대해 재사용하므로, 상위클래스의 모든 변수와 메소드에 대해 하위클래스에서 접근 가능한 변수와 메소드는 하위클래스의 것으로 생각할 수 있는데, 하위클래스가 상속할 수 있거나 그렇지 못한 상위클래스의 멤버는 다음과 같습니다.
같은 패키지 내에 있는 상위클래스에 있는 접근 지정자가 생략되어 있는 변수와 메소드는 상속할 수 있고, 메소드에 대해 재정의 할 수 있습니다.
class A {
int x, y, z;
…
…
}
class B extends A {
int x; // x는 새롭게 정의
float y; // y는 새롭게 정의
// z는 상속
…
int m3() {
this.x = super.x;
this.y = super.m1(this.x);
…
}
다음에 나오는 자바 프로그램에서 B 클래스가 A 클래스를 상속하도록 하였는데, 다음에 나오는 프로그램에서 상속 가능한 변수 또는 메소드에 대하여 생각해보고, 결과를 살펴보고 확인해 보세요.
class A { |
<프로그램 17.AccessRightTest.java>
다음은 접근 권한에 따른 상속을 보여주기 위한 자바 프로그램입니다. B 클래스가 A 클래스를 상속하도록 하였는데, 다음 중 상속과 관련하여 에러가 발생하는 부분에 대해서 다시 한 번 살펴보시기 바랍니다.
class A { |
<프로그램 18.AccessRightTest2.java>
추상클래스(abstract class)는 추상적인 클래스로써 그 구현이 덜 되었거나 또는 아직은 미완성 클래스이므로 실제 인스턴스(또는 객체)를 생성할 수 없도록 한 클래스입니다. 다시 말해서, 추상클래스는 객체가 가지는 특성들을 추상화시켜 놓았을 뿐 아직 구체화 시키지 못한 클래스이므로, 이 추상클래스를 상속하는 하위클래스에서 좀 더 구체화 시키도록 하는 것입니다. 따라서, 추상클래스를 상위클래스로 하여 상속하는 하위클래스는 추상클래스인 상위클래스에서 완전히 구현하지 못한 부분들을 완전하게 구현해 주어야만 하위클래스에 대한 객체 생성이 가능하고, 그렇지 못할 경우 하위클래스는 상위클래스인 추상클래스와 같이 미완성이므로 자체적으로 객체를 생성할 수 없고, 이 하위클래스는 다시 추상클래스가 됩니다.
추상메소드는 추상클래스와 마찬가지로 아직 구현이 이루어지지 않고 단지 그 프로토타입만을 가지고 있는 메소드를 말합니다. 추상메소드의 특징은 다음과 같습니다.
추상클래스는 추상메소드를 포함할 수 있고, 추상메소드를 포함하는 클래스는 반드시 추상클래스로 선언되어야 합니다. 추상메소드를 포함하고 있는 추상클래스를 상속하는 하위클래스는 추상클래스가 갖고 있는 모든 추상메소드를 구현하여 주어야 합니다. 그럴 경우, 하위클래스는 하나의 클래스처럼 사용할 수 있고, 인스턴스의 생성도 가능하지만, 추상메소드를 모두 구현해 주지 못한 경우에는 하위클래스도 구현이 완전히 이루어지지 않은 추상메소드를 포함하게 되므로 추상클래스가 되며, 이 때 반드시 추상클래스로 선언되어야 합니다. 그렇지 않을 경우 다음과 같은 에러가 발생합니다. 이러한 추상 클래스(abstract class)와 추상 메소드는 해당 클래스와 메소드의 앞에 abstract 키워드를 사용하여 다음과 같이 선언할 수 있습니다.
abstract class 클래스이름 {
abstract 메소드선언; // 메소드의 몸체는 없음
}
예)
abstract class AbstractClass {
abstract void abstractMethod();
}
다음에 나오는 예제는 abstract를 잘못 사용하고 있는 예를 보여주는 자바 프로그램입니다.
class A { |
<프로그램 19.AbstractTest.java>
위의 자바 프로그램을 살펴보면, 먼저, (a) 부분은 abstract 메소드를 포함하고 있으므로 abstract로 선언되어야 합니다. 그리고, (b) 부분에서 abstract 메소드는 메소드 몸체를 갖지 못합니다. 마지막으로, (c) 부분에서는 abstract 클래스를 상속하고 있고 abstract 메소드 중 m2가 아직 구현되지 않았으므로 abstract로 선언되어야 합니다.
class A { |
<프로그램 20.AbstractTest.java>
아래의 자바 프로그램을 컴파일 해 보면 에러가 발생하는데, 이는 추상클래스 참조형 변수를 선언할 수는 있지만, 인스턴스(또는 객체)를 생성할 수는 없기 때문입니다.
abstract class A { |
<프로그램 21. AbstractTest2.java>
매개변수, 지역변수, 인스턴스 변수, 클래스 변수 등과 같은 모든 종류의 변수를 final로 선언하면 그 변수의 내용을 변경할 수 없습니다. 상수는 보통 클래스 상수를 많이 사용합니다. 클래스 상수는 기본 자료형일 경우, 이름을 모두 대문자로 만들고, 이름이 여러 단어로 이루어져 있을 경우, 단어와 단어 사이를 ’_’로 구분하여 줍니다. 리터럴 상수에 대한 파이널 변수의 이점은 문서화의 한 형태로서 가독성을 높이고, 상수가 정의한 곳에서만 값을 바꾸어 주면 되므로 수정이 용이하고, 관련된 상수를 하나의 클래스에 모아 놓을 수 있으므로 C에서의 enum 자료형을 대신할 수 있습니다. final 변수에 아무런 초기값도 대입하지 않을 경우가 있는데, 이러한 상수를 공백 상수(blank constant)라 하고 프로그램 내에서 단 한번 값을 대입할 수 있는데, 이러한 공백 상수는 초기화 수식만으로 초기값을 계산할 수 없거나 초기값을 계산할 때 예외가 발생할 수 있는 경우 주로 사용합니다. 이렇게 한 번 설정된 final 변수의 값은 변경할 수 없고, 변경하려 할 경우 에러가 발생합니다.
상수 객체란 final 변수(상수)와 같이 그 내용을 변경할 수 없는 것을 말합니다. 다시 말해서, 객체의 내용을 외부에서 전혀 바꿀 수 없는 객체를 상수 객체라 합니다. 이렇게 객체의 내용을 외부에서 전혀 변경할 수 없게 하기 위해서는, 클래스를 정의할 때 모든 인스턴스 변수를 final로 선언하거나, private 접근권한을 줌으로써 외부에서 아예 접근하지 못하도록 하는 방법이 있습니다. 또한, 변수의 값을 변경할 수 있도록 제공해주는 메소드에 대해서도 private 접근권한을 줌으로써 외부에서 변수에 대해 변경할 수 있는 방법을 아예 없애버리는 것입니다. 실제로 자바에서 기본적으로 제공되고 있는 클래스 중 이러한 방법을 사용하여 내용을 전혀 변경할 수 없도록 정의된 클래스에 대한 예를 들면, String 클래스와 Integer 클래스 등이 있습니다. 그런데 한가지 재미있는 것은, final 변수가 참조형일 경우, final 변수가 직접 저장하고 있는 참조값은 바꿀 수 없지만, 참조하고 있는 객체 또는 배열의 내용은 바꿀 수 있다는 것입니다.
final 변수선언=초기값;
또는
final 변수선언;
…
변수=초기값;
다음은 final 변수(상수)에 대한 사용 예를 보여주기 위한 자바 프로그램입니다.
class ConstantClass { |
<프로그램 22.ConstantTest.java>
위의 자바 프로그램처럼 final 변수는 지역변수, 매개변수 등 모두 가능합니다. 대신, 한 번 값을 지정하였으면, 더 이상 변경할 수 없고, 변경하려고 하면 에러가 발생하게 됩니다. 다음은 final 변수(상수)에 대한 사용 예를 보여주기 위한 자바 프로그램입니다.
class MenuClass { |
<프로그램 23.EnumVarTest.java>
자바 언어에서는 C/C++에서 제공해 주고 있는 enum 형에 대해서 제공해 주고 있지 않습니다. 그러나, 이러한 enum 형에 대해서 자바에서 사용하고자 한다면, 위에서와 같이 final로 선언된 클래스 변수를 이용할 수 있습니다.
파이널 클래스와 파이널 메소드에 대한 필요성은 자바에서 객체지향 개념 중 상속을 제공해 주기 때문에 발생합니다. 간단하게 말해서, 파이널 클래스는 더 이상 상속될 수 없고, 파이널 메소드는 더 이상 재정의 될 수 없다는 것을 의미합니다.
파이널 클래스는 더 이상 상속될 수 없는 클래스를 나타냅니다. 다시 말해서, 어떤 클래스가 더 이상 상속되지 않도록 하기 위해서는 final 키워드를 이용하여 클래스를 선언하여 주면 됩니다. 현재 작성되는 클래스가 더 이상 기능이 추가될 필요없이 완벽한 클래스로 만들어졌다고 생각한다면 파이널 클래스로 선언하여 주면 됩니다. 이렇게 final 키워드를 이용하여 선언된 파이널 클래스를 상속하려고 한다면 에러가 발생합니다. 파이널 클래스를 선언하는 방법은 다음과 같습니다.
final class 클래스이름 {
}
파이널 메소드는 상속관계에 있는 상위클래스와 하위클래스에 대해, 상위클래스에 있는 메소드가 더 이상 하위클래스에 의해 재정의(overriding)되지 않도록 하기 위해 사용됩니다. 다시 말해서, 상위클래스가 가지고 있는 메소드에 대해 더 이상 구현될 필요가 없거나, 객체에 있어 매우 중요한 메소드이므로 하위클래스에 의해 재정의되면 안 되는 메소드이거나, 또는 객체의 일관성 있는 상태를 유지하기 위해 중요한 메소드일 경우 파이널 메소드로 선언하게 됩니다. 이러한 파이널 메소드를 선언하기 위한 방법은 다음과 같습니다.
class 클래스이름 {
final 메소드 선언;
}
다음은 파이널 클래스와 파이널 메소드를 사용하는 자바 프로그램입니다.
final class A { |
<프로그램 24.FinalTest.java>
위의 자바프로그램에서는 파이널 클래스는 더 이상 상속될 수 없지만 상속하려 하고 있고, 파이널 메소드는 더 이상 재정의 될 수 없지만 재정의 하려 하고 있습니다. 따라서, 위와 같은 에러가 발생합니다.
인터페이스란 구현되지 않고 – 메소드 몸체를 가지지 않고 – 메소드 정의(프로토타입)만 가지고 있고, 거기에 이와 관련된 상수값 만을 모아 놓은 클래스라 할 수 있습니다. 여기서 메소드 정의 즉 프로토타입이란 추상메소드라 할 수 있고, 상수란 파이널 변수라 할 수 있겠죠. 다시 말해서, 인터페이스란 추상메소드와 파이널 변수로만 이루어진 클래스라 할 수 있습니다. 따라서, 인터페이스 내에 정의된 메소드는 자바에 의해 자동으로 ‘public abstract’로 선언되게 됩니다. 마찬가지로 인터페이스 내에 선언된 변수는 자동으로 ‘public static final’로 선언되도록 되어 있습니다. 추상메소드를 포함하고 있는 추상클래스는 객체 생성을 할 수 없듯이, 구현이 이루어지지 않은 추상메소드를 포함하고 있는 인터페이스 역시 객체 생성을 할 수 없습니다. 따라서, 어떤 클래스가 이 인터페이스를 상속하여 인터페이스에 정의된 모든 메소드를 구현해 주어야 객체를 생성할 수 있겠지요. 이러한 관계를 ‘클래스가 인터페이스를 구현한다’라고합니다. 클래스와 클래스 사이에는 상속이 이루어지고, 클래스와 인터페이스 간에는 구현이 이루어지는 것입니다. 이 때, 클래스는 인터페이스의 모든 메소드 및 변수들을 상속하게 되며, 인터페이스에 정의되어 있는 모든 메소드를 구현해 주지 않을 경우 이 클래스는 인터페이스 메소드 즉 추상메소드를 포함하고 있으므로 추상클래스로 선언되어야 합니다. 이러한 인터페이스는 다음과 같은 경우에 유용하게 사용할 수 있습니다.
자바에서 인터페이스가 다중 상속을 할 수 있도록 해준다고 얘기하는데, 인터페이스는 다중 상속을 할 수 있도록 해 주는 것이 아니라 다중 상속을 하는 것처럼 해 주는 것입니다. 인터페이스와 클래스의 다중 상속 사이에는 다음과 같은 약간의 차이가 있기 때문입니다.
인터페이스의 계층 구조는 클래스의 계층 구조와 서로 무관합니다. 같은 인터페이스를 구현하는 클래스들은 클래스 계층 구조 상에서 서로 관련이 있을 수도 없을 수도 있습니다. 이러한 특성 때문에 진정한 다중 상속이라 할 수 없습니다. 그러나 자바에서는 하나의 인터페이스가 여러 개의 상위인터페이스를 가질 수 있도록 함으로써, 다중 인터페이스 상속을 할 수 있도록 해 줍니다.
[public] interface 인터페이스이름 [extends 상위인터페이스] {
상수선언;
메소드선언;
}
인터페이스 이름은 대문자로 시작하고, “able” 또는 ”ible”와 같은 형태로 끝나게 됩니다.
인터페이스는 클래스에 의해 구현되어야 합니다. 다시 말해서, 인터페이스는 자체적으로 객체를 생성할 수 없으므로, 클래스가 이 인터페이스를 상속(implements)하여 인터페이스에 정의된 모든 메소드를 구현해 주어야 합니다. 그렇게 할 경우에만 객체를 생성하여 사용할 수 있습니다. 그리고, 클래스의 상속에서 하나의 클래스는 하나의 클래스만을 직속으로 상속할 수 있는 것과는 달리, 하나의 클래스는 여러 개의 인터페이스를 구현해 줄 수 있습니다. 이렇게 클래스가 인터페이스를 구현한다는 것을 표현하기 위하여 다음과 같이 할 수 있습니다. 인터페이스 정의는 다음과 같이 인터페이스 선언부와 인터페이스 몸체부(body) 등 크게 두 부분으로 구성되어 있습니다.
인터페이스선언부 {
인터페이스몸체부
}
또는
[public] interface 인터페이스이름 [extends 상위인터페이스] {
상수선언;
메소드선언;
}
인터페이스도 클래스와 마찬가지로 인터페이스를 상속할 수 있습니다. 그러나, 클래스 상속과 한 가지 차이점은 클래스는 오직 하나의 클래스만 상속 가능한테 비해, 인터페이스는 여러 개의 인터페이스를 상속할 수 있다는 것입니다. 인터페이스가 상속하는 상위인터페이스들은 ‘,’로 구분하여 주고, 상위인터페이스가 가지는 모든 상수와 메소드들을 상속하게 됩니다.
자바에서 인터페이스를 선언할 때 또는 인터페이스 내에 선언된 메소드를 선언할 때, abstract 지정자를 사용하는 것은 불필요합니다. 왜냐하면 자바에서는 인터페이스 및 인터페이스 내의 메소드는 그 구현이 이루어지지 않았다는 특성 때문에 인터페이스 및 인터페이스 내의 메소드에 대해 내부적으로 abstract로 취급하기 때문입니다.
[public] interface 인터페이스이름 [extends 상위인터페이스] {
상수선언;
메소드선언;
}
인터페이스는 추상클래스와 같이 아직 구현이 이루어지지 않았으므로, 클래스에 의해 상속되어 그 구현이 완성되어야 객체(또는 인스턴스)를 생성하여 사용할 수 있습니다. 클래스가 인터페이스를 상속하여 구현하기 위해서는 다음과 같이 해 주어야 합니다.
접근지정자 class [extends 클래스이름]
implements 상위인터페이스리스트 {
// 인터페이스에 선언된 메소드 구현
}
인터페이스는 참조 자료형으로서, 기본 자료형이나 다른 참조 자료형과 같이 어느 곳에서나 사용할 수 있습니다. 다음은 인터페이스를 잘못 사용하는 자바 프로그램에 대한 예를 보여 주고 있습니다.
private interface InterfaceTest { |
<프로그램 25.InterfaceTest.java>
다음은 인터페이스를 선언하고 클래스를 이용하여 인터페이스를 상속 및 구현하는 자바 프로그램입니다.
interface DataStructure { |
<프로그램 26.InterfaceTest2.java>
자바에서 클래스가 인터페이스를 상속하여 구현하려 할 때, (a)와 같이 인터페이스 내에 있는 모든 메소드를 구현해 주지 않으면, 그 클래스는 추상클래스(abstract class)로 선언되어야 합니다. 또한, (b), (c)와 같이 인터페이스 또는 그 구현이 끝나지 않은(인터페이스를 제대로 구현하지 못한) 클래스에 대해서는 객체를 생성할 수 없습니다.
클래스 내부에 선언되는 클래스는 변수 또는 메소드와 마찬가지로 두 가지 종류가 있습니다. 클래스를 따로 선언하고 정의하기에는 간단하고 특정 클래스와 밀접한 관계를 갖는 경우 또는 클래스로 정의해야 되지만 다른 클래스에서는 사용하지 않고 특정 클래스 하나에서만 사용하는 경우, 특정 클래스 내부에 중첩된 클래스로 만듭니다. 이렇게 함으로써 클래스의 이름 짓는 것을 간단하게 하고 프로그램을 보다 알아보기 쉽게 할 수 있습니다. 또한, 이러한 중첩클래스를 사용하여 프로그램의 구조를 보다 더 간단하고 알아보기 쉽게 할 수 있습니다. 이러한 두 개의 클래스 중 클래스 내부에 static으로 선언된 클래스가 있는데, 이 클래스를 중첩클래스라 합니다. 중첩 클래스의 객체는 중첩클래스를 포함하고 있는 클래스의 이름을 패키지 이름처럼 사용할 수 있는 것일 뿐 서로 동등합니다. 그리고, 중첩 클래스가 포함하고 있는 메소드에서는 클래스 메소드에서처럼 인스턴스 변수 또는 인스턴스 메소드에 접근할 수 없지만, 클래스 변수 또는 클래스 메소드에는 접근할 수 있습니다. 마지막으로, 중첩클래스와 이 중첩클래스를 포함하고 있는 클래스는 컴파일 했을 때, 다음과 같이 각각에 대한 클래스 파일이 생성됩니다.
class A {
static class B {
…
}
…
}
컴파일 했을 때 생성되는 파일
A.class ß 중첩클래스를 포함하는 클래스
A$B.class ß 중첩클래스
다음은 중첩클래스를 사용할 때 주의해야 할 것에 대해 간단히 보여주는 자바 프로그램입니다.
class NestingClass { |
<프로그램 27.NestedClassTest.java>
다음은 정상적으로 중첩클래스를 정의하여 컴파일했을 경우, 다음과 같습니다.
class NestingClass2 { |
<프로그램 28.NestedClassTest2.java>
위와 같이, 정상적으로 컴파일 했을 경우 다음과 같은 세 개의 '.class' 파일이 생성됩니다.
중첩클래스와 같이 클래스 내부에 선언되는 클래스로서 중첩클래스와는 달리 static 없이 선언된 클래스를 말합니다. 중첩클래스를 사용하는 것과 같이 내부클래스를 사용하는 것 역시 프로그램의 구조를 더 좋게 할 수 있고, 특정 클래스와 관련 깊은 클래스를 그 클래스 외부에 따로 정의하지 않고 클래스의 내부에 정의함으로써, 프로그램을 보다 읽기 쉽고 알아보기 쉽게 해 줍니다.
이러한 내부클래스는 이벤트 처리, 데이터 구조 클래스 등을 위해 자주 사용되며, 내부클래스 객체는 생성 당시에 외부 클래스 객체에 대한 보이지 않는 참조값을 갖도록 초기화됩니다. 이를 통하여, 외부클래스 객체의 메소드와 필드가 내부클래스 객체의 메소드와 필드인 것처럼 접근할 수 있게 해 줍니다.
내부클래스의 메소드에서 외부클래스 객체의 참조값을 얻기 위해서는 “외부클래스이름.this”를 사용합니다. 이렇게 함으로써, 내부클래스에서 외부클래스의 변수 및 메소드를 자신의 변수 및 메소드인 것처럼 접근 가능합니다. 따라서, 중첩클래스에서는 자신을 포함하고 있는 클래스의 인스턴스 변수 및 인스턴스 메소드에는 접근할 수 없고, 단지 클래스 변수 및 클래스 메소드에만 접근가능 했지만, 내부클래스에서는 자신을 포함하고 있는 클래스의 클래스 변수 및 클래스 메소드에 접근할 수 있을 뿐만아니라, 인스턴스 변수 및 인스턴스 메소드에도 접근가능 합니다. 그러나, 내부클래스는 클래스 변수나 클래스 메소드를 가질 수 없고, 내부클래스를 선언하고, 이를 컴파일 했을 때 생성되는 바이트 코드의 클래스 파일 이름은 다음과 같습니다. 마지막으로, 내부클래스는 클래스 변수 또는 클래스 메소드를 가질 수 없습니다. 다시 말해서, static으로 선언된 변수 또는 메소드를 가질 수 없다는 것입니다.
class A {
class B {
…
}
…
}
외부클래스의 변수 또는 메소드 참조
A.this.변수또는 A.this.메소드
컴파일 했을 때 생성되는 파일
A.class ß 외부클래스
A$B.class ß 내부클래스
다음에 나오는 예제는 외부클래스와 내부클래스의 관계를 보여주기 위한 자바 프로그램입니다.
class OuterClass { |
<프로그램 29.OuterClassTest.java>
첫 번째와 두 번째 에러의 경우, 내부클래스에서는 static 변수 또는 static 메소드는 불가능하기 때문에 발생하는 것이고, 세 번째 에러의 경우에는 "OuterClass.InnerClass inner = outer. new InnerClass();"와 같이 바꾸어 주어야 합니다. 다음에 나오는 예제는 정상적으로 컴파일 가능한 자바 프로그램입니다.
class OuterClass2 { |
<프로그램 30.OuterClassTest2.java>
위와 같이, 정상적으로 컴파일 했을 경우 다음과 같은 세 개의 '.class' 파일이 생성됩니다.
지역클래스는 지역변수와 같이 메소드 내에 정의된 클래스를 가리킵니다. 지역클래스는 지역변수와 같이 메소드 내에서만 사용하고자 할 경우에 주로 사용합니다. 이러한 지역클래스는 메소드에서 자신을 포함하고 있는 클래스가 갖는 변수 및 메소드를 사용할 수 있듯이, 외부클래스가 포함하고 있는 변수 및 메소드를 사용할 수 있습니다. 그런데, 지역클래스를 포함하고 있는 메소드가 리턴되고 난 후에도 지역클래스 객체는 계속 존재하고, 계속 자신을 포함하고 있는 메소드의 지역변수에 접근하게 됩니다. 따라서, 지역클래스를 포함하고 있는 메소드의 지역변수 중 지역클래스에서 참조하는 지역변수는 항상 final로 선언함으로써, 지역클래스 객체가 언제라도 사용할 수 있도록 해 주어야 합니다. 다시 말해서 지역변수는 메소드가 끝난 후에는 사라지게 되므로, 지역변수를 final로 선언함으로써 항상 메모리에 존재하도록 해 주어야 합니다.
class 클래스이름 {
메소드선언 {
지역변수선언 // 항상 final로 선언
class 지역클래스이름 {
}
…
}
…
}
다음에 나와 있는 자바 프로그램에서는 지역클래스를 다룰 때 발생하기 쉬운 잘못을 보여주고 있습니다.
class LocalClassTest { |
<프로그램 31.LocalClassTest.java>
먼저, (a) 부분에서는 메소드 내에서 static 지역 변수를 선언하지 못하게 되어 있기 때문에 에러가 발생하고, (b)와 (c) 부분에서는 지역클래스에서는 자신을 포함하고 있는 메소드의 final 변수에만 접근 가능하기 때문입니다.
다음에 나오는 예제는 프라임 숫자를 구하기 위한 스레드 클래스를 메소드 내에 내부클래스로 선언한 자바 프로그램입니다.
class LocalClassTest2 { |
<프로그램 32.LocalClassTest2.java>
단순한 클래스 또는 인터페이스를 정의하여 사용할 때, 여러 곳에서 사용하는 것이 아니고 단 한번만 정의하여 사용한다거나 할 경우 주로 익명클래스를 사용합니다. 익명클래스는 클래스 또는 인터페이스에 대한 객체를 생성하면서 바로 클래스 또는 인터페이스를 정의하도록 되어 있습니다. 다시 말해서, new 수식이 있는 곳에서 바로 클래스 또는 인터페이스를 정의합니다. 이러한 익명클래스는 주로 클래스 또는 인터페이스를 구현하여 바로 사용하고자 할 때 이용됩니다. 그리고, 익명클래스를 사용할 때 한 가지 주의할 점은 익명클래스는 new 수식의 연장이므로 끝에 꼭 세미콜론(;)을 붙여주어야 한다는 것입니다. 다음과 같이 익명클래스를 정의하여 사용할 수 있습니다.
다음 문장들과 마찬가지로 new 수식 역시 메소드 내에서만 사용되므로 이름이 없다는 것만 제외하고는 익명클래스와 지역클래스는 모든 점에서 같습니다. 따라서, 익명클래스를 포함하고 있는 메소드의 지역변수 중 익명클래스에서 참조할 수 있는 지역변수는 final로 선언된 지역변수만 가능합니다.
new 클래스명() {
클래스 정의
};
또는
new 인터페이스명() {
인터페이스 정의 및 구현
};
위와 같이 제공된 클래스 또는 인터페이스를 이름없는 하위클래스를 정의한 후, 그 하위클래스의 객체를 만듭니다. 다음에 나와 있는 자바 프로그램은 위에서 지역클래스를 이용하여 했던 작업을 그대로 익명클래스를 이용하여 하고 있는 프로그램입니다.
class AnonymousClassTest { |
<프로그램 33.AnonymousClassTest.java>
댓글을 달아 주세요