文章目录
JAVA基础知识总结(三)
封装、继承、多态、和抽象是面向对象编程(OOP)四大基本特性。理清这四个概念能帮助我们构建模块化、可重用、可维护的代码,所以理解他们非常重要。接下来先介绍封装和继承。
封装
封装(Encapsulation) 是面向对象编程的四大基本特性之一,它是通过将对象的属性和行为隐藏起来,只暴露必要的方法给外部访问,从而保护数据和实现细节。这种方式使类内部的数据不可直接被外部修改,有效增强了程序的安全性和可维护性。
1. 封装的基本概念
封装是通过将类的字段(属性)声明为私有(private),然后提供**公共(public)**的方法(如 getter 和 setter)来间接访问这些字段。这样,外部类无法直接操作对象的数据,而只能通过类提供的方法来操作数据。
2. 封装的优点
- 数据保护:防止外部类直接访问和修改类的内部数据,确保对象的状态安全。
- 提高代码可维护性:可以在不影响外部类的情况下修改类的内部实现细节。
- 控制访问级别:通过提供特定的访问控制方法,控制对象数据的读写权限。
- 隐藏实现细节:用户只需知道如何使用类,而无需了解类内部是如何实现的。
3. 封装的实现
在 Java 中,封装通过使用访问修饰符来实现,常用的修饰符有 private
、protected
和 public
。
private
:仅类内部可访问,外部类和子类无法直接访问。public
:任何地方都可访问。protected
:同包和子类可访问,外部包的类不可访问。
4. 封装的典型示例
public class Person {
// 私有成员变量
private String name;
private int age;
// 提供公共的 getter 和 setter 方法
// 获取 name
public String getName() {
return name;
}
// 设置 name
public void setName(String name) {
this.name = name;
}
// 获取 age
public int getAge() {
return age;
}
// 设置 age,并可以加入数据校验逻辑
public void setAge(int age) {
if (age >= 0) {
this.age = age;
} else {
System.out.println("Invalid age.");
}
}
}
public class Main {
public static void main(String[] args) {
Person person = new Person();
person.setName("Alice"); // 设置姓名
person.setAge(25); // 设置年龄
System.out.println(person.getName()); // 输出: Alice
System.out.println(person.getAge()); // 输出: 25
}
}
在这个例子中,Person
类的属性 name
和 age
是私有的(private
),只能通过公共的 getName
、setName
、getAge
、setAge
方法来访问和修改。这种方式确保了数据的安全性和灵活性,例如在 setAge
中添加了年龄不能为负数的校验逻辑。
5. 访问修饰符的作用范围
修饰符 | 类内部 | 同包 | 子类 | 其他包 |
---|---|---|---|---|
private | 是 | 否 | 否 | 否 |
default | 是 | 是 | 否 | 否 |
protected | 是 | 是 | 是 | 否 |
public | 是 | 是 | 是 | 是 |
6. 封装的优点
- 提高数据安全性:防止外部类对内部数据进行不合法的访问或修改。
- 简化代码使用:通过简化的接口与用户交互,隐藏复杂的实现细节。
- 灵活性:可以通过 getter 和 setter 方法控制数据的访问和修改,还可以在方法中添加数据校验逻辑。
7. 封装与访问控制
封装不仅仅是为了数据隐藏,它还允许类提供控制数据访问的接口。类可以决定哪些信息可以暴露给外部,哪些信息应当保留在内部。这种设计能提高系统的灵活性与可扩展性。例如,通过 setter 方法可以引入数据校验逻辑,确保属性值符合预期,而不会随意被修改。
8. 封装的应用场景列举
- 数据保护:例如,在银行系统中,账户余额是一个敏感数据,必须通过严格控制的方式访问和修改。
- API 设计:在设计公共 API 时,封装可以隐藏内部实现细节,保持 API 的简单易用。
- 提高类的灵活性:封装的类可以根据需求更改内部实现,而不影响类的使用者。
9. 封装与数据校验
封装不仅仅是隐藏属性,还可以在 getter 和 setter 方法中加入逻辑来控制数据的合法性。例如,setAge()
方法可以检查传入的值是否为有效的年龄范围,从而确保类的状态始终是有效的。
继承
继承(Inheritance) 是面向对象编程的四大基本特性之一,指的是一个类可以继承另一个类的属性和方法,从而实现代码复用和扩展。在 Java 中,继承通过关键字 extends
实现。
1. 继承的基本概念
继承允许一个类(子类或派生类)继承另一个类(父类或基类)的成员(属性和方法),从而减少重复代码。通过继承,子类可以复用父类的已有功能,还可以在此基础上进行扩展或修改。
2. 继承的优点
- 代码复用:通过继承,子类无需重复编写父类已有的代码,直接继承其成员。
- 代码扩展性:可以通过继承对父类的功能进行扩展或修改,提升代码的灵活性和可维护性。
- 结构清晰:继承关系使代码结构更加清晰,逻辑上也更合理。
3. 继承的实现
继承在 Java 中是通过 extends
关键字实现的。Java 只支持单继承,即一个类只能有一个直接父类,但可以实现多个接口。
public class Animal {
// 父类属性
protected String name;
// 父类构造器
public Animal(String name) {
this.name = name;
}
// 父类方法
public void eat() {
System.out.println(name + " is eating.");
}
}
// 子类继承父类
public class Dog extends Animal {
private String breed;
// 子类构造器调用父类构造器
public Dog(String name, String breed) {
super(name); // 调用父类构造器
this.breed = breed;
}
// 子类特有方法
public void bark() {
System.out.println(name + " is barking.");
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog("Buddy", "Labrador");
dog.eat(); // 调用继承自父类的方法
dog.bark(); // 调用子类自己的方法
}
}
在这个例子中,Dog
类继承了 Animal
类,Dog
类除了可以使用父类的 eat()
方法外,还可以定义自己的 bark()
方法。
4. super 关键字
super
用于引用父类的属性和方法,可以用在子类中访问父类的构造器或重写的方法。super()
:调用父类的构造方法,必须是子类构造器的第一行。super.方法名()
:调用父类的成员方法,特别是在子类重写了父类方法时仍可以通过super
访问父类版本。
5. 方法重写(Override)
子类可以重写(Override)父类的方法,即在子类中提供与父类同名的、相同参数列表的方法,从而对父类方法进行修改或扩展。重写时,需使用 @Override
注解来标识。
@Override
public void eat() {
System.out.println(name + " is eating loudly.");
}
重写要求:
- 方法名、参数列表必须相同。
- 返回值类型可以是父类返回类型的子类型。
- 访问修饰符不能比父类的更严格。
6. 构造函数和继承
子类不能继承父类的构造函数,但可以通过 super()
调用父类的构造函数来初始化继承的属性。在创建子类对象时,首先会调用父类的构造函数,然后调用子类的构造函数。
public Dog(String name, String breed) {
super(name); // 调用父类构造函数
this.breed = breed;
}
7. 访问修饰符与继承
private
:子类无法访问父类的私有成员。protected
:子类可以访问父类的受保护成员。public
:子类可以访问父类的公共成员。default
(包访问权限):子类只能在同一包内访问父类的成员。
8. 继承与多态(多态的细节放在后面章节介绍)
多态是继承的一个重要应用,允许父类的引用指向子类的对象,从而在运行时决定调用哪个类的方法。
Animal animal = new Dog("Buddy", "Labrador");
animal.eat(); // 调用的是 Dog 类的 eat 方法
多态通过方法重写和运行时动态绑定实现。父类引用可以调用被重写的方法,实际调用的将是子类版本的方法。
9. final 关键字
- 使用
final
修饰的类不能被继承。例如,public final class Animal
表示Animal
类无法被继承。 - 使用
final
修饰的方法不能被子类重写。
10. Object 类
在 Java 中,所有类默认继承自 Object
类,Object
是所有类的根类。Object
类提供了一些常用方法,如 toString()
、equals()
和 hashCode()
,这些方法可以在子类中重写。
11. 单继承 vs 多继承
Java 不支持多继承,即一个类只能有一个直接父类。这是为了避免菱形继承问题,即子类继承两个父类时,如果这两个父类有相同的方法,可能会导致混乱和冲突。Java 通过接口机制弥补了单继承的局限,允许一个类实现多个接口。
12. 继承的好处与注意事项
- 好处:提高了代码复用性、扩展性、可维护性。
- 注意事项:过度使用继承可能导致代码耦合度增加,不利于维护。应在类之间有明显“is-a”关系时使用继承。
13. 继承与组合
有时继承不是最优选择,应提倡组合优于继承,即优先通过对象组合的方式来复用代码,而不是通过继承,特别是当类之间没有明确的“is-a”关系时。
总结
封装通过限制直接访问对象数据的方式,提高了数据的安全性和程序的灵活性。它通过提供访问控制方法,确保对象内部的状态只能通过安全的方式进行更改。封装是面向对象编程的重要机制,广泛应用于类的设计中。
继承是 Java 中的核心特性之一,通过继承,子类可以复用父类的代码并对其进行扩展。但应合理使用继承,避免滥用,以确保代码的可维护性和灵活性。