【Java零基础30天挑战·Day7】告别流水账:一文彻底搞懂Java面向对象
告别流水账:一文彻底搞懂Java面向对象
📝 前言:在前面的几篇文章中,我们学习了分支、循环和方法,这些属于面向过程的思维方式——也就是“第一步做什么,第二步做什么”。这在写小脚本时很管用,但当软件规模变大,面向过程的代码就会变成一团乱麻。
Java的真正魅力在于面向对象编程(OOP)。今天,我们就来拆解这个听起来高大上,其实无比贴近生活的编程思想!
🤔 一、 面向过程 vs 面向对象
一个经典的例子:把大象装进冰箱
-
面向过程(注重步骤):
- 打开冰箱门
- 把大象塞进去
- 关上冰箱门
*代表语言:C语言*
-
面向对象(注重角色):
- 找出三个对象:冰箱、大象、人
- 冰箱有个功能:开门、关门
- 大象有个功能:走进去
- 人有个功能:指挥冰箱和大象
*代表语言:Java*
一句话总结:面向过程是“自己动手做”,面向对象是“找懂行的人来做”。
🏭 二、 类与对象:图纸与实体
面向对象的核心是“找对象”,但对象从哪来?得先有类。
- 类:是一类事物的抽象描述,是图纸。
- 对象:是类的具体实例,是按图纸造出来的真车。
// 1. 定义类(画图纸)
public class Phone {
// 属性(名词):描述事物长什么样
String brand; // 品牌
double price; // 价格
// 方法(动词):描述事物能干什么
public void call() {
System.out.println("用" + brand + "打电话...");
}
}
// 2. 创建对象(按图纸造手机)
public static void main(String[] args) {
Phone myPhone = new Phone(); // new关键字,在堆内存中造出一个真手机
myPhone.brand = "华为"; // 给属性赋值
myPhone.price = 5999.0;
myPhone.call(); // 调用方法:用华为打电话...
}
💡 灵魂拷问:
Phone p1 = new Phone(); Phone p2 = p1;请问p1和p2是同一个对象吗?
答案:是的!p1和p2只是栈内存中的两个遥控器,它们指向了堆内存中同一个电视(对象)。用p2改了品牌,p1调用时品牌也会变。
🛡️ 三、 封装:我的隐私你别碰
如果外界能随意修改对象的属性,比如把手机价格改成 -100,这显然不合理。封装就是给属性加上保护锁,只暴露安全的访问通道。
封装三步走:
- 属性私有化(加
private关键字) - 提供公共的
get方法(对外提供读权限) - 提供公共的
set方法(对外提供写权限,并加入逻辑校验)
public class Phone {
private double price; // 外界不能直接访问了
// 提供set方法,并加入安全校验
public void setPrice(double price) {
if (price < 0) {
System.out.println("价格不能为负数!默认设为0");
this.price = 0; // this代表当前对象
} else {
this.price = price;
}
}
// 提供get方法
public double getPrice() {
return price;
}
}
避坑:以后写实体类,属性永远写
private,无脑生成getter/setter是好习惯!
🧬 四、 继承:薪火相传,站在巨人的肩膀上
当多个类有相同的属性和方法时,我们把这些共性抽取出来放到一个“父类”中,其他“子类”通过 extends 继承,实现代码复用。
// 1. 父类(泛指动物)
public class Animal {
public void eat() {
System.out.println("动物在吃饭...");
}
}
// 2. 子类继承父类(is-a关系:狗是动物)
public class Dog extends Animal {
public void bark() {
System.out.println("汪汪汪!");
}
}
// 3. 测试
Dog dog = new Dog();
dog.eat(); // 狗可以直接调用爸爸的方法
dog.bark(); // 也有自己特有的方法
方法重写(Override):如果子类对父类的方法不满意,可以重新写一遍!
public class Dog extends Animal {
@Override // 注解,用来检测是否正确重写
public void eat() {
System.out.println("狗在啃骨头..."); // 覆盖了父类的吃饭逻辑
}
}
⚠️ 避坑指南:继承尽量是单继承(Java不允许多继承,只能有一个亲爹)。且不要为了复用代码强行继承(比如让“鸟”继承“飞机”,它们只是都能飞,没有is-a关系)。
🎭 五、 多态:同一指令,千姿百态
多态是面向对象最核心、最难的特性。 通俗地说:同一种方法调用,由于对象不同,产生了不同的行为。
多态的三个前提:
- 有继承关系
- 有方法重写
- 父类引用指向子类对象(
Animal a = new Dog();)
public class Test {
public static void main(String[] args) {
// 父类引用指向子类对象
Animal a1 = new Dog();
Animal a2 = new Cat();
// 编译看左边,运行看右边
a1.eat(); // 输出:狗在啃骨头...
a2.eat(); // 输出:猫在吃鱼...
}
}
多态的巨大威力(扩展性):
如果不使用多态,你每加一种动物,就要改一次喂食方法。有了多态,参数写父类,以后加再多动物,代码一行都不用改!
// 没有多态的写法:要写无数个重载方法
public void feedDog(Dog d) { d.eat(); }
public void feedCat(Cat c) { c.eat(); }
// 多态的写法:一个方法搞定天下!
public void feedAnimal(Animal a) {
a.eat(); // 传什么动物,就调用什么动物的eat
}
⚠️ 多态的短板:不能调用子类特有的方法!
Animal a = new Dog();此时a只能调用Animal里有的方法,不能调用a.bark()(编译期编译器认为a是个Animal,Animal没有bark方法)。必须向下转型后才能调用:Dog d = (Dog)a; d.bark();
💎 六、 总结与避坑
面向对象不仅是语法,更是一种思维方式的升级。记住这个顺口溜:
- 封装:藏好细节,只留接口。(保护隐私)
- 继承:抽取共性,代码复用。(认祖归宗)
- 多态:父类引用,子类实现。(千变万化)
终极避坑:不要滥用面向对象!
并不是所有的场景都适合建类。有时候一个简单的工具类(如 Math),用静态方法直接调用反而更清爽。面向对象是为了管理复杂度,如果你的程序只有50行,强行套用设计模式,只会是灾难。
💬 互动时间:你第一次学多态的时候是不是也转不过弯来?你工作中遇到过哪些因为OOP设计不当导致的“祖传屎山”?欢迎在评论区吐槽交流!如果觉得这篇博客让你顿悟了,别忘了点赞👍 + 收藏⭐,我们下期见!
更多推荐
所有评论(0)