《Java编程思想》第五章 初始化与清理

目录

 

前言:

1.用构造器确保初始化

2.方法重载

   java基本类型从小到大排序

3.this关键字

3.1 static关键字

4.终结处理和垃圾回收

5.成员初始化

6.构造器初始化

6.1 静态数据初始化

7.可变参数列表

8.数组的初始化

总结


前言:

本系列是我本人阅读java编程思想这本书的读书笔记,主要阅读第五章到第十七章以及第二十一章的内容,今天的笔记是第五章

1.用构造器确保初始化

构造器采用和类名相同的名称,当创建对象时,java虚拟机会自动调用构造器,为对象分配内存空间,注意:构造器名和类名一样采用首字母大写的方式,而不能用标准的驼峰命名方式。

不接收任何参数的构造器称为默认构造器,构造器也可以接收参数,称为有参构造器,构造器没有返回值。

当你没有为类创建默认构造器而创建对象的时候,java虚拟机会为类创建一个默认的无参构造器,因为没有构造器的话将无法创建对象。

2.方法重载

        和普通方法一样,构造器也支持方法的重载,这样就可以以不止一种方式来初始化对象了。注意:方法重载可以用参数个数来区分,也可以用参数类型来区分,当涉及到基本类型参数时,因为java存在自动隐式转换,传入的参数如果小于方法声明中的形式参数,将自动提升为较大的类型。char除外,如果无法找到能接收char类型参数的方法,char将自动转换为int类型。

   java基本类型从小到大排序

3.this关键字

如果你希望在方法的内部获得当前对象的引用,请使用this关键字,this指代当前对象,且只能在方法内部使用,当方法需要返回当前对象的时候,就可以用 return this;通常方法返回当前对象是用来方便链式调用的,比如

public class Leaf{
    int i=0;
    Leaf increment(){
        i++;
        return this;
    }

   void print(){
    System.out.println("i="+i);    
  }
}

就可以实现这样的链式调用。

public static void main(String[] args){
    Leaf leaf=new Leaf();
    leaf.increment().increment().increment().print();
}

有的时候一个类有多个构造器,当在一个构造器中想调用另一个构造器的时候,this就发挥的作用,因为this表示当前对象的引用,直接调用this(...)就指代了一个构造方法。不过需要注意的是,虽然可以使用this调用一个构造器,但是只能调用一个,不能调用两个,而且this调用构造器必须放在起始位置。

this还可以用在方法中防止参数和成员变量的重名问题。

3.1 static关键字

了解了this关键字后对static关键字就变得更好了解,static(静态)方法就是没有this的方法,也就是不用创建对象就可以调用的方法,是属于类本身的方法

4.终结处理和垃圾回收

大家知道初始化的重要性,但是常常忘记了同样也重要的清理工作。虽然java是有垃圾回收机制来管理垃圾回收的问题,但是垃圾回收器无法回收“特殊”的内存区域,由于垃圾回收器只负责回收new 分配的内存,那么当你使用到了“特殊”的内存的时候,垃圾回收器就不知道该怎么办了,为了应对这种情况,java允许在类中定义一个finalize()方法,原理是垃圾回收器准备回收垃圾时,先调用finalize()方法,然后才会真正回收对象占用的内存。但是要记住,java的对象可能不被垃圾回收,而且垃圾回收不等于“析构“,所以,finalize()方法不能作为通用的清理方法。

通常来需要用到finalize()方法的地方主要是在java的本地方法(native method)中。

说到这里,需要来说一下几种垃圾收集机制

  •  引用计数法

引用计数法是一种简单但是速度很慢的垃圾回收技术。每个对象都有一个引用计数器,当有引用连接至对象,计数器+1,当引用离开作用域或被置为null,计数器-1。虽然管理引用计数器的开销不开,但这项开销在整个程序的生命周期中持续发生。虽然引用计数法听起来比较简单,但是有个很大的缺陷,当对象之间存在循环引用的时候,定位这样的对象组所需的工作量极大。值得一提的是,虽然引用计数法简单,但是java的垃圾回收器从来没有使用过这种垃圾收集算法。

  • 可达性分析

        对任何”活“的对象,一定能追溯到存活在堆栈或静态存储区的引用。由此,如果从静态存储区和堆栈开始遍历所有的引用,就能找到所有存活的对象。对于发现的每个活的对象,必须找到它所引用的对象,然后是此对象包含的所有引用,如此反复,直到”源于静态存储区和堆栈的引用“所形成的网络全部被访问到为止。

  • 停止-复制

       在可达性分析的方式下,java虚拟机将采用一种停止-复制的做法。先暂停程序的运行,然后将所有存活的对象从当前堆复制到另一个堆,剩下的则都是可回收的垃圾。当对象被复制到新堆时,他们是紧挨着的,新堆保持紧凑排列,这样剩下的空间就可以直接分配了。

       当对象从一处移到另一处时,所有指向它的引用必须修正。

       这种所谓的复制式的垃圾回收器其实效率很低,首先他需要两个堆,然后要在这两个堆之间来回倒腾,从而使得维护堆的时间多出一倍,第二是程序进入稳定状态后可能只会产生很少量的垃圾,但是这种回收器还是会把所有的内存都从一处复制到另一处,这很浪费。

  • 标记-清除

       为了避免停止-复制的方法产生的浪费现象,有一种新的方式就产生了,那就是标记-清除,早期的java虚拟机采用了这种技术。标记-清除对于只产生少量垃圾或者没有产生垃圾的情况时,速度很快。它的思路同样是从静态存储区和堆栈出发,遍历所有的引用,找出存活的对象,每找到一个存活的对象,就会给对象一个标记,这过程中不会回收任何对象,当所有的标记动作全部完成之后,才开始回收动作,在清理的时候,没有标记的对象会被释放,不会有任何复制的动作,但是这个方法也有一个弊端,那就是当回收结束之后剩下的空间都是不连续的,会产生很多的内存碎片,不利于新内存空间的分配。

  • 自适应垃圾回收

