一.java的四种访问权限
1.几个注意的细节:
- 类的访问权限只有两种:public和default(默认包访问权限)
- java编译单元概念:一个.java文件是一个编译单元,每个编译单元最多只能有一个public类,而且该public类名称必须与文件的名称相同(包括大小写,但不包括文件后缀名.java).如果该编译单元内还有其他类,那么这些类只能是default权限,不能被包外访问,而且这些类的主要作用是为public提供支持的(内部类算另外一个编译单元,因为内部类会被编译成另外一个.class文件)
- 构造函数也可以私有化(private),然后在类中实例化一个对象,再通过方法返回该对象
- 成员变量(在类里面声明的变量)在声明时可以不给它初始化,编译器会自动给这个成员变量初始化,但局部变量(在方法里面声明的变量)在声明时一定要给它初始化,因为编译器不会自动给局部变量初始化,任何变量在使用之前必须对它进行初始化。
2.Java有四种访问权限, 其中三种有访问权限修饰符,分别为private,protected和public,还有一种不带任何修饰符的默认访问权限(也叫包访问权限)。
- private: Java语言中对访问权限限制的最窄的修饰符,一般称之为“私有的”。被其修饰的类、属性以及方法只能被该类的对象访问,其子类不能访问,更不能允许跨包访问。
- default:即不加任何访问修饰符,通常称为”默认访问权限”。该模式下,只允许在同一个包中进行访问。
- protect: 介于public 和 private 之间的一种访问修饰符,一般称之为“继承访问权限”。被其修饰的类、属性以及方法只能被类本身的方法及子类访问,即使子类在不同的包中也可以访问。
- public: Java语言中访问限制最宽的修饰符,一般称之为“公共的”。被其修饰的类、属性以及方法不仅可以跨类访问,而且允许跨包(package)访问。
3.下面用表格的形式来展示四种访问权限之间的异同点.表格如下:
(1)public修饰的类:
\ | 当前类 | 同一个包的非子类 | 不同包的非子类 | 同一包的子类 | 不同包的子类 |
---|---|---|---|---|---|
public | √ | √ | √ | √ | √ |
protected | √ | √ | √ | √ | |
default | √ | √ | √ | ||
private | √ |
(2)默认包访问权限的类(对于一个包里面的包访问权限类A,在其他package的类里面import类A的时候就已经出错了。所以,其他package普通类根本就访问不了类A,其他package的类也成为不了类A的子孙类(即默认访问权限的类的子类只能在同一包下)):
\ | 当前类 | 同一个包的非子类 | 不同包的非子类 | 同一包的子类 | 不同包的子类 |
---|---|---|---|---|---|
public | √ | √ | √ | ||
protected | √ | √ | √ | ||
default | √ | √ | √ | ||
private | √ |
(3)详解protected访问权限:
说例子之前,我们应该先正确理解一句话:protected修饰的属性或方法,表示在同一个包内或者不同包的子类可以访问。
解析:
“不同包中的子类可以访问”(子类可访问,意思是用子类的对象去引用方法),是指当两个类不在同一个包中的时候,继承自父类的子类内部且主调(调用者)为子类的引用时才能访问父类用protected修饰的成员属性或方法。 在子类内部,主调为父类的引用时并不能访问此protected修饰的成员(super关键字除外)。
我们用一个例子来仔细说明:
public class MyObject {
public static void main(String[] args) {
Object o1 = new Object();//Object对象有一个clone()方法,该方法是protected的
o1.clone();//此处报错:The method clone() from the type Object is not visible
}
}
解析:
子类对象创建的过程,其实是先加载其父类文件,创建其父类对象之后,再加载子类文件,创建子类对象,最后形成一个包裹结构的对象(见博文《继承》中对象创建的内存分析).如果子类没有覆盖父类中的属性和方法,则子类的属性和方法只是对父类中同名属性和方法的引用(也就是说没被覆盖的方法其实是父类的);如果子类覆盖了父类中的属性和方法,则被覆盖的属性和方法是对自己的引用(也就是说被覆盖的方法才是子类自己的).
public class MyObject2 {
public static void main(String[] args) throws CloneNotSupportedException {
MyObject2 obj = new MyObject2();
obj.clone();//编译通过
}
String name = "xxx";
public void shuchu() throws CloneNotSupportedException{
clone();
}
}
解析:
子类引用可以直接调用父类的protected方法,而且子类中可以直接调用父类方法
Test1.java
class MyObject {}
public class Test {
public static void main(String[] args) {
MyObject obj = new MyObject();
obj.clone(); // 这里编译报错:The method clone() from the type Object is not visible
}
}
我们知道clone()是Object类的protected方法.这说明,该方法可以被同包(java.lang)下和它(java.lang.Object)的子类访问.这里MyObject类是Object的子类(默认继承java.lang.Object).
同样Test1也是java.lang.Object的子类。但是,在Test类中,主调却为MyObject对象,因此编译报错
Test2.java
class MyObject2 {
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Test2 {
public static void main(String[] args) throws CloneNotSupportedException {
MyObject2 obj = new MyObject2();
obj.clone(); // 编译通过
}
}
这里,我们在MyObject2类中覆盖父类的clone()方法,在另一个类Test2中调用clone()方法,编译通过。
编译通过的原因显而易见,当你在MyObject2类中覆盖clone()方法时,MyObject2类和Test2类在同一个包下,所以此protected方法对Test2类可见。
Test3.java
package 1
public class MyObject3 {
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
package 2
public class Test3 extends MyObject3 {
public static void main(String args[]) {
MyObject3 obj = new MyObject3();
obj.clone(); // Compile error.
Test3 tobj = new Test3();
tobj.clone();// Complie OK.
}
}
这里我用Test3类继承MyObject3,注意这两个类是不同包的,否则就是示例2的情形。在Test3类中调用Test3类的实例tobj的clone()方法,编译通过。而同样调用MyObject3类的实例obj的clone()方法,编译错误!
意想不到的结果,protected方法不是可以被继承类访问吗?
必须明确,类Test3确实是继承了类MyObject3(包括它的clone方法),所以在类Test3中可以调用自己的clone方法。但类MyObject3的protected方法对其不同包子类Test3来说,是不可见的。
二.面向对象的三大特性之封装
- 封装的定义:封装从字面上来理解就是包装的意思,专业点就是信息隐藏,是指利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体,数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。系统的其他对象只能通过包裹在数据外面的已经授权的操作来与这个封装的对象进行交流和交互。也就是说用户是无需知道对象内部的细节(当然也无从知道),但可以通过该对象对外的提供的接口来访问该对象。
- 封装的好处:
(1)良好的封装能够减少耦合
(2)类内部的结构可以*修改
(3)可以对成员进行更精确的控制
(4)隐藏信息,实现细节 - 实现封装的一般步骤:
(1)修改属性的可见性来限制对属性的访问。一般把属性声明为private
(2)为每个属性创建一对赋值方法和取值方法,用于对这些属性的访问。(get,set方法)
(3)在赋值和取值方法中,加入对属性的存取限制。