본문 바로가기

프로그래밍/JAVA

[JAVA] 변수와 메서드(variables, method)

JAVA의 정석(2nd Edition) (남궁 성 著) 164p~187p 를 참조해 코드를 작성했으며

개인적인 공부 내용을 적은 것이므로 오류가 있을 수 있습니다.


0. 들어가기에 앞서


오늘의 공부파트


static 이 붙으면 클래스변수, 클래스메서드가 될 수 있다.

아무것도 없으면 인스턴스변수, 인스턴스메서드 이다.


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

변수가 "클래스 영역"에 선언될 때와, "메서드 영역"에 선언될 때의 차이를 느끼자.


1. 인스턴스 변수(instance variable)


클래스 영역에 선언되며, 클래스의 인스턴스를 "생성"할 때 ( new 연산자 이용) 만들어진다.

따라서 인스턴스 변수 값을 읽거나 저장하기 전에는 먼저 인스턴스를 생성해야 한다.


누누히 말했듯이, 인스턴스는 메모리의 빈 공간에 저장된다.

만약 인스턴스를 10개 생성했다면, 10개의 독립적인 저장공간이 발생하는 것이다.

따라서 인스턴스마다 고유한 상태를 유지해야할 경우, 인스턴스 변수로 선언한다.


EX) 신용카드를 발급할 때 생각해보자. 카드마다 서로 다른 카드번호를 가져야 한다.

이러한 경우 인스턴스 변수를 선언한다.


2. 클래스 변수(class variable)


"static 변수 = 클래스 변수"


인스턴스 변수 앞에 static을 붙이기만 하면 된다. 독립적인 저장공간을 갖는 것이 인스턴스 변수라면

클래스변수는 모든 인스턴스가 공통된 저장공간(변수)을 공유한다.


EX) 신용카드를 발급할 때 생각해보자. 카드마다 똑같은 가로, 세로의 규격을 가져야 한다.

누구는 별 모양이고, 누구는 원 모양이면 카드 단말기에서 안 읽힐 것이다.


인스턴스 변수는 인스턴스를 생성 후에 사용가능한 반면에

(이유 : 인스턴스 마다 서로 다른 "상태"를 가져야 하기 때문이다.)


클래스 변수언제라도 바로 사용할 수 있으며, "클래스이름.클래스변수"의 형식으로 사용한다.


............................................................................... 추가 설명 ........................................................................................

1. 클래스 변수의 용도는 아래와 같다.


1. 인스턴스에 따라서 변하지 않는 값이 필요한 경우

2. 인스턴스를 생성할 필요가 없는 값을 클래스에 저장하고 싶은 경우

3. 값의 변경사항을 모든 인스턴스가 공유해야 하는 경우


2. 클래스가 로딩될 때 클래스 변수가 생성되며, 프로그램 종료시 까지 유지된다.


3. 클래스 변수 앞에 public을 붙이면, 프로그램 내 어디서나 접근할 수 있는 전역변수가 된다.

EX) public static variable = 10;


4. 클래스 변수가 초기화되는 시점은 JVM에 의해서 클래스가 메모리 공간에 올라가는 순간

...........................................................................................................................................................................................


3. 지역변수(local variable)


메서드 내에 선언되어서 메서드 내에서만 사용 가능하다. (생성자와 초기화 블럭 포함)

메서드가 종료되면 지역변수는 사용할 수 없다.


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


신용카드의 크기는 전 세계 어디나 똑같다. 가로 8.6cm, 세로 5.35cm이다.

그 이유는 ISO 국제표준규격을 따르기 때문이다.

카드마다 서로 크기가 다르면, ATM기계나, 지갑을 만드는 회사들은 골치를 겪을 것이다.


그러면 위 예제에서, 어느 것을 클래스 변수로 정해줘야 할까?

가로 8.6cm, 가로 5.35cm를 static 변수(클래스 변수)로 정해줘야 한다!!


그러면 코드로 바로 만들어보자!

서로 다른 상태를 가진 인스턴스를 생성하더라도 "가로"와 "세로" 높이는 똑같아야 한다.

따라서 static을 붙여 모든 인스턴스가 공통된 값을 갖도록 "클래스변수"로 만들었다.


