정적 수정자는이 코드에 어떤 영향을 미칩니 까?


109

내 코드는 다음과 같습니다.

class A {
    static A obj = new A();
    static int num1;
    static int num2=0;

    private A() {
        num1++;
        num2++;
    }
    public static A getInstance() {
        return obj;
    }
}

public class Main{
    public static void main(String[] arg) {
        A obj = A.getInstance();
        System.out.println(obj.num1);
        System.out.println(obj.num2);
    }
}

출력은 1 0이지만 이해할 수 없습니다.

누군가 나에게 설명 할 수 있습니까?


10
좋은 질문! 이것에서 무엇을 배워야할까요 :하지 마십시오! )
isnot2bad

답변:


116

Java에서는 두 단계가 발생합니다. 1. 식별, 2. 실행

  1. 에서 확인 위상 모든 정적 변수를 검출하고 디폴트 값으로 초기화.

    이제 값은 다음과 같습니다.
    A obj=null
    num1=0
    num2=0

  2. 두 번째 단계 인 실행 은 위에서 아래로 시작됩니다. Java에서 실행은 첫 번째 정적 멤버에서 시작됩니다.
    여기서 첫 번째 정적 변수는 static A obj = new A();이므로 먼저 해당 변수의 객체를 만들고 생성자를 호출하므로 num1및 값 num21.
    그런 다음 다시 static int num2=0;실행되어 num2 = 0;.

이제 생성자가 다음과 같다고 가정합니다.

 private A(){
    num1++;
    num2++;
    System.out.println(obj.toString());
 }

이것은 여전히 참조를 얻지 않았기 NullPointerException때문에 던질 것 obj입니다 class A.


11
확장하겠습니다 : static A obj = new A();아래 줄을 이동하면 static int num2=0;1과 1이 표시됩니다.
Thomas

2
여전히 나를 혼란스럽게하는 것은 num1에 명시 적 초기화가 없더라도 0으로 (암시 적으로) 초기화된다는 사실입니다. 명시 적 초기화와 암시 적 초기화 사이에는 실제로 차이가 없어야합니다 ...
isnot2bad

@ isnot2bad "암시 적 초기화"는 선언의 일부로 발생합니다. 선언에 상관없이 당신이 그들을 제시 주문할 무엇을 할당하기 전에 발생하지 않습니다. A obj = new A(); int num1; int num2 = 0;이로 바뀌 가져옵니다 A obj; int num1; int num2; obj = new A(); num2 = 0;. Java는이를 수행하므로 생성자에 num1, num2도달 할 때 정의됩니다 new A().
Hans Z

31

어떤 static수정 수단은 변수 선언인가 변수 클래스 변수보다는 인스턴스 변수이다. 즉 num1, 변수 는 하나 뿐이고 변수 는 하나 뿐입니다 num2.

(참고 : 정적 변수는 이름이 모든 곳에 표시되지 않는다는 점을 제외하면 일부 다른 언어의 전역 변수 와 같습니다 .로 선언 된 경우에도 public static규정되지 않은 이름은 현재 클래스 또는 수퍼 클래스에서 선언 된 경우에만 표시됩니다. , 또는 정적 가져 오기를 사용하여 가져 오는 경우 차이입니다. 진정한 글로벌은 어디에서나 자격없이 볼 수 있습니다.)

당신이 참조 할 때 그래서 obj.num1하고 obj.num2, 실제로 언급하는 누구의 실제 명칭이다 정적 변수 와 . 마찬가지로, 생성자가 증가 할 때 와 ,이 (각각)과 동일한 변수를 증분한다.A.num1A.num2num1num2

귀하의 예제에서 혼란스러운 주름은 클래스 초기화에 있습니다. 클래스는 먼저 모든 정적 변수를 기본값으로 초기화 한 다음 선언 된 정적 이니셜 라이저 (및 정적 이니셜 라이저 블록)를 클래스에 나타나는 순서대로 실행하여 초기화됩니다. 이 경우 다음이 있습니다.

