类与对象

什么是类和对象

一句话理解:类是图纸,对象是按图纸造出来的房子

你画了一份”别墅设计图”(类),然后按照这张图可以盖出很多栋别墅(对象),每栋别墅的颜色、门牌号可以不同,但结构一样

类定义了”有什么”(属性/字段)和”能干什么”(方法/行为),对象是类的一个具体实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 这是一个"类"——设计图
public class Dog {
String name; // 属性:名字
int age; // 属性:年龄

void bark() { // 方法:行为
System.out.println(name + "在汪汪叫!");
}
}

// 这是"对象"——按图纸造出来的具体的狗
Dog myDog = new Dog();
myDog.name = "旺财";
myDog.age = 3;
myDog.bark(); // 输出:旺财在汪汪叫!

类的定义:字段、方法、构造器

字段(Field)= 属性:描述这个东西”有什么”,比如狗有名字、年龄

方法(Method)= 行为:描述这个东西”能干什么”,比如狗能叫、能跑

构造器(Constructor):创建对象时自动调用的特殊方法,用来做”初始化”工作,后面详细讲

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Student {
// ---- 字段(属性) ----
String name;
int age;
String school;

// ---- 构造器 ----
public Student(String name, int age) {
this.name = name;
this.age = age;
}

// ---- 方法(行为) ----
public void introduce() {
System.out.println("我叫" + name + ",今年" + age + "岁");
}
}

创建对象:new 关键字

new 就像在工厂里按下”开工”按钮,JVM 会在堆内存里给你造一个新对象出来

语法:类名 变量名 = new 类名();

1
2
3
4
5
6
7
8
9
10
11
@Test
public void testNewObject() {
Student s1 = new Student("小明", 18);
Student s2 = new Student("小红", 17);

s1.introduce(); // 我叫小明,今年18岁
s2.introduce(); // 我叫小红,今年17岁

// s1 和 s2 是两个不同的对象,互不影响
System.out.println(s1 == s2); // false
}

this 关键字

**this 就是”我自己”**——在方法内部,this 指向当前正在调用这个方法的那个对象

最常见的用途:区分字段和参数同名的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Cat {
String name;

public Cat(String name) {
// 参数 name 和字段 name 同名了!
// this.name 是"我的字段",name 是"传进来的参数"
this.name = name;
}

public void sayHi() {
// this 可以省略,但写上更清晰
System.out.println("我是" + this.name);
}
}

this 还能用来调用本类的其他构造器:this(参数)(必须放在构造器第一行)

构造器:无参构造、有参构造、构造器重载

构造器就是”出厂设置”——对象被 new 出来的那一刻,自动执行

无参构造器:如果你什么构造器都不写,Java 会偷偷送你一个空的无参构造器;但只要你写了任何一个构造器,这个”赠品”就没了

有参构造器:创建对象时直接传值,省得一个个赋值

