初始化和清理

1、区别方法重载和重写:

重载:方法名称一致,通过参数列表区别不同的方法; 发生于本类或者父类、子类;

重写:方法返回值,方法名, 参数列表必须一致;发生于父类、子类

方法重载时调用的类型转换:

package com.zifuchuan;

public class MyTe8 {

    public static void main(String[] args) {
        byte a=6;
        short b=7;
        int c=7;
        long d=7;
        float e=1.1f;
        double f=1.2d;
        test(a);
        test(b);
        test(c);
        test(d);
        test(e);
        test(f);

        test(1.3);
        test('g');//字符类型被提升为int类型
        test(89);//常数89被当成int类型

        int h=10;
        test2(h);
    }

    public static void test(byte arg) {
        System.out.println("test(byte arg)");
    }

    public static void test(short arg) {
        System.out.println("test(short arg)");
    }

    public static void test(int arg) {
        System.out.println("test(int arg)");
    }

    public static void test(long arg) {
        System.out.println("test1(long arg)");
    }

    public static void test(float arg) {
        System.out.println("test(float arg)");
    }

    public static void test(double arg) {
        System.out.println("test(double arg)");
    }

    public static void test2(long arg) {
        System.out.println("test2(long arg)");
    }

    public static void test2(double arg) {
        System.out.println("test2(double arg)");
    }
}

可知,类型转化规则:

  • 传入参数的数据类型(实际参数类型)小于方法中申明的参数类型,实际数据类型会被提升,其中字符类型比较特殊,会被提升为int类型 ;
  • 传入参数的数据类型(实际参数类型)大于方法中申明的参数类型,会报错,需要强制类型转化;
  • 常数89被当成int类型;

2、对象创建,初始化过程

  • 首次调用Dog类的静态方法(构造器也是静态方法)/静态域首次被访问时候,java解析器查找类路径定位Dog.class文件。载入Dog.class文件(这将创建一个Class对象),静态初始化相关的动作都会执行(静态域、静态代码块按照程序中的顺序依次初始化),且在Class对象首次加载的时候执行一次。如果有父类,则先找到父类的class文件并载入,再加载子类。以此类推,最终以从基类一直到导出类的顺序,分别载入class文件和当前类的静态初始化,
  • 当调用构造器创建Dog对象时候,先在堆上为Dog对象分配存储空间,并清零(也就将对象的域设置成了默认值,比如引用置为null)。如果有基类,继续为基类的对象分配存储空间并清零(导出类的构造器默认调用基类的构造器)。以此类推,最终从导出类到基类的顺序分配空间并清零。最顶层的基类对象分配存储空间并清零后,会执行本类域处的初始化操作,然后执行本类构造器。
  • 基类构造器完成后,导出类依次执行所有在字段处和非静态代码块的初始化动作。以此类推,最终从基类到导出类的顺序初始化。
  • 执行导出类构造器其余部分。

示例:

package com.zifuchuan;

public class Beetle extends Insect{

    Beetle(){
        System.out.println("Beetle构造器初始化");
    }
static { System.out.println("Beetle静态代码块1"); } private static int x2=print("x2"); static { System.out.println("Beetle静态代码块2"); } public static void main(String[] args) { System.out.println("main函数执行"); Beetle beetle=new Beetle(); } } class Insect{ { System.out.println("Insect非静态代码块1"); } private int first=print2("Insect非静态域"); Insect(){ System.out.println("Insect构造器初始化"); } private static int x=print("x"); { System.out.println("Insect非静态代码块2"); } public int print2(String str){ System.out.println(str); return 47; } public static int print(String str){ System.out.println(str); return 47; } }

输出结果:

x
Beetle静态代码块1
x2
Beetle静态代码块2
main函数执行
Insect非静态代码块1
Insect非静态域
Insect非静态代码块2
Insect构造器初始化
Beetle构造器初始化

3、垃圾回收器如何工作

  • 引用计数–java虚拟机从未采用的垃圾回收技术

  引用计数的思想?

  每一个对象都含有一个引用计数器,当有新的引用连接至对象时候,引用计数加1;引用置空时候,引用计数减1。垃圾回收器在包含所有对象的列表上遍历,发现某个对象的引用计数器为0时,就是释放对象占用的资源。

缺陷:存在循环引用,可能出现本来应该被回收的对象,由于引用计数不为0无法被回收的情况

  如何解决“引用计数”的缺陷?

  对于一切存活的对象,一定能够最终追溯到其存活在堆栈或静态存储区中的引用,这个引用可能会穿过数个对象层次。基于此前提,从堆栈或静态存储区开始遍历所有的引用就能找到存活的对象。这解决了“引用计数”的缺陷。以下“停止-复制”、”标记-清扫”基于该思想。

  • 停止-复制

  先暂停程序,然后将所有存活的对象从当前堆复制到另一个堆,没有被复制的是垃圾,被回收;对象被复制到新堆后是整齐排列的。

缺陷:

  需要两个分离的堆来回复制;如果产生少量垃圾(不存活对象很少),甚至没有垃圾,垃圾回收器还是会将所有内存从一处复制另一处的堆,很浪费。

  • 标记-清扫

  不暂停程序,找到存活对象设置一个标记,当全部标记工作完成时候,暂停程序并清理未被标记的垃圾;剩下的堆空间不是整齐排列的。解决了“停止-复制”的缺陷。

JVM虚拟机会跟踪“停止-复制”的效果,如果对象很稳定,垃圾回收效率低,会切换到“标记-清扫”方式;JVM虚拟机会跟踪“标记-清扫”的效果,如果堆空间出现很多碎片,会切换回“停止-复制”的方式。这种方式工作方式通常称为“自适应”技术