主题
Java面向对象核心:JavaBean、对象数组与继承完全指南
目录
一、JavaBean详解
1.1 JavaBean的作用
核心作用:封装数据
JavaBean主要用于封装数据,即将数据赋值给JavaBean中的属性。在实际开发中,JavaBean类都与数据库表对应。
1.2 JavaBean与数据库表的对应关系
| 数据库 | JavaBean |
|---|---|
| 表名 | 类名 |
| 列名 | 属性名 |
| 列的类型 | 属性的数据类型 |
| 每一行数据 | JavaBean的对象 |
示例:
java
// 数据库表:user
// 列:id, username, password
public class User {
private int id;
private String username;
private String password;
// 构造方法
public User() {}
public User(int id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
// getter/setter方法
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}1.3 JavaBean在开发中的实际应用
应用场景一:添加功能
在添加功能中,JavaBean用于封装页面上发送过来的数据,一层一层传递到DAO层,在DAO层中调用JavaBean对象中的getXXX方法,将属性值获取出来,放到SQL语句中。
流程:
页面数据 → JavaBean封装 → Service层 → DAO层 → 数据库应用场景二:查询功能
在查询功能中,JavaBean用于封装从数据库中查询出来的数据,然后一层一层返回给页面上进行展示。
流程:
数据库查询结果 → JavaBean封装 → Service层 → 页面展示1.4 标准JavaBean的组成
一个标准的JavaBean应该包含:
- 私有属性:使用private修饰
- 无参构造方法:必须提供
- 有参构造方法:可选,建议提供
- getter/setter方法:为每个属性提供访问方法
快速生成: 使用IDE快捷键 Alt + Insert (Windows) 或 Cmd + N (Mac)
二、对象数组
2.1 概念
对象数组是指数组中存储的元素是对象类型的数据。
定义格式:
java
// 数据类型[] 数组名 = new 数据类型[长度];
Person[] arr = new Person[3];2.2 实战示例
需求: 定义一个数组,存3个Person对象,遍历数组,将Person对象中的属性值获取出来
Person类:
java
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}测试类:
java
public class Test01 {
public static void main(String[] args) {
// 1.定义存储Person对象的数组
Person[] arr1 = new Person[3];
// 2.创建三个Person对象
Person p1 = new Person("小王", 10);
Person p2 = new Person("小张", 20);
Person p3 = new Person("小李", 30);
// 3.将三个对象放到数组中
arr1[0] = p1;
arr1[1] = p2;
arr1[2] = p3;
// 4.遍历数组
for (int i = 0; i < arr1.length; i++) {
Person p = arr1[i];
System.out.println(p.getName() + "..." + p.getAge());
}
}
}2.3 对象数组的内存图解
对象数组在内存中的存储结构:
- 数组存储的是对象的引用(地址值)
- 实际的对象存储在堆内存中
- 通过数组索引可以访问到具体的对象
三、继承核心概念
3.1 什么是继承
面向对象三大特征: 封装、继承、多态
继承的定义: 多个类中有相同的成员,我们定义一个父类,将相同的成员抽取到父类中,子类直接继承父类,然后直接使用父类抽取出来的成员。
格式:
java
class 子类 extends 父类 {
// 子类特有的成员
}3.2 继承的注意事项
| 注意点 | 说明 |
|---|---|
| 私有成员 | 子类可以继承父类中私有和非私有成员,但不能直接使用私有成员 |
| 构造方法 | 构造方法不能继承 |
| 静态方法 | 静态方法可以继承但不能被重写 |
3.3 继承的使用示例
父类:
java
public class Employee {
String name;
int age;
public void work() {
System.out.println("员工正在工作...");
}
private void eat(){
System.out.println("员工正在吃...");
}
}子类:
java
public class Teacher extends Employee {
// Teacher类自动拥有name、age属性和work()方法
}
public class Manager extends Employee {
// Manager类自动拥有name、age属性和work()方法
}测试类:
java
public class Test01 {
public static void main(String[] args) {
Teacher t1 = new Teacher();
t1.name = "涛哥";
t1.age = 16;
System.out.println(t1.name + "..." + t1.age);
t1.work();
// t1.eat(); // 错误:不能直接使用父类中私有成员
System.out.println("======================");
Manager m1 = new Manager();
m1.name = "许姐";
m1.age = 18;
System.out.println(m1.name + "..." + m1.age);
m1.work();
}
}3.4 继承中成员变量的访问特点
情况一:成员变量不重名
java
public class Fu {
public int numFu = 100;
}
public class Zi extends Fu {
public int numZi = 10;
}
// 测试
Fu fu = new Fu();
System.out.println(fu.numFu); // 100
Zi zi = new Zi();
System.out.println(zi.numFu); // 100 (继承自父类)
System.out.println(zi.numZi); // 10 (子类自己的)情况二:成员变量重名
访问规则: 看等号左边是谁,先调用谁中的成员变量;子类没有找父类
java
public class Fu {
public int num = 10000;
}
public class Zi extends Fu {
public int num = 1000;
}
// 测试
Zi zi = new Zi();
System.out.println(zi.num); // 1000 (子类的)
Fu f = new Zi(); // 多态
System.out.println(f.num); // 10000 (父类的,看等号左边)3.5 继承中成员方法的访问特点
情况一:成员方法不重名
java
public class Fu {
public void numFu(){
System.out.println("父类numFu方法");
}
}
public class Zi extends Fu {
public void numZi(){
System.out.println("子类numZi方法");
}
}
// 测试
Zi zi = new Zi();
zi.numZi(); // 子类numZi方法
zi.numFu(); // 父类numFu方法 (继承而来)情况二:成员方法重名(方法重写)
访问规则: 看new的是谁,先调用谁中的成员方法;子类没有,找父类
java
public class Fu {
public void num(){
System.out.println("父类num方法");
}
}
public class Zi extends Fu {
@Override
public void num(){
System.out.println("子类num方法");
}
}
// 测试
Zi zi = new Zi();
zi.num(); // 子类num方法
Fu f = new Zi(); // 多态
f.num(); // 子类num方法 (看new的是谁)3.6 继承的特点
| 特点 | 说明 | 示例 |
|---|---|---|
| 单继承 | 一个类只能有一个直接父类 | class Zi extends Fu ✅class Zi extends Fu, Ye ❌ |
| 多层继承 | 支持多层继承体系 | class Zi extends Fuclass Fu extends Ye |
| 多子类 | 一个父类可以有多个子类 | class Zi extends Fuclass Bro extends Fu |
3.7 继承中构造方法的特点
核心特点: 创建子类对象时,先初始化父类
原因: 每一个类中的构造方法第一行默认有一个super(),不写JVM自动提供一个
super()的作用: 代表调用父类无参构造
示例:
java
public class Fu {
public Fu(){
System.out.println("父类无参构造方法");
}
}
public class Zi extends Fu {
public Zi(){
super(); // 默认存在,调用父类无参构造
System.out.println("子类无参构造方法");
}
public Zi(int num){
super(); // 默认存在
System.out.println("子类有参构造方法");
}
}
// 测试
Zi zi = new Zi();
// 输出:
// 父类无参构造方法
// 子类无参构造方法四、方法重写
4.1 什么是方法重写
定义: 子类中有一个和父类方法名以及参数列表都一样的方法,这个方法叫做重写的方法。
判断标准: 使用@Override注解,如果这个注解不报错就是重写的,否则就不是重写的方法。
4.2 方法重写的规则
| 规则 | 说明 |
|---|---|
| 权限修饰符 | 子类方法重写父类方法,必须保证权限大于等于父类权限 public → protected → 默认 → private |
| 方法签名 | 方法名和参数列表必须一模一样 |
| 返回值类型 | 应该是父类方法返回值类型的子类类型(一般情况下都一样) |
| 私有方法 | 私有方法不能被重写 |
| 构造方法 | 构造方法不能被重写 |
| 静态方法 | 静态方法不能被重写 |
4.3 方法重写示例
基础示例:
java
public class Fu {
public void num(){
System.out.println("父类num方法");
}
public Fu method01(){
return null;
}
}
public class Zi extends Fu {
@Override
public void num(){
System.out.println("子类num方法");
}
@Override
public Zi method01(){
// 返回值类型可以是父类返回值类型的子类
return null;
}
}4.4 方法重写的使用场景
场景: 在子类中对父类中的方法进行升级改造
实战案例:手机功能升级
java
// 旧手机
public class OldPhone {
public void call(){
System.out.println("打电话");
}
public void sendMessage(){
System.out.println("发短信");
}
public void show(){
System.out.println("显示手机号");
}
}
// 新手机
public class NewPhone extends OldPhone {
@Override
public void show(){
System.out.println("显示手机号");
System.out.println("显示归属地");
System.out.println("显示头像");
}
}
// 测试
public class Test01 {
public static void main(String[] args) {
OldPhone oldPhone = new OldPhone();
oldPhone.show(); // 显示手机号
System.out.println("======================");
NewPhone newPhone = new NewPhone();
newPhone.show();
// 显示手机号
// 显示归属地
// 显示头像
}
}五、super与this关键字
5.1 super关键字
概述: super代表的是父类引用
super的三种用法
| 用法 | 说明 | 示例 |
|---|---|---|
| 调用父类构造 | 只能在子类的构造中使用 | super() 调用父类无参构造super(实参) 调用父类有参构造 |
| 调用父类成员变量 | 在子类方法中使用 | super.成员变量名 |
| 调用父类方法 | 在子类方法中使用 | super.方法名() |
完整示例:
java
public class Fu {
public int num = 100;
public Fu(){
System.out.println("父类无参构造");
}
public Fu(int num){
System.out.println("父类有参构造");
}
public void show(){
System.out.println("父类show方法");
}
}
public class Zi extends Fu {
public int num = 10;
public Zi(){
super(); // 调用父类无参构造
System.out.println("子类无参构造");
}
public Zi(int num){
super(10); // 调用父类有参构造
System.out.println("子类有参构造");
}
@Override
public void show(){
super.show(); // 调用父类方法
System.out.println(num); // 子类num: 10
System.out.println(super.num); // 父类num: 100
System.out.println("子类show方法");
}
}注意事项:
- 在构造方法中,super关键字必须在第一行
- 不能和this关键字同时出现在构造方法第一行
5.2 this关键字
概述: this代表的是当前对象,哪个对象调用的this所在的方法,this就代表哪个对象
this的三种用法
| 用法 | 说明 | 示例 |
|---|---|---|
| 调用当前对象的构造 | 在本类的构造中使用 | this() 调用本类无参构造this(实参) 调用本类有参构造 |
| 调用当前对象的成员变量 | 在方法中使用 | this.成员变量名 |
| 调用当前对象的方法 | 在方法中使用 | this.方法名() |
完整示例:
java
public class Zi {
public int num = 10;
public Zi(){
this(10); // 调用本类有参构造方法
System.out.println("无参构造方法");
}
public Zi(int num){
System.out.println("有参构造方法");
}
public void show(){
int num = 100;
System.out.println(num); // 局部变量: 100
System.out.println(this.num); // 成员变量: 10
}
}注意事项:
- 在构造方法中使用this,必须在第一行
- 在构造方法中,不能this和super一起出现
5.3 super与this的区别
| 对比项 | super | this |
|---|---|---|
| 代表 | 父类引用 | 当前对象引用 |
| 访问成员变量 | super.变量 访问父类变量 | this.变量 访问本类变量 |
| 调用构造方法 | super() 调用父类构造 | this() 调用本类构造 |
| 调用方法 | super.方法() 调用父类方法 | this.方法() 调用本类方法 |
六、常见面试题
6.1 基础面试题 ⭐
问题1:什么是JavaBean?它在开发中的作用是什么?
答案:
JavaBean是一种标准的Java类,主要用于封装数据。它的作用包括:
- 封装数据:将数据以属性的形式存储在对象中
- 数据传递:在各层之间传递数据(页面→Service→DAO→数据库)
- 数据映射:与数据库表形成对应关系,一行数据对应一个JavaBean对象
标准JavaBean包含:
- 私有属性
- 无参构造方法
- getter/setter方法
问题2:继承的特点有哪些?
答案:
Java继承有三个主要特点:
单继承:一个类只能有一个直接父类
javaclass Zi extends Fu {} // 正确 class Zi extends Fu, Ye {} // 错误多层继承:支持多层继承体系
javaclass Zi extends Fu {} class Fu extends Ye {}多子类:一个父类可以有多个子类
javaclass Zi extends Fu {} class Bro extends Fu {}
问题3:父类的私有成员能否被继承?
答案:
能继承,但不能直接访问。
- 子类可以继承父类的私有成员(拥有该成员)
- 但由于private修饰符的限制,子类不能直接访问
- 需要通过父类提供的public的getter/setter方法来间接访问
问题4:构造方法能否被继承?
答案:
不能。
- 构造方法不能被继承
- 构造方法名必须与类名相同,如果继承会导致构造方法名与子类类名不一致
- 子类可以通过super关键字调用父类的构造方法
6.2 进阶面试题 ⭐⭐
问题5:什么是方法重写?和方法重载有什么区别?
答案:
方法重写(Override):
- 定义:子类中有一个和父类方法名、参数列表都相同的方法
- 发生在父子类之间
- 是运行时多态的体现
方法重载(Overload):
- 定义:同一个类中,方法名相同但参数列表不同的多个方法
- 发生在同一个类中
- 是编译时多态的体现
对比表格:
| 对比项 | 方法重写 | 方法重载 |
|---|---|---|
| 发生位置 | 父子类之间 | 同一个类中 |
| 方法名 | 必须相同 | 必须相同 |
| 参数列表 | 必须相同 | 必须不同 |
| 返回值类型 | 相同或是子类类型 | 无关 |
| 权限修饰符 | 大于等于父类 | 无关 |
问题6:方法重写需要遵循哪些规则?
答案:
权限修饰符:子类方法的权限必须大于等于父类方法的权限
public → protected → 默认 → private方法签名:方法名和参数列表必须完全一致
返回值类型:必须相同,或是父类方法返回值类型的子类类型
不能重写的方法:
- 私有方法(private)
- 构造方法
- 静态方法(static)
- final修饰的方法
问题7:创建子类对象时,构造方法的执行顺序是什么?
答案:
执行顺序: 父类构造 → 子类构造
原因:
- 子类构造方法的第一行默认有
super() - super()会调用父类的无参构造方法
- 这样保证在创建子类对象前,先初始化父类部分
示例:
java
class Fu {
public Fu() {
System.out.println("父类构造");
}
}
class Zi extends Fu {
public Zi() {
super(); // 默认存在
System.out.println("子类构造");
}
}
// 执行结果:
// 父类构造
// 子类构造6.3 高级面试题 ⭐⭐⭐
问题8:super和this关键字的区别是什么?
答案:
| 对比项 | super | this |
|---|---|---|
| 概念 | 父类引用 | 当前对象引用 |
| 访问成员变量 | super.变量 访问父类变量 | this.变量 访问本类变量 |
| 调用构造方法 | super() 调用父类构造 | this() 调用本类构造 |
| 调用方法 | super.方法() 调用父类方法 | this.方法() 调用本类方法 |
| 使用位置 | 子类构造方法中(第一行) | 本类构造方法中(第一行) |
| 共存性 | 不能和this同时出现在构造第一行 | 不能和super同时出现在构造第一行 |
关键点:
- super和this在构造方法中都必须在第一行
- 两者不能同时出现在同一个构造方法中
- super()由JVM默认提供,不写也会自动添加
问题9:继承中成员变量和成员方法的访问特点有什么不同?
答案:
成员变量:看等号左边
- 编译看左边,运行看左边
- 引用变量的类型决定了访问哪个成员变量
java
Fu f = new Zi();
System.out.println(f.num); // 访问父类的num成员方法:看new的是谁
- 编译看左边,运行看右边
- 实际对象类型决定了调用哪个方法(动态绑定)
java
Fu f = new Zi();
f.show(); // 调用子类重写的show方法原因: 方法重写是运行时多态,而成员变量不具备多态性。
问题10:如何为父类中private的成员变量赋值?
答案:
有两种方式:
方式一:使用setter方法
java
public class Employee {
private String name;
private int age;
// setter方法
public void setName(String name) { this.name = name; }
public void setAge(int age) { this.age = age; }
}
public class Teacher extends Employee {}
// 使用
Teacher t = new Teacher();
t.setName("涛哥");
t.setAge(16);方式二:使用构造方法
java
public class Employee {
private String name;
private int age;
public Employee(String name, int age) {
this.name = name;
this.age = age;
}
}
public class Teacher extends Employee {
public Teacher(String name, int age) {
super(name, age); // 调用父类有参构造
}
}
// 使用
Teacher t = new Teacher("涛哥", 16);七、经典练习题
7.1 基础练习题
练习1:学生对象数组排序
题目: 定义一个学生类,声明姓名、年龄、分数,创建5个学生对象为属性赋值,放数组中,然后按照分数排序。
参考代码:
java
public class Student {
private String name;
private int age;
private double score;
public Student() {
}
public Student(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
@Override
public String toString() {
return "Student{name='" + name + "', age=" + age + ", score=" + score + "}";
}
}
public class Test {
public static void main(String[] args) {
// 创建5个学生对象
Student[] students = new Student[5];
students[0] = new Student("张三", 18, 85.5);
students[1] = new Student("李四", 19, 92.0);
students[2] = new Student("王五", 18, 78.5);
students[3] = new Student("赵六", 20, 88.0);
students[4] = new Student("钱七", 19, 95.5);
// 按分数排序(冒泡排序)
for (int i = 0; i < students.length - 1; i++) {
for (int j = 0; j < students.length - 1 - i; j++) {
if (students[j].getScore() < students[j + 1].getScore()) {
Student temp = students[j];
students[j] = students[j + 1];
students[j + 1] = temp;
}
}
}
// 输出排序结果
System.out.println("按分数降序排序:");
for (Student student : students) {
System.out.println(student);
}
}
}7.2 进阶练习题
练习2:继承与重写综合练习
题目:
- 定义一个动物类(Animal),包含name属性和eat()方法
- 定义猫类(Cat)和狗类(Dog)继承Animal
- 重写eat()方法,输出不同的内容
- 在测试类中创建对象并测试
参考代码:
java
// 父类
public class Animal {
private String name;
public Animal() {
}
public Animal(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void eat() {
System.out.println(name + "在吃东西");
}
}
// 子类:猫
public class Cat extends Animal {
public Cat() {
}
public Cat(String name) {
super(name);
}
@Override
public void eat() {
System.out.println(getName() + "在吃鱼");
}
public void catchMouse() {
System.out.println(getName() + "在抓老鼠");
}
}
// 子类:狗
public class Dog extends Animal {
public Dog() {
}
public Dog(String name) {
super(name);
}
@Override
public void eat() {
System.out.println(getName() + "在吃骨头");
}
public void lookHome() {
System.out.println(getName() + "在看家");
}
}
// 测试类
public class Test {
public static void main(String[] args) {
Cat cat = new Cat("小花");
cat.eat(); // 小花在吃鱼
cat.catchMouse(); // 小花在抓老鼠
Dog dog = new Dog("大黄");
dog.eat(); // 大黄在吃骨头
dog.lookHome(); // 大黄在看家
// 多态
Animal a1 = new Cat("小黑");
a1.eat(); // 小黑在吃鱼
Animal a2 = new Dog("小白");
a2.eat(); // 小白在吃骨头
}
}八、学习建议
8.1 学习重点
JavaBean标准结构
- 必须掌握标准JavaBean的编写规范
- 理解JavaBean在开发中的数据传递作用
- 熟练使用IDE快速生成getter/setter方法
继承的核心概念
- 理解继承的设计理念:代码复用
- 掌握继承的使用场景和注意事项
- 理解继承中成员访问的特点
方法重写
- 掌握方法重写的规则
- 理解方法重写的使用场景
- 能够正确使用@Override注解
super和this关键字
- 理解两者的区别和用途
- 掌握在构造方法中的使用规则
- 理解继承中构造方法的执行顺序
8.2 常见错误
忘记无参构造
- 错误:写了有参构造,忘记写无参构造
- 后果:子类无法调用super()
- 解决:建议手动提供无参构造
权限修饰符错误
- 错误:重写方法权限小于父类
- 后果:编译错误
- 解决:保证子类方法权限 >= 父类方法权限
super和this位置错误
- 错误:super()或this()不在构造方法第一行
- 后果:编译错误
- 解决:必须在构造方法第一行
混淆重写和重载
- 错误:参数列表不一致,以为是重写
- 后果:实际是重载,不是重写
- 解决:使用@Override注解验证
8.3 学习路径
Day 08 学习路径:
1. JavaBean(数据封装)
↓
2. 对象数组(对象集合)
↓
3. 继承基础(代码复用)
↓
4. 方法重写(功能扩展)
↓
5. super和this(引用控制)8.4 实践建议
多写代码
- 每个知识点都要亲自编写代码验证
- 不要只看不练
画内存图
- 理解对象在内存中的存储
- 理解继承关系中对象的初始化过程
对比学习
- 对比重写和重载
- 对比super和this
- 对比成员变量和成员方法的访问特点
实际应用
- 思考继承在实际项目中的应用场景
- 尝试设计简单的继承体系
附录:知识点速查表
A. JavaBean规范
| 组成部分 | 要求 |
|---|---|
| 属性 | private修饰 |
| 构造方法 | 必须有无参构造 |
| getter方法 | public,返回属性值 |
| setter方法 | public,设置属性值 |
B. 继承特点
| 特点 | 说明 |
|---|---|
| 单继承 | 一个类只能有一个直接父类 |
| 多层继承 | 支持多层继承体系 |
| 多子类 | 一个父类可以有多个子类 |
C. 方法重写规则
| 规则 | 要求 |
|---|---|
| 权限修饰符 | 子类 >= 父类 |
| 方法名 | 必须相同 |
| 参数列表 | 必须相同 |
| 返回值类型 | 相同或是子类类型 |
D. super vs this
| 对比项 | super | this |
|---|---|---|
| 代表 | 父类引用 | 当前对象 |
| 调用构造 | super() | this() |
| 调用变量 | super.变量 | this.变量 |
| 调用方法 | super.方法() | this.方法() |
学习格言: 继承是面向对象的核心特性之一,理解继承是掌握多态的基础。多写代码,多画图,多思考!