Java中子类和父类构造方法的问题

本文深入讲解Java中的构造器概念,包括默认构造器、this和super的使用区别、构造器访问修饰符以及多层次调用父类构造方法等核心知识点。

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">转载自http://www.dedecms.com/knowledge/program/jsp-java/2013/0130/20935.html,本人对原文进行了一部分加工。</span>


在讲之前,一些基本知识是应该具备的:  

1、如果一个类中没有定义构造器,编译器在编译时会自动加上默认无参构造器:

public ClassName() {}
2、this 和 super 的区别。
3、每个类直接或间接是Object的子类,Object只有一个无参构造方法。
4、编译器会在每个构造方法的第一行隐式添加父类的默认无参构造器,即添加super()。
  新手容易犯的错误:
  
class Employee extends Object {
  public Employee(int id) {
  }
}
  class Teacher extends Employee {
  public Teacher() {
  }
}
以上代码会产生编译错误:
Implicit super constructor Employee() is undefined. Must explicitly invoke another constructor
  因为父类定义了一个带参数的构造器,因此编译器不会添加默认无参构造方法,但是因为在子类的构造器中没有显式调用父类的某个构造方法,因此编译器会自动添加super()方法, 但是父类中不存在默认无参构造方法,因此会提示默认无参构造方法未定义错误。
  修改后的代码:
  
class Employee {
  public Employee(int id) {
  }
}
  class Teacher extends Employee {
  public Teacher() {
  super(10);
  }
}
这样,在子类的构造器中显式调用了父类的某个构造器,所以编译器不会自动添加super()方法。

另外,非常值得一提的是,super()...之类的构造方法使用必须在第一行。

class Employee {
  public Employee(int id) {
  }
}
  class Teacher extends Employee {
  public Teacher() {
      statement;//语句
  super(10);
  }
}
以上就是错误的,可以认为,Java首先查看构造函数第一行,若不是super(...);则默认应用super();

构造器的访问修饰符:
一般可以用public,protected,default和private修饰,但是对于private,子类是无法调用该类构造器的。
多层次调用父类构造方法:
假设我们有这样一个层次结构:
Object->Employee->Teacher->Professor
  

class Employee {
  public Employee() {
  System.out.println("Employee constructor called");
  }
}
  class Teacher extends Employee {
  public Teacher() {
  System.out.println("Teacher constructor called");
  }
}
  class Professor extends Teacher {
  public Professor() {
  System.out.println("Professor constructor called");
  }
}
  public class Test {
  public static void main(String args[]) {
  Employee p = new Professor();
  }
}
打印结果:
  Employee constructor called
  Teacher constructor called
  Professor constructor called
  在创建Professor对象时(new Professor()),首先会找到该类的无参构造方法,然后首先调用super()方法,调用Teacher类的无参构造方法,接着再调用 Employee的无参构造方法,最后再调用Object的无参构造方法。最后再打印出信息。



