본문 바로가기
Study/JAVA

[Java] 6-3. 변수와 메서드

by jeongwle 2022. 9. 2.
728x90
반응형

변수와 메서드

1. 선언위치에 따른 변수의 종류

변수는 클래스변수, 인스턴스변수, 지역변수 모두 세 종류가 있다. 변수의 종류를 결정짓는 중요한 요소는 변수가 선언된 위치이다. 멤버변수를 제외한 나머지 변수들은 모두 지역변수이며, 멤버변수 중 static이 붙으면 클래스변수, 붙지 않으면 인스턴스변수이다.

변수의 종류 선언위치 생성시기
클래스 변수
(class variable)
클래스 영역 클래스가 메모리에 올라갈 때
인스턴스 변수
(instance variable)
인스턴스가 생성되었을 때
지역변수
(local variable)
클래스 영역 이외의 영역
(메서드, 생성자, 초기화 블럭 내부)
변수 선언문이 수행되었을 때

1. 인스턴스 변수(instance variable)
클래스 영역에 선언되고 클래스의 인스턴스를 생성할 때 만들어진다. 인스턴스는 독립적인 저장공간을 가지므로 서로 다른 값을 가질 수 있다. 인스턴스마다 고유한 상태를 유지해야할 경우 인스턴스변수로 선언한다.

2. 클래스변수(class variable)
클래스 변수를 선언하는 방법은 인스턴스변수 앞에 static을 붙이는 것이다. 클래스변수는 모든 인스턴스가 공통된 저장공간(변수)을 공유하게 된다. 한 클래스의 모든 인스턴스들이 공통의 값을 유지해야하는 경우 클래스변수로 선언해야 한다. 클래스변수는 인스턴스변수와 달리 인스턴스를 생성하지 않고도 언제라도 바로 사용할 수 있다. 클래스이름.클래스변수와 같은 형식으로 사용한다. 클래스가 메모리에 로딩될 때 생성되어 프로그램이 종료될 때 까지 유지된다. public을 앞에 붙이면 같은 프로그램 내 어디서나 접근할 수 있는 전역변수의 성격을 갖는다.
* 참조변수의 선언이나 객체의 생성과 같이 클래스의 정보가 필요할 때 클래스는 메모리에 로딩된다.

3. 지역변수(local variable)
메서드 내에 선언되어 메서드 내에서만 사용가능하고 메서드가 종료되면 소멸된다. for문 또는 while문의 블럭 내에 선언된 지역변수는 블럭 내에서만 사용가능하고 블럭을 벗어나면 소멸된다.

 

2. 클래스변수와 인스턴스변수

예시로 알아보자
class Card {
  String kind; // 무늬
  int number; // 숫자
  
  static int width = 100; // 너비
  static int height = 250; // 높이
}​

카드의 너비와 높이는 무늬와 숫자에 없이 모든 인스턴스가 공통적으로 같은 값을 유지해야하므로 클래스변수로 선언하였다. 카드의 너비나 높이를 변경해야할 경우 모든 카드를 바꿀 필요 없이 한 카드만 바꾸어도 모든 카드에 적용된다.

 

3. 메서드

메서드(method)는 특정 작업을 수행하는 일련의 문장들을 하나로 묶은 것이다.

메서드를 사용해야하는 이유
1. 높은 재사용성(reusability)
한번 만들어 놓은 메서드는 몇 번이고 호출할 수 있고 다른 프로그램에서도 사용이 가능하다.

2. 중복된 코드의 제거
반복되는 문장들을 묶어서 하나의 메서드로 작성해 놓으면 반복되는 문장을 메서드 호출 한 문장으로 대체할 수 있다. 전체 소스 코드의 길이가 짧아지고 변경사항이 발생했을 경우 수정해야할 코드의 양이 줄어들어 오류가 발생할 가능성도 줄어드는 등의 효과를 얻을 수 있다.

3. 프로그램의 구조화
큰 규모의 프로그램에서는 문장들을 작업단위로 나누어 여러 개의 메서드에 담아 프로그램의 구조를 단순화시키는 것이 필수적이다. 처음 프로그램을 설계할 때 내용이 없는 메서드를 작업단위로 만들어 놓고 하나씩 완성해가는 것도 프로그램을 구조화하는 좋은 방법이다.

 

4. 메서드의 선언과 구현

메서드는 크게 선언부와 구현부로 이루어진다. 메서드를 정의한다는 것은 선언부와 구현부를 작성하라는 말이다.
반환타입 메서드이름 (타입 변수명, 타입 변수명, ...)	// 선언부
// 구현부
{
	// 메서드 호출 시 실행될 코드
}

