Java GUI——网页浏览器开发

Java GUI——网页浏览器开发

前言:为了做java课设,学了一手Java GUI。感觉蛮有意思的,写写文章,做个视频记录一下。欢迎大家友善指出我的不足

网页浏览器开发录制视频,从头敲到尾

任务需求

界面需求

  • 菜单栏

    • 文件 【热键F】
      • 另存为 【热键A,快捷键CTRL+S】
      • 退出 【热键I,快捷键CTRL+E】
    • 编辑 【热键E】
      • 前进 【快捷键CTRL+Z】
      • 后退 【快捷键CTRL+D】
    • 视图 【热键V】
      • 全屏 【快捷键CTRL+U】
      • 查看源码 【热键C,快捷键CTRL+C】
      • 刷新 【快捷键CTRL+R】
  • 工具栏

    • 另存为
    • 后退
    • 前进
    • 查看源代码
    • 退出
  • 工具栏

    • label(地址)
    • 文本框
    • 转向按钮

功能需求

另存为:保存正在访问的界面

前进:访问现有的上一个界面

后退:访问现有页面的下一个页面

查看源文件:查看访问页面的HTML源文件、并提供保存、退出功能

参考样式
在这里插入图片描述

在这里插入图片描述

功能解析

界面搭建

  • 界面解析【主界面】
    在这里插入图片描述

tip:

  • WebView的使用注意事项

    • 需要在FX线程中使用

      /*** <p>{@code WebView} objects must be created and accessed solely from the* FX thread.*/
      
  • Platform.runLater说明

    • 能够启动JavaFX线程

      /*** Run the specified Runnable on the JavaFX Application Thread at some* unspecified*/
      
    • 启动前,不能启动FX runtime. 对于Swing,只要初始化第一个JFXPanel,就算启动FX runtime

      /*** <p>* This method must not be called before the FX runtime has been* initialized.* For Swing applications that use JFXPanel to display FX content, the FX* runtime is initialized when the first JFXPanel instance is constructed.* For SWT application that use FXCanvas to display FX content, the FX* <p>*/
      

WebView无法直接添加到JPanel中,需要借助JFXPanel,同时WebView的创建需要在独立的FX线程,因此WebView添加JPanel的代码稍微有些麻烦

Platform.runLater(() -> {// 不能再Platform.runLater之前运行, 否则就启动了FX runtime, Platform运行会报错webView = new WebView();// webPanel 是JFXPanelwebPanel.setScene(new Scene(webView));// 加载网页webView.getEngine().load(url);
});
// TODO 将webPanel添加到JPanel中
  • 界面解析【查看源代码界面】
    • 在这里插入图片描述

模块划分

在这里插入图片描述

URL存储数组,用单独的类(URLList)来维护

URLList,建立起主界面html代码界面, 监听器界面 沟通的桥梁。同时URLList维护的数组,能够存储URL访问历史记录,实现网页前进, 后退功能

代码编写

maven坐标

    <dependencies><!--Servlet--><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency><!-- Lombok 依赖 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.20</version><scope>provided</scope></dependency><!--MyBatis--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.5</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus</artifactId><version>3.5.2</version></dependency><!--MySQL--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.27</version></dependency><!--fastjson--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.62</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.0.6.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.1.2.RELEASE</version></dependency></dependencies><build><plugins><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.0</version></plugin></plugins></build>

utils

Constant

全局常量

package com.xhf.keshe.utils;import java.awt.*;public interface Constant {int MAIN_WIDTH = 1920;int MAIN_HEIGHT = 1080;String[] menuList1 = {"另存为(A)", "退出(I)"};String[] menuList2 = {"前进", "后退"};String[] menuList3 = {"全屏", "查看源码(C)", "刷新"};Font baseFont = new Font("仿宋", Font.BOLD, 20);Font smallFont = new Font("仿宋", Font.BOLD, 15);String[] toolBarButtonNameList = {"另存为", "后退", "前进", "查看源码", "退出"};int SOURCE_WIDTH = 800;int SOURCE_HEIGHT = 600;
}

WebsiteHTMLGetter

通过URL解析出html代码

