클래스 내부 구성 요소
필드와 지역 변수
필드field
는 클래스에 포함된 변수
로, 객체의 속성값을 지정할 수 있다.
필드는 지역 변수local variable
와 구분해야 한다.
지역 변수는 메서드에 포함된 변수
를 말한다.
즉, 필드와 지역 변수는 어떤 중괄호 안에 선언됐는지에 따라 구분할 수 있다.
클래스의 중괄호 안에 선언된 변수를 필드, 메서드의 중괄호 안에 선언된 변수를 지역 변수라고 생각하면 된다.
필드와 지역 변수의 가장 큰 차이점은 생성되는 메모리의 위치다.
필드는 힙 메모리의 객체 내부, 지역 변수는 스택 메모리에 생성된다.
스택 메모리에 저장되는 변수는 때가 되면 자바 가상 머신이 자동으로 삭제하지만,
힙 메모리의 객체 안에 저장되는 필드는 객체가 사라지지 않는 한 절대로 삭제되지 않는다.
class A {
// 상위 중괄호가 클래스일떄는 필드
int m = 3; // 필드
int n = 4; // 필드
void work1() {
// 상위 중괄호가 메서드일 때는 지역 변수
int k = 5; // 지역변수
System.out.println(k);
work2(3);
}
void work2(int i) {
int j = 4;
System.out.println(i + j);
}
}
그렇다면 스택 메모리의 변수는 언제 자동으로 삭제될까?
자신이 선언된 메서드의 중괄호가 닫혀 메서드가 종료되면,
그 메서드 안에 선언된 모든 지역 변수가 메모리에서 통째로 삭제된다.
필드와 지역 변수의 초깃값
필드와 지역 변수의 또 다른 차이점은 초깃값이다.
필드는 직접 초기화하지 않아도 강제로 초기화된다.
반면, 지역 변수는 직접 초기화하지 않으면 저장 공간이 빈 공간 그대로 있어 값을 출력하고자 할 때 오류가 발생한다.
class A {
int m;
int n;
void work1() {
int k;
System.out.println(k); //초깃값 없이 출력을 시도해 오류 발생
}
}
class A {
boolean m1;
int m2;
double m3;
String m4;
void printFieldValue() {
// 필드는 초기화하지 않아도 값이 강제 초기화돼 출력 가능
System.out.println(m1);
System.out.println(m2);
System.out.println(m3);
System.out.println(m4);
}
}
메서드
메서드는 클래스의 기능에 해당하는 요소다.
class A {
// 메서드
public static int sum(int a, int b) {
// 메서드 내용
}
}
리턴 타입은 메서드 종료 이후 변환(또는 반환)되는 값의 자료형을 의미한다.
메서드명은 변수명 선정 규칙과 동일하며, 관례적으로 소문자로 시작한다.
이후 소괄호 안에는 입력 매개변수가 들어오는데,
이는 메서드를 호출할 때 전달되는 값의 자료형과 전달받은 값을 저장할 지역 변수명을 정의한다.
마지막 중괄호 안에는 메서드의 내용이 들어가는데, 여기에 메서드가 수행해야 할 기능을 작성한다.
메서드 호출하기
클래스 외부에서 메서드 호출하기
메서드도 클래스의 멤버이므로 객체 안에 존재할 것이고,
클래스 외부에서 메서드를 사용하려면 먼저 객체를 생성해야 할 것이다.
class A {
// 리턴 타입 void, 입력매개변수 없음.
void print() {
System.out.println("안녕");
}
}
public class ExternalCallMethods {
public static void main(String[] args) {
// 객체 생성
A a = new A();
// 메서드 호출(멤버 활용)
a.print();
}
}
클래스 내부에서 메서드 호출하기
클래스 내부에 있는 메서드 끼리는 객체를 생성하지 않고 서로를 호출할 수 있다.
말 그대로 같은 멤버이기 때문이다. 필드 또한 멤버이므로 클래스 내부의 모든 메서드 안에서 객체를 생성하지 않고 자신이 속한 클래
스의 필드를 사용할 수 있다.
public class InternalCallMethods {
public static void main(String[] args) {
// 같은 클래스 안에 있는 내부 메서드 호출
print();
int a = twice(3);
System.out.println(a);
double b = sum(a, 5.8);
System.out.println(b);
}
public static void print() {
System.out.println("안녕");
}
public static int twice(int k) {
return k * 2;
}
public static double sum(int m, double n) {
return m + n;
}
}
생성자
생성자 constructor
는 객체를 생성하는 역할을 지닌 클래스의 내부 구성 요소다.
또한 객체 내에 포함되는 필드의 초기화 또한 주로 생성자 내에서 수행한다.
생성자의 특징
생성자를 작성할 때 꼭 지켜야 하는 문법적 규칙은 2가지다.
- 반드시 클래스명과 동일한 이름으로 지어야 한다.
- 메서드 와 비슷한 구조를 지니고 있지만, 리턴 타입이 없다.
여기서 헷갈리지 말아야 할 것이 ‘리턴 타입이 없다.’와 ‘리턴하지 않는다(
void
).’는 전혀 다른 이야기라는 것이다.
기본 생성자의 자동 추가
A a = new A()
와 같이 생성자를 호출해 객체를 만들 어 왔지만, 지금까지 생성자를 만든 기억은 없다.
지금까지 생성자를 만들지 않아도 정상적으로 객체가 생성됐던 이유는 생성자를 포함하지 않는 클래스에게
컴파일러가 기본 생성자를 추가해 줬기 때문이다.
class A {
int m;
void work() {}
// 컴파일러가 자동으로 기본생성자를 생성
A() {
}
}
this
키워드와 this()
메서드
클래스 내부에서는 객체의 생성 없이 필드와 메서드를 바로 사용할 수 있다고 했다.
하지만 모든 멤버는 항상 객체 속에만 존재한다.
그렇다면 어떻게 클래스 내부에서는 객체를 생성하지 않고 바로 필드와 메서드를 사용할 수 있을까?
내부 객체 참조 변수명인 this 키워드
모든 메서드에는 자신이 포함된 클래스의 객체를 가리키는 this
라는 참조 변수가 있다.
다만 this.
를 생략하면 *컴파일러가 자동으로 this.
를 추가해 주기 때문에
지금까지 **클래스 내부에서 필드와 메서드를 그대로 사용할 수 있던 것이다.
class A {
int m;
int n;
void init(int a, int b) {
int c;
c = 3;
this.m = a; //this.를 생략했을 때 자동으로 추가
this.n = b; //this.를 생략했을 때 자동으로 추가
}
void work() {
this.init(2, 3); //this.를 생략했을 때 자동으로 추가
}
}
public class ThisKeyword_1 {
public static void main(String[] args) {
// 클래스 객체 생성
A a = new A();
// 메서드 호출 / 필드값 활용
a.work();
System.out.println(a.m);
System.out.println(a.n);
}
}
클래스 내 다른 생성자를 호출하는 this()
메서드
this()
메서드는 자신이 속한 클래스 내부의 다른 생성자를 호출하는 명령이다.
this()
메서드를 구성할 때는 반드시 2가지 문법적 규칙을 지켜야 한다.
- 생성자의 내부에서만 사용할 수 있다. 즉, 생성자의 내부에서만 또 다른 생성자를 호출할 수 있다는 말이다.
- 생성자의 첫 줄에 위치해야 한다
class A {
A() {
System.out.println("첫 번째 생성자");
}
A(int a) {
this(); // 반드시 생성자의 첫 줄에 위치해야 함.
System.out.println("두 번째 생성자");
}
// 메서드에서는 this() 메서드 사용 불가능
void abc() {
this();
}
}
이런 문법 요소가 생긴 이유는 무엇일까?
생성자의 주요 역할은 필드를 초기화하는 것이라고 했다.
두 번째 생성자에서 첫 번째 생성자를 호출할 수 있다면 두 번째 생성자에서는 1개의 필드만 추가로 초기화하면 된다.
이와 마찬가지로 세 번째 생성자에서 두 번째 생성자를 호출할 수 있다면 추가로 1개의 필드만 초기화하 면 된다.
이것이 바로 this() 메서드가 필요한 이유다. 즉, 다음과 같이 this() 메서드를 이용해 생성자의 중복을 제거할 수 있다.
class A {
int m1, m2, m3, m4;
A() {
m1 = 1;
m2 = 2;
m3 = 3;
m4 = 4;
}
A(int a) {
m1 = a;
// m2 = 2;
// m3 = 3;
// m4 = 4;
this();
}
A(int a, int b) {
this(a);
// m1 = a;
m2 = b;
// m3 = 3;
// m4 = 4;
}
}
댓글남기기