JavaRush /Java Blog /Random-KO /Java의 할당 및 초기화
Viacheslav
레벨 3

Java의 할당 및 초기화

Random-KO 그룹에 게시되었습니다

소개

컴퓨터 프로그램의 주요 목적은 데이터 처리입니다. 데이터를 처리하려면 어떻게든 데이터를 저장해야 합니다. 데이터가 어떻게 저장되는지 이해하는 것이 좋습니다.
Java의 할당 및 초기화 - 1

변수

변수는 모든 데이터를 저장하는 컨테이너입니다. Oracle의 공식 튜토리얼: 멤버 변수 선언을 살펴보겠습니다 . 이 튜토리얼에 따르면 여러 유형의 변수가 있습니다.
  • 필드 : 클래스에 선언된 변수.
  • 지역 변수 : 메서드 또는 코드 블록의 변수입니다.
  • 매개변수 : 메소드 선언의 변수(서명에 있음)
모든 변수에는 변수 유형과 변수 이름이 있어야 합니다.
  • 변수의 유형은 변수가 나타내는 데이터(즉, 저장할 수 있는 데이터)를 나타냅니다. 우리가 알고 있듯이 변수의 유형은 원시적 ( 비원시적)이 아닌 원시적(기본값) 또는 객체 일 수 있습니다. 객체 변수의 경우 해당 유형은 특정 클래스에 의해 설명됩니다.
  • 변수 이름은 소문자여야 합니다(카멜 표기법의 경우). " 변수:이름 지정 " 에서 이름 지정에 대한 자세한 내용을 읽을 수 있습니다 .
또한 클래스 수준 변수인 경우, 즉 클래스 필드이므로 액세스 한정자를 지정할 수 있습니다. 자세한 내용은 클래스 멤버에 대한 액세스 제어를 참조하세요 .

변수 선언

그래서 우리는 변수가 무엇인지 기억합니다. 변수 작업을 시작하려면 변수를 선언해야 합니다. 먼저 지역변수를 살펴보겠습니다. IDE 대신 편의상 튜토리얼 포인트인 Online IDE 의 온라인 솔루션을 사용하겠습니다 . 온라인 IDE에서 이 간단한 프로그램을 실행해 보겠습니다.
public class HelloWorld{
    public static void main(String []args){
        int number;
        System.out.println(number);
    }
}
보시다시피 name number과 type을 사용하여 지역 변수를 선언했습니다 int. “Execute” 버튼을 누르면 오류가 발생합니다:
HelloWorld.java:5: error: variable number might not have been initialized
        System.out.println(number);
무슨 일이에요? 변수를 선언했지만 해당 값을 초기화하지 않았습니다. 이 오류는 실행 시(즉, 런타임이 아닌) 발생하지 않고 컴파일 시 발생했다는 점은 주목할 가치가 있습니다. 스마트 컴파일러는 지역 변수에 액세스하기 전에 해당 변수가 초기화되는지 여부를 확인했습니다. 따라서 다음과 같은 진술이 이어집니다.
  • 지역 변수는 초기화된 후에만 액세스해야 합니다.
  • 지역 변수에는 기본값이 없습니다.
  • 지역 변수의 값은 컴파일 타임에 확인됩니다.
따라서 변수를 초기화해야 한다고 들었습니다. 변수를 초기화하는 것은 변수에 값을 할당하는 것입니다. 그러면 그것이 무엇인지, 왜 그런지 알아봅시다.

지역 변수 초기화

