

在学习类和对象之前,我们先来了解什么是面向对象编程(OOP),它为什么会产生,以及它的核心思想是什么。
早期的编程采用面向过程(Procedure-Oriented Programming) 思想,关注 "怎么做",将问题分解为步骤,用函数实现每个步骤。但随着软件规模扩大,面向过程的代码维护性和扩展性越来越差。
20 世纪 80 年代,面向对象编程(Object-Oriented Programming) 思想应运而生,它关注 "用什么做",将问题分解为对象,通过对象之间的交互解决问题。Java 是典型的面向对象编程语言。

面向对象有三大核心特征:

类是对象的模板,对象是类的实例。本节将学习如何定义类、创建对象,以及类和对象在内存中的存储方式。
在 Java 中,类通过class关键字定义,包含属性(成员变量)和方法(成员函数)。
语法结构:
[访问修饰符] class 类名 {
// 属性定义
数据类型 属性名1;
数据类型 属性名2;
// 方法定义
[访问修饰符] 返回值类型 方法名(参数列表) {
// 方法体
}
}示例:定义一个学生类
/**
* 学生类:描述学生的共同属性和行为
*/
public class Student {
// 属性(成员变量)
String name; // 姓名
int age; // 年龄
String major; // 专业
// 方法(成员函数)
/**
* 学习方法
*/
public void study() {
System.out.println(name + "正在学习Java编程!");
}
/**
* 介绍自己
*/
public void introduce() {
System.out.println("大家好!我叫" + name + ",今年" + age + "岁,专业是" + major);
}
} 定义类后,需要创建对象才能使用。通过new关键字创建对象,使用.操作符访问对象的属性和方法。
步骤:
示例:使用 Student 类创建对象
/**
* 测试学生类的使用
*/
public class StudentTest {
public static void main(String[] args) {
// 1. 创建对象(声明引用 + 实例化对象)
Student student1 = new Student();
// 2. 访问对象属性并赋值
student1.name = "啊阿狸不会拉杆";
student1.age = 20;
student1.major = "人工智能";
// 3. 调用对象方法
student1.introduce();
student1.study();
// 创建第二个对象
Student student2 = new Student();
student2.name = "北海";
student2.age = 21;
student2.major = "电子信息";
student2.introduce();
student2.study();
}
}运行结果:

Java 中,对象和引用的存储位置不同,理解这一点对掌握 Java 内存模型至关重要。
内存分配流程图

代码演示内存分配:
public class MemoryDemo {
public static void main(String[] args) {
// 基本类型变量存储在栈中
int a = 10;
double b = 3.14;
// 对象引用存储在栈中,对象实例存储在堆中
Student s1 = new Student();
s1.name = "王五";
s1.age = 22;
// s2引用指向s1引用的对象(堆中同一个对象)
Student s2 = s1;
s2.major = "人工智能"; // 修改s2的属性,s1也会变化
System.out.println("s1的专业:" + s1.major); // 输出"人工智能"
System.out.println("s2的专业:" + s2.major); // 输出"人工智能"
}
}运行结果:

注意:当多个引用指向同一个对象时,修改任意引用的属性都会影响该对象本身。
UML(统一建模语言)类图是描述类结构的标准化图形。用 PlantUML 表示类图:
@startuml
class Student {
- String name
- int age
- String major
+ void study()
+ void introduce()
}
class StudentTest {
+ void main(String[] args)
}
StudentTest --> Student : 使用
@enduml
UML 类图说明:
[可见性] 属性名: 类型(-表示私有,+表示公有)[可见性] 方法名(参数): 返回类型方法是类的行为,用于实现特定功能。本节学习方法的设计原则、调用方式、重载、构造方法等核心知识。
设计方法应遵循 "单一职责原则":一个方法只做一件事,保持方法简洁易懂。
方法定义语法:
[访问修饰符] [static] 返回值类型 方法名([参数类型 参数名1, 参数类型 参数名2...]) {
// 方法体
[return 返回值;] // 如果返回值类型不是void,必须有return语句
}示例:设计一个计算器类的方法
/**
* 简单计算器类
*/
public class Calculator {
/**
* 加法计算
* @param a 第一个加数
* @param b 第二个加数
* @return 两个数的和
*/
public int add(int a, int b) {
return a + b; // 返回计算结果
}
/**
* 减法计算
* @param a 被减数
* @param b 减数
* @return 两个数的差
*/
public int subtract(int a, int b) {
return a - b;
}
/**
* 打印计算结果
* @param result 要打印的结果
*/
public void printResult(int result) { // void表示无返回值
System.out.println("计算结果:" + result);
}
}方法调用分为实例方法调用和静态方法调用(4.4 节详细讲解)。实例方法需要通过对象调用。
调用流程

示例:调用计算器类的方法
public class CalculatorTest {
public static void main(String[] args) {
// 创建计算器对象
Calculator calculator = new Calculator();
// 调用加法方法
int sum = calculator.add(10, 20);
calculator.printResult(sum); // 输出:计算结果:30
// 调用减法方法
int difference = calculator.subtract(50, 15);
calculator.printResult(difference); // 输出:计算结果:35
}
}
方法重载(Overload):在同一个类中,允许存在多个同名方法,只要它们的参数列表不同(参数个数、类型或顺序不同)。
作用:提高代码可读性,用相同的方法名实现相似功能。
示例:方法重载实践
/**
* 方法重载示例
*/
public class OverloadDemo {
// 1. 两个int相加
public int add(int a, int b) {
System.out.println("调用int+int的add方法");
return a + b;
}
// 2. 三个int相加(参数个数不同)
public int add(int a, int b, int c) {
System.out.println("调用int+int+int的add方法");
return a + b + c;
}
// 3. 两个double相加(参数类型不同)
public double add(double a, double b) {
System.out.println("调用double+double的add方法");
return a + b;
}
// 4. int和double相加(参数顺序不同)
public double add(int a, double b) {
System.out.println("调用int+double的add方法");
return a + b;
}
public static void main(String[] args) {
OverloadDemo demo = new OverloadDemo();
System.out.println("结果1:" + demo.add(10, 20));
System.out.println("结果2:" + demo.add(10, 20, 30));
System.out.println("结果3:" + demo.add(3.14, 2.5));
System.out.println("结果4:" + demo.add(10, 3.14));
}
}运行结果:

