基于《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. java一个大接口拆用多线程方式拆分成多个小接口

    问题引入 目的:我们的接口A  分别调用了a1 a2 a3 三个接口,最终返回值是 a1的返回值+a2的返回值+a3的返回值 如果同步执行 a1 a2 a3 然后结果相加 很慢 . 如果异步执行 无法 ...

  2. Haskell学习-functor

    原文地址:Haskell学习-functor 什么是Functor functor 就是可以执行map操作的对象,functor就像是附加了语义的表达式,可以用盒子进行比喻.functor 的定义可以 ...

  3. Asp.NetCore轻松学-部署到 IIS 进行托管

    前言 经过一段时间的学习,终于来到了部署服务这个环节,.NetCore 的部署方式非常的灵活多样,但是其万变不离其宗,所有的 Asp.NetCore 程序都基于端口的侦听,在部署的时候仅需要配置侦听地 ...

  4. 安全研究 | Jenkins 任意文件读取漏洞分析

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由云鼎实验室 发表于云+社区专栏 一.漏洞背景 漏洞编号:CVE-2018-1999002 漏洞等级:高危 Jenkins 7 月 18 ...

  5. JDBC mysql 相关内容笔记

    解决乱码: url字符串加上?useUnicode=true&characterEncoding=utf-8; mysql数据库无法插入中文数据问题:将mysql数据库的编码改为utf-8; ...

  6. SharePoint布局页引用(实战)

    分享人:广州华软 极简 一. 前言 SharePoint 布局页可使用在任何可引用页面布局的页面,学会在页面直接引用页面布局,可实现无代码形式修改页面.此文讲述2种常用使用页面布局方式.本文适用于初学 ...

  7. ArcGIS消除图斑重叠错误

    在生产中,经常会遇见有图斑重叠这种拓扑错误的矢量,大部分情况下,需要人工比对影像处理.但是如果只需要用到这些矢量的形状.面积,可以在ArcMap中用以下方法,快速消除图斑重叠错误,不必手工处理. 如下 ...

  8. Soot生成代码控制流图

    Soot可以对代码进行分析,提供了多种字节码分析和变换功能,通过它可以进行过程内和过程间的分析优化,以及程序流图的生成,还能通过图形化的方式输出. http://www.brics.dk/SootGu ...

  9. sql 语句 获取某张表某列字段最短的某几行数据

    sql 语句 获取某张表某列字段最短的某几行数据 SELECT C_name,C_code FROM Catalog where LEN(C_code)=LEN((SELECT top 1 C_cod ...

  10. redis缓存清除

    1.redis根目录调出命令行(cmd) 2.登录redis:redis-cli -h 127.0.0.1 -p 6379 3.查看所有key值:keys * 4.删除指定索引的值:del key 5 ...