我们先来说说消费者(客户端程序员)和生产者(类库开发人员)之间的关系,从消费者的角度出发,他们希望你的代码代码保持不变,哪怕是出现了新版本,这样就不耽搁他们的调用;但是从生产者的角度出发,他们必须对代码进行修改,以确保功能的更新,但同时又要兼顾这些改动不会对消费者造成影响。
所以情况已经很明显了,一边要改,一边不让改,为了解决这个问题,Java 提供了访问修饰符,供类库开发人员向客户端程序员指明那些是可用的,哪些不可用。访问权限的控制等级为:public、protected、default(该关键字可省略)和 private。
public
使用了 public 关键字,就意味着 public 之后紧跟着的类、接口、成员变量、方法都是对所有类可见,哪怕这些类不在同一个包下。我们先在 com.access 包下定义一个类 Ball:
package com.access;
public class Ball {
/**
* public 修饰无参构造器
*/
public Ball() {
System.out.println("Ball constructor!");
}
/**
* 没有修饰符,即为 default
*/
void play(){
System.out.println("play ball!");
}
}
然后在 com.model 包下新建一个 Student 类:
package com.model;
import com.access.Ball;
public class Student {
public static void main(String[] args) {
Ball ball = new Ball();
//play 方法不可调用,不信你试试
//ball.play();
}
}
这时候就发现调用 Ball 的 public 构造器毫无压力,但是调用 default 的方法就不行了,这是因为 default 修饰的方法只对当前包下面的类提供访问权限,其它包下的类禁止访问。
protected
关键字 protected 关键字修饰的访问权限,叫做继承访问权限。顾名思义,protected 处理的是继承的概念,我们通常会用一个子类通过关键字 extends 去继承一个父类,从而去获取父类的属性并改变父类现有成员的行为。那么问题来了,父类可能并不想让所有的类都能访问它的成员,而只想将权限赋予它的子类,那么就需要 protected 来完成这个任务了。
我们先在 com.access 包下定义一个 Fruit 类:
package com.access;
public class Fruit {
//用 protected 来修饰
protected double weight = 1.22;
//default 修饰
double size = 1.22;
}
然后在 com.model 包下定义一个 Banana 类:
package com.model;
import com.access.Fruit;
public class Banana extends Fruit{
void bite(){
//可以获取父类 weight 属性,但是不能获取 size
System.out.println(weight);
}
}
我们可以看到,子类和父类并不在一个包下,但是子类仍然可以访问父类 Fruit 的 weight 属性,同时也能看到父类中 default 修饰的 size 属性,子类是无法访问的。这就是 protected 关键字的功劳了,如果父类只想让子类(无论是否在同一个包)来访问它的某些属性,那么用 protected 修饰即可。
default
如果在一个成员、类、方法,接口前没有加任何关键字,那么就默认为 default,即包访问权限。这就意味着当前包中的所有其他类都对其有访问权限,但对于这个包之外的所有类,对其不可访问。
我们在 com.access 包下新建一个类 Food :
package com.access;
public class Food {
/**
* 没有关键字,即 default
*/
void eat() {
System.out.println("eat method");
}
}
/**
* 没有关键字修饰的类
*/
class Rice {
/**
* 没有关键字,即 default
*/
double weight = 1.22;
}
在 com.access 下再新建一个类 Dinner:
package com.access;
public class Dinner {
public static void main(String[] args) {
Food food = new Food();
//同包,可直接访问
food.eat();
//同包的类
Rice rice = new Rice();
System.out.println(rice.weight);
}
}
我们可以看到,随意访问同包下的没有加关键字(视为 default)的成员是没有压力的,这也就是设计目的了。包访问权限允许将包内所有的类组合起来,以使它们彼此之间可以轻松的相互作用。当把类组织起来放在一个包内,那么也就给他们的包访问权限的成员赋予了相互访问的权限。
private
关键字 private 的意思就是,除了当前类之外,任何其它类都无法访问这个成员。就相当于这个成员被隔离了,可以随意改变,而不用担心影响到其它的类。比如,以下这个单例:
public class SingletonTest {
/**
* 静态内部类
*/
private static class SingletonHandle {
private static SingletonTest singletonTest = new SingletonTest();
}
/**
* 私有构造器
*/
private SingletonTest() {
}
/**
* 通过静态内部类实现单例模式
* @return
*/
public static SingletonTest getSingletonTest() {
return SingletonHandle.singletonTest;
}
}
使用 private 关键字来修饰构造器,这样外部的类就无法通过 new 关键字访问构造器来获取该类的对象,而只能通过调用 getSinletonTest() 方法来获取类对象,这样就可以用过 private 关键字来控制消费者行为。同时,在一个类中,只要确定某个方法是“助手”方法,那么也可以定义为 private,以确保其它类的方法不会误用到它。
最后补充一点:类既不可以是 private 也不可以是 protected 的,对于类的访问权限,仅有两个选择:default 和 public。