从零开发Java坦克大战:架构设计与难点突破 (下)

  • 6. 游戏引擎类:

    • 6.1 完整源码展示: 

    • import javax.swing.*;
      import java.awt.*;
      import java.awt.event.KeyEvent;
      import java.awt.event.KeyListener;
      import java.util.ArrayList;
      import java.util.HashSet;
      import java.util.Random;
      import java.util.Set;public class GamePanel extends JPanel implements KeyListener {//GamePanel类是游戏的核心控制器,负责管理游戏循环、输入处理和游戏状态更新。private TankA tankA;private TankB tankB;private final Set<Integer> pressedKeys = new HashSet<>();private final Timer gameTimer;private final Random ran = new Random();private final BattleMaps map;private final scorePanel sPanel;private final ArrayList<Bullet> bullets = new ArrayList<>();private boolean gameOver = false;private String winner = "";public GamePanel() {map = new BattleMaps();sPanel = new scorePanel();// 生成坦克A的合法位置tankA = generatePositionA(45, 35);tankB = generatePositionB(45, 35);// 生成坦克B的合法位置(且不与A重叠)// 初始化游戏定时器(每16ms≈60FPS)//使用游戏循环(Timer)来定期处理按键状态,更新坦克位置。gameTimer = new Timer(7, e -> {processInput();// 处理输入updateGame();// 更新游戏状态SwingUtilities.invokeLater(this::repaint);// 请求重绘});gameTimer.start();setFocusable(true);addKeyListener(this);}private TankA generatePositionA(int width, int height) {Random ran = new Random();Rectangle tempRect;int x, y;do {x = 120 + ran.nextInt(900);y = 60 + ran.nextInt(750);tempRect = new Rectangle(x, y, width, height);} while (map.isCollidingWithWall(tempRect)); // 确保不生成在墙上return new TankA(x, y); // 或 TankB}private TankB generatePositionB(int width, int height) {Random ran = new Random();Rectangle tempRect;int x, y;do {x = 120 + ran.nextInt(900);y = 60 + ran.nextInt(750);tempRect = new Rectangle(x, y, width, height);} while (map.isCollidingWithWall(tempRect)); // 确保不生成在墙上return new TankB(x, y); // 或 TankB}private void processInput() {// 游戏结束时忽略所有输入if (gameOver) return;// 处理坦克A// 每次循环先重置速度tankA.setSpeedX(0);tankA.setSpeedY(0);// 根据当前按下的键设置速度if (pressedKeys.contains(KeyEvent.VK_A)) {tankA.setDirection(0);tankA.setSpeedX(-4);}if (pressedKeys.contains(KeyEvent.VK_D)) {tankA.setDirection(2);tankA.setSpeedX(4);}if (pressedKeys.contains(KeyEvent.VK_W)) {tankA.setDirection(1);tankA.setSpeedY(-4);}if (pressedKeys.contains(KeyEvent.VK_S)) {tankA.setDirection(3);tankA.setSpeedY(4);}// 处理坦克BtankB.setSpeedX(0);tankB.setSpeedY(0);if (pressedKeys.contains(KeyEvent.VK_LEFT)) {tankB.setDirection(0);tankB.setSpeedX(-4);}if (pressedKeys.contains(KeyEvent.VK_RIGHT)) {tankB.setDirection(2);tankB.setSpeedX(4);}if (pressedKeys.contains(KeyEvent.VK_UP)) {tankB.setDirection(1);tankB.setSpeedY(-4);}if (pressedKeys.contains(KeyEvent.VK_DOWN)) {tankB.setDirection(3);tankB.setSpeedY(4);}}private void updateGame() {if (gameOver) {return;}handleTankMovement(tankA);handleTankMovement(tankB);//更新子弹位置for (Bullet bullet : new ArrayList<>(bullets)) {bullet.move();//检测子弹与墙壁的碰撞if (map.isCollidingWithWall(bullet.getBounds())) {bullet.setActive(false);}//检测子弹与坦克碰撞if (bullet.isActive()) {if (bullet.isFormTankA() && bullet.getBounds().intersects(tankB.getBounds())) {gameOver = true;winner = "-TankA-";bullet.setActive(false);showGameOver();} else if (!bullet.isFormTankA() && bullet.getBounds().intersects(tankA.getBounds())) {gameOver = true;winner = "-TankB-";bullet.setActive(false);showGameOver();}}}bullets.removeIf(bullet -> !bullet.isActive());//移除不活跃的子弹}private void handleTankMovement(MoveObjects tank) {//保存移动前的位置int oldX = tank.getX();int oldY = tank.getY();//移动坦克tank.move();// 获取移动后的碰撞区域Rectangle newBounds = tank.getBounds();//检测是否会与墙体/敌方坦克发生碰撞if (map.isCollidingWithWall(newBounds)) {//碰撞后回退位置并重置速度tank.setX(oldX);tank.setY(oldY);tank.setSpeedX(0);tank.setSpeedY(0);} else if (tankA.getBounds().intersects(tankB.getBounds())) {tank.setX(oldX);tank.setY(oldY);tank.setSpeedX(0);tank.setSpeedY(0);}}private void showGameOver() {pressedKeys.clear();// 在显示对话框前清除按键状态SwingUtilities.invokeLater(() -> {int option = JOptionPane.showConfirmDialog(this, "  " + winner + "   Wins!!!\n WANT PLAY AGAIN?", "--Game Over--", JOptionPane.YES_NO_OPTION);if (option == JOptionPane.YES_OPTION) {resetGame();} else {System.exit(0);}});}private void resetGame() {// 重置游戏前再次确保清除按键状态pressedKeys.clear();tankA = generatePositionA(45, 35);tankB = generatePositionB(45, 35);bullets.clear();winner = "";gameOver = false;requestFocusInWindow();}@Overrideprotected void paintComponent(Graphics g) {//自动启用Swing双缓冲,避免闪烁super.paintComponent(g);// 清空背景,清除前一帧画面 确保每次绘制都是全新的画面,避免画面残留//底层原理:默认会使用组件的背景色填充整个区域Graphics2D g2d = (Graphics2D) g.create();//创建图形上下文副本map.paintMap(g2d);tankA.drawTankA(g2d);tankB.drawTankB(g2d);//绘制所有子弹for (Bullet bullet : bullets) {bullet.draw(g2d);}sPanel.drawTankPicture(g2d);g2d.dispose();//保证图形状态隔离}private Bullet createBullet(MoveObjects tank, boolean fromTankA) {int tankHeadX;int tankHeadY;if (tank.getDirection() == 0 || tank.getDirection() == 2) {tankHeadX = tank.getX() + (tank.getWidth() / 2);tankHeadY = tank.getY() + (tank.getHeight() / 2) - 2;} else {tankHeadX = tank.getX() + (tank.getHeight() / 2) - 2;tankHeadY = tank.getY() + (tank.getWidth() / 2);}return new Bullet(tankHeadX, tankHeadY, tank.getDirection(), fromTankA);}@Overridepublic void keyPressed(KeyEvent e) {pressedKeys.add(e.getKeyCode());//添加子弹发射功能if (e.getKeyCode() == KeyEvent.VK_Q) {bullets.add(createBullet(tankA, true));} else if (e.getKeyCode() == KeyEvent.VK_SLASH) {bullets.add(createBullet(tankB, false));}}@Overridepublic void keyReleased(KeyEvent e) {if (gameOver) {pressedKeys.clear();// 在显示对话框前清除按键状态} else {pressedKeys.remove(e.getKeyCode());}// 处理平滑停止(可选)processInput();}@Overridepublic void keyTyped(KeyEvent e) {}
      }
      /*注意:(1)Swing的绘图应该在事件分派线程(EDT)中进行,使用多线程可能导致不可预测的行为。因此,应该将所有绘图逻辑放在一个主循环中,使用Swing的Timer来定期触发重绘,而不是在独立的线程中使用while循环和Thread.sleep(2)KeyListener的keyPressed事件机制设计为单次触发模式,无法跟踪组合按键状态
      当同时按下多个键时,操作系统会快速交替触发多个keyPressed事件,但无法保持持续状态(3)Swing使用被动绘制机制,应重写paintComponent()方法getGraphics()获取的是临
      时图形上下文,无法持久化
      */
    • 6.2 思路与架构

      • 6.2.1 类的设计思路

        • 由于GamePanel类承载着我们整个游戏流程控制的核心方法, 核心可移动的物体(对象)部署以及游戏状态的实时检测与控制, 这决定了本类需要继承JPanel父类并且实现KeyListener监听器, 同时需要传入整个游戏项目中的核心对象作为私有参数; 简洁起见我们尽可能将游戏循环封装在类的无参构造器中便于主类在实例化对象时直接启动.
      • 6.2.2 核心属性梳理

        • 为了便于本类的众方法对对象的操作, 我们尽可能将所需的对象设置为全局变量, 将需要和GamePanel同时创建出来的对象于无参构造器中进行初始化
        • private TankA tankA;private TankB tankB;private final Set<Integer> pressedKeys = new HashSet<>();private final Timer gameTimer;private final Random ran = new Random();private final BattleMaps map;private final scorePanel sPanel;private final ArrayList<Bullet> bullets = new ArrayList<>();private boolean gameOver = false;private String winner = "";public GamePanel() {map = new BattleMaps();sPanel = new scorePanel();// 生成坦克A的合法位置tankA = generatePositionA(45, 35);tankB = generatePositionB(45, 35);// 生成坦克B的合法位置(且不与A重叠)// 初始化游戏定时器(每16ms≈60FPS)//使用游戏循环(Timer)来定期处理按键状态,更新坦克位置。gameTimer = new Timer(7, e -> {processInput();// 处理输入updateGame();// 更新游戏状态SwingUtilities.invokeLater(this::repaint);// 请求重绘});gameTimer.start();setFocusable(true);addKeyListener(this);}

    • 6.3 核心方法梳理与分析

      • a. 坦克A/B坐标的初始化

        • 创建一个随机器, 在地图的左上角到右下角的有效空间内随机生成坦克的x, y坐标, 利用BattleMap类中的: isCollidingWithWall()方法循环判断是否生成在了合法位置, 最终返回一个新的坦克对象(x,y)
        • private TankA generatePositionA(int width, int height) {Random ran = new Random();Rectangle tempRect;int x, y;do {x = 120 + ran.nextInt(900);y = 60 + ran.nextInt(750);tempRect = new Rectangle(x, y, width, height);} while (map.isCollidingWithWall(tempRect)); // 确保不生成在墙上return new TankA(x, y); // 或 TankB}
      • b. 处理键盘对坦克的操作

        • 首先判断游戏的状态是否结束, 每次循环处理输入时先将坦克的速度置零, 根据当前按下的键设置坦克的方向与速度(A/B同理)
        • private void processInput() {// 游戏结束时忽略所有输入if (gameOver) return;// 处理坦克A// 每次循环先重置速度tankA.setSpeedX(0);tankA.setSpeedY(0);// 根据当前按下的键设置速度if (pressedKeys.contains(KeyEvent.VK_A)) {tankA.setDirection(0);tankA.setSpeedX(-4);}if (pressedKeys.contains(KeyEvent.VK_D)) {tankA.setDirection(2);tankA.setSpeedX(4);}if (pressedKeys.contains(KeyEvent.VK_W)) {tankA.setDirection(1);tankA.setSpeedY(-4);}if (pressedKeys.contains(KeyEvent.VK_S)) {tankA.setDirection(3);tankA.setSpeedY(4);}
      • c. 重写键盘监听器方法(点按, 按下, 松开)

        • 我们在设置全局变量时将pressedKey设置为了HashSet类型, 这就代表着我们不允许两个键盘指令被加入到Set中, 保证了对坦克操作的特定性.
        • 1. 按下:   每当按下键盘时获取指令并传入Set中, 如果是子弹发射键则创建新的子弹对象并传入bullets的ArrayList中进行绘制.
        • 2. 松开:  每当松开当前按下的键时, 如果游戏结束直接清除Set中所有的键盘指令, 如果没有则调用remove()方法移除当前指令.
        • 3. 点按:  无需此操作.
        • @Overridepublic void keyPressed(KeyEvent e) {pressedKeys.add(e.getKeyCode());//添加子弹发射功能if (e.getKeyCode() == KeyEvent.VK_Q) {bullets.add(createBullet(tankA, true));} else if (e.getKeyCode() == KeyEvent.VK_SLASH) {bullets.add(createBullet(tankB, false));}}@Overridepublic void keyReleased(KeyEvent e) {if (gameOver) {pressedKeys.clear();// 在显示对话框前清除按键状态} else {pressedKeys.remove(e.getKeyCode());}// 处理平滑停止(可选)processInput();}@Overridepublic void keyTyped(KeyEvent e) {}

      • d. 控制坦克移动

        • 以我们的MoveObject类作为作为参数, 便于坦克状态的集中控制, 移动前我们先获取坦克的当前位置便于给碰撞方法传递参数, 之后调用移动坦克方法并获取移动后坦克矩形的外边界, 将边界传入碰撞检测方法检测坦克是否与墙或者另一坦克发生碰撞, 如果是则将坐标设为刚才获取的位置并将速度置零.
        • private void handleTankMovement(MoveObjects tank) {//保存移动前的位置int oldX = tank.getX();int oldY = tank.getY();//移动坦克tank.move();// 获取移动后的碰撞区域Rectangle newBounds = tank.getBounds();//检测是否会与墙体/敌方坦克发生碰撞if (map.isCollidingWithWall(newBounds)) {//碰撞后回退位置并重置速度tank.setX(oldX);tank.setY(oldY);tank.setSpeedX(0);tank.setSpeedY(0);} else if (tankA.getBounds().intersects(tankB.getBounds())) {tank.setX(oldX);tank.setY(oldY);tank.setSpeedX(0);tank.setSpeedY(0);}}
      • e. 创建子弹

        •  首先确定返回值类型为Bullet类 , 设置参数为MoveObject对象与子弹来源;  通过简单计算确定子弹创建的位置与坦克当前的位置的偏差, 最终返回新的子弹对象
        • private Bullet createBullet(MoveObjects tank, boolean fromTankA) {int tankHeadX;int tankHeadY;if (tank.getDirection() == 0 || tank.getDirection() == 2) {tankHeadX = tank.getX() + (tank.getWidth() / 2);tankHeadY = tank.getY() + (tank.getHeight() / 2) - 2;} else {tankHeadX = tank.getX() + (tank.getHeight() / 2) - 2;tankHeadY = tank.getY() + (tank.getWidth() / 2);}return new Bullet(tankHeadX, tankHeadY, tank.getDirection(), fromTankA);}
      • f. 组件可视化方法(游戏画面的核心)

        • 直接先看代码我慢慢解释
        •  @Overrideprotected void paintComponent(Graphics g) {//自动启用Swing双缓冲,避免闪烁super.paintComponent(g);// 清空背景,清除前一帧画面 确保每次绘制都是全新的画面,避免画面残留//底层原理:默认会使用组件的背景色填充整个区域Graphics2D g2d = (Graphics2D) g.create();//创建图形上下文副本map.paintMap(g2d);tankA.drawTankA(g2d);tankB.drawTankB(g2d);//绘制所有子弹for (Bullet bullet : bullets) {bullet.draw(g2d);}sPanel.drawTankPicture(g2d);g2d.dispose();//保证图形状态隔离}
        • paintComponent 是 Swing 组件绘制的核心方法,负责组件的可视化呈现。以下是简明扼要的解释:
        • 1. 基本概念
          • 作用: 自定义组件的绘制逻辑
          • 继承关系: JComponent.paint() → paintComponent()
          • 典型实现:
          • @Override
            protected void paintComponent(Graphics g) {super.paintComponent(g); // 1. 清空背景// 2. 自定义绘制代码
            }
        • 2. 触发调用:
        • 3. 关键应用: 
          • super.paintComponent(g); // 清屏Graphics2D g2d = (Graphics2D)g;map.paintMap(g2d);      // 1. 绘制地图(底层)tankA.draw(g2d);        // 2. 绘制坦克(中层) bullets.forEach(b->b.draw(g2d)); // 3. 绘制子弹(上层)
      • g. 刷新游戏

        • 刷新的东西无非就是子弹和坦克:
        • 首先判断游戏是否结束, 如没有则首先控制当前坦克的动作; 其次遍历List更新子弹位置, 移动子弹, 获取子弹的矩形边界 判断子弹是否与墙壁发生碰撞 如碰则直接设置为毁灭; 之后分别判断子弹边界与坦克A,B是否重合, 根据情况设置游戏状态,赢家, 展示游戏结束画面
        • private void updateGame() {if (gameOver) {return;}handleTankMovement(tankA);handleTankMovement(tankB);//更新子弹位置for (Bullet bullet : new ArrayList<>(bullets)) {bullet.move();//检测子弹与墙壁的碰撞if (map.isCollidingWithWall(bullet.getBounds())) {bullet.setActive(false);}//检测子弹与坦克碰撞if (bullet.isActive()) {if (bullet.isFormTankA() && bullet.getBounds().intersects(tankB.getBounds())) {gameOver = true;winner = "-TankA-";bullet.setActive(false);showGameOver();} else if (!bullet.isFormTankA() && bullet.getBounds().intersects(tankA.getBounds())) {gameOver = true;winner = "-TankB-";bullet.setActive(false);showGameOver();}}}bullets.removeIf(bullet -> !bullet.isActive());//移除不活跃的子弹}
      • h. 重置游戏

        • 关键难点:  pressedKeys.clear();
        • 在游戏结束时(A坦克被击中时), 如果玩家A仍然按着 "W" 键, 随着游戏进程结束这个元素不会被remove()掉, 因此在游戏重新开始时为了防止某个坦克不受控制的向某个方向移动, 应清空Set内所有的键盘指令
        •  private void resetGame() {// 重置游戏前再次确保清除按键状态pressedKeys.clear();tankA = generatePositionA(45, 35);tankB = generatePositionB(45, 35);bullets.clear();winner = "";gameOver = false;requestFocusInWindow();}
      • i.  游戏结束选择盘

        • 应用: SwingUtilities.invokeLater 来启动EDT事件调度, 防止展示结束界面进入游戏主线程

        • private void showGameOver() {pressedKeys.clear();// 在显示对话框前清除按键状态SwingUtilities.invokeLater(() -> {int option = JOptionPane.showConfirmDialog(this, "  " + winner + "   Wins!!!\n WANT PLAY AGAIN?", "--Game Over--", JOptionPane.YES_NO_OPTION);if (option == JOptionPane.YES_OPTION) {resetGame();} else {System.exit(0);}});}
  • 7. 装饰界面

    • 逻辑类似于坦克图片的绘制: 独立于GamePanel便于后续增加有趣的功能
    •  private final ImageIcon[] imgPic = new ImageIcon[2];public void drawTankPicture(Graphics2D g2d) {imgPic[0] = new ImageIcon("D:\\桌面\\Xing\\Photos\\TankB.png");imgPic[1] = new ImageIcon("D:\\桌面\\Xing\\Photos\\TankA.png");g2d.drawImage(imgPic[0].getImage(), 230, 830, 100, 60, null);g2d.drawImage(imgPic[1].getImage(), 830, 830, 100, 60, null);}

  • 8. Summary

  • 我的坦克大战基于本地Java Swing框架, 通过gameTimer定时刷新界面实现了游戏的流畅运行,在本地实现双人对战非常刺激与流畅, 虽然并非契合主流游戏开发的流程与现有标准框架, 但是实现了一个教学级的坦克大战游戏开发全流程, 非常适合新手熟悉Java的Swing框架 和 面向对象中类的设计与关联的基本思维, 是一个有趣而简单的实战项目
    • 核心点再析:

    • JFrame:游戏的主窗口容器,负责处理操作系统级事件(关闭、最小化等)

    • GamePanel:继承自JPanel的自定义组件,作为游戏绘制表面(Surface)

    • 双缓冲机制:Swing默认启用双缓冲,通过RepaintManager管理后台缓冲区

    • 绘制触发repaint()调用会触发Swing的异步重绘请求,最终由事件派发线程(EDT)执行paintComponent

    • 事件分发线程(EDT):所有输入事件由Swing的EDT统一派发

  • DeepSeek对我的项目进行了理性的评估,内容如下

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/90445.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/90445.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Git下载与安装全攻略

引言 Git是当今最流行的分布式版本控制系统&#xff0c;由Linus Torvalds于2005年创建。它帮助开发者高效管理代码变更&#xff0c;支持多人协作开发&#xff0c;是现代软件开发不可或缺的工具。无论是个人项目还是团队协作&#xff0c;Git都能提供强大的版本控制功能。 本文…

【Elasticsearch】快照生命周期管理 SLM(理论篇)

《Elasticsearch 集群》系列&#xff0c;共包含以下文章&#xff1a; 1️⃣ 冷热集群架构2️⃣ 合适的锅炒合适的菜&#xff1a;性能与成本平衡原理公式解析3️⃣ ILM&#xff08;Index Lifecycle Management&#xff09;策略详解4️⃣ Elasticsearch 跨机房部署5️⃣ 快照与恢…

深入理解 UDP 协议:从原理到实战的技术解析

UDP&#xff08;User Datagram Protocol&#xff0c;用户数据报协议&#xff09;作为 TCP 的 "轻量型伙伴"&#xff0c;在实时通信、流媒体传输等场景中发挥着不可替代的作用。与 TCP 的可靠传输不同&#xff0c;UDP 以 "简单、快速、无连接" 为设计理念&a…

c语言-数据结构-沿顺相同树解决对称二叉树问题的两种思路

二叉树OJ前言对称二叉树前言 本篇继续讲解二叉树OJ题目之对称二叉树 对称二叉树 题目链接&#xff1a;https://leetcode.cn/problems/symmetric-tree/description/ 该题要求比较这棵树是否对称&#xff0c;对称&#xff0c;指的是结构对称并且值也要对称&#xff0c;即对应…

云原生可观测-日志观测(Loki)最佳实践

一、Loki 简介 云原生可观测三大支柱 支柱工具用途MetricsPrometheus性能趋势、系统负载LogsLoki原始事件记录、错误诊断TracesTempo / Jaeger分布式链路追踪 一、Loki 简介 1.1 Loki 是什么 Loki 是由 Grafana Labs 开发的 日志聚合系统&#xff0c;与 Prometheus 架构一…

Windows Server 2003 R2系统C盘扩容教程

一、PAGreen软件下载 下载地址&#xff1a; ExtPart.zip https://pan.baidu.com/s/1FxK61XNI0t-4JIEWK1QA8Q?pwd8888 提取码: 8888 二、将软件解压缩 (1)、执行步骤一下载的程序 双击下图所示可执行程序 (2)、选择好解压路径&#xff0c;点击「Unzip」进行解压缩 (3)、磁…

Kubernetes配置管理

目录什么是ConfigMap创建ConfigMap1&#xff1a;基于目录创建ConfigMap1.创建conf目录&#xff0c;放置文件2.基于目录下的所有文件创建ConfigMap3.查看当前创建的ConfigMap2&#xff1a;基于文件创建ConfigMap1.单个文件创建ConfigMap2.使用带有key的命令创建ConfigMap3.多个文…

golang怎么实现每秒100万个请求(QPS),相关系统架构设计详解

一.需求 使用Golang,以Gin框架为基础,设计一个能够处理每秒100万请求(QPS 1M)的系统架构 注意:100万QPS是一个很高的数字,单机通常难以处理,所以必须采用分布式架构,并且需要多层次的架构设计和优化 二.搭建步骤 1.系统架构设计 为了实现高并发,需要考虑以下几个方面…

HCIA再复习

第一章.网络基础1.1 网络类型分类网络按照二层链路类型分为以下四种&#xff1a;多点接入网络&#xff08;MA&#xff09;&#xff1a;1&#xff0c;广播型多点接入&#xff08;BMA&#xff09;&#xff1a;如以太网&#xff0c;支持广播&#xff0c;设备通过MAC地址通信&#…

Qt 数据库连接池实现与管理

在 Qt 应用程序中&#xff0c;频繁创建和销毁数据库连接会带来显著的性能开销。数据库连接池通过复用现有连接&#xff0c;避免重复创建和销毁连接的开销&#xff0c;从而提高应用程序的响应速度和吞吐量。本文将详细介绍 Qt 中数据库连接池的实现与管理方法。 一、数据库连接池…

数据采集分析:从信息洪流中掘金的科学与艺术

——如何将原始数据转化为商业决策的黄金&#xff1f;&#x1f310; 引言&#xff1a;我们正淹没在数据的海洋&#xff0c;却渴求着知识的甘泉每天全球产生 2.5万亿字节 数据&#xff08;相当于每秒下载4.5万部高清电影&#xff09;&#xff0c;但未经分析的数据如同未提炼的原…

Oracle国产化替代:一线DBA的技术决策突围战

从“如履薄冰”到“游刃有余”,中国数据库的自主之路正重塑技术人的思维地图。 “凌晨三点的最后一次数据校验通过,割接系统绿灯全亮——**河北移动核心账务系统的Oracle数据库已被GoldenDB完全替代**。”2025年6月底,这场持续两年的攻坚战画上句号。当全省业务流量平稳切…

OS19.【Linux】进程状态(1)

目录 1.情景引入 2.操作系统学科对进程状态的分类 运行状态 基于时间片的轮转调度算法 阻塞状态 等待IO设备的例子 等待其他进程中需要获取的数据 进程唤醒 挂起状态(全称为阻塞挂起状态) 简单谈谈虚拟内存管理 就绪状态 笔面试题 3.Linux对进程状态的分类 R和S状…

Hadoop小文件合并技术深度解析:HAR文件归档、存储代价与索引结构

HDFS小文件问题的背景与挑战在Hadoop分布式文件系统&#xff08;HDFS&#xff09;的设计哲学中&#xff0c;"大文件、流式访问"是核心原则。然而现实场景中&#xff0c;海量小文件&#xff08;通常指远小于HDFS默认块大小128MB的文件&#xff09;的涌入却成为系统性能…

Verilog 提取信号的上升沿或者下降沿

上升沿提取代码&#xff1a;reg [1:0] F1;always (posedge clk)beginif(rst_n 1b0) F1[1:0]<2b00;else F1[1:0]<{F1[0],start_i};endwire start_l2h (F1[1:0]2b01)?1b1:1b0;下降沿提取代码&#xff1a;reg [1:0] F1;always (posedge clk)b…

.Net core 部署到IIS出现500.19Internal Server Error 解决方法

.Net core 部署到IIS&#xff0c;网页出现500.19Internal Server Error 解决方法解决方法 在URL:https://dotnet.microsoft.com/zh-tw/download/dotnet/8.0下载并安装dotnet-hosting-8.0.18-win.exe 重启IIS服务器

Linux 基本命令整理

&#x1f427; Linux 基本命令整理 为了方便初学者快速掌握 Linux 常用命令&#xff0c;以下是经过分类整理的核心命令及用法说明。 &#x1f4c2; 目录操作与文件管理 pwd 核心功能&#xff1a;打印当前工作目录的绝对路径&#xff0c;明确用户所在位置。 实操示例&#x…

牛客周赛 Round 101(题解的token计算, 76修地铁 ,76选数,76构造,qcjj寄快递,幂中幂plus)

A题解的token计算要记住c中的对数函数&#xff1a;log(n) 是自然对数&#xff08;以e为底&#xff09;ln(nlog10(n) 是以10为底的对log1p(n) 是ln(1n)&#xff0c;提供更高的数值精log2(n) 是以2为底的对logl(n) 和 log10l(n) 是long double版#define _CRT_SECURE_NO_WARNINGS …

商场导航软件:3D+AI 基于Deepseek 模型的意图识别技术解析

本文面向室内导航工程师、商场导航系统优化师及LBS 应用开发的技术员&#xff0c;解析商场室内导航系统 3DAI 三大核心技术模块&#xff0c;并提供可直接复用的工程解决方案。如需获取商场导航系统技术方案可前往文章最下方获取&#xff0c;如有项目合作及技术交流欢迎私信作者…

借助Aspose.HTML控件,使用 Python 编程将网页转换为 PDF

使用 Python 将网页转换为 PDF 有时您需要离线访问网页&#xff0c;使其更易于访问。因此&#xff0c;将HTML页面转换为PDF即可满足您的需求。令人惊讶的是&#xff0c;您可以在几秒钟内在 Python 项目中启用 HTML 到 PDF 的转换。本指南将为 Python 开发人员介绍一个功能强大…