初始化和清理

  在Java中,通过提供构造器,类的设计者可以确保每个对象都会得到初始化。

  构造器的命名问题:所取的任何名字都可能与类的某个成员名称冲突;调用构造器时编译器的责任,编译器应知道该调用哪些方法。Java采取了构造器与类名相同的方法。

  不接受任何参数的构造器叫默认构造器,也称无参构造器。在Java中,初始化和创建是捆绑在一起的。构造器时一种特殊的方法,它没有返回值,它可以被重载。

  方法被重载的过程中,若传入的数据类型小于方法中声明的参数类型,实际数据类型会被提升。若无法接受char参数,会自动把char直接提升到int。若传入的实际参数大于重载方法声明的形式类型,则需要强制转换,否则编译器会报错。

  默认构造器(无参数类型)是没有形式参数的,它的作用时创建一个默认对象。若在类中没有写任何的构造器,编译器会自动帮你创建一个,但若是自己写了一个构造器,编译器将不再创建无参构造器。此时,若没有创建无参构造器,但创建了一个无参对象时,编译器会报错。

  this关键字职能在方法内部使用,表示对调用方法的那个对象的引用。当需要返回当前对象的引用时,如在return中,可以使用return this;this对于当前对象传递给其他方法也很有用。

Demo

  public class Leaf{

    int i = 0;

    Leaf increment{

      i ++;

      return this;

    }

    void print(){

      System.out.print("i="+i);

    }

    public static void main(String args[]){

      Leaf f = new Leaf();

      f.increment().increment().increment().print();  //output: i=3

    }

  } 

  

  class Person{

    public void eat(Apple apple){

      Apple peeled = apple.getPeeled();

      System.out.println("Yummy");

    }

  }

  class Peeler{

    static Apple peel(Apple apple){

      return apple;

    }

  }  

  class Apple{

    Apple getPeeled(){

      return Peeler.peel(this);

    }

  }

  public class PassingThis{

    public static void main(String argsp[]){

      new Person().eat(new Apple);

    }  

  }

  在构造器中调用另一个构造器需使用this。若为this添加参数列表,这将产生对符合参数列表的某个构造器产生明确调用。但不可在一个构造器内调用两个构造器。只能调用一个构造器并且必须将构造器置于最起始处,否则报错。除构造器外,编译器禁止在任何方法中调用构造器。

  static方法就是没有this的方法。在static方法的内部不能调用飞静态方法。static方法可以在没有创建对象的情况下通过类本身调用,类似于全局变量。

  假定你的对象获得一块特殊的内存区域(并非使用new),由于垃圾回收机器只知道释放那些由new分配的内存,所以它不知道该如何释放块对象的内存。为了应对这种情况,Java允许在类中定义一个finalize()方法。一旦垃圾回收机器准备释放对象占用的存储空间,将首先调用其finalize()方法。并且在下一次垃圾回收动作发生时,才真正回收对象占用的空间。在C++中,对象一定会被销毁。而在Java中,对象并非总是被垃圾回收,即垃圾回收并不等于析构。

  finalize()方法主要用于通过某种创建对象方式以外的方法为对象分配存储空间,这种情况下可能是在内存分配时采用了类似C语言的做法。即在Java踪调用飞Java代码(本地方法)。本地方法目前只支持C和C++。如在C中调用了malloc()函数来分配存储空间,此时需要在finalize()中调用free()来释放空间。

  若Java虚拟机(JVM)并未面临内存耗尽的情形,它是不会浪费时间去执行垃圾回收以恢复内存的。  

  System.gc()用于强制进行终结动作。

  引用计数时一种简单但速度很慢的垃圾回收技术。每个对象都含有一个引用计数器,当有引用连接到对象时,引用计数加1.当引用离开作用于或被置为null时,引用计数减1.垃圾回收器会在含有全部对象的列表上遍历,当发现某个对象的引用计数为0时,就释放其占用的空间。这种方法有一个缺陷:若对象之间存在循环引用,可能会出现对象应被回收,但计数器不为0的情况。引用计数常用来说明垃圾回收的工作方式,但从未被应用到任何一种JVM实现中。

  垃圾回收机制的另一种思想:对任何活的对象,一定能追溯到其活在堆栈或静态存储区之中的引用。这个引用链条可能会超过数个对象层次,若从堆栈和静态存储区开始,遍历所有的引用,就能找到所有活的对象,如此反复进行,直到“根源于堆栈和静态存储区的引用”形成了网络全部被访问为止。

  JVM采用了一种自适应的垃圾回收技术。JVM采用了一种“停止——复制(stop and copy)”来找到活的的对象,停止——复制的做法是:先暂停程序的运行,然后将所有的活的对象从当前堆复制到另一个堆,没有被复制的全部都是垃圾。当对象被复制到新堆时,他们是一个挨一个的,所以新堆保持紧凑排列,然后就可以按照“传送带”(堆指针向前移动,分配内存空间)方式直接分配新空间。这种复制式回收器效率会降低。有两个原因:首先要复制需要两个堆,从而得维护实际需要多一倍的空间。JVM处理这个问题的方式是按需从堆中分配几块比较大的内存,复制动作发生在这些大块内存之间。第二个问题在于复制。程序进入稳定状态后,可能只有少量垃圾,甚至没有垃圾,但复制式回收器仍会将所有内存自一处复制到另一处,JVM为避免这一情况,会进行检查,若没有新垃圾产生,会转到另一种工作模式(自适应)。这种模式称为“标记-清扫(mark and sweep)”。这种方式速度相当慢,但若垃圾少甚至没有垃圾时就会快很多。“标记-清扫”的思路是从堆栈和静态存储区域出发,遍历所有的引用,进而找出所有存活的对象,每当找到一个存活的对象时,就会给对象设计一个标记,这个过程不会回收任何对象,只有全部标记工作完成的时候,清理动作才会开始,在清理的过程中,没有标记的对象将会被释放,不会发生任何复制动作,所以剩下的堆空间时不连续的。垃圾回收器要是希望得到连续空间的话,就得重新整理剩下的对象。JVM将内存分以较大的块位单位,每个块都用相应的代数(generarton count)来记录他是否还存活。若块在某处被引用,其代数会增加。JVM会进行监视,若所有对象都很稳定,垃圾回收器的效率降低的话会切换到“标记——扫描”方式。同时,JVM会跟踪“标记——扫清”效果,若堆空间出现很多碎片,会切换到“停止——复制”方式。

  即时编译器(Just In Time,JIT)可以把程序全部或部分编译成本地机器码。当需要装载某个类时,编译器会找到其.class文件,然后将该类的字节码装入内存。此时,有两种方案可选:一种是让JIM编译所有代码,这种做法有两个缺陷:花更多的时间和增加可执行代码的长度。导致页面调用从而降低程序速度。另一种做法称为惰性评估(lazy evaluation),即只在必要时才编译代码。

  初始化顺序:变量定义的先后顺序决定了初始化的顺序,即使遍历定义散步在方法定义之间,它们仍旧会在任何方法被调用之前得到初始化。初始化的顺序是先是静态对象,后是非静态对象。

  static不能应用于局部变量,若一个域是静态的基本类型域,并未对它进行初始化,那么它就会获得基本类型的标准初值。若它是一个对象引用,他的默认值为null。

  对象的创建过程如下:假设有一个Dog类

