![]() Jose M. Fern�ndez |
요약: 이 연재 기고는 구조적 프로그래밍에 대한
전통적인 책을 본보기로 한다. 첫 번째 기고에서 자바 언어의 특성에 대해 간단한 소개를 했고,
이번에는 자료형, 변수, 제어문 등을 계속해서 공부하려고 한다. 여기에는 클래스(class)라는
대단히 중요한 주제가 있다. 클래스의 개념은 자바 프로그램 언어의 핵심이 된다.
To center the classes topic, the
first part will be as ESQUEMATICA�? as possible, due to the similarity to the
rest of the programming languages.
자바 주석은 다음의 세 가지 양식 중의 하나일 수 있다:
식별자는 컴파일러가 그것들을 식별하도록 변수, 클래스, 메소드(method)에 주어진 이름이다. 알파벳 문자, 숫자, 밑줄, 달러 기호의 어떤 문자열을 사용할 수 있다. 숫자로 시작해서는 안 된다.
자바는 구분자로서 몇 개의 특수한 문자를 사용한다. 가장 자주 쓰이는 것은 ; 구분자이지만, 또한 다음을 더 알아 보자:
기호 | 해설 |
() | 메소드 정의와 호출 시 매개변수 목록을 보유한다. 또한 표현식 우선 순위(expressions precedence)를 변경하기 위해 사용된다. 혹은, 제어 문장과 자료형 변환에서 표현식을 수용한다. |
{} | 자동적으로 초기화되는 배열(vector)의 값들을 수용한다. 코드 블럭(code block)을 수용한다. 클래스, 메소드, 지역 영역(local scope)과 함께 사용된다. |
[] | 매트릭스(matrix) 자료형을 정의한다. 매트릭스에 있는 값을 참조한다. |
; | 문장 구분자. |
, | 변수 선언에서 연속적인 식별자들들 구분한다. for 문에서 문장들을 구분한다. |
. | 패키지(package), 하위 패키지, 클래스 명칭을 구분한다. 참조된 변수로부터 변수 또는 메소드를 구분한다. |
핵심어는 자바 언어로 사용되어지는 식별자이며 자바에 의해 정의된 바와 다르게 사용될 수 없다. 다음 표는 자바 핵심어들을 보인다:
abstract | double | int | super |
boolean | else | interface | switch |
break | extends | long | synchronized |
byte | false | native | this |
byvalue | final | new | threadsafe |
case | finally | null | throw |
catch | float | package | transient |
char | for | private | true |
class | goto | protected | try |
const | if | public | void |
continue | implements | return | while |
default | import | short | |
do | instanceof | static |
이전 기고에서 자바는 완전하게 객체지향적이라고 언급했다. 그럼에도 불구하고, 효율성을 고려해서, 자바는 객체가 아닌 "단순한" 자료형 여덟 개를 정의한다. 게다가, 이식성을 이유로, 모든 자료형은 유일하게 정의된 범위를 가진다.
단순 자료형은 네 개로 묶어 구분할 수 있다:
자료형 | 명칭 | 크기 | 범위 |
정수 | long | 64 | -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 |
int | 32 | -2,147,483,648 ~ 2,147,483,647 | |
short | 16 | -32,768 ~ 37,767 | |
byte | 8 | -128 ~ 127 | |
실수 | float | 32 | 3.4 e-038 a 3.4 e+038 |
double | 64 | 1.7 e-308 a 1.7 e+308 | |
문자 | char | 16 | Unicode |
부울린 | boolean | true 또는 false |
일반적으로, 변수는 이런 방식으로 선언된다:
identifier type [=value] [,identifier [=value]......]; |
예:
int a = 4, b, c=7; char c; myclass class; |
대부분의 프로그래밍 언어들은 두 가지의 영역 범주를 정의한다. 전역(global)과 지역(local)이 그것이다. 그러나 이는 자바 객체 지향적 모델에 적합하지 않다. 이 모델에 기초해서, 두 개의 주요 영역은 클래스와 메소드에 의해 정의되는 것이다.
int
형은 byte
형을 저장할 만큼 충분히 커야 한다.
그래야 명시적인 변환을 필요로 하지 않는다.
정수 형은 char
형 또는 boolean
형과 호환되지 않는다.
뿐만 아니라 boolean
형은 다른 나머지 자료형들과 전혀 호환되지 않는다.
int 값을 byte 형에 저장하고 싶다면, 다음과 같은 형식에 따라 명시적인 변환을 사용하여야 한다:
(type) value |
여기서, type은 자료형 변환 대상을 나타낸다. 예:
int a; char b; a=(int) b; |
자동화된 형 변환은 정보의 상실을 초래하기 때문에 반드시 조심해야 한다. 예를 들면:
부동 소수 형을 정수 형으로 변환할 때, 소수점 이하가 무시된다:
int a; double d= 125.43; a=(int) d;변수는 값으로 125.를 가진다 byte b; int i=257; b=(byte) i;b는 값으로 1을 가지는데, 256이 byte 형의 범위인 경우에 257을 256으로 나눈 결과로부터 나온 것이다. byte b; double d= 340.123; b=(byte) d;b는 84의 값을 가지게 된다 |
이런 모든 변환은 컴파일러와 인터프리터로 수행된다. 어떤 컴파일 오류도 찾아내지 못한다면 말이다.
연산자 | 해설 |
+ | 더하기 |
- | 빼기 |
* | 곱하기 |
/ | 나누기 |
% | 나머지 |
++ | 증가 |
+= | 더하고 대입 |
-= | 빼고 대입 |
*= | 곱하고 대입 |
/= | 나누고 대입 |
%= | 나머지를 구하고 대입 |
-- | 감소 |
나머지 연산자는 정수와 부동 소수점 값 양쪽에 적용될 수 있다. 예를 들면:
int a=38; double d=41.95; int c=a%10; double e=d%10; |
c 변수는 8이 된다. e 변수는 1.95를 가진다.
대입 연산자들은 다음과 같은 구성에 유용하다:
a = a + 4; 은 a+=4; 와 같다
a = a % 2; 은 a%=2; 와 같다
보통, 다음과 같이 말할 수 있다:
var = var op expression; 은 var op= expression; 로 치환될 수 있다.
연산자 | 해설 |
~ | 비트 수준의 단항 NOT |
& | 비트 수준의 AND |
| | 비트 수준의 OR |
^ | 비트 수준의 배타적 OR |
>> | 우측 쉬프트 |
>>> | 0으로 채워지는 우측 쉬프트 |
<< | 좌측 쉬프트 |
&= | 비트 수준의 AND와 대입 |
|= | 비트 수준의 OR와 대입 |
^= | 비트 수준의 배타적 OR와 대입 |
>>= | 우측 쉬프트와 대입 |
>>>= | 0으로 채워지는 우측 쉬프트와 대입 |
<<= | 좌측 쉬프트와 대입 |
연산자 | 해설 |
== | 와 같은 |
!= | 와 다른 |
> | 보다 큰 |
< | 보다 작은 |
>= | 같거나 보다 큰 |
<= | 같거나 보다 작은 |
다른 언어들(C/C++)과는 달리, boolean 값들은 True와 False이다. 즉, 이것들은 수치가 아니다..
연산자 | 해설 |
& | 논리적 AND |
| | 논리적 OR |
^ | 논리적 XOR(배타적 OR) |
|| | 단축 OR |
&& | 단축 AND |
! | 단항 논리적 NOT |
&= | AND 대입 |
|= | OR 대입 |
^= | XOR 대입 |
== | 와 같은 |
!= | 와 같지 않은 |
?: | 삼항 If-then-else |
OR 단축 연산자의 결과는 다음과 같다: 두 번째 연산자의 값이 어찌되었든지 간에 첫 번째 연산자가 참이면 참이다. 마찬가지로, AND 단축 연산자는 두 번째 연산자의 값에 상관없이 첫 번째 연산자가 거짓이면 거짓이다.
삼항 연산자의 일반적인 형식이다:
Expession1 ? expession2 : expression3
만일 Expression1이 참이면, expresion2가 실행되고; 만일 거짓이면, expression3이 실행된다.
연산 우선 순위:
( ) | [ ] | . | |
++ | -- | ~ | ! |
* | / | % | |
+ | - | ||
>> | >>>> | << | |
> | >= | < | <= |
= = | != | ||
& | |||
' | |||
| | |||
&& | |||
|| | |||
?: | |||
= | Op= |
구분 | 문장 | 해설 |
선별 | if | if ( condition ) sentence1; else sentence2; |
여러 if들 | If (condition ) sentence; else if (condition) sentence; else if (condition) sentence; . . else sentence; | |
switch | switch (expression){ case value1: sentence; break; case value2: sentence; break; . ... default : sentence; } | |
반복 | while | while (condition) { sentence; } |
do while | do { sentences; } while (condition) | |
for | for (initialization, condition, iteration) { sentences; } | |
분기 | break | switch로부터 빠짐.
loop로부터 빠짐 |
continue | 현 loop 반복으로부터 빠져 나오지만 동일한 loop를 계속한다 | |
return | 메소드로부터 명확하게 리턴한다 |
클래스는 객체를 위한 모델 (패턴)이고, 객체는 클래스의 인스턴스(instance)이다. 자바는 전역 함수 또는 변수를 지원하지 않아, 모든 프로그램 활동(actions) (메소드)은 반드시 클래스 내에서 정의되어야 한다.
클래스는 예약어 "class"를 사용하여 정의된다. 보통의 클래스 정의는 다음과 같다:
class class_name { Type instance1_of_variable; Type instance2_of_variable; . . ... type instance_variable; type method_name(parameter_list){ //body of the method } . . .. type name_of_the_method(parameter_list){ //body of the method } } |
클래스 내부의 변수 또는 데이터를 인스턴스 변수(variables of instances)라고 부른다. 메소드는 코드를 보유하고, 클래스의 데이터가 어떻게 사용될 수 있는지 정의한다.
클래스의 객체를 얻기 위해서 두 단계가 필요하다:
객체에 동적으로 메모리를 할당하고 그것의 참조를 얻을 것.
variable = new name_of_the_class(); |
여기서, "variable"은 생성하려는 클래스의 변수이고, name_of_the_class는 인스턴스로 되는 클래스의 명칭이다. 객체의 생성을 그림으로 표현한다면 다음과 같다:
type name_of_the_method (parameter_list) { //method_body } |
"type"은 메소드에 의해 리턴되는 자료형이다; 클래스 형을 포함하여 어떤 유효한 자료형이라도 가능하다. 혹은, 어떤 값도 리턴하지 않을 수도 있다(void).
매개변수 목록(parameter list)은 자료형-식별자가 쌍으로 연속된 것인데 쉼표로 구분된다.(역자주: 영문 번역에서는 콜론으로 구분한다고 되어 있지만, 자바 언어 문법을 참조하여 쉼표로 바꾸어 바로 잡음.) 매개변수는 메소드에 주어진 인자의 값을 받는 변수이다. 메소드가 매개변수를 가지지 않는다면, 목록은 비어 있을 것이다.
"void"가 아닌 값을 리턴하는 메소드 사용법:
return value; |
여기서 value는 리턴되는 값이다.
자바는 매우 많은 메소드를 제공하는데 무척 다루기 쉽고 강력하다. 그래서 여기서부터 이번 기고의 끝까지, 메소드의 매우 중요한 관점을 살펴 보려고 한다.
그렇지만, 그러기에 앞서, 간단한 예로서 이전의 개념을 다시 검토해 보자.
(풀과 같이) 사각형 모양의 상자의 용량을 계산하는 클래스를 생성하자:
코드 | 주석 |
class capacity { double length; double width; double height; void CalcVolume () { double volume ; volume = length*width*height; System.out.println("Volume: " + volume); } } |
여러분이 알 수 있듯이, 세 개의 인스턴스 변수 length, width, height를 보유하는 "capacity"라는 이름의 클래스를 정의했다. 또한 그릇 부피를 계산하는 메소드를 정의했다. 소스 파일의 이름을 capacity.java라고 부를 것이다. 그것을 컴파일하면, capacity.class 클래스(파일)이 생길 것이다. |
이 클래스는 독자적으로 어떠한 작동도 행할 수 없으며, 애플릿과 같이, 명령어 행(command line)으로부터 실행되는 main() 메소드를 가지지 않는다. 이 클래스의 객체 (인스턴스)를 생성할 수 있는 템플릿(template)을 생성한 것이다. 이 목적을 이루기 위해, 명령어 행으로부터 실행될 수 있는 클래스를 생성하려고 한다.
코드 | 주석 |
class example { public static void main(String Arg[]){ capacity p1=new capacity(); capacity p2=new capacity(); p1.length = 16; p1.width=3; p1.height=2; // p2.length = 25; p2.width=6; p2.height=3; // p1.CalcVolume(); // p2.CalcVolume(); } } |
capacity 형의 변수가 두 개 정의된다. p1, p2가 그것이다.
new 연산자로 capacity 형의 객체를 두 개 생성하며, p1, p2 변수를 통해 객체들을 참조할 수 있다.
다음으로, 생성된 객체의 변수 각각에 값을 대입한다. p1으로 참조한 객체의 CalcVolume() 메소드를 호출한다. 그러면, 결과로 "Volume: 96"이 화면에 보여진다. p2로 참조한 객체도 동일한 방법으로, "Volume: 450"이 화면에 보연진다. |
p1.CalcVolume()가 실행되는 경우, 자바 해석기가 CalcVolume() 내에 정의된 코드의 제어를 따른다. 일단 모든 제어 문장들이 실행되면 호출한 루틴에 리턴하고 실행은 호출의 바로 다음 행에서 계속된다.
매개변수를 가진 메소드. 값 리턴.
메소드의 대다수는 메소드를 일반화하도록 허용하는 매개변수와 함께 사용된다. 게다가, 메소드는 값을 리턴할 수 있다, 그래서 상이한 상황에서도 사용될 수 있는, 다양한 데이터와 함께 작동 가능한 메소드를 만들 수 있다.
위의 예제를 개선하자:
코드 | 주석 |
class capacity { double CalcVolume (double l, double a, double p) { double volume=l*a*p ; return volume; } } |
CalcVolume 메소드는 세 개의 매개변수를 수령하도록 수정되었다. 또한 double 형을 리턴하도록 정의되었다. 이 작동은 return volume 명령에 의해 수행된다. |
class example { public static void main(String Arg[]){ capacity p1 = new capacity(); capacity p2 = new capacity(); double vol; vol=p1.CalcVolume(10,3,2); System.out.println("Volume: " + vol); // vol=p2.CalcVolume(25,5,2); System.out.println("Volume: " + vol); } } |
메소드의 호출은 원하는 매개변수를 전달하여 행해진다. vol 변수에 메소드의 값을 리턴하는데, 변수는 메소드와 동일한 형이어야 한다. |
클래스의 주요한 관점은 생성자(constructors)이다. 클래스의 객체가 생성될 때, 이 항목들은 어떤 일들이 일어나는가를 정의한다. 이들의 대부분은 클래스 정의 내에 자신의 생성자를 명시적으로 정의한다. 만약 정의되지 않으면, (위의 예제와 같이) 자바는 기본 생성자(default constructor)를 사용한다.
클래스에 포함된 생성자는 정확하게 클래스와 동일한 명칭을 가진다. 문장 구성법은 메소드의 그것과 유사하다. 객체를 생성한 후에 new 연산자가 종료되기 전에 생성자는 자동적으로 실행된다.
생성자는 어떤 자료형도 가지지 않는다. 묵시적으로 클래스의 형을 리턴하기 때문이다.
생성자는 모든 객체 상태의 초기화로 수행된다. 그런데, 객체의 인스턴스를 생성하는 코드는 그것을 이용하는 준비된 객체를 가진다. 사전에, 인스턴스 변수는 생성자에 의해 초기화된다.
이는 메소드와 함께 이뤄지기 때문에, 생성자는 그것들을 월씬 유용하게 만드는 매개변수를 가질 수 있다 . 이 모든 새로운 관점을 연구하기 위해 위의 예제를 수정하도록 하자.
코드 | 주석 |
class capacity { double length; double width; double height; // capacity(double l, double a, double p){ length=l; width=a; height=p; } // void CalcVolume () { double volume ; volume=large*wide*high; return volume; } } |
생성자가 클래스에 추가되는데, 클래스와 동일한 명칭을 가지지만 어떤 자료형도 리턴하지 않는 메소드의 일면을 보인다. 이 생성자는 수령한 인자로 선언된 변수의 인스턴스를 초기화한다. |
class example { public static void main(String Arg[]) { capacity p1=new capacity(10,5,2); capacity p2=new capacity(25,6,2); double vol; vol=p1.CalcVolume(); System.out.println("Volume: " + vol); // vol=p2.CalcVolume(); System.out.println("Volume: " + vol); } } |
new 연산자는 클래스의 인스턴스를 생성한다. 그 클래스의 생성자한테 필요한 매개변수를 전한다. |
만일 객체에 대한 어떤 참조도 존재하지 않는다면, 이 객체는 더 이상 사용될 수 없다고 가정되어, 할당된 메모리가 해제된다. 이 처리는 프로그램 실행을 통해 자동화된 방법으로 수행되기 때문에 명시적으로 객체를 파괴(destroy)할 필요가 없다.
그럼에도 불구하고, 클래스에 소멸자(finisher)를 덧붙이기 위해 메소드 "finalize()"가 사용된다. 해석기가 객체를 파괴할 때 이 메소드가 실행된다. 이 메소드에, 객체를 파괴하기 전에 실행되어지는 작동을 포함시킬 것이다.
메소드 중첩(method overload).
다형성(하나의 인터페이스를 위한 다수의 메소드)는 객체 지향 프로그래밍에서 기본적인 핵심(PILLARS )의 하나이다. 자바는 메소드 중첩에 의해 다형성을 구현한다.
동일한 명칭을 가지는 상이한 메소드들이 클래스 내에서 정의될 수 있지만, 그것들은 상이한 매개변수 목록 또는 최소한 상이한 리턴 자료형을 반드시 가져야 한다. 중첩된 메소드가 호출될 때 사용되어지는 메소드의 버전을 구별하기 위하여 자바 해석기는 자료형 및 호출과 함께 전달된 인자들을 사용한다.
비록 다형성을 완전히 불가능하도록 만들지만, 중첩된 메소드의 각 버전은 상이한 태스크(task)를 수행할 수 있다. 그래서 될 수 있으면 메소드 중첩은 어떤 관계를 암시할 수 있어야 한다. 메소드가 중첩되어지는 것과 동일한 방식으로 생성자도 중첩될 수 있다.
인자 전달.
일반적으로, 프로그래밍 언어는 인자를 전달하기 위해 두 가지 방식을 허용한다:
접근 통제.
객체 지향 프로그래밍에서 또 다른 기본적인 핵심은 캡슐화(ENCAPSULATION)이다. 이것은 데이터가 코드와 연관된다는 것을 의미한다. 더욱이, 캡슐화는 접근 통제를 제공한다. 다시 말해서, 프로그램의 일부는 클래스의 멤버(member)에 접근할 수 있다.
자바에서 접근 지정은 'public', 'private', 'protected'이다.
메소드 또는 인스턴스-변수가 'public'으로 정의될 때, 프로그램의 어떤 부분으로부터도 접근이 가능하다. 만일 'private'으로 정의된다면, 클래스 자체의 다른 메소드에 의해서만 접근이 가능하다. 기본적으로 모든 메소드 또는 인스턴스-변수들은 (그 자신이 속한 패키지에) 'public'이다.
'protected' 지정자는 상속과 함께 작용할 때 사용된다. 상속은 다음 번 주제이다.
A 상위 클래스(superclass)의 특성을 상속하는 클래스인 B를 만들기 위한 B 정의를 보인다:
class B extends A { // class definition } |
클래스 B는 A 상위 클래스의 모든 멤버를 포함하는데, 후자는 자유롭게 사용되는 무명(AUTONOMOUS) 클래스일 수 있다. 또한, 클래스 B는 어떤 다른 클래스의 상위 클래스가 될 수 있다.
자바는 상이한 상위 클래스들로부터 하나의 하위 클래스(subclass)를 만드는 다중 상속(multiple inheritance)을 허용하지 않는다.
만일 상위 클래스에 'private'로 정의된 멤버들이 있다면, 상속된 클래스에 의해 접근이 불가능하다. 하위 클래스가 즉시적으로 상위 클래스를 참조할 필요가 있다면, 예약어 'super'가 사용될 수 있다. 이 방법으로 하위 클래스에 의해 은닉된(hidden) 상위 클래스의 생성자 또는 메소드를 호출할 수 있다.
하위 클래스의 메소드가 상위 클래스의 메소드와 동일한 명칭과 자료형을 가질 때, 메소드가 중복된다(overwritten)고 말한다. 이 특성은 자바의 강력한 일면의 한 기초를 형성한다. "동적 선별 메소드(Dynamic selection method)"라고 부른다. 이는, 매번 호출마다 어느 메소드가 사용되어야 하는지 결정이, 참조된 변수 자료형에 의존하여 적절하게 실행 중(execution time)에 수행된다는 것을 의미한다.
다음 기고에서는 추상 클래스(abstract classes), 인터페이스(interfaces) 등과 같은 상속의 모든 능력을 배울 것이다.
본 웹싸이트는 Miguel Angel Sep�lveda님에 의해 관리됩니다. � Jose M. Fernandez 1998 LinuxFocus 1998 |