본문 바로가기
네이버클라우드/JAVA 웹 프로그래밍

JAVA 15일차 (2023-06-13) 자바 기초 DAY13_변수의 종류

by prometedor 2023. 6. 13.

Exam0100.java

$ java ... Exam0110↵

// # 변수의 종류

package com.eomcs.oop.ex03;

public class Exam0100 {

  // static 필드 = 클래스 필드(변수)
  // - 클래스를 로딩할 때 Method Area 영역에 생성된다.
  // - 클래스는 단 한 번만 로딩된다.
  // - 따라서 스태틱 변수도 한 번만 생성된다.
  // - JVM을 종료할 때 메모리에서 한꺼번에 제거된다.
  static int a;

  // non-static 필드 = 인스턴스 필드
  // - new 연산자를 실행할 때 Heap 영역에 생성된다.
  // - new 연산자를 실행할 때마다 생성된다.
  // - Garbage Collector에 의해 인스턴스가 해제될 때 제거된다.
  int b;

  public static void main(String[] args /* 파라미터 = 로컬 변수 */) {

    // 로컬 변수
    // - 메서드가 호출될 때 JVM Stack 영역에 생성된다.
    // - 메서드 호출이 끝나면 제거된다.
    int c;
    c = 100;

    // <=== 현재 실행 시점
    // - Method Area: a 변수 존재
    // - JVM Stack: args, c, obj 변수 존재
    // - Heap: 아직 생성된 객체 없음

    Exam0100 obj; // obj는 main()을 호출할 때 시작 시점에 JVM Stack에 생성된 상태이다.

    obj = new Exam0100();

    // <=== 현재 실행 시점
    // - Method Area: a 변수 존재
    // - JVM Stack: args, c, obj 변수 존재
    // - Heap: b 변수 존재

    System.out.println(c);

  }
}

ㄴ 이 코드의 실행과정을 그림으로 나타내면 위 그림과 같음

 

Exam0110.java

$ java ... Exam0110↵

// # 인스턴스 변수

package com.eomcs.oop.ex03;

public class Exam0110 {

  // 지금 당장 A 클래스 앞에 붙은 static은 고민하지 말라!
  // 이 예제의 목표는 인스턴스 변수이다.
  static class A {
    // 인스턴스 변수 = 논스태틱 변수
    // => new 명령을 통해 생성된다.
    // => new 명령을 실행하기 전까지는 인스턴스 변수는 존재하지 않는다.
    // => Heap 영역에 생성된다.
    // => static이 붙지 않는다.
    int v1; // 4바이트 int 값을 저장할 메모리를 만들라는 명령!
    boolean v2; // true/false 논리값을 저장할 메모리를 만들라는 명령!
    // 이 명령은 new 명령을 실행할 때 비로서 실행된다.
  }

  public static void main(String[] args) {

    // A 클래스에 대해 new 명령을 사용하기 전에는 v1, v2 메모리는 존재하지 않는다.
    // 단지 설계도일 뿐이다.
    A obj1 = new A(); // A 클래스에서 변수 선언 명령을 실행한다. 주의! 메서드 정의는 실행하지 않는다!
    A obj2 = new A();
    A obj3 = new A();

    // 이렇게 생성된 메모리를 "인스턴스", "객체"라고 부른다.
    // 이 인스턴스의 주소를 저장하는 obj1, obj2, obj3를 "레퍼런스"라 부른다.
    // 인스턴스가 생성될 때 만들어지는 v1, v2를 변수를 "인스턴스 변수"라 부른다.

    // 인스턴스 변수는 레퍼런스를 통해 사용할 수 있다.
    obj1.v1 = 100;
    obj2.v1 = 200;
    obj3.v1 = 300;

    System.out.printf("%d, %d, %d\n", obj1.v1, obj2.v1, obj3.v1);
  }
}

// 인스턴스 변수는 new 명령을 실행할 때 마다 생성되기 때문에
// 각각 구분되는 개별 데이터를 저장할 때 사용한다.

