본문 바로가기
Everyday Study

2024.06.28 (금) { 상속, 자동변수, 참조변수, 액세스연산자, 클래스 리터럴 }

by xogns93 2024. 6. 28.

**자동 변수(Automatic Variable)**는 주로 **지역 변수(Local Variable)**를 의미합니다. C, C++ 등 많은 프로그래밍 언어에서 자동 변수는 함수나 블록이 시작될 때 자동으로 생성되고, 해당 함수나 블록이 끝날 때 자동으로 소멸되는 변수를 말합니다.

특징:

  • 자동 할당 및 해제: 함수가 호출될 때 메모리가 자동으로 할당되고, 함수가 반환될 때 메모리가 자동으로 해제됩니다.
  • 지역성: 자동 변수는 선언된 함수나 블록 내부에서만 접근할 수 있습니다.
  • 스택 영역: 일반적으로 자동 변수는 스택 메모리 영역에 할당됩니다.

2. 메모리 관리 측면에서의 자동 변수

일부 프로그래밍 언어에서는 변수의 수명 관리 방식을 설명할 때 자동 변수와 정적 변수(Static Variable)를 구분하기도 합니다.

  • 자동 변수(Automatic Variable): 함수 호출 시점에 생성되고 종료 시점에 소멸됩니다. 주로 스택에 할당됩니다.
  • 정적 변수(Static Variable): 프로그램 실행 동안 메모리에 유지됩니다. 주로 데이터 영역에 할당됩니다.

자바에서의 참조 변수

자바에서 참조 변수는 객체를 가리킵니다. 기본 자료형 변수는 실제 값을 저장하지만, 객체는 참조 변수를 통해 메모리 힙에 저장된 객체를 참조합니다. 이를 통해 객체의 멤버 변수와 메서드에 접근할 수 있습니다.

class MyClass {
    int value;
    MyClass(int value) {
        this.value = value;
    }
}

public class Main {
    public static void main(String[] args) {
        MyClass obj = new MyClass(10); // obj는 MyClass 객체를 참조
        MyClass ref = obj; // ref도 같은 객체를 참조
        ref.value = 20; // obj의 value 값이 20으로 변경됨
        System.out.println(obj.value); // 출력: 20
    }
}

참조 변수의 특징 (자바)

  • 객체 참조: 참조 변수는 객체를 가리키며, 객체의 멤버와 메서드에 접근할 수 있습니다.
  • 값 변경: 참조 변수를 통해 객체의 값을 변경하면, 원본 객체도 영향을 받습니다.
  • null 참조: 초기화되지 않은 참조 변수는 null 값을 가질 수 있으며, 이를 통해 아무 객체도 참조하지 않는 상태를 나타냅니다.

 


디폴트 생성자란?

  • 디폴트 생성자 (Default Constructor): 클래스에 생성자가 명시적으로 정의되지 않은 경우, 컴파일러가 자동으로 제공하는 매개변수가 없는 생성자입니다. 그러나, 클래스에 매개변수가 있는 생성자가 하나라도 정의되어 있다면, 컴파일러는 디폴트 생성자를 자동으로 생성하지 않습니다. 이 경우 디폴트 생성자를 사용하고자 한다면, 직접 정의해주어야 합니다.

 

  • 인스턴스 (Instance): 클래스에서 생성된 객체. 예: Person person1 = new Person();
  • 오브젝트 (Object): 클래스의 인스턴스를 의미하며, 상태와 동작을 포함하는 소프트웨어 엔티티.
  • 메서드 (Method): 클래스의 동작을 정의하는 함수. 객체의 상태를 변경하거나 특정 작업을 수행함.

 

클래스 패스 (Classpath):

클래스 패스는 자바 프로그램이 클래스 파일을 찾는 경로를 설정하는 방법입니다. 자바에서 클래스 파일들은 패키지에 따라 여러 디렉토리나 JAR 파일에 분산되어 있을 수 있습니다. 클래스 패스를 설정함으로써 JVM(Java Virtual Machine)은 클래스 파일을 올바르게 찾아서 실행할 수 있습니다.

일반적으로 클래스 패스는 환경 변수를 통해 설정하거나 실행할 때 -classpath 또는 -cp 옵션을 사용하여 지정합니다. 

 