static A obj = new A();
static int num1;
static int num2=0;

다음과 같이 발생합니다.

  1. 통계는 기본 초기 값으로 시작합니다. A.obj입니다 nullA.num1/ A.num2제로입니다.

  2. 첫 번째 선언 ( A.obj)은의 인스턴스 A()A증분 A.num1및에 대한 생성자를 만듭니다 A.num2. 때 선언이 완료 A.num1하고 A.num2모두 1, 그리고A.obj 새롭게 구축 지칭 A예.

  3. 두 번째 선언 ( A.num1)에는 이니셜 라이저 A.num1가 없으므로 변경되지 않습니다.

  4. 세 번째 선언 ( A.num2)에는에 0을 할당하는 이니셜 라이저가 A.num2있습니다.

따라서 클래스 초기화의 끝에서 A.num1is 1and A.num2is 0... 그리고 이것이 print 문이 보여주는 것입니다.

이 혼란스러운 동작은 실제로 정적 초기화가 완료되기 전에 인스턴스를 만들고 사용중인 생성자 가 아직 초기화되지 않은 정적 에 의존 하고 수정 한다는 사실에 있습니다. 이것은 실제 코드에서 피해야 할 일입니다.


16

1,0이 맞습니다.

클래스가로드되면 모든 정적 데이터가 선언되어 초기화됩니다. 기본적으로 int는 0입니다.

  • 먼저 A가 생성됩니다. num1 및 num2가 1과 1이 됨
  • static int num1;아무것도하지 않는 것보다
  • 보다 static int num2=0;이 NUM2에 0을 기록

9

이는 정적 이니셜 라이저의 순서 때문입니다. 클래스의 정적 표현식은 하향식 순서로 평가됩니다.

호출 내지 제의 생성자 A된 세트 num1num21 모두 :

static A obj = new A();

그때,

static int num2=0;

호출되고 num2 = 0으로 다시 설정됩니다.

그래서 num11이고 num20입니다.

참고로 생성자는 정적 변수를 수정해서는 안됩니다. 이는 매우 나쁜 설계입니다. 대신 Java에서 Singleton구현 하는 다른 접근 방식을 시도하십시오 .


6

JLS의 섹션은 §12.4.2 에서 찾을 수 있습니다 .

자세한 초기화 절차 :

9. 다음으로, 클래스의 클래스 변수 이니셜 라이저와 정적 이니셜 라이저 또는 인터페이스의 필드 이니셜 라이저를 텍스트 순서 로 실행합니다. 단, 최종 클래스 변수와 값이 컴파일되는 인터페이스의 필드는 예외입니다. -시간 상수가 먼저 초기화됩니다.

따라서 세 개의 정적 변수는 텍스트 순서로 하나씩 초기화됩니다.

그래서

static A obj = new A();
//num1 = 1, num2 = 1;
static int num1;
//this is initilized first, see below.
static int num2=0;
//num1 = 1, num2 = 0;

주문을 다음과 같이 변경하는 경우 :

static int num1;
static int num2=0;
static A obj = new A();

결과는입니다 1,1.

(가) 있습니다 static int num1;때문에 초기화 변수가되지 않습니다 ( §8.3.2 ) :

필드 선언자에 변수 이니셜 라이저가 포함 된 경우 선언 된 변수에 대한 할당 (§15.26)의 의미를 가지며 선언자가 클래스 변수 (즉, 정적 필드)를위한 것이라면 변수 이니셜 라이저는 다음과 같습니다. 클래스가 초기화 될 때 평가되고 할당이 정확히 한 번 수행됨

그리고이 클래스 변수는 클래스가 생성 될 때 초기화됩니다. 이것은 먼저 발생합니다 ( §4.12.5 ).