ㄴ 이 코드의 실행과정을 그림으로 나타내면 아래 그림과 같음

 

 

Exam0120.java

$ java ... Exam0120↵

// # 인스턴스 변수 응용 - 성적 데이터 저장할 메모리 만들기

package com.eomcs.oop.ex03;

public class Exam0120 {

  // 1) 성적 데이터를 설계할 클래스이기 때문에 그에 맞는 클래스명을 사용하라!
  static class Score {

    // 2) 여러 명의 구별되는 성적 데이터를 저장해야 하기 때문에
    // 인스턴스 변수로 메모리를 설계하라!
    String name;
    int kor;
    int eng;
    int math;
    int sum;
    float average;
  }

  public static void main(String[] args) {

    // 저장하고 싶은 데이터 개수 만큼 인스턴스를 생성하라!
    Score s1 = new Score(); // 1명 분의 성적 데이터를 저장할 메모리
    Score s2 = new Score();
    Score s3 = new Score();

    // 각 인스턴스에 한 명의 성적 데이터를 저장하라!
    s1.name = "홍길동";
    s1.kor = 100;
    s1.eng = 90;
    s1.math = 80;
    s1.sum = s1.kor + s1.eng + s1.math;
    s1.average = s1.sum / 3f;

    s2.name = "임꺽정";
    s2.kor = 100;
    s2.eng = 100;
    s2.math = 100;
    s2.sum = s2.kor + s2.eng + s2.math;
    s2.average = s2.sum / 3f;

    s3.name = "유관순";
    s3.kor = 100;
    s3.eng = 90;
    s3.math = 60;
    s3.sum = s3.kor + s3.eng + s3.math;
    s3.average = s3.sum / 3f;

  }
}

ㄴ 이 코드의 실행과정을 그림으로 나타내면 아래 그림과 같음

 

Exam0130.java

$ java ... Exam0130↵

// # 클래스 변수

package com.eomcs.oop.ex03;

public class Exam0130 {

  // 지금 당장 A 클래스 앞에 붙은 static은 고민하지 말라!
  // 이 예제의 목표는 스태틱 변수이다.
  static class A {

    // 클래스 변수 = 스태틱 변수
    // - static 이 붙은 변수이기 때문에 "스태틱 변수"라고도 부른다.
    // - 클래스를 로딩하는 순간 자동 생성된다.
    // - 클래스와 함께 "Method Area" 영역에 존재한다.
    // - 클래스 이름으로 접근
    //   클래스 이름으로 접근한다고 해서 "클래스에 소속된 변수", "클래스 변수"라 부른다.
    // - 문법
    //     static 데이터타입 변수명;
    //
    static int v1;
    static boolean v2;
  }

  public static void main(String[] args) {

    // 클래스 변수 사용법
    // 클래스명.스태틱변수명 = 값;
    // 클래스를 사용하는 순간 클래스가 로딩되고, 스태틱 변수는 자동 생성된다.
    A.v1 = 100;
    A.v2 = true;

    System.out.printf("%d, %b\n", A.v1, A.v2);
  }
}

// JVM을 실행하는 동안 한 번 클래스가 로딩되면 같은 클래스에 대해 중복 로딩되지 않는다. 
// 클래스 변수는 클래스가 로딩될 때 자동 생성되기 때문에
// 클래스에 대해 딱 한 번 생성된다.
//

// ## 클래스 로딩
// - 외부 저장장치(예: HDD, USB 메모리, DVD-ROM 등)에 있는 .class 파일을
//   JVM이 관리하는 메모리로 로딩하는 것.
// - 클래스의 코드를 사용하는 시점에 메모리(Method Area 영역)에 로딩된다.

