访问权限控制
1.Java文件组织
.java文件: 一个包中有多个.java文件,每个java文件都是一个编译单元。编译一个.java文件时,.java文件中的每个类都会有一个输出文件,输出文件名称和.java文件中每个类的名称相同,只是多一个后缀名.class。每个编译单元内只能有一个public类,该类的名称必须与文件的名称相同(包括大小写)。非public类对包外不可见,主要为public class服务。
可运行程序:是一组可以打包并压缩为一个Java文档文件的.class文件。
package: 类库是一组类文件,每个文件都有一个public类,以及任意数量的非public类。因此每个文件都有一个构件。若将这些构件(每一个都有自己独立的.java和.class文件)从属于同一个群组,就可以使用关键字package。除注释外的第一行代码,在文件起始处写。
注意Java包的命名全部选用小写字母。
2.类库的使用
使用import关键字:import java.lang.* import java.util.ArrayList
条件编译:不改变程序代码,可以切换开关产生不同的行为,如调试功能。
3.包权限访问修饰词
pubic、protected、friendly、private
包访问权限:
- 如果包中成员为public,则包及包之外都可以访问该成员。
- 如果包中成员为friendly,则只有包之内的才能对其进行访问。
- 如果包中成员为private,则只有包含该成员的类才能对其进行访问。——但是可以通过访问器(accessor)和变异器(mutator)方法(get/set)读取或者改变原来的值。
- 继承的类可以访问protected和public成员
//get、set方法
public class User{
private String username;
private int age;
public void setUsername(String username){
this.username = username;
}
public char getUsername(String username){
return username;
}
}
类的访问权限修饰符:public friendly
成员变量和成员方法访问权限修饰符:private public protected friendly
4.重点程序 lunch.java:
class Soup1{
private Soup1() {}//构造器私有化
public static Soup1 makesoup(){//用static方法不创建对象,调用构造器
return new Soup1();
}
}
class Soup2{
private Soup2() {}
private static Soup2 ps1 = new Soup2();
//私有的静态对象,容易忘记ps1是静态的
public static Soup2 access(){
return ps1;//只能访问唯一的Soup2对象
}
public void f() {}
}
public class Lunch{
void testStatic() {
Soup1 soup = Soup1.makesoup();//静态方法创建对象,可创建多个
}
void testSingleton() {
Soup2.acess().f();//只可以访问唯一的一个对象
}
}
5.包名的编码和解析
包名的第一部分是类的创建者的反顺序的域名。
第二部分是将package名称分解为机器上的一个目录。当Java程序运行需要加载class文件时,可以确定文件在目录上所处的位置。首先,Java解释器将找出环境变量CLASSPATH,CLASSPATH包含一个或多个目录,用作查找.class文件的根目录。从根目录开始,解释器获取包的名称并将每个句点替换成反斜杠,以从CLASSPATH根中产生一个路径名称。
6.封装
封装:封装是指把数据成员和相关方法都放进一个类中,并使用访问权限来控制其可见性。
第六章 复用类
1.组合
在新类中使用现有类的对象作为成员变量。由于新的类是有现有类的对象组成,所以这种方法称为组合。
组合复用的是功能。
对象引用的三种初始化方法:
- 指定初始化:
在定义对象的地方进行初始化,这意味着在构造器调用之前他们完成了初始化。
- 构造器初始化:
在构造器中初始化对象。
- 懒惰初始化
就在正要使用对象之前,进行初始化。对象引用初始化为NULL。
2.继承
按照现有类的类型创造新类。不改变类的形式,增加新的成员变量和方法。
继承复用的是接口。
为便于继承,成员变量定义为private,方法定义为public。
2.1 extends 和 super 关键字用法:
class Cleanser{
public void scrub() {}
}
public class Detergent extends Cleanser{//用extends
public void scrub(){
append("Detergent.scrub()");
super.scrub();//调用基类的方法
}
}
super用于调用基类的方法,和在基类构造器带参数的情况下,在派生类构造器中显式调用基类构造器。
class Game{
Game(int i) {
}
}
class BoardGame extends Game{
BoardGame(int i){
super(i);
}
}
//或者也可以
class ComputerGame extends Game{
Computer(){
super(11);
}
}
2.2 名称屏蔽
如果Java中基类已经拥有某个已被多次重载的方法名称,那么在导出类中重新定义该方法并不会屏蔽在基类中的任何版本(这一点与C++不同)。
重写是重新覆盖父类的方法,在一些子类要实现的方法中,方法名、参数列表和返回值类型都和父类的方法一样,但具体实现与父类不同,这时候就需要重写父类的方法。
如果没有重写,那么子类调用一个子类没有的方法时,实际上是在调用父类。
以下是方法重载的一个例子。
class Homer {
char doh(char 'c') {
print("doh(char)");
}
float doh(float f) {
print("doh(float)");
}
}
class Milhouse {}
class Bart extends Homer {
void doh(Milhouse m) {
print("doh(Milhouse)");
}
}
public class Hide{
public static void main(String[] args) {
Bart b = new Bart();
b.doh(1);//doh(float)
b.doh('x');//doh(char)
b.doh(new Milhouse());//doh(Milhouse)
} //不能构造b.doh(0.2324);可以构造b.doh(0.2324f);
}
2.3 基类中的protected成员和方法可以被派生类访问
3.final关键字
3.1 final数据
对于基本类型,final使基本类型的数值不发生改变。
对于对象引用,final使引用恒定不变。但是对象的数值是可以修改的。
必须在域的定义处或者每个构造器中用表达式对final进行赋值。
注意:是每个构造器!
final与static:
对于常量,两者差别不大。最好使用public static final
来声明常量。
带有恒定初始值的final static常量全部用大写字母命名,单词之间用下划线隔开。
private final int valueone = 9;
private static final int VAULE_TWO = 99;
对于特殊变量:也不能认为某数据是final的,所以一定可以在编译前知道它的值。
private final int i4 = rand.nextInt(20);
//每次new新对象时值会改变,对于同一个对象值不变
private static final int INT_5 = rand.nextInt(20);
//对于所有创建的对象,经过第一次创建对象初始化后,值永远不会发生改变。
final参数:
Java允许在参数列表中,以声明的方式将参数指明为final。这意味着将无法在方法中更改引用所指向的对象。
3.2 final方法
目的:锁定方法,防止派生类修改;考虑效率,如果一个方法是final的,所有对该方法的调用都转为内嵌调用。
3.3 final类
不可以被继承
4.基类的初始化
(1)在创建派生类对象时,该对象包含一个基类的子对象。基类的子对象的初始化是通过调用基类构造器实现,自动在派生类的构造器中插入对基类构造器的调用。
(2)基类的构造器在派生类构造器之前被调用。
(3)如果没有默认的构造器或构造器带参数必须显式的调用。
class Insect {
int i = 9;
int j;
int m = prt("Insect.m initialized");
Insect() {
prt("i = " + i + ", j = " + j);
j = 39;
}
static int x1 =
prt("static Insect.x1 initialized");
static int prt(String s) {
System.out.println(s);
return 47;
}
}
public class Beetle extends Insect {
int k = prt("Beetle.k initialized");
int l = prt("Beetle.l initialized");
Beetle() {
prt("k = " + k);
prt("j = " + j);
}
static int x2 =
prt("static Beetle.x2 initialized");
public static void main(String[] args) {
prt("Beetle constructor");
Beetle b = new Beetle();
}
}
第一步:访问Beetle.main()函数
第二步:加载器启动并找出Beetle类的编译代码(在Beetle.class中)
第三步:继续加载基类Insect.class
第四步:对基类中的static进行初始化
第五步:对派生类中的static进行初始化
第六步:创建对象
第七步:将对象的内存设为二进制零值,所有的基本类型设为默认值,所有的引用设为NULL
第八步:基类实例变量初始化
第九步:调用基类构造器
第十步:派生类实例变量初始化
十一步:调用派生类构造器
执行结果:
static Insect.x1 initialized
static Beetle.x2 initialized
Beetle constructor
Insect.m initialized
i = 9, j = 0
Beetle.k initialized
Beetle.l initialized
k = 47
j = 39