java实现推箱子小游戏
本文实例为大家分享了java实现推箱子游戏的具体代码,供大家参考,具体内容如下
运行示例:
图形界面由swing组件构成
生成地图的算法如下
创建地图算法(produceMap):先将二维数组全置为1,代表初始状态全为墙。先随机产生两个不重合的点,一个作为人的起点,另一个作为箱子的起点。从起点开始,随机朝四个方向走一定的步数,若走到的点不是空地,即对应数组元素不为0,则将该点设为空地,即数组元素设为0。将地图分为左上、左下、右上和右下四个区域,并设置四个变量记录四个区域已开辟空地的数量。每开辟一块空地,代表该空地所在区域的变量加1。走完规定的步数后,在四个区域中找到开辟空地数量最小的区域,在该区域内随机产生一个点,以该点作为起点循环执行上述操作,直到开辟的空地的总数大于地图总面积的一半,创建完成。
解决地图算法(solveMap):创建一个状态类,将箱子的一个位置和在搜索这个箱子的过程中人的一个位置以及箱子走到这个位置的步数表示为一个状态。建立一个优先队列,队列元素为状态类的对象,步数小的状态的优先级高。从起点开始按照上下左右的顺序进行广度优先搜索。建立一个HashMap,键为状态类,值为布尔类,用来保存每个状态的是否被走过,再建立一个HashMap,键为点类,值也为点类,保存箱子的每一个位置的前一个位置,用于最后输出路径。使用优先队列可以使推动箱子次数多的状态推迟出队,从而保证箱子每个能推动的方向都被访问到并推动,若直接使用队列,则无法使每种情况都走到。而当地图较大的时候,采用HashMap存储访问信息可以节省较大的空间。优先队列每次执行出队操作的时候记录出队状态的步数,当当前出队状态的步数和上一个出队状态的步数不同的时候,将保存状态访问信息的HashMap清空。另设置一个HashMap键为点类,值为整型类,保存每个点箱子访问过几次,若超过四次,则不访问该点。由优先队列的性质可知,出队的状态的步数是队列中步数最小的状态,即当当前出队的状态的步数和前一个不同的时候,箱子前一步的状态已全部出队。这时箱子之前的访问状态已经不再需要,及时清空保存访问状态的HashMap可以节省很大的内存空间。当广搜搜到箱子在终点位置的状态,则搜索结束。若结束状态的步数小于规定的最小步数,则返回解决失败,否则返回解决成功。若未搜到箱子在终点位置的状态并且队列已空,则返回解决失败。
记录路径算法(recordPath):根据在解决地图的过程中建立的记录箱子每个位置前一个位置的一个HashMap,从终点开始,一步步找回起点,并将在这过程中找到的点按顺序入栈,然后再将栈中的点逐个出栈并记录在一个点的数组中,以实现路径的正序输出。
Main_Class.java
public class Main_Class { public static void main(String args[]) { PTB_Frame frame=new PTB_Frame("Push The Box"); frame.setBounds(0,0,1200,1200); } }
PTB_Frame.java
import java.awt.*; import java.awt.event.*; import javax.swing.*; @SuppressWarnings("serial") public class PTB_Frame extends JFrame { private Font font = new Font("宋体", Font.PLAIN, 23); private Map_Manager map_manager=new Map_Manager(); private ConsolePanel console = new ConsolePanel(map_manager); private JButton creat_map=new JButton("创建地图"); private MapPanel map=new MapPanel(map_manager); PTB_Frame(String title) { init(); this.setTitle(title); this.setVisible(true); this.setDefaultCloseOperation(EXIT_ON_CLOSE); } void init() { this.setLayout(null); console.setBounds(0, 0, 1200,250); console.add(creat_map); this.add(console); map.setBounds(80, 250, 1200, 800); this.add(map); creat_map.setFont(font); creat_map.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { console.creatMap(); map.setMap(map_manager.getHeight(), map_manager.getWidth(), map_manager.getStepOfMap(), map_manager.getMap()); map.creatMap(); } }); } }
ConsolePanel.java
import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @SuppressWarnings("serial") public class ConsolePanel extends JPanel { Font font = new Font("宋体", Font.PLAIN, 23); JTextField t_height = new JTextField(3); JTextField t_width = new JTextField(3); JTextField t_diff = new JTextField(5); JButton get_path = new JButton("查看最短路径"); JTextArea show_path = new JTextArea(5, 40); int height, width; double diff; Map_Manager map_manager; ConsolePanel(Map_Manager map_manager) { this.map_manager = map_manager; UIManager.put("Label.font", font); this.add(new JLabel("地图高度:")); t_height.setFont(font); this.add(t_height); this.add(new JLabel("(3~100的整数)")); this.add(new JLabel("地图宽度:")); t_width.setFont(font); this.add(t_width); this.add(new JLabel("(3~100的整数)")); this.add(new JLabel("地图难度:")); t_diff.setFont(font); this.add(t_diff); this.add(new JLabel("(1.0~10.0之间的小数)")); this.add(new JLabel("注:地图高度和宽度以及难度越大,生成地图时间越长")); get_path.setFont(font); this.add(get_path); show_path.setFont(font); show_path.setLineWrap(true);// 自动换行 show_path.setWrapStyleWord(true);// 换行不断字 JPanel show_path_panel = new JPanel(); show_path_panel.add(new JScrollPane(show_path));//滚动窗口 this.add(show_path_panel); get_path.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { Map_Manager.Point path[] = map_manager.getPath(); if (path != null) { show_path.setText(null); for (int i = 0; i<map_manager.getStepOfMap(); ++i) show_path.append(path[i] + " "); } } }); } public void creatMap() { try { height = Integer.valueOf(t_height.getText()); width = Integer.valueOf(t_width.getText()); diff = Double.valueOf(t_diff.getText()); if (height < 3 || height > 100 || width < 3 || width > 100 || diff < 1 || diff > 10) throw new NumberFormatException(); map_manager.setMap(height, width, diff); map_manager.creatMap(); show_path.setText(null); } catch (NumberFormatException ex) { JOptionPane.showMessageDialog(getRootPane(), "参数格式不正确"); } } }
MapPanel.java
import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.*; @SuppressWarnings("serial") public class MapPanel extends JPanel { private Font font = new Font("宋体", Font.PLAIN, 23); private JPanel map_area; JPanel control_bar = new JPanel(); private JButton drawback, restart,start; private JLabel l_min_step, l_left_step, l_passed_step; private JLabel show_cur_point; private JLabel show_cur_box; private Point person = new Point(); private Point start_of_person = new Point(); private Point start_of_box = new Point(); private Point end = new Point(); private Point box = new Point(); private int width, height; private int map[][] = new int[100][100]; private int di[][] = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } }; private int min_step, passed_step = 0; private Stack<Operation> operation_recorder = new Stack<Operation>(); private JButton block[][] = new JButton[100][100]; private Map_Manager map_manager; private class Point implements Cloneable { int x, y; public boolean equals(Object obj) { if (!(obj instanceof Point)) return false; Point point = (Point) obj; return this.x == point.x && this.y == point.y; } public Object clone() { Point newPoint = null; try { newPoint = (Point) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return newPoint; } public String toString() { return "(" + (this.x + 1) + "," + (this.y + 1) + ")"; } } public void setMap(int height, int width, int min_step, int map[][]) { this.height = height; this.width = width; this.min_step = min_step; this.map = map; } private class PtbKeyAdapter extends KeyAdapter { public void keyPressed(KeyEvent e) { int keycode = e.getKeyCode(); if (keycode == KeyEvent.VK_UP) { pointMove(0); } else if (keycode == KeyEvent.VK_DOWN) { pointMove(1); } else if (keycode == KeyEvent.VK_LEFT) { pointMove(2); } else if (keycode == KeyEvent.VK_RIGHT) { pointMove(3); } } } private class Operation { int stuff;// 1代表箱子,0代表人 int dir;// 0代表上,1代表下,2代表左,3代表右 Operation(int stuff, int dir) { this.stuff = stuff; this.dir = dir; } } private boolean accessible(Point point) {// point可走则返回true if (point.x < 0 || point.x >= height || point.y < 0 || point.y >= width)// 越界 return false; if (map[point.x][point.y] == 1)// 走到墙上 return false; return true; } private void pointMove(int dir) { if(passed_step>=min_step) return; // 先判断能否进行交换,若能,则交换两按钮颜色值 Point cur_point = new Point(); cur_point.x = person.x + di[dir][0]; cur_point.y = person.y + di[dir][1]; if (!accessible(cur_point))// 当前点不可走 return; if (cur_point.equals(box)) { // 当人前进方向上前一个点是箱子 Point next_box = new Point(); next_box.x = box.x + di[dir][0]; next_box.y = box.y + di[dir][1]; if (!accessible(next_box))// 箱子无法推动 return; // 如果箱子能前进,则人也前进,不能则人不能前进 go(box, dir); ++this.passed_step; updateStep(); operation_recorder.push(new Operation(1, dir)); } go(person, dir); show_cur_point.setText(" "+person.toString()+" "); operation_recorder.push(new Operation(0, dir)); if (box.equals(end)) JOptionPane.showMessageDialog(this.getRootPane(), "WINNER WINNER CHICKEN DINNER!"); else if(this.passed_step==this.min_step) JOptionPane.showMessageDialog(this.getRootPane(), "江河犹在,命数已尽,悲哉!"); } private void go(Point point, int dir) {// 实现前进部分的代码 Color color = block[point.x][point.y].getBackground(); if (point.equals(end)) block[point.x][point.y].setBackground(Color.GREEN); else block[point.x][point.y].setBackground(Color.WHITE); point.x += di[dir][0]; point.y += di[dir][1]; block[point.x][point.y].setBackground(color); } private void updateStep() { l_passed_step.setText(Integer.toString(passed_step)); l_left_step.setText(Integer.toString(min_step - passed_step)); show_cur_box.setText(" "+box.toString()+" "); } public void paintMap() { map_area = new JPanel(new GridLayout(this.height, this.width)); for (int i = 0; i < this.height; i++) for (int j = 0; j < this.width; j++) { block[i][j] = new JButton(); if (map[i][j] == 0)// 数组中0为路 block[i][j].setBackground(Color.WHITE); else if (map[i][j] == 1)// 数组中1为墙 block[i][j].setBackground(Color.BLACK); else if (map[i][j] == 2)// 数组中2为箱子位置 { block[i][j].setBackground(Color.BLUE); start_of_box.x = i; start_of_box.y = j; } else if (map[i][j] == 3)// 数组中3为终点 { block[i][j].setBackground(Color.GREEN); end.x = i; end.y = j; } else if (map[i][j] == 4) {// 数组中4为人的位置 block[i][j].setBackground(Color.RED); start_of_person.x = i; start_of_person.y = j; } map_area.add(block[i][j]); } person = (Point) start_of_person.clone(); box = (Point) start_of_box.clone(); l_min_step.setText(Integer.toString(min_step)); show_cur_point.setText(" "+person.toString()+" "); passed_step=0; updateStep(); int map_height=750,map_width=750; if(this.height>this.width) map_width=(750/this.height)*this.width; else if(this.width>this.height) map_height=(750/this.width)*this.height; map_area.setBounds(0, 0, map_width, map_height); this.add(map_area); } public MapPanel(Map_Manager map_manager) { this.map_manager = map_manager; init(); } private void init() { this.setLayout(null); map_manager.setMap(20, 20, 7.0); map_manager.creatMap(); this.height = map_manager.getHeight(); this.width = map_manager.getWidth(); this.min_step = map_manager.getStepOfMap(); this.map = map_manager.getMap(); UIManager.put("Label.font", font); drawback = new JButton("后退一步"); restart = new JButton("重新开始"); start=new JButton("开始"); control_bar.add(new JLabel("当前箱子的位置")); show_cur_box=new JLabel(); control_bar.add(show_cur_box); control_bar.add(new JLabel("当前人的位置")); show_cur_point=new JLabel(); control_bar.add(show_cur_point); control_bar.add(new JLabel("最短步数:")); l_min_step = new JLabel(); l_min_step.setFont(font); control_bar.add(l_min_step); control_bar.add(new JLabel("已走步数:")); l_passed_step = new JLabel(); l_passed_step.setFont(font); control_bar.add(l_passed_step); control_bar.add(new JLabel("剩余步数:")); l_left_step = new JLabel(); l_left_step.setFont(font); control_bar.add(l_left_step); control_bar.add(new JLabel("注:这里的步数是")); control_bar.add(new JLabel("箱子移动的步数")); control_bar.add(new JLabel("红色代表人")); control_bar.add(new JLabel("蓝色代表箱子")); control_bar.add(new JLabel("绿色代表终点")); restart.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { block[person.x][person.y].setBackground(Color.WHITE); person = (Point) start_of_person.clone(); block[box.x][box.y].setBackground(Color.WHITE); box = (Point) start_of_box.clone(); block[person.x][person.y].setBackground(Color.RED); block[box.x][box.y].setBackground(Color.BLUE); block[end.x][end.y].setBackground(Color.GREEN); passed_step = 0; show_cur_point.setText(" "+person.toString()+" "); updateStep(); } }); drawback.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (operation_recorder.empty()) return; Operation cur_op = operation_recorder.peek(); operation_recorder.pop(); int dir; switch (cur_op.dir) {// 得到相反方向 case 0: dir = 1; break; case 1: dir = 0; break; case 2: dir = 3; break; default: dir = 2; } // 推箱子的时候,箱子先走,人再走 // 不推箱子的时候直接就是人走,所以栈顶元素始终是人的操作 go(person, dir);// 人相反方向走一步 show_cur_point.setText(" "+person.toString()+" "); if (!operation_recorder.empty()&&operation_recorder.peek().stuff == 1) { // 下一个是箱子,即人从该位置推动的箱子 go(box, dir);// 箱子相反方向走一步 operation_recorder.pop(); --passed_step; updateStep(); } } }); PtbKeyAdapter ptbkeyadapter=new PtbKeyAdapter(); start.addKeyListener(ptbkeyadapter); restart.addKeyListener(ptbkeyadapter); drawback.addKeyListener(ptbkeyadapter); start.setFont(font); restart.setFont(font); drawback.setFont(font); control_bar.add(start); control_bar.add(restart); control_bar.add(drawback); control_bar.setBounds(850, 100, 200, 600); this.paintMap(); this.add(control_bar); } public void creatMap() { this.setLayout(null); this.map_area.removeAll(); this.map_area.setVisible(false); paintMap(); this.revalidate(); this.map_area.setVisible(true); } }
Map_Manager.java
import java.util.*; public class Map_Manager { private PTB_Map ptb_map = new PTB_Map(); private int di[][] = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } }; private double search_limit; private int area[] = new int[4]; private Map<Status, Boolean> visit_of_status = new HashMap<Status, Boolean>(); private Map<Point, Integer> visit_of_box = new HashMap<Point, Integer>(); private PriorityQueue<Status> q = new PriorityQueue<Status>(new Comparator<Status>() { public int compare(Status s1, Status s2) { return s1.step - s2.step; } }); private class PTB_Map { private int width; private int height; private int step_of_map; private int accessible_point; private Point end_p = new Point(); private Status start = new Status(); private int matrix[][] = new int[100][100]; private Map<Point, Point> path_map = new HashMap<Point, Point>(); private Point[] path = new Point[1000]; private double ratio_of_space; private double ratio_of_step; } public class Point implements Cloneable { int x, y; public boolean equals(Object obj) { if (!(obj instanceof Point)) return false; Point point = (Point) obj; return this.x == point.x && this.y == point.y; } public int hashCode() { return this.x * ptb_map.width + this.y; } public Object clone() { Point newPoint = null; try { newPoint = (Point) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return newPoint; } public String toString() { return "(" + (this.x + 1) + "," + (this.y + 1) + ")"; } } private class Status implements Cloneable { Point box = new Point(); Point person = new Point(); int step; public int hashCode() { return this.box.hashCode() * this.person.hashCode() + this.step; } public boolean equals(Object obj) { if (!(obj instanceof Status)) return false; Status status = (Status) obj; return this.box.equals(status.box) && this.person.equals(status.person) && this.step == status.step; } public Object clone() { Status newStatus = null; try { newStatus = (Status) super.clone(); newStatus.box = (Point) box.clone(); newStatus.person = (Point) person.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return newStatus; } } public void setMap(int height, int width, double degree_of_difficulty) { this.ptb_map.height = height; this.ptb_map.width = width; this.ptb_map.ratio_of_space = 0.5; double ratio = 1; if (height + width < 20) ratio = 0.05 * (height + width); this.ptb_map.ratio_of_step = 0.5 + degree_of_difficulty * 0.05 * ratio; this.search_limit = (this.ptb_map.height * this.ptb_map.width) / 10; } public int getHeight() { return ptb_map.height; } public int getWidth() { return ptb_map.width; } public int getStepOfMap() { return ptb_map.step_of_map; } private boolean accessible(Point point) {// point可走则返回true if (point.x < 0 || point.x >= ptb_map.height || point.y < 0 || point.y >= ptb_map.width)// 越界 return false; if (ptb_map.matrix[point.x][point.y] == 1)// 走到墙上 return false; return true; } private void creatSpace(Point point) { Random random = new Random(); int l = 0; while (l <= search_limit) { int dir = random.nextInt(4); if (point.x + di[dir][0] < 0 || point.x + di[dir][0] >= ptb_map.height || point.y + di[dir][1] < 0 || point.y + di[dir][1] >= ptb_map.width) continue;// 若往该方向走一步越界,则换个方向 point.x += di[dir][0]; point.y += di[dir][1]; if (this.ptb_map.matrix[point.x][point.y] != 0) { if (point.y < ptb_map.width / 2) { if (point.x < ptb_map.height / 2) ++area[0];// 该点在左上方 else ++area[1];// 该点在左下方 } else { if (point.x < ptb_map.height / 2) ++area[2];// 该点在右上方 else ++area[3];// 该点在右下方 } this.ptb_map.matrix[point.x][point.y] = 0; ++this.ptb_map.accessible_point; } ++l; } } private boolean produceMap() {// 返回值为地图是否创建成功 Random random = new Random(); // 重置地图的矩阵 for (int i = 0; i < ptb_map.height; ++i) for (int j = 0; j < ptb_map.width; ++j) ptb_map.matrix[i][j] = 1; do// 随机设置人和箱子的初始位置 { ptb_map.start.box.x = random.nextInt(ptb_map.height); ptb_map.start.box.y = random.nextInt(ptb_map.width); ptb_map.start.person.x = random.nextInt(ptb_map.height); ptb_map.start.person.y = random.nextInt(ptb_map.width); } while (ptb_map.start.box.equals(ptb_map.start.person)); ptb_map.accessible_point = 0; ptb_map.matrix[ptb_map.start.person.x][ptb_map.start.person.x] = 0; // 设置一定的墙 ptb_map.accessible_point = 0; Point start = (Point) ptb_map.start.person.clone();// 最开始走的位置为人的初始位置 area[0] = area[1] = area[2] = area[3] = 0; creatSpace(start); while (ptb_map.accessible_point < (ptb_map.height * ptb_map.width) * ptb_map.ratio_of_space) { int min = 10000, min_area = 0; for (int i = 0; i < 4; ++i) if (area[i] < min) { min = area[i]; min_area = i; } switch (min_area) { case 0:// 左上 start.x = random.nextInt(ptb_map.height / 2); start.y = random.nextInt(ptb_map.width / 2); break; case 1:// 左下 start.x = random.nextInt(ptb_map.height / 2) + ptb_map.height / 2; start.y = random.nextInt(ptb_map.width / 2); break; case 2:// 右上 start.x = random.nextInt(ptb_map.height / 2); start.y = random.nextInt(ptb_map.width / 2) + ptb_map.width / 2; break; case 3:// 右下 start.x = random.nextInt(ptb_map.height / 2) + ptb_map.height / 2; start.y = random.nextInt(ptb_map.width / 2) + ptb_map.width / 2; break; } creatSpace(start); } ptb_map.end_p = (Point) start.clone(); ptb_map.matrix[ptb_map.start.person.x][ptb_map.start.person.y] = 4; ptb_map.matrix[ptb_map.start.box.x][ptb_map.start.box.y] = 2; ptb_map.matrix[ptb_map.end_p.x][ptb_map.end_p.y] = 3; return true; } private boolean solveMap() {// 返回值为当前地图是否有路径 q.clear(); visit_of_box.clear(); ptb_map.path_map.clear(); ptb_map.step_of_map = 0; ptb_map.start.step = 0; int pre_step = -1; q.add(ptb_map.start); visit_of_status.put(ptb_map.start, true); while (!q.isEmpty()) { Status pre_Status = (Status) q.peek().clone(); if (pre_Status.step != pre_step) { visit_of_status.clear(); pre_step = pre_Status.step; } for (int i = 0; i < 4; ++i) { Status cur_Status = (Status) pre_Status.clone(); cur_Status.person.x += di[i][0]; cur_Status.person.y += di[i][1]; if (!accessible(cur_Status.person))// 该点不可走 continue; if (visit_of_status.containsKey(cur_Status))// 该点已经走过 continue; // 保存当前点的前一个,用于输出路径 if (cur_Status.person.equals(cur_Status.box))// 走到箱子上 { Point next_box = new Point();// 当前人会把箱子推到的位置 next_box.x = cur_Status.box.x + di[i][0]; next_box.y = cur_Status.box.y + di[i][1]; if (!accessible(next_box))// 该点不可走 continue; if (ptb_map.path_map.containsKey(next_box)) continue; if (visit_of_box.containsKey(next_box)) { if (visit_of_box.get(next_box) > 4)// 当前位置箱子已走过四次 continue; } // 箱子可以走到该点,则箱子走一步 ptb_map.path_map.put(next_box, cur_Status.box); cur_Status.box = next_box; ++cur_Status.step; if (!visit_of_box.containsKey(cur_Status.box)) visit_of_box.put(cur_Status.box, 1); else { int t = visit_of_box.get(cur_Status.box); ++t; visit_of_box.put(cur_Status.box, t); } if (cur_Status.box.equals(ptb_map.end_p))// 箱子走到终点 { ptb_map.step_of_map = cur_Status.step; q.clear(); if (ptb_map.step_of_map < (ptb_map.height + ptb_map.width) * ptb_map.ratio_of_step) return false; else return true; } } q.add(cur_Status); visit_of_status.put(cur_Status, true); } pre_Status = null; q.poll(); } return false; } public void recordPath() {// 记录路径 Stack<Point> output_path = new Stack<Point>();// 用于输出路径 Point cur_point = (Point) ptb_map.end_p.clone();// 从终点开始 int step = -1; while (step != ptb_map.step_of_map) { ++step; output_path.push(cur_point); cur_point = ptb_map.path_map.get(cur_point); if (cur_point == null) break; } int i = 0; while (!output_path.empty())// 将路径保存在点数组里 { ptb_map.path[i] = output_path.peek(); output_path.pop(); ++i; } } public void creatMap() { int i = 0; do { while (!produceMap()) ; ++i; } while (!solveMap()); recordPath(); printMap();// System.out.println(i);// } public int[][] getMap() { return ptb_map.matrix; } public Point getStartBoxPoint() { return ptb_map.start.box; } public Point getStartPersonPoint() { return ptb_map.start.person; } public Point getEndPoint() { return ptb_map.end_p; } private void printMap() { for (int i = 0; i < ptb_map.height; ++i) { System.out.print(ptb_map.matrix[i][0]); for (int j = 1; j < ptb_map.width; ++j) System.out.print(" " + ptb_map.matrix[i][j]); System.out.println(); } } public Point[] getPath() { return ptb_map.path; } }
更多精彩游戏,请参考专题《java经典小游戏》
更多有趣的经典小游戏实现专题,分享给大家:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
相关文章
Spring Boot 使用观察者模式实现实时库存管理的步骤
在现代软件开发中,实时数据处理非常关键,本文提供了一个使用SpringBoot和观察者模式开发实时库存管理系统的详细教程,步骤包括创建项目、定义实体类、实现观察者模式、集成Spring框架、创建RESTful API端点和测试应用等,这将有助于开发者构建能够即时响应库存变化的系统2024-09-09
最新评论