현대카드에서 이종석의 카드와 김우빈의 카드를 만들어주었다.


만약에! 국제표준기구(ISO)가 미쳐서 카드의 규격을 바꿔야 한다고 한다면? 

  "HyundaeCard(클래스이름).width or height(클래스변수)"로 변경하면 된다.


현대카드에서 이종석의 스페셜 에디션 VVIP 카드를 만들어 주려고 한다면?  

  "personA(참조변수).width(클래스변수)"로 변경하면 된다.


그런데, "참조변수.클래스변수"로 하면, 클래스변수를 인스턴스 변수로 오해할 수 있기 때문에 주의하자.

  personA.width라고 하면 다른 클래스가 갖고 있는 static 변수인지,

  나의 클래스가 갖는 변수인지 알 수 없다.



다시 새기자. 클래스 변수는 static을 붙여서 모두가 공유 가능!

인스턴스 변수는 인스턴스 생성후 사용 가능!!

클래스이름.클래스변수로 사용 가능!!


3. 메서드


이제 위 그림에서 아래 부분에 해당하는 "메서드"를 살펴 볼 것이다.



 1.  변수(필드)가 물체의 상태라면, 메소드는 물체의 행동에 해당한다. 명령이라고도 할 수 있겠다.


  소문자로 시작되며, 둘 이상의 단어를 조합해야 하는 경우 새 단어는 대문자로 시작한다. EX) getName


int channel = 7;

void channelUp () { ++channel ; }


둘 중 어느 것이 메서드인가? 

리턴타입이 void이고, 매개변수는 없는 void ChannelUp()이 메서드이다.


좀 더 자세하게 설명하면,

void channelUp() 부분이 

{  ++ channel ; } 부분이 구현부라고 한다.

이렇게 선언부와 구현부가 있는 것을 "메서드"라고 한다.


2. 메서드는 function(기능)이다. 

메서드는 어떤 작업을 수행하기 위한 명령문의 집합이다. 


위 그림과 비교해서 비교를 해보자.


괄호 ( int a, int b )에 선언된 매개변수는 지역변수로 간주되어 메서드 내에서만 사용할 수 있다.


여기서 매개변수(Parameter)와 인자(Argument)를 비교해야 할 필요가 있다.

          • 매개 변수( Parameter ) : 그 전달된 인자를 받아들이는 변수를 의미한다.
          • 인자( Argument ) : 어떤 함수를 호출시에 전달되는 값을 의미한다.


4. 리턴문 (return)


1. 반환값이 없는 경우 : return

2. 반환값이 있는 경우 : return 반환값 (이 경우 반환값은 리턴타입과 일치해야 한다.)


5. 메서드의 호출


1. 참조변수.메서드이름(              );  매개변수가 없는 경우

2. 참조변수.메서드이름(값1, 값2, ...);  매개변수가 있는 경우


일단 메서드를 호출하려면 그 전에 메서드가 정의되어 있어야 한다.

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; }
}


add, subtract와 같은 메서드를 호출하기 위해서는 MyMath클래스의 인스턴스(객체)를 생성해야 한다.


MyMath aa = new MyMath();

이런식으로 MyMath 클래스의 인스턴스를 먼저 생성한 뒤,

참조변수 aa를 통해 메서드 호출이 가능하다.


님이라는 글자에 점하나 찍으면 남이 되듯이

"참조변수에 점하나를 찍으면" 메서드 호출이 가능하다. 이를 도식화해보자.


aa.add(1, 3)를 하면 4가 나올 것이다. 하지만 이 4을 어떻게 우리가 볼 수 있는가?

다른 값에 담아줘야 한다. long answer = aa.add ( 1 , 3 )을 통해 answer에 저장뒤

answer를 출력하면 정답이 나온다.


................................................. 심화내용...............................................................................................


좀 더 심화를 하면, 우리가 자주 사용하는 <String 클래스>가 있다.

쉽게 말해 문자열 클래스라고 생각하면 되는데, 이는 활용빈도가 높아서 위 그림과 달리

인스턴스를 생성하지 않아도 바로 사용할 수 있다.

예를 들어, str.lenght (길이), str.concat (결합) 이렇게 말이다.

................................................................................................................................................................


6. JVM의 메모리 구조



1. 메서드영역(Method Area)


