[Java 초급] 2. 자바의 변수와 메모리 구조 (응용편)

Kyung Jae, Cheong·2024년 8월 22일
0
post-thumbnail

2. 자바의 변수와 메모리 구조 (응용편)

  • Java에서 변수는 크게 기본형 변수(Primitive Type)와 참조형 변수(Reference Type)로 나뉩니다.
    • 이 두 가지는 메모리 구조, 사용 방법, 그리고 메서드와 클래스에서의 특징 등에서 차이가 있습니다.

2-1. 기본형 변수와 참조형 변수

기본형 변수 (Primitive Type)

  • 기본형 변수는 Java에서 가장 기본적인 데이터 타입입니다.
  • Java는 총 8가지의 기본형 타입을 제공합니다
    • 정수형: byte, short, int, long
    • 실수형: float, double
    • 문자형: char
    • 논리형: boolean
  • 기본형 변수는 실제 값을 직접 저장하며, 메모리의 스택(Stack) 영역에 저장됩니다.
    • 메모리 영역에 대해선 밑에서 다시 정리할 예정입니다.
int age = 25;
double salary = 3000.50;
char grade = 'A';
boolean isStudent = true;
  • 위 예시에서, age, salary, grade, isStudent 변수는 각각 스택에 실제 값을 저장합니다.

참조형 변수 (Reference Type)

  • 참조형 변수는 기본형 변수와 달리, 객체의 주소(참조)를 저장하는 변수입니다.
    • 참조형 변수는 클래스, 배열, 인터페이스, 열거형 등으로 생성된 객체를 가리킵니다.
    • 실제 데이터는 힙(Heap) 메모리 영역에 저장되고, 참조형 변수는 이 데이터를 가리키는 주소값을 스택(Stack) 영역에 저장합니다.
  • 결국 기본형 변수가 아닌 변수는 모두 참조형 변수라 생각하시면 됩니다.
String name = "John Doe";
int[] scores = {85, 90, 78};
Student student = new Student("Jane Doe", 3);
  • 위 예시에서, name, scores, student 변수는 모두 참조형 변수입니다.
    • name 변수는 String 객체를 참조하고, scores 변수는 int 배열을 참조하며, student 변수는 Student 객체를 참조합니다.

2-2. Java의 메모리 구조

  • Java의 메모리 구조는 크게 세 가지로 나눌 수 있습니다
    • 몇개 더 있지만, 지금 단계에서 다룰 내용은 아닌듯하여 생략합니다.
  • Java의 메모리 구조
    • 메서드 영역(Method Area): 클래스 정보, 상수, static 변수, 메서드 코드 등을 저장합니다.
      • static에 관해선 추후 제어자를 다룰때 다시 정리 예정입니다.
    • 스택(Stack): 기본형 변수와 메서드 호출 시 생성되는 지역 변수들이 저장됩니다.
      • 메모리가 자동으로 할당되고 해제됩니다.
    • 힙(Heap): 객체와 배열이 저장됩니다. 참조형 변수가 가리키는 실제 데이터가 위치합니다.
      - 메모리 관리는 가비지 컬렉터(Garbage Collector)가 담당합니다.
  • 참고 할만한 자료 링크 : JAVA-☕-그림으로-보는-자바-코드의-메모리-영역스택-힙

2-3. 변수 선언 위치에 따른 변수 분류

  • Java에서 변수는 선언 위치에 따라 크게는 지역 변수(Local Variable)와 멤버 변수(Member Variable)로 분류됩니다.
    • 이들 변수는 선언 위치에 따라 메모리 할당 시점, 유효 범위, 접근 방법 등이 달라집니다.

2-3-1. 지역 변수 (Local Variable)

  • 지역 변수는 메서드나 생성자, 초기화 블록 내에서 선언된 변수로, 해당 블록 내에서만 유효합니다. 메서드나 블록이 끝나면 지역 변수는 메모리에서 사라집니다.
    • 선언 위치: 메서드, 생성자, 또는 초기화 블록 내부
    • 유효 범위: 해당 메서드나 블록 내에서만 사용 가능
    • 메모리 할당 시점: 메서드나 블록이 호출될 때 스택(Stack)에 메모리 할당
    • 기본값: 자동으로 초기화되지 않으므로 반드시 초기화해야 사용 가능
public void calculateSum() {
    int sum = 0;  // 지역 변수
    for (int i = 0; i < 10; i++) {
        sum += i;
    }
    System.out.println("Sum: " + sum);
    // 'sum' 변수는 calculateSum 메서드 내에서만 유효
}

매개변수 (Parameter Variable)

  • 메서드나 생성자에 인자로 전달되는 변수인 매개변수도 지역변수로 분류되는데, 매개변수는 메서드나 생성자가 호출될 때 생성되고 종료되면 소멸됩니다.
    • 선언 위치: 메서드 또는 생성자의 매개변수 선언부
    • 유효 범위: 메서드나 생성자가 실행되는 동안 유효
    • 메모리 할당 시점: 메서드나 생성자가 호출될 때 스택(Stack)에 메모리 할당
public void greet(String name) {  // 매개변수
    System.out.println("Hello, " + name);
}

2-3-2. 멤버 변수 (Member Variable)

  • 멤버 변수는 클래스의 속성으로, 객체가 생성될 때 힙(Heap) 메모리 영역에 할당됩니다. 멤버 변수는 다시 인스턴스 변수클래스 변수 두 가지로 나뉩니다.