// ## 클래스의 코드를 사용하는 시점?
// - 스태틱 멤버(필드와 메서드)를 사용할 때
//     예) A.v1 = 200; <--- 스태틱 변수 v1 사용
//     예) System.out.println(A.v1); <--- 스태틱 변수 out 사용 
//     예) Integer.parseInt(..); <--- 스태틱 메서드 parseInt() 사용
// - new 명령을 사용하여 인스턴스를 생성할 때
//     예) new A();
// - 한 번 클래스가 로딩되면 JVM을 종료할 때까지 유지한다.
// - 물론 강제로 클래스를 unloading 할 수 있다. 
//   그리고 다시 로딩할 수 있다.

// ## 주의! 클래스를 로딩할 거라고 착각하는 경우
// - 다음과 같이 레퍼런스 변수를 선언할 때는 클래스를 로딩하지 않는다. 
//   로딩하지 않는다! 로딩하지 않는다! 로딩하지 않는다!
// 예) A obj;
// 예) String str;

// ## 클래스 로딩 과정
// $ java com.eomcs.oop.ex03.Exam0130
// 1) 클래스 파일 'Exam0130.class'을 찾는다.
//    - JDK에서 제공하는 기본 라이브러리에서 찾는다.
//    - JVM을 실행할 때 -classpath(또는 -cp) 지정한 CLASSPATH 디렉토리에서 찾는다.
//    - CLASSPATH에 없으면 JVM을 실행하는 현재 폴더에서 찾는다. 
//    - 그래도 없으면 오류를 띄운다.
// 2) 바이트코드 검증(Verify)
//    - 클래스의 바이트코드 유효성을 검사한다.
// 3) Exam0130.class를 "Method Area 영역"에 로딩한다.
//    - 즉 클래스를 외부 저장소(HDD)에서 내부 저장소(RAM)로 로딩한다.
//    - bytecode를 분석하여 코드(생성자, 메서드)와 상수를 따로 분리하여 보관한다.
// 4) 스태틱 필드 및 메서드 테이블 준비(Prepare)
//    - Method Area 에 스태틱 필드 생성한다.
//    - 클래스 내부에서 사용하는 이름(변수명, 메서드명, 클래스명 등) 목록을 준비한다.
// 5) 참조하는 외부 클래스나 인터페이스 검사(Resolve)
//    - 로딩된 클래스가 참조하는 외부 클래스나 인터페이스의 유효성을 검사한다.
// 6) 클래스 초기화시키기
//    - 스태틱 블록(static initializers)을 실행한다.
// 7) main() 메서드를 호출한다.
//    - 클래스를 실행하는 것이라면 main() 메서드를 찾아 실행한다.

// ## Exam0130의 main() 메서드 호출
// 1) main() 메서드에 선언된 로컬 변수를 "JVM 스택 영역"에 생성한다.
//    - args 변수를 스택 영역에 생성한다.
// 2) main()의 코드를 실행한다.
//    - A.v1 = 100;
//      => A.class 를 "Method Area"에 로딩한다.
//      => A의 클래스(스태틱) 필드를 "Method Area"에 생성한다.
//      => `A.v1 = 100` 문장을 실행한다.
//    - A.v2 = true;
//      => A 클래스가 이미 로딩되었기 때문에 다시 로딩하지 않는다.
//      => `A.v2 = true` 문장을 실행한다.
//    - System.out.printf() 를 실행한다.
//

// ## JVM이 관리하는 메모리 영역
// 1) Heap
//    - new 명령으로 생성한 인스턴스 변수가 놓인다.
//    - 즉 인스턴스 필드가 이 영역에 생성된다.
//      - 메서드는 생성하지 않는다!
//    - 가비지 컬렉터는 이 메모리의 가비지들을 관리한다.
// 2) JVM Stack
//    - 각 스레드가 개인적으로 관리하는 메모리 영역이다.
//    - 스레드에서 메서드를 호출할 때 메서드의 로컬 변수를 이 영역에 만든다.
//    - 메서드가 호출될 때 그 메서드가 사용하는 로컬 변수를 프레임에 담아 만든다.
//    - 메서드 호출이 끝나면 그 메서드가 소유한 프레임이 삭제된다.
// 3) Method Area
//    - JVM이 실행하는 바이트코드(.class 파일)를 두는 메모리 영역이다.
//      - 바이트코드를 그대로 메모리에 두는 것이 아니라, 멤버의 종류에 따라 적절하게 분류한다. 
//    - 즉 클래스 코드가 이 영역에 놓이는 것이다.
//    - JVM은 코드를 실행할 때 이 영역에 놓은 명령어를 실행하는 것이다.
//    - 개발자가 작성한 클래스, 메서드 등 이런 코드들이 이 영역에 놓이는 것이다.
//    - 스태틱 필드를 이 영역에 생성한다.
//    - 주의! 
//      Heap에는 개발자가 작성한 명령어가 없다.
//

