类与对象
- 类的定义
-
- 类的基本语法
- 修饰符详解
-
- a) 访问控制修饰符
- b) 类型修饰符
- 继承与实现
-
- extends – 类的继承
- implements – 接口的实现
- 类体组成
-
- 成员变量的定义
- 成员方法的定义
- 对象的创建与成员访问
-
- 对象的创建
-
- 成员变量的默认值
- 对象的成员访问
- 对象与成员的生命周期
-
- a) 对象的生命周期
- b) 成员变量的生命周期
- 成员变量的作用域
- 对象的初始化和构造方法
-
- 对象初始化概述
- 三种初始化方式详解
-
- a) 定义时指定初值
- b) 初始化代码块
- c) 构造方法初始化
- 初始化执行顺序演示
- 构造方法的特性与注意事项
-
- 基本语法规则
- 构造方法的重载
- 默认构造方法
- 构造方法不能被继承和重写
- 引用变量的理解
- 深入理解 `this` 关键字
-
- `this` 的基本概念(类型/使用范围/引用约束)
- `this` 的三种核心用途
-
- a) 通过 `this` 访问当前对象的成员变量
- b) 通过 `this` 访问当前对象的非静态成员方法
- c) 通过 `this` 访问当前对象的其他构造方法
- 深入理解:`this` 是如何传递的?
- 深入理解:对象创建时 `this` 是如何出现的?
-
- 对象创建的六个步骤
- 代码层面的体现
- 为什么要有这些步骤
简单理解,对象是一种实体,而类是一种规范。
类的定义
类是Java面向对象编程中最基本的组成单位,它是一种抽象的数据类型,用于描述具有相同属性和行为的一组对象。类是创建对象的模板或蓝图。
类的基本语法
在Java中,使用class关键字来定义一个类,完整的类定义语法如下:
[修饰符] class 类名 [extends 父类名] [implements 接口列表] {
// 类体部分
// 1. 成员变量的定义
[修饰符] 数据类型 变量名;
// 2. 成员方法的定义
[修饰符] 返回值类型 方法名([参数列表]) [throws 异常列表] {
// 方法体
}
}
语法说明:
| [修饰符] | 类的访问权限和特性修饰符 | 可选 |
| class | 定义类的关键字 | 必需 |
| [extends 父类名] | 指定继承的父类 | 可选(默认继承Object类) |
| [implements 接口列表] | 指定实现的接口,多个接口用逗号分隔 | 可选 |
修饰符详解
类的修饰符可以分为两大类:访问控制修饰符和类型修饰符。多个修饰符可以同时使用,它们之间用空格隔开,顺序可以任意排列。
a) 访问控制修饰符
| public | 公有访问级别,任何位置都可以访问 |
| 缺省(默认) | 不写任何访问修饰符,只能被同一包中的类访问 |
注意: 一个Java源文件中只能有一个public修饰的类,且该类名必须与文件名相同。
b) 类型修饰符
| abstract | 抽象类,不能被实例化,只能被继承 |
| final | 最终类,不能被继承 |
修饰符使用示例:
// public修饰的类,可以被任何包访问
public class Student {
// 类体
}
// 缺省(默认)访问级别的类,只能被同一包中的类访问
class Teacher {
// 类体
}
// 抽象类,不能被实例化
public abstract class Animal {
// 类体
}
// 最终类,不能被继承
public final class MathUtils {
// 类体
}
// 多个修饰符组合使用
public abstract class Shape {
// 类体
}
继承与实现
extends – 类的继承
extends关键字用于指定当前类要继承的父类。Java是单继承语言,即一个类只能有一个直接父类。
// 父类定义
public class Animal {
protected String name;
public void eat() {
System.out.println(name + "正在进食");
}
}
// 子类继承父类
public class Dog extends Animal {
public void bark() {
System.out.println(name + "正在汪汪叫");
}
}
继承的特点:
- 子类继承父类所有的非私有成员(变量和方法)
- 如果没有使用extends指定父类,则默认继承java.lang.Object类
- 所有类最终都继承自Object类
implements – 接口的实现
implements关键字用于指定当前类要实现的接口。一个类可以实现多个接口,接口之间用逗号分隔。
// 定义接口
public interface Flyable {
void fly();
}
public interface Swimable {
void swim();
}
// 类实现多个接口
public class Bird implements Flyable, Swimable {
@Override
public void fly() {
System.out.println("鸟儿在飞翔");
}
@Override
public void swim() {
System.out.println("鸟儿在游泳");
}
}
类体组成
类体由两部分核心内容组成:成员变量(属性)和成员方法(行为)。
成员变量的定义
[修饰符] 数据类型 变量名 [= 初始值];
成员变量的修饰符:
| 访问控制 | public、protected、private |
| 静态修饰 | static |
| 常量修饰 | final |
| 特性修饰 | transient、volatile |
示例:
public class Person {
// 成员变量定义
public String name; // 公有访问
private int age; // 私有访问
protected String idCard; // 受保护访问
static String country = "中国"; // 静态变量
final double PI = 3.14159; // 常量
}
成员方法的定义
[修饰符] 返回值类型 方法名([参数列表]) [throws 异常列表] {
// 方法体
[return 返回值;]
}
成员方法的修饰符:
| 访问控制 | public、protected、private |
| 静态修饰 | static |
| 抽象修饰 | abstract |
| 最终修饰 | final |
| 同步修饰 | synchronized |
| 本地修饰 | native |
示例:
public class Calculator {
// 无参数无返回值的方法
public void showInfo() {
System.out.println("这是一个计算器类");
}
// 带参数带返回值的方法
public int add(int a, int b) {
return a + b;
}
// 静态方法
public static void printVersion() {
System.out.println("版本 1.0");
}
// 声明抛出异常的方法
public void divide(int a, int b) throws ArithmeticException {
if (b == 0) {
throw new ArithmeticException("除数不能为0");
}
System.out.println("结果:" + a / b);
}
}
| extends | 指定继承的父类 | class Dog extends Animal { } |
| implements | 指定实现的接口 | class Bird implements Flyable { } |
| 访问修饰符 | 控制访问权限 | public、private、protected |
| 类型修饰符 | 定义类的特性 | abstract、final |
| 成员变量 | 定义类的属性 | private String name; |
| 成员方法 | 定义类的行为 | public void study() { } |
对象的创建与成员访问
对象的创建
在Java中,对象是类的实例,创建对象需要通过new关键字来完成。创建对象的同时,通常会将对象的引用赋值给一个变量,语法格式如下:
<类名> 引用变量名 = new <类名>([参数列表])
其中,[参数列表]是可选的,用于调用类的构造方法进行对象初始化。
示例:
// 创建Person类的对象,并将引用赋值给p
Person p = new Person();
// 创建带参数的构造方法的对象
Person p2 = new Person("张三", 20);
成员变量的默认值
如果在定义成员变量时没有显式指定初始值,Java虚拟机会自动为其赋予默认值,这是Java语言的安全机制,确保成员变量在使用前总有一个确定的初始状态。
| byte、short、int、long | 0 |
| float、double | 0.0 |
| char | ‘\\u0000’(空字符) |
| boolean | false |
| 任何引用类型(如String、数组、对象) | null |
示例:
public class DefaultValuesExample {
// 实例变量的默认值
int num; // 默认值:0
double price; // 默认值:0.0
boolean flag; // 默认值:false
char ch; // 默认值:\\u0000
String name; // 默认值:null
int[] numbers; // 默认值:null
// 静态变量的默认值
static int staticNum; // 默认值:0
static String staticName; // 默认值:null
public static void main(String[] args) {
DefaultValuesExample obj = new DefaultValuesExample();
// 输出结果均为对应类型的默认值
System.out.println("num: " + obj.num); // 0
System.out.println("name: " + obj.name); // null
System.out.println("staticNum: " + staticNum); // 0
}
}
对象的成员访问
对象创建完成后,可以通过引用变量来访问对象的成员,包括实例变量(成员变量)和实例方法(成员方法)。访问语法为:
引用变量名.成员变量名 // 访问对象的属性
引用变量名.成员方法名() // 调用对象的方法
示例:
Person p = new Person();
p.name = "李四"; // 访问成员变量
p.age = 25; // 访问成员变量
p.sayHello(); // 调用成员方法
对象与成员的生命周期
a) 对象的生命周期
- 对象通过new关键字创建后,将在堆内存中分配空间。
- 当没有任何引用变量指向该对象时,对象将变为垃圾对象。
- Java的垃圾回收处理器会自动发现并清除这些无引用的对象,释放内存空间。
b) 成员变量的生命周期
成员变量的生命周期与其所属对象完全一致:
- 创建时:当对象通过new创建时,成员变量被分配内存并完成初始化。
- 销毁时:当对象被垃圾回收时,成员变量所占用的内存也随之释放。
class MyClass {
int x; // 成员变量
}
MyClass obj = new MyClass(); // 成员变量x被创建
obj = null; // 对象成为垃圾,成员变量x等待回收
成员变量的作用域
成员变量的作用域是整个类内部,具体特点如下:
class MyClass {
private int x; // 私有成员变量
public int y; // 公有成员变量
public void setX(int value) {
x = value; // 在成员方法中直接访问私有变量x
}
public int getX() {
return x; // 在成员方法中直接访问私有变量x
}
public void printY() {
System.out.println(y); // 访问公有变量y
}
}
| 对象创建 | 类名 引用变量 = new 类名(参数) |
| 成员访问 | 引用变量.成员变量 或 引用变量.成员方法() |
| 对象销毁 | 无引用指向时,由垃圾回收器自动回收 |
| 成员变量生命周期 | 与所属对象相同,随对象创建而创建,随对象销毁而释放 |
| 成员变量作用域 | 整个类内部,所有方法均可直接访问 |
对象的初始化和构造方法
对象初始化概述
当使用new关键字创建对象时,Java虚拟机会为对象分配内存并进行初始化。对象的初始化是指为对象的成员变量设置初始值的过程。在Java中,对象的初始化可以通过以下三种方式完成:
这三种初始化方式的执行顺序为:
定义时赋值 → 初始化代码块 → 构造方法
三种初始化方式详解
a) 定义时指定初值
在声明成员变量的同时直接赋值,是最简单直接的初始化方式。
public class Point {
private int x = 10; // 定义时赋初值
private int y = 20; // 定义时赋初值
private String name = "原点"; // 引用类型赋初值
}
b) 初始化代码块
初始化代码块是使用{}括起来的代码段,在每次创建对象时都会自动执行(在构造方法之前)。
public class Point {
private int x;
private int y;
// 实例初始化块
{
x = 20; // 覆盖定义时的赋值
y = 30;
System.out.println("实例初始化块执行");
}
// 静态初始化块:在类加载时执行一次,用于初始化静态变量
static {
System.out.println("静态初始化块执行");
// 可以初始化静态变量
}
}
c) 构造方法初始化
通过构造方法为成员变量赋值,是最常用、最灵活的初始化方式。
public class Point {
private int x;
private int y;
// 无参构造方法
public Point() {
this.x = 0;
this.y = 0;
}
// 带参构造方法
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
初始化执行顺序演示
public class Point {
private int x = 10; // 第一步:定义时赋值
// 第二步:实例初始化块
{
x = 20;
System.out.println("初始化块执行,x = " + x);
}
// 第三步:构造方法
public Point() {
x = 30;
System.out.println("构造方法执行,x = " + x);
}
public static void main(String[] args) {
Point p = new Point();
// 输出顺序:
// 初始化块执行,x = 20
// 构造方法执行,x = 30
}
}
构造方法的特性与注意事项
构造方法是Java中一种特殊的方法,用于初始化新创建的对象。它具有以下重要特性:
基本语法规则
- 名称必须与类名完全相同
- 没有返回值类型(连void也不能写)
- 不能被static、final、synchronized、abstract、native修饰
public class Student {
private String name;
private int age;
// 正确的构造方法
public Student() {
name = "未知";
age = 0;
}
// 错误示例:不能有返回值类型
// public void Student() { } // 这不是构造方法,而是普通方法
}
构造方法的重载
一个类可以有多个构造方法,只要参数列表不同即可。这称为构造方法的重载。
public class Point {
private int x;
private int y;
// 无参构造方法
public Point() {
this(0, 0); // 调用另一个构造方法
}
// 两个参数的构造方法
public Point(int x, int y) {
this.x = x;
this.y = y;
}
// 一个参数的构造方法
public Point(int value) {
this(value, value); // 调用两个参数的构造方法
}
}
// 使用时,系统会根据参数自动匹配对应的构造方法
Point p1 = new Point(); // 调用无参构造
Point p2 = new Point(5, 8); // 调用两个参数的构造
Point p3 = new Point(10); // 调用一个参数的构造
默认构造方法
如果类中没有定义任何构造方法,系统会自动提供一个无参的默认构造方法。
public class Point {
private int x;
private int y;
// 没有定义任何构造方法
}
// 仍然可以调用无参构造
Point p = new Point(); // 系统提供的默认构造方法
注意: 如果类中已经定义了任何构造方法,系统将不再提供默认的无参构造。
public class Point {
private int x;
private int y;
public Point(int x, int y) { // 定义了带参构造
this.x = x;
this.y = y;
}
}
// 错误!此时系统不再提供默认无参构造
// Point p = new Point(); // 编译错误
// 正确方式
Point p = new Point(5, 8); // 必须使用已定义的构造方法
构造方法不能被继承和重写
- 构造器不能被继承:子类不能继承父类的构造方法
- 构造器不能被重写(Override):构造方法的名称必须与当前类名相同,因此无法重写
class Parent {
public Parent() {
System.out.println("Parent constructor");
}
public Parent(String name) {
System.out.println("Parent constructor with name");
}
}
class Child extends Parent {
// 这不是重写,而是定义子类自己的构造方法
public Child() {
System.out.println("Child constructor");
}
public Child(String name) {
// 可以通过super()调用父类构造方法
super(name); // 调用父类的带参构造
System.out.println("Child constructor with name");
}
}
引用变量的理解
在Java中,对象是通过引用变量来操作和访问的。理解引用变量的本质对于掌握Java内存模型至关重要。
public class Point {
private int x = 10; // 定义时赋初值
private int y = 20; // 定义时赋初值
// 无参构造方法
public Point() {
this(0, 0); // 调用另一个构造方法
}
// 两个参数的构造方法
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
public void main(String[] args) {
Point p1 = new Point(); // p1指向新创建的Point对象
Point p2 = new Point(5, 8); // p2指向另一个新创建的Point对象
Point p3 = p1; // p3和p1指向同一个Point对象
}
引用变量的内存示意图: 
深入理解 this 关键字
this 的基本概念(类型/使用范围/引用约束)
this 是一个指向当前对象的引用,它在实例方法或构造方法中使用。
核心要点:
this 的类型:this 的类型是对应类的引用类型,即哪个对象调用,this 就是哪个对象的引用类型。
public class Student {
public void printThis() {
System.out.println(this); // 输出:Student@15db9742(某个具体的对象地址)
}
public static void main(String[] args) {
Student s1 = new Student();
Student s2 = new Student();
s1.printThis(); // 这里的 this 指向 s1
s2.printThis(); // 这里的 this 指向 s2
}
}
this 的使用范围:this 只能在"成员方法"中使用(包括实例方法和构造方法),不能在静态方法中使用。
this 的引用约束:在"成员方法"中,this 只能引用当前对象,不能再引用其他对象。
public void test() {
this = new Student(); // 编译错误!不能修改 this 的指向
}
this 的三种核心用途
根据上述基本概念,this 主要有以下三种用法:
a) 通过 this 访问当前对象的成员变量
当实例变量(成员变量)和局部变量(方法参数或方法内定义的变量)重名时,可以使用 this 来明确指代实例变量。这是最常见的 this 使用场景。
public class Student {
private String name; // 实例变量
private int age; // 实例变量
// 构造方法:参数名与实例变量同名
public Student(String name, int age) {
// this.name 指实例变量,name 指方法参数
this.name = name;
this.age = age;
}
// Setter 方法:参数名与实例变量同名
public void setName(String name) {
this.name = name; // 必须使用 this 区分
}
public void setAge(int age) {
this.age = age; // 必须使用 this 区分
}
public void introduce() {
System.out.println("我叫" + name + ",今年" + age + "岁");
// 此处虽然没有 this,但实际相当于 this.name 和 this.age
}
}
b) 通过 this 访问当前对象的非静态成员方法
this 可以用来调用当前对象的其他实例方法,虽然通常可以省略,但在某些场景下显式使用 this 可以提高代码的可读性。
public class Calculator {
private int result;
public Calculator add(int value) {
this.result += value;
this.display(); // 使用 this 调用当前对象的方法
return this; // 返回当前对象,支持链式调用
}
public void display() {
System.out.println("当前结果:" + this.result);
}
public static void main(String[] args) {
Calculator calc = new Calculator();
calc.add(5).add(3); // 链式调用
}
}
c) 通过 this 访问当前对象的其他构造方法
在一个构造方法中,可以使用 this(…) 调用类中的另一个构造方法。这种方式称为构造方法的重用,可以有效减少代码重复。
使用规则:
- this(…) 必须放在构造方法的第一行
- 一个构造方法中只能调用一次 this(…)
- 如果使用了 this(…),就不能再使用 super(…)(因为都要求在第一行)
public class Rectangle {
private double width;
private double height;
private String color;
// 完整构造方法
public Rectangle(double width, double height, String color) {
this.width = width;
this.height = height;
this.color = color;
System.out.println("完整构造方法被调用");
}
// 构造方法1:只指定宽高,颜色默认为黑色
public Rectangle(double width, double height) {
this(width, height, "黑色"); // 调用完整构造方法
}
// 构造方法2:无参构造方法,默认宽高为1,颜色为黑色
public Rectangle() {
this(1, 1); // 调用二参构造方法
}
// 构造方法3:正方形,颜色默认为黑色
public Rectangle(double side) {
this(side, side); // 调用二参构造方法
}
}
| this 的类型 | 对应类类型的引用 | 哪个对象调用,this 就是哪个对象的引用 |
| 使用范围 | 只能在成员方法中使用 | 不能在静态方法中使用 |
| 引用约束 | 只能引用当前对象 | 不能修改 this 的指向 |
| 访问成员变量 | this.成员变量 | 解决局部变量与成员变量同名问题 |
| 访问成员方法 | this.成员方法() | 调用当前对象的其他实例方法 |
| 调用其他构造方法 | this(参数) | 实现构造方法重用 |
深入理解:this 是如何传递的?
很多初学者会疑惑:this 到底是怎么来的?其实,this 是 Java 编译器自动传递的隐藏参数。
来看下面的代码对比:
public class Date {
private int year;
private int month;
private int day;
// 我们平时写的代码(使用 this)
public void setDate(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
}
实际上,Java 编译器在处理实例方法时,会自动在参数列表的最前面添加一个隐藏参数,即当前对象的引用。上面的代码在编译层面相当于:
// 编译器内部视角:隐藏的 this 参数
public void setDate(Date this, int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
- 当调用 d1.setDate(2024, 1, 15) 时,JVM 会自动将 d1 的引用作为第一个参数传递进去
- 方法内部的 this 就指向了调用该方法的对象
- 这就是为什么我们说:哪个对象的引用调用了该方法,this 就代表哪个对象
验证示例:
public class Date {
private int year;
private int month;
private int day;
public void setDate(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
public void printThis() {
System.out.println("当前 this 指向:" + this);
}
public static void main(String[] args) {
Date d1 = new Date();
Date d2 = new Date();
d1.printThis(); // 输出:当前 this 指向:Date@15db9742 (d1的地址)
d2.printThis(); // 输出:当前 this 指向:Date@6d06d69c (d2的地址)
d1.setDate(2024, 1, 15); // 调用时,JVM自动传递 d1 作为 this
d2.setDate(2024, 1, 16); // 调用时,JVM自动传递 d2 作为 this
}
}
这个理解为什么重要?
为什么静态方法不需要传递对象引用?
深入理解:对象创建时 this 是如何出现的?
为了更深入地理解 this,我们需要知道在 JVM 层面,一个对象是如何被创建出来的。当我们在程序中写下一行简单的代码:
Date d = new Date(2024, 3, 10);
在 JVM 层面,实际上经历了复杂的六个步骤:
对象创建的六个步骤

| 1. 类加载检查 | 检测 Date 类是否已加载 | 如果没有加载,JVM 通过类加载器加载该类 |
| 2. 分配内存 | 为对象分配堆空间 | 计算对象所需内存大小,在堆中划分一块内存 |
| 3. 并发安全处理 | 保证线程安全 | 多个线程同时创建对象时,保证空间分配不冲突 |
| 4. 内存空间初始化 | 设置默认值 | 所有成员变量设置为默认值(0、false、null) |
| 5. 设置对象头 | 存储对象元数据 | 记录对象的哈希码、GC分代年龄、类型指针等 |
| 6. 调用构造方法 | 执行初始化代码 | 执行程序员编写的构造方法,给成员变量赋值 |
代码层面的体现
public class Date {
private int year; // 步骤4:初始化为0
private int month; // 步骤4:初始化为0
private int day; // 步骤4:初始化为0
// 构造方法 – 步骤6被调用
public Date(int year, int month, int day) {
// 此时对象已经存在,this 已经指向这个对象
// 成员变量已经有默认值(0,0,0)
this.year = year; // 将形参year的值赋给对象的year
this.month = month; // 将形参month的值赋给对象的month
this.day = day; // 将形参day的值赋给对象的day
}
}
为什么要有这些步骤
步骤1-3:JVM 需要确保对象能够被正确创建,不会因为类未加载或多线程冲突而出错
步骤4(初始化默认值):保证对象在任何构造方法执行之前都处于一个确定的状态
// 即使不在构造方法中赋值,成员变量也有默认值
Date d = new Date(); // 如果不赋值,year=0, month=0, day=0
步骤5(对象头):每个对象都需要一些元数据来支持 JVM 的功能(如垃圾回收、同步等)
步骤6(构造方法):这是程序员可以控制的步骤,通过 this 给成员变量赋予想要的值
在步骤6(构造方法执行)时:
- 对象已经存在(步骤1-5已经完成)
- 内存已经分配,对象头已经设置好
- this 引用已经存在,指向这块内存
- 构造方法中的 this 就是在这个时候被传入的
这就是为什么我们可以在构造方法中使用 this 来给成员变量赋值:
public Date(int year, int month, int day) {
// this 已经指向步骤1-5创建好的对象
this.year = year; // 给对象的year字段赋值
this.month = month; // 给对象的month字段赋值
this.day = day; // 给对象的day字段赋值
}