변수 초기화는 Java에서 가장 까다로운 주제 중 하나입니다. 왜냐하면... 메모리 작업, JVM 구현, JVM 사양 및 기타 무섭고 까다로운 작업과 매우 밀접하게 관련되어 있습니다. 하지만 적어도 어느 정도는 알아내려고 노력할 수 있습니다. 단순한 것에서 복잡한 것으로 가봅시다. 변수를 초기화하기 위해 할당 연산자를 사용하고 이전 코드의 줄을 변경합니다.
int number = 2;
이 옵션에서는 오류가 발생하지 않으며 값이 화면에 표시됩니다. 이 경우 어떻게 되나요? 추론해 봅시다. 변수에 값을 할당하려면 해당 변수에 값을 저장해야 합니다. 값은 어딘가에 저장되어야 한다는 것이 밝혀졌습니다. 그런데 어디에 있습니까? 디스크에? 그러나 이는 매우 느리며 우리에게 제한을 가할 수 있습니다. 우리가 "지금 여기"에 데이터를 빠르고 효율적으로 저장할 수 있는 유일한 장소는 메모리라는 것이 밝혀졌습니다. 이는 메모리에 일부 공간을 할당해야 함을 의미합니다. 이것은 사실이다. 변수가 초기화되면 프로그램이 실행될 Java 프로세스에 할당된 메모리에 해당 변수에 대한 공간이 할당됩니다. Java 프로세스에 할당된 메모리는 여러 영역으로 나뉩니다. 어느 쪽이 공간을 할당할지는 변수가 어떤 유형으로 선언되었는지에 따라 달라집니다. 메모리는 힙(Heap), 스택(Stack) 및 비힙(Non-Heap) 섹션으로 구분됩니다 . 스택 메모리부터 시작해 보겠습니다. 스택은 스택(예: 책 더미)으로 번역됩니다. LIFO(Last In, First Out) 데이터 구조입니다. 즉, 책 더미와 같습니다. 책을 추가할 때는 맨 위에 놓고, 빼낼 때는 맨 위에 있는 책(즉, 가장 최근에 추가한 책)을 가져옵니다. 그래서 우리는 프로그램을 시작합니다. 우리가 알고 있듯이 Java 프로그램은 JVM, 즉 Java 가상 머신에 의해 실행됩니다. JVM은 프로그램 실행이 시작되어야 하는 위치를 알아야 합니다. 이를 위해 "진입점"이라고 하는 기본 메서드를 선언합니다. JVM에서 실행하기 위해 메인 스레드(Thread)가 생성됩니다. 스레드가 생성되면 메모리에 자체 스택이 할당됩니다. 이 스택은 프레임으로 구성됩니다. 각각의 새로운 메소드가 스레드에서 실행될 때, 새로운 프레임이 할당되고 스택의 맨 위에 추가됩니다(책 더미에 있는 새 책과 같습니다). 이 프레임에는 객체 및 기본 유형에 대한 참조가 포함됩니다. 예, 그렇습니다. int는 스택에 저장됩니다. 왜냐하면... int는 기본 유형입니다. 프레임을 할당하기 전에 JVM은 프레임에 무엇을 저장할지 이해해야 합니다. 이러한 이유로 "변수가 초기화되지 않았을 수 있습니다"라는 오류가 발생합니다. 초기화되지 않으면 JVM이 스택을 준비할 수 없기 때문입니다. 따라서 프로그램을 컴파일할 때 스마트 컴파일러는 실수를 방지하고 모든 것을 망가뜨리는 것을 방지하는 데 도움이 됩니다. (!) 명확성을 위해 저는 매우 훌륭한 기사인 " Java Stack and Heap: Java Memory Allocation Tutorial "을 추천합니다. 마찬가지로 멋진 동영상으로 연결됩니다.
메서드가 완료된 후 이러한 메서드에 할당된 프레임은 스레드의 스택에서 삭제되고 모든 데이터와 함께 이 프레임에 할당된 메모리도 함께 지워집니다.

지역 개체 변수 초기화

코드를 좀 더 까다롭게 다시 변경해 보겠습니다.
public class HelloWorld{

    private int number = 2;

    public static void main(String []args){
        HelloWorld object = new HelloWorld();
        System.out.println(object.number);
    }

}
여기서 무슨 일이 일어날까요? 그것에 대해 다시 이야기합시다. JVM은 프로그램을 어디에서 실행해야 하는지 알고 있습니다. 그녀는 주요 방법을 봅니다. 스레드를 생성하고 이에 대한 메모리를 할당합니다(결국 스레드는 실행에 필요한 데이터를 어딘가에 저장해야 합니다). 이 스레드에서는 기본 메서드에 프레임이 할당됩니다. 다음으로 HelloWorld 개체를 만듭니다. 이 개체는 더 이상 스택에 생성되지 않고 힙에 생성됩니다. 객체는 기본 유형이 아니라 객체 유형이기 때문입니다. 그리고 스택은 힙의 객체에 대한 참조만 저장합니다(어떻게든 이 객체에 액세스해야 합니다). 다음으로, main 메소드의 스택에는 println 메소드를 실행하기 위한 프레임이 할당됩니다. 기본 메소드를 실행하면 모든 프레임이 삭제됩니다. 프레임이 파괴되면 모든 데이터가 파괴됩니다. 객체 객체는 즉시 파괴되지 않습니다. 첫째, 이에 대한 참조가 파괴되어 아무도 더 이상 객체 객체를 참조하지 않으며 메모리에서 이 객체에 대한 액세스가 더 이상 불가능합니다. 스마트 JVM에는 이를 위한 자체 메커니즘, 즉 가비지 수집기(가비지 수집기 또는 줄여서 GC)가 있습니다. 그런 다음 다른 사람이 참조하지 않는 메모리 개체를 제거합니다. 이 프로세스는 위에 제공된 링크에서 다시 설명되었습니다. 설명이 담긴 영상도 있어요.

필드 초기화 중