ㄴ 이 코드의 실행과정을 그림으로 나타내면 아래 그림과 같음

Exam0140.java

$ java ... Exam0140↵

// # 클래스 변수와 인스턴스 변수 생성 시점과 메모리 영역

package com.eomcs.oop.ex03;

public class Exam0140 {

  static class A {
    static int v1;
    int v2;
  }

  public static void main(String[] args) {

    // 클래스 변수는 클래스가 로딩되는 순간 바로 사용할 수 있다.
    // 클래스가 로딩되는 경우:
    // - 클래스 변수나 클래스 메서드를 사용할 때
    // - 인스턴스를 생성할 때
    // - 단 중복 로딩되지 않는다.
    //
    A.v1 = 100;

    // v2 는 인스턴스 변수이기 때문에 사용하기 전에 new 명령으로 먼저 생성해야 한다.
    // A.v2 = 200; // 컴파일 오류!

    A p = new A();
    // 이제 v2 변수는 Heap에 생성되었다.
    // A클래스의 인스턴스를 만들 때
    // static 이 안붙은 변수(non-static 변수 = 인스턴스 변수)가 그 대상이다.
    //
    // v2 인스턴스 변수는 인스턴스 주소를 통해 사용해야 한다.
    // 클래스이름으로 사용할 수 없다.
    //    A.v2 = 200; // 컴파일 오류!

    p.v2 = 200; // OK!

    // 인스턴스 변수는 인스턴스를 만들 때 마다 생성된다.
    A p2 = new A(); // 새 v2 변수가 생성된다.
    p2.v2 = 300;

    System.out.printf("A.v1=%d, p.v2=%d, p2.v2=%d\n", A.v1, p.v2, p2.v2);
  }
}

ㄴ 이 코드의 실행과정을 그림으로 나타내면 아래 그림과 같음

 

설명

1. Exam0140.class를 메모리에 로딩한 다음 Exam0140.class 에 static 필드가 존재하는지 확인(해당 스태틱 필드 생성)

2. Exam0140.class에 main() 메서드가 존재하는지 확인 후, 존재한다면 main() 메서드 호출

3. main() 메서드 실행 전 main() 메서드가 사용할 로컬 변수를 JVM Stack 영역에 준비 (args, p, p2)

4. A.class의 스태틱 필드인 v1에 100을 넣어야 하는데, A.class가 아직 로딩 안 됐으므로 Method Area 영역에서 A.class 를 로딩하여 A.class에 스태틱 필드가 있는지 확인 후, 존재한다면 생성하고(v1 => 초기 값은 0) 스태틱 필드 v1 에 100을 저장

6. A.class의 설계도에 따라 Heap 영역에 논스태틱 변수(=인스턴스 변수) v2를 생성하고, 생성된 인스턴스 주소 1200(임의로 지정)을 p 레퍼런스에 저장

7. p 레퍼런스가 가리키는 인스턴스의 인스턴스 변수 v2(v2라는 필드)에 200을 저장

8. A.class의 설계도에 따라 Heap 영역에 새로운 논스태틱 변수(=인스턴스 변수) v2를 생성하고, 생성된 인스턴스 주소 1300(임의로 지정)을 p2 레퍼런스에 저장

9. p2 레퍼런스가 가리키는 인스턴스의 인스턴스 변수 v2(v2라는 필드)에 300을 저장