int add(int a, int b)
{
	int result = a + b;
    return result;
}​

메서드 선언부
메서드의 선언부는 메서드의 이름과 매개변수 선언, 그리고 반환타입으로 구성되어 있다. 메서드가 작업을 수행하기 위해 어떤 값을 필요로 하고 작업의 결과로 어떤 타입의 값을 반환하는지에 대한 정보를 제공한다.

매개변수 선언
매개변수는 메서드가 작업을 수행하기 위해 필요한 값을 제공받기 위한 것이다. 필요한 개수만큼 변수를 선언하고 구분은 쉼표로 한다. 제공받을 정보가 필요없다면 아무것도 적지 않으면 된다.

메서드의 이름
이름만으로도 메서드가 무슨 기능을 수행하는지 쉽게 알 수 있도록 의미있는 이름을 짓도록 노력하자.

반환타입
메서드의 작업 결과인 반환 값의 타입을 적는다. 반환값이 없는 경우에는 반환타입에 void를 적는다.

메서드의 구현부
메서드 선언부 다음 나오는 블럭{}을 메서드의 구현부라 한다. 메서드 호출 시 실행될 문장들을 넣는다.

return문
메서드의 반환타입이 void가 아닌 경우 구현부{} 안에 return 반환값; 이 반드시 포함되어 있어야 한다. 이 값의 타입은 반환타입과 일치하거나 자동 형변환이 가능한 것이어야 한다. 또한 반환값의 개수는 단 하나이어야 한다.

 

5. 메서드의 호출

메서드를 정의했다 하더라도 호출하지 않으면 아무 일도 일어나지 않는다. 메서드를 호출하는 방법은 '메서드이름(값1, 값2, ...);' 이다. 메서드를 호출할 때 괄호()안에 인자의 개수와 순서는 호출된 메서드에 선언된 매개변수와 일치해야 한다.

 

6. return 문

return문은 현재 실행중인 메서드를 종료하고 호출한 메서드로 되돌아간다. 반환타입이 void가 아니라면 반드시 return문을 사용하여야 한다.

메서드의 구현부{}를 작성할 때 매개변수의 값이 적절한 것인지 확인해야한다. 적절하지 않은 매개변수가 넘어왔을 경우 매개변수의 값을 보정해서 쓰고, 보정이 불가능한 경우 return문을 사용해서 작업을 중단해야한다.

 

7. JVM의 메모리 구조

응용프로그램이 실행되면 JVM은 시스템으로부터 프로그램을 수행하는데 필요한 메모리를 할당받고 이 메모리를 용도에 따라 여러 영역으로 나누어 관리한다. 그 중 3가지 주요 영역(method area, call stack, heap)에 대해 알아보자.

1.메서드 영역(method area)
프로그램 실행 중 어떤 클래스가 사용되면 JVM은 해당 클래스의 클래스파일을 분석하여 클래스에 대한 정보(클래스 데이터)를 이곳에 저장한다. 클래스변수도 이 영역에서 생성된다.

2. 힙(heap)
인스턴스가 생성되는 공간이다. 프로그램 실행 중 생성되는 모든 인스턴스는 이곳에 생성된다. 인스턴스변수들이 생성되는 공간이다.

3. 호출스택(call stack 또는 execution stack)
호출스택은 메서드의 작업에 필요한 메모리 공간을 제공한다. 메서드가 호출되면 호출스택에 호출된 메서드를 위한 메모리가 할당되며 메서드가 실행되는 동안 생성되는 지역변수(매개변수 포함)들과 연산의 중간결과 등을 저장하는데 사용된다. 그리고 메서드가 호출종료되면 메모리공간은 반환되어 비워진다. 호출스택의 최상위에 있는 메서드가 현재 실행중인 메서드이고 바로 아래에 있는 메서드가 바로 위의 메서드를 호출한 메서드이다.

 

8. 기본형 매개변수와 참조형 매개변수

자바에서는 메서드를 호출할 때 매개변수로 지정한 값을 메서드의 매개변수에 복사해서 넘겨준다. 매개변수의 타입이 기본형일 때는 기본형 값이 복사되고 참조형이면 인스턴스의 주소가 복사된다. 참조형의 경우 주소를 알 수 있기 때문에 값을 읽는 것 뿐만 아니라 값을 변경할 수 있다.
class Data {
    int x;
}

public class PrimitiveVsReferenceEx {
    public static void main(String[] args) {
        Data d = new Data();
        d.x = 10;

        System.out.println("main x = " + d.x); // 10
        changePrimitive(d.x);
        System.out.println("main x = " + d.x); // 10
        changeReference(d);
        System.out.println("main x = " + d.x); // 1000

    }