java虚拟机会根据自身情况灵活的切换上述不同的处理方式

5.成员初始化

java尽力保证所有变量在使用前都得到恰当的初始化,但对于方法局部变量的初始化,在程序员程序员自己初始化,如果未初始化,编译时会报出尚未初始化的错误。对于类的成员变量,java会给它们一个默认的初始值:

boolean  false
char     []
int      0
byte     0
short    0
long     0
float    0.0
double   0.0
引用对象  null

6.构造器初始化

可以用构造器进行初始化。在运行时刻,可以调用方法或执行某些动作来确定初值,但是这也无法阻止自动初始化的进行。

6.1 静态数据初始化

静态数据属于类对象,无论创建多少对象,静态数据只会占用一份存储区域。static关键字不能用于局部变量,因为局部变量不属于类而属于对象,如果一个成员变量是静态的,而且没有进行初始化,那么基本类型的就会获得基本类型的默认初始值,而引用类型的就会获得null的默认初始化值。多说一句,单例设计模式就用到了静态数据初始化

public class Singleton {  
    private static Singleton instance = new Singleton();  
    private Singleton (){}  
    public static Singleton getInstance() {  
    return instance;  
    }  
}

数据初始化的顺序是先静态对象,后非静态对象,那么一个类中要是包括了静态代码块普通代码块构造函数这些的话,初始化顺序回事怎么样的呢

public class LifeCycle {
    // 静态属性
    private static String staticField = getStaticField();
    // 静态方法块
    static {
        System.out.println(staticField);
        System.out.println("静态方法块初始化");
        System.out.println("Static Patch Initial");
    }
    // 普通属性
    private String field = getField();
    // 普通方法块
    {
        System.out.println(field);
        System.out.println("普通方法块初始化");
        System.out.println("Field Patch Initial");
    }
    // 构造函数
    public LifeCycle() {
        System.out.println("构造函数初始化");
        System.out.println("Structure Initial ");
    }

    public static String getStaticField() {
        String statiFiled = "Static Field Initial";
        System.out.println("静态属性初始化");
        return statiFiled;
    }

    public static String getField() {
        String filed = "Field Initial";
        System.out.println("普通属性初始化");
        return filed;
    }   
    // 主函数
    public static void main(String[] argc) {
        new LifeCycle();
    }
}

执行结果为:

静态属性初始化
Static Field Initial
静态方法块初始化
Static Patch Initial
普通属性初始化
Field Initial
普通方法块初始化
Field Patch Initial
构造函数初始化
Structure Initial 

 由此可见,普通类的初始化顺序是

静态变量
静态代码块
普通变量
普通代码块
构造函数

那么含有继承关系和接口之后呢

继承的子类:

父类静态变量
父类静态代码块
子类静态变量
子类静态代码块
父类普通变量
父类普通代码块
父类构造函数
子类普通变量
子类普通代码块
子类构造函数

那么抽象的实现子类呢

抽象的实现子类: 接口 - 抽线类 - 实现类

接口静态变量
抽象类静态变量
抽象类静态代码块
实现类静态变量
实习类静态代码块
抽象类普通变量
抽象类普通代码块
抽象类构造函数
实现类普通变量
实现类普通代码块
实现类构造函数

7.可变参数列表

java的可变参数列表是的从jdk1.5开始新增的,其英文名词varargs。

可以传未知个数的参数,无需手动将参数列表装入数组。

语法:

//可变参数列表的varname被编译后其实是一个数组
anymethod(typename ... varname){}

一个简单例子:

public class Test {
    public static void varArgs(int ... numbers){
        //numbers其实是数组,它有length属性,numbers.length可以获取它的参数个数;可以用下标访问值,比如number[0],当然这是要number.lenth>0时候才可以访问的。
        for(int s: numbers){
            System.out.print(s+",");
        }
        System.out.println();
    }
    public static void main(String arg[]){
      varArgs();
      varArgs(1,2);
      varArgs(1,2,3,4,5,6);
    }
}

8.数组的初始化

要定义一个数组,只要在类型后面加上[]即可,编译器不允许指定数组的大小。当数组定义的时候,其实只是定义了数组的一个引用,那么为了给数组创建相应的存储空间,就必须写初始化表达式,这种初始化表达式是由一对花括号加一些值组成的,就像这样

int[] a1={1,2,3,4,5,6};

如果在初始化的时候不知道数组里要多少个元素该怎么办呢,可以使用new来创建元素

Random rand=new Random(30);
int[] a=new int[rand.nextInt(20)];

   如果创建了引用类型的数组,例如整形类型的包装类Integer

Random rand=new Random(30);
Integer[] a=new Integer[rand.nextInt(20)];

这里的a数组创建后,其实是一个引用数组,直到创建新的Integer对象,并把对象赋值给引用,才算初始化完成,如果忘记创建对象,并试图使用数组中的空引用,那么就会报错。

a[i]=rand.nextInt(16);

总结

通过本章的学习,我们了解到了初始化和构造器在java中的重要地位,构造器能确保数据的正确初始化和清理,同时我们也通过学习垃圾清理了解到了几种垃圾回收的机制,知道了java的垃圾回收器会自动回收内存,还通过构造器初始化静态数据初始化了解了数据初始化的顺序,还了解了可变参数列表,到此,本章就结束了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值