一、为什么需要类与对象?
没有类的问题
想象你要描述一只猫:
// 用零散变量描述一只猫
String cat1Name = "咪咪";
int cat1Age = 3;
String cat1Color = "橘色";
String cat2Name = "小花";
int cat2Age = 2;
String cat2Color = "黑白";
// 再描述一只狗?又要加一堆变量...
String dog1Name = "旺财";
int dog1Age = 5;
String dog1Breed = "金毛";
痛点:
| 问题 | 说明 |
|---|---|
| 变量散落 | 猫的名字、年龄、颜色分散在不同变量里,没有"归属感" |
| 无法归类 | 看不出 cat1Name 和 cat2Name 是同一类东西 |
| 传参麻烦 | 把一只猫传给方法,要传3个参数 |
| 无法复用 | 每新增一只猫都要重新定义一组变量 |
有类的写法:
class Cat {// 定义"猫"这个类型
String name;
int age;
String color;
void meow() {
System.out.println(name + ":喵喵喵!");}}// 使用
Cat cat1 = new Cat();
cat1.name = "咪咪";
cat1.age = 3;
cat1.color = "橘色";
Cat cat2 = new Cat();
cat2.name = "小花";
cat2.age = 2;
cat2.color = "黑白";
cat1.meow(); // 咪咪:喵喵喵!
好处:把相关的数据和操作打包在一起,像现实中的"模板"一样,可以批量生产。
二、什么是类?什么是对象?
核心类比:图纸 vs 实物
| 概念 | 现实类比 | Java中的角色 |
|---|---|---|
| **类(Class)** | 建筑图纸 | 模板/蓝图,定义"长什么样" |
| **对象(Object)** | 按图纸建好的房子 | 实例,真正存在的东西 |
| 成员变量 | 图纸上标注的"房间数、面积" | 对象的特征(属性) |
| 成员方法 | 图纸上标注的"功能:住人、采光" | 对象能做的事(行为) |
一句话:类是"设计图",对象是"按图造出来的东西"。一张图纸可以造很多房子,一个类可以创建很多对象。
类的定义语:
修饰符 class 类名
// 成员变量(属性)
数据类型 变量名;
// 成员方法(行为)
修饰符 返回值类型 方法名(参数) {
// 方法体
完整示例:定义一个 Person 类
public class Person {
// ========== 成员变量(属性) ==========
String name; // 姓名
int age; // 年龄
char gender; // 性别
// ========== 成员方法(行为) ==========
public void introduce() {
System.out.println("我叫" + name + ",今年" + age + "岁");}
public void birthday() {
age++;
System.out.println("生日快乐!现在" + age + "岁了");}}
创建对象(使用类)
public class TestPerson {
public static void main(String\[\] args) {
// 创建 Person 对象
Person p1 = new Person();
// 给成员变量赋值
p1.name = "张三";
p1.age = 20;
p1.gender = '男';
// 调用方法
p1.introduce(); // 我叫张三,今年20岁
p1.birthday(); // 生日快乐!现在21岁了
// 再创建一个对象
Person p2 = new Person();
p2.name = "李四";
p2.age = 18;
p2.introduce(); // 我叫李四,今年18岁}}
内存图解:
栈内存 堆内存
───── ─────
p1 ──→ 0x100 ──→ Person{name="张三", age=20, gender='男'}
p2 ──→ 0x200 ──→ Person{name="李四", age=18, gender='男'}
每个 new 都会在堆里创建一个独立的对象,互不影响。
三、构造方法(Constructor)
什么是构造方法?
构造方法是在创建对象时自动调用的方法,用来初始化对象。
就像买手机时,出厂设置会自动帮你完成一些初始化(语言、时区、欢迎界面),你不需要手动一个个设置。
构造方法的特点
| 特点 | 说明 |
|---|---|
| 方法名必须和类名完全相同 | 包括大小写 |
| 没有返回值类型 | 连 void 都不能写 |
| 创建对象时自动调用 | new 类名() 就是在调用构造方法 |
| 可以重载 | 一个类可以有多个构造方法 |
默认构造方法
如果你一个构造方法都没写,Java 会自动给你一个:
java
public class Person {
String name;
int age;
// 没写构造方法
}
// 编译时 Java 自动补上:
// public Person() {} ← 无参构造方法,啥也不干
所以你可以写 new Person() 而不会报错。
一旦写了构造方法,默认的就没了
java
public class Person {
String name;
int age;
// 写了有参构造
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
// 此时再写 new Person() → ❌ 编译报错!
// 因为默认的无参构造方法被"覆盖"了
⚠️ 最佳实践:如果你写了有参构造方法,同时手动补上无参构造方法,避免别人 new Person() 时报错。
构造方法重载
java
public class Person {
String name;
int age;
String idCard;
// 无参构造
public Person() {
System.out.println("无参构造被调用");
}
// 有参构造:只初始化姓名和年龄
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("有参构造(name, age)被调用");
}
// 有参构造:初始化全部属性
public Person(String name, int age, String idCard) {
this.name = name;
this.age = age;
this.idCard = idCard;
System.out.println("有参构造(全参)被调用");
}
public void introduce() {
System.out.println("我叫" + name + ",今年" + age + "岁");
}
}
// 测试
public class Test {
public static void main(String\[\] args) {
Person p1 = new Person(); // 调用无参
Person p2 = new Person("张三", 20); // 调用双参
Person p3 = new Person("李四", 18, "110xxx"); // 调用三参
p2.introduce();
p3.introduce();
}
}
输出:
无参构造被调用
有参构造(name, age)被调用
有参构造(全参)被调用
我叫张三,今年20岁
我叫李四,今年18岁
四、this 关键字(重点)
this 是什么?
this 代表"当前对象"的引用------谁调用方法,this 就是谁。
生活类比
想象你在教室里,老师说"请把你的书拿出来":
"你"就是 this
张三听到 → this = 张三的书
李四听到 → this = 李四的书
为什么需要 this?
问题场景:成员变量和局部变量同名
java
public class Person {
String name; // 成员变量
int age; // 成员变量
public void setName(String name) {
name = name; // ❌ 哪个 name 是哪个?
}
}
Java 的就近原则:name = name 意思是"把参数 name 赋值给参数 name 自己",成员变量完全没被碰。
使用 this 解决问题
java
public class Person {
String name;
int age;
// 构造方法中使用 this
public Person(String name, int age) {
this.name = name; // this.name → 成员变量,name → 参数
this.age = age; // this.age → 成员变量,age → 参数
}
// setter 中使用 this
public void setName(String name) {
this.name = name; // 明确告诉编译器:左边是成员变量
}
public void setAge(int age) {
this.age = age;
}
public void introduce() {
System.out.println("我叫" + this.name + ",今年" + this.age + "岁");
// 这里 this. 可以省略,编译器会自动加
// 写 this.name 更清晰,尤其在复杂代码中
}
}
this 的三种用法总结
this 调用其他构造方法(进阶技巧)
| 用法 | 语法 | 说明 |
|---|---|---|
| 区分成员变量和局部变量 | this.成员变量 = 局部变量 | 最常用 |
| 调用本类其他构造方法 | this(参数) | 必须写在构造方法第一行 |
| 在成员方法中指代当前对象 | this.方法名() | 较少用,编译器通常自动加 |
java
public class Person {
String name;
int age;
String idCard;
// 无参构造 → 调用双参构造,给默认值
public Person() {
this("未知", 0); // 调用 Person(String, int)
}
public Person(String name, int age) {
this(name, age, "未登记"); // 调用全参构造
}
public Person(String name, int age, String idCard) {
this.name = name;
this.age = age;
this.idCard = idCard;
}
}
⚠️ 注意:this(参数) 必须写在构造方法的第一行,且不能和 super() 同时出现。
五、综合练习(手把手)
练习1:定义一个 Student 类
需求:
成员变量:name(姓名)、score(成绩)
构造方法:无参、有参
方法:study() 打印学习信息、showInfo() 展示信息
在构造方法和 setter 中使用 this
java
public class Student {
// ========== 成员变量 ==========
private String name; // 后面学封装时会讲 private
private int score;
// ========== 构造方法 ==========
// 无参构造
public Student() {
this("匿名", 0); // 调用有参构造给默认值
}
// 有参构造
public Student(String name, int score) {
this.name = name; // this 区分成员变量和参数
this.score = score;
}
// ========== 成员方法 ==========
public void study() {
System.out.println(this.name + "正在学习,当前成绩:" + this.score);
this.score += 5; // 学习后成绩+5
System.out.println("学习后成绩提升为:" + this.score);
}
public void showInfo() {
System.out.println("学生信息:姓名=" + this.name + ",成绩=" + this.score);
}
// ========== setter/getter(使用 this) ==========
public void setName(String name) {
this.name = name;
}
public void setScore(int score) {
if (score < 0 || score > 100) {
System.out.println("成绩必须在0-100之间!");
return;
}
this.score = score;
}
public String getName() {
return this.name;
}
public int getScore() {
return this.score;
}
}
// 测试类
public class TestStudent {
public static void main(String\[\] args) {
// 使用无参构造
Student s1 = new Student();
s1.showInfo(); // 匿名,0分
// 使用有参构造
Student s2 = new Student("小明", 85);
s2.showInfo(); // 小明,85分
// 调用方法
s2.study(); // 学习后成绩+5
s2.showInfo(); // 小明,90分
// 使用 setter 修改
s2.setName("大明");
s2.setScore(95);
s2.showInfo(); // 大明,95分
// 测试成绩校验
s2.setScore(200); // 成绩必须在0-100之间!
}
}
练习2:定义一个 Car 类
需求:
成员变量:brand(品牌)、price(价格)、color(颜色)
构造方法:无参、有参、全参
方法:drive() 打印行驶信息
在构造方法中用 this 调用其他构造方法
java
public class Car {
String brand;
double price;
String color;
// 无参构造
public Car() {
this("未知品牌", 0.0, "白色"); // 调用全参构造
}
// 双参构造
public Car(String brand, double price) {
this(brand, price, "白色"); // 调用全参构造,默认白色
}
// 全参构造
public Car(String brand, double price, String color) {
this.brand = brand;
this.price = price;
this.color = color;
}
public void drive() {
System.out.println(this.color + "的" + this.brand +
"正在行驶,价格" + this.price + "万");
}
public void showInfo() {
System.out.println("品牌:" + brand + ",价格:" + price +
"万,颜色:" + color);
}
}
// 测试
public class TestCar {
public static void main(String\[\] args) {
Car c1 = new Car(); // 无参
Car c2 = new Car("丰田", 15.0); // 双参
Car c3 = new Car("宝马", 45.0, "黑色"); // 全参
c1.drive();
c2.drive();
c3.drive();
}
}
六、⚠️ 避坑指南
坑1:成员变量未初始化就使用
java
public class Person {
int age;
String name;
public void test() {
System.out.println(age); // 0(int 默认值)
System.out.println(name); // null(引用类型默认值)
}
}
规则:成员变量有默认值(int → 0,double → 0.0,boolean → false,引用类型 → null)。局部变量没有默认值,必须手动初始化。
坑2:构造方法写了返回值类型
java
public void Person() { } // ❌ 这不是构造方法!这是普通方法,名字恰好叫 Person
构造方法不能有返回值类型,连 void 都不行。
坑3:this 在静态方法中使用
java
public class Person {
String name;
public static void test() {
System.out.println(this.name); // ❌ 编译报错!static 方法中没有 this
}
}
原因:static 方法属于类,不属于任何对象。没有对象就没有 this。后面学 static 时会深入讲。
坑4:构造方法互相调用形成死循环
java
public class Person {
public Person() {
this("未知"); // 调用有参
}
public Person(String name) {
this(); // ❌ 又调用无参 → 无限循环 → 编译报错
}
}
编译器会检测到这种循环并拒绝编译。
坑5:在构造方法中调用普通方法(潜在风险)
java
public class Person {
String name;
public Person(String name) {
this.name = name;
introduce(); // 构造方法中调用方法 → 如果子类重写 introduce 会有问题
}
public void introduce() {
System.out.println("我叫" + name);
}
}
构造方法中调用可重写的方法,在继承场景下可能导致子类对象未完全初始化就被使用。建议构造方法中只做赋值操作。
七、总结速查表
| 概念 | 核心要点 |
|---|---|
| 类 | 模板/蓝图,定义属性和行为 |
| 对象 | 类的实例,用 new 创建 |
| 成员变量 | 对象的属性,有默认值 |
| 构造方法 | 创建对象时自动调用,用于初始化 |
| 构造方法名 | 必须和类名完全相同 |
| 构造方法重载 | 一个类可以有多个构造方法,参数列表不同 |
| this | 代表当前对象 |
| this.成员变量 | 区分成员变量和局部变量同名 |
| this(参数) | 调用本类其他构造方法,必须写在第一行 |
| 默认构造方法 | 没写任何构造方法时,Java 自动提供无参构造 |
八、课后练习
练习1(基础):定义一个 Book 类
成员变量:title(书名)、author(作者)、price(价格)
构造方法:无参、有参
方法:showInfo() 打印图书信息
在构造方法和 setter 中使用 this
练习2(进阶):定义一个 BankAccount 类
成员变量:accountNumber(账号)、balance(余额)
构造方法:无参(默认余额0)、有参
方法:deposit(double amount) 存款、withdraw(double amount) 取款(需判断余额是否充足)
在构造方法中使用 this 调用其他构造方法
练习3(挑战):定义一个 Circle 类
成员变量:radius(半径)
构造方法:无参(默认半径1)、有参
方法:getArea() 计算面积、getPerimeter() 计算周长
在构造方法中校验半径不能为负数