【六. Java面向对象编程入门指南】
6. java面向对象基础
6.1 设计对象并使用
6.1.1 类与对象的本质理解
-
类的本质:类是对现实事物的模板化抽象。好比 “图纸”
-
对象的本质:对象是类的具体实例。好比根据图纸造出的 “真实汽车”
6.1.2 类的定义细节
-
成员变量的默认值规则
数据类型 默认值 示例(未赋值时) 整数类型(如 int) 0 int age; → 0 浮点类型(如 double) 0.0 double price; → 0.0 字符类型(char) ‘\u0000’(空字符) char gender; → 空字符 布尔类型(boolean) false boolean isStudent; → false 引用类型(如 String) null String name; → null -
成员方法的注意事项:
-
无需
static
修饰:因为成员方法属于对象层面的行为,依赖具体对象调用 -
可直接访问成员变量:成员方法内部可直接使用
成员变量名
,无需 “对象名.” 前缀(本质是通过this
隐式调用)
-
6.1.3 对象的创建与使用深度解析
-
创建对象的底层逻辑:
-
分配堆内存:
new 类名()
会在堆内存中开辟一块空间,存储对象的属性值和方法引用 -
初始化属性:成员变量按默认值初始化(如
int
为 0,String
为null
) -
返回对象地址:将堆内存地址赋值给栈中的引用变量(如
Phone p = ...
中的p
)
-
-
访问成员的底层逻辑
-
访问属性:
对象名.属性名
→ 通过引用变量找到堆内存中的对象,直接操作属性值例子:p.brand = "小米"
→ 找到p
指向的对象,修改其brand
属性为 “小米” -
调用方法:
对象名.方法名()
→ 通过引用变量找到对象,根据方法引用找到方法区中的代码执行例子:p.call()
→ 执行方法区中Phone类
的call()
方法逻辑
-
-
例子:
public class Car { // 成员变量(属性) String color; // 颜色,默认null int speed; // 速度,默认0 boolean isStart; // 是否启动,默认false // 成员方法(行为) public void start() { // 启动车 isStart = true; System.out.println("汽车已启动,当前速度:" + speed); } public void accelerate(int addSpeed) { // 加速 if (!isStart) { // 未启动时无法加速 System.out.println("请先启动汽车!"); return; } speed += addSpeed; System.out.println("加速后速度:" + speed + "km/h"); } } // 使用对象 public class CarDemo { public static void main(String[] args) { // 1. 创建对象 Car c = new Car(); // 2. 访问属性(直接操作,未封装时) System.out.println("初始颜色:" + c.color); // 输出:null c.color = "红色"; // 赋值 System.out.println("修改后颜色:" + c.color); // 输出:红色 // 3. 调用方法 c.start(); // 输出:汽车已启动,当前速度:0 c.accelerate(50); // 输出:加速后速度:50km/h c.accelerate(-20); // 合法,速度减为30km/h } }
6.2 封装
6.2.1 为什么需要封装?
-
场景举例:若直接暴露成员变量(如
public int age;
),外部代码可能赋值非法数据(如年龄 - 5 岁),导致逻辑错误 -
封装的优势:
-
数据安全:通过
setter
方法添加校验逻辑,防止非法数据 -
结构清晰:外部只需通过固定方法操作数据,无需关心内部实现
-
可维护性:修改内部逻辑时(如调整年龄范围),只需修改
setter
,不影响外部调用
-
6.2.2 private
关键字的深度用法
私有化成员变量的步骤:
-
声明时加
private
:private String name; // 私有化姓名,外部不可直接访问
-
提供
public
的getter/setter
方法:
* `getter`方法:用于读取属性值,命名规则`getXxx()`(布尔类型用`isXxx()`)public String getName() { // 获取姓名 return name; }
setter
方法:用于设置属性值,可添加校验逻辑,命名规则setXxx(参数)
public void setAge(int age) { // 设置年龄 if (age < 0 || age > 150) { // 年龄必须在0-150之间 System.out.println("年龄非法!请重新设置。"); return; // 终止方法,不赋值 } this.age = age; // 合法时赋值 }
封装 vs 不封装的对比:
场景 | 未封装(public 成员变量) | 封装(private+getter/setter) |
---|---|---|
赋值年龄 - 5 岁 | 直接赋值成功,逻辑错误 | setAge(-5)触发校验,拒绝赋值 |
代码维护(修改年龄范围) | 需修改所有赋值代码 | 只需修改setAge方法的校验逻辑 |
外部调用复杂度 | 直接操作属性,耦合度高 | 通过固定方法操作,结构清晰 |
6.2.3 封装的最佳实践
-
所有成员变量必须私有化:除非有特殊需求(如工具类的静态变量),否则绝不使用
public
修饰成员变量 -
setter
方法必填校验逻辑:根据业务规则对输入数据进行合法性检查(如年龄、邮箱格式、密码长度等) -
getter
方法保持简单:通常仅返回属性值,不添加复杂逻辑(复杂逻辑应封装到其他业务方法中)
6.3 this关键字
6.3.1 this
的三大作用
- 区分同名变量:当方法参数与成员变量同名时,用
this
明确指代成员变量
public void setName(String name) { this.name = name; // this.name是成员变量,name是方法参数 }
- 调用成员方法:在成员方法内部,可通过
this.方法名()
调用本类的其他成员方法(通常可省略this.
)
public void study() { System.out.println("开始学习..."); this.doHomework(); // 调用本类的doHomework方法 } public void doHomework() { System.out.println("正在写作业..."); }
- 调用构造方法:在构造方法中,用
this(参数)
调用本类的其他构造方法(必须位于构造方法第一行)
public class Student { private String name; private int age; // 无参构造调用有参构造(初始化默认值) public Student() { this("匿名学生", 0); // 调用Student(String, int)构造 } // 有参构造 public Student(String name, int age) { this.name = name; this.age = age; } }
6.3.2 this
的本质:当前对象的引用
-
当对象
stu
调用方法setName("张三")
时,方法内部的this
就代表stu
对象 -
栈内存:stu --> 堆地址0x123 堆内存(对象): Student { name: null, age: 0, setName(String name) { this.name = name; // this指向stu对象,即给stu的name赋值 } }
6.4 构造方法
6.4.1 构造方法的核心特性
- 名称必须与类名完全一致(包括大小写):
public class Dog { public Dog() { // 构造方法名必须是Dog } }
- 没有返回值类型(连
void
也不能写):
public Dog() { // 正确 } public void Dog() { // 错误!这是普通方法,不是构造方法 }
- 创建对象时必须调用:每次使用
new
关键字创建对象时,必然会执行一个构造方法(默认无参或自定义构造)
6.4.2 默认构造方法与自定义构造的关系
-
默认构造的生成条件:当类中没有任何构造方法时,系统自动生成无参构造(
public 类名() {}
)。 -
自定义构造的影响:一旦定义了任意构造方法(无参或有参),系统不再生成默认无参构造
public class Cat { // 自定义有参构造 public Cat(String name) { } // 此时系统不会生成无参构造,以下代码会报错! // Cat c = new Cat(); // 错误:无法找到无参构造 }
- 推荐做法:无论是否需要,都手动添加无参构造,避免后续扩展时因缺少构造方法报错
6.4.3 构造方法的重载规则
- 重载条件:构造方法名相同,参数列表不同(参数个数、类型、顺序不同),与返回值无关(构造无返回值)
public class Person { public Person() {} // 无参构造 public Person(String name) {} // 1个参数(String) public Person(int age) {} // 1个参数(int),与上一个构成重载(类型不同) public Person(String name, int age) {} // 2个参数 }
- 禁止重载的情况:参数列表完全相同,即使方法体不同,也无法构成重载(编译报错)
6.5 标准JavaBean
6.5.1 JavaBean 的完整规范
- 成员变量:全部
private
修饰,禁止外部直接访问
private String name; private int age;
- 构造方法:提供无参构造和全参构造
public Student() { //无参构造} public Student(String name, int age) { // 全参构造 this.name = name; this.age = age; }
getter/setter
方法:为每个成员变量提供对应方法
public String getName() { return name; } public void setName(String name) {this.name = name;}
- 可选:实现
Serializable
接口(用于对象序列化,如网络传输、持久化存储)
6.5.2 快速生成 JavaBean 的技巧(IDE 工具)
-
IDEA 操作:
-
在类中按
Alt+Insert
-
选择
Constructor
/Getter and Setter
-
勾选需要的成员变量
-
生成代码
-
6.6 对象内存图
6.6.1 单个对象的内存分布(以Student
类为例)
栈内存:s --> 堆地址0x100
堆内存:
Student { name: "李四", // 通过setName赋值 age: 20, // 通过setAge赋值 show(): 方法引用 --> 方法区中的show()代码
}
方法区:
Student类的成员方法(show()等)存储在此
6.6.2 多个对象的内存关系
栈内存:s1 --> 0x100,s2 --> 0x200
堆内存:
0x100: Student { name: "A", age: 18 }
0x200: Student { name: "B", age: 20 }
方法区:
Student类的成员方法(show()等)被所有对象共享
6.7 成员变量与局部变量的区别
对比维度 | 成员变量 | 局部变量 |
---|---|---|
定义位置 | 类中方法外 | 方法内或方法参数 |
内存位置 | 堆内存(属于对象) | 栈内存(属于方法栈帧) |
生命周期 | 随对象创建而存在,随对象销毁而消失 | 随方法调用而存在,随方法结束而消失 |
默认值 | 有默认值(如int为 0,String为null) | 无默认值,必须先赋值后使用 |
作用域 | 整个类范围内可见 | 仅限方法内或代码块内可见 |
public class Demo { int memberVar = 10; // 成员变量,默认值10 public void method() { int localVar = 20; // 局部变量,无默认值,必须赋值 System.out.println(memberVar); // 正确,成员变量可在方法内访问 // System.out.println(localVar2); 错误,未定义 } }