关于抽象类中构造函数的一些学习

本文详细解析了Java中子类实例化时的初始化流程,包括构造方法与初始化方法的区别,以及子类如何调用父类构造方法的过程。通过具体的代码示例说明了初始化的顺序及其背后的原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

、子类在创建实例后,类初始化方法会调用父类的初始化方法(除了java.lang.Object类,因为java.lang.Object类没有父类),而这种调用会逐级追述,直到java.lang.Object的初始化方法。
这个地方我说的是初始化方法,而不是构造方法,因为构造方法是相对于java源程序而言,而编译后的class文件是初始化方法即"<init>"方法(红色部分为方法名),初始化方法是由java源程序的三个部分组成的,一个部分是成员字段后的直接的初始化语句,例如private int i=0;private Date date=new Date();等等,第二个部分是由初始化块组成,例如:

1 public class Test{
2    private int i=0;//初始化第一部分
3    //以下大括号内为初始化第二部分
4    {
5      this.i=4;
6      \\do something......
7    }
8 }

第三个部分就是java源代码中的构造方法中的代码,java源代码中有几个构造方法,那么class文件中就有几个初始化方法,编译器会把第一部分与第二部分分别复制到每个初始化方法的前端,然后把初始化方法对应参数的构造方法的代码复制到相应初始化方法中(这里说的复制其实应该说是编译,不过为了让你更好理解所以如此说).

那么说初始化方法如何追述其父类的,这也关系到初始化方法的结构,初始化方法的执行顺序以及结构就如上所说,但是每个初始化方法的第一个执行指令就是调用另外一个初始化方法,这个初始化方法可能是自身类某个初始化方法,例如你的构造函数中第一句有类似this(...)这种语句,那么初始化方法就会调用自身类的指定构造方法;如果你的构造方法中没有指定构造方法调用,那么初始化方法会默认调用父类无参数初始化方法,如果你的子类第一句为super(....),那么初始化方法会调用父类指定初始化方法。这种调用过程会递归进行调用,直到这个类是java.lang.Object类。
调用初始化方法并不代表会生成对象,你的java代码中出现new关键字加上构造方法的调用,只会生成一个对象,其父类对象不会生成,所以调用父类为抽象类的构造方法完全是合理的。而且初始化方法对于虚拟机来说只是一个名称叫做"<init>"的普通方法,区别只是生成对象以后调用而已(sun 的jdk私有包中有绕过构造方法生成对象的方式,可以证明之上说法,具体如何我这里不陈述)。
然后回答你的第二个问题,抽象类中的构造方法其实是用来给继承的子类来用的,因为构造方法相当于初始化方法,当子类调用构造方法时必须调用父类构造方法,所以你可以在子类产生对象时抽象类中按需求初始化抽象类中的字段以及执行一些初始化代码。其实并不是一定要生成某个类的实例才调用构造方法,子类也需要调用父类构造方法。而生成实例也并不一定会调用构造方法,在某些特殊实现中或者特殊情况下,生成实例不会调用构造方法。而调用了构造方法也不一定就生成了一个实例,但是那一定是一个实例调用的,就像一个普通的实例方法一样。

 

 1 abstract class Person{
 2     private String name;
 3     
 4     public Person(String n){
 5         name=n;
 6     }
 7     
 8     public String getName() {
 9         return name;
10     }
11 
12     public abstract String getInformation();
13 }
14 
15 class Employee extends Person{
16     private double salary;
17 
18     public Employee(String n,double s) {
19         super(n);
20         salary=s;
21     }
22 
23     public double getSalary() {
24         return salary;
25     }
26 
27     @Override
28     public String getInformation() {
29         return "级别:员工、工资:"+getSalary();
30     }
31 
32 }
33 
34 public class MainTest{
35 public static void main(String[] args){
36 Person p=new Employee(); 
37 }
38 }

抽象类不能被实例化是说Person p=new Person();这样是错误的;但是Person p=new Employee(); 是可以的,创建子类的实例,父类的引用指向它
在上述主程序中,new Employee后,它会初始化对象,初始化顺序为:
父类静态块初始化---->子类静态块初始化---->父类非静态块初始化---->父类构造方法---->子类非静态块初始化---->子类构造方法。(先静后动,先父后子) 
这个主程序会先初始化person类的非静态属性和构造函数再初始化employees类的非静态属性和构造函数

这就是你第一个问题的当子类的父类为抽象类时,构造方法如何追溯?而抽象类作为父类也会创建实例么?没有创建实例只是构造函数在对抽象类Person的属性初始化,对于一个类的声明,赋值     和使用两件事情,前者不需要实例化,后者必须实例化 

抽象类中的构造方法是怎么回事?就是初始化属性的作用

