클래스 불러오기와 클래스의 초기화 순서

1. 정적 변수, 정적 코드 블록, 변수, 초기화 코드 블록, 구조기, 그들의 순서는 (정적 변수, 정적 코드 블록)>(변수, 초기화 코드 블록)>구조기
하위 클래스와 상위 클래스가 있는 경우는 다음과 같습니다.
(1) 단일 클래스의 실행 상황, 코드는 다음과 같다:public class InitOrderTest {//정적 변수
public static String staticField = "    ";

//   
public String field = "  ";

//       
static {
    System.out.println(staticField);
    System.out.println("      ");
}

//     
{
    System.out.println(field);
    System.out.println("    ");
}

//    
public InitOrderTest() {
    System.out.println("   ");
}

public static void main(String[] args) {
    new InitOrderTest();
}

} 출력은 다음과 같습니다.
(2) 상속의 경우 다음과 같습니다.
class Parent {//정적 변수 public static String p StaticField ='부모-정적 변수', protected int i = 1, protected int j = 8;//변수 public String p Field ='부모-변수'
//        
static {   
    System.out.println(p_StaticField);   
    System.out.println("  --      ");   
}   

//      
{   
    System.out.println(p_Field);   
    System.out.println("  --    ");   
}   

//     
public Parent() {   
    System.out.println("  --   "); 
    System.out.println("i=" + i + ", j=" + j);
    j = 9;
}   

}
public class SubClass extends Parent {
//      
public static String s_StaticField = "  --    ";

//    
public String s_Field = "  --  ";   

//        
static {   
    System.out.println(s_StaticField);   
    System.out.println("  --      ");   
}   
//      
{   
    System.out.println(s_Field);   
    System.out.println("  --    ");   
}   

//     
public SubClass() {   
    System.out.println("  --   "); 
    System.out.println("i=" + i + ",j=" + j);
}   

//      
public static void main(String[] args) {
    new SubClass();   
}   

} 출력은 다음과 같습니다.
지금은 결과가 이미 자명해졌다.하위 클래스의 정적 변수와 정적 초기화 블록의 초기화는 부모 클래스의 변수, 초기화 블록과 구조기가 초기화되기 전에 완성되었다.정적 변수, 정적 초기화 블록, 변수, 초기화 블록의 초기화 순서는 클래스에 나타난 선후 순서에 달려 있다.프로세스 분석 수행(1) SubClass에 액세스합니다.main (), (이것은 static 방법) 을 사용하면 마운터가 컴파일된 SubClass 클래스의 코드 (즉 SubClass.class 파일) 를 찾을 수 있습니다.마운트하는 과정에서 마운트기는 기본 클래스 (즉 extends가 표시하는 의미) 가 있음을 알아차리고 다시 마운트합니다.기본 대상을 만들든 안 만들든 이 과정은 항상 발생할 것이다.만약 기류에 기류가 있다면 두 번째 기류도 불러올 것이다. 이런 식으로 추정된다.
(2) 루트 클래스의static 초기화를 실행하고 다음 파생 클래스의static 초기화를 수행한다.이 순서는 파생류의'static 초기화'가 기본 구성원의 정확한 초기화에 의존할 수 있기 때문에 매우 중요하다.
(3) 필요한 모든 클래스가 마운트가 끝났을 때main () 방법체를 실행하고 new SubClass () 로 대상을 만듭니다.
(4) 클래스 SubClass에 부모 클래스가 존재하면 부모 클래스의 구조 함수를 호출합니다. 슈퍼를 사용하여 어떤 구조 함수 (즉 Beetle () 구조 함수를 호출하는 첫 번째 일을 지정할 수 있습니다.
기류의 구조 과정과 구조 순서는 파생류와 같다.먼저 기류 중의 각 변수는 서면 순서에 따라 초기화한 다음에 기류의 구조 함수의 나머지 부분을 집행한다.
(5) 하위 클래스 구성원의 데이터를 성명한 순서에 따라 초기화하고 하위 클래스 구조 함수의 나머지 부분을 집행한다.
다음은 자바의 클래스 로딩 메커니즘을 분석하고 클래스 로딩 메커니즘을 분석하기 전에 다음 문제를 보십시오. class Singleton {private static Singleton singleton = new Singleton (), public static int value1, public static int value2 = 0;
private Singleton() {
    value1++;
    value2++;
}

public static Singleton getInstance() {
    return singleton;
}

}
class Singleton2 {public static int value1;public static int value2 = 0;private static Singleton2 singleton2 = new Singleton2();
private Singleton2() {
    value1++;
    value2++;
}

public static Singleton2 getInstance2() {
    return singleton2;
}

}
public class TestClassLoader {public static void main(String[] args) {Singleton singleton = Singleton.getInstance();System.out.println("Singleton1 value1:"+ singleton.value1);System.out.println("Singleton1 value2:"+ singleton.value2);
    Singleton2 singleton2 = Singleton2.getInstance2();
    System.out.println("Singleton2 value1:" + singleton2.value1);
    System.out.println("Singleton2 value2:" + singleton2.value2);
}

} 출력 결과는 다음과 같습니다.
jvm 클래스의 로드는 로드, 검증, 준비, 해석, 초기화, 사용, 로드로 나뉘는데 다음과 같다.
다음은 로드, 검증, 준비, 해석, 이 다섯 가지 과정을 초기화하는 구체적인 동작을 살펴본다.
1.1 로드는 주로class 파일 (반드시.class는 아닙니다. ZIP 패키지, 네트워크에서 가져올 수 있습니다) 의 바이너리 바이트 흐름을 JVM으로 읽습니다.로드 단계에서 JVM은 3가지 일을 완성해야 한다. 1) 클래스의 한정된 이름을 통해 이 클래스의 2진 바이트 흐름을 얻는다.2) 바이트 흐름이 대표하는 정적 저장 구조를 방법 구역의 운행 시 데이터 구조로 전환한다.3) 메모리에 이 종류의java를 생성한다.lang.Class 대상은 방법구와 같은 다양한 데이터의 접근 입구입니다.
1.2 연결 1.2.1 검증 검증은 연결 단계의 첫 번째 단계로 불러오는 바이트 흐름이 JVM 규범에 부합되는지 확인한다.검증 단계는 다음과 같은 4단계의 검증 동작을 완성한다. 1) 파일 형식 검증 2) 메타데이터 검증(Java 언어 규범에 부합되는지 여부) 3) 바이트 코드 검증(프로그램의 의미가 합법적이고 논리적인지 확인) 4) 기호 인용 검증(다음 해석이 정상적으로 실행될 수 있도록 확보)
1.2.2 준비 준비는 연결 단계의 두 번째 단계로 정적 변수를 방법구에 메모리를 분배하고 기본 초기값을 설정한다.
1.2.3 해석 해석은 연결 단계의 세 번째 단계로 가상 기기가 상수지 안의 기호 인용을 직접 인용으로 바꾸는 과정이다.
1.3 초기화 초기화 단계는 클래스 불러오는 과정의 마지막 단계로 주로 프로그램의 값 부여 문구에 따라 클래스 변수에 값을 부여한다.주: 1) 부류가 있고 부류가 초기화될 때 먼저 부류를 초기화한다.2) 하위 클래스 초기화 문장을 진행한다.
언제 클래스를 초기화해야 합니까?1) new와 같은 종류의 실례화 대상을 사용할 때;2) 클래스의 정적 필드를 읽거나 설정할 때(final에 의해 수식된 필드는 컴파일러에 넣을 때 상수지의 정적 필드를 제외한다).3) 클래스 정적 방법을 호출할 때;4) 반사 클래스를 사용합니다.forName ("xxxx") 에서 클래스를 반사 호출할 때, 이 클래스를 초기화해야 합니다.5) 하나의 클래스를 초기화할 때 부류가 있으면 먼저 부류를 초기화한다(주: 1. 인터페이스를 제외하고 부류는 호출할 때 초기화된다. 2. 부류는 부류의 정적 필드를 인용하면 부류의 초기화만 유발한다).6) 시작 클래스로 표시된 클래스(main() 방법을 포함하는 클래스)를 초기화합니다.7) JDK1을 사용하는 경우7의 동적 언어가 지원될 때, 만약java.invoke.MethodHandle 인스턴스의 마지막 해석 결과 REFgetStatic、REF_putStatic、REF_vokeStatic의 방법 핸들, 그리고 이 방법 핸들에 대응하는 클래스가 초기화되지 않았으면 먼저 초기화를 촉발해야 합니다.
상기 상황을 하나의 클래스에 대해 주동적으로 인용하고 상기 몇 가지 상황만 있으면 클래스를 초기화해야 한다.다시 돌아서서 처음 면접 문제를 분석한다. 싱글튼 출력 결과: 10 원인:
1 먼저main의 Singleton singleton = Singleton을 실행합니다.getInstance(); 클래스 2 불러오기: 클래스 Singleton 3 클래스를 불러오는 검증 4 클래스의 준비: 정적 변수에 메모리를 분배하고 기본값을 설정합니다.여기서singleton(인용 형식)을null,value1,value2(기본 데이터 형식)로 설정하고 기본값05클래스의 초기화(부치문에 따라 수정):private static Singleton singleton = new Singleton()를 실행합니다.Singleton의 구조기 실행:value1++;value2++; 이때value1,value2는 모두 1이publicstatic intvalue1을 실행합니다.public static int value2 = 0; 이때value1=1,value2=0
Singleton2 출력 결과: 1 이유:
1 먼저main의 Singleton2 singleton2 = Singleton2를 실행합니다.getInstance2(); 클래스 2 불러오기: 클래스 Singleton 2 3 클래스를 불러오는 검증 4 클래스의 준비: 정적 변수에 메모리를 분배하고 기본값을 설정합니다.여기는value1,value2(기본 데이터 형식)에 기본값 0,singleton2(인용 형식)는null,5가지 초기화(부치문구에 따라 수정):publicstatic intvalue2=0을 실행합니다.이때value2=0(value1은 변하지 않고 여전히 0);private static Singleton singleton = new Singleton () 을 실행합니다.Singleton2의 구조기 실행:value1++;value2++; 이때value1,value2는 모두 1,즉 마지막 결과입니다
클래스 로더 간의 계층 관계는 다음과 같습니다.
클래스 캐리어 간의 이런 차원 관계를 양친위 파견 모델이라고 한다.부모위 파견 모형은 맨 윗부분의 부팅 클래스 로더(Bootstrap ClassLoader)를 제외하고 나머지 클래스 로더에는 자신의 부모 클래스 로더가 있어야 한다고 요구한다.이곳의 클래스 캐리어 간의 부자 관계는 일반적으로 상속 관계로 이루어진 것이 아니라 조합으로 이루어진 것이다.
부모 위임 모형의 작업 과정이 클래스가 클래스 로딩 요청을 받아들이면 그 자신은 이 요청을 불러오지 않고 이 클래스 로딩 요청을 부모 로더에 위임하여 부팅 클래스 로더(Bootstrap ClassLoader)에 도착할 때까지 층층이 전송한다.부모 클래스 마운트가 이 요청을 불러올 수 없을 때만 하위 마운트가 직접 불러오려고 시도합니다.
양친위 파견 모델의 코드는 양친위 파견 모델의 코드 실현이java에 집중된다.lang.ClassLoader의 loadClass () 방법에서1) 우선 클래스가 불러오는지 확인하고 부모 클래스 마운트기를 호출하는loadClass() 방법이 없음;2) 부모 클래스 마운트가 비어 있으면 기본적으로 부트 클래스 마운트를 부모 마운트로 사용합니다.3) 부모 클래스를 불러오는 데 실패하면 ClassNotFoundException 이상을 던진 후 자신의findClass () 방법을 호출합니다.
loadClass 소스 코드는 다음과 같습니다.
양친위 파견 모델을 파괴하고 양친위 파견 모델은 각 종류의 캐리어 탑재 기초 유형의 통일성 문제를 잘 해결했다.즉, 기초적인 클래스일수록 상부의 캐리어가 불러옵니다.만약 불러오는 기초 클래스에 사용자 코드를 리셋해야 하는데, 이때 맨 윗부분의 클래스 로더가 이 사용자 코드를 식별하지 못하면 어떻게 합니까?이럴 때는 양친위 파견 모형을 파괴해야 한다.다음은 두 가지 예를 들어 양친위 파견 모델을 파괴하는 과정을 소개한다.
JNDI 파괴 양친위 파견 모델 JNDI는 자바 표준 서비스로 코드는 부팅 클래스 마운트에 의해 불러옵니다.그러나 JNDI는 독립 제조업체가 구현한 코드를 리셋해야 하는데, 클래스 로더가 이러한 리셋 코드 (SPI) 를 식별할 수 없다.이 문제를 해결하기 위해, 라인 상하문 클래스 캐리어를 도입했다.Thread.setContextClassLoader() 설정루틴 상하문 클래스 로더를 이용하여 필요한 SPI 코드를 불러옵니다. 즉, 부모 클래스 로더가 하위 클래스 로더를 요청하여 클래스 로더를 완성하는 과정을 요청하여 부모위 파견 모델을 파괴했습니다.
스프링 파괴 양친위 파견 모델인 스프링은 사용자 프로그램을 조직하고 관리해야 한다. 사용자 프로그램은 일반적으로 WEB-INF 디렉터리에 놓고 WebAppClassLoader 클래스 로더가 불러오고 스프링은 Common 클래스 로더나 Shared 클래스 로더가 불러온다.Spring은 WEB-INF의 사용자 프로그램에 어떻게 접근합니까?라인 상하문 클래스 마운트를 사용합니다.Spring 로드 클래스에 사용되는 classLoader는 모두 Thread를 통과합니다.currentThread().getContextClassLoader()에서 가져옵니다.스레드를 만들 때 기본적으로 AppClassLoader 클래스 로더를 만듭니다. (Tomcat의 WebAppclassLoader 클래스 로더에 대응하는 것): setContextClassLoader (AppClassLoader).이걸로 사용자 프로그램을 불러옵니다.즉 getContextClassLoader () 를 통해 WebAppclassLoader를 얻을 수 있는 모든 스레드입니다.구체적인 로드 메커니즘은 이 형제가 더욱 상세하다.https://blog.csdn.net/m0_38075425/article/details/81627349

좋은 웹페이지 즐겨찾기