인스턴스 변수 (Instance Variable)

  • 인스턴스 변수는 클래스 내에 선언되며, 각 객체(instance)가 개별적으로 가지는 변수입니다.
  • 객체가 생성될 때마다 별도로 할당되며, 객체가 소멸되면 함께 소멸됩니다.
    • 선언 위치: 클래스 내부, 메서드나 생성자 외부
    • 유효 범위: 객체가 존재하는 동안 해당 객체에서 접근 가능
    • 메모리 할당 시점: 객체가 생성될 때 힙(Heap)에 메모리 할당
    • 기본값: 자동으로 초기화됨 (숫자 타입은 0, 객체 참조는 null 등)
public class Person {
    String name;  // 인스턴스 변수
    int age;      // 인스턴스 변수

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void introduce() {
        System.out.println("My name is " + name + " and I am " + age + " years old.");
    }
}

클래스 변수 (Class Variable)

  • 클래스 변수는 static 키워드로 선언되며, 클래스 전체에서 공유됩니다.
  • 클래스 변수는 클래스가 메모리에 로드될 때 단 한 번 할당되며, 모든 객체가 이 변수를 공유합니다.
    • 선언 위치: 클래스 내부, static 키워드를 사용
    • 유효 범위: 클래스가 메모리에 있는 동안 유효, 모든 객체에서 공유
    • 메모리 할당 시점: 클래스가 메모리에 로드될 때 메서드 영역(Method Area)에 메모리 할당
    • 기본값: 자동으로 초기화됨 (숫자 타입은 0, 객체 참조는 null 등)
public class Counter {
    static int count = 0;  // 클래스 변수

    public Counter() {
        count++;  // 모든 객체가 공유하는 변수
    }

    public static void showCount() {
        System.out.println("Count: " + count);
    }
}

2-4. 참조형 변수의 메서드 및 클래스에서의 특징

2-4-1. 참조형 변수와 메서드

  • 참조형 변수를 메서드의 인자로 전달할 때, 해당 변수는 객체의 주소를 전달합니다.
    • 따라서 메서드 내에서 객체의 속성을 변경하면, 원본 객체에도 영향을 미칩니다.
public class ReferenceExample {
    public static void modifyArray(int[] arr) {
        arr[0] = 99;  // 첫 번째 요소를 99로 변경
    }

    public static void main(String[] args) {
        int[] numbers = {1, 2, 3};
        modifyArray(numbers);
        System.out.println(numbers[0]);  // 출력: 99
    }
}
  • 위 예시에서 modifyArray 메서드는 numbers 배열을 참조하는 주소를 전달받아 배열의 첫 번째 요소를 변경합니다.
    • 이 변경 사항은 메서드가 끝난 후에도 main 메서드에서 확인할 수 있습니다.

2-4-2. 참조형 변수와 객체 비교

  • 참조형 변수는 객체의 주소를 가리키기 때문에, 두 객체를 비교할 때는 == 연산자를 사용하는 것이 아닌 .equals() 메서드를 사용해야 합니다.
String name1 = new String("John");
String name2 = new String("John");

System.out.println(name1 == name2);       // 출력: false (주소가 다름)
System.out.println(name1.equals(name2));  // 출력: true (내용이 같음)
  • 위 예시에서 name1과 name2는 같은 문자열 값을 가지지만, 각각 다른 주소를 참조합니다.
    • == 연산자는 주소를 비교하므로 false를 반환하지만, .equals() 메서드는 객체의 내용을 비교하므로 true를 반환합니다.

2-4-3. 참조형 변수의 null 값

  • 참조형 변수는 null 값을 가질 수 있으며, 이는 해당 변수가 어떤 객체도 참조하지 않음을 의미합니다.
    • 이를 잘못 다루면 NullPointerException이 발생할 수 있습니다.
String name = null;
System.out.println(name.length());  // NullPointerException 발생
  • 위 코드에서 name 변수가 null 값을 가지므로, length() 메서드를 호출할 때 NullPointerException이 발생합니다.
    • 따라서 참조형 변수를 사용할 때는 null 여부를 반드시 확인해야 합니다.

2-4-4. 클래스와 참조형 변수의 관계

  • 클래스는 참조형 변수를 정의할 수 있는 설계도이며, 이를 통해 다양한 데이터 타입의 객체를 생성할 수 있습니다.
    • 참조형 변수를 통해 객체를 생성하고, 이 객체는 클래스에 정의된 속성과 메서드를 활용할 수 있습니다.
public class Person {
    String name;
    int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void introduce() {
        System.out.println("My name is " + name + " and I am " + age + " years old.");
    }

    public static void main(String[] args) {
        Person person1 = new Person("Alice", 30);
        person1.introduce();  // 출력: My name is Alice and I am 30 years old.

        Person person2 = person1;
        person2.name = "Bob";
        person1.introduce();  // 출력: My name is Bob and I am 30 years old.
    }
}

마무리

  • Java에서 기본형 변수와 참조형 변수는 메모리에서의 처리 방식과 사용 방법에서 큰 차이를 보입니다.
    • 기본형 변수는 값 자체를 저장하는 반면, 참조형 변수는 객체의 주소를 저장합니다.
    • 참조형 변수는 메서드에서 객체의 상태를 변경할 수 있고, 객체의 비교나 null 처리와 같은 중요한 개념을 포함합니다.
  • 이러한 차이를 이해하고 적절히 사용하는 것이 Java 프로그래밍의 중요한 부분이며, 객체지향 프로그래밍을 효과적으로 활용하기 위해 반드시 숙지해야 할 개념들입니다.
  • 다음 포스팅에서는 패키지와 클래스의 활용법에 대해서 다루어보도록 하겠습니다.
profile
일 때문에 포스팅은 잠시 쉬어요 ㅠ 바쁘다 바빠 모두들 화이팅! // Machine Learning (AI) Engineer & BackEnd Engineer (Entry)

0개의 댓글