欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 焦点 > JAVAfx项目总结 算法题

JAVAfx项目总结 算法题

2025/5/18 18:21:16 来源:https://blog.csdn.net/2402_89056915/article/details/148002040  浏览:    关键词:JAVAfx项目总结 算法题

一、长短链接

1、短连接

对于短链接,在写完这个项目的理解是这样的,对于发给后端的每一个请求都有一个新建socket,当完成前端和后端的通信或者断开时即可给这个socket关闭。还有写的过程中查询资料室知的一些知识点。

(1)短链接的优点

a.对于服务器的资源占用较少

b.相对长连接实现起来相对简单

c.服务器不需要记录用户端的状态

(2)短链接的缺点

a.会频繁的和后端建立链接,断开连接

b.延迟高,不利于实时通信

c.不适用于高并发,很消耗后端的CPU和内存

(3)短链接的应用场景

a.低频请求

b.无状态服务(包括静态资源请求下载,登录注册忘记密码)

c.高安全性要求(银行等交易场所,避免信息被盗窃)

以下就是短链接的基本实现代码

用户端

import java.io.*;

import java.net.Socket;

public class ShortConnectionClient {

    public static void main(String[] args) {

        String serverAddress = "localhost";

        int port = 8080;

        

        try {

            // 每次请求都创建新的Socket连接

            Socket socket = new Socket(serverAddress, port);

            

            // 获取输出流,向服务器发送数据

            PrintWriter out = new PrintWriter(socket.getOutputStream(), true);

            out.println("Hello Server!");

            

            // 获取输入流,读取服务器响应

            BufferedReader in = new BufferedReader(

                new InputStreamReader(socket.getInputStream()));

            String response = in.readLine();

            System.out.println("Server response: " + response);

            

            // 关闭连接

            socket.close();

        } catch (IOException e) {

            e.printStackTrace();

        }

    }

}