即使没有显式的使用static关键字,构造器实际上也是静态方法。因此,当首次创建Dog对象时,Dog类的静态方法/域首次被访问时,Java解释器必须查找类路径,以定位Dog.class文件

然后再入Dog.class文件,有关静态初始化的所有动作都会被执行

当使用new创建对象时,首先在堆上为Dog分配足够的空间

这块存储空间会被清零。即将Dog对象中的所有的基本类型都设置成了默认值,而引用被设置成null

执行所有出现于字段定义处的初始化动作

执行构造器

  Java允许将多个静态初始化动作组成一个特殊的静态子句,也称静态块

  static{

    int i=5;

    String str = "a";

  }

  编译器不允许指定数组的大小,只提供了数组的引用,也没数组对象本身分配任何空间

  可变参数列表可以创建以Object数组为参数的方法。

  getClass()方法属于Object的一部分,它将产生对象的类,并且打印该类时可以看到表示该类型的编码字符串

  可变参数列表与自动包装机制可以和谐共处,可以在单一的参数列表中将类型混合在一起,而自动包装机制将有选择的将int参数提升为Integer

  当创建enum时,编译器会自动添加一些特性 ,会创建toString()方法,还会创建ordinary()方法用来表示某个特定enum常量的声明顺序,以及static values()方法用来按照enum常量声明顺序产生由这些常量值构成的数组

  

原文地址:https://www.cnblogs.com/forerver-elf/p/4897462.html