### Java加载顺序及父类子类的加载时机 在 Java加载机制中,的加载初始化遵循一定的顺序。以下是关于加载顺序以及父类子类加载时机的具体说明: #### 1. 加载过程中的主要阶段 Java 虚拟机 (JVM) 将加载分为三个主要阶段:加载、链接初始化。 - **加载** JVM 在此阶段将的 `.class` 文件从磁盘或其他资源中读入内存,并将其存储在方法区中[^1]。 - **链接** 链接阶段进一步细分为验证、准备解析三步: - **验证**: 确保加载的文件格式正确且符合 JVM 规范。 - **准备**: 为的静态变量分配内存并设置默认初始值(如 `0`, `null` 或其他型的零值)。 - **解析**: 把中的符号引用替换为直接引用。 - **初始化** 初始化阶段负责执行的初始化代码,包括静态变量赋值语句静态代码块。此时,父类会在子类之前完成初始化[^2]。 --- #### 2. 父类子类的加载顺序 当一个被加载时,如果该父类,则父类会被优先加载初始化。具体流程如下: ##### (1)加载后的初始化阶段 - **父类静态部分** - 执行父类的静态变量赋值操作。 - 执行父类的静态代码块。 - **子类静态部分** - 执行子类的静态变量赋值操作。 - 执行子类的静态代码块。 > 注意:无论何时创建子类的对象,父类的静态内容总是先于子类的静态内容被执行[^3]。 ##### (2)实例化对象阶段 - **父类非静态部分** - 对父类的成员变量进行赋值。 - 执行父类的非静态代码块。 - 调用父类构造方法。 - **子类非静态部分** - 对子类的成员变量进行赋值。 - 执行子类的非静态代码块。 - 调用子类构造方法。 > 同一别中,构造方法的优先级低于成员变量的赋值语句初始化块。 --- #### 3. 示例代码分析 以下是一个简单的例子来展示父类子类的加载顺序: ```java class Parent { static { System.out.println("Parent Static Block"); } public Parent() { System.out.println("Parent Constructor"); } } class Child extends Parent { static { System.out.println("Child Static Block"); } public Child() { System.out.println("Child Constructor"); } } public class Main { public static void main(String[] args) { new Child(); } } ``` 运行结果: ``` Parent Static Block Child Static Block Parent Constructor Child Constructor ``` 解释: 1. 创建 `Child` 实例前,首先加载 `Parent` `Child` 。 2. 按照加载顺序,`Parent` 的静态代码块先执行,接着是 `Child` 的静态代码块。 3. 接下来进入实例化阶段,由于继承关系,`Parent` 的构造函数先被调用,最后才是 `Child` 的构造函数。 --- #### 4. 特殊情况下的加载触发条件 除了显式的 `new` 关键字外,还有以下几种场景会触发加载: - 访问某个的静态字段或静态方法。 - 使用反射访问某的内容。 - 主程序入口 (`main`) 方法所在会被优先加载。 - 动态代理或 JNI 加载某些特定[^4]。 ---
### Java 子类继承父类成员方法的规则 在 Java 中,子类通过继承机制可以从父类获得其定义的方法。以下是关于子类如何继承父类成员方法的具体规则: #### 继承范围 子类可以继承父类中的 **public** **protected** 成员方法[^1]。然而,父类中的 **private** 方法不会被子类直接继承,尽管可以通过反射技术间接访问这些方法[^2]。 #### 覆盖与重载 如果子类中存在与父类同名的方法,则会发生方法覆盖(Override)。此时,子类中的方法会替代父类中的方法,在运行时动态绑定到具体的实现版本。需要注意的是,覆盖的前提条件是方法签名完全一致,并且子类方法的访问修饰符不能更严格于父类方法[^3]。 另外,子类还可以在其内部定义新的方法来扩展功能,这属于方法重载(Overload),它并不影响父类已有方法的行为。 #### 构造方法特殊性 值得注意的一点是,构造方法不属于可被继承的内容之一。每当创建一个新的子类实例时,默认情况下都会先隐式调用父类的一个构造函数完成必要的初始化工作;如果没有显式的指定某个特定参数列表匹配上的超构建者,则默认尝试执行无参形式的那个[^4]。 ```java // 示例代码展示继承情况下的方法行为 class Parent { public void showInfo(){ System.out.println("This is parent info."); } } class Child extends Parent{ @Override public void showInfo(){ super.showInfo(); // 显示调用了来自Parent里的showInfo() System.out.println("And this is child additional information."); } public static void main(String[] args){ Child myChild = new Child(); myChild.showInfo(); } } ``` 以上程序片段展示了基本的概念应用例子:`Child` 重新定义了 `showInfo()` 函数以增加额外的信息打印逻辑,同时保留原有部分的功能输出。 #### 总结说明 综上所述,Java 提供了一种强大而灵活的方式让开发者利用面向对象编程原则设计软件结构——即允许后代实体共享祖先特性的同时又能适当地调整或增强它们的表现形式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值