Java 클래스 초기화 및 실례화의 "레이어"2개
먼저 Java에서 트리거를 초기화하는 조건을 살펴보겠습니다.
(1) new 실례화 대상을 사용하여 정적 데이터와 방법에 접근할 때, 즉 지령: new, getstatic/putstatic과 invokestatic을 만났을 때;
(2) 반사로 클래스를 호출할 때;
(3) 하나의 클래스를 초기화할 때 부류가 초기화되지 않으면 부류의 초기화를 촉발한다.
(4) 입구main 방법이 있는 클래스를 실행한다.
(5) JDK1.7 동적 언어 지원에서 메소드 핸들이 있는 클래스입니다. 초기화가 시작되지 않으면 초기화됩니다.
컴파일한 후에
그 중에서 조건(3), 방법 호출의 측면에서 볼 때 하위 클래스의
그러나 주의해야 할 것은'촉발'은 초기화를 완성하는 것이 아니다. 이것은 하위 클래스의 초기화가 상위 클래스의 초기화 종료보다 앞당겨질 수 있다는 것을 의미한다. 이것이 바로'위험'의 소재이다.
1. 클래스 초기화의 예:
이 예에서 나는 하나의 외곽류를 사용하여 2개의 계승 관계가 있는 정적 구성원류를 포함한다. 외곽류의 초기화와 정적 구성원류는 인과 관계가 없기 때문에 이렇게 보여주는 것은 안전하고 편리하다.
부류 A와 자류 B는 각각main 함수를 포함하고 위의 트리거 조건(4)에서 알 수 있듯이 이 두 개의main 함수를 호출하여 서로 다른 클래스 초기화 경로를 트리거한다.
이 예는 상위 클래스가 하위 클래스의 static 인용을 포함하고 정의된 곳에서 초기화하는 문제입니다.
public class WrapperClass {
private static class A {
static {
System.out.println(" A ...");
}
// static
private static B b = new B();
protected static int aInt = 9;
static {
System.out.println(" A ...");
}
public static void main(String[] args) {
}
}
private static class B extends A {
static {
System.out.println(" B ...");
}
//
private static int bInt = 9 + A.aInt;
public B() {
// static
System.out.println(" B " + "bInt " + bInt);
}
static {
System.out.println(" B ... " + "aInt :" + bInt);
}
public static void main(String[] args) {
}
}
}
시나리오 1: 클래스 B의 main 함수를 입력할 때 결과를 출력합니다.
/**
* A ...
* B bInt 0
* A ...
* B ...
* B ... aInt :18
*/
분석:main 함수의 호출은 클래스 B의 초기화를 촉발하고 클래스 B에 들어가는 따라서 부모 클래스에 하위 클래스 유형을 포함하는static역을 포함하고 값을 부여하는 동작을 하면 하위 클래스의 실례화는 클래스 초기화가 완료되기 전에 진행될 수 있음을 알 수 있다.
시나리오 2: 클래스 A의 main 함수를 입력할 때 결과를 출력합니다.
/**
* A ...
* B ...
* B ... aInt :9
* B bInt 9
* A ...
*/
분석: 상황 1의 분석을 통해 우리는 클래스 B의 초기화 트리거 클래스 A의 초기화로 인해 클래스 A에서 클래스 변수 b의 실례화는 클래스 B의 초기화가 완성되기 전에 진행될 수 있음을 알 수 있다. 만약에 클래스 A를 먼저 초기화하면 클래스 변수가 실례화될 때 클래스 B의 초기화를 먼저 촉발하여 실례화 전에 초기화할 수 있지 않을까?답은 긍정적이지만, 이것은 여전히 문제가 있다.출력에 의하면 클래스 B의 초기화는 클래스 A의 초기화가 완성되기 전에 진행되었다. 이로 인해 클래스 변수 aInt의 변수는 클래스 B의 초기화가 완성된 후에야 초기화되었다. 그래서 클래스 B의 도메인 bInt가 얻은 aInt의 값은'0'이지 우리가 예상한'18'이 아니다.
결론: 종합적으로 보면 부류에 자류 유형의 클래스 변수를 포함하고 정의를 실례화하는 것은 매우 위험한 행위이다. 구체적인 상황은 예처럼 직설적이지 않을 수 있다. 호출 방법은 정의에 값을 부여하는 것처럼 위험을 내포하고 있다. 부류 유형의 static역을 포함하더라도 static 방법을 통해 값을 부여해야 한다.JVM은 static 방법이 호출되기 전에 모든 초기화 동작을 완성할 수 있기 때문이다. (물론 이런 보증도 static B b = new B () 를 포함해서는 안 된다.이러한 초기화 행위);
2. 실례화된 예:
먼저 객체가 작성되는 과정을 알아야 합니다.
(1) new 명령을 만나면 클래스가 불러오기, 검증, 준비, 해석, 초기화를 완성했는지 확인한다. (해석 과정은 기호 인용을 직접 인용으로 해석하는 것이다. 예를 들어 방법명은 하나의 기호 인용이다. 초기화가 끝난 후에 이 기호 인용을 사용할 때 할 수 있다. 바로 동적 귀속을 지원하기 위해서이다.) 이 과정을 먼저 진행하지 못했다.
(2) 메모리를 분배하고 빈 목록이나 바늘이 부딪치는 방법을 사용하며 새로 분배된 메모리를'0'으로 설정하기 때문에 모든 실례 변수는 이 부분에서 기본적으로 0(null로 인용)으로 초기화하는 과정을 거친다.
(3)
이 예는'구조기,clone 방법,read Object 방법에서 덮어쓸 수 있는 방법을 사용하지 마세요'라는 것을 위반한 것으로 더 잘 알려질 수 있습니다.그 이유는 바로 자바의 다태적, 즉 동적 귀속에 있다.
부류 A의 구조기에는protected 방법이 포함되어 있으며, 부류 B는 그 자류이다.
public class WrongInstantiation {
private static class A {
public A() {
doSomething();
}
protected void doSomething() {
System.out.println("A's doSomething");
}
}
private static class B extends A {
private int bInt = 9;
@Override
protected void doSomething() {
System.out.println("B's doSomething, bInt: " + bInt);
}
}
public static void main(String[] args) {
B b = new B();
}
}
출력 결과:
/**
* B's doSomething, bInt: 0
*/
분석: 우선 구조기를 제공하지 않을 때 자바 컴파일러가 기본 구조기를 생성하고 시작 부분에서 부류 구조기를 호출하기 때문에 클래스 B의 구조기는 클래스 A의 구조기를 먼저 호출한다는 것을 알아야 한다.클래스 A에서protected 방법인doSomething을 호출했습니다. 출력 결과에서 우리는 실제적으로 호출된 것은 하위 클래스의 방법이 실현된 것을 보았습니다. 이때 하위 클래스의 실례화가 시작되지 않았기 때문에 bInt는'예상'처럼 9가 아니라 0입니다.
이것은 동적 연결이기 때문에doSomething은protected 방법이기 때문에invokevirtual 명령을 통해 호출된 것이다. 이 지령은 대상의 실례 유형에 따라 대응하는 방법을 찾아서 실현한다. (여기가 바로 B의 실례 대상이고 대응하는 방법은 클래스 B의 방법으로 실현된다.) 따라서 이 결과가 있다.
결론: 앞에서 말한 바와 같이'구조기,clone 방법,readObject 방법에서 덮어쓸 수 있는 방법을 사용하지 마세요'.
다음은 여러분을 위해 소개된 자바 클래스의 초기화와 실례화 중 2개의'지뢰밭'입니다. 여러분의 학습에 도움이 되기를 바랍니다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
JPA + QueryDSL 계층형 댓글, 대댓글 구현(2)이번엔 전편에 이어서 계층형 댓글, 대댓글을 다시 리팩토링해볼 예정이다. 이전 게시글에서는 계층형 댓글, 대댓글을 구현은 되었지만 N+1 문제가 있었다. 이번에는 그 N+1 문제를 해결해 볼 것이다. 위의 로직은 이...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.