《Java开发手册》学习进程之第10章构造器应用

构造器与返回类型:

构造器没有返回类型,若将返回值类型(包括void)添加到构造器上,编译不会报错,但此时不再是构造器了,而只是一个与所在类同名的方法而已。

构造器与方法是两个不同的概念:

  • 构造器不能像方法一样能被对象引用调用。
  • 构造器是创建对象时需执行的代码,由new调用。方法是类或对象具有的行为,由引用调用。

无参数的构造器(资料来源:《Java核心技术卷1(中文第9版)》127页)

如果在编写一个类时没有编写构造器,那么系统就会提供一个无参数的构造器。这个构造器将所有实例域设置为默认值(如果实例域在类中没有被初始化)。

如果类中至少提供了一个构造器,但是没有提供无参数的构造器,则在构造对象时如果没有提供参数就会被视为不合法。

有关构造器与继承关系的问题(构造器的级联调用):

class Animal
{
    public Animal()
    {
        System.out.println("我是Animal的构造器!");
    }
}

class Bird extends Animal
{
    public Bird()
    {
        System.out.println("我是Bird的构造器!");
    }
}
public class Sample10_6
{

    public static void main(String[] args)
    {
        new Bird();

    }

}

  • 执行new Bird()语句,进入Bird构造器体。
  • 调用Animal的构造器,进入Animal构造器体,因为Animal是Bird的父类。
  • 调用Object的构造器,进入Object构造器体,因为Object是Animal的父类。
  • 执行Object构造器中的代码直到完成,返回Animal构造器。
  • 执行Animal构造器中的代码直到完成,返回Bird构造器。
  • 执行Bird构造器中的代码直到完成。

如果没有在构造器中明确调用指定的父类或兄弟构造器,编译器会自动将调用父类无参数构造器的代码添加为构造器代码的第一句。

由上可知,由于级联调用,自己编写的调用父类构造器的代码必须位于构造器的第一句,否则编译报错。即在第一句里写上super然后加上一对圆括号,圆括号中放上对应父类构造器需要的参数。所以,如果开发人员需要自己编写,那么编译器就不会再自动添加调用父类(无参数)构造器的代码。

在实际开发中,若不在构造器中编写调用父类构造器的代码,系统只能自动调用父类的无参数构造器;若需要明确调用父类中某个构造器,则必须自己编写代码。

构造器被设计为级联调用的原因:

根据替代性原理,子类对象必须能够替代父类对象,因此在构造子类对象之前,要首先构造一个父类对象,然后在父类对象的基础上进一步构造子类对象。Java的子类对象中都隐含着一个父类对象,子类对象是在父类对象的基础上进一步雕琢完成的。

调用兄弟构造器:

一旦使用了“this(XX)”调用兄弟构造器,在该构造器中编译器将不再自动添加“super();”,即在该构造器中系统将不再自动调用父类的无参数构造器。因为用this(XX)执行到调用的构造器时,会执行构造器中编译器自动为其添加的“super();”或开发人员自己编写的super语句。

单列模式:

class Singleton
{
    private static Singleton singleInstance;
    public static Singleton getInstance()
    {
        if (singleInstance == null)
        {
            singleInstance = new Singleton();
        }
        return singleInstance;
    }
    private Singleton()
    {
        System.out.println("执行单列模式类的构造器!!");
    }
}
public class Sample10_15
{

    public static void main(String[] args)
    {
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();

        if (s1 == s2)
        {
            System.out.println("两个引用指向同一个对象!");
        }
        else
        {
            System.out.println("两个引用指向不同的对象!");
        }

    }

}

输出的结果为:
执行单列模式类的构造器!!
两个引用指向同一个对象!

虽然获取了两次对象引用,但构造器只执行了一次,说明只创建了一个对象。

两次获取的引用相等,也说明了只有一个对象。

程序的加载:

在Java中,当创建一个对象时总的加载过程如下

  • 首先加载要创建对象的类及其直接父类与间接父类。
  • 在类被加载的同时会加载静态成员,主要包括静态成员变量的初始化、静态语句块的执行,在加载时按代码的先后顺序执行。
  • 需要的类加载完成后,开始创建对象。首先会加载非静态的成员,主要包括非静态成员变量的初始化、非静态成员块的执行,在加载时按代码的先后顺序执行。
  • 最后执行构造器。构造器执行完成,则对象生成。

上面那一段话是《Java开发手册》书上的总结。

而我的总结是:

  • 首先加载要创建对象的类及其直接父类与间接父类。
  • 在加载类这一步中,先加载对象类的“顶层”类,然后再由“顶层”类加载到下一层的类,……,直到加载到对象的类为止。
  • 在上一步中,加载每一个类时,先加载静态成员,这一点和书上的总结一样。
  • 需要的类加载完成后,开始创建对象。
  • 在创建对象这一步中,先创建“顶层”类的对象,然后再创建下一层类的对象,……,直到所有的对象创建完为止。
  • 在上一步中,创建每一个对象时,先加载非静态的成员,然后执行该类的构造器后,才算是创建完了对象。

 

 

时间: 2024-12-31 03:27:54

《Java开发手册》学习进程之第10章构造器应用的相关文章

【转】线程池体系介绍及从阿里Java开发手册学习线程池的正确创建方法

