java拾遗录2
主题:core java2——初始化清理和访问权限
一、初始化
Java中,每个类在使用前都需要进行初始化,所以理解初始化是理解java的开端。
在Java中有两个隐藏了的方法:<clinit></clinit>和<init></init>。这两个方法是属于Java虚拟机的,也就是说在程序中我们是不可以调用的。
public class PrintClass {
public static void main(String[] args){
new B();
}
PrintClass(String var) {
System.out.println(var);
}
}
class A {
static PrintClass pClassA1 = new PrintClass("A. 静态成员的初始化1");
static {
System.out.println("A. 静态初始化块1");
}
static {
System.out.println("A. 静态初始化块2");
}
static PrintClass pClassA2 = new PrintClass("A. 静态成员的初始化2");
{
System.out.println("A. 实例初始化块1");
}
PrintClass pClassA = new PrintClass("A. 实例成员的初始化");
{
System.out.println("A. 实例初始化块2");
}
public int Avar;
public A() {
System.out.println("A. 构造方法");
doSomething();
}
private void doSomething() {
// public void doSomething() {
Avar = 1111;
System.out.println("Avar=" + Avar);
}
}
//class B extends A
class B extends A
{
public static void main(String[] args){
new B();
}
public int Bvar = 2222;
{
System.out.println("B. 实例初始化块1");
}
{
System.out.println("B. 实例初始化块2");
}
PrintClass pClassB = new PrintClass("B. 实例成员的初始化");
static {
System.out.println("B. 静态初始化块1");
}
static PrintClass pClassB1 = new PrintClass("B. 静态成员的初始化1");
static PrintClass pClassB2 = new PrintClass("B. 静态成员的初始化2");
static {
System.out.println("B. 静态初始化块2");
}
public B() {
System.out.println("B. 构造方法");
doSomething();
}
public void doSomething() {
System.out.println("Bvar=" + Bvar);
}
}
结果如下:
A. 静态成员的初始化1
A. 静态初始化块1
A. 静态初始化块2
A. 静态成员的初始化2
B. 静态初始化块1
B. 静态成员的初始化1
B. 静态成员的初始化2
B. 静态初始化块2
A. 实例初始化块1
A. 实例成员的初始化
A. 实例初始化块2
A. 构造方法
Avar=1111
B. 实例初始化块1
B. 实例初始化块2
B. 实例成员的初始化
B. 构造方法
Bvar=2222
由此可知当新建一java对象(上面main方法中new B())时,它的内部初始化顺序为:
1. 父类静态成员和静态初始化块 ,按在代码中出现的顺序依次执行
2. 子类静态成员和静态初始化块 ,按在代码中出现的顺序依次执行
3. 父类实例成员和实例初始化块 ,按在代码中出现的顺序依次执行
4. 父类构造方法
5. 子类实例成员和实例初始化块 ,按在代码中出现的顺序依次执行
6. 子类构造方法
二、该死的无参构造
java会在每一个类中默认给你造一个无参的构造,如果你定义了有参构造那么系统将不会给你再添加无参构造,但在通过反射方式来创建对象,是会需要一个无参构造的。这点在hibernate上用的很多,所以写javabean时,最好再显式的加上一个无参构造。
三、关于垃圾回收
首先要记住三点:
1 垃圾对象可能还不会被垃圾回收
2 垃圾回收并不等同于“析构”
3 显式的调用你想销毁某个对象时的操作,而不是将它加在finalize()中
再看几个小例子
程序段1:
1.fobj = new Object ( ) ;
2.fobj. Method ( ) ;
3.fobj = new Object ( ) ;
4.fobj. Method ( ) ;
问:这段代码中,第几行的fobj 符合垃圾收集器的收集标准?
答:第3行。因为第3行的fobj被赋了新值,产生了一个新的对象,即换了一块新的内存空间,也相当于为第1行中的fobj赋了null值。这种类型的题在认证0考试中是最简单的。
程序段2:
1.Object sobj = new Object ( ) ;
2.Object sobj = null ;
3.Object sobj = new Object ( ) ;
4.sobj = new Object ( ) ;
问:这段代码中,第几行的内存空间符合垃圾收集器的收集标准?
答:第1行和第3行。因为第2行为sobj赋值为null,所以在此第1行的sobj符合垃圾收集器的收集标准。而第4行相当于为sobj赋值为null,所以在此第3行的sobj也符合垃圾收集器的收集标准。
如果有一个对象的句柄a,且你把a作为某个构造器的参数,即 new Constructor ( a )的时候,即使你给a赋值为null,a也不符合垃圾收集器的收集标准。直到由上面构造器构造的新对象被赋空值时,a才可以被垃圾收集器收集。
程序段3:
1.Object aobj = new Object ( ) ;
2.Object bobj = new Object ( ) ;
3.Object cobj = new Object ( ) ;
4.aobj = bobj;
5.aobj = cobj;
6.cobj = null;
7.aobj = null;
问:这段代码中,第几行的内存空间符合垃圾收集器的收集标准?
答:第7行。注意这类题型是认证考试中可能遇到的最难题型了。
行1-3分别创建了Object类的三个对象:aobj,bobj,cobj
行4:此时对象aobj的句柄指向bobj,所以该行的执行不能使aobj符合垃圾收集器的收集标准。
行5:此时对象aobj的句柄指向cobj,所以该行的执行不能使aobj符合垃圾收集器的收集标准。
行6:此时仍没有任何一个对象符合垃圾收集器的收集标准。
行7:对象cobj符合了垃圾收集器的收集标准,因为cobj的句柄指向单一的地址空间。在第6行的时候,cobj已经被赋值为null,但由cobj同时还指向了aobj(第5行),所以此时cobj并不符合垃圾收集器的收集标准。而在第7行,aobj所指向的地址空间也被赋予了空值null,这就说明了,由cobj所指向的地址空间已经被完全地赋予了空值。所以此时cobj最终符合了垃圾收集器的收集标准。 但对于aobj和bobj,仍然无法判断其是否符合收集标准。
总之,在Java语言中,判断一块内存空间是否符合垃圾收集器收集标准的标准只有两个:
1.给对象赋予了空值null,以下再没有调用过。
2.给对象赋予了新值,既重新分配了内存空间。
最后再次提醒一下,一块内存空间符合了垃圾收集器的收集标准,并不意味着这块内存空间就一定会被垃圾收集器收集。
另外,调用System.gc()只是建议JVM进行GC。至于JVM到底会不会做,那就不好说啦。通常不建议自己手动调用System.gc(),还 是让JVM自行决定比较好。另外,使用JVM命令行参数“-XX:+DisableExplicitGC”可以让System.gc()不起作用。
四、那些访问权限的故事
Java中的访问权限
1、public修饰词,表示成员是公开的,所有其他类都可以访问;
2、private修饰词,表示成员是私有的,只有自身可以访问;
3、无修饰词,表示包访问权限(friendly),同一个包内可以访问;
4、protected,表示受保护权限,体现在继承,即子类可以访问父类受保护成员,同时相同包内的其他类也可以访问protected成员。
5、类的访问限制,仅有public和包访问权限
a、一个Java文件只能有一个public类
b、public类的名称必须同Java文件名完全一致
c、若Java文件中没有public类,则文件名可以任意
6、final关键字
a、final数据成员,使用前总是被初始化并被强制要求在定义处或构造器中赋值;一旦赋值后,对于基本类型其值会恒定不变,而对于对象引用会始终指向赋值的对象,但指向对象自身是可以修改的;
b、final参数,对于基本类型表示无法改变参数的值,对于对象引用表示无法改变引用所指的对象;
c、final方法,保证方法不会在继承后修改和重载;所有的private方法都隐含式final的;Java中使用动态绑定(后期绑定)实现多态,除了static和final方法是使用前期绑定的;
d、final类,表示该类不可被继承。
总的来说,这一部分没什么难点,随便看看就懂了,关键是一点:
选择最合适的访问权限,即能大能小,选择小的。
比如说我有一个工具类,它用来返回数据库连接,那么这个类提供一个方法,用于返回连接getCon()
那么这个方法用public static Connection getCon(){}就比较好,但如果不是工具类,是一个普通的类,那么就没必要了,比如下面这个例子:
package dao;
public class UserDaoImpl{
//用于保存对象
public void save(){
if (exist())
System.out.println("保存失败");
else
System.out.println("保存成功");
}
//用于判断对象是否已经存在
public boolean exist(){
System.out.println("判断是否存在");
return false;
}
}
package test;
import dao.UserDaoImpl;
public class Test4kongzhi{
public static void main(String[] args){
UserDaoImpl userDaoImpl = new UserDaoImpl();
userDaoImpl.save();
}
}
这两段代码,主要模拟了对象的存储,首先要判断对象是否存在,然后再存储,这时候我们发现,也许把
public boolean exist(){
System.out.println("判断是否存在");
return false;
}
这个方法改为private修饰比较好,因为这个方法不需要,或者说不要暴露给调用者,所以用private修饰比较好。
ok,今天可能就这么多,后面继续。