자바에서는 모든 클래스가 기본적으로 Object 클래스를 상속받습니다. Object 클래스는 자바 클래스의 최상위 클래스이며, 모든 클래스는 Object 클래스의 하위 클래스입니다. 이는 자바의 모든 객체가 Object 클래스의 멤버를 사용할 수 있음을 의미합니다.

자바에서 클래스는 오브젝트 클래스를 상속 (눈에보이진않지만)


메서드 영역 (Method Area 또는 PermGen/메타스페이스):

  • 클래스에 대한 메타데이터 (클래스 이름, 메서드 정보, 상수 풀 등)가 저장됩니다.
  • 스태틱 변수(static 변수)도 여기에 할당됩니다.

메서드 영역에는 클래스의 구조(메서드 코드, 상수, 변수의 이름 등)가 저장됩니다. 이 영역에는 스태틱 필드(static fields)도 포함됩니다. 스태틱 필드는 클래스 단위로 하나만 생성되어 프로그램 실행 동안 메모리에 유지되며, 모든 인스턴스가 공유하는 변수입니다. 따라서 어떤 클래스의 인스턴스를 생성하든지 간에 해당 클래스의 스태틱 필드는 동일한 메모리 공간을 참조하게 됩니다.

 

힙 영역 (Heap):

  • 객체 인스턴스가 저장되는 영역입니다.
  • 프로그램 실행 중 동적으로 생성된 객체들이 여기에 할당됩니다.
  • 가비지 컬렉션에 의해 관리됩니다.

힙 영역은 객체 인스턴스들이 생성될 때 사용됩니다. 자바 런타임은 힙 영역에서 객체의 메모리 할당과 해제를 관리하며, 가비지 컬렉션을 통해 더 이상 사용되지 않는 객체들을 정리합니다. 객체의 인스턴스 변수들은 각 객체의 힙 영역에 저장되며, 인스턴스마다 고유한 값을 가집니다.

 


메모리 구조 요약

 

  1. 힙(Heap):
    • 객체와 인스턴스 변수가 저장됩니다.
    • 인스턴스 필드(instanceCounter)는 힙에 저장됩니다.
  2. 스택(Stack):
    • 각 스레드마다 별도의 스택이 생성되며, 메서드 호출 시마다 프레임이 생성되어 지역 변수와 중간 결과값을 저장합니다.
  3. 메서드 에리어(Method Area):
    • 클래스 메타데이터, static 필드, 상수 풀, 메서드 코드가 저장됩니다.
    • static 필드(staticCounter)는 메서드 에리어에 저장됩니다.

결론

  • 메서드 에리어는 JVM의 중요한 메모리 영역으로, 클래스 수준의 데이터를 저장합니다.
  • static 필드는 메서드 에리어에 저장되며, 클래스가 로드될 때 초기화되고, 해당 클래스의 모든 인스턴스가 공유합니다.
  • 인스턴스 필드는 각 객체마다 힙에 개별적으로 저장됩니다.

 

자바에서 참조 변수의 크기는 고정적입니다. 대부분의 자바 가상 머신(JVM) 구현체에서는 참조 변수의 크기가 4바이트 또는 8바이트로 결정됩니다. 이는 플랫폼에 따라 다를 수 있지만, 보통은 32비트 JVM에서는 4바이트, 64비트 JVM에서는 8바이트로 설정됩니다.

따라서 자바의 참조 변수는 메모리 상에서 고정된 크기를 가지며, 이 크기에 따라 offset(오프셋)을 계산하여 객체의 필드나 배열 요소에 접근할 수 있습니다. 예를 들어, 객체의 인스턴스 변수에 접근할 때는 객체의 시작 위치에서 해당 변수의 오프셋을 더해 실제 메모리 위치를 계산합니다.

 


액세스 연산자(access operator)

프로그래밍 언어에서 변수나 메서드에 접근할 때 사용하는 기호입니다. 주로 객체 지향 프로그래밍 언어에서 사용되며, 다양한 언어마다 다양한 형태의 액세스 연산자가 있을 수 있습니다. 여기서는 몇 가지 주요 언어의 액세스 연산자를 설명하겠습니다.

 

1. 점 연산자 (Dot Operator)

가장 일반적으로 사용되는 액세스 연산자는 점 연산자(.)입니다. 점 연산자는 객체의 멤버(필드와 메서드)에 접근할 때 사용됩니다. 예를 들어, 자바에서는 다음과 같이 사용됩니다.

class MyClass {
    int myField;
    
    void myMethod() {
        System.out.println("Hello, World!");
    }
}

public class Main {
    public static void main(String[] args) {
        MyClass obj = new MyClass();
        obj.myField = 10;     // 점 연산자로 필드에 접근
        obj.myMethod();       // 점 연산자로 메서드 호출
    }
}

 

2. 대괄호 연산자 (Bracket Operator)

대괄호 연산자([])는 주로 배열이나 컬렉션 등의 인덱스 기반 요소 접근에 사용됩니다. 예를 들어, 배열에서 특정 인덱스의 요소에 접근할 때 대괄호 연산자가 사용됩니다.

int[] arr = {1, 2, 3, 4, 5};
int element = arr[2];   // 대괄호 연산자로 배열 요소 접근

 


this 키워드

자바와 같은 객체 지향 프로그래밍 언어에서 사용되는 특수한 키워드입니다. this는 현재 객체를 가리키는 참조(Reference)를 의미하며, 주로 다음과 같은 상황에서 사용됩니다.

 

1. 현재 객체의 인스턴스 변수에 접근

this 키워드를 사용하여 현재 객체의 인스턴스 변수에 접근할 수 있습니다. 이는 특히 메서드의 매개변수와 인스턴스 변수의 이름이 같을 때 유용합니다.

public class MyClass {
    private int x;

    public void setX(int x) {
        this.x = x;  // this를 사용하여 인스턴스 변수에 접근
    }
}

위 예제에서 setX 메서드의 매개변수 x와 인스턴스 변수 x의 이름이 같기 때문에 this를 사용하여 인스턴스 변수에 접근합니다.

 

2. 다른 생성자 호출

한 생성자에서 다른 생성자를 호출할 때 this 키워드를 사용할 수 있습니다. 이러한 기능을 생성자에서만 사용할 수 있으며, 객체를 초기화하는 데 유용합니다.

public class MyClass {
    private int x;
    private int y;

    public MyClass() {
        this(0, 0);  // 다른 생성자 호출
    }

    public MyClass(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

위 예제에서 MyClass() 생성자는 this(0, 0);을 통해 다른 생성자 MyClass(int x, int y)를 호출하여 코드의 중복을 줄입니다.

 

주의사항

  • this는 인스턴스 메서드에서만 사용할 수 있습니다. 정적(static) 메서드에서는 this를 사용할 수 없습니다.
  • this는 현재 객체의 참조를 가리키므로, 객체가 생성되어야 사용할 수 있습니다. 생성자 내부에서 this를 사용할 때는 반드시 다른 생성자 호출이나 인스턴스 변수에 접근을 해야 합니다.

 


클래스 리터럴

클래스 리터럴은 클래스 정의를 의미하는 코드 블록을 말합니다. 객체지향 프로그래밍에서 클래스는 객체를 생성하기 위한 청사진 역할을 하며, 클래스 리터럴은 이 클래스의 정의를 포함하는 코드입니다. 

 

public class Car {
    // 클래스 변수 (정적 변수)
    public static String type = "Vehicle";

    // 인스턴스 변수
    private String brand;
    private String model;
    private int year;
    private int speed;

    // 생성자
    public Car(String brand, String model, int year) {
        this.brand = brand;
        this.model = model;
        this.year = year;
        this.speed = 0; // 초기 속도는 0
    }

    // 인스턴스 메서드: 속도 증가
    public void accelerate(int increment) {
        this.speed += increment;
    }

    // 인스턴스 메서드: 속도 감소
    public void decelerate(int decrement) {
        this.speed -= decrement;
        if (this.speed < 0) {
            this.speed = 0; // 속도는 0보다 작을 수 없음
        }
    }

    // 인스턴스 메서드: 현재 속도 반환
    public int getSpeed() {
        return this.speed;
    }

    // 클래스 메서드 (정적 메서드)
    public static String getType() {
        return type;
    }

    // 인스턴스 변수 접근자
    public String getBrand() {
        return brand;
    }

    public String getModel() {
        return model;
    }

    public int getYear() {
        return year;
    }
}

위 코드에서 public class Car { ... } 부분이 클래스 리터럴입니다. 이 리터럴은 다음과 같은 구성 요소를 포함합니다:

  1. 클래스 이름: Car는 클래스의 이름입니다.
  2. 클래스 변수: type은 클래스 변수로, 모든 인스턴스가 공유하는 데이터입니다.
  3. 인스턴스 변수: brand, model, year, speed는 인스턴스 변수로, 각 인스턴스에 고유한 데이터를 저장합니다.
  4. 생성자: Car(String brand, String model, int year)는 생성자로, 새로운 인스턴스를 초기화합니다.
  5. 인스턴스 메서드: accelerate와 decelerate는 특정 인스턴스와 관련된 동작을 정의합니다.
  6. 클래스 메서드 (정적 메서드): getType은 클래스 자체에 관련된 동작을 정의합니다.
  7. 인스턴스 메서드: getSpeed는 현재 속도를 반환합니다.
  8. 접근자 메서드: getBrand, getModel, getYear는 인스턴스 변수에 접근하는 메서드입니다.

이 클래스를 사용하여 객체를 생성하고 메서드를 호출할 수 있습니다:

 

 

밑은 Car 클래스를 사용하는 예제

public class Main {
    public static void main(String[] args) {
        // Car 클래스의 인스턴스 생성
        Car myCar = new Car("Toyota", "Corolla", 2020);

        // 인스턴스 메서드 호출: 속도 증가
        myCar.accelerate(50);
        System.out.println("Current speed: " + myCar.getSpeed());  // Current speed: 50

        // 인스턴스 메서드 호출: 속도 감소
        myCar.decelerate(20);
        System.out.println("Current speed: " + myCar.getSpeed());  // Current speed: 30

        // 클래스 메서드 호출
        System.out.println("Car type: " + Car.getType());  // Car type: Vehicle

        // 인스턴스 변수 접근자 호출
        System.out.println("Brand: " + myCar.getBrand());  // Brand: Toyota
        System.out.println("Model: " + myCar.getModel());  // Model: Corolla
        System.out.println("Year: " + myCar.getYear());    // Year: 2020
    }
}

 


int[] anArray; 배열 참조변수 null값이 세팅되버린다

anArray = new int[10];

자바에서 배열도 클래스 취급

 

' instanceof ' 는 자바에서 사용되는 연산자로, 특정 객체가 특정 클래스의 인스턴스인지를 확인하는 데 사용됩니다. 이 연산자는 boolean 값을 반환하며, 객체가 해당 클래스의 인스턴스이거나 해당 클래스의 하위 클래스의 인스턴스일 때 true를 반환하고, 그렇지 않을 때 false를 반환합니다. 

 


 

상속의 주요 개념과 용어

 

부모 클래스 (슈퍼 클래스, superclass)

  • 다른 클래스에게 상속을 주는 클래스를 말합니다.
  • 상속하는 클래스는 extends 키워드를 사용하여 부모 클래스를 지정합니다.

자식 클래스 (서브 클래스, subclass)

  • 다른 클래스로부터 상속을 받는 클래스를 말합니다.
  • 부모 클래스의 모든 멤버(필드와 메서드)를 상속받아 사용할 수 있습니다.
  • 필요에 따라 새로운 멤버를 추가하거나 부모 클래스의 메서드를 재정의할 수 있습니다.

 

재사용성과 확장성

  • 상속을 통해 부모 클래스의 코드를 재사용할 수 있습니다. 예를 들어, 여러 클래스가 공통된 특성을 가지고 있을 때 각각의 클래스마다 중복되는 코드를 작성하지 않고, 부모 클래스에서 공통 부분을 정의하고 이를 상속받아 사용할 수 있습니다.
  • 자식 클래스는 부모 클래스의 기능을 확장할 수 있습니다. 즉, 부모 클래스에 존재하는 메서드를 오버라이딩(재정의)하거나 새로운 메서드를 추가하여 자신만의 기능을 추가할 수 있습니다.

단일 상속 vs 다중 상속

  • 자바에서는 단일 상속(single inheritance)만을 지원합니다. 즉, 하나의 클래스는 하나의 직계 부모 클래스만을 가질 수 있습니다.
  • 이는 클래스 간의 복잡성을 줄이고, 코드의 충돌 가능성을 방지하기 위한 것입니다.
  • 인터페이스(interface)를 이용하여 다중 상속과 유사한 효과를 얻을 수 있습니다.