jdk1.7中java.util.concurrent.Executor线程池体系介绍 java.util.concurrent.Executor : 负责线程的使用与调度的根接口  |–ExecutorService:Executor的子接口,线程池的主要接口  |–ThreadPoolExecutor:ExecutorService的实现类  |–ScheduledExecutorService:ExecutorService的子接口,负责线程的调度  |–ScheduledThreadPo

从阿里Java开发手册学习线程池的正确创建方法

前言 最近看阿里的 Java开发手册,上面有线程池的一个建议: [强制]线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险. 结合最近面试的经历,发现这条建议还是十分有用的,因为自己经常使用Executors提供的工厂方法创建线程池,所以忽略了线程池内部的实现.特别是拒绝策略,面试被问到两次,因为使用Executors创建线程池不会传入这个参数而使用默认值所以我们常常忽略这一参

《Java开发手册》学习进程之第16章多线程

多线程编程可以使程序具有两条或两条以上的并发执行线索,就像日常工作中由多人同时合作完成一个任务一样.这在很多情况下可以改善程序的响应性能,提高资源的利用效率. 例如,开发一个网上购物时自动邮件通知的程序,在用户单击"提交"按钮确认订单时,一方面要显示信息提示用户订单已确认,一方面应该自动给用户发送一份电子邮件. 如果是单线程模式,则需要等待邮件发送完成之后,再显示提示信息,由于邮件发送的过程相对较慢,用户可能要经过漫长的等待才看到确认信息,界面响应性能不是很好. 而显示订单确认信息与发

《Java开发手册》学习进程之第6章对象和类

传统的过程化程序设计通过设计一系列的过程——算法来求解问题.这些过程一旦被确定,下一步就要开始寻找存储数据的方式,即“程序 = 算法 + 数据结构”.而面向对象的程序设计(即OOP,Object Oriented Programming)调换了这个次序,将数据放在第一位,之后再考虑操纵数据的算法. 在OOP中,程序被看作是相互协作的对象集合,每个对象都是某个类的实例.所有类构成了一个通过继承关系相联系的层次结构. 由于面向过程的编程模式是先将一系列过程(即函数)设计好再通过参数输入求解的,所以对

《Java开发手册》学习进程之第7章访问控制符

类的访问控制符: 虽然访问控制符有四种,但针对外部类来说,只需要公共的和默认的就够了. public表示任何类都可以使用该类. 当一个类被定义为public的时候,它就可以在任何类里被访问了. 成员的访问控制: 成员(即成员变量和方法)能被其他类访问主要取决于两个方面因素: 成员变量和方法所在类的可见性是其能被正常访问的前提条件: 其次是成员变量和方法的访问控制符直接决定了是否可以被访问. Static关键字:  Java中没有全局变量的概念. 若static修饰成员变量,当生成类的对象时,该类

《Java开发手册》学习进程之第2章基本数据类型

2.1  数据类型 Java表示整数常量时,默认情况下为int型:若整数常量前面有个0,则表示八进制数:若整数常量前面有个‘0x’或’0X’,则表示十六进制. long型常量需在后面加个’l’或’L’.用System.out.println()方法输出时,其后缀名并不会输出. 如果某个浮点值没有使用float关键字(’f‘或’L‘)作为后缀名,则系统默认为double型.同long型常量一样,其后缀名也并不会输出. 注意:别把double型的常量赋值给float型的变量,把长度为8字节的doub

《Java开发手册》学习进程之第15章内部类

非静态内部类: 从非静态内部类外面看,完全可以将其看成是外部类的一个非静态成员,与普通的成员没有什么区别.只是这个成员不再是基本数据类型,也不再是对象引用,而是一个类,由一个类来扮演成员的角色. 内部类的访问限制修饰符除了外部类的public和默认外,还可以添加private和protected. 虽然内部类位于外部类的类体中,但编译后内部类与外部类各自产生一个类文件. 因为非静态内部类扮演的是外部类非静态成员的角色,而非静态成员只有在所属对象存在的情况下才存在,因此非静态内部类在创建了外部类对

《Java开发手册》学习进程之第9章接口

接口中的成员变量: 与类不同,在接口中只能定义常量属性,并且可以不必使用public.statict和final修饰,因为即使不使用这些修饰符,编译时也会为其加上,即接口中的成员变量隐含的是"public static final"的.另外,必须在声明的同时给出其值. 接口中的方法: 接口中的方法必须是抽象的,原因是接口只是代表了一个契约,表示实现它的类有什么样的功能,具体实现是由实现它的类完成的. 接口中的方法必须是public.abstract的,不能像接口中的成员变量一样使用fi

《Java开发手册》学习进程之第3章运算符详解

运算符重载是指同一个运算符在不同的情况下执行不同的操作. 例如,"+"运算符在Java中就有不同的功能: 加法运算 数值正号 字符串连接 特别要注意要在字符串连接的表达式中字符串与其他基本数据类型的变量的连接问题. 例如: (1)System.out.println(a + m + n)中,a为一String对象,m和n为int型变量(也可以是byte,short,float等其他基本数据类型).根据此表达式中的运算符优先级可知,a先与m相连接,其合并为一个字符串,然后再与n连接并以字