从 TypeScript 到 Java(5):构造函数与对象创建 —— Java 世界的生命起点
从 TypeScript 到 Java(5):构造函数与对象创建 —— Java 世界的生命起点
系列导读:
前面我们了解了类、访问修饰符与封装,这一篇我们要进入面向对象的核心之一——对象的诞生。在 TypeScript 中,你可能习惯写:
const user = new User("Tom");但在 Java 中,这行代码背后发生的事情要复杂得多。
我们将从new开始,完整理解对象的生命周期。
☕ 一句话理解
在 Java 中,对象创建 = 分配内存 + 调用构造函数 + 返回引用。
Java 的对象构造是“强类型、单入口、可重载”的;
TypeScript 的构造函数更灵活,但约束更弱。
🧩 Java 的构造函数(Constructor)
构造函数是类中用于初始化对象的特殊方法,名字必须与类名相同:
public class User {
private String name;
// 构造函数
public User(String name) {
this.name = name;
}
public void sayHello() {
System.out.println("Hello, " + name + "!");
}
}
public class Main {
public static void main(String[] args) {
User user = new User("Alice");
user.sayHello(); // 输出:Hello, Alice!
}
}
注意:
- 构造函数 没有返回类型;
- 可以有多个构造函数(重载);
- 若没有定义构造函数,编译器会自动生成一个无参构造函数。
🔁 构造函数重载(Constructor Overloading)
Java 支持多个构造函数,只要参数签名不同即可:
public class Book {
private String title;
private double price;
public Book() {
this("Untitled", 0.0); // 调用另一个构造函数
}
public Book(String title) {
this(title, 10.0);
}
public Book(String title, double price) {
this.title = title;
this.price = price;
}
public void info() {
System.out.println(title + " - $" + price);
}
}
public class Main {
public static void main(String[] args) {
new Book().info(); // Untitled - $0.0
new Book("Java 入门").info(); // Java 入门 - $10.0
new Book("深入Java", 45.0).info(); // 深入Java - $45.0
}
}
this(...) 的作用:允许在一个构造函数中调用另一个,避免重复初始化逻辑。
🧱 默认构造函数与编译器行为
如果你不定义任何构造函数:
public class Dog {
public void bark() {
System.out.println("Woof!");
}
}
编译器会自动生成一个默认构造函数:
public Dog() {}
这样你依然可以写:
new Dog().bark();
但如果你显式声明了一个构造函数(哪怕带参数),
Java 就不会自动生成默认构造函数。
若你此时想用无参 new Dog(),就会编译错误。
🔍 对比 TypeScript 的构造函数
在 TypeScript 中:
class User {
constructor(public name: string) {}
}
const user = new User("Alice");
区别点:
| 特性 | Java | TypeScript |
|---|---|---|
| 构造函数命名 | 与类名相同 | 统一用 constructor |
| 是否可重载 | ✅ 支持重载 | ❌ 不支持重载(只能可选参数) |
| 自动生成 | ✅ 默认无参构造 | ✅ 默认空构造 |
| this 调用 | ✅ 可调用其他构造 | ⚠️ 不支持调用其他构造 |
| 编译级检查 | 严格 | 较宽松(编译后为 JS) |
🧬 new 背后的工作原理
当执行:
User user = new User("Alice");
Java 实际上做了以下步骤:
-
类加载
- 首次使用
User类时,JVM 通过类加载器(ClassLoader)加载.class文件。
- 首次使用
-
内存分配
- 在堆内存(Heap)中为对象分配空间。
-
成员变量默认初始化
- 所有字段先被赋予默认值(int → 0, boolean → false, 引用 → null)。
-
执行构造函数
- 按声明顺序执行字段初始化语句,然后执行构造函数。
-
返回引用
new表达式返回对象的引用,赋值给变量。
可视化流程图:
new User("Alice")
↓
类加载检查
↓
分配堆内存
↓
字段默认值初始化
↓
执行构造函数
↓
返回对象引用
这种明确的内存与生命周期控制,使得 Java 在大型系统中运行更稳定、可预测。
🧠 构造函数与继承的协作
在继承关系中,子类构造函数必须调用父类构造函数。
class Animal {
public Animal(String name) {
System.out.println("Animal: " + name);
}
}
class Dog extends Animal {
public Dog() {
super("Dog"); // 必须显式或隐式调用父类构造函数
System.out.println("Dog constructor");
}
}
public class Main {
public static void main(String[] args) {
new Dog();
}
}
输出:
Animal: Dog
Dog constructor
Java 编译器会自动在子类构造函数的第一行插入:
super();
除非你显式写了其他父类构造调用。
🔍 TypeScript 的继承对比
class Animal {
constructor(name: string) {
console.log("Animal:", name);
}
}
class Dog extends Animal {
constructor() {
super("Dog");
console.log("Dog constructor");
}
}
表面相似,但 TypeScript 没有 JVM、堆内存的概念;super() 只是调用父类函数逻辑,并不会触发底层内存管理。
⚙️ 对象初始化顺序(重要)
如果类中既有字段初始化、又有构造函数,执行顺序如下:
class Example {
int a = 10;
Example() {
System.out.println("a = " + a);
}
}
输出:
a = 10
即:
- 字段初始化先执行;
- 构造函数随后执行。
如果存在继承关系,顺序是:
父类字段初始化 → 父类构造 → 子类字段初始化 → 子类构造。
🧭 小结
| 关键点 | 说明 |
|---|---|
| 构造函数 | 与类名相同,用于初始化对象 |
| 重载 | 支持多种初始化方式 |
| 默认构造 | 无定义时自动生成 |
| this(…) | 调用同类其他构造函数 |
| super(…) | 调用父类构造函数 |
| 对象创建过程 | 加载类 → 分配堆内存 → 默认初始化 → 执行构造 |
| 与 TS 区别 | Java 有严格生命周期与内存管理 |
🚀 实战建议
- 在小项目中练习重载构造函数;
- 理解
super()的调用顺序; - 使用调试工具(如 IntelliJ 的 Debug)逐步观察
new的执行过程; - 试着画出对象创建的流程图(推荐用 PlantUML 可视化)。
🔮 下一篇预告
第 6 篇:《继承、接口与抽象类 —— Java 的类型扩展机制》
我们将探索 Java 面向对象的三大支柱之一:
👉 继承(Inheritance)、接口(Interface)、抽象类(Abstract Class),
看看它们如何共同构建出 Java 的强大类型系统。
更多推荐



所有评论(0)