프로그램의 모든 변수에는 값이 사용되기 전에 값이 있어야합니다. 각 클래스 변수, 인스턴스 변수 또는 배열 구성 요소는 생성 될 때 기본값으로 초기화됩니다 (§15.9, §15.10) : 유형 바이트의 경우 기본값 즉, (byte) 0의 값입니다. short 유형의 경우 기본값은 0, 즉 (short) 0의 값입니다. int 유형의 경우 기본값은 0, 즉 0입니다. long 유형의 경우 기본값은 0, 즉 0L입니다. float 유형의 경우 기본값은 양수 0, 즉 0.0f입니다. double 유형의 경우 기본값은 양수 0, 즉 0.0d입니다. char 유형의 경우 기본값은 널 문자, 즉 '\ u0000'입니다. 부울 유형의 경우 기본값은 false입니다. 모든 참조 유형 (§4.3)의 경우 기본값은 null입니다.


2

이런 식으로 생각하면 도움이 될 것입니다.

클래스는 객체의 청사진입니다.

개체는 인스턴스화 될 때 변수를 가질 수 있습니다.

클래스는 변수를 가질 수도 있습니다. 이들은 정적으로 선언됩니다. 따라서 개체 인스턴스가 아닌 클래스에 설정됩니다.

응용 프로그램에서 모든 클래스 중 하나만 가질 수 있으므로 해당 클래스를위한 전역 저장소와 비슷합니다. 물론 이러한 정적 변수는 애플리케이션의 어느 곳에서나 액세스하고 수정할 수 있습니다 (공용이라고 가정).

다음은 정적 변수를 사용하여 생성 된 인스턴스 수를 추적하는 "Dog"클래스의 예입니다.

"Dog"클래스는 클라우드이고 주황색 상자는 "Dog"인스턴스입니다.

개 클래스

더 읽어보기

도움이 되었기를 바랍니다!

퀴즈처럼 느껴진다면이 아이디어는 Plato 가 처음 소개 한 것입니다.


1

static 키워드는 주로 메모리 관리를 위해 Java에서 사용됩니다. 변수, 메서드, 블록 및 중첩 클래스에 정적 키워드를 적용 할 수 있습니다. static 키워드는 클래스의 인스턴스보다 클래스에 속합니다. static 키워드에 대한 간략한 설명은 다음과 같습니다.

http://www.javatpoint.com/static-keyword-in-java


0

위의 많은 답변이 정확합니다. 그러나 실제로 무슨 일이 일어나고 있는지 설명하기 위해 아래에 약간의 수정을가했습니다.

위에서 여러 번 언급했듯이 클래스 A가 완전히로드되기 전에 클래스 A의 인스턴스가 생성됩니다. 따라서 정상적인 '행동'으로 간주되는 것은 관찰되지 않습니다. 이는 재정의 할 수있는 생성자에서 메서드를 호출하는 것과 크게 다르지 않습니다. 이 경우 인스턴스 변수는 직관적 인 상태가 아닐 수 있습니다. 이 예제에서 클래스 변수는 직관적 인 상태가 아닙니다.

class A {
    static A obj = new A();
    static int num1;
    static int num2;
    static {
        System.out.println("Setting num2 to 0");
        num2 = 0;
    }

    private A() {
        System.out.println("Constructing singleton instance of A");
        num1++;
        num2++;
    }

    public static A getInstance() {
        return obj;
    }
}

public class Main {

    public static void main(String[] arg) {
        A obj = A.getInstance();
        System.out.println(obj.num1);
        System.out.println(obj.num2);
    }
}

출력은

Constructing singleton instance of A
Setting num2 to 0
1
0

0

java는 호출되지 않고 생성 될 때까지 정적 또는 비 정적 데이터 멤버의 값을 초기화하지 않습니다.

그래서 여기서 num1과 num2가 main에서 호출 될 때 값으로 초기화됩니다.

num1 = 0 + 1; 과

num2 = 0;

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.