클래스에 지정된 필드의 초기화는 해당 필드가 정적인지 여부에 따라 특별한 방식으로 발생합니다. 필드에 static 키워드가 있는 경우 이 필드는 클래스 자체를 참조하고, static이라는 단어가 지정되지 않은 경우 이 필드는 클래스의 인스턴스를 참조합니다. 예를 들어 이를 살펴보겠습니다.
public class HelloWorld{
    private int number;
    private static int count;

    public static void main(String []args){
        HelloWorld object = new HelloWorld();
        System.out.println(object.number);
    }
}
이 예에서는 필드가 서로 다른 시간에 초기화됩니다. 숫자 필드는 HelloWorld 클래스 객체가 생성된 후 초기화됩니다. 그러나 Java 가상 머신이 클래스를 로드하면 count 필드가 초기화됩니다. 클래스 로딩은 별개의 주제이므로 여기서는 섞지 않겠습니다. 클래스가 런타임에 알려지면 정적 변수가 초기화된다는 점을 아는 것은 가치가 있습니다. 여기서 더 중요한 것이 있는데 여러분은 이미 이것을 알아차렸습니다. 어디에도 값을 지정하지 않았지만 작동합니다. 그리고 실제로. 필드인 변수는 지정된 값이 없으면 기본값으로 초기화됩니다. 숫자 값의 경우 부동 소수점 숫자의 경우 0 또는 0.0입니다. 부울의 경우 이는 거짓입니다. 그리고 모든 객체 유형 변수의 경우 값은 null이 됩니다(이에 대해서는 나중에 설명하겠습니다). 왜 그럴까요? 하지만 객체는 힙(힙 내)에 생성되기 때문입니다. 이 영역에 대한 작업은 런타임에 수행됩니다. 그리고 실행 전에 메모리를 준비해야 하는 스택과 달리 런타임에 이러한 변수를 초기화할 수 있습니다. 이것이 Java에서 메모리가 작동하는 방식입니다. 그런데 여기에는 또 하나의 특징이 있습니다. 이 작은 조각은 기억의 여러 구석을 다루고 있습니다. 우리가 기억하는 것처럼 프레임은 기본 메소드를 위해 스택 메모리에 할당됩니다. 이 프레임은 힙 메모리에 객체에 대한 참조를 저장합니다. 그런데 카운트가 어디에 저장되나요? 우리가 기억하는 것처럼 이 변수는 객체가 힙에 생성되기 전에 즉시 초기화됩니다. 이것은 정말 까다로운 질문입니다. Java 8 이전에는 PERMGEN이라는 메모리 영역이 있었습니다. Java 8부터 이 영역이 변경되어 METASPACE라고 합니다. 기본적으로 정적 변수는 클래스 정의의 일부입니다. 메타데이터입니다. 따라서 메타데이터 저장소인 METASPACE에 저장되는 것이 논리적이다. MetaSpace는 동일한 Non-Heap 메모리 영역에 속하며 그 일부입니다. 변수가 선언되는 순서를 고려하는 것도 중요합니다. 예를 들어 다음 코드에는 오류가 있습니다.
public class HelloWorld{

    private static int b = a;
    private static int a = 1;

    public static void main(String []args){
        System.out.println(b);
    }

}

null이 무엇인가요?

위에서 설명한 대로 객체 유형의 변수가 클래스의 필드인 경우 기본값으로 초기화되며 해당 기본값은 null입니다. 하지만 Java에서 null은 무엇입니까? 가장 먼저 기억해야 할 점은 기본 유형은 null이 될 수 없다는 것입니다. 그리고 null은 어떤 객체도 참조하지 않는 특수 참조이기 때문입니다. 따라서 개체 변수만 null이 될 수 있습니다. 두 번째로 이해해야 할 중요한 점은 null이 참조라는 것입니다. 나는 또한 그들의 무게를 참조합니다. 이 주제에서는 stackoverflow에 대한 질문인 " 널 변수에 메모리 공간이 필요합니까 " 라는 질문을 읽을 수 있습니다 .

초기화 블록

변수 초기화를 고려할 때 초기화 블록을 고려하지 않는 것은 죄악입니다. 다음과 같습니다:
public class HelloWorld{

    static {
        System.out.println("static block");
    }

    {
        System.out.println("block");
    }

    public HelloWorld () {
        System.out.println("Constructor");
    }

    public static void main(String []args){
        HelloWorld obj = new HelloWorld();
    }

}
출력 순서는 정적 블록, 블록, 생성자입니다. 보시다시피 초기화 블록은 생성자보다 먼저 실행됩니다. 때로는 이것이 편리한 초기화 수단이 될 수도 있습니다.

결론

이 짧은 개요를 통해 이것이 어떻게 작동하고 왜 작동하는지에 대한 통찰력을 얻을 수 있었으면 좋겠습니다. #비아체슬라프
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION