基于《Java编程思想》第四版

构造与析构

在C++中通过构造函数和析构函数来保证:对象在使用前被正确初始化,在使用后被正确回收。Java中同样存在构造函数,但是没有析构函数。之所以没有析构函数是因为对象实际的存储期由GC决定,程序员无法明确析构函数何时会被执行。

GC会在回收对象前执行Objectprotected void finalize()方法,子类可以通过重写finalize()方法来清理资源。但是因为GC回收对象时间的不确定性,加上finalize()本身可能引入的问题,所以并不赞成使用该方法。不过Java还提供下面的格式,保证在finally里的语句必然在函数正常返回或者抛异常返回前执行,因此可以将资源的回收放在该语句块中。

try{

}finally{

}

对象初始化

在C++中,类对象实例化时,是不会初始化对象空间的,完全依靠构造函数将成员变量设置为正确的初值,但是在Java中,当得到对象的空间后会先全部清零,再开始初始化对应成员变量。

C++初始化成员变量有两种方式

  • 初始化列表
  • 构造函数内赋值

Java则有三种方式

  • 变量定义处赋值
  • 初始化语句块:分为静态初始化语句块和非静态初始化语句块,静态初始化语句块只能包含静态成员变量,非静态初始化语句块可以包含静态成员变量和非静态成员变量
  • 构造函数内赋值

通过下面的代码来加深印象

class MyType{
    MyType(String msg){
        System.out.println(msg);
    }
}

class Base{
    MyType b1;
    static MyType b2 = new MyType("Base static member use =");
    MyType b3 = new MyType("Base non-static member use =");
    MyType b4;
    static MyType b5;
    {
        b4 = new MyType("Base non-static member in {}");
        b5 =  new MyType("Base static member in {}");
    }
    static MyType b6;
    static {
        b6 =  new MyType("Base static member in static {}");
    }
    Base(){
        b1 = new MyType("Base non-static member in Base()");
        System.out.println("Base()");
    }
}

class Derived extends Base{
    MyType d1;
    static MyType d2 = new MyType("Derived static member use =");
    MyType d3 = new MyType("Derived non-static member use =");
    MyType d4;
    static MyType d5;
    {
        d4 = new MyType("Derived non-static member in {}");
        d5 =  new MyType("Derived static member in {}");
    }
    static MyType d6;
    static {
        d6 =  new MyType("Derived static member in static {}");
    }
    Derived(){
        d1 = new MyType("Derived non-static member in Derived()");
        System.out.println("Derived()");
    }
}
public class Main {
    public static void main(String[] args) {
      Derived d1 = new Derived();
      System.out.println("----");
      Derived d2 = new Derived();
    }
}

代码的输出顺序如下,将分析也包含在输出中了

// 初始化子类时,会先初始化父类,此时先初始化静态成员变量。顺序:定义时赋值>初始化语句块。
Base static member use =
Base static member in static {}
// 父类的静态成员初始化结束后,开始初始化子类的静态成员变量。顺序:定义时赋值>初始化语句块。
Derived static member use =
Derived static member in static {}
// 以上对静态成员表初始化只在第一次加载class时执行的,后续都不会再执行了。

// 开始初始化父类的非静态成员变量。顺序:定义时赋值>初始化语句块>构造函数
Base non-static member use =
Base non-static member in {}
Base static member in {} // 此处的静态成员变量赋值会在每次类实例化对象时,执行
Base non-static member in Base()
Base()
// 开始初始化子类的非静态成员变量。顺序:定义时赋值>初始化语句块>构造函数
Derived non-static member use =
Derived non-static member in {}
Derived static member in {}
Derived non-static member in Derived()
Derived()
----
// 第二次实例化对象时,只会执行非静态成员变量的定义赋值、非静态初始化语句以及构造函数内赋值
Base non-static member use =
Base non-static member in {}
Base static member in {}
Base non-static member in Base()
Base()
Derived non-static member use =
Derived non-static member in {}
Derived static member in {}
Derived non-static member in Derived()
Derived()

总体顺序就是

  • 在首次加载class时,执行静态成变量的定义赋值和静态初始化语句块内的赋值。
  • 在每次实例化对象时,执行非静态成员变量的定义赋值、非静态初始化语句块内的赋值、构造函数内的赋值。

如果不实例化对象,而是直接访问静态成员变量,那么只会执行父类静态成变量的定义赋值和静态初始化语句块内的赋值

public class Main {
    public static void main(String[] args) {
        System.out.println( Derived.b5 );
    }
}
/// 输出如下
Base static member use =
Base static member in static {}
null

因为子类可能使用到父类的东西,所以总是先初始化父类再初始化子类。同样的初始化方法和类型(static或者non-static)的成员变量的初始化顺序与其定义顺序一致。这些和C++都是一样的。加载class的细节应该是被隐藏在Java解释器里了,通过字节码看不到完整的流程,这和C++就不一样了。