为什么有构造方法却不能实例化?没有这样的说法,只能针对类实例化对象
抽象类不能被实例化,构造函数被可以重载不能被覆盖,构造函数不能被继承
构造函数和类名相同,所以,继承下来也不是构造函数了,还有构造函数是一个对类初始化的特殊方法,代表这个类,一个基类是不可能知道子类会扩展什么,如果继承了就可以不写代码了,那么子类的新属性怎么初始化?当然这2点是我个人的看法

 

官方的说法是方法区,方法区存放类类型、类型的方法、类型成员变量等等。
其实子类所谓的追述父类构造方法,更确切的说应该是初始化方法的调用,因为方法信息都存放在方法区,所以都是直接调用方法区中的初始化方法。

<think>嗯,用户问的是C++抽象类构造函数的定义,我需要先回忆一下相关知识。首先,抽象类是指含有至少一个纯虚函数的类,不能实例化,只能作为基类。那构造函数抽象类中的定义有什么特别的地方吗? 记得构造函数不能是虚函数,所以抽象类构造函数和普通类的构造函数定义方式应该是一样的。不过,可能用户会有疑问,既然抽象类不能实例化,为什么还需要构造函数呢?这时候需要解释,虽然抽象类本身不能被实例化,但它的派生类在实例化时,会调用基类的构造函数,所以抽象类中仍然需要定义构造函数来初始化成员变量。 然后要考虑构造函数的访问权限。通常,如果派生类要调用基类的构造函数,基类的构造函数需要是public或者protected的。如果构造函数是private的,那么派生类无法访问,可能导致编译错误。所以,在抽象类中,构造函数通常设置为protected,这样既防止直接实例化,又允许派生类调用。 接着可能需要举例说明。比如,抽象类Animal有纯虚函数sound(),它的构造函数用来初始化成员变量,比如age。然后派生类Dog实现sound(),并在构造函数中调用Animal的构造函数进行初始化。这样用户就能理解构造函数抽象类中的实际应用。 另外,用户可能混淆抽象类构造函数和纯虚函数的关系,需要指出它们是不同的概念。构造函数负责对象的初始化,而纯虚函数定义接口,两者在抽象类中都是必要的,但功能不同。 还要注意,抽象类的析构函数通常应该声明为虚函数,以确保正确释放资源。但构造函数不同,不需要也不能是虚函数。这可能也是一个需要注意的点,但用户的问题集中在构造函数,可能不需要深入讨论析构函数,但如果有机会可以稍微提一下。 最后总结一下,抽象类中的构造函数定义方式与普通类相同,但通常设为protected,避免直接实例化,同时允许派生类调用。通过例子说明构造函数的用途,帮助用户理解其必要性。</think>在 C++ 中,**抽象类**(包含至少一个纯虚函数的类)的构造函数定义与普通类相似,但由于抽象类本身无法直接实例化,其构造函数的设计需要遵循一些特殊规则。以下是关键点: --- ### 1. **构造函数的定义** - **语法与普通类一致**:抽象类构造函数没有特殊语法,可以像普通类一样定义。 - **作用**:初始化抽象类的成员变量,为派生类提供基类部分的初始化逻辑。 - **示例**: ```cpp class Animal { // 抽象类(包含纯虚函数) public: Animal(int age) : age_(age) {} // 构造函数 virtual ~Animal() = default; // 虚析构函数(推荐) virtual void sound() = 0; // 纯虚函数 private: int age_; }; ``` --- ### 2. **构造函数不能是虚函数** - C++ 不允许虚构造函数,这是语言设计上的限制。 - 但析构函数**必须声明为虚函数**(如果可能被多态使用),以确保派生类对象能正确释放资源。 --- ### 3. **构造函数的调用规则** - **派生类构造函数隐式/显式调用基类构造函数**: ```cpp class Dog : public Animal { public: Dog(int age, std::string name) : Animal(age), name_(name) {} // 显式调用基类构造函数 void sound() override { std::cout << "Woof!" << std::endl; } private: std::string name_; }; ``` - 若抽象类构造函数未显式调用,编译器会尝试调用默认构造函数。若抽象类没有默认构造函数,会报错。 --- ### 4. **构造函数的访问权限** - **推荐设为 `protected`**: - 防止用户直接实例化抽象类(编译错误)。 - 允许派生类调用基类构造函数。 ```cpp class Animal { protected: // 避免外部直接实例化 Animal(int age) : age_(age) {} }; ``` --- ### 5. **为什么抽象类需要构造函数?** - **初始化基类成员**:抽象类可能包含数据成员,需通过构造函数初始化。 - **为派生类提供初始化接口**:派生类必须通过基类构造函数初始化继承的成员。 --- ### 总结 - 抽象类构造函数用于初始化基类成员,语法与普通类相同。 - 构造函数不能是虚函数,但析构函数应声明为虚函数。 - 推荐将构造函数设为 `protected`,阻止直接实例化。 - 派生类必须显式或隐式调用抽象类构造函数。 示例代码:[在线运行](https://godbolt.org/z/7b6cK3q5h)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值