    static void changePrimitive(int x) {
        x = 1000;
        System.out.println("changePrimitive x = " + x); // 1000
    }

    static void changeReference(Data d) {
        d.x = 1000;
        System.out.println("changeReference x = " + d.x); // 1000
    }
}​


 

9. 참조형 반환타입

매개변수 뿐만 아니라 반환타입도 참조형이 될 수 있다. 반환값이 객체의 주소가 된다는 말이다.
class Number {
    int x;
}

public class ReferenceReturnEx {
    public static void main(String[] args) {
        Number n = new Number();
        n.x = 10;
        Number n2 = copy(n); // 반환값의 타입이 Number이므로 Number타입 참조형 변수에 저장

        System.out.println("n.x = " + n.x); // 10
        System.out.println("n2.x = " + n2.x); // 10
    }

    static Number copy(Number number) {
        Number tmp = new Number();
        tmp.x = number.x;
        return tmp;
    }
}​


 

10. 재귀호출(recursive call)

메서드 내부에서 메서드 자신을 다시 호출하는 것을 재귀호출(recursive call)이라 한다. 그리고 재귀호출을 하는 메서드를 재귀 메서드라 한다.
void method() {
  method(); // 자기 자신을 호출
}​

재귀호출 또한 조건문을 통해 탈출할 장치를 마련하지 않으면 무한히 자기자신을 호출한다. 반복문과 비교할 때 재귀호출의 수행시간이 더 오래 걸린다. 그럼에도 불구하고 재귀호출로 작성하면 단순한 구조로 바뀌기 때문에 알아보기가 쉬워진다. 재귀호출은 비효율적이므로 재귀호출의 간결함이 주는 이득이 충분히 큰 경우에만 사용하자.

 

11. 클래스 메서드(static 메서드)와 인스턴스 메서드

클래스 메서드도 클래스변수처럼 객체를 생성하지 않고도 클래스이름.메서드이름으로 호출이 가능하다. 반면 인스턴스 메서드는 객체를 생성해야만 호출할 수 있다.

1. 클래스를 설계할 때 멤버변수 중 모든 인스턴스에 공통으로 사용하는 것에 static을 붙인다.
생성된 각 인스턴스는 서로 독립적이기 때문에 각 인스턴스의 변수는 서로 다른 값을 유지한다. 그러나 모든 인스턴스에서 같은 값이 유지되어야 하는 변수는 static을 붙여 클래스변수로 정의해야 한다.

2. 클래스 변수는 인스턴스를 생성하지 않아도 사용할 수 있다.
static이 붙은 변수는 클래스가 메모리에 올라갈 때 이미 자동적으로 생성되어 있다.

3. 클래스 메서드는 인스턴스 변수를 사용할 수 없다.
인스턴스변수는 인스턴스가 반드시 존재해야만 사용할 수 있다. 클래스메서드는 인스턴스 생성 없이 호출이 가능하다. 때문에 클래스 메서드에서 인스턴스변수의 사용을 금지한다. 클래스 메서드를 사용할 때 인스턴스가 없을 수도 있기 때문이다.

4. 메서드 내에서 인스턴스 변수를 사용하지 않는다면 static을 붙이는 것을 고려한다.
메서드 호출시간이 짧아지므로 성능이 향상된다. static을 안 붙인 메서드는 실행 시 호출되어야할 메서드를 찾는 과정이 추가적으로 필요하여 시간이 더 걸린다.

 

12. 클래스 멤버와 인스턴스 멤버간의 참조와 호출

같은 클래스에 속한 멤버들 간에는 별도의 인스턴스를 생성하지 않고 서로 참조 또는 호출이 가능하다. 클래스 멤버가 인스턴스 멤버를 참조 또는 호출하고자 하는 경우엔 인스턴스를 생성해야한다. 그 이유는 위에서 배웠다.

클래스멤버(클래스변수와 클래스메서드)는 언제나 참조 또는 호출이 가능하여 인스턴스멤버가 클래스 멤버를 사용하는 것은 문제가 없다. 클래스멤버간의 참조 또는 호출 역시 문제가 없다. 인스턴스멤버간의 호출도 아무런 문제가 없다. 하나의 인스턴스멤버가 존재한다는 것은 인스턴스가 이미 생성되었다는 것을 의미하고 다른 인스턴스멤버들도 모두 존재한다는 뜻이기 때문이다. 다만 클래스 멤버가 인스턴스 멤버를 참조 또는 호출하기 위해서는 객체를 생성해야 한다.
728x90
반응형

댓글