构造器重载:同一个类可以有多个构造器,参数不同就行(这就是重载)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void testConstructor() {
// 无参构造
Phone p1 = new Phone();
p1.brand = "华为";
p1.price = 5999;

// 有参构造——一步到位
Phone p2 = new Phone("苹果", 8999);

// 另一个有参构造——只传品牌
Phone p3 = new Phone("小米");

p1.showInfo(); // 华为,5999元
p2.showInfo(); // 苹果,8999元
p3.showInfo(); // 小米,0元
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Phone {
String brand;
int price;

// 无参构造
public Phone() {
}

// 有参构造:两个参数
public Phone(String brand, int price) {
this.brand = brand;
this.price = price;
}

// 有参构造:一个参数(构造器重载)
public Phone(String brand) {
this.brand = brand;
this.price = 0;
}

public void showInfo() {
System.out.println(brand + "," + price + "元");
}
}

常见坑:写了有参构造后忘了补无参构造,然后 new Phone() 报错

方法重载(Overload)

同一个类里,方法名相同,参数列表不同(参数个数不同、类型不同、顺序不同),就叫重载

跟返回值无关!只看方法名 + 参数列表

你可以理解为:同一个动作的不同版本,比如”打印”可以打印整数、打印字符串、打印浮点数

1
2
3
4
5
6
7
@Test
public void testOverload() {
Calculator calc = new Calculator();
System.out.println(calc.add(1, 2)); // 3(调用 int 版本)
System.out.println(calc.add(1.5, 2.5)); // 4.0(调用 double 版本)
System.out.println(calc.add(1, 2, 3)); // 6(调用三参数版本)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Calculator {
// 两个 int 相加
public int add(int a, int b) {
return a + b;
}

// 两个 double 相加(参数类型不同 → 重载)
public double add(double a, double b) {
return a + b;
}

// 三个 int 相加(参数个数不同 → 重载)
public int add(int a, int b, int c) {
return a + b + c;
}
}
对比项 方法重载(Overload)
发生位置 同一个类
方法名 必须相同
参数列表 必须不同(个数/类型/顺序)
返回值 无要求,不参与判断
关键词 无特殊关键词

重载 vs 重写的完整对比见 封装继承多态

栈 vs 堆内存

一句话理解:栈是桌面(放遥控器/便签),堆是仓库(放大家电)

栈(Stack):存放基本类型的值、方法调用、局部变量、对象的引用(地址)

堆(Heap):存放 new 出来的对象本身

引用就像一张写着仓库地址的便签条,贴在桌面上(栈),指向仓库里的真正货物(堆)

文字版内存图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌──────────────── 栈内存 (Stack) ────────────────┐
│ │
│ int age = 18; → age: [18] │
│ double score = 95.5; → score: [95.5] │
│ Student s1 = new ...; → s1: [0x001] ──────┐ │
│ Student s2 = new ...; → s2: [0x002] ────┐ │ │
│ │ │ │
└─────────────────────────────────────────────│─│──┘
│ │
┌──────────────── 堆内存 (Heap) ────────────────┐
│ │ │ │
│ [0x002] Student对象 ◄────────────┘ │ │
name: "小红" │ │
age: 17 │ │
│ │ │
│ [0x001] Student对象 ◄──────────────┘ │
name: "小明"
age: 18
│ │
└────────────────────────────────────────────────┘

关键点

基本类型(int, double, boolean 等)→ 值直接存在栈上,参考 变量与数据类型

对象 → 栈上存引用(地址),堆上存对象本身

s1 == s2 比较的是栈上的地址,不是对象的内容!

1
2
3
4
5
6
7
8
9
10
11
@Test
public void testMemory() {
int a = 10;
int b = 10;
System.out.println(a == b); // true(比较值)

Student s1 = new Student("小明", 18);
Student s2 = new Student("小明", 18);
System.out.println(s1 == s2); // false!(比较地址,两个不同对象)
// 要比较内容,需要重写 equals() 方法,后面会学
}

static 关键字:静态变量、静态方法

一句话理解:static 的东西属于”类本身”,不属于某个具体对象

类比:学校的”校训”是 static 的(属于学校,所有学生共享),而”姓名”不是 static 的(每个学生不同)

静态变量:所有对象共享同一份,改了一个全都变

静态方法:不需要创建对象就能调用,用 类名.方法名() 直接调

还记得 [hello world](/2026/04/04/hello world/) 里的 public static void main 吗?

main 方法是 static 的,因为程序启动时还没有任何对象存在,JVM 必须通过类名直接调用它

public:公开的,JVM 能访问

static:静态的,不需要 new 对象就能调用

void:没有返回值

main:固定的方法名,JVM 约定的程序入口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
public void testStatic() {
// 静态变量:通过类名访问
Chinese.nation = "中国";

Chinese c1 = new Chinese("小明");
Chinese c2 = new Chinese("小红");

// 所有对象共享同一个 nation
System.out.println(c1.name + " - " + Chinese.nation); // 小明 - 中国
System.out.println(c2.name + " - " + Chinese.nation); // 小红 - 中国

// 静态方法:通过类名直接调用,不需要 new 对象
Chinese.showNation(); // 我们都是中国人
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Chinese {
String name; // 实例变量:每个对象各一份
static String nation; // 静态变量:所有对象共享一份

public Chinese(String name) {
this.name = name;
}

// 静态方法
public static void showNation() {
System.out.println("我们都是" + nation + "人");
// 注意:静态方法里不能用 this,也不能直接访问实例变量
}
}
对比项 实例成员 静态成员(static)
归属 属于对象 属于
内存 每个对象各一份 全局只有一份
访问方式 对象名.xxx 类名.xxx(推荐)
能否访问实例成员 不能(没有 this)
何时加载 new 对象时 类加载时(更早)

常见坑:在 static 方法里写 this.xxx,编译直接报错,因为 static 方法不属于任何对象,没有”自己”

包(package)的概念和 import

一句话理解:package 就是文件夹,用来给类分门别类,避免重名冲突

就像你电脑里有”工作”文件夹和”生活”文件夹,里面都可以有一个叫 记录.txt 的文件,但不会搞混

包名规范:公司域名倒写 + 项目名 + 模块名,全小写

例如:com.taobao.order.service

import:要用别的包里的类,就得先 import 导入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 声明当前类在哪个包
package com.example.demo;

// 导入别的包里的类
import java.util.ArrayList;
import java.util.Scanner;

// 导入整个包下所有类(不推荐,不清晰)
// import java.util.*;

public class MyApp {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Scanner sc = new Scanner(System.in);
}
}

注意java.lang 包下的类(String, System, Math 等)不需要 import,Java 自动导入

IDE使用(IDEA) 里,通常 IDE 会帮你自动 import,按 Alt + Enter 即可

常见坑汇总

说明 正确做法
忘写无参构造器 写了有参构造后,Java 不再赠送无参构造 手动补上无参构造
== 比较对象 比较的是地址,不是内容 .equals() 比较内容
static 方法用 this static 没有”自己”,编译报错 去掉 this,或改成实例方法
重载只改返回值 只改返回值不算重载,编译报错 必须改参数列表
包名大写 不规范,虽然能跑但同事会打你 全小写,用点分隔

练习题

练习1:定义一个 Book

属性:书名(String)、价格(double)、作者(String)

构造器:无参 + 全参

方法:showInfo() 打印书籍信息

创建 2 个 Book 对象并调用 showInfo()

练习2:给上面的 Book 类加一个 static 变量 bookCount,每创建一个 Book 对象,bookCount 就 +1,最后打印总共创建了几本书

练习3(思考题):下面的代码输出什么?为什么?

1
2
3
4
Student s1 = new Student("小明", 18);
Student s2 = s1;
s2.name = "小红";
System.out.println(s1.name); // 输出什么?

提示:画一下栈和堆的内存图,想想 s1 和 s2 指向的是同一个对象还是两个对象

学完这篇,接下来去看 封装继承多态,了解面向对象的三大核心思想


上一章 目录 下一章
数组 java基础 封装继承多态