해당 게시물은 자바의 정석을 정리한 내용 입니다.
3. 변수와 메서드
3.1. 선언 위치에 따른 변수의 종류
변수의 선언 위치
"변수의 선언 위치가 변수의 종류와 범위(scope)를 결정한다."
변수는 클래스 변수, 인스턴스 변수, 지역 변수 모두 세 종류가 있다.
아래 예제에서 모두 3개의 int형 변수가 선언되어 있는데, iv와 cv는 클래스 영역에 선언되어 있으므로 멤버변수이다.
그 중 cv는 키워드 static과 함께 선언되어 있으므로 클래스 변수이며, iv는 인스턴스 변수이다.
lv는 메서드 영역에 선언되어 있으므로 지역변수이다.
[참고] 클래스 영역에 선언된 변수를 멤버 변수라 한다.
멤버 변수 중에 static이 붙은 것은 클래스 변수(static 변수), static이 붙지 않은 것은 인스턴스 변수라 한다.
class Variables
{
int iv; // 인스턴스 변수
static int cv; // 클래스 변수(static 변수)
void method(){
int lv = 0; // 지역 변수
}
}
변수의 종류와 특징
1) 인스턴스 변수(instance variable)
- 인스턴스 변수는 각 인스턴스의 개별적인 저장공간이며 인스턴스 마다 다른 값 저장 가능
- 인스턴스 생성 후, "참조변수.인스턴스변수명"으로 접근
- 클래스의 인스턴스를 생성할 때 만들어지며 참조변수가 없을 때 가비지컬렉터에 의해 자동 제거됨
- 인스턴스 마다 고유한 상태를 유지해야하는 속성의 경우, 인스턴스 변수로 선언
2) 클래스 변수(class variable)
- 클래스 변수는 같은 클래스의 모든 인스턴스들이 공유하는 변수
- 인스턴스 생성 없이 "클래스이름.클래스변수명"으로 접근
- 클래스가 메모리에 로딩(loading)될 때 생성되어 프로그램이 종료될 때까지 유지된다.
- 한 클래스의 모든 인스턴스들이 공통적인 값을 유지해야하는 속성의 경우, 클래스 변수로 선언
3) 지역 변수(local variable)
- 지역 변수는 메서드 내에 선언되며 메서드의 종료와 함께 소멸
- 조건문, 반복문의 블럭 내에 선언된 지역변수는 블럭을 벗어나면 소멸
3.2. 클래스 변수와 인스턴스 변수
예를 들어, Card 클래스를 작성한다면 각 Card 인스턴스는 자신 만의 무늬(kind)와 숫자(number)를 유지하고 있어야 하므로
이들을 인스턴스 변수로 선언하고, 각 카드의 폭(width)와 높이(height)는 모든 인스턴스가 공통적으로 같은 값을
유지해야하므로 클래스 변수로 선언 하였다.
class Card{
String kind; // 무늬
int number; // 숫자
static int width = 100; // 폭
static int height = 250; //높이
}
class CardTest{
public static void main(String args[]) {
// 클래스 변수(static) 변수는 객체 생성 없이 "클래스이름.클래스변수"로 직접 사용 가능하다.
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 = "Spade";
c2.number = 4;
System.out.println("c1은 " + c1.kind + ", " + c1.number + "이며, 크기는 (" + c1.width + ", " + c1.height + ")" );
System.out.println("c2는 " + c2.kind + ", " + c2.number + "이며, 크기는 (" + c2.width + ", " + c2.height + ")" );
System.out.println("c1의 width와 height를 각각 50, 80으로 변경합니다.");
c1.width = 50; // 클래스 변수의 값을 변경한다.
c1.height = 80; // 클래스 변수를 사용할 때는 Card.width와 같은 형식 사용이 좋다.
System.out.println("c1은 " + c1.kind + ", " + c1.number + "이며, 크기는 (" + c1.width + ", " + c1.height + ")" );
System.out.println("c2는 " + c2.kind + ", " + c2.number + "이며, 크기는 (" + c2.width + ", " + c2.height + ")" );
}
}
class Card {
String kind; // 카드의 무늬 - 인스턴스 변수
int number; // 카드의 숫자 - 인스턴스 변수
static int width = 100; // 카드의 폭 - 클래스 변수
static int height = 250; // 카드의 높이 - 클래스 변수
}
3.3. 메서드
- 메서드(method)는 특정 작업을 수행하는 일련의 문장들을 하나로 묶은 것이다.
- 메서드를 사용하는 이유
1) 높은 재사용성(reusability)
2) 중복된 코드 제거
3) 프로그램의 구조화
3.4. 메서드의 선언과 구현
메서드의 선언과 구현
메서드는 선언부와 구현부로 이루어져 있다.
반환타입 메서드이름 (타입 변수명, 타입 변수명 ...){
// 메서드 호출 시 수행 될 코드
}
int add(int a, int b)
{
int result = a + b;
return result; // 호출한 메서드로 결과를 반환한다.
}
3.5. 메서드의 호출
메서드를 호출해야만 구현부의 문장들이 수행된다.
메서드명( 값1, 값2, ...); // 메서드를 호출하는 방법
print99danAll();
int result = add(3, 5);
• 인자(argument)와 매개변수(parameter)
- 인자(argument) 또는 인수 : 메서드를 호출할 때 괄호()안에 지정한 값들을 말한다.
인자의 개수와 순서는 호출된 메서드에 선언된 매개변수와 일치해야 한다.
- 인자는 메서드가 호출되면서 매개변수에 대입되므로, 인자의 타입은 매개변수의 타입과 일치하거나
자동 형 변환이 가능한 것이어야 한다.
class MyMathTest {
public static void main(String args[]) {
MyMath mm = new MyMath();
long result1 = mm.add(5L, 3L);
long result2 = mm.subtract(5L, 3L);
long result3 = mm.multiply(5L, 3L);
double result4 = mm.divide(5L, 3L);
System.out.println("add(5L, 3L) = " + result1);
System.out.println("subtract(5L, 3L) = " + result2);
System.out.println("multiply(5L, 3L) = " + result3);
System.out.println("divide(5L, 3L) = " + result4);
}
}
class MyMath {
long add(long a, long b) {
long result = a+b;
return result;
// 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;
}
}
3.6. return문
- return문은 현재 실행중인 메서드를 종료하고 호출한 메서드로 되돌아간다.
- 반환 값의 유무에 관계 없이 모든 메서드에는 반드시 하나의 return문이 있어야한다.
- 그런데도 반환 타입이 void인 경우, return문 없이 아무런 문제가 없었던 이유는 컴파일러가 메서드의 마지막에 'return;'을
자동적으로 추가해주었기 때문이다.
- 메서드의 반환 타입이 'void'가 아닌 경우, 반드시 구현부 안에 'return 반환값;'이 포함되어 있어야 한다.
- 이 값의 타입은 반환 타입과 일치하거나 적어도 자동 형변환이 가능한 것이어야 한다.
int max(int a, int b){
int result = 0;
if(a > b)
result = a;
else
result = b;
return result;
}
3.7. JVM의 메모리 구조
1) 메서드 영역(Method Area)
- 클래스 정보와 클래스 변수가 저장되는 곳
2) 호출 스택(Call Stack)
- 메서드의 작업 공간
- 메서드가 호출되면 메서드 수행에 필요한 메모리를 할당 받고, 메서드가 종료되면 사용하던 메모리를 반환한다.
(해당 메모리는 메서드가 작업을 수행하는 동안, 지역변수(매개변수 포함)들과 연산의 중간 결과 등을 저장하는데 사용된다.)
3) 힙(Heap)
- 인스턴스가 생성되는 공간
- new 연산자에 의해서 생성되는 배열과 객체는 모두 여기에 생성 된다. (인스턴스 변수 포함)
• 호출스택의 특징
1) 메서드가 호출되면 수행에 필요한 메모리를 스택에 할당 받는다.
2) 메서드가 수행을 마치고 나면 사용 했던 메모리를 반환하고 스택에서 제거된다.
3) 호출 스택의 제일 위에 있는 메서드가 현재 실행 중인 메서드이다.
4) 아래에 있는 메서드가 바로 위의 메서드를 호출한 메서드이다.
class CallStackTest {
public static void main(String[] args) {
firstMethod();
}
static void firstMethod() {
secondMethod();
}
static void secondMethod() {
System.out.println("secondMethod()");
}
}
위의 예제에서 사용된 메서드들은 static 메서드이며 static 메서드는 객체를 생성하지 않고도 메서드를 호출 할 수 있다.
3.8. 기본형 매개변수와 참조형 매개변수
자바에서는 메서드를 호출할 때 매개변수로 지정한 값을 메서드의 매개변수에 복사해서 넘겨준다.
매개변수의 타입이 기본형(primitive type)일 때는 기본형 값이 복사되지만, 참조형(reference type)이면 인스턴스의 주소가 복사된다.
① 기본형 매개변수 : 변수의 값을 읽기만 할 수 있다. (read only)
② 참조형 매개변수 : 변수의 값을 읽고 변경 할 수 있다. (read & write)
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);
change(d.x);
System.out.println("After change(d.x)");
System.out.println("main() : x = " + d.x);
}
static void change(int x) { // 기본형 매개변수
x = 1000;
System.out.println("change() : x = " + x);
}
}
class Data { int x; }
class ReferenceParamEx {
public static void main(String[] args) {
Data d = new Data();
d.x = 10;
System.out.println("main() : x = " + d.x);
change(d);
System.out.println("After change(d)");
System.out.println("main() : x = " + d.x);
}
static void change(Data d) { // 참조형 매개변수
d.x = 1000;
System.out.println("change() : x = " + d.x);
}
}
3.9. 참조형 반환타입
"반환 타입이 '참조형'이라는 것은 메서드가 '객체의 주소'를 반환한다는 것을 의미한다."
class Data { int x; }
class ReferenceReturnEx {
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를 생성한다.
tmp.x = d.x; // d.x의 값을 tmp.x에 복사한다.
return tmp; // 복사한 객체의 주소를 반환한다.
}
}
3.10. 재귀 호출(recursive call)
- 재귀 호출(recursive call)' : 메서드의 내부에서 메서드 자신을 다시 호출하는 것을 말함
- 재귀 메서드 : 재귀 호출을 하는 메서드를 말함
static int factorial(int n){
int result = 0;
if( n == 1)
result = 1;
else
result = n * factorial(n-1); // 다시 메서드 자신을 호출한다.
return result;
}
3.11. 클래스 메서드(static 메서드)와 인스턴스 메서드
1) 클래스 메서드(static 메서드)
- 클래스 메서드(static 메서드)는 객체 생성 없이 '클래스이름.메서드명()'으로 호출한다.
- 인스턴스 변수나 인스턴스 메서드를 사용하지 않는 메서드를 클래스 메서드로 정의한다.
- 메서드 내에서 인스턴스 변수 사용 불가
2) 인스턴스 메서드
- 인스턴스 메서드는 객체 생성 후, '참조변수.메서드명()'으로 호출한다.
- 인스턴스 변수나 인스턴스 메서드와 관련된 작업을 하는 메서드를 인스턴스 메서드로 정의한다.
- 메서드 내에서 인스턴스 변수 사용 가능
클래스의 멤버변수 중 모든 인스턴스에 공통된 값을 유지해야하는 것이 있는지 살펴 보고 있으면, static을 붙여준다.
작성한 메서드 중에서 인스턴스 변수나 인스턴스 메서드를 사용하지 않는 메서드에 static을 붙일 것을 고려한다.
class MyMath2 {
long a, b;
// 인스턴스변수 a, b만을 이용해서 작업하므로 매개변수가 필요없다.
long add() { return a + b; } // a, b는 인스턴스변수
long subtract() { return a - b; }
long multiply() { return a * b; }
double divide() { return a / b; }
// 인스턴스변수와 관계없이 매개변수만으로 작업이 가능하다.
static long add(long a, long b) { return a + b; } // 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(double a, double b) { return a / b; }
}
class MyMathTest2 {
public static void main(String args[]) {
// 클래스메서드 호출. 인스턴스 생성없이 호출가능
System.out.println(MyMath2.add(200L, 100L));
System.out.println(MyMath2.subtract(200L, 100L));
System.out.println(MyMath2.multiply(200L, 100L));
System.out.println(MyMath2.divide(200.0, 100.0));
MyMath2 mm = new MyMath2(); // 인스턴스를 생성
mm.a = 200L;
mm.b = 100L;
// 인스턴스메서드는 객체생성 후에만 호출이 가능함.
System.out.println(mm.add());
System.out.println(mm.subtract());
System.out.println(mm.multiply());
System.out.println(mm.divide());
}
}
위의 예제에서는 add(), subtract() 등은 인스턴스 변수인 a와 b만으로도 충분히 작업이 가능하기 때문에 매개변수를 선언하지 않았다.
반면에 add(long a, long b), subtract(long a, long b) 등은 인스턴스 변수 없이 매개변수 만으로 작업하기 때문에 클래스 메서드로 선언 하였다.
3.12. 클래스 멤버와 인스턴스 멤버 간의 참조와 호출
- 같은 클래스에 속한 멤버들 간에는 별도의 인스턴스를 생성하지 않고도 서로 참조 또는 호출이 가능하다.
- 단, 클래스 멤버가 인스턴스 멤버를 참조 또는 호출하고자 하는 경우에는 인스턴스를 생성해야 한다.
그 이유는 인스턴스 멤버가 존재하는 시점에 클래스 멤버는 항상 존재하지만, 클래스 멤버가 존재하는 시점에
인스턴스 멤버가 존재하지 않을 수도 있기 때문이다.
class TestClass{
void instanceMethod() {} // 인스턴스 메서드
static void staticMethod() {} // static 메서드
void instanceMethod2() { // 인스턴스 메서드
instanceMethod(); // 다른 인스턴스 메서드를 호출한다.
staticMethod(); // static 메서드를 호출한다.
}
static void staticMethod2() { // static 메서드
instanceMethod(); // 에러!!! 인스턴스 메서드를 호출할 수 없다.
staticMethod(); // static 메서드는 호출 할 수 있다.
}
}
class TestClass2{
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); // 클래스 변수는 사용할 수 있다.
}
}
- 인스턴스 메서드는 인스턴스 변수를 사용할 수 있지만, static 메서드는 인스턴스 변수를 사용할 수 없다.
클래스 멤버는 언제나 참조 또는 호출이 가능하지만 인스턴스 멤버는 반드시 객체를 생성한 후에만 참조 또는 호출이 가능하다.
4.1. 오버로딩이란?
- 메서드 오버로딩(method overloading)은 한 클래스 내에 같은 이름의 메서드를 여러 개 정의하는 것이다.
4.2. 오버로딩의 조건
① 메서드 이름이 같아야 한다.
② 매개변수의 개수 또는 타입이 달라야 한다.
4.3. 오버로딩의 예
long add(int a, long b) { return a+b; }
long add(long a, int b) { return a+B; }
4.4. 오버로딩의 장점
- 메서드의 이름을 기억하기도 쉽고 이름도 짧게 할 수 있어서 오류의 가능성을 많이 줄일 수 있음
class OverloadingTest {
public static void main(String args[]) {
MyMath3 mm = new MyMath3();
System.out.println("mm.add(3, 3) 결과:" + mm.add(3,3));
System.out.println("mm.add(3L, 3) 결과: " + mm.add(3L,3));
System.out.println("mm.add(3, 3L) 결과: " + mm.add(3,3L));
System.out.println("mm.add(3L, 3L) 결과: " + mm.add(3L,3L));
int[] a = {100, 200, 300};
System.out.println("mm.add(a) 결과: " + mm.add(a));
}
}
class MyMath3 {
int add(int a, int b) {
System.out.print("int add(int a, int b) - ");
return a+b;
}
long add(int a, long b) {
System.out.print("long add(int a, long b) - ");
return a+b;
}
long add(long a, int b) {
System.out.print("long add(long a, int b) - ");
return a+b;
}
long add(long a, long b) {
System.out.print("long add(long a, long b) - ");
return a+b;
}
int add(int[] a) { // 배열의 모든 요소의 합을 결과로 돌려준다.
System.out.print("int add(int[] a) - ");
int result = 0;
for(int i=0; i < a.length;i++) {
result += a[i];
}
return result;
}
}
4.5. 가변인자(variable arguments)와 오버로딩
- 가변인자(variable arguments)는 메서드의 매개변수 개수를 동적으로 지정할 수 있는 기능이다. ( JDK 1.5 부터 )
- 가변인자는 타입... 변수명 과 같은 형식으로 선언
- 가변인자는 항상 마지막 매개변수이어야 한다. 그렇지 않으면 컴파일 에러가 발생한다.
- 가변인자는 내부적으로 배열을 이용하는 것이다. 그래서 가변인자가 선언된 메서드를 호출할 때마다 배열이 새로 생성된다.
이런 비효율이 숨어 있으므로 꼭 필요한 경우에만 사용하자.
- 가변인자와 매개변수의 타입을 배열로 하는 것의 차이점
→ 매개변수의 타입을 배열로 하면, 반드시 인자를 지정해 줘야하기 때문에, 아래 코드처럼 인자를 생략할 수 없다.
String concatenate(String[] str) { ... } // 인자로 배열을 지정
String result = concatenate(); // 에러. 인자가 필요함.
- 가변인자를 사용한 메서드는 가능하면 오버로딩하지 않는 것이 좋다.
- 가변인자 예시
아래 코드의 concatenate 메서드는 매개변수로 입력된 문자열에 구분자를 사이에 포함시켜 결합해서 반환한다.
가변인자로 매개변수를 선언 했기 때문에 문자열을 개수의 제약 없이 매개변수로 지정할 수 있다.
static String concatenate(String delim, String... args){
String result = "";
for(String str : args){
result += str + delim;
}
return result;
}
class VarArgsEx {
public static void main(String[] args) {
String[] strArr = { "100", "200", "300" };
System.out.println(concatenate("", "100", "200", "300"));
System.out.println(concatenate("-", strArr));
System.out.println(concatenate(",", new String[]{"1", "2", "3"}));
System.out.println("["+concatenate(",", new String[0])+"]");
System.out.println("["+concatenate(",")+"]");
}
static String concatenate(String delim, String... args) {
String result = "";
for(String str : args) {
result += str + delim;
}
return result;
}
/*
static String concatenate(String... args) {
return concatenate("",args);
}
*/
} // class
'Java > 객체 지향 개념 ~' 카테고리의 다른 글
자바의 정석 (Chapter 7_3. 객체지향개념 2) (0) | 2020.08.03 |
---|---|
자바의 정석 (Chapter 7_2. 객체지향개념 2) (0) | 2020.08.02 |
자바의 정석 (Chapter 7_1. 객체지향개념 2) (0) | 2020.08.02 |
자바의 정석 (Chapter 6_3. 객체지향개념 1) (0) | 2020.08.01 |
자바의 정석 (Chapter 6_1. 객체지향개념 1) (0) | 2020.01.21 |
댓글