package com.xhf.keshe.utils;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;public class WebsiteHTMLGetter {public static String getHTMLCode(String url) throws IOException {URL website = new URL(url);HttpURLConnection connection = (HttpURLConnection) website.openConnection();connection.setRequestMethod("GET");// 获取网页内容BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));StringBuilder htmlCode = new StringBuilder();String line;while ((line = reader.readLine()) != null) {htmlCode.append(line).append("\n");}reader.close();return htmlCode.toString();}
}

URLList

维护URL记录

package com.xhf.keshe.utils;import java.util.ArrayList;
import java.util.List;public class URLList {private static List<String> queue = new ArrayList<String>();private static int pointer = -1;// 获取当前元素public static String getCur() {if (queue.size() == 0) return null;return queue.get(pointer);}// 指针右移public static boolean right() {if (pointer == queue.size() - 1) {return false;}pointer++;return true;}// 指针左移public static boolean left() {if (pointer == 0) {return false;}pointer--;return true;}// 添加元素public static boolean add(String url) {if (pointer + 1 > queue.size() - 1) {queue.add(url);++pointer;}else {queue.set(++pointer, url);}return true;}
}

界面

主界面

package com.xhf.keshe;import com.xhf.keshe.listener.*;
import com.xhf.keshe.utils.Constant;
import com.xhf.keshe.utils.URLList;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.scene.web.WebView;import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;/*** 主界面*/
public class Main extends JFrame {public static JPanel toolBarPanel;/*** http地址接收栏*/private static JTextField http = new JTextField();/*** 转向*/private JButton redirect = new JButton("转向");/*** 显示web界面的panel*/public static JFXPanel webPanel;/*** 显示website*/public static WebView webView;/*** 刷新界面*/public static void refreshWebSite(String url) {Platform.runLater(() -> {webView = new WebView();webPanel.setScene(new Scene(webView));// 加载网页webView.getEngine().load(url);// 重新加载httphttp.setText(url);});}public Main() {this.setSize(Constant.MAIN_WIDTH, Constant.MAIN_HEIGHT);// 居中this.setLocationRelativeTo(null);this.setVisible(true);this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);// 初始化界面initUI();refreshWebSite(http.getText());}/*** 初始化界面*/private void initUI() {// 初始化所有的panel界面initPanel();// 初始化菜单栏initMenu();// 初始化工具栏1initToolBar1();// 初始化工具栏2initToolBar2();}/*** 初始化panel界面*/private void initPanel() {toolBarPanel = new JPanel();toolBarPanel.setLayout(new GridLayout(2, 1));webPanel = new JFXPanel();webPanel.setLayout(new BorderLayout());webPanel.add(toolBarPanel, BorderLayout.NORTH);add(webPanel);}/*** 初始化二号工具栏*/private void initToolBar2() {JToolBar jToolBar2 = new JToolBar();JLabel jLabel = new JLabel("地址");jLabel.setFont(Constant.smallFont);jToolBar2.add(jLabel);http.setFont(Constant.smallFont);http.setText("https://www.baidu.com/");URLList.add("https://www.baidu.com/");jToolBar2.add(http);redirect.setFont(Constant.smallFont);// 执行网页跳转逻辑redirect.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {URLList.add(http.getText());refreshWebSite(http.getText());}});jToolBar2.add(redirect);toolBarPanel.add(jToolBar2);}/*** 初始化一号工具栏*/private void initToolBar1() {JToolBar jToolBar1 = new JToolBar();for (int i = 0; i < Constant.toolBarButtonNameList.length; i++) {JButton jButton = new JButton(Constant.toolBarButtonNameList[i]);jButton.setFont(Constant.smallFont);jToolBar1.add(jButton);try {if (Constant.toolBarButtonNameList[i].equals("另存为")) {jButton.addActionListener(new SaveCodeListener());} else if (Constant.toolBarButtonNameList[i].equals("后退")) {jButton.addActionListener(new URLmoveListener(URLmoveListener.BACKEND));} else if (Constant.toolBarButtonNameList[i].equals("前进")) {jButton.addActionListener(new URLmoveListener(URLmoveListener.FORWARD));} else if (Constant.toolBarButtonNameList[i].equals("查看源码")) {jButton.addActionListener(new GetSourceCodeListener());} else {jButton.addActionListener(new QuitListener(this));}}catch (Exception e) {e.printStackTrace();}}toolBarPanel.add(jToolBar1);}/*** 初始化菜单栏*/private void initMenu() {JMenuBar jMenuBar = new JMenuBar();// 初始化 '文件' 菜单JMenu jMenu1 = new JMenu("文件(ALT+F)");jMenu1.setMnemonic(KeyEvent.VK_F);jMenu1.setFont(Constant.baseFont);for (int i = 0; i < Constant.menuList1.length; i++) {JMenuItem item = new JMenuItem(Constant.menuList1[i]);item.setFont(Constant.baseFont);// 添加热键, 快捷键if (Constant.menuList1[i].equals("另存为(A)")) {// Aitem.setMnemonic(KeyEvent.VK_A);// ctrl + sitem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.CTRL_MASK));item.addActionListener(new SaveCodeListener());}else if (Constant.menuList1[i].equals("退出(I)")) {// Iitem.setMnemonic(KeyEvent.VK_I);// ctrl + eitem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, ActionEvent.CTRL_MASK));item.addActionListener(new QuitListener(this));}jMenu1.add(item);}// 初始化 '编辑' 菜单JMenu jMenu2 = new JMenu("编辑(ALT+E)");jMenu2.setMnemonic(KeyEvent.VK_E);jMenu2.setFont(Constant.baseFont);for (int i = 0; i < Constant.menuList2.length; i++) {JMenuItem item = new JMenuItem(Constant.menuList2[i]);item.setFont(Constant.baseFont);// 添加快捷键if (Constant.menuList2[i].equals("前进")) {// ctrl + zitem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z, ActionEvent.CTRL_MASK));item.addActionListener(new URLmoveListener(URLmoveListener.FORWARD));}else if (Constant.menuList2[i].equals("后退")){// ctrl + ditem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, ActionEvent.CTRL_MASK));item.addActionListener(new URLmoveListener(URLmoveListener.BACKEND));}jMenu2.add(item);}// 初始化 '视图' 菜单JMenu jMenu3 = new JMenu("视图(ALT+V)");jMenu3.setMnemonic(KeyEvent.VK_V);jMenu3.setFont(Constant.baseFont);for (int i = 0; i < Constant.menuList3.length; i++) {JMenuItem item = new JMenuItem(Constant.menuList3[i]);item.setFont(Constant.baseFont);// 添加热键, 快捷键if (Constant.menuList3[i].equals("全屏")) {// ctrl + uitem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_U, ActionEvent.CTRL_MASK));item.addActionListener(new FullScreenListener(this));}else if (Constant.menuList3[i].equals("查看源码(C)")) {// citem.setMnemonic(KeyEvent.VK_C);// ctrl + citem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, ActionEvent.CTRL_MASK));item.addActionListener(new GetSourceCodeListener());}else if (Constant.menuList3[i].equals("刷新")) {// ctrl + ritem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, ActionEvent.CTRL_MASK));item.addActionListener(new RefreshWebSite());}jMenu3.add(item);}jMenuBar.add(jMenu1);jMenuBar.add(jMenu2);jMenuBar.add(jMenu3);this.setJMenuBar(jMenuBar);}public static void main(String[] args) {Main main = new Main();}
}

查看源代码界面

package com.xhf.keshe.source;import com.xhf.keshe.listener.QuitListener;
import com.xhf.keshe.listener.SaveCodeListener;
import com.xhf.keshe.utils.Constant;
import com.xhf.keshe.utils.URLList;
import com.xhf.keshe.utils.WebsiteHTMLGetter;import javax.swing.*;
import java.awt.*;
import java.io.*;public class SourcePage extends JFrame{/*** 工作界面*/private JPanel workspace = new JPanel();public SourcePage() {this.setSize(Constant.SOURCE_WIDTH, Constant.SOURCE_HEIGHT);// 居中this.setLocationRelativeTo(null);this.setVisible(true);// 初始化界面initUI();}/*** 初始化UI*/private void initUI() {workspace.setLayout(new BorderLayout());// 初始化标题JLabel title = new JLabel("源代码");title.setFont(Constant.baseFont);title.setHorizontalAlignment(SwingConstants.CENTER);// 初始化文本域initTextArea();// 初始化按钮initButton();add(workspace);}/*** 初始化按钮*/private void initButton() {// 添加按钮显示区域JPanel buttonPanel = new JPanel();buttonPanel.setLayout(new FlowLayout());// 创建按钮JButton save = new JButton("保存");save.setFont(Constant.baseFont);save.addActionListener(new SaveCodeListener());JButton quit = new JButton("退出");quit.addActionListener(new QuitListener(this));quit.setFont(Constant.baseFont);buttonPanel.add(save);buttonPanel.add(quit);// 添加到panel中workspace.add(buttonPanel, BorderLayout.SOUTH);}/*** 初始化文本域*/private void initTextArea() {JTextArea sourceCode = new JTextArea();// 设置自动换行sourceCode.setLineWrap(true);// 设置自动换行时,以单词为单位换行sourceCode.setWrapStyleWord(true);try {String URL = URLList.getCur();String htmlCode = WebsiteHTMLGetter.getHTMLCode(URL);// 获取源代码sourceCode.setText(htmlCode);} catch (IOException e) {e.printStackTrace();JOptionPane.showMessageDialog(null, "Could not read website source code, please try again or check the http is correct");}JScrollPane jScrollPane = new JScrollPane(sourceCode);// 添加源代码显示区域workspace.add(jScrollPane, BorderLayout.CENTER);}
}

listener

FullScreenListener

监听器:实现窗体全屏功能

package com.xhf.keshe.listener;import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;public class FullScreenListener implements ActionListener {private final JFrame fullScreenFrame;private boolean isFullScreen = false;public FullScreenListener(JFrame fullScreenFrame) {this.fullScreenFrame = fullScreenFrame;}/*** Invoked when an action occurs.** @param e*/@Overridepublic void actionPerformed(ActionEvent e) {isFullScreen = !isFullScreen;if (isFullScreen) {fullScreenFrame.setExtendedState(JFrame.MAXIMIZED_BOTH);} else {fullScreenFrame.setExtendedState(JFrame.NORMAL);}}
}

GetSourceCodeListener

监听器:获取查看源代码窗体

package com.xhf.keshe.listener;import com.xhf.keshe.source.SourcePage;import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;public class GetSourceCodeListener implements ActionListener {/*** Invoked when an action occurs.** @param e*/@Overridepublic void actionPerformed(ActionEvent e) {// 创建JFrameSourcePage sourcePage = new SourcePage();}
}

QuitListener

package com.xhf.keshe.listener;import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;/*** 执行退出逻辑*/
public class QuitListener implements ActionListener {private JFrame jFrame;public QuitListener(JFrame frame) {this.jFrame = frame;}/*** Invoked when an action occurs.** @param e*/@Overridepublic void actionPerformed(ActionEvent e) {// 点击退出按钮时,关闭当前JFramejFrame.dispose();}
}

RefreshWebSite

监听器:刷新网页

package com.xhf.keshe.listener;import com.xhf.keshe.Main;
import com.xhf.keshe.utils.URLList;import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;public class RefreshWebSite implements ActionListener {@Overridepublic void actionPerformed(ActionEvent e) {Main.refreshWebSite(URLList.getCur());}
}

SaveCodeListener

监听器:保存源代码

package com.xhf.keshe.listener;import com.xhf.keshe.utils.URLList;
import com.xhf.keshe.utils.WebsiteHTMLGetter;import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;public class SaveCodeListener implements ActionListener {@Overridepublic void actionPerformed(ActionEvent e) {String URL = URLList.getCur();String htmlCode = null;try {htmlCode = WebsiteHTMLGetter.getHTMLCode(URL);} catch (IOException ioException) {ioException.printStackTrace();}String fileName = removeUrlPrefix(URL);System.out.println(fileName);try {String path = "E:\\B站视频创作\\Java课设\\网页浏览器开发\\代码\\src\\main\\resources\\sourceCode\\";// 写文件writeFile(path + fileName, htmlCode);} catch (IOException exp) {exp.printStackTrace();}}/*** 写文件* @param filePath* @param content* @throws IOException*/private static void writeFile(String filePath, String content) throws IOException {File file = new File(filePath);System.out.println(filePath);// 创建文件输出流try (PrintWriter writer = new PrintWriter(new FileWriter(file))) {// 写入文件内容writer.print(content);}}/*** 去除网址前缀* @param url* @return*/private static String removeUrlPrefix(String url) {// 去除"http://"前缀if (url.startsWith("http://")) {url = url.substring(7);}// 去除"https://"前缀else if (url.startsWith("https://")) {url = url.substring(8);}return url;}
}

URLmoveListener

监听器:控制网页界面前进、后退

package com.xhf.keshe.listener;import com.xhf.keshe.Main;
import com.xhf.keshe.utils.URLList;import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;public class URLmoveListener implements ActionListener {public static int FORWARD = 0;public static int BACKEND = 1;private int direction;public URLmoveListener(int direction) {this.direction = direction;}public void actionPerformed(ActionEvent e) {boolean flag = false;// 向前移动if (direction == FORWARD) {flag = URLList.right();if (!flag) {JOptionPane.showMessageDialog(null, "已经是最新的网页了");}}else if (direction == BACKEND) {flag = URLList.left();if (!flag) {JOptionPane.showMessageDialog(null, "已经是最旧的网页了");}}if (flag) {Main.refreshWebSite(URLList.getCur());}}
}

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

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

相关文章

接口测试之文件上传

在日常工作中&#xff0c;经常有上传文件功能的测试场景&#xff0c;因此&#xff0c;本文介绍两种主流编写上传文件接口测试脚本的方法。 首先&#xff0c;要知道文件上传的一般原理&#xff1a;客户端根据文件路径读取文件内容&#xff0c;将文件内容转换成二进制文件流的格式…

vue3中使用vue-simple-uploader

vue-simple-uploader本身是基于vue2的&#xff0c;直接npm i vue-simple-uploader -S下载下来版本的是0.7.6。在vue3中无法使用会报错。 解决&#xff1a;使用next安装接下来要发布的版本就会下载1.0.1版本&#xff0c;即可使用vue3 npm i vue-simple-uploadernext -S 注意&…

【linux】ssh 和adb connect区别

问&#xff1a;ssh 与ping的区别 答&#xff1a;SSH&#xff08;Secure Shell&#xff09;和Ping是两种完全不同的网络工具。 SSH是一种加密的网络协议&#xff0c;用于安全地远程管理或访问远程计算机。它提供了一种安全的通信方式&#xff0c;可以在不安全的网络上进行远程登…

SpringMVC的架构有什么优势?——视图与模型(二)

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 「推荐专栏」&#xff1a; ★java一站式服务 ★ ★ React从入门到精通★ ★前端炫酷代码分享 ★ ★ 从0到英雄&#xff0c;vue成神之路★ ★ uniapp-从构建到提升★ ★ 从0到英雄&#xff…

【数据结构】‘双向链表’冲冲冲

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

Prometheus技术文档-概念

Prometheus是一个开源的项目连接如下&#xff1a; Prometheus首页、文档和下载 - 服务监控系统 - OSCHINA - 中文开源技术交流社区 基本概念&#xff1a; Prometheus是一个开源的系统监控和告警系统&#xff0c;由Google的BorgMon监控系统发展而来。它主要用于监控和度量各种…

《UNIX 传奇:历史与回忆》读后感

《UNIX 传奇&#xff1a;历史与回忆》 是 bwk&#xff08;Brian W. Kernighan&#xff09;2019 年的新作&#xff0c;回忆了 UNIX 在大半个世纪的风雨历程&#xff0c;是一本引人入胜的书籍。通过对 UNIX 操作系统的历史和发展进行详细的叙述和回顾&#xff0c;让我对这个操作系…

微信-公众号服务器配置,小程序token认证

参考微信文档 这部分代码试用于微信公众号的服务器token校验,且适用于小程序的token校验 接入概述 | 微信开放文档 @RequestMapping(value = "/message/verify")public @ResponseBody String verify(HttpServletRequest request, HttpServletResponse respons…

【eNSP】静态路由

【eNSP】静态路由 原理网关路由表 实验根据图片连接模块配置路由器设备R1R2R3R4 配置PC的IP地址、掩码、网关PC1PC2PC3 配置静态路由查看路由表R1R2R3R4测试能否通信 原理 网关 网关与路由器地址相同&#xff0c;一般路由地址为.1或.254。 网关是当电脑发送的数据的目标IP不在…

CSS基础介绍笔记1

官方文档 CSS指的是层叠样式&#xff08;Cascading Style Sheets&#xff09;地址&#xff1a;CSS 教程离线文档&#xff1a;放大放小&#xff1a;ctrl鼠标滚动为什么需要css&#xff1a;简化修改HTML元素的样式&#xff1b;将html页面的内容与样式分离提高web开发的工作效率&…

ROS实现自定义信息以及使用

常见的消息包 消息包定义一般如下&#x1f447; &#xff08;1&#xff09;创建包和依赖项 &#xff08;2&#xff09;在新建的qq_msgs的包新建msgs的文件夹&#xff0c;在该文件夹里面新建Carry.msg类型的文件。 其实&#xff0c;Carry.msg就是你自己定义的消息类型&am…

C++入门(小白篇1—编译器安装-代码注释等)

前言&#xff1a; 最近想学一下一下C看了一些博客内容写的倒是很充实&#xff0c;但是&#xff0c;细节不到位&#xff0c;我是有Python基础的&#xff0c;所以学习来蛮快的&#xff0c;但是对于小白的话&#xff0c;有好多小细节大多数博客还是不够详细&#xff0c;由此我想写…

【机器学习】在 MLOps构建项目 ( MLOps2)

My MLOps tutorials: Tutorial 1: A Beginner-Friendly Introduction to MLOps教程 2&#xff1a;使用 MLOps 构建机器学习项目 一、说明 如果你希望将机器学习项目提升到一个新的水平&#xff0c;MLOps 是该过程的重要组成部分。在本文中&#xff0c;我们将以经典手写数字分类…

新型高速 JavaScript 运行时 Bun 0.7 发布

导读近日&#xff0c;新型 JavaScript 运行时 Bun 正式发布了 0.7 版本&#xff0c;带来了重大的升级。据悉&#xff0c;Bun 是一个配套齐全的 JavaScript 解决方案&#xff0c;集运行时、打包器、转译器和包管理器于一体&#xff0c;追求极致的运行速度。此次更新主要集中在与…

5G RedCap

5G RedCap指的是3GPP所提出的5G标准。与之前发布的5G标准相比&#xff0c;功能更加精简。5G RedCap于2019年6月首次被纳入3GPP R17研究项目。 把一些不必要的功能去掉就可以带来模组价格的降低。背后的基本想法是&#xff1a;为物联网应用定义一种新的、不那么复杂的NR设备。 …

Android Framework解析——WMS原理

作者&#xff1a;bobby_developer 1. WMS原理&#xff1a;WMS角色与实例化过程 window:它是一个抽象类&#xff0c;具体实现类为 PhoneWindow &#xff0c;它对 View 进行管理。Window是View的容器&#xff0c;View是Window的具体表现内容&#xff1b; windowManager:是一个接…

智慧图书馆:数字发展时代的文化粮仓

以下内容来自于易知微官网&#xff0c;进入官网可以了解更多详情。 注意&#xff1a;案例数据均为虚拟数据 书籍犹如灯塔指引着人们前进的方向&#xff0c;同时图书馆也是一个城市的灵魂。图书馆在为我们提供学习场所的同时&#xff0c;也在城市文明建设过程中起着不可替代的…

事件循环原理

事件循环 浏览器的进程模型 何为进程&#xff1f; 程序运行需要有它自己专属的内存空间&#xff0c;可以把这块内存空间简单的理解为进程 每个应用至少有一个进程&#xff0c;进程之间相互独立&#xff0c;即使要通信&#xff0c;也需要双方同意。 何为线程&#xff1f; 有…

企业邮箱安全对比:哪家公司的产品更可靠?

邮箱仍然是企业沟通的关键组成部分&#xff0c;但往往容易受到安全威胁。为了保护敏感信息&#xff0c;企业需要采取措施使企业邮箱更加安全。这可以通过投资先进的安全解决方案&#xff0c;创建限制或控制访问的策略&#xff0c;并定期对员工进行最佳实践培训来实现。 1、投资…

java动态生成excel并且需要合并单元格

java动态生成excel并且需要合并单元格 先上图看一下预期效果 集成poi <dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-base</artifactId><version>4.0.0</version> </dependency> <dependency><…