前言
Java 是面向对象语言。本章主要学习什么是面向对象,如何使用,并补充 this 关键字的内容。
this 关键字
在学封装之前,先了解 this 关键字。
什么是 this 关键字 this 关键字在面向对象编程中用于指向当前对象的引用。简单来说,谁调用,this 就拿谁。
public class Student {
private String name;
public void setName(String name) {
this.name = name; // 这里的 this 指向当前 Student 对象
}
}
为什么使用 this 关键字 主要解决以下两类问题:
- 成员变量与局部变量同名时
- 当方法参数或局部变量与类的成员变量同名时,this 用于明确区分成员变量
public class Student {
private String name;
public void setName(String name) {
// name 与 name 的变量名冲突
this.name = name;
}
}
一、封装
什么是封装:封装是面向对象编程(OOP)的三大特性之一,指将数据和操作数据的方法绑定在一起,并对外隐藏内部实现细节。
为什么用封装:通过封装,可以控制数据的访问权限,避免外部直接修改对象内部状态,提高代码的安全性和可维护性。
如何用封装:将字段私有化(private),公开化(public),加上 get 和 set 方法。封装的设计要求:合理隐藏、合理暴露。
public class Book {
// 私有化 + 类型 + 变量名
private String name;
private String author;
private double price;
// get, set 方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
// 有参构造器
public Book(String name, String author, double price) {
this.name = name;
this.author = author;
this.price = price;
}
// 无参构造器
public Book() {}
}
下面介绍两种快捷方式来快速进行封装的要求。
快捷方式:
1. IDEA 自带的快捷方法:
定义完 private 参数名称和参数名后,回车然后右键鼠标。
找到第三个 getter 和 setter,然后点击。
接着 Shift 全选,点击确定。
这里是第一行的构造函数,确定是有参构造器,无选择是无参构造器,都需要创建。
2. Lombok 导包:
导入 Lombok 包,@Data,然后点击 Alt 键,等效于 get, set,下面的 all…和 no…等同于有参构造器和无参构造器。
二、继承
是什么:继承表示一种 is-a 关系。例如,狗是动物的一种,所以 Dog 类可以继承 Animal 类。子类会自动获得父类的所有非私有字段和方法,同时可以添加新功能或重写父类方法。
为什么使用继承:它提高了代码的可维护性和可扩展性,避免重复编写相同的代码。
如何继承:使用 extends 关键字来声明子类继承父类。父类必须定义好,子类在声明时指定父类。
注意事项:Java 中只能继承一个父类,就像人只有一个父亲一样,但可以多层继承,相当于父亲也有父亲。
public class ParentClass {
// 父类的字段和方法
}
public class ChildClass extends ParentClass {
// 子类可以添加新字段或重写父类方法
}
下面提一个需求:父类动物,子类分别是猫和狗并重写各自方法 (叫声)。
// 父类
class Animal {
public void makeSound() {
System.out.println("动物发出声音");
}
}
// 子类猫
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("喵喵");
}
}
// 子类狗
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("汪汪");
}
}
// 测试类
public class Main {
public static void main(String[] args) {
Cat myCat = new Cat();
Dog myDog = new Dog();
myCat.makeSound(); // 输出:喵喵
myDog.makeSound(); // 输出:汪汪
}
}
继承机制:子类通过 extends 关键字继承父类,获得父类的方法和属性。 方法重写:子类使用@Override注解重写父类方法 测试结果:运行 Main 类时,myCat.makeSound() 输出'喵喵',myDog.makeSound() 输出'汪汪',证明了子类成功重写了父类方法。
三、多态
是什么:多态指同一个行为(如方法调用)在不同对象上具有不同的实现方式。表现为:对象多态,行为多态。
为什么使用多态:
- 代码扩展性:新增子类无需修改原有代码。
- 接口统一:通过父类引用操作不同子类对象。
- 解耦合:降低模块间的依赖关系。
如何使用多态:
- 继承:子类继承父类
- 方法重写:子类重写父类的方法
- 向上转型:父类引用指向子类对象
我们用上面的代码为例子:
// 测试类
public class Main {
public static void main(String[] args) {
Animal myCat = new Cat(); // 多态,左边父类,右边运行子类
Animal myDog = new Dog(); // 多态,左边父类,右边运行子类
myCat.makeSound(); // 输出:喵喵
myDog.makeSound(); // 输出:汪汪
}
}
注意:多态的类型转换
- 向上转型 (Upcasting)
- 定义:将子类类型的引用转换为父类类型的引用。
- 特点:是隐式发生的,通常不需要显式强制转换。安全:因为子类对象'是一个'父类对象(继承关系),这种转换总是成功的。通过父类引用只能访问父类中声明的方法和属性。即使子类覆盖了父类方法,调用时执行的是子类的覆盖方法(动态绑定),但不能访问子类独有的成员。目的:提高代码的通用性。例如,可以将各种子类对象放入父类类型的集合中统一处理。
class Animal {}
class Cat extends Animal {}
Cat cat = new Cat();
Animal animal = cat; // 向上转型:Cat -> Animal
- 向下转型 (Downcasting)
- 定义:将父类类型的引用转换回子类类型的引用。
- 特点:是显式发生的,需要使用强制转换语法。不安全:并非所有父类引用都指向该特定子类对象。如果父类引用实际指向的是其他子类对象或不相关的对象,转换会失败并抛出 ClassCastException。成功转换后,可以使用子类特有的方法和属性。目的:当需要访问子类特有功能时使用。但在设计良好的代码中,应优先考虑通过多态(方法重写、接口)来避免频繁向下转型。安全检查:通常需要配合 instanceof 运算符进行类型检查,以确保转换的安全性。
Animal animal = new Cat(); // animal 实际指向 Cat 对象
if (animal instanceof Cat) {
// 安全检查
Cat cat = (Cat) animal; // 向下转型:Animal -> Cat,显式且需要检查
cat.catchMouse(); // 可以调用 Cat 特有方法
} else {
// 处理非 Cat 类型的情况
}
- instanceof 运算符
- 用于在运行时检查一个对象是否是某个类或其子类、实现类的实例。
- 语法:对象引用 instanceof 类/接口名
- 返回布尔值:true 表示是该类型(或兼容类型),false 表示不是。
- 主要用于向下转型前的安全检查,避免 ClassCastException。
- 总结:向上转型:子 -> 父,隐式,安全,限制访问范围。向下转型:父 -> 子,显式,有风险,需配合 instanceof 检查,可访问子类特有成员。良好的设计应尽量依赖多态和方法重写来实现行为多样性,减少对向下转型的需求。


