필드(Field)란?
필드는 객체의 속성, '클래스에 포함된 변수'를 가르킨다.
변수의 선언된 위치에 따라 변수의 종류가 달라지므로, 변수의 종류를 파악하기 위해서는 변수가 어느 영역에 선언되었는지 확인하는 것이 중요하다.
그렇다면 클래스에 포함된 변수(=필드)와 일반 변수의 차이는 무엇일까?
필드는 생성자와 메소드 전체에서 사용되며, 객체가 소멸되지 않는 한 객체와 함께 존재한다.
변수는 생성자와 메소드 내에서만 사용되고, 생성자와 메소드가 실행 종료되면 자동 소멸된다.
변수의 종류
변수의 종류 | 선언 위치 | 생성시기 |
클래스 변수 (class variable) |
클래스 영역 | 클래스가 메모리에 올라갈 때 |
인스턴스 변수 (instance variable) |
인스턴스가 생성되었을 때 | |
지역 변수 (local variable) |
클래스 영역 이외의 영역 (메서드, 생성자, 초기화 블럭 내부) |
변수 선언문이 수행되었을 때 |
1. 클래스 변수(class variable)
클래스 변수 선언방법은 인스턴스 변수 앞에 static을 붙이기만 하면 된다.
public static으로 많이 사용하는데, 이와 같이 public을 앞에 붙이게 되면 같은 프로그램 내에서 어디서나 접근 할 수 있는 '전역 변수(global variable)'의 성격을 갖는다.
한 클래스의 모든 인스턴스들이 공통적인 값을 유지해야하는 속성의 경우, 클래스로 선언해야 한다.
클래스 변수는 모든 인스턴스가 공통된 저장공간(변수)을 공유하게 된다.
또한 인스턴스를 생성하지 않고도 언제라도 바로 사용이 가능하다.
'클래스이름.클래스변수'와 같은 형식으로 사용 할 수 있다,
예를 들어 Variables클래스의 클래스변수 cv를 사용하려면 'Variables.cv'와 같이 사용하면 된다.
2. 인스턴스 변수(instance variable)
클래스 영역에 선언되고, 클래스의 인스턴스를 생성할 때 만들어진다. 그렇기에 먼저 인스턴스를 생성해야한다.
인스턴스는 독립적인 저장공간을 가지므로 서로 다른 값을 가질 수 있다.
인스턴스마다 고유한 상태를 유지해야하는 속성의 경우, 인스턴스 변수로 선언한다.
3. 지역 변수(local variable)
지역변수는 매서드 내에 선언되어 메서드 내에서만 사용이 가능하다
그렇기에 메서드가 종료되면 소멸되어 사용할 수 없게 된다.
반복문(for, while)의 블럭 내에 선언된 지역 변수는, 변수가 선언된 블럭{} 내에서만 사용이 가능하다.
Q. 그럼 지역 변수랑 매개 변수랑 같을까?
A. 간단하게 생각하면, 매개변수는 메서드나 함수에 전달되는 값을 받는 변수이고, 지역변수는 메서드나 함수 내에서 선언되고 사용되는 범위가 제한된 변수이다.
class Variables { int iv; // 인스턴스 변수 static int cv; // 클래스변수(static변수) void method(){ int iv = 0; // 지역변수 } } public class Member01 { public static void main(String[] args) { System.out.println( variable.cv ); // 클래스 변수 참조 Variables variable = new Variables(); // 인스턴스 생성 System.out.println( variable.iv ); // 인스턴스 변수 참조 System.out.println( variable.method() ); // 메서드안의 지역변수 출력 } }
이렇게 선언된 위치에 따라 변수의 종류도, 사용 방법도 달라진다.
메서드(Method)란?
'메서드(method)'는 간단히 말하면, 특정 작업을 수행하는 문장들을 묶어놓은 것이라고 할 수 있다.
메서드의 기본 구성
반환타입 메서드이름 (타입 변수명, 타입 변수명, ...){ // 메서드 호출 시 수행될 코드 return 반환값; }
- 반환타입(return type)
- 메서드가 모든 작업을 마치고 반환하는 데이터(결과/출력)의 타입을 명시
- 메서드 이름
- 메서드를 호출하기 위한 이름 명시
- 변수의 명명규칙대로 작성
- 동사 또는 기능을 알 수 있는 이름
- 매개변수(parameter)
- 메서드가 작업을 수행하는데 필요한 값들(입력)을 제공받기 위한 것
- 필요한 값의 개수만큼 변수 선언
- 변수의 타입이 같아도 변수의 타입을 생략할 수 없음
- 구현부
- 메서드의 고유 기능을 수행하는 명령문의 집합
- 중괄호 { } 안에 표현됨
메서드의 호출
메서드이름(값1, 값2, ...); // 메서드를 호출하는 방법 print99danAll(); // void print99danAll()을 호출함 int result = add(3, 5); // int add(int x, int y)를 호출하고, 결과를 result에 저장
메서드를 사용하는 이유
1. 높은 재사용성(reusability)
Java API에서 제공하는 메서드들을 사용하면서, 경험한 것처럼 한번 만들어 놓은 메서드는 몇 번이고 호출할 수 있으며,
다른 프로그램에서도 사용이 가능하다.
2. 중복된 코드의 제거
코드를 작성하다보면, 같은 내용의 문장들이 반복되는 경우가 있다,
이렇게 반복되는 문장들을 묶어 하나의 메서드로 작성해 놓으면, 반복되는 문장들 대신 메서드를 호출하는 한 문장으로 대체할 수 있다.
그러면, 전체 코드의 길이도 짧아지고, 가독성이 좋아지며 변경사항이 생겼을 때 수정해야할 코드의 양도 줄어들어 오류 발생 가능성도 줄어들 수 있다.
public static void main(String[] args) { ... for(int i = 0; i<10; i++) { numArr[i] = {4, 5, 6, 7, 8}; for (int i = 0; i < 10; i++) { System.out.println(numArr[i]); System.out.println(); } ...중간 생략 ... for (int i = 0; i < 10; i++) { System.out.println(numArr[i]); System.out.println(); } } }
이렇게 반복되는 for문이 있다고 예를들자, 메서드를 사용하지 않으면 같은 코드가 계속 반복이 된다,
그렇게되면 코드가 길어지고 해당 문장에서 변경사항이 생겼을 때, 하나하나 고쳐야 하므로 일도 많아지고 복잡해진다.
메서드를 만들어서 코드에 적용하면 다음과 같다.
public static void main(String[] args) { ... for(int i = 0; i<10; i++) { numArr[i] = {4, 5, 6, 7, 8}; printArr(numArr); // 배열 출력 ...중간 생략 ... printArr(numArr); // 배열 출력 } } static void printArr(int[] numArr){ for (int i = 0; i < 10; i++) { System.out.println(numArr[i]); System.out.println(); } }
반복적으로 나타나는 문장들을 printArr이라는 int배열 메서드로 만들어서 사용하면 코드 중복이 제거되고,
변경사항이 발생했을 때 이 메서드만 수정하면 되기에, 관리도 쉽고 오류의 발생 가능성도 낮아진다.
3. 프로그램의 구조화
큰 규모의 프로그램에서는 문장들을 작업단위로 나눠, 여러 개의 메서드에 담아 프로그램의 구조를 단순화시키는 것이 필수이다.
main 메서드는 프로그램의 전체 흐름이 한눈에 들어올 정도로 단순하게 구조화하는 것이 좋다,
그래야 나중에 프로그램에 문제가 발생해도 해당 부분은 쉽게 찾아 해결 할 수 있기 때문이다.
파라미터(parameter) / 아규먼트(arguments)
파라미터와 아규먼트는 주로 '인수, 인자'라는 뜻으로 사용한다. 조금 더 명확하게 차이를 알아보자.
- 파라미터(parameter) : 함수를 정의할 때 입력으로 전달된 값을 받는 변수 즉 매개변수를 의미한다.
- 아규먼트(arguments) : 메서드가 호출될 때, 전달하는 입력 값을 의미
public class Sample { public static int sum(int a, int b) { // 메소드를 정의하는 입력값 a, b 는 매개변수(parameter) return a+b; } public static void main(String[] args) { sample.sum(3, 4); // 메소드를 사용하는 입력값 3, 4는 인수(argument) } }
클래스 메서드 / 인스턴스 메서드
변수와 같이, 메서드에서도 두 가지로 나뉜다.
메서드 앞에 static이 붙어 있으면 클래스 메서드이고, 붙어 있지 않으면 인스턴스 메서드이다.
클래스 메서드는 클래스 변수처럼, 객체를 생성하지 않고도 '클래스이름, 메서드이름(매개변수)'와 같은 식으로 호출이 가능하다.
인스턴스 메서드는 반드시 객체를 생성해야만 호출할 수 있다.
다만 주의할 사항은 클래스 메서드(static 메서드)는 인스턴스 변수를 사용할 수 없다.
또한 클래스 변수(static 변수)는 인스턴스를 생성하지 않아도 사용할 수 있다.
static이 붙은 변수(클래스변수)는 클래스가 메모리에 올라갈 때 이미 자동적으로 생성되기 때문이다.
public 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(), multiply(), divide()는 인스턴스변수인 a와 b만으로도 충분히 작업이 가능하기 때문에,
매개변수를 필요로 하지 않으므로 괄호()에 매개변수를 선언하지 않았다.
반면에 add(long a, long b) 등은 인스턴스변수 없이 매개변수만으로 작업을 수행하기 때문에 static을 붙여서 클래스 메서드로 선언했다.
그래서 main 메서드에서 보면, 클래스메서드는 객체생성없이바로 호출이 가능했고,
인스턴스 메서드는 MyMath2클래스의 인스턴스를 생성한 후에야 호출이 가능했다.
메서드 오버로딩(overloading)
자바에서는 한 클래스 내에 이미 사용하려는 이름과 같은 이름을 가진 메서드가 있더라도 매개변수의 개수 또는 타입이 다르면, 같은 이름을 사용해서 메서드를 정의할 수 있다.
이처럼, 한 클래스 내에 같은 이름의 메서드를 여러 개 정의하는 것을 '메서드 오버로딩(method overloading)'또는 '오버로딩(overloading)'이라 한다.
메소드 오버로딩을 구현하기 위해서는 다음과 같은 조건을 만족해야 한다.
- 메소드의 이름이 같아야 한다.
- 매개변수의 개수 또는 타입이 달라야 한다.
오버로딩의 대표적인 예시는 println 메서드이다, 항상 println메서드 괄호 값 안에 값만 지정해 출력했었다.
System.out.println(1000); System.out.println(3.1111); System.out.println("Hello Java!"); System.out.println(true);
보통 매개변수를 정의 할 때 타입을 정해서 만드는데, println 메서드는 어떻게 가능한 것일까?
간단하게 생각하면 된다, 다양한 매개변수를 받는 동일한 메서드명을 여러개 정의하면 되는 것이다.
또 다른 오버로딩의 예시를 보자
int add(int a, int b) { return a+b; } long add(long a, long b) { return a+b; } long add(int[] a) { // 배열의 모든 요소의 합을 반환한다. long result = 0; for(int i = 0; i < a.length; i++) { result += a[i]; } return result; }
위 메서드들은 모두 바르게 오버로딩되어 있다.
정의된 매개변수가 서로 다르긴 해도, 세 메서드 모두 매개변수로 넘겨받은 값을 더해서 그 결과를 돌려주는 일을 한다.
오버로딩 없이 메서드명을 다 다르게해서, 다른 메서드 형태로 정의해도 큰 차이는 없다.
그러나 오버로딩을 통해 여러 메서드들이 하나의 이름으로 정의 될 수 있다면,
기억하기 쉽고 이름도 짧기에 오류의 가능성을 줄일 수 있을 것이다.
References
'❤️ JAVA > Basic' 카테고리의 다른 글
[JAVA] 제어자(modifier) (0) | 2024.10.18 |
---|---|
[JAVA] 생성자(consructor) (0) | 2024.10.17 |
[JAVA] 객체 지향 프로그래밍(OOP)과 클래스 (2) | 2024.10.15 |
[JAVA] JAVA 2주차(컬렉션) (0) | 2024.10.10 |
[JAVA] JAVA 2주차(배열) (0) | 2024.10.10 |