深入理解Java构造器
//https://me.youkuaiyun.com/zhihuiyt 未经允许谢绝转发!
Java构造器概述
构造器是一个特殊的方法,用于创建实例时执行初始化。了解C++的同学肯定知道构造函数,从功能看,与java的构造器有着异曲同工之处。在初学java语言时,我们就利用语句 Person person = new Person();对对象Person进行实例化操作。其实,这是一个利用new关键字调用构造器的典型用法(表达可能不是很专业),其过程主要包含了两步:
Person person = new Person(); |
1)系统为对象分配内存空间,并未对象执行默认初始化;(把所有基本类型的实例变量设定为0或false(对布尔实例变量),把所有引用类型的实例变量设为null) |
2)执行构造体内语句,对实例对象进行显示初始化 |
构造器的语法格式:
修饰符 className(形参列表){ 修饰符类型 :public 允许系统中任何位置的类来创建该类对象
//构造体语句 To Do ... protected 主要用于被子类调用
} private 阻值其他类创建该类的实例--->单例设计模式
需要说明的是若用户希望有多个初始化过程,则可以在该类中提供多个构造器。如果一个类中有多个构造器,就形成了构造器重载。
构造器重载---静态的多态性
构造器重载与函数重载的定义是相同的,同一个类中具有多个构造器,多个构造器的形参列表不同,即被称为构造器重载。系统会根据传入实参列表的类型与个数决定调用哪个构造器。
需要特殊说明的是,若一个类中包含多个构造器,其中一个构造器的执行体里完全包含另一个构造器的执行体。那么如何在一个构造器中直接调用另一个构造器呢?
class Person{
private String name;
private int age;
private int eyeNum;
public Person(String name,int age)
{
this.name = name;
this.age = age;
System.out.println("调用父类含有name,age构造函数");
}
public Person(String name,int age,int eyeNum)
{
//如何在一个构造器内直接调用另一个构造器?
//this(name,age);
//this.eyeNum = eyeNum;
}
}
在上文中已经讲到,通过new关键字来调用构造器,如果按照以下的方式来调用构造器会出现什么问题呢?
public Person(String name,int age,int eyeNum)
{
Person person = new Person(name,age);
person.eyeNum = eyeNum;
}
问题是显而易见的,虽然在代码块内确实完成了person对象的初始化过程,但是该对像是一个局部变量,离开代码块后内存自动释放,并不能真正的完成初始化过程。即使,将person作为全局变量,也只能完成一个person对象的初始化。那么如何在构造器B中调用构造器A中的初始化代码,又不会重新创建对象,可以使用this关键字来调用相应的构造器!
public Person(String name,int age,int eyeNum)
{
//如何在一个构造器内直接调用另一个构造器?
//利用this调用相应的构造器
this(name,age);
this.eyeNum = eyeNum;
}
继承中构造器调用顺序
面向对象的三大特性,封装、继承、多态,想必大家都很熟悉,在此关于继承的特性不在具体阐述,<大家牢记父类方法重写时“两同两小一大原则”>。
两同:函数名和形参列表相同
两小:1)子类中函数返回类型要比父类的返回类型更小或者相等
2)子类中抛出的异常类要比父类抛出的异常类更小或者相等
一大 :子类的访问权限要比父类的访问权限更大或者相等 private < default <protected <public
利用代码说明在继承中构造器的调用顺序,代码如下:
三个类的继承结构如下所示;
上图详细的介绍了在三个类中构造器的执行顺序,故main函数的输出应为:
核心要点
1、子类构造器调用父类构造器顺序。
(1)子类构造器中存在super显示调用,则会根据传入实参的列表调用父类构造器
(2)子类构造器中存在this调用,则会根据传入实参的列表调用本类的构造器
(3)若子类构造其中既不存在super也不存在this,则会调用父类的无参构造!
2、this 与super调用的区别
两者最重要的区别在于,this调用本类的构造器,super调用父类的构造器。