1、概述
Java中在声明类的成员时,会通过访问修饰符声明其访问权限,大概分为public、protected、default和private。
2、各种访问修饰符标识的可见性概括
各种修饰符标识的可见性,可以通过一张表格来概括:
类内部 | 本包 | 子类 | 外部包 | |
---|---|---|---|---|
public | Yes | Yes | Yes | Yes |
protected | Yes | Yes | Yes | No |
default | Yes | Yes | No | No |
private | Yes | No | No | No |
3、private修饰符代表的可见性说明
private修饰符代表的可见性在Oracle的Java文档的描述如下:
Otherwise, 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.
也就是说,如果一个成员被声明为private,它就只能在定义该成员的最外层类的范围之内访问。具体地,有两点容易被忽略。
3.1 可见性是针对于类来说的,不是对象
Java语言是面向对象的,理解成员的可见性时,容易理解为对象的可见性,认为在任何地方访问对象的私有成员时,都需要通过public的get方法。其实,不是这样的,看下面的例子:
public class Point {
private int x, y;
public Point(int x, int y){
this.x = x;
this.y = y;
}
public void move(int dx, int dy) { x += dx; y += dy; }
public int getX() { return x; }
public int getY() { return y; }
public double getDistance(Point p){
return Math.sqrt(
Math.pow((this.x - p.x), 2) + //这里因为是在对象p的类内部,所以可以直接访问p.x
Math.pow((this.y - p.y), 2)
);
}
public static void main(String []args){
Point p1 = new Point(0, 0);
Point p2 = new Point(1, 1);
System.out.println(p1.getDistance(p2));
System.out.println(p2.x); //因为这里仍然属于类的内部,是可以直接访问的p2.x的
}
}
其实,有一种很常见的情况可以印证这一点。在重写equals方法的时候,会经常使用到Objects.equals()方法,经常会将对象的私有成员直接传进去比较。下面是一个例子:
import java.util.Objects;
public class Point {
private int x, y;
public Point(int x, int y){
this.x = x;
this.y = y;
}
public void move(int dx, int dy) { x += dx; y += dy; }
public int getX() { return x; }
public int getY() { return y; }
/**
* 重写equals方法
* */
@Override
public boolean equals(Object p){
//a quick test to see if the objects are identical
if(this == p)
return true;
//must return false if the explicit parameter is null
if(p == null)
return false;
//if the classes don't match, they can't be equal
if(getClass() != p.getClass())
return false;
//now we know p is a non-null Point
Point other = (Point) p;
//test whether the fields have identical values.
return Objects.equals(this.x, other.x) && Objects.equals(this.y, other.y);
}
@Override
/**
* 如果重新定义了equals方法,就必须也重新定义hashCode方法
* 如果不这样的话,就会违反Object.hashCode的通用约定,
* 从而导致该类无法结合所有基于散列的集合一起正常运作,这样的集合包括HashMap、HashSet和Hashtable。
* */
public int hashCode(){
return Objects.hash(x, y);
}
public static void main(String []args){
Point p1 = new Point(0, 0);
Point p2 = new Point(0, 0);
System.out.println(p1 == p2);
System.out.println(p2.equals(p1));
}
}
3.2 内部类的私有成员可以在内部类的外面访问
前面的oracle文档翻译中,加粗的部分就是说的这个意思。见下面一个来源于Stack Overflow的例子,如下:
class ABC{
class XYZ{
private int x=10;
}
/**
* 类型后面三个点(String...),是从Java 5开始,Java语言对方法参数支持一种新写法,
* 叫可变长度参数列表,其语法就是类型后跟...,表示此处接受的参数为0到多个Object类型的对象,或者是一个Object[]。
* */
public static void main(String... args){
ABC.XYZ xx = new ABC().new XYZ();
System.out.println("Hello :: "+xx.x); //This is allowed!!
}
}