[4/8] Java 기본 Summary (w. 인프런 김영한)

차재현·2024년 12월 31일
0

[ Backend Study - Java ]

목록 보기
5/11
post-thumbnail

Section 7. 자바 메모리 구조와 static

Chapter 1. 자바 메모리 구조

  • 메서드 영역
    • 클래스 정보 보관 (바이트 코드(.class), 필드, 메서드, 생성자 등 모든 실행 코드)

      • 우리가 개발한 .java 파일에서 Class들의 모든 정보를
        컴파일 결과물인 바이트코드(.class)로 보관한다
    • static 변수 보관

    • 리터럴 변수 보관 (java가 알아서 최적화하여 보관해준다 정도로 알고 넘어가자)

      💡 인스턴스의 메서드 코드는 메서드 영역에 보관!

      1. A 메서드를 가지고있는 B 클래스로
        100개의 인스턴스를 생성하면, 힙 영역에 100개의 인스턴스가 보관된다.
      2. 이때 100개의 인스턴스에는 똑같은 A 메서드가 100개 있다.
        이것은 메모리 낭비이기 때문에 클래스의 메서드 코드는 메서드 영역에서 공통으로 보관한다.
      3. 힙 영역의 각 인스턴스에는 변수에 대한 메모리만 할당되고 메서드를 위한 메모리 할당하지 않는다.
      4. 즉, 인스턴스를 통해 메서드를 호출한다면 메서드 영역의 메서드 코드를 호출하여 실행하는 것이다.
  • 스택 영역
    • 실제 프로그램이 실행되는 영역
    • 메서드가 호출되어 실행될 때 마다 하나씩 스택 프레임이 쌓임
      • 각 스택 프레임에는 지역 변수, 중간 연산결과, 메소드 호출 정보 등을 포함
    • 맨 마지막 메서드부터 실행이 끝나면 스택 프레임이 제거됨
  • 힙 영역
    • new를 통해 만들어진 인스턴스 보관
    • 더 이상 참조되지 않는 인스턴스는 가비지 컬렉션(GC)이 제거함

Chapter 2. 스택과 큐 자료 구조

  • Stack
    • LIFO (후입 선출)
  • Queue
    • FIFO (선입 선출)
  • 프로그램 실행과 메서드 호출에는 스택 구조가 적합

Chapter 3. 스택 영역

  • 예시 코드
    package memory;
    
    public class JavaMemoryMain1 {
    
        public static void main(String[] args) {
            System.out.println("main start");
            method1(10);
            System.out.println("main end");
        }
    
        static void method1(int m1) {
            System.out.println("method1 start");
            int cal = m1 * 2;
            method2(cal);
            System.out.println("method1 end");
        }
    
        static void method2(int m2) {
            System.out.println("method2 start");
            System.out.println("method2 end");
        }
    }
    /* 출력 결과
    main start
    method1 start
    method2 start
    method2 end
    method1 end
    main end
    */
  • 정리
    • 자바는 스택 영역을 사용해서 메서드 호출과 지역 변수(매개변수 포함)를 관리한다.
    • 메서드를 계속 호출하면 스택 프레임이 계속 쌓인다.
    • 지역 변수(매개변수 포함)는 스택 영역에서 관리한다.
    • 스택 프레임이 종료되면 지역 변수도 함께 제거된다.
    • 스택 프레임이 모두 제거되면 프로그램도 종료된다.

Chapter 4. 스택 영역과 힙 영역

  • 예시 코드
    package memory;
    
    public class Data {
        private int value;
    
        public Data(int value) {
            this.value = value;
        }
    
        public int getValue() {
            return value;
        }
    }
    package memory;
    
    public class JavaMemoryMain2 {
    
        public static void main(String[] args) {
            System.out.println("main start");
            method1();
            System.out.println("main end");
        }
    
        static void method1() {
            System.out.println("method1 start");
            Data data1 = new Data(10);
            method2(data1);
            System.out.println("method1 end");
        }
    
        static void method2(Data data2) {
            System.out.println("method2 start");
            System.out.println("data.value=" + data2.getValue());
            System.out.println("method2 end");
        }
    
    }
  • 예시 코드 실행에 따른 스택 영역, 힙 영역 내부 모습