클래스가 사용되면 해당 클래스의 클래스파일(*.class)를 읽어서 분석한 뒤 클래스 정보를 이곳에 저장


클래스를 하나 만들때 마다 *.class파일이 하나 생겨난다.


여기에서 우리가 위에서 배운 클래스변수(static 변수)도 이 영역에 함께 생성된다.

클래스가 로딩될 때 클래스 변수가 생성되며, 프로그램 종료시 까지 유지된다.


★2. 호출스택(call stack)


호출스택에 호출된 메서드의 작업에 필요한 메모리가 할당되며, 메서드가 작업을 수행하는 동안 

지역변수 및 연산의 중간결과 등을 저장한다. 메서드가 작업을 마치면 메모리공간은 반환된다.


 호출스택의 특징

1. 메서드가 호출되면 수행에 필요한 만큼의 메모리를 스택에 할당받는다.

2. 메서드가 수행을 마치고 나면 사용했던 메모리를 반환하고 스택에서 제거된다.

3. 호출스택의 제일 위에 있는 메서드가 현재 실행 중인 메서드이다.

4. 아래에 있는 메서드가 바로 위의 메서드를 호출한 메서드이다.

뭐 이렇게 적립되는 형식일 것이다.


3. 힙(Heap)


인스턴스가 생성되는 공간이다. 즉, 인스턴스 변수들이 생성되는 공간이다.



변수가 생성되는 곳만 따져서 도식화하면 아래의 그림과 같다.

실제로는 ( 클래스 ⊃ 클래스변수, 인스턴스 ⊃ 인스턴스변수, 메서드 ⊃ 지역변수이다).


1. 클래스가 로딩될떄 클래스변수가 생성되며 프로그램 종료시까지 유지된다.

(말 그대로 클래스가 쩌는 놈이다.)



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


class MyMath {
long add(long a, long b) { return a+b; }
long subtract(long a, long b) { return a-b; }
long multiplay(long a, long b) { return a*b; }
double divide(double a, double b) { return a/b; }
}

위 예제를 다시 돌아가보자. 매개변수는 어떤 것인가?

메서드 (   ) 이 괄호 안에 있는 것들이 매개변수일 것이다.


이 매개변수에는 타입이 두 개가 존재한다.


① 기본형(primitive) 매개변수 - 변수의 값을 "읽기"만 가능 (단순히 저장된 값만 얻음)

② 참조형(reference) 매개변수 - 변수의 값을 "읽고" , "변경" 가능 (값이 저장된 곳의 "주소"를 얻음)


기본형으로 선언하면 단순히 저장된 값을 얻어오는 것이지만

참조형으로 선언하면 값이 저장된 곳의 주소를 알 수 있기 때문에 읽기 뿐만 아니라 변경도 가능하다.


그러면 예제에서 그 차이를 살펴보도록 하자.




8. 재귀호출


이정도 했으면 메서드가 무엇인지 대충 감을 잡았을 것이다.

메서드 중에서는 메서드 내부에서 자기 자신을 다시 호출하는 메서드가 있다. 

이를 재귀호출(recursive call)이라고 한다.


재귀호출은 반복적으로 메서드를 호출하는 것이기 때문에 추가비용이 발생한다. 

따라서 간단한 코드를 작성하는 것이 좋다.



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



<변수>

클래스 변수를 만드려면 인스턴스 변수 앞에 static을 붙이면 된다고 했다.

클래스 변수를 호출할 때는 "클래스이름.클래스변수"


<메서드>

메서드 역시 마찬가지다. 메서드 앞에 static을 붙이면 클래스 메서드가 되고

객체를 생성하지 않고도 "클래스이름.메서드이름(매개변수)"로 호출이 가능하다.


클래스 메서드 : 인스턴스와 관계없는 메서드

인스턴스 메서드 : 메서드의 작업을 수행하는데 인스턴스변수를 필요로 하는 메서드.


그러면 어느 경우에 클래스 변수를 선언해야 할까?

인스턴스와 관계없는(인스턴스 변수, 인스턴스 메서드) 메서드를 클래스 메서드로 정의하면 된다.

여기까지 이야기해서는 잘 와닿지 않을 것이다.


 인스턴스 메서드는 찌질이다. 우리 형(MyMath)을 부르고 나서야 사용이 가능하다.

