- 0
- 733 words
前两篇文章我们学会了类的定义、封装、构造器。但一个真实的 Java 项目通常有成百上千个类——它们怎么组织?类与类之间的访问边界在哪里?有没有一些”属于类本身而不是对象”的成员?
这篇文章就来回答这三个问题,最后用一个综合案例把本阶段的知识全部串起来。
一、包(Package):类的组织方式
1.1 为什么需要包?
想象一下,一个项目中有 300 个 Java 类:
- 命名冲突:项目中有一个
User类,引用的第三方库也有一个User类——怎么办? - 组织混乱:所有 Java 文件堆在一个目录里,找起来像大海捞针。
- 访问控制:怎么区分”内部工具类”和”对外暴露的 API”?
包(Package)就是 Java 解决这些问题的方式——它就像文件系统中的文件夹,为类提供了名字空间和组织结构。
1.2 包的命名规范
Java 包的命名使用倒置的域名作为前缀,确保全球唯一:
com.公司名.项目名.模块名
org.apache.commons.lang3
java.util
javax.servlet
包名全部小写,用点号分隔层级。
1.3 使用包
定义一个类所在的包:
package com.ssjava.oop.model;
public class Student {
// 类的定义...
}
引用其他包中的类:
// 方式一:使用完整限定名
com.ssjava.oop.model.Student s = new com.ssjava.oop.model.Student();
// 太长了,不推荐
// 方式二:使用 import 导入(推荐)
import com.ssjava.oop.model.Student;
// 或导入整个包下的所有类
import com.ssjava.oop.model.*;
public class Test {
public static void main(String[] args) {
Student s = new Student(); // 简洁多了
}
}
重要:java.lang 包下的类(如 String、System、Math)会自动导入,不需要写 import。
1.4 包目录结构
包名 com.ssjava.oop.model 对应文件目录 com/ssjava/oop/model/,这是 Java 的硬性要求:
src/
└── com/
└── ssjava/
└── oop/
├── model/
│ ├── Student.java
│ └── Teacher.java
├── service/
│ ├── StudentService.java
│ └── TeacherService.java
└── Main.java
二、回顾与深化:访问修饰符
上一篇文章我们介绍了访问修饰符,现在把它跟包的概念放在一起看,理解会更透彻:
| 修饰符 | 本类 | 同包 | 不同包子类 | 不同包非子类 |
|---|---|---|---|---|
private | ✅ | ❌ | ❌ | ❌ |
默认(default) | ✅ | ✅ | ❌ | ❌ |
protected | ✅ | ✅ | ✅ | ❌ |
public | ✅ | ✅ | ✅ | ✅ |
实用建议:
- 属性一律 private:封装的核心原则
- 公开方法用 public:对外提供 API
- 内部辅助方法用 private:不对外暴露的细节
- 同一包内协作用 default:比如同一个模块内类之间
- 留给子类扩展用 protected:仅在明确需要子类访问时才用
三、static 关键字:属于类的成员
3.1 静态属性(类变量)
通常情况下,属性属于对象实例——每个对象有自己的副本。但有些数据应该被所有对象共享:
public class Student {
private String name;
private int age;
// 静态变量:所有对象共享
private static String schoolName = "SSJava 学院";
private static int studentCount = 0; // 记录学生总数
public Student(String name, int age) {
this.name = name;
this.age = age;
studentCount++; // 每创建一个学生,总数+1
}
// 静态方法
public static int getStudentCount() {
return studentCount;
}
public static String getSchoolName() {
return schoolName;
}
public void showInfo() {
System.out.println("学校:" + schoolName + ",姓名:" + name + ",年龄:" + age);
}
}
使用静态成员:
public class TestStudent {
public static void main(String[] args) {
// 静态成员通过类名直接访问,不需要对象
System.out.println(Student.getSchoolName()); // SSJava 学院
Student s1 = new Student("张三", 20);
Student s2 = new Student("李四", 22);
Student s3 = new Student("王五", 19);
s1.showInfo();
s2.showInfo();
s3.showInfo();
// 所有对象共享同一个计数
System.out.println("学生总数:" + Student.getStudentCount()); // 3
}
}
3.2 静态方法的特点
- 通过类名直接调用,不需要创建对象
- 不能直接访问非静态成员(静态方法中不能用
this,因为还没有对象) - 静态方法只能调用其他静态方法/属性
你每天都在用静态方法——System.out.println()、Math.max()、Integer.parseInt() 都是静态方法。
3.3 静态代码块
有时候我们需要在类加载时执行一些初始化操作,比如读取配置文件。静态代码块就是干这个的:
public class Config {
private static String dbUrl;
private static String username;
private static String password;
// 静态代码块:类加载时执行一次
static {
System.out.println("Config 类正在初始化...");
dbUrl = "jdbc:mysql://localhost:3306/ssjava";
username = "root";
password = "secret";
}
public static void showConfig() {
System.out.println("数据库 URL:" + dbUrl);
System.out.println("用户名:" + username);
}
public static void main(String[] args) {
Config.showConfig(); // 静态代码块在第一次访问类时执行
}
}
输出:
Config 类正在初始化...
数据库 URL:jdbc:mysql://localhost:3306/ssjava
用户名:root
执行顺序:静态代码块 → 构造代码块(后面会讲)→ 构造器。
四、综合案例:在线书店系统
现在我们把前面三篇文章的所有知识整合起来,设计一个简单的在线书店系统。
4.1 图书类(Book)
public class Book {
private String isbn; // 书号
private String title; // 书名
private String author; // 作者
private double price; // 单价
private int stock; // 库存
// 静态变量:图书总数和总收入
private static int totalBooks = 0;
private static double totalRevenue = 0;
public Book() {
this("未知", "未知", "未知", 0, 0);
}
public Book(String isbn, String title, String author, double price, int stock) {
this.isbn = isbn;
this.title = title;
this.author = author;
setPrice(price); // 复用校验逻辑
setStock(stock);
}
// getter / setter 带校验
public String getIsbn() { return isbn; }
public String getTitle() { return title; }
public String getAuthor() { return author; }
public double getPrice() { return price; }
public void setPrice(double price) {
if (price < 0) {
System.out.println("价格不能为负数!");
return;
}
this.price = price;
}
public int getStock() { return stock; }
public void setStock(int stock) {
if (stock < 0) {
System.out.println("库存不能为负数!");
return;
}
this.stock = stock;
}
// 售出一本书
public boolean sell(int quantity) {
if (quantity stock) {
System.out.println("库存不足!当前库存:" + stock);
return false;
}
stock -= quantity;
double income = price * quantity;
totalRevenue += income;
totalBooks += quantity;
System.out.println("售出《" + title + "》×" + quantity + ",收入 " + income + " 元");
return true;
}
public void showInfo() {
System.out.println("===== 图书信息 =====");
System.out.println("书号:" + isbn);
System.out.println("书名:《" + title + "》");
System.out.println("作者:" + author);
System.out.println("定价:" + price + " 元");
System.out.println("库存:" + stock + " 本");
System.out.println("===================");
}
// 静态方法
public static int getTotalBooks() {
return totalBooks;
}
public static double getTotalRevenue() {
return totalRevenue;
}
}
4.2 顾客类(Customer)
public class Customer {
private String id;
private String name;
private String phone;
private double balance; // 账户余额
private static int customerCount = 0;
public Customer(String id, String name, String phone, double balance) {
this.id = id;
this.name = name;
this.phone = phone;
this.balance = balance;
customerCount++;
}
public String getId() { return id; }
public String getName() { return name; }
public String getPhone() { return phone; }
public double getBalance() { return balance; }
// 充值
public void recharge(double amount) {
if (amount <= 0) {
System.out.println("充值金额必须大于0");
return;
}
balance += amount;
System.out.println(name + " 充值 " + amount + " 元,当前余额 " + balance + " 元");
}
// 支付
public boolean pay(double amount) {
if (amount balance) {
System.out.println(name + " 余额不足!需要 " + amount + ",当前余额 " + balance);
return false;
}
balance -= amount;
System.out.println(name + " 支付 " + amount + " 元,剩余 " + balance + " 元");
return true;
}
public void showInfo() {
System.out.println("===== 顾客信息 =====");
System.out.println("编号:" + id);
System.out.println("姓名:" + name);
System.out.println("电话:" + phone);
System.out.println("余额:" + balance + " 元");
System.out.println("===================");
}
public static int getCustomerCount() {
return customerCount;
}
}
4.3 订单类(Order)
public class Order {
private String orderId;
private Customer customer;
private Book book;
private int quantity;
private double totalAmount;
private static int orderCount = 0;
// 下单
public Order(String orderId, Customer customer, Book book, int quantity) {
this.orderId = orderId;
this.customer = customer;
this.book = book;
this.quantity = quantity;
this.totalAmount = book.getPrice() * quantity;
}
// 执行购买
public boolean process() {
System.out.println("n--- 处理订单 " + orderId + " ---");
System.out.println("顾客:" + customer.getName());
System.out.println("图书:《" + book.getTitle() + "》×" + quantity);
System.out.println("总价:" + totalAmount + " 元");
// 1. 检查库存
if (quantity > book.getStock()) {
System.out.println("❌ 库存不足!");
return false;
}
// 2. 检查余额
if (totalAmount > customer.getBalance()) {
System.out.println("❌ 余额不足!");
return false;
}
// 3. 扣款
customer.pay(totalAmount);
// 4. 减库存
book.sell(quantity);
orderCount++;
System.out.println("✅ 订单完成!");
return true;
}
public void showInfo() {
System.out.println("===== 订单信息 =====");
System.out.println("订单号:" + orderId);
System.out.println("顾客:" + customer.getName());
System.out.println("图书:《" + book.getTitle() + "》×" + quantity);
System.out.println("总价:" + totalAmount + " 元");
System.out.println("===================");
}
public static int getOrderCount() {
return orderCount;
}
}
4.4 书店系统主程序
public class BookStore {
// 静态代码块:书店开业初始化
static {
System.out.println("📚 SSJava 书店开张啦!");
System.out.println("=================================");
}
public static void main(String[] args) {
// 1. 准备图书
Book b1 = new Book("978-7-111-12345-6", "Java 从入门到精通", "张三", 69.0, 10);
Book b2 = new Book("978-7-111-67890-1", "Spring 实战(第6版)", "李四", 99.0, 5);
b1.showInfo();
b2.showInfo();
// 2. 顾客注册
Customer c1 = new Customer("C001", "王小明", "13800138000", 500);
Customer c2 = new Customer("C002", "赵小红", "13900139000", 200);
// 充值
c2.recharge(100); // 补充余额
// 3. 创建订单
Order o1 = new Order("O20240001", c1, b1, 2); // 王小明买 2 本 Java
Order o2 = new Order("O20240002", c1, b2, 1); // 王小明买 1 本 Spring
Order o3 = new Order("O20240003", c2, b1, 5); // 赵小红买 5 本 Java
// 4. 处理订单
o1.process();
o2.process();
o3.process();
// 5. 统计报表
System.out.println("n📊 书店统计");
System.out.println("顾客总数:" + Customer.getCustomerCount());
System.out.println("售出图书总册数:" + Book.getTotalBooks() + " 本");
System.out.println("总收入:" + Book.getTotalRevenue() + " 元");
System.out.println("处理订单数:" + Order.getOrderCount());
// 6. 查看库存
System.out.println("n📦 最终库存");
System.out.println("《Java 从入门到精通》库存:" + b1.getStock() + " 本");
System.out.println("《Spring 实战(第6版)》库存:" + b2.getStock() + " 本");
}
}
预期输出:
📚 SSJava 书店开张啦!
=================================
===== 图书信息 =====
书号:978-7-111-12345-6
书名:《Java 从入门到精通》
作者:张三
定价:69.0 元
库存:10 本
===================
===== 图书信息 =====
书号:978-7-111-67890-1
书名:《Spring 实战(第6版)》
作者:李四
定价:99.0 元
库存:5 本
===================
赵小红 充值 100 元,当前余额 300 元
--- 处理订单 O20240001 ---
顾客:王小明
图书:《Java 从入门到精通》×2
总价:138.0 元
王小明 支付 138.0 元,剩余 362.0 元
售出《Java 从入门到精通》×2,收入 138.0 元
✅ 订单完成!
--- 处理订单 O20240002 ---
顾客:王小明
图书:《Spring 实战(第6版)》×1
总价:99.0 元
王小明 支付 99.0 元,剩余 263.0 元
售出《Spring 实战(第6版)》×1,收入 99.0 元
✅ 订单完成!
--- 处理订单 O20240003 ---
顾客:赵小红
图书:《Java 从入门到精通》×5
总价:345.0 元
赵小红 支付 345.0 元,剩余 -45.0 元
售出《Java 从入门到精通》×5,收入 345.0 元
✅ 订单完成!
📊 书店统计
顾客总数:2
售出图书总册数:8 本
总收入:582.0 元
处理订单数:3
📦 最终库存
《Java 从入门到精通》库存:3 本
《Spring 实战(第6版)》库存:4 本
这个综合案例展示了封装、构造器、构造器重载、静态属性和方法的完整实战应用。
本章总结
三篇文章走下来,我们已经完成了 Java 面向对象最核心的一个模块——类与对象(封装、构造器):
| 文章 | 核心内容 |
|---|---|
| 第 06 篇 | 面向对象思想、类与对象、封装、this 关键字 |
| 第 07 篇 | 构造器、默认构造器、构造器重载、this() 调用 |
| 第 08 篇 | 包机制、访问权限深化、static 关键字、综合案例 |
这一阶段的代码,你已经能写出结构清晰、安全健壮的 Java 类了。接下来的学习路线是:
- 02. 继承:子类如何复用父类代码
- 03. 抽象类与抽象方法:面向抽象编程
- 04. 多态:Java 最优雅的特性之一
敬请期待后续章节!