注意:方法重载与返回值类型无关,仅由参数列表决定。
构造方法(Constructor):用于初始化对象的特殊方法,在创建对象时自动调用。
特点:
static、final、abstract修饰分类:
示例:构造方法的使用
/**
* 构造方法示例
*/
public class ConstructorDemo {
String name;
int age;
// 1. 无参构造方法
public ConstructorDemo() {
System.out.println("调用无参构造方法");
name = "默认姓名";
age = 0;
}
// 2. 有参构造方法(方法重载)
public ConstructorDemo(String n, int a) {
System.out.println("调用有参构造方法");
name = n;
age = a;
}
public void introduce() {
System.out.println("姓名:" + name + ",年龄:" + age);
}
public static void main(String[] args) {
// 使用无参构造方法创建对象
ConstructorDemo obj1 = new ConstructorDemo();
obj1.introduce(); // 输出:姓名:默认姓名,年龄:0
// 使用有参构造方法创建对象
ConstructorDemo obj2 = new ConstructorDemo("张三", 20);
obj2.introduce(); // 输出:姓名:张三,年龄:20
}
}
this关键字代表当前对象,用于:
this(参数),必须放在构造方法第一行)return this;)示例:this 关键字的应用
/**
* this关键字示例
*/
public class ThisDemo {
String name;
int age;
// 1. 使用this区分成员变量和局部变量
public ThisDemo(String name, int age) {
this.name = name; // this.name指成员变量,name指参数
this.age = age;
}
// 2. 使用this调用其他构造方法
public ThisDemo() {
this("默认姓名", 0); // 调用有参构造方法,必须在第一行
}
// 3. 使用this返回当前对象
public ThisDemo setName(String name) {
this.name = name;
return this; // 返回当前对象,支持链式调用
}
public ThisDemo setAge(int age) {
this.age = age;
return this;
}
public void introduce() {
System.out.println("姓名:" + this.name + ",年龄:" + this.age);
}
public static void main(String[] args) {
ThisDemo obj1 = new ThisDemo("张三", 20);
obj1.introduce(); // 输出:姓名:张三,年龄:20
ThisDemo obj2 = new ThisDemo();
// 链式调用:因为setName和setAge返回当前对象
obj2.setName("李四").setAge(22);
obj2.introduce(); // 输出:姓名:李四,年龄:22
}
}
Java 中方法参数传递只有值传递:
示例:参数传递机制演示
/**
* 参数传递机制演示
*/
public class ParameterPassing {
// 基本类型参数传递
public void modifyPrimitive(int num) {
num = 100; // 修改的是副本,不影响原变量
System.out.println("方法内num:" + num);
}
// 引用类型参数传递
public void modifyObject(Student student) {
student.age = 30; // 修改对象属性,会影响原对象
// 修改引用本身,不影响原引用
student = new Student();
student.name = "新学生";
student.age = 18;
System.out.println("方法内对象:" + student.name + "," + student.age);
}
public static void main(String[] args) {
ParameterPassing demo = new ParameterPassing();
// 测试基本类型传递
int num = 10;
demo.modifyPrimitive(num);
System.out.println("方法外num:" + num); // 仍为10,未被修改
// 测试引用类型传递
Student s = new Student();
s.name = "原学生";
s.age = 20;
demo.modifyObject(s);
System.out.println("方法外对象:" + s.name + "," + s.age); // 年龄变为30
}
}
// 复用之前定义的Student类
class Student {
String name;
int age;
}运行结果:

被static修饰的变量和方法称为静态变量(类变量)和静态方法(类方法),它们属于类本身,而非对象。
静态变量:用static修饰,属于类,所有对象共享同一内存空间,在类加载时初始化。
作用:存储所有对象共享的数据,如计数器、常量等。
示例:静态变量的使用
/**
* 静态变量示例
*/
public class StaticVariableDemo {
// 实例变量:每个对象有独立的副本
String name;
// 静态变量:所有对象共享
static int count; // 计数器,统计创建的对象数量
public StaticVariableDemo(String name) {
this.name = name;
count++; // 每次创建对象,计数器加1
}
public static void main(String[] args) {
// 静态变量可以通过类名直接访问
System.out.println("初始count:" + StaticVariableDemo.count);
StaticVariableDemo obj1 = new StaticVariableDemo("对象1");
System.out.println("创建对象1后count:" + StaticVariableDemo.count);
StaticVariableDemo obj2 = new StaticVariableDemo("对象2");
System.out.println("创建对象2后count:" + StaticVariableDemo.count);
// 也可以通过对象访问(不推荐)
System.out.println("通过obj1访问count:" + obj1.count);
}
}运行结果:

