Java 类与对象

背景与动机#

Java 是典型的面向对象语言。基础语法解决“语句怎么写”,类与对象解决“数据和行为如何组织”。

可以先这样理解:

  • 类是模板,描述一类事物有哪些属性和行为。
  • 对象是类创建出来的具体实例。

核心原理拆解#

1. 类定义对象的结构#

class User {
String name;
int age;
void sayHello() {
System.out.println("hello, " + name);
}
}

这里 nameage 是字段,sayHello 是方法。

2. 使用 new 创建对象#

User user = new User();
user.name = "Tom";
user.age = 18;
user.sayHello();

new User() 会创建一个 User 对象,变量 user 保存对这个对象的引用。

img
img

2.1 从内存角度理解 new 做了什么#

如果把上面的代码再往底层一点看,可以先建立一个简化理解:

  • 栈:放方法调用过程中的局部变量和引用
  • 堆:放真正创建出来的对象实例
  • 方法区:放类的元信息、方法字节码、静态变量、常量等

img
img

可以把它和这段代码对应起来:

User user = new User();
user.name = "Tom";
user.age = 18;

这里发生的事情可以按顺序理解为:

  1. main() 执行时,会有自己的栈帧。
  2. 局部变量 user 存在当前方法的栈帧里。
  3. new User() 在堆中创建一个真正的 User 对象。
  4. 栈里的 user 保存的不是整个对象本身,而是指向堆中对象的引用。
  5. User 这个类的结构信息,比如字段、方法、字节码等,可以先理解为放在方法区中。

这也是为什么我们常说:

  • 基本类型局部变量经常直接放在栈帧里
  • 对象一般创建在堆里
  • 变量名拿到的很多时候只是“对象引用”

3. Java 创建对象不只有一种方式#

img
img

日常开发里最常见的是 new,但严格来说,Java 创建对象常见有这些方式:

  • new 调用构造方法创建对象
  • 通过工厂方法返回对象
  • 通过反射、clone、反序列化等特殊机制创建对象
User u1 = new User("Tom", 18); // 最常见
User u2 = User.of("Jerry", 20); // 工厂方法

其中最值得先掌握的是前两种:

  • new:最直接,适合正常创建对象
  • 工厂方法:适合把创建规则收进类内部,比如缓存对象、隐藏实现细节、统一参数校验

反射、clone、反序列化都属于更进阶或特定场景下的对象创建方式,这里先知道它们存在即可,展开理解可以放到后续更具体的类库主题里。

4. 构造方法用于初始化对象#

构造方法名称必须和类名一致,并且没有返回值类型:

class User {
String name;
int age;
User(String name, int age) {
this.name = name;
this.age = age;
}
}

创建对象时会调用构造方法:

User user = new User("Tom", 18);

构造参数的意义,不只是“把值传进去”,更重要的是让对象在创建时就具备一份完整、合法的初始状态。比如用户对象必须有名字和年龄,就可以要求调用方在 new User(...) 时一次性传入。

5. 无参构造、带参构造和重载构造#

一个类可以有多个构造方法,只要参数列表不同,就叫构造方法重载:

class User {
String name;
int age;
User() {
this("anonymous", 0);
}
User(String name) {
this(name, 0);
}
User(String name, int age) {
this.name = name;
this.age = age;
}
}

这样做的好处是:

  • 调用方可以按自己已有的数据量选择不同构造方法
  • 真正的初始化逻辑集中在一个主构造方法里,避免重复赋值代码

这里的 this("anonymous", 0)this(name, 0) 不是访问字段,而是在当前构造方法里继续调用同类的另一个构造方法,这种写法叫构造方法链。

6. 什么时候类不能直接实例化#

并不是所有类都能直接 new

  • abstract class 不能直接实例化,因为抽象类本身通常只提供公共结构或部分实现
  • interface 不能直接实例化,因为接口只描述能力约定,不提供完整对象
  • 构造方法是 private 的类,不能在类外直接 new

例如下面这些写法都不行:

abstract class Animal {
}
interface Flyable {
}
// Animal animal = new Animal(); // 编译失败
// Flyable flyable = new Flyable(); // 编译失败

private 构造方法常见于工具类、单例模式或不希望外部随便创建对象的场景。

7. this 表示当前对象#

当参数名和字段名相同时,常用 this 区分:

this.name = name;

左边的 this.name 是当前对象字段,右边的 name 是构造方法参数。

在构造方法里,this(...) 还可以调用当前类的其他构造方法;而 this.name 这种写法表示访问当前对象字段。两者都叫 this,但含义不一样,要结合上下文看。

最小可运行代码示例#

public class ClassObjectDemo {
public static void main(String[] args) {
User user1 = new User();
User user2 = new User("Tom");
User user3 = new User("Jerry", 18);
user1.sayHello();
user2.sayHello();
user3.sayHello();
}
}
class User {
String name;
int age;
User() {
this("anonymous", 0);
}
User(String name) {
this(name, 0);
}
User(String name, int age) {
this.name = name;
this.age = age;
}
void sayHello() {
System.out.println("hello, " + name + ", age=" + age);
}
}

常见陷阱与错误示例#

1. 构造方法写了返回值类型#

错误示例:

void User(String name) {
this.name = name;
}

这不是构造方法,而是一个普通方法。

2. 对象引用为 null 时调用方法#

User user = null;
user.sayHello();

这会抛出 NullPointerException。调用对象方法前,要确认引用不是 null

3. 写了带参构造后,以为默认无参构造还会自动存在#

class User {
User(String name) {
}
}
User user = new User(); // 编译失败

如果类里已经手动声明了构造方法,编译器就不会再额外赠送默认无参构造。需要无参构造时,要自己写出来。

4. 试图直接实例化抽象类或接口#

抽象类和接口都不能直接 new。它们的作用是定义抽象层、通用能力或约束,真正被实例化的通常是具体子类或实现类。

5. 字段直接公开导致状态随意修改#

学习阶段可以先直接访问字段,但工程代码通常会配合封装,把字段设为 private,通过方法控制修改规则。

面试高频问题#

1. 类和对象有什么区别#

类是模板,描述属性和行为;对象是根据类创建出来的具体实例。

2. 构造方法有什么特点#

构造方法名称和类名一致,没有返回值类型,创建对象时自动调用,通常用于初始化对象状态。

3. 为什么有些类不能实例化#

因为它们不是“可直接创建的完整对象类型”。比如抽象类通常只提供公共骨架,接口只定义行为约定,private 构造方法则主动限制外部创建。

4. 无参构造和有参构造怎么选#

如果对象创建时必须带上关键数据,优先提供有参构造;如果允许先创建、后赋值,或者需要给默认值,可以补无参构造。很多时候会同时提供多个重载构造方法。

5. Java 创建对象有哪些方式#

最常见的是 new,此外还有工厂方法、反射、clone、反序列化等方式。入门和日常业务代码里,优先先掌握 new 和工厂方法。

6. final 代表什么#

final 表示这个东西不能再被改写,具体是不能重新赋值、不能重写方法,还是不能继承类,要看它修饰的目标。

7. this 代表什么#

this 代表当前对象,常用于访问当前对象字段、方法,或解决参数名和字段名冲突。

一句话总结#

类定义结构和行为,对象承载具体状态;理解对象创建方式、构造方法、finalthis,就抓住了“类与对象”这一章最核心的主线。

文章目录

文章目录