4 분 소요

필드와 지역 변수

필드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가지 문법적 규칙을 지켜야 한다.

  1. 생성자의 내부에서만 사용할 수 있다. 즉, 생성자의 내부에서만 또 다른 생성자를 호출할 수 있다는 말이다.
  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;
  }
}

참조

Do it! 자바 완전 정복

댓글남기기