J2SE理解之一:声明和访问控制
Java的声明和访问控制
张磊zhangleispirit@gmail.com
Java的声明元素主要包括类(包括抽象类),接口,数组,异常和枚举类型的声明。其中各个部分又包含如下的各种基本元素:
类(包括抽象类):属性声明,构造方法声明,方法声明,变量(注:这里的变量不包含属性)
接口:接口常量和接口方法
数组,异常,枚举。
类的声明:
1.类本身的声明:
对类的声明来说,主要包括类的访问权限声明和非访问修饰符的使用。对于一个普通的Java类(POJO)来说,主要的访问权限修饰符只有两个public和默认权限,内部类可以有private权限。非访问修饰符主要包括abstract,final和strictfp.
(1)这里先讨论非访问修饰符,下面是基本的规则:
abstract:声明一个类是抽象的。
final:声明一个类是可以被继承的。
strictfp:声明一个类中所有的方法在处理浮点数时遵循IEEE754规则。
由上面的这些规则可以得出一个结论:一个类不可能既声明为abstract的,又声明为final的。
抽象类的实质即为继承它的类定义了一组行为准则,任何继承它的类都必须实现抽象类所定义的抽象方法,抽象类不可以实例化,抽象类中可以不包含抽象方法,但是含有抽象方法的类一定要声明为抽象类。
final类表示一个类似不能被继承的。如果你不希望一个类的行为被改变,或者说该类的行为是整个系统的基础,就跟数学中的数字一样的话,就可以把这个类声明为final的。
(2)类的访问权限修饰符:
首先需要弄清楚的是类的访问包括哪些方面:
一个类中创建另一个类的实例;
一个类继承自另一个类;
一个类中的方法访问另一个类的方法和变量。
类的访问权限声明为public的,表示该类可以被任意的类访问,当然,在其他类中访问该类时,还是要在源文件的头部做相应的import工作。
类的访问权限为默认权限,则表示和该类处在同一个包下的类可以访问它 ,此处注意以下比较容易忽视的地方:
源文件一:
package cert;
class test1 {}
源文件二:
package cert2;
import cert.test1;
public class test2 extends test1{}
这里test2类会产生错误,因为test1是默认访问权限,test2 使用继承的方式访问test1,由于这两个类不在同一个包中,因此test2是无法看到test1 的。
2.类的成员访问权限声明
(1)访问权限的声明:
类的成员可以使用全部的四种访问权限:public ,protected,默认,private。
这里需要记住的第一条准则就是:判断类的成员的访问权限,必须先判断类的访问权限,如果整个类对于另一类来说都是不可见的,那么这个类的所有成员都是不可见的。
类的成员的访问权限,当涉及到子类时会变得很复杂,这里首先要弄清楚两种访问的区别:
一个类的方法的代码是否能够访问另一个类的成员(即:通过在一个类中创建另一个类的对象,通过该对象来实现对另一个类的访问)。
一个类是否能够继承超类的成员。
public成员访问:
如果一个类的成员声明为public,那么通过创建类的实例的方式,都是可以访问的,只要类的访问权限没有问题。通过继承,子类可以拥有超类的public成员,而且,第三个类访问该子类时,仍然可以访问子类所继承的超类的成员。
举例说明如下:(注:三个类位于不同的源文件中)
package cert;
public class Test1 {
public String doThing(){
return "zhanglei ";
}
}
package cert2;
import cert.Test1;
class Test2 extends Test1{
public String doThing2(){
return "hello,"+doThing();
}
}
package cert2;
import cert2.Test2;
public class Test3{
public static void main(String[] args){
Test2 test=new Test2();
System.out.println(test.doThing2());
}
}
private 成员访问:
表示该成员是该类的私有成员,任何类通过上面的两种方式都是不可访问的。只可以在该类的定义中使用,或者通过该类的get和set方法来访问。
这里插一点:一个类的构造函数是可以被声明为私有的,一旦该类的构造方法被声明为私有的,则必须提供一个public的方法来创建该类的实例,主要用在工厂模式和单态模式中。
另外一个需要注意的地方是:对于超类的私有方法,若在子类中出现了相同签名的方法,这不是重写。而只是刚好超类和子类的两个方法相同。其理论依据就是:子类是不能继承超类的私有方法的。
默认成员访问:
表示该类的成员在同一个包下的其他类中是可以通过上面两种方式来访问的。
protected成员访问:
这个访问权限是最麻烦的,分成两种情况来讨论:
1.当考虑一个类通过创建另一个类的实例的方式实现访问时,它产生的行为与默认权限相同,既在同一个包中的类可以相互创建实例访问。这里有一种特殊情况:就是当一个类是另一个类的子类时,如果两个类不在同一个包中,那么子类通过创建超类的实例的方式是不可以访问的,就和两个基本类的行为一样,这里不要受继承关系的影响。
2.当考虑一个类继承另一个类的成员时,当超类的成员声明为protected 的,则包内和包外的继承自它的子类都可以拥有该成员,而且对于子类来说,它通过继承拥有的成员对它来说是私有的,任何第三方的类都是不可访问的。
3类的成员非访问权限的声明:
final:final类是防止有类继承该类。final方法是防止该方法在子类中被重写。另外final 关键字可以被用在方法的参数,表示该参数是只读的:
public String getThing(int number1,final number2){}
abstract:声明一个抽象方法,方法以分号结束,没有大括号。
public String setSomething(); //抽象方法
public String setSomething(){ } //实例方法
abstract抽象方法不能被声明为private的,因为抽象方法始终需要被重写。
abstract抽象方法不能和static同用。
synchronized:只能用于方法声明,表示方法一次只能被一个线程访问。
native:只能用于方法声明,表示该方法是用其他的语言来实现的,native的方法体必须是以;分号结束,没有方法体。
具有可变参数列表的方法:
举例:void doStuff(int... x){}
void doStuff(String str,Animal... animal){}
规则是这样的:1.参数类型后面接省略号(...),一个空格,以及保存接收参数的数组名称
2.可变参数必须是最后一个参数,且方法中只能有一个可变参数。
接口的声明:
1.接口声明:与类本身的声明相同,接口隐含是abstract的,因此不能声明为final的。另外需要注意接口之间是可以多继承的,使用extends关键字。
2.接口方法的声明:接口的方法,隐含都是public和abstract的,因此接口方法不能声明为final,static,private和protected的。
注意:接口和抽象类的方法声明,其子类在实现这个方法时,不能修改方法声明的访问权限。
3.接口常量的声明:接口内允许放置常量,因此隐含public static final的,实现接口的类不能修改接口中定义的常量的值。
数组的声明:
数组有两种声明方式:
int[] key;或者
int key[];
多维数组:
String[][] str;或者
String str[][];或者
String[] str[];
注意:数组声明中,不能包含数组的长度。
枚举的声明和定义:
1枚举的声明:
枚举的声明位置有两种:一种是单独成类。另一种是放在类中,但是不能放在类的方法中。枚举的访问修饰只能使用public和默认。
枚举不能被声明为static,final ,abstract.
举例:
第一种方式:enum Test{SMALL,MIDDLE,BIG};
第二种方式:public class Test2{
enum Test{SMALL,MIDDLE,BIG};
Test test;
public static void main(String[] args){
Test2 test2=new Test2();
test2.test=Test.BIG;
}
}
2枚举的实质及分析:枚举的实质就是一个类,拿Test枚举来说,可以这样类比:Test是一个类,它的成员是三个Test类型的对象SMALL,MIDDLE,BIG.
枚举可以定义构造函数和方法(因为枚举还是一个类)
例如:
enum Test{
SMALL(1),MIDDLE(2),BIG(3);//这里用整数值初始化了三个对象,隐式调用了构造函数
Test(int num){
this.num=num;
}//这里定义了构造函数
private int num;//枚举类的核心参数,可以做任意修改
public int getNum(){
return num;
}
}
注:枚举类型的构造函数是可以重载的。