静态方法:用static修饰,属于类,可通过类名直接调用,无需创建对象。
特点:
this和super关键字示例:静态方法的使用
/**
* 静态方法示例
*/
public class StaticMethodDemo {
static int a = 10; // 静态变量
int b = 20; // 实例变量
// 静态方法
public static void staticMethod() {
System.out.println("静态方法访问静态变量a:" + a);
// 静态方法不能访问实例变量
// System.out.println("静态方法访问实例变量b:" + b); // 编译错误
// 静态方法可以调用其他静态方法
anotherStaticMethod();
// 静态方法不能使用this
// System.out.println(this.a); // 编译错误
}
// 另一个静态方法
public static void anotherStaticMethod() {
System.out.println("这是另一个静态方法");
}
// 实例方法
public void instanceMethod() {
// 实例方法可以访问静态变量和静态方法
System.out.println("实例方法访问静态变量a:" + a);
staticMethod();
// 实例方法可以访问实例变量
System.out.println("实例方法访问实例变量b:" + b);
}
public static void main(String[] args) {
// 静态方法通过类名直接调用
StaticMethodDemo.staticMethod();
// 实例方法需要通过对象调用
StaticMethodDemo obj = new StaticMethodDemo();
obj.instanceMethod();
}
}
单例模式:确保一个类只有一个实例,并提供全局访问点,属于创建型设计模式。
实现方式:
示例 1:饿汉式单例
/**
* 饿汉式单例模式
*/
public class SingletonHungry {
// 私有静态实例,类加载时初始化
private static SingletonHungry instance = new SingletonHungry();
// 私有构造方法,防止外部创建实例
private SingletonHungry() {}
// 公有静态方法,提供全局访问点
public static SingletonHungry getInstance() {
return instance;
}
public void showMessage() {
System.out.println("饿汉式单例模式:Hello Singleton!");
}
}示例 2:懒汉式单例(线程安全)
/**
* 懒汉式单例模式(线程安全)
*/
public class SingletonLazy {
// 私有静态实例,初始为null
private static SingletonLazy instance;
// 私有构造方法
private SingletonLazy() {}
// 公有静态方法,首次使用时创建实例
// synchronized确保线程安全
public static synchronized SingletonLazy getInstance() {
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
public void showMessage() {
System.out.println("懒汉式单例模式:Hello Singleton!");
}
}单例模式测试:
/**
* 单例模式测试类
*/
public class SingletonTest {
public static void main(String[] args) {
// 测试饿汉式单例
SingletonHungry hungry1 = SingletonHungry.getInstance();
SingletonHungry hungry2 = SingletonHungry.getInstance();
System.out.println("饿汉式是否为同一实例:" + (hungry1 == hungry2)); // true
hungry1.showMessage();
// 测试懒汉式单例
SingletonLazy lazy1 = SingletonLazy.getInstance();
SingletonLazy lazy2 = SingletonLazy.getInstance();
System.out.println("懒汉式是否为同一实例:" + (lazy1 == lazy2)); // true
lazy1.showMessage();
}
}
递归:方法调用自身的编程技巧,用于解决可分解为相似子问题的问题(如阶乘、斐波那契数列、树遍历等)。
递归三要素:
示例 1:递归计算阶乘
/**
* 递归计算阶乘
*/
public class RecursionFactorial {
/**
* 计算n的阶乘
* n! = n × (n-1) × ... × 1
* 递归关系:n! = n × (n-1)!
* 终止条件:0! = 1
*/
public static int factorial(int n) {
// 递归终止条件
if (n == 0) {
return 1;
}
// 递归调用:n! = n × (n-1)!
return n * factorial(n - 1);
}
public static void main(String[] args) {
int num = 5;
int result = factorial(num);
System.out.println(num + "的阶乘是:" + result); // 输出:5的阶乘是:120
}
}
示例 2:递归计算斐波那契数列
/**
* 递归计算斐波那契数列
*/
public class RecursionFibonacci {
/**
* 计算斐波那契数列第n项
* 斐波那契数列:1, 1, 2, 3, 5, 8...
* 递归关系:f(n) = f(n-1) + f(n-2)
* 终止条件:f(1) = 1, f(2) = 1
*/
public static int fibonacci(int n) {
// 递归终止条件
if (n == 1 || n == 2) {
return 1;
}
// 递归调用
return fibonacci(n - 1) + fibonacci(n - 2);
}
public static void main(String[] args) {
int n = 10;
System.out.println("斐波那契数列第" + n + "项是:" + fibonacci(n)); // 输出:55
}
}
对象从创建到销毁有完整的生命周期,本节学习对象的初始化顺序、垃圾回收机制和变量作用域。
实例变量(非静态成员变量)的初始化顺序:
{ }包裹的代码)示例:实例变量初始化顺序
/**
* 实例变量初始化顺序演示
*/
public class InstanceInitialization {
// 1. 默认初始化(int默认0)
int a;
// 2. 显式初始化
int b = 20;
// 3. 实例代码块
{
c = 30;
System.out.println("执行实例代码块,c=" + c);
}
int c; // 注意:这里可以先使用后定义(但不推荐)
// 4. 构造方法初始化
public InstanceInitialization() {
a = 10;
System.out.println("执行构造方法,a=" + a);
}
public static void main(String[] args) {
InstanceInitialization obj = new InstanceInitialization();
System.out.println("最终a=" + obj.a + ", b=" + obj.b + ", c=" + obj.c);
}
}运行结果:
执行实例代码块,c=30
执行构造方法,a=10
最终a=10, b=20, c=30静态变量的初始化顺序:
static { }包裹的代码)特点:静态初始化只在类加载时执行一次,优先于实例初始化。
示例:静态变量初始化顺序
/**
* 静态变量初始化顺序演示
*/
public class StaticInitialization {
// 1. 默认初始化(static int默认0)
static int x;
// 2. 显式初始化
static int y = 200;
// 3. 静态代码块
static {
x = 100;
z = 300; // 可以先使用后定义(不推荐)
System.out.println("执行静态代码块,x=" + x + ", z=" + z);
}
static int z;
// 实例相关
int m = 10;
{
System.out.println("执行实例代码块,m=" + m);
}
public StaticInitialization() {
System.out.println("执行构造方法");
}
public static void main(String[] args) {
System.out.println("第一次创建对象:");
StaticInitialization obj1 = new StaticInitialization();
System.out.println("\n第二次创建对象:");
StaticInitialization obj2 = new StaticInitialization();
System.out.println("\n静态变量最终值:x=" + x + ", y=" + y + ", z=" + z);
}
}运行结果:
执行静态代码块,x=100, z=300
第一次创建对象:
执行实例代码块,m=10
执行构造方法
第二次创建对象:
执行实例代码块,m=10
执行构造方法
静态变量最终值:x=100, y=200, z=300注意:静态初始化只执行一次,无论创建多少对象都不会重复执行。
垃圾回收器(Garbage Collector, GC):JVM 自动管理内存的机制,负责回收不再被引用的对象所占用的内存。
工作原理:
相关方法:
System.gc():建议 JVM 执行垃圾回收(但不确定立即执行)finalize():对象被回收前调用(不推荐依赖此方法)示例:垃圾回收演示
/**
* 垃圾回收演示
*/
public class GarbageCollectionDemo {
// 重写finalize方法
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("对象被垃圾回收器回收了!");
}
public static void main(String[] args) {
// 创建对象
GarbageCollectionDemo obj1 = new GarbageCollectionDemo();
GarbageCollectionDemo obj2 = new GarbageCollectionDemo();
// 让对象失去引用
obj1 = null;
obj2 = null;
// 建议JVM执行垃圾回收
System.gc();
// 给GC一些时间执行
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}可能的运行结果:
对象被垃圾回收器回收了!
对象被垃圾回收器回收了!注意:垃圾回收的时机由 JVM 决定,
System.gc()只是建议,不能保证立即执行。
变量作用域:变量可被访问的范围,分为:
变量生存期:变量从创建到销毁的时间,取决于变量类型和作用域。
示例:变量作用域演示
/**
* 变量作用域演示
*/
public class VariableScopeDemo {
// 成员变量:作用域为整个类
int memberVar = 100; // 实例成员变量
static int staticVar = 200; // 静态成员变量
public void method1() {
// 局部变量:作用域限于method1方法
int localVar1 = 10;
System.out.println("method1访问成员变量:" + memberVar);
System.out.println("method1访问局部变量:" + localVar1);
}
public void method2() {
// 局部变量:与method1的localVar1同名但不冲突
int localVar1 = 20;
System.out.println("method2访问局部变量:" + localVar1);
// 代码块内的局部变量
if (true) {
int blockVar = 30; // 作用域限于当前if代码块
System.out.println("代码块内访问blockVar:" + blockVar);
}
// 编译错误:超出blockVar的作用域
// System.out.println("代码块外访问blockVar:" + blockVar);
}
public static void main(String[] args) {
VariableScopeDemo obj = new VariableScopeDemo();
obj.method1();
obj.method2();
// 静态方法可以访问静态成员变量
System.out.println("main方法访问静态变量:" + staticVar);
// 静态方法不能直接访问实例成员变量
// System.out.println("main方法直接访问实例变量:" + memberVar); // 编译错误
// 通过对象访问实例成员变量
System.out.println("main方法通过对象访问实例变量:" + obj.memberVar);
}
}包(Package)用于组织类,避免类名冲突,类似于文件夹的作用。
包的定义:使用package关键字,放在 Java 源文件第一行,格式:
package 包名; // 例如:package com.example.demo;命名规范:
com.公司名.项目名.模块名)com.example对应目录com/example)示例:定义包
创建文件com/example/package_demo/MyPackageClass.java:
// 定义包(必须在第一行)
package com.example.package_demo;
/**
* 包演示类
*/
public class MyPackageClass {
public void showMessage() {
System.out.println("这是com.example.package_demo包中的类");
}
}当需要使用其他包的类时,需要导入:
import 包名.类名;import 包名.*;(不推荐,可能引发冲突)包名.类名(不导入直接使用)示例:导入和使用其他包的类
创建文件com/example/package_demo/ImportDemo.java:
package com.example.package_demo;
// 显式导入Java标准库的类
import java.util.Date;
// 导入当前包的类(同一包下可以省略,但显式导入更清晰)
import com.example.package_demo.MyPackageClass;
/**
* 类的导入演示
*/
public class ImportDemo {
public static void main(String[] args) {
// 使用导入的类
MyPackageClass myClass = new MyPackageClass();
myClass.showMessage();
// 使用导入的Date类
Date date = new Date();
System.out.println("当前时间:" + date);
// 使用全限定类名(不导入直接使用)
java.util.Scanner scanner = new java.util.Scanner(System.in);
System.out.println("请输入一个数字:");
int num = scanner.nextInt();
System.out.println("你输入的数字是:" + num);
scanner.close();
}
}Java 编译单元:一个.java源文件,有以下规则:
public类,类名必须与文件名相同public类package语句最多一个,必须在第一行(可选)import语句在package之后,类定义之前(可选)示例:Java 编译单元结构
文件名:CompilationUnitDemo.java(必须与 public 类名相同)
// 1. 包声明(可选)
package com.example.demo;
// 2. 导入语句(可选)
import java.util.ArrayList;
import java.util.List;
// 3. public类(最多一个,与文件名相同)
public class CompilationUnitDemo {
public static void main(String[] args) {
System.out.println("这是public类");
NonPublicClass obj = new NonPublicClass();
obj.show();
}
}
// 4. 非public类(可以多个)
class NonPublicClass {
public void show() {
System.out.println("这是非public类");
}
}
// 5. 另一个非public类
class AnotherClass {
// 类内容
}本章我们学习了 Java 面向对象编程的核心概念 —— 类和对象,主要内容包括:
面向对象编程是 Java 的核心思想,掌握类和对象的使用是学好 Java 的基础,后续的继承、多态等高级特性都基于这些基础知识。

