一、先给一道例题
在学生管理系统中,我们需要存储和处理学生的基本信息,例如姓名、学号、年龄和成绩。如果使用基本类型变量逐个存储,代码会非常冗余且难以维护。请用Java面向对象的思想设计一个学生信息管理系统,要求如下:
设计一个Student类,包含学号(id)、姓名(name)、年龄(age)和成绩(score)四个私有属性。
提供无参构造方法和带参构造方法,用于创建学生对象。
为每个属性提供对应的getter和setter方法。
在Student类中定义一个showInfo()方法,用于输出学生的完整信息。
编写测试类,创建3个学生对象,将它们存入数组,并遍历数组输出所有学生的信息。-5-9
二、解题思路
2.1 类与对象的核心概念
类是Java面向对象程序设计的核心概念之一。类可以被理解为一种“蓝图”或“模板”,它定义了某一类事物所共有的属性(数据)和行为(方法)。例如,现实世界中的“学生”这个概念是一个抽象的说法,它描述了学生应该具有学号、姓名、年龄等特征,以及学习、考试等行为——这就是“类”的概念。
对象则是根据这个“蓝图”实际创建出来的具体实例。例如,“学号为2024001、姓名为张三、年龄18岁的那个具体学生”就是一个对象。类是抽象的、静态的,而对象是具体的、动态的。类是创建对象的模板,对象是类的具体表现。
2.2 为什么要使用类与对象
在面向对象编程出现之前,程序开发主要使用面向过程的方式,数据和操作数据的函数是分离的。这种方式在小型程序中尚可应对,但随着软件规模的增长,代码会变得越来越难以维护和扩展。
类与对象的引入解决了这个问题,它带来了以下优势:
-
数据封装:将数据和操作数据的方法绑定在一起,形成一个整体
-
信息隐藏:通过访问修饰符(如private)将内部实现细节隐藏起来,只暴露必要的接口
-
代码复用:通过创建多个对象,可以重复使用同一个类的定义
-
符合人类思维:用计算机语言模拟现实世界的事物及其关系,更直观、易理解
2.3 类的设计步骤
设计一个类通常遵循以下步骤:
定义类名:使用class关键字,类名通常采用大驼峰命名法(如Student、BankAccount)
定义属性:声明类的成员变量,描述对象的状态特征,通常使用private修饰
定义构造方法:用于创建对象时初始化属性,方法名与类名相同,没有返回值类型
定义getter和setter方法:提供对外部访问私有属性的安全通道,体现封装性
定义其他业务方法:根据实际需求,定义类的行为功能-5-9
三、代码实现与逐行解释
3.1 完整代码
java
复制
下载
/**
* 学生类 – 定义学生的属性和行为
* 这是面向对象程序设计的核心:将数据(属性)和操作(方法)封装在一起
*/
class Student {
// ——————– 私有属性(封装的核心)——————–
// 属性使用private修饰,外部无法直接访问,必须通过getter/setter方法操作
private int id; // 学号
private String name; // 姓名
private int age; // 年龄
private double score; // 成绩
// ——————– 构造方法 ——————–
/**
* 无参构造方法
* 当使用new Student()时调用,创建对象后各属性为默认值
* 注意:如果在类中定义了有参构造,建议仍然保留无参构造,因为很多框架(如Spring)依赖它
*/
public Student() {
System.out.println("调用了无参构造方法");
}
/**
* 带参构造方法(构造方法重载)
* 当使用new Student(2024001, "张三", 18, 95.5)时调用,一步完成对象的创建和初始化
* @param id 学号
* @param name 姓名
* @param age 年龄
* @param score 成绩
*/
public Student(int id, String name, int age, double score) {
this.id = id; // this.id表示当前对象的id属性,id是参数
this.name = name;
this.age = age;
this.score = score;
System.out.println("调用了带参构造方法:" + name);
}
// ——————– Getter和Setter方法(封装的安全通道)——————–
/**
* 获取学号
* @return 学号
*/
public int getId() {
return id;
}
/**
* 设置学号
* @param id 学号
*/
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
/**
* 设置姓名
* 可以在此方法中添加数据校验逻辑,体现封装的业务规则控制能力
* @param name 姓名
*/
public void setName(String name) {
// 示例:可以在这里添加姓名不能为空的校验
if (name == null || name.trim().isEmpty()) {
System.out.println("姓名不能为空!");
return;
}
this.name = name;
}
public int getAge() {
return age;
}
/**
* 设置年龄
* 在setter方法中加入业务校验,确保数据合法性
* @param age 年龄(应在0-120之间)
*/
public void setAge(int age) {
if (age < 0 || age > 120) {
System.out.println("年龄应在0-120之间!");
return;
}
this.age = age;
}
public double getScore() {
return score;
}
/**
* 设置成绩
* @param score 成绩(应在0-100之间)
*/
public void setScore(double score) {
if (score < 0 || score > 100) {
System.out.println("成绩应在0-100之间!");
return;
}
this.score = score;
}
// ——————– 业务方法 ——————–
/**
* 展示学生信息
* 将对象的内部状态以格式化的形式输出
*/
public void showInfo() {
System.out.println("学号:" + id + ",姓名:" + name + ",年龄:" + age + ",成绩:" + score);
}
/**
* 判断学生是否及格
* @return 成绩>=60返回true,否则返回false
*/
public boolean isPass() {
return score >= 60;
}
/**
* 根据成绩返回等级
* @return 等级(A/B/C/D/F)
*/
public String getGrade() {
if (score >= 90) {
return "A";
} else if (score >= 80) {
return "B";
} else if (score >= 70) {
return "C";
} else if (score >= 60) {
return "D";
} else {
return "F";
}
}
}
/**
* 测试类 – 演示类与对象的创建和使用
* 面向对象编程的三大步骤:
* 1. 定义类(已完成)→ 2. 创建对象 → 3. 调用方法
*/
public class ClassAndObjectDemo {
public static void main(String[] args) {
// ——————– 方式一:使用无参构造 + setter方法 ——————–
System.out.println("========== 方式一:无参构造 + setter ==========");
Student stu1 = new Student(); // 创建对象,调用无参构造
stu1.setId(2024001); // 通过setter方法设置属性
stu1.setName("张三");
stu1.setAge(18);
stu1.setScore(95.5);
stu1.showInfo(); // 调用业务方法
// ——————– 方式二:使用带参构造(一步完成) ——————–
System.out.println("\\n========== 方式二:带参构造 ==========");
Student stu2 = new Student(2024002, "李四", 19, 82.0);
stu2.showInfo();
System.out.println("是否及格:" + (stu2.isPass() ? "是" : "否"));
System.out.println("成绩等级:" + stu2.getGrade());
Student stu3 = new Student(2024003, "王五", 20, 57.5);
stu3.showInfo();
System.out.println("是否及格:" + (stu3.isPass() ? "是" : "否"));
System.out.println("成绩等级:" + stu3.getGrade());
// ——————– 对象数组的使用 ——————–
System.out.println("\\n========== 学生对象数组 ==========");
// 声明并创建学生对象数组,长度为3
Student[] students = new Student[3];
students[0] = stu1; // 将已创建的对象存入数组
students[1] = stu2;
students[2] = stu3;
// 遍历数组,输出所有学生信息
System.out.println("所有学生信息如下:");
for (int i = 0; i < students.length; i++) {
System.out.print("第" + (i + 1) + "位学生:");
students[i].showInfo();
}
// ——————– 增强for循环遍历 ——————–
System.out.println("\\n========== 增强for循环遍历 ==========");
for (Student s : students) {
s.showInfo();
}
}
}
运行结果:
text
复制
下载
========== 方式一:无参构造 + setter ==========
调用了无参构造方法
学号:2024001,姓名:张三,年龄:18,成绩:95.5
========== 方式二:带参构造 ==========
调用了带参构造方法:李四
学号:2024002,姓名:李四,年龄:19,成绩:82.0
是否及格:是
成绩等级:B
调用了带参构造方法:王五
学号:2024003,姓名:王五,年龄:20,成绩:57.5
是否及格:否
成绩等级:F
========== 学生对象数组 ==========
所有学生信息如下:
第1位学生:学号:2024001,姓名:张三,年龄:18,成绩:95.5
第2位学生:学号:2024002,姓名:李四,年龄:19,成绩:82.0
第3位学生:学号:2024003,姓名:王五,年龄:20,成绩:57.5
========== 增强for循环遍历 ==========
学号:2024001,姓名:张三,年龄:18,成绩:95.5
学号:2024002,姓名:李四,年龄:19,成绩:82.0
学号:2024003,姓名:王五,年龄:20,成绩:57.5
3.2 逐行代码解释
① 类的定义与属性声明
java
复制
下载
class Student {
private int id;
private String name;
private int age;
private double score;
}
-
class关键字用于定义一个类,Student是类名,遵循大驼峰命名法
-
private是访问修饰符,表示这些属性只能在当前类内部被访问,外部无法直接读取或修改
-
这种将属性隐藏起来的做法就是封装,它保护了数据的完整性和安全性
② 构造方法
java
复制
下载
public Student() {
System.out.println("调用了无参构造方法");
}
public Student(int id, String name, int age, double score) {
this.id = id;
this.name = name;
this.age = age;
this.score = score;
}
-
构造方法是创建对象的入口,方法名必须与类名完全相同,且没有返回值类型(void也不行)
-
无参构造在没有自定义构造方法时由编译器自动提供;但如果手动添加了有参构造,编译器不再提供无参构造,建议手动保留
-
this关键字用于区分成员变量和参数——当成员变量名和参数名相同时,this.id指的是当前对象的id属性,而id指的是传入的参数-9
③ Setter方法中的业务校验
java
复制
下载
public void setName(String name) {
if (name == null || name.trim().isEmpty()) {
System.out.println("姓名不能为空!");
return;
}
this.name = name;
}
-
Setter方法不仅仅是简单的赋值,还可以加入业务逻辑验证
-
这样做的好处是:数据验证逻辑集中在一处,避免在多个调用处重复编写校验代码
-
如果以后校验规则发生变化,只需要修改这一个方法即可——这正是封装带来的维护性优势
④ 对象数组
java
复制
下载
Student[] students = new Student[3];
students[0] = stu1;
-
对象数组的声明与基本类型数组类似:类名[] 数组名 = new 类名[长度]
-
与基本类型数组不同,对象数组的每个位置存储的是对象的引用(即对象在内存中的地址),而不是对象本身
-
创建数组后,需要将已经创建的对象存入数组,然后才能使用
⑤ 增强for循环(for-each)
java
复制
下载
for (Student s : students) {
s.showInfo();
}
-
增强for循环是Java 5引入的语法糖,专门用于遍历数组和集合
-
语法:for (元素类型 变量名 : 数组/集合)
-
每次循环,变量s会依次指向数组中的每一个元素,代码更加简洁、不易出错-5
四、总结
通过以上学生类的设计与使用,我们可以总结出以下几个核心要点:
类与对象的本质关系
-
类是对象的“蓝图”或“模板”,定义了对象应该具备的属性和行为
-
对象是根据类创建出来的具体实例,一个类可以创建无数个对象
-
类是静态的概念(存在于代码层面),对象是动态的概念(存在于运行时内存中)
创建对象的标准流程
-
声明对象引用:类名 对象名;
-
实例化对象:对象名 = new 类名();(new关键字在堆内存中分配空间)
-
合并写法:类名 对象名 = new 类名();
类的核心组成部分及作用
-
属性(成员变量):描述对象的状态特征,通常设为private
-
构造方法:创建对象时的初始化入口,方法名与类名相同,无返回值
-
getter/setter方法:对外访问私有属性的安全通道,可在此添加业务校验逻辑
-
业务方法:定义对象的具体行为功能(如showInfo、isPass等)
封装带来的核心价值
-
数据安全性:通过private隐藏内部数据,外部无法随意篡改
-
数据合法性:在setter方法中加入验证逻辑,确保对象始终处于合法状态
-
降低耦合度:外部只依赖方法,不依赖内部实现细节
-
提高可维护性:修改内部实现时不影响外部调用代码
对象数组的要点
-
对象数组存储的是对象的引用(地址),不是对象本身
-
先创建数组,再创建对象并存入,最后才能使用
-
遍历对象数组时,注意处理可能存在的null元素
命名规范建议
-
类名:大驼峰命名法,如Student、BankAccount
-
变量名/方法名:小驼峰命名法,如studentName、showInfo
-
常量名:全大写+下划线,如MAX_SCORE
五、更多练习题目推荐
以下是可以直接访问的Java类与对象相关练习题(请复制链接后自行替换 [colon] 为 :,[dot] 为 .):
https://www.luogu.com.cn/problem/P5740
https://www.luogu.com.cn/problem/P5741
https://www.luogu.com.cn/problem/P5742
https://blog.csdn.net/2401_87890252/article/details/152521736
https://blog.csdn.net/2501_91241024/article/details/149905735
https://blog.csdn.net/m0_63792223/article/details/129464959
以上洛谷题目都是经典的面向对象练习题,P5740要求封装学生类并找出总分最高的学生,P5741要求实现学生之间的相似度比较,P5742则需要计算综合评分并判断等级-3-6。练习建议:可以尝试在Student类中增加更多属性(如班级、电话),并扩展相应的业务方法(如修改密码、计算平均分等),进一步熟悉类与对象的设计流程。






