在Java中,一般认为有4种访问控制符,从最严格的到最宽松为private、default、protected、public。这篇文章讨论这四个访问控制符可以用于修饰类(class)、域(field)、方法(method)、构造器(constructor)的情况。除了这四种类型外,接口、enum等也可以使用访问控制符,但是不是每个控制符都能使用。
(在下文中,如果不加说明,类一般指top-level class)
1、 private修饰符
无论是域、方法还是构造器被声明为private,意味着只能在本类中才能使用它们。所谓使用,对于域来说就是访问,方法就是调用,构造器就是新建一个对象。
1.1 private修饰类:
有些地方的文章会说类不能声明为private,理论上来说这是错的。private修饰一个类时,只能用于声明member class(成员类解释),【参考Java8语言规范的8.1.1】这时候只有成员类(member class)的外部类能够实例化这个类(如果这个类可以被实例化,也就是不是抽象类。)。看下面代码(已经通过编译验证):
/**
* @author Brandon B. Lin
*
*/
public class Test2 {
public void test() {
new PrivateClass();
new PrivateStaticClass();
}
private class PrivateClass {
private PrivateClass() {
}
}
private static class PrivateStaticClass{
private PrivateStaticClass() {
}
}
}
可以看到,成员类可以使用private来修饰,因此它只能在这个类中被实例化(如果可以被实例化)。注意到的是,尽管上面的两个成员类的构造器都是private,依然能够在外部类创建实例,这是嵌套类区别于非嵌套类的一个体现。如果我们在成员类价格abstract,则在其外部类也不能被实例化!有关内部类,一篇很好的英文文章: http://www.learn-java-tutorial.com/inner-classes.cfm
1.2 private修饰构造器:
如果构造器被声明为private,意味着除了本类以外,不能调用构造函数,也就是不能实例化这个类。(注意开头的声明,这里的类指的是top-level class,对于成员类,即使构造器为private,在其外部类一个可以实例化。
private构造器的一个典型应用场景就是单例模式,将构造器声明为private,使得外界不能对该类进行实例化。具体代码如下:
public class Singleton {
private Singleton() {
// do something
}
private static Singleton instance = null;
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
1.3 private修饰域和方法:
一般情况下,域和方法情况十分类似,因此我们将两者和在一起讨论。private修饰域和方法的时候也很明确,就是这些域不能在类以外被使用,方法不能在类外被调用。域不能在类外被访问并不代表外界无法访问这些域,可以通过get方法对外界提供接口。
2. default修饰符
默认修饰符有时候也称为包访问控制符。
2.1 default修饰类
对于top-level class,不是被public修饰就是没有修饰符,也就是default修饰符。default修饰的top-level类,只能在同一个包中使用,例如被实例化(如果可以被实例化),当然也包括调用静态方法等等其他操作。对于member class,也可以不使用修饰符,也就是default修饰符。如果不使用修饰符,则在同一个包中,可以通过其外部类的实例对象来实例化member class。
2.2 default构造器
如果构造器没有访问控制符,说明在同一个包的范围内,可以使用该类,即实例化这个类。
2.3 default域和方法
没有使用访问控制符的域和方法,可以在包范围内被使用。
3. protected修饰符
3.1 protected class:
如果protected用于修饰类,那只能修饰member class,和private修饰类是一样的。如果一个member class被protected修饰,那么除了在其外部类可以使用这个member class外,外部类的子类也可以使用它(通过外部类的子类的实例)。例如,我们有下面一个类Pkg1Public:
/**
* @author Brandon B. Lin
*
*/
public class Pkg1Public {
private class Pkg1Private {
}
protected class Pkg1Protected {
}
}
我们定义了两个成员类,一个为private,一个protected,那么这两个成员类在其外部类,也就是Pkg1Public中都可以被使用。我们再定义一个Pkg1Public类的子类:ExtClass,如下:
/**
* @author Brandon B. Lin
*
*/
public class ExtClass extends Pkg1Public {
public ExtClass() {
//
}
/**
* @param args
*/
public static void main(String[] args) {
ExtClass ext = new ExtClass();
Pkg1Protected p = ext.new Pkg1Protected();
//Pkg1Private p1 = ext.new Pkg1Private();
}
}
可以看到,被声明为protected的member class可以通过外部类的子类的实例进行使用,而private的成员类则不行(呗注释掉的一行是不合法的。)
3.2 protected修饰构造器
如果一个类的构造器被修饰为protected,那么这个类可以在同一个包或者其子类中被实例化。在同一个包中被实例化容易理解。在子类中被实例化到底什么玩意儿?如果其子类也在同一个包,那一回事。如果子类不在同一个包,那么这个被protected修饰的构造器一样可以被使用,一般通过super来调用。例如,父类代码如下:
/**
* @author Brandon B. Lin
*
*/
public class People {
protected People() {
}
}
子类跟父类在不同的package中,代码如下:
/**
* @author Brandon B. Lin
*
*/
public class Children extends People {
public Children() {
super();
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
在子类的构造器中,可以调用父类的构造器,及super();如果将父类中的构造器protected去掉,变为包访问,则调用super()是错误的,编译通不过。
3.3 protected修饰域和方法
这个相对简单了,被protected修饰的域和方法,除了包访问范围外,在子类也可以被访问,即使在不同的包。不多解释。
4.public修饰符
public意思是全世界都可以使用。
4.1 public修饰class:
没什么可说呀。top-level要么没有修饰符,要么public。而成员类可以被四种访问控制符任何一个修饰,即使使用public修饰member class不是个好的设计。
4.2 public构造器
简单明了,全世界都可以实例化这个类。能够使用构造器进行实例化的前提当然是这个类可以被使用,比如如果这个类使用默认修饰符,那么在其他包中跟你没办法引用这个类(也就是你根本import不进去),也就没办法使用构造器。
4.3 public修饰域和方法
一样简单,全世界都可以。但是需要注意的是,用public修饰一个实例域不是什么好主意,把static或者final static的域限定为public倒是个好想法。我们这里说导出都可以访问,其实不然,比如我们定义一个类,不是public,如下:
/**
* @author Brandon B. Lin
*
*/
class Car {
public static final int wheel=4;
private String color;
public Car(String color) {
this.color = color;
}
public String getColor() {
return color;
}
}
在其他包中,我们根本没办法访问这个Car类,更不用说访问域和方法了。所以也不是全世界都能访问。
初稿暂时这样,日后进一步修改完善,欢迎指错。
版权所有,转载请注明出处。