Chapter 5. static 변수

  • 특정 클래스로부터 생성된 다수의 인스턴스에서 공용으로 사용할 수 있는 변수이다!
  • 예시 코드
    package static1;
    
    public class Data3 {
        public String name;
        public static int count; //static
    
        public Data3(String name) {
            this.name = name;
            count++;
        }
    }
    package static1;
    
    public class DataCountMain3 {
    
        public static void main(String[] args) {
            Data3 data1 = new Data3("A");
            System.out.println("A count=" + Data3.count);
    
            Data3 data2 = new Data3("B");
            System.out.println("B count=" + Data3.count);
    
            Data3 data3 = new Data3("C");
            System.out.println("C count=" + Data3.count);
    
            //추가
            //인스턴스를 통한 접근 
            Data3 data4 = new Data3("D");
            System.out.println(data4.count); 
            
            Data3 data5 = new Data3("E");
            System.out.println(data5.count);
            // -> static 변수는 공용으로 사용하기 위함인데, 마치 각자 다른 변수처럼 보이게 된다.
    
            //클래스를 통한 접근
            System.out.println(Data3.count);
        }
    }
    /* 출력 결과
    A count = 1
    B count = 2
    C count = 3
    4
    4
    */
  • static 변수 정의 : 클래스에서 자료형 앞에 static 을 붙이면 된다.
  • static 변수 접근 : 클래스명.static변수명 (Data3.coount)

용어 정리

  • 멤버 변수 : Class 내부에 속해있는 변수
    • 인스턴스 변수
      • static 변수가 아닌 모든 변수
      • 인스턴스를 생성해야만 사용 가능
      • 인스턴스 생성시마다 새로 생성됨
    • 클래스 변수
      • = static 변수(또는 정적 변수)
      • 자바 프로그램 시작 시, 딱 1개 생성
      • 자바 프로그램 종료 시에 삭제됨

변수와 생명주기

  • 지역 변수(매개변수 포함)
    • 지역 변수는 스택 영역에 있는 스택 프레임 안에 보관된다. 메서드가 종료되면 스택 프레임도 제거 되는데 이때 해당 스택 프레임에 포함된 지역 변수도 함께 제거된다. 따라서 지역 변수는 생존 주기가 짧다.
  • 인스턴스 변수
    • 인스턴스에 있는 멤버 변수를 인스턴스 변수라 한다. 인스턴스 변수는 힙 영역을 사용한다.
      힙 영역은 가비지 컬렉션이 발생하기 전까지는 생존하기 때문에 보통 지역 변수보다 생존 주기가 길다.
  • 클래스 변수
    • 클래스 변수는 메서드 영역의 static 영역에 보관되는 변수이다. 메서드 영역은 프로그램 전체에서
      사용하는 공용 공간이다. 클래스 변수는 해당 클래스가 JVM에 로딩 되는 순간 생성된다. 그리고 JVM이 종료될 때 까지 생명주기가 이어진다. 따라서 가장 긴 생명주기를 가진다.

💡 개발자는 남이 내 코드를 가져다 쓰기 편하게 코딩해야 한다!
개발은 결국 협업으로 이뤄지기에, 남이 내 코드를 사용할 경우가 많다.
그래서 개발자는 남이 봤을 때 사용하기 편하게 코드를 작성해야 된다!
현재 static 변수 사용의 경우에도 클래스를 통한 접근을 추천하는 이유가
인스턴스로 접근하게되면 남이 보기에 클래스 변수인지, 인스턴스 변수인지 혼동을 줄 수 있기 때문이다.

따라서 static 변수라면, 누가 봐도 확실하게 알 수 있는 클래스를 통한 접근으로(클래스 변수로) 사용하는 것이 좋다!

Chapter 6. static 메서드

  • static 변수와 마찬가지로
    • 메서드의 자료형 앞에 static을 붙여 사용할 수 있다.
    • 정적 메서드 또는 클래스 메서드로 불린다.
    • 클래스명.static 메서드명 으로 호출하여 사용할 수 있다.
    • 다른 개발자에게 혼동을 주지 않기 위해 인스턴스명.static 메서드명 으로 호출하지 않는다.

정적 메서드 활용

  • 객체 생성이 필요 없이 메서드의 호출만으로 필요한 기능을 수행할 때 주로 사용한다.
    예를 들어 간단한 메서드 하나로 끝나는 유틸리티성 메서드에 자주 사용한다.
    수학의 여러가지 기능을 담은 클래스를 만들 수 있는데, 이 경우 인스턴스 변수 없이 입력한 값을
    계산하고 반환하는 것이 대부분이다.
    • 이럴 때 정적 메서드를 사용해서 유틸리티성 메서드를 만들면 좋다.
  • 단! static 메서드에는 제한이 있다.
    • static 메서드는 자신이 속해 있는 클래스의 static 변수와 static 메서드만 접근할 수 있다.
      즉, 인스턴스 변수나 인스턴스 메서드에는 접근할 수 없다.
    • 반대로, 접근 제어자만 허락한다면 어디서든지 static 메서드를 호출하여 사용할 수 있다.
    • 물론 인스턴스의 참조값을 매개변수로 넣어준다면 static 메서드도 인스턴스 변수, 메서드에 접근 가능!

간단히 컴파일 과정을 생각해보면 된다.
먼저 인스턴스 변수/메서드는 객체가 생성되어야 참조값이 생긴다.
그리고 참조값을 알아야 인스턴수 변수/메서드에 접근이 가능하다.

