Java中可以通过访问控制符来控制访问权限。其中包含的类别有:public, “友好的”(无关键字), protected 以及 private。在C++中,访问指示符控制着它后面所有定义,直到又一个访问指示符加入为止,而在Java中,每个访问指示符都只控制着对那个特定定义的访问。

为Java创建一个源码文件时,它通常叫做一个“编译单元”(有时也叫做“翻译单元”)。每个编译单元都必须有一个以.java结尾的名字。而且在编译单元内部,可以有一个公共(public)类,它必须拥有与文件相同的名字(包括大小写形式,但排除.java文件扩展名)。同时每个编译单元内都只能有一个public类,其它的类可在包外部进行隐藏,因为它们是非“公共”的。

在编译.java文件时,我们会获得一个名字完全相同的输出文件;但对于.java文件中的每个类,它们都有一个.class扩展名。一个有效的程序就是一系列.class文件,它们可以封装和压缩到一个JAR文件里。Java解释器负责对这些文件的寻找、装载和解释。

注:Java并没有强制一定要使用解释器。一些固有代码的Java编译器可生成单独的可执行文件。

Java解释器的工作程序如下:首先,它找到环境变量CLASSPATH。CLASSPATH包含了一个或多个目录,它们作为一种特殊的“根”使用,从这里展开对.class文件的搜索。从那个根开始,解释器会寻找包名,并将每个点号替换成一个斜杠,从而生成从CLASSPATH根开始的一个路径名(如package foo.bar.baz会变成foo/bar/baz或foo\bar\baz)。随后将它们连接到一起,成为CLASSPATH内的各个条目(入口)。以后搜索.class文件时,就可以从这些地方开始查找准备创建的类名对应的名字。此外,它也会搜索一些标准目录。

自动编译

为导入的类首次创建一个对象时,编译器会在适当的目录里寻找同名的.class文件。若只发现X.class,它就是必须使用的那个类。然而,如果它在相同的目录中还发现一个X.java,编译器就会比较两个文件的日期标记。如果X.java比X.class新,就会自动编译X.java生成一个最新的X.class。

Java访问指示符

如果根本不指定访问指示符,则为默认访问,它通常称为“友好”访问。这意味着当前包内的其他所有类都能访问“友好”的成员。但对包外的所有类来说,这些成员却是“私有”的,外界不得访问。由于一个编译单元只能从属于单个包,所以单个编译单元内的所有类相互间都是自动“友好”的。因此,我们也说友好元素拥有“包访问”权限。

为了获得对一个类成员的访问权限,唯一的方法就是:

  1. 使成员成为"public"。这样所有人从任何地方都可以访问它
  2. 变成一个“友好”成员,方法是舍弃所有访问控制符,并将其类置于相同的包内。这样一来,其他类就可以访问成员
  3. 一个继承的类既可以访问一个protected成员,也可以访问一个public成员(但不可以访问private成员)。只有两个类位于相同的包内时,它才可以访问友好成员
  4. 提供“访问器/变化器”方法(亦称“获取/设置”方法),以便读取和修改值。

对于private关键字,除非是那个特定的类或者该类的方法中,否则没有人能够访问private关键字修饰的成员。

当使用private对默认构造器进行定义时,可以防止对这个类的继承。

我们使用extend关键字来继承基类。语法如:

Class A extend B{}

其中A将成为B的衍生类。在衍生器的构建器中,Java会自动插入对基础类构建器的调用,当然,只会调用默认构建器,如果需要使用基类的含参构建器,则需要通过super(参数列表)来进行调用。同时,在衍生类进行初始化的时候,将首先对其基类进行初始化。需要注意的是,当基础类中不包含无参构建器时,需要在衍生类的构建器的第一行进行基类构建器的指定。