设计一个学生信息管理系统,实现添加学生、显示所有学生信息、统计学生数量的功能,综合运用类和对象、静态变量、构造方法、方法重载等知识点。
完整代码:
package com.example.studentmanagement;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/**
* 学生类
*/
class Student {
// 学生属性
private String id; // 学号
private String name; // 姓名
private int age; // 年龄
private String major; // 专业
// 无参构造方法
public Student() {}
// 有参构造方法(方法重载)
public Student(String id, String name, int age, String major) {
this.id = id;
this.name = name;
this.age = age;
this.major = major;
}
// getter和setter方法
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
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 String getMajor() {
return major;
}
public void setMajor(String major) {
this.major = major;
}
// 显示学生信息
public void showInfo() {
System.out.println("学号:" + id + ",姓名:" + name +
",年龄:" + age + ",专业:" + major);
}
}
/**
* 学生管理类
*/
class StudentManager {
// 静态变量:统计学生总数
private static int totalStudents = 0;
// 存储学生对象的集合
private List<Student> students = new ArrayList<>();
/**
* 添加学生(方法重载1:通过Student对象添加)
*/
public void addStudent(Student student) {
students.add(student);
totalStudents++; // 学生总数加1
}
/**
* 添加学生(方法重载2:通过参数添加)
*/
public void addStudent(String id, String name, int age, String major) {
Student student = new Student(id, name, age, major);
students.add(student);
totalStudents++; // 学生总数加1
}
/**
* 显示所有学生信息
*/
public void showAllStudents() {
if (students.isEmpty()) {
System.out.println("暂无学生信息!");
return;
}
System.out.println("=== 所有学生信息 ===");
for (Student student : students) {
student.showInfo();
}
}
/**
* 获取学生总数(静态方法)
*/
public static int getTotalStudents() {
return totalStudents;
}
}
/**
* 主程序类
*/
public class StudentManagementSystem {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
StudentManager manager = new StudentManager();
while (true) {
System.out.println("\n=== 学生信息管理系统 ===");
System.out.println("1. 添加学生");
System.out.println("2. 显示所有学生");
System.out.println("3. 显示学生总数");
System.out.println("4. 退出");
System.out.print("请选择操作:");
int choice = scanner.nextInt();
scanner.nextLine(); // consume newline
switch (choice) {
case 1:
System.out.print("请输入学号:");
String id = scanner.nextLine();
System.out.print("请输入姓名:");
String name = scanner.nextLine();
System.out.print("请输入年龄:");
int age = scanner.nextInt();
scanner.nextLine(); // consume newline
System.out.print("请输入专业:");
String major = scanner.nextLine();
// 调用添加学生方法
manager.addStudent(id, name, age, major);
System.out.println("学生添加成功!");
break;
case 2:
manager.showAllStudents();
break;
case 3:
System.out.println("当前学生总数:" + StudentManager.getTotalStudents());
break;
case 4:
System.out.println("谢谢使用,再见!");
scanner.close();
return;
default:
System.out.println("无效选择,请重新输入!");
}
}
}
}设计一个单例模式的日志工具类,提供日志输出功能,确保整个应用只有一个日志实例。
完整代码:
package com.example.singletonlog;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* 单例模式日志工具类
*/
public class Logger {
// 私有静态实例(懒汉式)
private static Logger instance;
// 私有构造方法,防止外部创建
private Logger() {}
// 公有静态方法,提供全局访问点(线程安全)
public static synchronized Logger getInstance() {
if (instance == null) {
instance = new Logger();
}
return instance;
}
/**
* 输出普通日志
*/
public void info(String message) {
String time = getCurrentTime();
System.out.println("[" + time + "] [INFO] " + message);
}
/**
* 输出错误日志
*/
public void error(String message) {
String time = getCurrentTime();
System.err.println("[" + time + "] [ERROR] " + message);
}
/**
* 获取当前时间字符串
*/
private String getCurrentTime() {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return LocalDateTime.now().format(formatter);
}
// 测试单例日志工具
public static void main(String[] args) {
// 获取日志实例
Logger logger1 = Logger.getInstance();
Logger logger2 = Logger.getInstance();
// 验证是否为同一实例
System.out.println("logger1和logger2是否为同一实例:" + (logger1 == logger2));
// 使用日志工具
logger1.info("应用程序启动");
logger1.info("用户登录成功");
logger2.error("数据库连接失败");
logger2.info("应用程序退出");
}
}以上就是本章的全部内容,希望通过理论讲解和实践案例,能帮助大家掌握类和对象的核心知识。动手练习是学好编程的关键,建议大家亲自运行代码,并尝试修改和扩展功能,加深理解。如有问题欢迎在评论区交流! 😊