Java: "실효"private 수정자
위에서private 수식자의 기본 기능을 묘사하였는데, 오늘은private 기능이 효력을 상실한 상황을 연구하고자 합니다.
Java 내부 클래스
자바에서 많은 사람들이 내부 클래스를 사용한 적이 있다고 믿는다. 자바는 한 클래스에서 다른 클래스를 정의할 수 있다. 클래스 안의 클래스는 내부 클래스이고 플러그인 클래스라고도 부른다.간단한 내부 유형의 실현은 다음과 같다.
class OuterClass {
class InnerClass{
}
}
오늘의 문제는 자바 내부류와 관련이 있는데 본고의 연구와 관련된 내부류 지식만 언급하고 자바 내부류의 후속적인 글은 소개하겠습니다.첫 번째 실효?
우리가 프로그래밍에서 자주 사용하는 장면은 내부 클래스에서 외부 클래스에 접근하는private 구성원 변수나 방법이다. 이것은 가능하다.아래의 코드와 같이 실현됩니다.
public class OuterClass {
private String language = "en";
private String region = "US";
public class InnerClass {
public void printOuterClassPrivateFields() {
String fields = "language=" + language + ";region=" + region;
System.out.println(fields);
}
}
public static void main(String[] args) {
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.new InnerClass();
inner.printOuterClassPrivateFields();
}
}
이것은 왜일까요? 프라이빗 장식이 아닌 구성원은 구성원이 말한 클래스에만 접근할 수 있습니까?프라이빗이 정말 효력을 잃었나요?컴파일러가 장난을 치고 있습니까?
우리는javap 명령을 사용하여 생성된class 파일 두 개를 보십시오
OuterClass의 역컴파일 결과
15:30 $ javap -c OuterClass
Compiled from "OuterClass.java"
public class OuterClass extends java.lang.Object{
public OuterClass();
Code:
0: aload_0
1: invokespecial #11; //Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #13; //String en
7: putfield #15; //Field language:Ljava/lang/String;
10: aload_0
11: ldc #17; //String US
13: putfield #19; //Field region:Ljava/lang/String;
16: return
public static void main(java.lang.String[]);
Code:
0: new #1; //class OuterClass
3: dup
4: invokespecial #27; //Method "<init>":()V
7: astore_1
8: new #28; //class OuterClass$InnerClass
11: dup
12: aload_1
13: dup
14: invokevirtual #30; //Method java/lang/Object.getClass:()Ljava/lang/Class;
17: pop
18: invokespecial #34; //Method OuterClass$InnerClass."<init>":(LOuterClass;)V
21: astore_2
22: aload_2
23: invokevirtual #37; //Method OuterClass$InnerClass.printOuterClassPrivateFields:()V
26: return
static java.lang.String access$0(OuterClass);
Code:
0: aload_0
1: getfield #15; //Field language:Ljava/lang/String;
4: areturn
static java.lang.String access$1(OuterClass);
Code:
0: aload_0
1: getfield #19; //Field region:Ljava/lang/String;
4: areturn
}
어?아니오, OuterClass에서 우리는 이 두 가지 방법을 정의하지 않았습니다.
static java.lang.String access$0(OuterClass);
Code:
0: aload_0
1: getfield #15; //Field language:Ljava/lang/String;
4: areturn
static java.lang.String access$1(OuterClass);
Code:
0: aload_0
1: getfield #19; //Field region:Ljava/lang/String;
4: areturn
}
주어진 주석을 보면access$0은outerClass의language 속성을 되돌려줍니다.access$1은 outerClass의 지역 속성을 되돌려줍니다.그리고 이 두 가지 방법은 모두 OuterClass의 실례를 매개 변수로 받아들인다.이 두 가지 방법은 왜 생성되었을까, 어떤 작용이 있을까?우리는 내부류의 반번역 결과를 보면 알 수 있다.OuterClass$InnerClass의 역컴파일 결과
15:37 $ javap -c OuterClass\$InnerClass
Compiled from "OuterClass.java"
public class OuterClass$InnerClass extends java.lang.Object{
final OuterClass this$0;
public OuterClass$InnerClass(OuterClass);
Code:
0: aload_0
1: aload_1
2: putfield #10; //Field this$0:LOuterClass;
5: aload_0
6: invokespecial #12; //Method java/lang/Object."<init>":()V
9: return
public void printOuterClassPrivateFields();
Code:
0: new #20; //class java/lang/StringBuilder
3: dup
4: ldc #22; //String language=
6: invokespecial #24; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
9: aload_0
10: getfield #10; //Field this$0:LOuterClass;
13: invokestatic #27; //Method OuterClass.access$0:(LOuterClass;)Ljava/lang/String;
16: invokevirtual #33; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: ldc #37; //String ;region=
21: invokevirtual #33; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: aload_0
25: getfield #10; //Field this$0:LOuterClass;
28: invokestatic #39; //Method OuterClass.access$1:(LOuterClass;)Ljava/lang/String;
31: invokevirtual #33; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
34: invokevirtual #42; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
37: astore_1
38: getstatic #46; //Field java/lang/System.out:Ljava/io/PrintStream;
41: aload_1
42: invokevirtual #52; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
45: return
}
다음 코드는access$0의 코드를 호출합니다. OuterClass의language 개인 속성을 얻기 위해서입니다.13: invokestatic#27;//Method OuterClass.access$0:(LOuterClass;)Ljava/lang/String;
다음 코드는access$1의 코드를 호출했습니다. OutherClass의region 개인 속성을 얻기 위해서입니다.
28: invokestatic#39;//Method OuterClass.access$1:(LOuterClass;)Ljava/lang/String;
주의: 내부 클래스 구조를 할 때 외부 클래스의 인용을 전달하고 내부 클래스의 속성으로 하기 때문에 내부 클래스는 외부 클래스의 인용을 가진다.
this$0은 내부 클래스가 가지고 있는 외부 클래스 인용입니다. 구조 방법을 통해 인용을 전달하고 값을 부여합니다.
final OuterClass this$0;
public OuterClass$InnerClass(OuterClass);
Code:
0: aload_0
1: aload_1
2: putfield #10; //Field this$0:LOuterClass;
5: aload_0
6: invokespecial #12; //Method java/lang/Object."<init>":()V
9: return
작은 매듭이 부분은private가 효력을 상실한 것처럼 보이지만 실제로는 효력을 상실하지 않습니다. 내부 클래스가 외부 클래스의 개인 속성을 호출할 때, 그 진정한 실행은 컴파일러가 생성한 속성을 호출하는 정적 방법 (즉acess$0,access$1 등) 을 호출하여 이 속성 값을 가져오기 때문입니다.이 모든 것은 컴파일러의 특수 처리이다.
이번에도 실효?
위의 작법이 자주 사용된다면, 이런 작법은 접촉이 적지만 운행할 수 있지 않을까.
public class AnotherOuterClass {
public static void main(String[] args) {
InnerClass inner = new AnotherOuterClass().new InnerClass();
System.out.println("InnerClass Filed = " + inner.x);
}
class InnerClass {
private int x = 10;
}
}
위와 같이javap를 사용하여 역컴파일해 보십시오.근데 이번에는 Inner Class 결과를 한번 보도록 하겠습니다.
16:03 $ javap -c AnotherOuterClass\$InnerClass
Compiled from "AnotherOuterClass.java"
class AnotherOuterClass$InnerClass extends java.lang.Object{
final AnotherOuterClass this$0;
AnotherOuterClass$InnerClass(AnotherOuterClass);
Code:
0: aload_0
1: aload_1
2: putfield #12; //Field this$0:LAnotherOuterClass;
5: aload_0
6: invokespecial #14; //Method java/lang/Object."<init>":()V
9: aload_0
10: bipush 10
12: putfield #17; //Field x:I
15: return
static int access$0(AnotherOuterClass$InnerClass);
Code:
0: aload_0
1: getfield #17; //Field x:I
4: ireturn
}
컴파일러가 자동으로 개인 속성을 가져오는 뒷문 방법인access$0을 생성하여 x의 값을 가져옵니다.AnotherOuterClass.class의 반컴파일 결과
16:08 $ javap -c AnotherOuterClass
Compiled from "AnotherOuterClass.java"
public class AnotherOuterClass extends java.lang.Object{
public AnotherOuterClass();
Code:
0: aload_0
1: invokespecial #8; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #16; //class AnotherOuterClass$InnerClass
3: dup
4: new #1; //class AnotherOuterClass
7: dup
8: invokespecial #18; //Method "<init>":()V
11: dup
12: invokevirtual #19; //Method java/lang/Object.getClass:()Ljava/lang/Class;
15: pop
16: invokespecial #23; //Method AnotherOuterClass$InnerClass."<init>":(LAnotherOuterClass;)V
19: astore_1
20: getstatic #26; //Field java/lang/System.out:Ljava/io/PrintStream;
23: new #32; //class java/lang/StringBuilder
26: dup
27: ldc #34; //String InnerClass Filed =
29: invokespecial #36; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
32: aload_1
33: invokestatic #39; //Method AnotherOuterClass$InnerClass.access$0:(LAnotherOuterClass$InnerClass;)I
36: invokevirtual #43; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
39: invokevirtual #47; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
42: invokevirtual #51; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
45: return
}
그 중에서 이 호출은 외부 클래스가 내부 클래스의 실례를 통해 개인 속성 x를 얻는 작업이다33: invokestatic#39;//Method AnotherOuterClass$InnerClass.access$0:(LAnotherOuterClass$InnerClass;)I
총결산
그중에 자바 공식 문서에 이런 말이 있어요.
if the member or constructor is declared private, then access is permitted if and only if it occurs within the body of the top level class (§7.6) that encloses the declaration of the member or constructor.
구성원과 구조 방법이 개인 수식자로 설정되어 있고 외부 클래스만 접근할 때 허용된다는 뜻이다.
어떻게 내부 유형의 개인 구성원이 외부에 방문하지 못하게 합니까
위의 두 부분을 보고 나면 내부 클래스의 개인 구성원들이 외부 클래스에 방문하지 않으려면 모두 어렵다고 느낄 것이다. 누가 컴파일러에게 오지
mRunnable 대상의 유형은 익명 내부 클래스의 유형이 아니라 Runnable이기 때문에 (정상적으로 얻을 수 없음), Runanble에는 x라는 속성이 없기 때문에 mRunnable.x는 허용되지 않습니다.
public class PrivateToOuter {
Runnable mRunnable = new Runnable(){
private int x=10;
@Override
public void run() {
System.out.println(x);
}
};
public static void main(String[] args){
PrivateToOuter p = new PrivateToOuter();
//System.out.println("anonymous class private filed= "+ p.mRunnable.x); //not allowed
p.mRunnable.run(); // allowed
}
}
최종 요약본고에서private는 표면적으로 효력을 상실한 것처럼 보이지만 실제로는 없는 것이고 호출할 때 간접적인 방법을 통해 개인적인 속성을 얻는다.
자바의 내부 클래스 구조는 외부 클래스에 대한 응용을 가지고 있으며, C++는 그렇지 않다는 점은 C++와 다르다.
자바의 디테일을 깊이 있게 다룬 책
Java 프로그래밍 사상
Sun Corporation Technology: Effective Java 중국어 버전
Java 가상 머신 이해: JVM의 고급 기능 및 모범 사례
이상은 Java private 수식자에 대한 자료 정리입니다. 후속으로 관련 자료를 계속 보충합니다. 본 사이트에 대한 지지에 감사드립니다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 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에 따라 라이센스가 부여됩니다.