今天我们先了解StringBuilder、BigDecimal、Date、LocalDateTime这些前置核心技术,再手把手带你开发一个「局域网群聊软件」。
目录
[1. 字符串拼接神器:StringBuilder](#1. 字符串拼接神器:StringBuilder)
[为什么需要 StringBuilder?](#为什么需要 StringBuilder?)
[2. 高精度计算:BigDecimal](#2. 高精度计算:BigDecimal)
[为什么需要 BigDecimal?](#为什么需要 BigDecimal?)
[3. 日期时间处理:Date vs LocalDateTime](#3. 日期时间处理:Date vs LocalDateTime)
[① 传统日期类:Date(不推荐)](#① 传统日期类:Date(不推荐))
[② 现代日期类:LocalDateTime(推荐)](#② 现代日期类:LocalDateTime(推荐))
[1. 项目需求](#1. 项目需求)
[2. 技术选型](#2. 技术选型)
[3. 核心架构](#3. 核心架构)
[4. 分步实现](#4. 分步实现)
[5. 运行项目](#5. 运行项目)
一、前置核心技术:
1. 字符串拼接神器:StringBuilder
为什么需要 StringBuilder?
Java 中的**
String** 是不可变对象 ,每次拼接字符串(比如用+)都会创建新对象,频繁拼接时性能极低。而StringBuilder是可变的字符序列,专门解决字符串拼接效率问题。
核心用法
| 方法 | 作用 |
|---|---|
append() |
追加字符串 / 数字 / 对象等 |
toString() |
转换为最终的 String |
入门示例:
java
// 拼接用户消息(项目中会用到)
public class StringBuilderDemo {
public static void main(String[] args) {
StringBuilder sb = new StringBuilder();
// 拼接发送者、时间、消息内容
sb.append("张三")
.append(" 2024-08-08 10:00:00")
.append("\n")
.append("大家好呀~")
.append("\n");
// 转换为最终字符串
String msg = sb.toString();
System.out.println(msg);
}
}
2. 高精度计算:BigDecimal
为什么需要 BigDecimal?
float和**double** 会有精度丢失问题(比如0.1 + 0.2 ≠ 0.3),涉及金额、精准计算时必须用BigDecimal。
核心用法
| 方法 | 作用 |
|---|---|
BigDecimal(String val) |
构造方法(推荐用字符串入参) |
add() |
加法 |
subtract() |
减法 |
multiply() |
乘法 |
divide() |
除法(需指定精度) |
入门示例:
java
import java.math.BigDecimal;
public class BigDecimalDemo {
public static void main(String[] args) {
// 避免用double入参,防止精度丢失
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.2");
// 加法
BigDecimal sum = a.add(b);
System.out.println("0.1 + 0.2 = " + sum); // 输出0.3
// 除法(指定保留2位小数,四舍五入)
BigDecimal c = new BigDecimal("10");
BigDecimal d = new BigDecimal("3");
BigDecimal divide = c.divide(d, 2, BigDecimal.ROUND_HALF_UP);
System.out.println("10 ÷ 3 = " + divide); // 输出3.33
}
}
3. 日期时间处理:Date vs LocalDateTime
① 传统日期类:Date(不推荐)
Date是 Java 早期的日期类,存在线程不安全、API 设计混乱(比如月份从 0 开始)等问题,仅做了解即可。
简单示例:
java
import java.util.Date;
public class DateDemo {
public static void main(String[] args) {
// 获取当前时间
Date now = new Date();
System.out.println("当前时间(Date):" + now); // 格式不直观
}
}
② 现代日期类:LocalDateTime(推荐)
Java 8 推出的LocalDateTime解决了Date的所有痛点,线程安全、API 清晰、支持格式化,是开发首选。
核心用法
| 方法 | 作用 |
|---|---|
LocalDateTime.now() |
获取当前时间 |
DateTimeFormatter.ofPattern() |
定义时间格式 |
format() |
格式化时间为字符串 |
入门示例:
java
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class LocalDateTimeDemo {
public static void main(String[] args) {
// 1. 获取当前时间
LocalDateTime now = LocalDateTime.now();
// 2. 定义格式化规则:年-月-日 时:分:秒 星期 上/下午
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss EEE a");
// 3. 格式化时间(项目中用于拼接消息时间)
String nowStr = dtf.format(now);
System.out.println("格式化后的时间:" + nowStr);
// 输出示例:2024-08-08 10:15:30 周四 上午
}
}
二、实战项目:局域网群聊软件
学会了前置技术,我们用一个「局域网群聊软件」把知识落地!这个项目能实现:输入昵称登录、实时显示在线人数、群聊消息即时发送 / 接收。
1. 项目需求
- 登录界面:仅需输入昵称即可登录;
- 群聊界面:展示在线人数、消息展示框、消息输入框、发送按钮;
- 核心功能:实时更新在线人数、群聊消息即时通讯。
2. 技术选型
- GUI 界面:Swing(Java 内置的图形界面工具,入门友好);
- 网络通信:Socket(实现客户端与服务端的 TCP 通信);
- 基础工具:前面学的LocalDateTime(格式化消息时间)、StringBuilder(拼接消息内容)。
3. 核心架构
整个项目分为「服务端」和「客户端」两部分:
- 服务端:监听客户端连接、存储在线用户、转发群聊消息、更新在线人数;
- 客户端:登录界面、聊天界面、与服务端通信(发送 / 接收消息)。
4. 分步实现
第一步:登录界面
**功能:**输入昵称,点击「进入」连接服务端,点击「取消」退出程序。
java
package com.itheima.ui;
import javax.swing.*;
import java.awt.*;
public class ChatEntryFrame extends JFrame {
private JTextField nicknameField; // 昵称输入框
private JButton enterButton; // 进入按钮
private JButton cancelButton; // 取消按钮
public ChatEntryFrame() {
// 窗口基础设置
setTitle("局域网聊天室-登录");
setSize(350, 150);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null); // 窗口居中
setResizable(false); // 禁止调整大小
// 布局设置(顶部:昵称输入;底部:按钮)
JPanel mainPanel = new JPanel(new BorderLayout());
mainPanel.setBackground(Color.decode("#F0F0F0"));
add(mainPanel);
// 顶部:昵称输入区域
JPanel topPanel = new JPanel();
JLabel nicknameLabel = new JLabel("昵称:");
nicknameLabel.setFont(new Font("楷体", Font.BOLD, 16));
nicknameField = new JTextField(10);
nicknameField.setFont(new Font("楷体", Font.PLAIN, 16));
topPanel.add(nicknameLabel);
topPanel.add(nicknameField);
mainPanel.add(topPanel, BorderLayout.NORTH);
// 底部:按钮区域
JPanel buttonPanel = new JPanel();
enterButton = new JButton("进入");
enterButton.setBackground(Color.decode("#007BFF"));
enterButton.setForeground(Color.WHITE);
cancelButton = new JButton("取消");
cancelButton.setBackground(Color.decode("#DC3545"));
cancelButton.setForeground(Color.WHITE);
buttonPanel.add(enterButton);
buttonPanel.add(cancelButton);
mainPanel.add(buttonPanel, BorderLayout.SOUTH);
// 按钮事件:进入聊天室
enterButton.addActionListener(e -> {
String nickname = nicknameField.getText();
if (!nickname.isEmpty()) {
dispose(); // 关闭登录窗口
// 连接服务端+打开聊天界面(后续补充)
new ClientChatFrame(nickname);
} else {
JOptionPane.showMessageDialog(this, "请输入昵称!");
}
});
// 按钮事件:取消退出
cancelButton.addActionListener(e -> System.exit(0));
this.setVisible(true);
}
public static void main(String[] args) {
new ChatEntryFrame(); // 启动登录界面
}
}
第二步:聊天界面
**功能:**展示消息、输入消息、显示在线人数、发送消息。
java
package com.itheima.ui;
import javax.swing.*;
import java.awt.*;
public class ClientChatFrame extends JFrame {
public JTextArea smsContent = new JTextArea(23, 50); // 消息展示框
private JTextArea smsSend = new JTextArea(4, 40); // 消息输入框
public JList<String> onLineUsers = new JList<>(); // 在线用户列表
private JButton sendBn = new JButton("发送"); // 发送按钮
public ClientChatFrame(String nickname) {
initView();
// 后续补充:连接服务端、接收/发送消息的逻辑
this.setVisible(true);
}
private void initView() {
// 窗口基础设置
this.setTitle("局域网聊天室-群聊");
this.setSize(700, 600);
this.setLayout(new BorderLayout());
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
// 消息展示框:不可编辑、带滚动条
smsContent.setEditable(false);
smsContent.setBackground(new Color(0xdd, 0xdd, 0xdd));
JScrollPane smsContentScrollPane = new JScrollPane(smsContent);
this.add(smsContentScrollPane, BorderLayout.CENTER);
// 底部:消息输入+发送按钮
JPanel bottomPanel = new JPanel(new BorderLayout());
JScrollPane smsSendScrollPane = new JScrollPane(smsSend);
bottomPanel.add(smsSendScrollPane, BorderLayout.CENTER);
bottomPanel.add(sendBn, BorderLayout.EAST);
this.add(bottomPanel, BorderLayout.SOUTH);
// 右侧:在线用户列表
JScrollPane userListScrollPane = new JScrollPane(onLineUsers);
userListScrollPane.setPreferredSize(new Dimension(120, 500));
this.add(userListScrollPane, BorderLayout.EAST);
// 发送按钮事件(后续补充:发送消息到服务端)
sendBn.addActionListener(e -> {
String msg = smsSend.getText();
if (!msg.isEmpty()) {
// 发送消息逻辑(后续补充)
smsSend.setText(""); // 清空输入框
}
});
}
}
第三步:服务端核心逻辑
**功能:**监听端口、接收客户端连接、处理登录 / 群聊消息、转发消息、更新在线人数。
java
package com.itheima;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public class Server {
// 存储在线用户:key=客户端Socket,value=昵称
public static final Map<Socket, String> onLineSockets = new HashMap<>();
private static final int PORT = 8888; // 监听端口
public static void main(String[] args) {
System.out.println("服务端启动中...");
try {
ServerSocket serverSocket = new ServerSocket(PORT);
// 循环监听客户端连接
while (true) {
System.out.println("等待客户端连接...");
Socket socket = serverSocket.accept(); // 接收客户端连接
new ServerReaderThread(socket).start(); // 开启线程处理该客户端
System.out.println("客户端连接成功:" + socket.getInetAddress());
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 处理客户端消息的线程类
static class ServerReaderThread extends Thread {
private Socket socket;
public ServerReaderThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
DataInputStream dis = new DataInputStream(socket.getInputStream());
// 循环接收客户端消息(1=登录,2=群聊)
while (true) {
int type = dis.readInt();
switch (type) {
case 1:
// 处理登录:接收昵称,更新在线列表
String nickname = dis.readUTF();
onLineSockets.put(socket, nickname);
updateOnLineUserList(); // 更新所有客户端的在线人数
break;
case 2:
// 处理群聊:接收消息,转发给所有在线客户端
String msg = dis.readUTF();
sendMsgToAll(nickname, msg); // 转发消息
break;
}
}
} catch (Exception e) {
// 客户端下线:移除socket,更新在线列表
System.out.println("客户端下线:" + socket.getInetAddress());
onLineSockets.remove(socket);
updateOnLineUserList();
}
}
// 给所有在线客户端转发群聊消息(用到StringBuilder+LocalDateTime)
private void sendMsgToAll(String nickname, String msg) {
// 1. 拼接消息(昵称+时间+内容)
StringBuilder sb = new StringBuilder();
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
sb.append(nickname)
.append(" [")
.append(dtf.format(now))
.append("]: \n")
.append(msg)
.append("\n");
String sendMsg = sb.toString();
// 2. 转发给所有在线客户端
for (Socket s : onLineSockets.keySet()) {
try {
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
dos.writeInt(2); // 标记为群聊消息
dos.writeUTF(sendMsg);
dos.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 更新所有客户端的在线用户列表
private void updateOnLineUserList() {
Collection<String> names = onLineSockets.values();
for (Socket s : onLineSockets.keySet()) {
try {
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
dos.writeInt(1); // 标记为在线列表消息
dos.writeInt(names.size()); // 在线人数
for (String name : names) {
dos.writeUTF(name); // 逐个发送昵称
}
dos.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
第四步:客户端通信逻辑
**功能:**连接服务端、发送登录 / 群聊消息、接收服务端的在线列表 / 群聊消息。
java
// 补充到ClientChatFrame类中
private Socket socket;
private DataOutputStream dos;
public ClientChatFrame(String nickname) {
initView();
// 连接服务端
try {
socket = new Socket("127.0.0.1", 8888); // 本地测试用127.0.0.1
dos = new DataOutputStream(socket.getOutputStream());
// 发送登录消息(类型1+昵称)
dos.writeInt(1);
dos.writeUTF(nickname);
dos.flush();
// 开启线程接收服务端消息
new ClientReaderThread().start();
} catch (Exception e) {
JOptionPane.showMessageDialog(this, "连接服务端失败!");
e.printStackTrace();
}
this.setVisible(true);
}
// 接收服务端消息的线程
class ClientReaderThread extends Thread {
@Override
public void run() {
try {
DataInputStream dis = new DataInputStream(socket.getInputStream());
while (true) {
int type = dis.readInt();
switch (type) {
case 1:
// 接收在线列表:更新右侧用户列表
int size = dis.readInt();
String[] names = new String[size];
for (int i = 0; i < size; i++) {
names[i] = dis.readUTF();
}
onLineUsers.setListData(names); // 更新列表
break;
case 2:
// 接收群聊消息:展示到消息框
String msg = dis.readUTF();
smsContent.append(msg); // 追加消息
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 补充发送按钮的事件(发送群聊消息)
sendBn.addActionListener(e -> {
String msg = smsSend.getText();
if (!msg.isEmpty()) {
try {
dos.writeInt(2); // 标记为群聊消息
dos.writeUTF(msg);
dos.flush();
smsSend.setText(""); // 清空输入框
} catch (Exception ex) {
JOptionPane.showMessageDialog(this, "发送消息失败!");
}
}
});
5. 运行项目
- 先启动Server类(服务端);
- 启动ChatEntryFrame类(客户端登录界面);
- 输入昵称,点击「进入」,打开聊天界面;
- 启动多个客户端,即可实现群聊和在线人数实时更新。
三、总结
- 掌握了StringBuilder(高效拼接)、BigDecimal(精准计算)、LocalDateTime(日期处理)这些 Java 核心工具类;
- 用 Swing+Socket 开发了一个简易的局域网群聊软件,把基础类落地到实战中。