Java 中的 public、protected、package-private 和 private 有什么区别?
-
03-07-2019 - |
题
在Java中,对于何时使用每个访问修饰符是否有明确的规则,即默认值(包私有), public
, protected
和 private
, ,同时制作 class
和 interface
以及处理继承问题?
解决方案
官方教程可能对您有所帮助
│ Class │ Package │ Subclass │ Subclass │ World │ │ │(same pkg)│(diff pkg)│ ────────────┼───────┼─────────┼──────────┼──────────┼──────── public │ + │ + │ + │ + │ + ────────────┼───────┼─────────┼──────────┼──────────┼──────── protected │ + │ + │ + │ + │ ────────────┼───────┼─────────┼──────────┼──────────┼──────── no modifier │ + │ + │ + │ │ ────────────┼───────┼─────────┼──────────┼──────────┼──────── private │ + │ │ │ │ + : accessible blank : not accessible
其他提示
(警告:我不是Java程序员,我是Perl程序员.Perl没有正式的保护,这也许就是我理解这个问题的原因:))
私人
就像你想的那样,只有声明它的类才能看到它。
Package Private
只能在声明它的包中查看和使用。这是Java中的默认值(有些人认为是错误的。)
受保护的
包私有+可以通过子类或包成员看到。
公共
每个人都可以看到它。
已发布
在我控制的代码之外可见。 (虽然不是Java语法,但对于这个讨论很重要。)
C ++定义了一个名为“friend”的附加级别。而且你知道的越少越好。
什么时候应该使用什么?整个想法是隐藏信息的封装。您希望尽可能隐藏用户完成某些操作的详细信息。为什么?因为那样你可以在以后更改它们而不会破坏任何人的代码。这使您可以优化,重构,重新设计和修复错误,而无需担心有人正在使用您刚刚彻底检查过的代码。
因此,经验法则是将事物视为必须可见。从私有开始,只根据需要添加更多可见性。只公开那些对用户来说绝对必要的信息,你公开的每一个细节都会让你重新设计系统。
如果您希望用户能够自定义行为,而不是将内部公开,以便他们可以覆盖它们,那么将这些内容推入对象并公开该接口通常是更好的主意。这样他们就可以简单地插入一个新对象。例如,如果您正在编写CD播放器并希望“去查找有关此CD的信息”,可自定义,而不是将这些方法公开,您将所有功能放入其自己的对象,并使您的对象getter / setter公开。通过这种方式吝啬暴露你的胆量可以促进良好的成分和关注点的分离
就个人而言,我只坚持“私人”。和“公共”。许多OO语言就是这样。 "受保护的"可以很方便,但它确实是一个骗子。一旦界面不仅仅是私人的,它就在你的控制之外,你必须去寻找其他人的代码才能找到用途。
这就是“已发表”的想法。改变一个界面(重构它)需要你找到所有正在使用它的代码并改变它。如果界面是私有的,那么没问题。如果它受到保护,你必须找到你所有的子类。如果它是公开的,你必须找到使用你的代码的所有代码。有时这是可能的,例如,如果您正在处理仅供内部使用的公司代码,那么如果接口是公共的则无关紧要。您可以从公司存储库中获取所有代码。但是如果界面被“发布”,如果有代码在你的控制之外使用它,那么你就被软管了。您必须支持该接口或冒险破坏代码。甚至受保护的接口也可以被认为是已发布的(这就是我不打扰受保护的原因)。
许多语言发现公共/受保护/私有的层次性过于限制,与现实不符。为此,有一个特质课程的概念,但这是另一个节目。
这是该表的更好版本。(未来的证明,带有模块栏。)
说明
A 私人的 成员 (
i
) 是 仅有的 在声明的同一类中可以访问。会员有 没有访问修饰符 (
j
) 只能在同一包中的类中访问。A 受保护的 成员 (
k
) 可在同一包中的所有类中访问 和 在其他包的子类中。A 民众 成员 (
l
)可供所有类访问(除非它驻留在 模块 不会导出它声明的包)。
选择哪个修改器?
访问修饰符是一个帮助您防止意外破坏封装的工具(*). 。问问自己是否希望该成员成为类、包、类层次结构的内部成员或根本不是内部成员,并相应地选择访问级别。
例子:
- 一个字段
long internalCounter
可能应该是私有的,因为它是可变的并且是实现细节。 - 只能在工厂类(在同一个包中)中实例化的类应该具有包限制的构造函数,因为不可能从包外部直接调用它。
- 一个内部
void beforeRender()
在渲染之前调用并在子类中用作钩子的方法应该受到保护。 - A
void saveGame(File dst)
从 GUI 代码调用的方法应该是公共的。
(*) 封装到底是什么?
| highest precedence <---------> lowest precedence
*———————————————+———————————————+———————————+———————————————+———————
\ xCanBeSeenBy | this | any class | this subclass | any
\__________ | class | in same | in another | class
\ | nonsubbed | package | package |
Modifier of x \ | | | |
————————————————*———————————————+———————————+———————————————+———————
public | ✔ | ✔ | ✔ | ✔
————————————————+———————————————+———————————+———————————————+———————
protected | ✔ | ✔ | ✔ | ✘
————————————————+———————————————+———————————+———————————————+———————
package-private | | | |
(no modifier) | ✔ | ✔ | ✘ | ✘
————————————————+———————————————+———————————+———————————————+———————
private | ✔ | ✘ | ✘ | ✘
简单的规则。首先声明一切都是私密的。然后随着需求的出现向公众发展,设计需要保证。
当暴露成员时,问问自己是否暴露了表示选择或抽象选择。第一个是你想要避免的东西,因为它会在实际表示中引入太多依赖,而不是它的可观察行为。
作为一般规则,我尝试通过子类化来避免重写方法实现;搞砸逻辑太容易了。如果您打算覆盖它,则声明抽象受保护的方法。
另外,在重写时使用@Override注释可以防止在重构时破坏。
它实际上比简单的网格显示要复杂一些。网格告诉您是否允许访问,但是访问到底是什么?此外,访问级别以复杂的方式与嵌套类和继承交互。
“默认”访问(通过缺少关键字指定)也称为 包私有. 。例外:在接口中,没有修饰符意味着公共访问;禁止使用除 public 之外的修饰符。枚举常量始终是公共的。
概括
是否允许使用此访问说明符访问成员?
- 会员是
private
:仅当成员与调用代码在同一类中定义时。 - 成员是包私有的:仅当调用代码位于成员的直接封闭包内时。
- 会员是
protected
:相同的包,或者如果成员是在包含调用代码的类的超类中定义的。 - 会员是
public
:是的。
访问说明符适用于什么
局部变量和形式参数不能使用访问说明符。由于根据范围规则,它们本质上是外部无法访问的,因此它们实际上是私有的。
对于顶级范围内的类,仅 public
和包私有是允许的。这种设计选择大概是因为 protected
和 private
在包级别将是多余的(没有包的继承)。
所有访问说明符都可以用于类成员(构造函数、方法和静态成员函数、嵌套类)。
有关的: Java 类的可访问性
命令
访问说明符可以严格排序
公共 > 受保护 > 包私有 > 私有
意思是 public
提供最多的访问权限, private
至少。私有成员上可能的任何引用也对包私有成员有效;对包私有成员的任何引用在受保护的成员上都有效,等等。(将受保护成员的访问权授予同一包中的其他类被认为是一个错误。)
笔记
- 一个类的方法 是 允许访问同一类的其他对象的私有成员。 更准确地说,类 C 的方法可以访问 C 的任何子类的对象上的 C 的私有成员。Java 不支持按实例限制访问,仅支持按类限制访问。(与 Scala 相比,Scala 确实支持使用
private[this]
.) - 您需要访问构造函数来构造对象。因此,如果所有构造函数都是私有的,则该类只能由类内的代码(通常是静态工厂方法或静态变量初始值设定项)来构造。对于包私有或受保护的构造函数也是如此。
- 仅具有私有构造函数还意味着该类不能在外部进行子类化,因为 Java 要求子类的构造函数隐式或显式调用超类构造函数。(但是,它可以包含一个子类化它的嵌套类。)
内部类
你还必须考虑 嵌套的 范围,例如内部类。复杂性的一个例子是内部类具有成员,这些成员本身可以采用访问修饰符。所以你可以有一个带有公共成员的私有内部类;会员可以访问吗?(见下文。)一般规则是查看范围并递归思考以查看是否可以访问每个级别。
然而,这相当复杂,要了解完整的细节, 请参阅 Java 语言规范. 。(是的,过去存在编译器错误。)
要了解它们如何相互作用,请考虑这个示例。有可能“泄漏”私有内部类;这通常是一个警告:
class Test {
public static void main(final String ... args) {
System.out.println(Example.leakPrivateClass()); // OK
Example.leakPrivateClass().secretMethod(); // error
}
}
class Example {
private static class NestedClass {
public void secretMethod() {
System.out.println("Hello");
}
}
public static NestedClass leakPrivateClass() {
return new NestedClass();
}
}
编译器输出:
Test.java:4: secretMethod() in Example.NestedClass is defined in an inaccessible class or interface
Example.leakPrivateClass().secretMethod(); // error
^
1 error
一些相关问题:
根据经验:
private
:类范围。default
(或者package-private
):包范围。protected
:package scope + child
(就像包一样,但我们可以从不同的包中子类化它)。protected 修饰符始终保持“父子”关系。public
:到处。
因此,如果我们将访问权限分为三种权限:
- (直接的 (从同一类内的方法调用,或通过“this”语法调用)。
- (参考 (使用对类的引用或通过“点”语法调用方法)。
- (遗产 (通过子类化)。
然后我们有这个简单的表:
+—-———————————————+————————————+———————————+
| | Same | Different |
| | Package | Packages |
+—————————————————+————————————+———————————+
| private | D | |
+—————————————————+————————————+———————————+
| package-private | | |
| (no modifier) | D R I | |
+—————————————————+————————————+———————————+
| protected | D R I | I |
+—————————————————+————————————+———————————+
| public | D R I | R I |
+—————————————————+————————————+———————————+
在很短的时间内
public
:从任何地方都可以访问。protected
:可由同一包的类和任何包中的子类访问。- 默认(未指定修饰符):可由同一包的类访问。
private
:只能在同一个类中访问。
Java中最容易被误解的访问修饰符是 protected
. 。我们知道它与默认修饰符类似,但有一个例外,子类可以看到它。但如何呢?这是一个例子,希望能澄清这个困惑:
假设我们有2个班级;
Father
和Son
, ,每个都有自己的包:package fatherpackage; public class Father { } ------------------------------------------- package sonpackage; public class Son extends Father { }
让我们添加一个受保护的方法
foo()
到Father
.package fatherpackage; public class Father { protected void foo(){} }
方法
foo()
可以在 4 种上下文中调用:在位于同一包中的类中
foo()
被定义为 (fatherpackage
):package fatherpackage; public class SomeClass { public void someMethod(Father f, Son s) { f.foo(); s.foo(); } }
在子类内部,在当前实例上通过
this
或者super
:package sonpackage; public class Son extends Father { public void sonMethod() { this.foo(); super.foo(); } }
在类型为同一类的引用上:
package fatherpackage; public class Father { public void fatherMethod(Father f) { f.foo(); // valid even if foo() is private } } ------------------------------------------- package sonpackage; public class Son extends Father { public void sonMethod(Son s) { s.foo(); } }
在其类型为父类的引用上,并且它是 里面 包裹在哪里
foo()
被定义为 (fatherpackage
) [这可以包含在上下文中。1]:package fatherpackage; public class Son extends Father { public void sonMethod(Father f) { f.foo(); } }
以下情况无效。
在其类型为父类的引用上,并且它是 外部 包裹在哪里
foo()
被定义为 (fatherpackage
):package sonpackage; public class Son extends Father { public void sonMethod(Father f) { f.foo(); // compilation error } }
子类包内的非子类(子类从其父类继承受保护的成员,并使它们对非子类私有):
package sonpackage; public class SomeClass { public void someMethod(Son s) throws Exception { s.foo(); // compilation error } }
私人的
- 方法、变量和构造函数
声明为私有的方法、变量和构造函数只能在声明的类本身内访问。
- 类和接口
私有访问修饰符是最严格的访问级别。类和接口不能是私有的。
笔记
如果类中存在公共 getter 方法,则可以在类外部访问声明为私有的变量。在超类中声明为 protected 的变量、方法和构造函数只能由其他包中的子类或受保护成员类的包中的任何类访问。
受保护
- 类和接口
protected 访问修饰符不能应用于类和接口。
方法、字段可以声明为 protected,但是接口中的方法和字段不能声明为 protected。
笔记
受保护的访问使子类有机会使用辅助方法或变量,同时防止不相关的类尝试使用它。
民众
声明为公共的类、方法、构造函数、接口等可以从任何其他类访问。
因此,可以从属于 Java 世界的任何类访问在公共类中声明的字段、方法、块。
- 不同的套餐
但是,如果我们尝试访问的公共类位于不同的包中,则仍然需要导入该公共类。
由于类继承,类的所有公共方法和变量都被其子类继承。
默认-无关键字:
默认访问修饰符意味着我们没有显式声明类、字段、方法等的访问修饰符。
- 在相同的包内
没有任何访问控制修饰符声明的变量或方法可供同一包中的任何其他类使用。接口中的字段是隐式 public static final 的,接口中的方法默认是 public 的。
笔记
我们不能覆盖静态字段。如果您试图覆盖它不会显示任何错误,但是它不起作用,除了我们之外。
相关答案
参考链接
http://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html http://www.tutorialspoint.com/java/java_access_modifiers.htm
私人:仅限课堂访问权限
默认(无修饰符):对类和包的有限访问权限
受保护:对类,包和子类(包内和包外)的有限访问权限
公开:可访问类,包(所有)和子类......简而言之,无处不在。
差异可以在已经提供的链接中找到,但使用的链接通常归结为“最少知识原理”。只允许所需的最低可见性。
访问修饰符用于限制多个级别的访问。
公开:它基本上就像您可以从任何类访问一样简单,无论它是否在同一个包中。
如果您在同一个包中,可以直接访问,但如果您在另一个包中,则可以创建该类的对象。
默认值:可以从任何类包中的同一个包中访问它。
要访问,您可以创建该类的对象。但是你不能在包外面访问这个变量。
受保护:您可以访问同一个包中的变量以及任何其他包中的子类。 所以基本上它是默认+继承行为。
要访问基类中定义的受保护字段,您可以创建子类的对象。
私有:可以在同一个类中访问。
在非静态方法中,您可以直接访问,因为此引用(也在构造函数中),但要在静态方法中访问,您需要创建类的对象。
Java中的访问修饰符。
Java访问修饰符用于在Java中提供访问控制。
<强> 1。默认值:强>
只能访问同一个包中的类。
例如,
// Saved in file A.java
package pack;
class A{
void msg(){System.out.println("Hello");}
}
// Saved in file B.java
package mypack;
import pack.*;
class B{
public static void main(String args[]){
A obj = new A(); // Compile Time Error
obj.msg(); // Compile Time Error
}
}
此访问权限比公开和受保护更受限制,但受限于私有。
<强> 2。公共强>
可以从任何地方访问。 (全球访问)
例如,
// Saved in file A.java
package pack;
public class A{
public void msg(){System.out.println("Hello");}
}
// Saved in file B.java
package mypack;
import pack.*;
class B{
public static void main(String args[]){
A obj = new A();
obj.msg();
}
}
输出:你好
第3。私人强>
只能在同一个班级内访问。
如果您尝试访问另一个类上的私有成员,则会抛出编译错误。例如,
class A{
private int data = 40;
private void msg(){System.out.println("Hello java");}
}
public class Simple{
public static void main(String args[]){
A obj = new A();
System.out.println(obj.data); // Compile Time Error
obj.msg(); // Compile Time Error
}
}
<强> 4。受保护的强>
仅可访问同一包中的类和子类
例如,
// Saved in file A.java
package pack;
public class A{
protected void msg(){System.out.println("Hello");}
}
// Saved in file B.java
package mypack;
import pack.*;
class B extends A{
public static void main(String args[]){
B obj = new B();
obj.msg();
}
}
输出:你好
我只想解决一个非常容易出错的细节,包括本页上的大多数答案。 “默认”访问(当不存在访问修饰符时)并不总是与包私有相同. 。这取决于事情是什么。
默认情况下,非成员类型(即未在另一个类型中声明的类、枚举、接口和注释类型)是包私有的。(JLS§6.6.1)
默认情况下,类成员和构造函数是包私有的。(JLS§6.6.1)
枚举构造函数是 默认为私有. 。(事实上,枚举构造函数 必须 是私有的,尝试将它们公开或受保护是错误的)。枚举常量是公共的,不允许任何访问说明符。默认情况下,枚举的其他成员是包私有的。(JLS§8.9)
接口和注释类型的所有成员都是 默认公开. 。(事实上,接口和注释类型的成员 必须 是公开的,尝试将它们设为私有或受保护是错误的。)(JLS §9.3 至 9.5)
民众 - 可从应用程序中的任何位置访问。
默认 - 可从包中获取。
受保护的 - 可从其他包中的包和子类访问。还有
私人的 - 只能从其类访问。
包装可见。默认。不需要修饰符。
仅对班级可见(私人)。
向全世界展示(公开)。
对包和所有子类(受保护的)可见。
可以声明变量和方法,而不需要调用任何修饰符。默认示例:
String name = "john";
public int age(){
return age;
}
私人访问修饰符 - 私有:
声明为private的方法,变量和构造函数只能在声明的类本身中访问。私有访问修饰符是限制性最强的访问级别。类和接口不能是私有的。
如果类中存在公共getter方法,则可以在类外部访问声明为private的变量。
使用private修饰符是对象封装自身并隐藏来自外部世界的数据的主要方式。
示例:
Public class Details{
private String name;
public void setName(String n){
this.name = n;
}
public String getName(){
return this.name;
}
}
公共访问修饰符 - public:
声明为public的类,方法,构造函数,接口等可以从任何其他类访问。因此,可以从属于Java Universe的任何类访问在公共类中声明的字段,方法,块。
但是,如果我们尝试访问的公共类位于不同的包中,则仍需要导入公共类。
由于类继承,类的所有公共方法和变量都由其子类继承。
示例:
public void cal(){
}
受保护的访问修饰符 - 受保护:
在超类中声明受保护的变量,方法和构造函数只能由另一个包中的子类或受保护成员类的包中的任何类访问。
受保护的访问修饰符无法应用于类和接口。方法,字段可以声明为protected,但是接口中的方法和字段不能声明为protected。
受保护的访问权使子类有机会使用辅助方法或变量,同时防止非相关类尝试使用它。
class Van{
protected boolean speed(){
}
}
class Car{
boolean speed(){
}
}
.... 受保护:受保护的访问修饰符有点棘手,你可以说是默认访问修饰符的超集。受保护的成员与默认成员相同,只要涉及相同包中的访问。不同之处在于,受保护成员也可以被声明成员的类的子类访问,这些成员位于父类所在的包之外。
但是这些受保护的成员只能通过继承&#8220;在包之外访问。即,您可以直接访问其他包中存在的子类中受保护的成员,就好像该成员存在于子类本身中一样。但是,通过使用父类的引用,该受保护的成员将无法在包外的子类中访问。 ....
David的回答提供了每个访问修饰符的含义。至于何时使用每一个,我建议公开所有类和每个类的方法,以供外部使用(其API),其他一切都是私有的。
随着时间的推移,您将了解何时将某些类包装为私有以及何时声明某些受保护以供子类使用的方法。
笔记: 这只是一个 补充 对于已接受的答案。
这和Java有关 访问修饰符.
从 Java 访问修饰符:
Java 访问修饰符指定哪些类可以访问给定的 类及其字段、构造函数和方法。访问修饰符可以 类、其构造函数、字段和 方法。Java 访问修饰符有时也在日常的 作为 Java 访问规范,但正确的名称是 Java 访问 修改器。类、字段、构造函数和方法可以有 四种不同的 Java 访问修饰符:
- 项目清单
- 私人的
- 默认(包)
- 受保护的
- 民众
从 控制对类成员的访问 教程:
访问级别修饰符确定其他类是否可以使用特定字段或调用特定方法。分为两个级别 访问控制:
- 在顶层 - 公共或包私有(无显式修饰符)。
- 在成员级别 - 公共、私有、受保护或包私有(无显式修饰符)。
一个类可以使用修饰符 public 来声明,在这种情况下,该类的 类对任何地方的所有类都是可见的。如果一个类没有修饰符 (默认设置,也称为包-私有),它只可见于 在自己的软件包内
下表显示了每个修饰符允许的成员的访问。
╔═════════════╦═══════╦═════════╦══════════╦═══════╗ ║ Modifier ║ Class ║ Package ║ Subclass ║ World ║ ╠═════════════╬═══════╬═════════╬══════════╬═══════╣ ║ public ║ Y ║ Y ║ Y ║ Y ║ ║ protected ║ Y ║ Y ║ Y ║ N ║ ║ no modifier ║ Y ║ Y ║ N ║ N ║ ║ private ║ Y ║ N ║ N ║ N ║ ╚═════════════╩═══════╩═════════╩══════════╩═══════╝
第一列数据显示类本身是否可以访问 访问级别所定义的成员。正如您所看到的,一个类总是 可以访问自己的成员。第二栏表示是否 类在同一个软件包中(无论它们的 亲子关系)可以接触该成员。第三栏显示 在此软件包之外声明的该类的子类是否具有 访问该成员。第四栏表示是否所有类别 可以访问该成员。
访问级别以两种方式影响您。首先,当您使用 这些类来自其他来源,如 Java 平台中的类、 访问级别决定了这些类中哪些成员属于自己的 类可以使用。其次,当你编写一个类时,你需要决定 类中每个成员变量和每个方法的访问级别 应该有。
Public Protected Default和private是访问修饰符。
它们用于封装,或隐藏和显示类的内容。
- 班级可以是公开的或默认的
- 班级成员可以是公开的,受保护的,默认的或私人的。 醇>
私人课程无法在课堂外访问 默认只能在包中访问。 受保护的包以及任何扩展它的类。 公众对所有人开放。
通常,成员变量是私有的,但成员方法是公共的。
我常常意识到,通过创建现实世界的类比,可以记住任何语言的基本概念。这是我理解 Java 中访问修饰符的类比:
假设您是一名大学学生,您有一位朋友周末来看望您。假设校园中央有一尊大学创始人的巨大雕像。
当你带他到校园时,你和你的朋友首先看到的就是这座雕像。这意味着任何走进校园的人都可以在未经大学许可的情况下观看这座雕像。这使得雕像成为 民众.
接下来,您想带您的朋友去宿舍,但为此您需要将他注册为访客。这意味着他获得了进入校园的通行证(与您的相同),可以进入校园的各个建筑物。这将使他的门禁卡成为 受保护.
您的朋友想要登录校园 WiFi,但没有任何凭据。他上网的唯一方法是您与他共享您的登录信息。(请记住,每个上大学的学生也拥有这些登录凭据)。这将使您的登录凭据为 无修饰符.
最后,您的朋友想阅读您发布在网站上的学期进度报告。然而,每个学生都有自己的个人登录名来访问校园网站的这一部分。这将使这些凭证成为 私人的.
希望这可以帮助!
当您考虑访问修饰符时,只需以这种方式考虑它(适用于变量和方法):
public
- &gt;从每个地方都可以访问
private
- &gt;只能在声明它的同一个类中访问
现在,当涉及到 default
和 protected
默认
- &gt;不存在访问修饰符关键字。这意味着它严格适用于该类包。 无处在该软件包之外,可以访问它。
protected
- &gt;略微不如 default
更严格,除了相同的包类,它可以被包之外的子类访问,它被声明。
所有关于封装(或正如Joe Phillips所述,最少知识)。
从限制性最强的(私有)开始,看看以后是否需要限制较少的修饰符。
我们都使用方法和成员修饰符,例如private,public,...但是开发人员做的一件事就是使用包来逻辑地组织代码。
例如: 您可以将敏感的安全方法放在“安全”包中。 然后放一个公共类来访问此包中的一些安全相关代码,但保留其他安全类 package private 。 因此,其他开发人员只能使用此包之外的公共类(除非他们更改修饰符)。 这不是安全功能,但指导用法。
Outside world -> Package (SecurityEntryClass ---> Package private classes)
另一件事是,彼此依赖很多的类可能最终会出现在同一个包中,如果依赖性太强,最终可能会被重构或合并。
如果恰恰相反,您将所有内容设置为 public ,则不清楚应该或不应该访问的内容,这可能导致编写大量javadoc(不会通过编译器执行任何操作) ...)。