(本文章属于个人学习笔记,总结归纳,如有错误请指出)
构造器:
构造器通常也叫构造方法,创建对象时被自动调用的特殊方法,构造器名(方法名)必须与类名相同,可以有参数,没有返回值。
构造器中不允许有返回值。构造器中允许存在return语句,但是return什么都不返回,如果你指定了返回值,虽然编译器不会报出任何错误,但是JVM会认为他是一个与构造器同名的函数罢了,这样就会出现一些莫名其妙的无法找到构造器的错误,这里是要加倍注意的。
当我们新建一个类的时候,没有任何构造器,但在编译的时候,编译器会默认提供一个无参构造器(.class文件中),这就是为什么没定义任何构造器,却可以new 某个对象。
public class Ray {
/*public Ray() {
}*/
}
当然我们也可以手动定义构造器,在里面加入代码or参数,对对象进行初始化。
这里要注意的是如果我们如果我们手动定义了构造器,编译时将不会自动提供构造器。
构造器的继承
子类构造器会默认调用父类无参构造器,如果父类没有无参构造器,则必须在子类构造器的第一行通过 super关键字指定调用父类的哪个构造器。
public class Ray extends SuperRay{
public Ray() {
//super(); 默认调用
}
}
构造器、静态代码块、构造代码块的执行顺序
无继承的情况下的执行顺序
静态代码块:只在程序启动后执行一次,优先级最高
构造代码块:任何一个构造器被调用的时候,都会先执行构造代码块,优先级低于静态代码块
构造器:优先级低于构造代码块
总结一下优先级:静态代码块 > 构造代码块 > 构造器
有继承的情况下的执行顺序:
父类静态代码块:只在程序启动后执行一次,优先级最高
子类静态代码块:只在程序启动后执行一次,优先级低于父类静态代码块
父类构造代码块:父类任何一个构造器被调用的时候,都会执行一次,优先级低于子类静态代码块
父类构造器:优先级低于父类构造代码
子类构造代码块:子类任何一个构造器被调用的时候,都会执行一次,优先级低于父类构造器
子类构造器:优先级低于子类构造代码块
总结一下优先级:父类静态代码块 > 子类静态代码块 > 父类构造代码块 > 父类构造器 > 子类构造代码块 > 子类构造器
然后我又想到了一个比较特殊的情况:在子类的构造器有继承的情况下调用自己的构造器(this()),那么父类的构造器应该在哪调用(super())?
子类代码
package com.ray.oop.demo06;
public class Ray extends SuperRay {
static {
System.out.println("子类的静态代码块,程序启动后执行,只会执行一次,先执行父类的,再执行子类的");
}
{
System.out.println("子类构造代码块,每次调用构造方法都会执行的");
}
//子类无参构造
public Ray(){
super(); //调用父类无参构造器
System.out.println("子类无参构造器运行");
}
//子类有参构造,两个参数
public Ray(String str){
this(); //调用子类无参构造器 ↑
System.out.println("子类带参构造器:" + str);
}
}
父类代码
package com.ray.oop.demo06;
public class SuperRay {
static {
System.out.println("父类的静态代码块,程序启动后执行,只会执行一次");
}
{
System.out.println("父类构造代码块,每次调用构造方法都会执行的");
}
//父类无参构造
public SuperRay(){
System.out.println("父类无参构造运行");
}
//父类有参构造
public SuperRay(String str){
System.out.println("父类的带参构造器:" + str);
}
}
实例化输出
public static void main(String[] args) {
Ray subClass2 = new Ray("子类参数1"); //调用子类有参构造
}
输出结果
父类的静态代码块,程序启动后执行,只会执行一次
子类的静态代码块,程序启动后执行,只会执行一次,先执行父类的,再执行子类的
父类构造代码块,每次调用构造方法都会执行的
父类无参构造运行
子类构造代码块,每次调用构造方法都会执行的
子类无参构造器运行
子类带参构造器:子类参数1
可以看到,构造器的执行顺序是:父类构造器>子类无参构造器>子类有参构造器。
我们把子类中调用自己构造器和调用父类构造器的方法调换一下位置,再看输出结果
package com.ray.oop.demo06;
public class Ray extends SuperRay {
static {
System.out.println("子类的静态代码块,程序启动后执行,只会执行一次,先执行父类的,再执行子类的");
}
{
System.out.println("子类构造代码块,每次调用构造方法都会执行的");
}
//子类无参构造
public Ray(){
this("子类参数1"); //调用子类本身的有参构造器
System.out.println("子类无参构造器运行");
}
//子类有参构造,两个参数
public Ray(String str){
super(); //调用父类的无参构造器
System.out.println("子类带参构造器:" + str);
}
}
实例化输出
public static void main(String[] args) {
Ray subClass2 = new Ray(); //调用子类无参构造
}
输出结果(第二次)
父类的静态代码块,程序启动后执行,只会执行一次
子类的静态代码块,程序启动后执行,只会执行一次,先执行父类的,再执行子类的
父类构造代码块,每次调用构造方法都会执行的
父类无参构造运行
子类构造代码块,每次调用构造方法都会执行的
子类带参构造器:子类参数1
子类无参构造器运行
输出结果(第一次)
父类的静态代码块,程序启动后执行,只会执行一次
子类的静态代码块,程序启动后执行,只会执行一次,先执行父类的,再执行子类的
父类构造代码块,每次调用构造方法都会执行的
父类无参构造运行
子类构造代码块,每次调用构造方法都会执行的
子类无参构造器运行
子类带参构造器:子类参数1
通过我们比较两次的输出结果,可以发现子类构造器的执行顺序发生了改变。
第一次构造器执行顺序是:父类构造器>子类无参构造器>子类有参构造器。
第二次构造器执行顺序是:父类构造器>子类有参构造器>子类无参构造器。
我们可以发现构造器是由内向外依次执行的。