이제 코드가 컴파일 되는 과정을 보자.
1. 메인 메서드 실행 전에 배경 준비를 위해 Class 정보가 메서드 영역에 저장되어야 한다.
2. static 메서드 또한 같이 저장된다.
3. 하지만 이 순간에는 객체가 생성되지도 않기에 static 메서드 내부에선 인스턴스 변수/메서드를 어떤값을 가져와야할지 이해할 수 없다.
4. 그래서 컴파일 에러 발생!

  • 예시 코드
    package static2;
    
    public class DecoData {
    
        private int instanceValue;
        private static int staticValue;
    
        public static void staticCall() {
            //instanceValue++; //인스턴스 변수 접근, compile error
            //instanceMethod(); //인스턴스 메서드 접근, compile error
    
            staticValue++; //정적 변수 접근
            staticMethod(); //정적 메서드 접근
        }
    
        public static void staticCallWithParam(DecoData data1){ // 인스턴스 참조값 전달
            data1.instanceValue++; // 인스턴스 변수 접근 가능
            data1.instanceCall(); // 인스턴스 메서드 접근 가능
        }
    
        public void instanceCall() {
            instanceValue++; //인스턴스 변수 접근
            instanceMethod(); //인스턴스 메서드 접근
    
            staticValue++; //정적 변수 접근
            staticMethod(); //정적 메서드 접근
        }
    
        private void instanceMethod() {
            System.out.println("instanceValue=" + instanceValue);
        }
    
        private static void staticMethod() {
            System.out.println("staticValue=" + staticValue);
        }
    }
    
    package static2;
    
    //import static DecoData.staticCall();
    //import static DecoData.*;
    
    public class DecoDataMain {
    
        public static void main(String[] args) {
            System.out.println("1. 정적 호출");
            DecoData.staticCall();
    
            System.out.println("2. 인스턴스 호출1");
            DecoData data1 = new DecoData();
            data1.instanceCall();
    
            System.out.println("3. 인스턴스 호출2");
            DecoData data2 = new DecoData();
            data2.instanceCall();
    
            System.out.println("추가");
            //인스턴스를 통한 접근
            DecoData data3 = new DecoData();
            data3.staticCall(); // static 변수와 마찬가지로 이렇게 쓰지 말자!
    
            //클래스를 통한 접근
            DecoData.staticCall();
    
            System.out.println("매개변수로 인스턴스 참조값 넘겨주기");
            DecoData.staticCallWithParam(data3); 
            // 만약 인스턴스 참조값을 전달해 준다면 static 메서드에서 인스턴스 변수/메서드 접근 가능!
        }
    }
    
    /* 출력 결과
    1. 정적 호출
    staticValue=1
    2. 인스턴스 호출1
    instanceValue=1
    staticValue=2
    3. 인스턴스 호출2
    instanceValue=1
    staticValue=3
    추가
    staticValue=4
    staticValue=5
    매개변수로 인스턴스 참조값 넘겨주기
    instanceValue=2
    staticValue=6
    */
  • import static ~
    • static 메서드, static 변수를 자주 사용하게 된다면 상단에 아예 import 할 수 있다!
    • import static으로 고정해두면, 클래스명. 으로 호출하지 않고 바로 메서드/변수명으로 호출하여 사용할 수 있다.
      DecoData.staticCall(); //기존 static 메서드 호출방법
      ⬇️
      import static DecoData.staticCall();
      ...
      staticCall(); //바로 이렇게 사용 가능!

💡 main() 메서드도 static 메서드

public static void main(String[] args){
...
}

우리가 지금까지 흔히 봐왔던 main() 메서드이다.
main() 메서드도 static 메서드이기에 static 메서드와 static 변수만 호출할 수 있다!

  • 즉, main() 메서드 안에서 할 수 있는 것에는 제한이 있다!
    • 현재 위치하고 있는 Class의 내부 static 메서드/변수 접근 가능
    • 객체 생성 및 객체 멤버 메서드/변수 접근 가능
    • 지역 변수 선언 가능

💡 객체 생성을 금지하고 싶다면, 생성자를 private 해주면 된다!

[ TIP 유용한 단축키 ]

  • option + command + Enter
    • 새로운 객체를 생성할 때, new ~ 만 입력하면, 생성자를 기준으로 자료형과 변수를 앞에 만들어준다!
  • option + enter
    • 변수나 메서드를 자동으로 생성해준다!
    • static 메서드 호출 시, 클래스명. 으로 호출하는데, 단축키 사용하면 import static 해줌과 동시에
      호출에 사용했던 클래스명. 을 자동으로 모두 제거해준다!
    • 매우 사용성이 좋으니 적극 활용해보자!
profile
Develop what? and why?

0개의 댓글