3. 변수와 메서드
3.1 선언위치에 따른변수의 종류
class Variables
{
int iv; // 인스턴수 변수
static cv; // 클래스 변수
void method() // 메서드 영역
{
int lv = 0; //지역변수
}
}
변수의 종류와 특징
| 변수의 종류 | 선언 위치 | 생성시기 |
| 클래스 변수 | 클래스 영역 | 클래스가 메모리에 올라갔을 때 |
| 인스턴수 변수 | 클래스 영역 | 인스턴스가 생성되었을 때 |
| 지역 변수 | 클래스 영역 이외의 영역 (메서드,생성자, 초기화 블럭 내부) | 변수 선언문이 수행되었을 때 |
- 인스턴스 변수(instance variable)
- 클래그 영역에 선언, 클래스의 인스턴스를 생성할 때 만들어짐
- 인스턴스 변수의 값을 읽거나 저장하기 위해서는 먼저 인스턴스를 생성해야 한다
- 인스턴스는 독립적인 공간을 가지므로 서로 다른 값을 가질 수 잇따
- 클래스 변수(class variable)
- 인스턴스 변수 앞에 static만 붙이면 된다
- 독립적인 저장공간을 가지는 인스턴스 변수와 달리 클래스 변수는 모든 인스턴스가 공통된 저장공간을 공유하게 된다.
- 클래스 변수는 인스턴스변수와 달리 인스턴스를 생성하지 않고도 언제라도 바로 사용할 수 있다.
- 클래스이름.클래스변수 의 형태로 사용
- 클래스가 메모리에 로딩될 때 생성되어 프로그램 종료까지 유지되며, public을 앞에 붙이면 같은 프로그램 내에서 어디서나 접근 가능한 전역변수(global variable)의 성격을 갖는다.
- 지역 변수(local variable)
- 메서드 내에서 선언되어 메서드 내에서만 사용 가능
- for,while문의 블럭 내에 선언된 지역변수는 지역변수가 선언된 블럭{} 내에서만 사용 가능
3.2 클래스변수과 인스턴스 변수
- 게임 카드 클래스의 예
- 속성 - 카드의 무늬, 숫자, 폭, 높이 中 어떤 것이 인스턴스? 클래스?
class Card { String kind; // 무늬 int number; //숫자 static int width = 100; //폭 static int height = 250 // 높이 }- 인스턴스 변수 - 각자 자신만의 무늬, 숫자를 유지해야함
- 클래스 변수 - 각 카드의 폭과 높이는 모든 인스턴스와 동일하게 유지해야함
- 카드의 폭을 변경해야 하는 경우 width값만 변경해도 모든 카드의 width가 변한다.

3.3 메서드
- 메서드는 특정 작업을 수행하는 일련의 문장들을 하나로 묶은 것이다. (수학의 함수와 유사)
메서드를 사용하는 이유
1. 높은 재사용성
- 몇 번이든 호출 할 수 있다
2. 중복된 코드의 제거
- 프로그램을 작성하다보면 같은 내용읨 문장이 여러번 나올 때가 있다.
- 이 문장을 하나의 메서드로 작성하면 그 문장들을 한 문장의 메서드로 대체할 수 있다
- 오류가 발생할 가능성이 적어지고, 유지보수가 간편해진다
3. 프로그램의 구조화
class Ex{
public static void main(String args[])
{
int[] numArr = new int[10];
initArr(numArr);
printArr(numArr);
sortArr(numArr);
printArr(numArr);
}
}
→ 이와 같이 작업단위를 메서드로 나누어서 만들면 main 메서드가 간단해진다.
- 프로그램을 처음 설계할 때 빈 메서드 작업단위로 만들어놓고 하나씩 완성해 나가는 것도 좋다.
3.4 메서드의 선언과 구현
반환타입 메서드이름(타입 변수명, 타입 변수명)
{
}
//
int add(int a, int b)
{
int result = a + b;
return result; // 호출한 메서드로 결과를 반환한다.
}
매개변수 선언
- 메서드가 작업을 수행하는데 필요한 값들을 입력받기 위한 공간, 쉼표(’,’)로 구분한다
int add(int x, int y){}
int add(int x,y) // 같은 타입의 변수라도 다음과 같이 생략할 수 없다.
메서드의 이름(method name)
- 이름만으로 메서드의 기능을 알기 쉽도록 함축적, 의미 있는 이름을 지어야 한다
반환타입(return type)
- 메서드의 작업 수행 결과(출력)인 반환값의 타입을 적어야 한다. 없는 경우 void로 적는다
void print99danAll()
{
for(int i = 1; i <= 9; i++)
{
for(int j = 2; j <= 9; j++)
{
System.out.print(j+"*"+i+"="+(j*i)+" ");
}
System.out.println();
}
}
메서드의 구현부(method)
- {}내의 문장인데 메서드를 호출했을 때 수행될 문장들을 적는다
return문
- 메서드 타입이 void가 아닌 경우, 구현부{} 내에 ‘return 반환값’이 반드시 포함되어 있어야 한다.
- 이 값의 타입은 반환타입과 일치하거나 적어도 형변환이 가능해야 한다.
- return문은 단 하나의 값만 반환할 수 있다.
지역변수(local variable)
- 메서드 내의 선언된 변수들은 그 메서드 내에서만 사용할 수 있으므로 서로 다른 메서드라면 같은 이름의 변수를 선언해도 된다. 이처럼 메서드 내에 선언된 변수를 ‘지역변수’라고 한다
3.5 매서드의 호출
print99danAll(); // void print99danAll()을 호출
int result = add(3,5); int add(int x, int y)를 호출하고, 결과를 result에 저장
인자(argument)와 매개변수(parameter)
- 메서드를 호출할 때 괄호() 안에 지정해준 값들을 ‘인자(argument)’또는 ‘인수’라고 하는데, 인자의 개수와 순서는 호출된 메서드에 선언된 매개변수와 일치해야한다.
메서드의 실행흐름
- 같은 클래스 내의 메서드끼리는 참조변수를 사용하지 않고 서로 호출 가능
- static 메서드는 같은 클래스 내의 인스턴스 메서드를 호출할 수 없다
class MyMathTest{
public static void main(String args[]){
MyMath mm = new MyMath();
long result1 = mm.add(5L, 3L);
System.out.println(result1);
}
}
class MyMath{
long add(long a, long b){ return a + b;}
}
- 이 코드와 같이 main메서드에서 MyMath 내의 메서드를 실행하려는 경우, MyMath 클래스의 인스턴스를 생성한 후 참조변수 mm을 통해서 해야한다.
3.6 return문
- return 문은 현재 실행중이 메서드를 종료하고 호출한 메서드로 돌아간다.
- void 문에 return문을 쓰지 않고도 문제가 없는 이유는 컴파일러가 메서드 마지막에 자동적으로 return;을 추가해주기 때문이다.
void printGugudan(int dan){
for(int i = 1; i <= 9; i++){
System.out.printf("%d * %d = %d%n", dan, i, dan * i);
}
return; // 반환타입이 void일 경우 생략가능
}
- 반환타입이 void가 아닌 경우, 반드시 return 문을 적어주어야 한다.
- return문이 없으면 컴파일 에러 (error: missing return statement)가 발생한다
- 아래의 코드는 두 값 중에서 큰 값을 반환하는 메서드이다. if 조건식의 결과에 따라 return문이 실행되지 않는 경우 error가 발생한다.
int max(int a, int b){
if(a > b)
return a;
}
//위의 코드가 아닌 아래의 코드로 작성해주어야 error가 발생하지 않는다.
int max(int a, int b){
if(a > b)
return a;
else
return b;
}
반환값(return value)
int add(int x, int y){
int result = x + y;
return result;
}
//와 같은 형태를 아래와 같이 나타낼 수 있다.
int add(int x, int y){
return x + y;
}
- return문의 반환값으로 주로 변수가 오지만, 수식이 올 수도 있다
매개변수의 유효성 검사
float divide(int x, int y) {
//작업을 하기 전에 나누는 수가 0인지 확인한다
if(y == 0)
{
System.out.println("0으로 나눌 수 없습니다");
return 0; // 매개변수가 유효하지 않으므로 메서드를 종료한다.
}
return x / (float) y;
}
- 메서드의 구현부{}를 작성할 때 가장 먼저 해야할 일은 매개변수의 값이 적절한 것인지 확인하는 것이다.
- 타입만 맞으면 어떤 값이든 메서드로 넘어오기 때문에 모든 경우의 수에 대해 고민하고, 그에 대비한 코드를 작성해야 한다.
3.7 JVM의 메모리 구조
1. 메서드 영역
- 프로그램 실행 중 어떤 클래스가 사용되면, JVM은 해당 클래스의 클래스파일을 읽어서 클래스에 대한 정보를 메서드 영역에 저장한다. 이 때, 그 클래스의 클래스 변수도 이 영역에 저장된다
2. 힙(Heap)
- 인스턴스가 생성되는 공간
- 프로그램 실행 중 생성되는 인스턴스는 모두 이곳에 저장된다.
3. 호출 스택(call stack 또는 execution stack)
- 호출 스택은 메서드의 작업에 필요한 메모리 공간을 제공한다
- 메서드가 호출되면 호출스택에 호출된 메서드를 위한 메모리가 할당된다
- 메서드가 작업을 수행하는 동안 지역변수(매개변수 포함)들과 연산의 중간결과 등을 저장하는데 사용된다.
- 메서드가 작업을 마치면 할당되었던 메모리공간은 반환되어 지워진다.

3.8 기본형 매개변수와 참조형 매개변수

자바에서는 메서드를 호출할 때 매개변수로 지정한 값을 메서드의 매개변수에 복사해서 넘겨준다.
-
- 기본형 - 기본형 값 복사
- 참조형 - 인스턴스의 주소 복사
class Data { int x;}
class PrimitiveParamEx {
public static void main(String args[]){
Data d = new Data();
d.x = 10;
System.out.println("main() : x = " + d.x); // 현재 d.x의 값
change(d.x); // d.x을 1000으로 변경 실행
System.out.println("After change(d.x)"); // d.x 실행 이후를 뜻하는 문장
System.out.println("main() : x = " + d.x); // 바뀐후의 d.x
}
static void change(int x)
{
x = 1000;
System.out.println("change() : x = " + x); // 메서드 내 change의 실행 결과
}
}

위 코드의 실행 흐름
- change 메서드가 호출되면서 ‘d.x’가 change 메서드의 매개변수 x에 복사됨
- change 메서드에서 x의 값을 1000으로 변경
- change 메서드가 종료되면서 매개변수 x는 스택에서 제거됨
→ 즉 d.x의 값이 변경된 것이 아닌 change 메서드의 매개변수 x의 값만 변경된 것이다.
참조 방법
class Data { int x;}
class PrimitiveParamEx {
public static void main(String args[]){
Data d = new Data();
d.x = 10;
System.out.println("main() : x = " + d.x); // 현재 d.x의 값
change(d.x); // d.x을 1000으로 변경 실행
System.out.println("After change(d)"); // 참조변수 d 기입
System.out.println("main() : x = " + d.x); // 바뀐후의 d.x
}
static void change(Data d) // int 값이 아닌 참조변수 d를 입력한다
{
d.x = 1000;
System.out.println("change() : x = " + x); // 메서드 내 change의 실행 결과
}
}
실행 흐름
- change 메서드가 호출되면서 참조변수 d(주소)의 값이 매개변수 d에 복사됨, 이제 매개변수 d에 저장된 주소값으로 x에 접근 가능
- change 메서드에서 매개변수 d로 x의 값을 1000으로 변경
- change 메서드가 종료되면서 매개변수 d는 스택에서 제거됨
class PrimitiveParamEx {
public static void main(String args[]){
int[] x = {10}; // 크기가 1인 배열 선언, x[0] = 10;
System.out.println("main() : x = " + x[0]);
change(x);
System.out.println("After change(x)");
System.out.println("main() : x = " + x[0]);
}
static void change(int[] x) // 참조형 매개변수
{
x[0] = 1000;
System.out.println("change() : x = " + x[0]);
}
}
- 임시적으로 간단하게 처리할 떄는 별도의 클래스를 선언하는 것보다 배열을 이용할 수 있다
반환값이 있는 메서드를 반환값이 없는 메서드로 바꾸는 방법
int add(int a, int b){
return a + b;
}
// 배열을 선언하여 아래와 같이 작성할 수 있다.
void add(int a, int b, int[] result){
result[0] = a + b;
}
3.9 참조형 반환타입
- 매개변수가 아닌 반환타입도 참조형이 될 수 있다
class Data { int x;}
class PrimitiveParamEx {
public static void main(String args[]){
Data d = new Data();
d.x = 10;
Data d2 = copy(d);
System.out.println("d.x =" +d.x);
System.out.println("d2.x =" +d2.x);
}
static Data copy(Data d)
{
Data tmp = new Data();
tmp.x = d.x;
return tmp;
}
}
- copy 메서드를 호출하면서 참조변수 d의 값이 매개변수 d에 복사된다.
- 새로운 객체를 생성한 다음, d.x에 저장된 값을 tmp.x에 복사한다.
- copy메서드가 종료되면서 반환한 tmp의 값은 참조변수 d2에 저장된다.
- copy 메서드가 종료되어 tmp가 사라졌지만, d2로 새로운 객체를 다룰 수 있다.

3.10 재귀호출
- 메서드 내부에서 메서드 자신을 다시 호출하는 것을 ‘재귀호출(recursive call)’이라고 한다.
void method(){
method() //재귀호출. 메서드 자신을 호출한다
}
→ 재귀 호출문이 오로지 재귀 호출이면 무한히 반복하므로 조건이 필요하다
void method(int n){
if(n == 0)
return;
System.out.println(n);
method(--n); //재귀호출. method(n)을 호출
}
- 이코드는 매개변수 n을 1씩 감소시켜주면서 재귀호출을 하고, n값이 0이되면 재귀호출을 중단한다.
- 재귀호출은 반복문과 유사한 점이 ㅁ낳으며, 대부분의 재귀호출은 반복문으로 작성하는 것이 가능하다
void method(int n) {
while(n! = 0){
System.out.println(n--);
}
}
→ 그러나 반복문은 그저 같은 문장을 반복해서 수행하는 것이지만, 재귀함수는 호출,복사,종료 등 반복문보다 수행시간이 더 오래걸린다.
재귀호출을 사용하는 이유
- 재귀호출이 주는 논리적 간결함 때문에 사용
- 효율적이라도 코드를 알아보기 어려운것보다 비효율적이어도 알아보기 쉽게 작성하는 것이 논리적 오류가 발생할 확률이 줄고 수정하기에 좋다.
class FactorialTest{
public static void main(String args[]){
int result = factorial(4);
System.out.println(result);
}
static int factorial(int n){
int result = 0;
if(n == 1) result = 1;
else result = n * factorial(n-1);
return result;
}
}
- facorial 함수를 아래와 같이 더 간단히 할 수 있다.
static int factorial(int n){
if(n == 1) return 1;
return n * factorial(n-1);
}
재귀함수가 무한히 호출된다면?

→ 매개변수 유효성 검사의 중요성
static int factorial(int n){
if(n <= 0 || n > 12) return -1;
if(n ==1) return 1;
return n * factorial(n - 1);
}
→ n의 상한값을 12로 정한 이유 → int의 최대값이 약 21억까지이므로
예제
class PowerTest{
public static void main(String args[])
{
int x = 8, n = 3;
int total = 0;
for(int i = 1; i <= n;i++ ) total += power(x, i);
System.out.println(total);
}
static int power(int x, int n)
{
if(n == 1) return x;
return x * power(x, n-1);
}
}

3.11 클래스 메서드와 인스턴스 메서드

어떤 것을 클래스 메서드로?
- 인스턴스 메서드 - 인스턴스 변수와 관련된 작업을 하는, 즉 메서드의 작업을 수행하는데 인스턴스 변수를 필요로 한다
- 클래스 메서드 - 인스턴스와 관계없는 메서드를 클래스 메서드로 정의한다.
중요
1. 클래스를 설계할 때, 멤버변수 중 모든 인스턴스에 공통으로 사용하는 것에 static을 붙인다.
- 모든 인스턴스에서 같은 값이 유지되어야 하는 변수는 static을 붙여서 클래스변수로 정의해야한다.
2. 클래스 변수는 인스턴스를 생성하지 않아도 사용할 수 있다.
- static이 붙은 변수는 클래스가 메모리에 올라갈때 이미 자동적으로 생성된다
3. 클래스 메서드(static메서드)는 인스턴스 변수를 사용할 수 없다.
- 인스턴스 변수는 인스턴스가 반드시 존재햐만 사용할 수 있는데, 클래스메서ㄴ드는 인스턴스 생성 없이 호출이 가능하므로 클래스 메서드가 출되었을 때 인스턴스가 존재하지 않을 수도 있다.
- 반면에 인스턴스변수나 인스턴스 메서드에서는 static이 붙은 멤버들을 사용하는 것이 가능하다. 인스턴스 변수가 존재한다는 것은 static 변수가 존재한다는 것은 이미 메모리에 존재한다는 것을 의미한다
4. 메서드 내에서 인스턴스 변수를 사용하지 않는다면, static을 붙이는 것을 고려한다.
- 인스턴스 변수를 필요로하지 않는다면 static을 붙이자. 메서드 호출시간이 짧아지므로 성능이 향상된다. static을 붙이지 않은 인스턴트 메서드는 실행 시 호출되어야할 메서드를 찾는 과정이 추가되어 시간이 더 걸린다.
예제
class MyMath2{
long a, b;
long add() { return a + b;} // a,b는 인스턴스 변수
static long add(long a, long b){ return a + b;} // a,b 는 지역변수
}
class MyMathTest2{
public static void main(String args[]){
System.out.println(MyMath2.add(4L, 2L)); // 클래스메서드 호출, 인스턴스 생성 없이 호출 가능
MyMath2 mm = new MyMath2(); // 인스턴스를 생성
mm.a = 3L;
mm.b = 4L;
// 인스턴스메서드는 객체생성 후에만 사용가능함
System.out.println(mm.add());
}
}
3.12 클래스 멤버와 인스턴스 멤버간의 참조와 호출
- 같은 클래스에 속한 멤버들 간에는 별도의 인스턴스를 생헝하지 않고도 서로 참조 또는 호출이 가능하다.
- 단 클래스 멤버가 인스턴스 멤버를 참조 또는 호출하고자 하는 경우는 인스턴스를 생성해야 한다.
- 이유 : 인스턴스 멤버가 존재하는 시점에 클래스 멤버는 항상 존재하지만, 클래스멤버가 존재하는 시점에 인스턴스 멤버가 존재하지 않을 수도 있기 때문이다
class TestClass{
void instanceMethod(){} // 인스턴스 메서드
static void staticMethod(){} // static 메서드
void instanceMethod2(){
instanceMethod(); */이 인스턴스 메서드가 사용되려면, 이미 인스턴스가 만들어진 상태
이기 때문에 인스턴스 메서드 호출이 가능하다*/
staticMethod(); // 호출 가능
}
static void staticMethod2(){
instanceMethod(); */ 에러!! 클래스메서드가 외부에서 사용될때, 인스턴스가 존재하지
않는 상태일 수도 있다*/
staticMethod(); // 호출 가능
}
}
- 변수와 메서드 간의 관계도 마찬가지이다.
class TestClass{
int iv; // 인스턴스 변수
static int cv; // 클래스 변수
void instanceMethod(){
System.out.println(iv); // 사용 가능
System.out.println(cv); // 사용 가능
}
static void staticMethod(){ // static 메서드
System.out.println(iv); // 에러, 인스턴스 변수를 사용할 수 없다
System.out.println(cv); // 클래스 변수는 사용할 수 있다.
}
}
'Java > Java 스터디' 카테고리의 다른 글
| Chapter 07. 객체지향 프로그래밍 II (part 1 상속) (0) | 2023.06.30 |
|---|---|
| Chapter 06. 객체지향 프로그래밍 I (part 4~6) (1) | 2023.03.13 |
| Chapter 06. 객체지향 프로그래밍 I (part 1~2) (0) | 2023.02.21 |
| Chapter 05. 배열 (0) | 2023.02.15 |
| Chapter 04. 조건문과 반복문 (0) | 2023.02.15 |