public class Test {
public static void main(String[] args) {
B b = new B();
}
} class A{
public A(){
System.out.println("construct A");
}
} class B extends A{
public B(){
System.out.println("construct B");
}
}
// output
/*
construct A
construct B
*/
public class Test {
public static void main(String[] args) {
B b = new B("hello");
}
} class A{
public A(String a){
System.out.println("construct A with param " + a);
}
} class B extends A{
public B(String b){
super(b);
System.out.println("construct B");
}
}
// output
/*
construct A with param hello
construct B
*/

许多程序设计语言都有自己的办法告诉编译器某个数据是“常数”。常数主要应用于下述两个方面:

  1. 编译期常数,它永远不变
  2. 在运行期初始化一个值,我们不希望它发生变化

对于编译期的常数,编译器(程序)可将常数值“封装”到需要的计算过程中。也就是说,计算可在编译期间提前执行,从而节省运行时的一些开销。在Java中,这些形式的常数必须属于基本数据类型,而且要用final关键字进行表达。在对这样的一个常数进行定义时,必须给出一个值。并且对于final字段,其储存的一个数据是不得改变的。

对于基本数据类型,final会将值变成一个常数,但对于对象句柄,final会将句柄变成一个常数。进行声明时,必须将句柄初始化到一个具体的对象。而且永远不能将句柄变成指向另一个对象。然而,对象本身是可以修改的。

对于自变量来说,如果我们使用final进行修饰,则不能在之后对其值进行修改,而如果是一个方法(函数)被我们使用final进行修饰,则该方法的行为在继承期间将会保持不变,而且不可被覆盖或改写。如果我们希望一个类不能被继承,我们同样也可以使用final进行修饰。

在同时有static且进行继承时,程序的初始化顺序为:装载程序首先会注意到它的基础类,随后将之载入。无论是否准备生成那个基础类的一个对象,这个过程都会发生。若基础类中含有另一个基础类,则另一个基础类随即也会载入,以此类推。接下来,会在根基础类执行static初始化,再在下一个衍生类执行,以此类推。此时,所有必要的类已经装载完毕,所以能够创建对象。首先这个对象中的所有基本数据类型都会设成它们的默认值,而将对象句柄设置为null。随后会调用基础类构建器。基础类的构建采用与衍生类构建器完全相同的处理过程。基础类构建器完成之后,实例变量会按本来的顺序得以初始化。最后,执行构建器剩余的主体部分。

Thinking in Java学习杂记(1-4)

Thinking in Java学习杂记(第7章)

最新文章

  1. 继续Get News List
  2. 调用discuz编辑器再也不是问题了
  3. find the peak value
  4. lnmp 下安装yaf
  5. Https要点
  6. Flash Builder 条件编译的实现
  7. PostConstruct注解
  8. 【解决方案】纯js动态克隆表一行元素
  9. 分库分表后跨分片查询与Elastic Search
  10. PVID和VID彻底研究(上) ——PVID的作用及和VID的区别
  11. linux 查找匹配文件中包含指定字符的 前五行,这里是指所有匹配的前五行
  12. Knockout: 使用CSS绑定和event的blur失去焦点事件, 给未通过校验的输入框添加红色边框突出显示.
  13. UpdateData()用法
  14. pthread_once函数的简单示例
  15. Leetcode 39
  16. shell ssh远程执行命令
  17. 使用原生JavaScript实现对select增加option标签并附加value属性
  18. 【转】CentOS下expect 安装
  19. es6的Set和Map数据结构
  20. week05《Java程序设计》第五次学习总结

热门文章

  1. 【原创】面试官问我G1回收器怎么知道你是什么时候的垃圾?
  2. C++走向远洋——37(工资类,2)
  3. MYSQL对数据库和表的基本操作
  4. 达拉草201771010105《面向对象程序设计(java)》第十八周学习总结
  5. Ajax&Json案例
  6. jvm GC算法和种类
  7. Day 3 DP
  8. spring——AOP原理及源码(三)
  9. angularV4+学习笔记
  10. C++对拍