[자바의 정석] Chapter 6 객체 지향 프로그래밍 요약(2)
변수와 메서드
선언위치에 따른 변수의 종류
o 변수가 선언된 위치에 따라 변수의 종류가 달라지기에 변수가 어느 영역에 선언되었는지를 확인하는 것이 중요하다.
변수의 종류 |
선언위치 |
생성시기 |
클래스 변수 (class variable) |
클래스 영역 |
클래스가 메모리에 올라갈 때 |
인스턴스 변수 (instance variable) |
인스턴스가 생성되었을 때 |
|
지역 변수 (local variable) |
클래스 영역 이외의 영역 (메서드, 생성자, 초기화 블록 내부) |
변수 선언문이 수행되었을 때 |
1. 인스턴스 변수
클래스 영역에 선언되며, 클래스의 인스턴스를 생성할 때 만들어진다. 그렇기에 인스턴스 변수 값을 읽어 오거나 저장하기 위해서는 먼저 인스턴스를 생성해야 하며 인스턴스는 독립적인 공간을 가지므로 서로 다른 값을 가질 수 있다. 인스턴스마다 고유한 상태를 유지해야하는 속성의 경우, 인스턴스 변수로 선언한다.
2. 클래스 변수
인스턴스 변수 앞에 static을 붙이면 클래스 변수가 되며 인스턴스 변수는 인스턴스 별로 독립적인 공간을 가졌다면 클래스 변수는 모든 인스턴스가 공통으로 공통된 저장공간(변수)를 공유하게 된다. 한 클래스의 모든 인스턴스 들이 공통된 값을 유지해야 하는 경우 클래스 변수를 사용한다. 클래스 변수는 인스턴스를 생성하지 않고도 사용할 수 있는 특징이 있다. 클래스가 메모리에 로딩될 때 생성되어 프로그램이 종료될 때까지 유지되며, public을 앞에 붙이면 같은 프로그램 내에서 어디서나 접근할 수 있는 전역 변수의 성격을 갖는다.
class Tv {
static int tt=0;
}
class TvTest {
public static void main(String args[]) {
int i = Tv.tt; // 인스턴스 생성 없이 사용 }
|
3. 지역 변수
메서드 내에서 선언되어 메서드 내에서 사용되며 메서드가 종료되면 자동으로 소멸된다. 반복문 내에서 선언된 지역변수는 선언된 지역{ } 내에서만 사용이 가능하다.
클래스변수와 인스턴스변수
o 인스턴스 변수는 인스턴스가 생성될 때마다 생성되므로 인스턴스마다 각기 다른 값을 유지할 수 있지만, 클래스 변수는 모든 인스턴스가 하나의 저장공간을 공유하므로, 항상 공통된 값을 갖는다.
o 클래스 변수 사용 시 참조변수를 통해서도 클래스변수를 사용할 수 있지만 이렇게 사용하면 클래스 변수를 인스턴스 변수로 오해할 수 있기에 ‘클래스이름.클래스변수’의 형태로 사용하는 것이 좋다.
package JavaProject;
public class Test {
public static void main(String[] args) {
//클래스는 객체생성 없이 변수 직접 사용 가능
System.out.println("Card.Width = " + Card.width);
System.out.println("Card.Height = " + Card.height);
Card c1 = new Card();
c1.kind = "heart";
c1.number = 7;
Card c2 = new Card();
c2.kind = "Space";
c2.number = 4;
System.out.printf("c1은 %s, %d이며, 크기는 (%d, %d)입니다.\n", c1.kind, c1.number, c1.width, c1.height);
System.out.printf("c2은 %s, %d이며, 크기는 (%d, %d)입니다.\n", c2.kind, c2.number, c2.width, c2.height);
c1.width = 50; //class 변수 값 변경
c1.height = 80;
// 인스턴스 c1을 통해 witdth, height변수를 변경하였으나 클래스 변수이기에 인스턴스 c2에서도 같이 적용된다.
System.out.printf("c1은 %s, %d이며, 크기는 (%d, %d)입니다.\n", c1.kind, c1.number, c1.width, c1.height);
System.out.printf("c2은 %s, %d이며, 크기는 (%d, %d)입니다.\n", c2.kind, c2.number, c2.width, c2.height);
}
}
class Card{
String kind;
int number;
static int width = 100;
static int height = 250;
}
|
- 카드게임의 예로 클래스 변수와 인스턴스 변수의 차이점을 알아본다. 카드는 카드마다 무늬, 숫자는 다르지만 카드 크기(폭, 높이)는 모두 같다.
- 그렇기에 모든 Card 인스턴스는 폭(width), 높이(height)변수는 같은 값을 유지해야 하므로 클래스 변수(static)로 선언하고 무늬(kink), 숫자(number)는 인스턴스 별로 다른 값을 가지기에 인스턴스 변수로 선언하였다.
- 클래스 변수(static)인 width, height는 Card인스턴스를 생성하지 않고 ‘클래스이름.클래스변수’와 같은 방식으로 사용할 수 있고 Card인스턴스인 c1과 c2는 width, height 변수에 대해 같은 저장공간을 참조하므로 항상 같은 값을 갖는다.
메서드
o 메서드(method)는 특정 작업을 수행하는 일련의 문장들을 하나로 묶은 것이다. 어떤 값을 입력하면 이 값으로 작업을 수행한 후 결과를 반환한다.
메서드를 사용하는 이유
1. 높은 재사용성(reusability)
한번 만들어 놓은 메서드는 계속 호출해서 사용할 수 있으며 다른 프로그램에서도 사용이 가능하다.
2. 중복된 코드의 제거
같은 내용의 반복되는 문장들을 하나의 메서드로 묶어 작성하면 반복되는 문장들 대신 메서드 호출 한 번으로 대체할 수 있다. 이러면 코드의 길이도 짧아지고 수정 시 수정하는 코드 양도 줄어들어 오류 발생 가능성을 줄일 수 있다.
3. 프로그램의 구조화
큰 규모의 프로그램의 경우 문장들을 작업 단위로 담아 구조를 단순화시키는 것이 필수다. 나중에 프로그램에 문제가 발생해도 쉽게 찾을 수 있다. 프로그램을 설계할 때 내용이 없는 메서드를 작업 단위로 만들어 놓고, 하나씩 완성해가는 것도 프로그램을 구조화하는 좋은 방법이다.
메서드의 선언과 구현
o 메서드는 크게 ‘선언부(header, 머리)’와 ‘구현부(body, 몸통)’로 이루어져 있다.
메서드 선언부(method declaration, method header)
o 메서드 선언부는 ‘메서드 이름’, ‘매개변수 선언’, ‘반환 타입’으로 구성되어 있다. 이는 메서드가 어떤 값을 필요로 하고 어떤 타입의 결과로 반환하는지에 대한 정보를 제공한다.
매개변수 선언(parameter declaration)
o 매개변수는 메서드가 수행하기 위해 필요한 값들을 입력 받기 위한 것이며 ‘,’로 구분하여 필요한 만큼 선언할 수 있다. 그러나 일반 변수와 달리 두 변수 타입이 같아도 변수 타입을 생략할 수는 없다. 매개변수의 개수는 거의 제한이 없으며 입력해야 할 값이 많을 경우 배열이나 참조변수를 사용하면 된다.
int add(int x, int y) {} // OK
int add(int x, y) {} // 에러, 매개변수 타입을 생략할 수 없다.
|
메서드의 이름
o 메서드는 특정 작업을 수행하므로 add처럼 동사인 경우가 많으며 이름만으로 메서드의 기능을 알 수 있도록 함축적으로 의미 있는 이름을 짓도록 해야한다.
반환타입(return type)
o 메서드의 작업 수행 결과(출력)의 반환 값의 타입(int, double 등)을 적으며 반환 값이 없는 경우 ‘void’를 적는다.
메서드의 구현부(method body)
o 메소드 선언부의 다음에 오는 괄호{ }를 ‘메서드 구현부’라고 한다. 메서드가 호출될 때 수행되는 문장들을 적는다.
return문
o 메서드의 반환타입이 ‘void’가 아닌 경우, 구현부 안에 반환값 ‘return문’이 반드시 있어야한다. 반환값을 호출한 메서드로 전달하는데 이 값의 타입은 반환타입과 일치하거나 자동 형변환이 가능한 것이어야 한다. 입력(매개변수)은 여러 개 일수도 있어도 출력 값은 최대 하나만 허용한다.
지역변수(local variable)
o 메서드 내에서 선언된 변수를 ‘지역 변수’라고 한다. 메서드 내에서만 사용할 수 있기에 서로 다른 메서드라면 같은 이름의 변수를 선언해도 된다. 매개변수 또한 메서드 내에 선언된 것으로 간주되므로 지역변수이다.
메서드의 호출
o 메서드를 호출할 때 괄호( )안에 지정해준 값들은 인자(argument) 또는 인수라고 하는데 인자의 개수와 순서는 호출된 메서드의 매개변수와 일치해야 한다.
o 인자는 메서드 호출 시 자동으로 매개변수에 대입되므로 매개변수와 타입이 같거나 자도 형변환이 가능한 것이어야 한다.
메서드의 실행흐름
o 같은 클래스내의 메서드끼리는 참조 변수를 사용하지 않고도 서로 호출이 가능하지만 static메서드는 같은 클래스 내의 인스턴스 메서드(static이 없는)를 호출할 수 없다.
class MyMath {
long add(long a, long b) {return a+b;}
long subtract (long a, long b) {return a-b;}
long multiply (long a, long b) {return a*b;}
double divide (double a, double b) {return a / b;}
}
class MyMathTest {
public static void main(String[] args) {
MyMath mm = new MyMath();
long result = mm.add(5L, 3L);
long result2 = mm.subtract(5L, 3L);
long result3 = mm.multiply(5L, 3L);
// double대신 long를 인자로주었다. double로 자동 형변환되어 입력된다.
double result4 = mm.divide(5L, 3L);
System.out.println("add(5L, 3L) = " + result);
System.out.println("subtract(5L, 3L) = " + result2);
System.out.println("multiply(5L, 3L) = " + result3);
System.out.println("divide(5L, 3L) = " + result4);
}
}
|
return문
o return문은 현재 실행중인 메소드를 종료하고 호출한 메서드로 돌아간다. 반환값 유무에 상관없이 모든 메서드에는 return이 적어도 한 개는 있어야 하며 반환 타입이 void인 경우 return문이 없어도 문제가 없었던 이유는 컴파일러가 자동으로 메서드 마지막에 return문을 추가해주기 때문이다.
매개변수의 유효성 검사
o 메서드의 구현부{ }를 작성할 때 제일 먼저 해야 하는 일이 매개변수의 값이 적절한 것인지 확인하는 것이다. 예를 들어 나누기 기능이 구현된 메서드의 경우 입력값을 정수형으로 받고 반환값을 실수형으로 반환한다. 그런데 나누는 값을 0으로 받을 경우 나누기를 제대로 할 수 없으므로 return문을 이용해 작업을 중단하고 메서드를 종료시켜야 한다.
o 이렇듯 적절하지 않은 값이 넘어온 경우 매개변수 값을 보정하던지 아니면 return을 사용해 메서드를 중단하던지 해서 유효성을 확인해야 한다.