클래스 메서드는 역시 클래스가 있다. 그냥 맘대로 사용이 가능하다.


이 멋있는 클래스 메서드는 다음과 같은 특징이 있다.


1. 클래스 설계시, 멤버변수 중 모든 인스턴스에 공통적으로 사용해야 하는 것에 static을 붙인다.


2. 클래스변수(static)는 인스턴스를 생성하지 않고도 사용할 수 있다.


3. 클래스메서드(static)는 인스턴스 변수를 사용할 수 없다.

   - 인스턴스 변수는 인스턴스 생성 후에 사용가능하다.
   - 그러나 static이 붙은 메서드는 인스턴스 생성 없이 호출가능하므로 클래스 메서드가 호출되었을 때 인스턴스가 존재할 수도 있고 존재하지 않을 수도 있다. 그래서 클래스메서드에서 인스턴스변수의 사용을 금지한다.


4. 메서드 내에서 인스턴스변수를 사용하지 않는다면, static을 붙이는 것을 고려한다

   - 메서드 호출시간이 짧아져서 효츌이 높아진다.


여기서 매개변수는 지역변수이고, 인스턴스변수와 다르다는 것을 인지해야 한다!!!!!

위 예제를 다시 살펴보면, 인스턴스 변수 없이 작업하는 게 어떤 것이 있는가?


static long add(long a, long b) { return a + b };

static long subtract(long a, long b)  { return a - b };

static long multiply(long a, long b)  { return a * b };

static double divide(long a, long b)   { return a / b };


여기서 메서드의 매개변수 "지역변수" 이다. 인스턴스 변수가 아니다.

인스턴스 변수 없이 작업할 수 있는것은 static으로 클래스메서드를 만들어버리자.


위에서 long a; long b; 를 선언했다고 해서 이 인스턴스 변수를 사용하는 것이 아니다.

매개변수를 통해서 사용자가 직접 전달해주는 지역변수 값이다.



10. 클래스멤버와 인스턴스멤버간의 호출


같은 클래스에 속한 멤버간에는 인스턴스를 생성하지 않고도 서로 참조, 호출이 가능하다.

단, 클래스멤버가 인스턴스멤버(변수, 메서드)를 참조, 호출하고자 할 때는 인스턴스를 생성해야 한다.


WHY? 인스턴스멤버(변수, 메서드)가 존재하는 시점에 클래스멤버는 항상 존재한다.

 하지만, 클래스멤버가 존재하는 시점에 인스턴스 멤버가 존재할 수도 있고 존재안 할수도 있다.

 ( new 연산자를 이용해 생성했는지 안했는지 모름)


.

클래스 메서드는 다른 인스턴스 메서드 뿐만 아니라 인스턴스 변수 (이 둘을 합쳐 인스턴스멤버)를

사용할 수 없다!


클래스멤버(변수 + 메서드)는 언제나 참조 또는 호출이 가능하기 때문에

"인스턴스 멤버(변수 + 메서드)"가 클래스 멤버를 사용하는 것은 문제되지 않는다.


그러나, 인스턴스멤버(변수 + 메서드)는 반드시 객체를 생성 후에만 참조, 호출이 가능하기 떄문에

클래스 멤버가 인스턴스 멤버를 참조, 호출하기 위해서는 객체를 생성해야 한다.


반면에, 인스턴스멤버간의 호출에는 아무런 문제가 없다. 

하나의 인스턴스 멤버가 존재한다는 것은 인스턴스가 이미 생성되었다는 것을 의미한다.

즉, 다른 인스턴스멤버들도 모두 존재하는 것이다.


같은 클래스 내에서 클래스멤버가 인스턴스멤버를 참조, 호출하는 경우는 드물다.

만약 그런 경우가 있다면, 인스턴스메서드로 작성해야 할 메서드를 

클래스메서드로 작성한 건 아닌지 생각해봐야 한다.


다음 내용은 메서드 오버로딩으로 이어진다.

왜냐하면 클래스 내에서 메서드를 여러 개 정의해야 할 경우 기존의 메서드를 좀만 변경하면

코드의 중복 없이 같은 이름으로 메서드를 정의할 수 있기 때문이다.