- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Java:"失效"的private修饰符由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
在Java编程中,使用private关键字修饰了某个成员,只有这个成员所在的类和这个类的方法可以使用,其他的类都无法访问到这个private成员.
上面描述了private修饰符的基本职能,今天来研究一下private功能失效的情况.
Java内部类 。
在Java中相信很多人都用过内部类,Java允许在一个类里面定义另一个类,类里面的类就是内部类,也叫做嵌套类。一个简单的内部类实现可以如下 。
1
2
3
4
|
class
OuterClass {
class
InnerClass{
}
}
|
今天的问题和Java内部类相关,只涉及到部分和本文研究相关的内部类知识,具体关于Java内部类后续的文章会介绍.
第一次失效?
一个我们在编程中经常用到的场景,就是在一个内部类里面访问外部类的private成员变量或者方法,这是可以的。如下面的代码实现.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
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();
}
}
|
这是为什么呢,不是private修饰的成员只能被成员所述的类才能访问么?难道private真的失效了么?
编译器在捣鬼?
我们使用javap命令查看一下生成的两个class文件 。
OuterClass的反编译结果 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
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中我们并没有定义这两个方法 。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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的region属性。并且这两个方法都接受OuterClass的实例作为参数。这两个方法为什么生成呢,有什么作用呢?我们看一下内部类的反编译结果就知道了.
OuterClass$InnerClass的反编译结果 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
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就是内部类持有的外部类引用,通过构造方法传递引用并赋值.
1
2
3
4
5
6
7
8
9
10
|
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等)来获取这些属性值。这一切都是编译器的特殊处理.
这次也失效?
如果说上面的写法很常用,那么这样的写法是不是很少接触,但是却可以运行.
1
2
3
4
5
6
7
8
9
10
11
|
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反编译看一下。不过这次我们先看一下InnerClass的结果 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
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的反编译结果 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
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 。
再来个总结 。
其中java官方文档 有这样一句话 。
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是不被允许的.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
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表面上看上去失效了,但实际上是没有的,而是在调用时通过间接的方法来获取私有的属性.
Java的内部类构造时持有对外部类的应用,C++不会,这一点和C++不一样.
深入Java细节的书籍 。
Java编程思想 Sun 公司核心技术丛书:Effective Java中文版 深入理解Java虚拟机:JVM高级特性与最佳实践 。
以上就是对Java private 修饰符的资料整理,后续继续补充相关资料,谢谢大家对本站的支持! 。
最后此篇关于Java:"失效"的private修饰符的文章就讲到这里了,如果你想了解更多关于Java:"失效"的private修饰符的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我正在编写一个具有以下签名的 Java 方法。 void Logger(Method method, Object[] args); 如果一个方法(例如 ABC() )调用此方法 Logger,它应该
我是 Java 新手。 我的问题是我的 Java 程序找不到我试图用作的图像文件一个 JButton。 (目前这段代码什么也没做,因为我只是得到了想要的外观第一的)。这是我的主课 代码: packag
好的,今天我在接受采访,我已经编写 Java 代码多年了。采访中说“Java 垃圾收集是一个棘手的问题,我有几个 friend 一直在努力弄清楚。你在这方面做得怎么样?”。她是想骗我吗?还是我的一生都
我的 friend 给了我一个谜语让我解开。它是这样的: There are 100 people. Each one of them, in his turn, does the following
如果我将使用 Java 5 代码的应用程序编译成字节码,生成的 .class 文件是否能够在 Java 1.4 下运行? 如果后者可以工作并且我正在尝试在我的 Java 1.4 应用程序中使用 Jav
有关于why Java doesn't support unsigned types的问题以及一些关于处理无符号类型的问题。我做了一些搜索,似乎 Scala 也不支持无符号数据类型。限制是Java和S
我只是想知道在一个 java 版本中生成的字节码是否可以在其他 java 版本上运行 最佳答案 通常,字节码无需修改即可在 较新 版本的 Java 上运行。它不会在旧版本上运行,除非您使用特殊参数 (
我有一个关于在命令提示符下执行 java 程序的基本问题。 在某些机器上我们需要指定 -cp 。 (类路径)同时执行java程序 (test为java文件名与.class文件存在于同一目录下) jav
我已经阅读 StackOverflow 有一段时间了,现在我才鼓起勇气提出问题。我今年 20 岁,目前在我的家乡(罗马尼亚克卢日-纳波卡)就读 IT 大学。足以介绍:D。 基本上,我有一家提供簿记应用
我有 public JSONObject parseXML(String xml) { JSONObject jsonObject = XML.toJSONObject(xml); r
我已经在 Java 中实现了带有动态类型的简单解释语言。不幸的是我遇到了以下问题。测试时如下代码: def main() { def ks = Map[[1, 2]].keySet()
一直提示输入 1 到 10 的数字 - 结果应将 st、rd、th 和 nd 添加到数字中。编写一个程序,提示用户输入 1 到 10 之间的任意整数,然后以序数形式显示该整数并附加后缀。 public
我有这个 DownloadFile.java 并按预期下载该文件: import java.io.*; import java.net.URL; public class DownloadFile {
我想在 GUI 上添加延迟。我放置了 2 个 for 循环,然后重新绘制了一个标签,但这 2 个 for 循环一个接一个地执行,并且标签被重新绘制到最后一个。 我能做什么? for(int i=0;
我正在对对象 Student 的列表项进行一些测试,但是我更喜欢在 java 类对象中创建硬编码列表,然后从那里提取数据,而不是连接到数据库并在结果集中选择记录。然而,自从我这样做以来已经很长时间了,
我知道对象创建分为三个部分: 声明 实例化 初始化 classA{} classB extends classA{} classA obj = new classB(1,1); 实例化 它必须使用
我有兴趣使用 GPRS 构建车辆跟踪系统。但是,我有一些问题要问以前做过此操作的人: GPRS 是最好的技术吗?人们意识到任何问题吗? 我计划使用 Java/Java EE - 有更好的技术吗? 如果
我可以通过递归方法反转数组,例如:数组={1,2,3,4,5} 数组结果={5,4,3,2,1}但我的结果是相同的数组,我不知道为什么,请帮助我。 public class Recursion { p
有这样的标准方式吗? 包括 Java源代码-测试代码- Ant 或 Maven联合单元持续集成(可能是巡航控制)ClearCase 版本控制工具部署到应用服务器 最后我希望有一个自动构建和集成环境。
我什至不知道这是否可能,我非常怀疑它是否可能,但如果可以,您能告诉我怎么做吗?我只是想知道如何从打印机打印一些文本。 有什么想法吗? 最佳答案 这里有更简单的事情。 import javax.swin
我是一名优秀的程序员,十分优秀!