Java学习点滴——对象实例化的更多相关文章

  1. Java学习之对象实例化

    一个对象实例化过程:Person p = new Person();1,JVM会读取指定的路径下的Person.class文件,并加载进内存,并会先加载Person的父类(如果有直接的父类的情况下). ...

  2. Java学习——面对对象的思想入门

          本文是看过<head first Java>之后的一点感悟,写点东西帮忙以后回忆,Java目前在我的工作中用到还不多,而我又对面对对象的编程非常的感兴趣.曾经在MFC平台上写过 ...

  3. Java学习点滴——初识Java

    基于<Java编程思想>第四版 前言 "程序就是算法加数据结构",而算法就是控制语句加操作符,编写一个程序就是使用控制语句加操作符去操作数据结构,因此我从Java的控制 ...

  4. Java 学习:对象和类

    对象和类 从认识的角度考虑是先有对象后有类.对象,是具体的事物.类,是抽象的,是对对象的抽象. 从代码运行角度考虑是先有类后又对象.类是对象的模板. 对象:对象是类的一个实例,有状态和行为. 类:类是 ...

  5. Java学习点滴——Integer缓存

    前言 一切从下面这段代码开始 public static void test(String[] agrs){ Integer a = 1; Integer b = 2; System.out.prin ...

  6. Java学习点滴——泛型

    基于<Java编程思想>第四版 前言 虽然Java的泛型在语法上和C++相比是类似的,但在实现上两者是全然不同的. 语法 Java只需要一个<>就可定义泛型.在<> ...

  7. Java学习点滴——Class和反射

    基于<Java编程思想>第四版 前言 我们要操作一个类实例对象时,一般都要先知道这个类有哪些方法或者成员变量.反射就是在我们不知道这个类有哪些方法或成员变量时,使用特定方式得到类的这些信息 ...

  8. Java学习笔记--对象克隆

    转自:Edward_qing_Lee 的专栏 http://blog.csdn.net/edward_qing_lee/article/details/8249102 一.java 方法参数 理解: ...

  9. JAVA学习一 对象数组

    对象数组 今天在写一个代码,才发现自己对于对象数组的理解是不够的,那么就讲讲自己现在的理解. 对于数组中的每一个元素都是一个针对对象的引用 他会指向你的具体的一个堆上的对象,它本身知识一个地址值,与其 ...

随机推荐

  1. Javapoet源码解析

    Javapoet:是生成.java源文件的开源API  github:https://github.com/square/javapoet   以生成一个HelloWrold.java文件为例: pa ...

  2. C#-Xamarin的Android项目开发(二)——控件应用

    相信我,这不是一篇吐槽文章.... 基础控件 Android的控件和控件样式非常特别,它是一种内联特别高的设计模式,换句话说,它是非常烂的设计.... 但在这种特别的关系里还是有一定的规律的,下面我们 ...

  3. LaTeX 公式编辑

    推荐一篇关于LaTeX的文档:<一份不太简短的LATEX 2ε介绍> 1. 常用数学公式符号: 求期望 $\mathbb{E}$ \mathbb{E} 正负无穷 $+\infty$,$-\ ...

  4. link/Extended dependency 无法显示连接

    把矩形控件先去掉,然后就能看到表与表之间的 link/Extended dependency 连线了.

  5. 工厂模式讲解, 引入Spring IOC

    目录 引入 简单工厂 抽象工厂 Spring的bean工厂 模拟Spring工厂实现 模拟IOC 引入 假设有一个司机, 需要到某个城市, 于是我们给他一辆汽车 public class Demo { ...

  6. python之循序渐进学习装饰器

    python装饰器的定义:在代码运行期间在不改变原函数定义的基础上,动态给该函数增加功能的方式称之为装饰器(Decorator) 装饰器的优点和用途: 1. 抽离出大量函数中与函数功能本身无关的的雷同 ...

  7. docker(4)docker的网络,自定义网桥

    Docker 的网络 运行 ifconfig 找到 docker0 : 虚拟网卡默认网卡名称为docker0 查看docker 的网桥: 我这里默认们没有进行安装 网桥管理设备:进行安装一下: yum ...

  8. js防抖和节流

    今天在网上看到的,里面的内容非常多.说下我自己的理解. 所谓的防抖就是利用延时器来使你的最后一次操作执行.而节流是利用时间差的办法,每一段时间执行一次.下面是我的代码: 这段代码是右侧的小滑块跟随页面 ...

  9. 解决mysql中只能通过localhost访问不能通过ip访问的问题

    解决mysql中只能通过localhost访问不能通过ip访问的问题 原因是没开权限 SELECT * FROM USER WHERE USER='root'; grant all privilege ...

  10. Windows Server 2016-命令行批量导出AD用户列表信息

    本章节为大家带来如何通过Powershell或ldifde命令行方式导出域用户列表信息,方便大家日常运维工作中使用. Powershell方式导出现有Staff目录下所有用户信息列表: Get-ADU ...