后端接收

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class ShortConnectionServer {
    public static void main(String[] args) {
        int port = 8080;
        
        try (ServerSocket serverSocket = new ServerSocket(port)) {
            System.out.println("Server started on port " + port);
            
            while (true) {
                // 接受客户端连接(每次都是新的连接)
                Socket clientSocket = serverSocket.accept();
                System.out.println("Client connected: " + clientSocket.getInetAddress());
                
                // 处理客户端请求
                handleClient(clientSocket);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    private static void handleClient(Socket clientSocket) {
        try (
            BufferedReader in = new BufferedReader(
                new InputStreamReader(clientSocket.getInputStream()));
            PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true)
        ) {
            // 读取客户端消息
            String message = in.readLine();
            System.out.println("Received from client: " + message);
            
            // 发送响应
            out.println("Hello Client! Your message: " + message);
            
            // 关闭连接(短链接特点)
            clientSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

观了别人写的代码后我自己又突然有了想法,是不是后端可以有多个端口,负责不同的业务,于是我进行了查询,以下便是我的理解。

后端多端口

对于后端多个端口,每个端口都有自己负责的业务,可以有短链接请求端口,长连接建立端口,以及资源等其他业务请求端口。

(1)优点

a.业务需求多样化

b.性能优化

c.安全性 && 隔离

2.长连接

对于长连接,我的理解是当用户登录之后进入了主界面时,长连接也要建立了,而建立长连接,则是通过前端和后端while循环来建立,源源不断的看是否有请求的传递。

(1)长连接的优点

a.减少连接建立和断开的开销

b.适合频繁通信的场景

c.保持会话状态

d.更快的响应速度

(2)长连接的缺点

a.占用服务器资源

b.需要心跳机制维护

c.网络环境适应性较

d.复杂性较高

(3)长连接的应用场景

a.实时通信(qq,微信这种聊天软件等)

b.高频短数据交互

c.数据库/缓存连接池

d.API网关/微服务通信

长连接基本代码实现

用户端

import java.io.*;
import java.net.*;
import java.util.concurrent.*;

public class LongConnectionClient {
    private static final String HOST = "localhost";
    private static final int PORT = 8888;
    private static final ScheduledExecutorService scheduler = 
        Executors.newScheduledThreadPool(1);

    public static void main(String[] args) {
        try {
            Socket socket = new Socket(HOST, PORT);
            socket.setSoTimeout(5000); // 设置读取超时
            
            PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
            BufferedReader in = new BufferedReader(
                new InputStreamReader(socket.getInputStream()));
            
            // 发送业务消息
            for (int i = 0; i < 5; i++) {
                String message = "业务消息_" + i;
                out.println(message);
                
                String response = in.readLine();
                System.out.println("收到响应:" + response);
                
                Thread.sleep(2000);
            }
            
            // 关闭连接
            scheduler.shutdown();
            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void startHeartbeat(PrintWriter out) {
        // 每10秒发送一次心跳
        scheduler.scheduleAtFixedRate(() -> {
            try {
                out.println("HEARTBEAT");
                System.out.println("发送心跳");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, 0, 10, TimeUnit.SECONDS);
    }
}

服务器

import java.io.*;
import java.net.*;
import java.util.concurrent.*;

public class LongConnectionServer {
    private static final int PORT = 8888;
    private static final int MAX_THREADS = 100;
    private static final ExecutorService threadPool = Executors.newFixedThreadPool(MAX_THREADS);

    public static void main(String[] args) {
        try (ServerSocket serverSocket = new ServerSocket(PORT)) {
            System.out.println("长连接服务端启动,监听端口:" + PORT);
            
            while (true) {
                Socket clientSocket = serverSocket.accept();
                // 设置读写超时时间(毫秒)
                clientSocket.setSoTimeout(30000);
                // 长连接线程
                threadPool.execute(new ClientHandler(clientSocket));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    static class ClientHandler implements Runnable {
        private final Socket socket;
        private long lastHeartbeatTime;

        public ClientHandler(Socket socket) {
            this.socket = socket;
            this.lastHeartbeatTime = System.currentTimeMillis();
        }

        @Override
        public void run() {
            try (BufferedReader in = new BufferedReader(
                    new InputStreamReader(socket.getInputStream()));
                 PrintWriter out = new PrintWriter(
                    socket.getOutputStream(), true)) {
                
                System.out.println("客户端连接:" + socket.getRemoteSocketAddress());

                String inputLine;
                while ((inputLine = in.readLine()) != null) {
                    // 心跳检测
                    if ("HEARTBEAT".equals(inputLine)) {
                        lastHeartbeatTime = System.currentTimeMillis();
                        out.println("HEARTBEAT_ACK");
                        continue;
                    }
                    
                    // 处理业务逻辑
                    System.out.println("收到消息:" + inputLine);
                    String response = "处理结果:" + inputLine;
                    out.println(response);
                    
                    // 检查连接是否超时(30秒无心跳)
                    if (System.currentTimeMillis() - lastHeartbeatTime > 30000) {
                        System.out.println("连接超时,关闭连接");
                        break;
                    }
                }
            } catch (SocketTimeoutException e) {
                System.out.println("读取超时,关闭连接");
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    socket.close();
                    System.out.println("连接关闭:" + socket.getRemoteSocketAddress());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

二、序列化

1.序列化是将对象转换为字节流的过程,以便存储或传输;

2.反序列化则是将字节流恢复为对象。它在分布式系统、数据持久化和网络通信中起着关键作用。

3. 序列化的核心作用

(1)对象持久化(存储到文件/数据库)

  • 将内存中的对象保存到磁盘,程序重启后可恢复。

  • 示例:游戏存档、用户会话保存。

(2)网络传输(跨进程/跨机器通信)

  • 对象 → 字节流 → 网络传输 → 字节流 → 对象。

  • 示例:RPC调用、微服务通信。

(3)深拷贝(Deep Copy)

  • 通过序列化/反序列化实现对象的完全复制。

  • 示例:避免Java的浅拷贝问题。

(4)跨语言数据交换

  • 通用序列化格式(如JSON、Protocol Buffers)支持不同语言解析。

  • 示例:Java服务与Python客户端通信。

4. 序列化的核心意义

(1)解决对象传输问题

  • 网络只能传输字节流,序列化是对象传输的基础。

(2)实现分布式系统通信

  • 微服务、RPC、消息队列(如Kafka)依赖序列化传递对象。

(3)保证数据一致性

  • 序列化协议定义了数据的编码规则,避免解析错误。

(4)提高系统扩展性

  • 通过版本兼容的序列化协议(如Protobuf),支持字段动态增减。

四、IO流

在项目中,IO流主要用于文件读写和网络数据传输,核心使用了以下类:

1. 字节流(Byte Streams)

FileInputStream`/`FileOutputStream  
  用于读写二进制文件(如图片、视频)。

// 读取文件
  try (FileInputStream fis = new FileInputStream("file.bin")) {
      byte[] buffer = new byte[1024];
      int bytesRead;
      while ((bytesRead = fis.read(buffer)) != -1) {
          // 处理数据
      }
  }

  // 写入文件
  try (FileOutputStream fos = new FileOutputStream("output.bin")) {
      fos.write(dataBytes);
  }

2. 字符流(Character Streams)


BufferedReader`/`BufferedWriter 

// 读取文本文件
  try (BufferedReader br = new BufferedReader(new FileReader("config.txt"))) {
      String line;
      while ((line = br.readLine()) != null) {
          System.out.println(line);
      }
  }

  // 写入文本文件
  try (BufferedWriter bw = new BufferedWriter(new FileWriter("log.txt"))) {
      bw.write("Log message");
      bw.newLine();
  }

3. 对象流(Object Streams)


ObjectInputStream`/`ObjectOutputStream
  用于序列化和反序列化Java对象(如用户数据持久化)。


  // 序列化对象到文件
  try (ObjectOutputStream oos = new ObjectOutputStream(
          new FileOutputStream("user.dat"))) {
      oos.writeObject(user);
  }

  // 从文件反序列化对象
  try (ObjectInputStream ois = new ObjectInputStream(
          new FileInputStream("user.dat"))) {
      User user = (User) ois.readObject();
  }

4. 网络IO(Socket流)


Socket.getInputStream()`/`Socket.getOutputStream()
  用于网络通信,结合`BufferedReader`和`PrintWriter`处理文本协议。

// 服务端读取客户端数据
  try (BufferedReader in = new BufferedReader(
          new InputStreamReader(socket.getInputStream()))) {
      String message = in.readLine();
  }

  // 客户端发送数据到服务端
  try (PrintWriter out = new PrintWriter(
          socket.getOutputStream(), true)) {
      out.println("Hello Server!");
  }

五、多线程在项目中的应用


多线程主要用于并发处理客户端请求和异步任务。

1. 线程池管理


ExecutorService
  避免频繁创建/销毁线程,提升性能。

ExecutorService threadPool = Executors.newFixedThreadPool(10);
  threadPool.execute(() -> {
      // 处理客户端请求
  });

2. 线程同步


synchronized`关键字
  保护共享资源(如数据库连接池)。
 

public synchronized void updateData() {
      // 线程安全操作
  }

六、文件分片上传/下载


通过IO流实现大文件的分块处理。

1. 文件分片上传


// 客户端分片发送
try (FileInputStream fis = new FileInputStream("large_file.zip");
     BufferedInputStream bis = new BufferedInputStream(fis)) {
    byte[] buffer = new byte[1024 * 1024]; // 1MB分片
    int bytesRead;
    while ((bytesRead = bis.read(buffer)) != -1) {
        socket.getOutputStream().write(buffer, 0, bytesRead);
    }
}

2. 服务端分片接收


try (FileOutputStream fos = new FileOutputStream("received_file.zip");
     BufferedOutputStream bos = new BufferedOutputStream(fos)) {
    byte[] buffer = new byte[1024 * 1024];
    int bytesRead;
    while ((bytesRead = socket.getInputStream().read(buffer)) != -1) {
        bos.write(buffer, 0, bytesRead);
    }
}

七、MySQL数据库操作


使用JDBC进行CRUD操作。

1. 数据库连接


String url = "jdbc:mysql://localhost:3306/mydb";
String user = "root";
String password = "123456";
try (Connection conn = DriverManager.getConnection(url, user, password)) {
    // 执行SQL
}

2. 查询数据


String sql = "SELECT * FROM users WHERE id = ?";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
    stmt.setInt(1, 1001);
    ResultSet rs = stmt.executeQuery();
    while (rs.next()) {
        String name = rs.getString("name");
    }
}

3. 插入/更新数据


String sql = "INSERT INTO users (name, age) VALUES (?, ?)";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
    stmt.setString(1, "Alice");
    stmt.setInt(2, 25);
    stmt.executeUpdate();
}

总结


| 技术       | 在项目中的应用                                                     |
|-------------|------------------------------------------------------------------|
| IO流       | 文件读写、网络数据传输、对象序列                     |
| 多线程    | 并发处理客户端请求任务执行                     |
| 文件分片 | 大文件的上传与下载                                              |
| MySQL   | 用户数据存储、查询和更新                                    |
| JavaFX   | 构建图形界面,与后端Socket/数据库交互             |

通过结合这些技术,项目实现了:  


1.客户端与服务端的稳定通信(长短链接结合)  
 2.高效文件传输(分片处理)  
 3.数据持久化(MySQL + 序列化)  
 4.用户友好的图形界面(JavaFX)

一、算法题

1.

解题思路:正常的并查集,加一个map来实现string->int即可

#include <iostream>
#include <vector>
#include <stack>
#include <queue>
#include <climits>
#include <algorithm>
#include <map>
#define ll long long
using namespace std;int n, m, p;int find(int x, vector<int>& parent) {if (parent[x] != x)parent[x] = find(parent[x], parent);return parent[x];
}void setUnion(int a, int b, vector<int>& parent, vector<int>& rank) {a = find(a,parent);b = find(b,parent);if (a == b)return;int x1 = min(a, b);int x2 = max(a, b);if (rank[x1] > rank[x2])parent[x2] = x1;else if (rank[x1] < rank[x2])parent[x2] = x1;else {parent[x1] = x2;rank[x2]++;}}bool isConnect(int a, int b, vector<int>& parent) {a = find(a, parent);b = find(b, parent);return a == b;
}int main() {cin >> n >> m;vector<int>parent(n + 1, 0); // 节点的上一级map<string,int> tt;vector<int>rank(n + 1, 0); // 建立最小的树for (int i = 1; i <= n; i++) { // 先指向自身parent[i] = i;string s; cin >> s;tt[s] = i;}for (int i = 1; i <= m; i++) { // 建立亲戚关系string a, b; cin >> a >> b;int x1 = tt[a], x2 = tt[b];setUnion(x1, x2, parent, rank);}cin >> p;for (int i = 1; i <= p; i++) {string a, b; cin >> a >> b;int x1 = tt[a], x2 = tt[b];if (isConnect(x1, x2, parent))cout << "Yes." << endl;elsecout << "No." << endl;}return 0;
}

 2.

解题思路: 并查集,限定了合并条件,根据题目的合并条件合成即可

#include <iostream>
#include <vector>
#include <stack>
#include <queue>
#include <climits>
#include <algorithm>
#include <map>
#define ll long long
using namespace std;map<string, string> parent;// 查找并返回name的最早祖先,并进行路径压缩
string findAncestor(string name) {if (name != parent[name]) {parent[name] = findAncestor(parent[name]);}return parent[name];
}int main() {char prefix;string name;string current_parent;cin >> prefix;while (prefix != '$') {cin >> name;if (prefix == '#') {current_parent = name;if (parent.find(name) == parent.end()) {parent[name] = name; // 初始化父亲为自身}} else if (prefix == '+') {parent[name] = current_parent;} else if (prefix == '?') {cout << name << ' ' << findAncestor(name) << endl;}cin >> prefix;}return 0;
}

3.

解题思路:把题目看成图,每个点都是顶点,距离都是权重,就可以用kruskal了。

#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
#include <iomanip>
using namespace std;struct Edge {int u, v;double w;bool operator<(const Edge& other) const {return w < other.w;}
};vector<Edge> edges;  
vector<int> parent;  
int S, P;  
vector<pair<int, int>> d; int find(int x) {if (parent[x] != x) {parent[x] = find(parent[x]);}return parent[x];
}void unionset(int x, int y) {int rootX = find(x);int rootY = find(y);if (rootX != rootY) {parent[rootY] = rootX;}
}double kruskal() {parent.resize(P + 1);for (int i = 1; i <= P; i++) {parent[i] = i;}sort(edges.begin(), edges.end());double ans = 0.0;int k = P - S; for (const auto& edge : edges) {if (find(edge.u) != find(edge.v)) {unionset(edge.u, edge.v);ans = edge.w;k--;if (k == 0) {break;}}}return ans;
}int main() {cin >> S >> P;d.resize(P + 1);for (int i = 1; i <= P; i++) {cin >> d[i].first >> d[i].second;}for (int i = 1; i <= P; i++) {for (int j = i + 1; j <= P; j++) {double dx = d[i].first - d[j].first;double dy = d[i].second - d[j].second;double dis = sqrt(dx * dx + dy * dy);edges.push_back({ i, j, dis });}}double ans = kruskal();cout << fixed << setprecision(2) << ans << endl;return 0;
}

 4.

解题思路:开始想了好久,发现N<=6,这个数量好像可以直接遍历所有的情况来讨论,但是对于面积的计算还是有点迷,就看了下别人的思路。让放下的油滴尽可能的扩大,相加所有的面积,每次枚举都取面积的最大值

#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
#include <iomanip>using namespace std;double area(int N, const vector<pair<int, int>>& points, int left, int right, int bottom, int top) {double max_total_area = 0.0;vector<int> indices(N);for (int i = 0; i < N; ++i) {indices[i] = i;}do {vector<double> radii(N, 0.0);double total_area = 0.0;for (int i = 0; i < N; ++i) {int x = points[indices[i]].first;int y = points[indices[i]].second;double min_r = min({x - left, right - x, y - bottom, top - y});for (int j = 0; j < i; ++j) {int xj = points[indices[j]].first;int yj = points[indices[j]].second;double dx = x - xj;double dy = y - yj;double distance = sqrt(dx * dx + dy * dy);min_r = min(min_r, distance - radii[j]);if (min_r <= 0) break;}if (min_r > 0) {radii[i] = min_r;total_area += M_PI * min_r * min_r;}}if (total_area > max_total_area) {max_total_area = total_area;}} while (next_permutation(indices.begin(), indices.end()));double area_rect = (right - left) * (top - bottom);return area_rect - max_total_area;
}int main() {int N;cin >> N;int x1, y1, x2, y2;cin >> x1 >> y1 >> x2 >> y2;int left = min(x1, x2);int right = max(x1, x2);int bottom = min(y1, y2);int top = max(y1, y2);vector<pair<int, int>> points(N);for (int i = 0; i < N; ++i) {cin >> points[i].first >> points[i].second;}sort(points.begin(), points.end());double remaining_area = area(N, points, left, right, bottom, top);cout << static_cast<int>(round(remaining_area)) << endl;return 0;
}

5.

解题思路:第一次写这种题,我最开始就是直接用dfs来搜索,但是时间超限,然后我看了一下别人的思路,可以用个二维数组存储已经知道的每个的位置的最大值,当dfs到这个位置,直接从二维数组里取出来就行了,这个方法也是记忆化搜索

#include <iostream>
#include <vector>
#include <stack>
#include <queue>
#include <climits>
#include <algorithm>
#include <map>
#define ll long long
using namespace std;// 定义四个方向
int dirs[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};// 全局变量存储矩阵和dp数组
vector<vector<int>> matrix;
vector<vector<int>> dp;
int R, C;// DFS函数,返回从(x, y)出发的最长滑坡长度
int dfs(int x, int y) {// 如果已经计算过,直接返回结果if (dp[x][y] != -1) {return dp[x][y];}// 初始化为1,因为至少可以滑到自己dp[x][y] = 1;// 遍历四个方向for (int i = 0; i < 4; ++i) {int newX = x + dirs[i][0];int newY = y + dirs[i][1];// 检查边界条件和高度是否下降if (newX >= 0 && newX < R && newY >= 0 && newY < C && matrix[newX][newY] < matrix[x][y]) {// 递归计算相邻点的最长滑坡长度,并更新当前点的dp值dp[x][y] = max(dp[x][y], dfs(newX, newY) + 1);}}return dp[x][y];
}int main() {// 读取行数和列数cin >> R >> C;matrix.assign(R, vector<int>(C));for (int i = 0; i < R; ++i) {for (int j = 0; j < C; ++j) {cin >> matrix[i][j];}}// 初始化dp数组为-1,表示未计算dp.assign(R, vector<int>(C, -1));// 遍历所有点,计算最长滑坡长度int maxLength = 0;for (int i = 0; i < R; ++i) {for (int j = 0; j < C; ++j) {if (dp[i][j] == -1) { // 如果尚未计算dfs(i, j);}maxLength = max(maxLength, dp[i][j]);}}// 输出结果cout << maxLength << endl;return 0;
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词