java拾遗录2

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赋值为nulla也不符合垃圾收集器的收集标准。直到由上面构造器构造的新对象被赋空值时,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类的三个对象:aobjbobjcobj 
4:此时对象aobj的句柄指向bobj,所以该行的执行不能使aobj符合垃圾收集器的收集标准。 
5:此时对象aobj的句柄指向cobj,所以该行的执行不能使aobj符合垃圾收集器的收集标准。 
6:此时仍没有任何一个对象符合垃圾收集器的收集标准。 
7:对象cobj符合了垃圾收集器的收集标准,因为cobj的句柄指向单一的地址空间。在第6行的时候,cobj已经被赋值为null,但由cobj同时还指向了aobj(第5行),所以此时cobj并不符合垃圾收集器的收集标准。而在第7行,aobj所指向的地址空间也被赋予了空值null,这就说明了,由cobj所指向的地址空间已经被完全地赋予了空值。所以此时cobj最终符合了垃圾收集器的收集标准。 但对于aobjbobj,仍然无法判断其是否符合收集标准。 
总之,在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,今天可能就这么多,后面继续。







 

你可能感兴趣的:(java 从头到尾)