Java 模拟 QQ/ 微信截图功能

Java 模拟 QQ 微信截图功能 这只是简单的模拟,功能略显简陋,可供学习参考。QQ 截图,点击截图按钮,桌面画面会定住不动,画矩形框截取矩形框内容。若这时你在播放视频你会发现截图时视频画面停止了,但是背景音乐还在继续播放,其实这时只是个障眼法,一张全屏静止图片覆盖了整个电脑屏幕,然后用矩形裁剪静止图片。这里我们使用 JFrame,两个类,启动类 MainFrame,图片窗口类 ImageFrame。代码参考自网络并作些许修改。MainFrame启动类,按钮截全屏并传图片给图片容器 ImageFrame 显示。import javax.swing.*;import java.awt.*;import java.awt.image.BufferedImage;public class MainFrame extends JFrame { JButton capture; public MainFrame() {capture = new JButton(" 截图 "); Container container = getContentPane(); container.add(capture); setSize(130, 80); setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); setAlwaysOnTop(true);// 居中显示按钮 这里我就不给居中了 直接显示右下角// setLocationRelativeTo(null); setLocation(Toolkit.getDefaultToolkit().getScreenSize().width - 180, Toolkit.getDefaultToolkit().getScreenSize().height - 100); capture.addActionListener(e -> {// 使自己隐藏 setVisible(false);// 截图 Robot robot = null; try {robot = new Robot(); } catch (AWTException awtException) {awtException.printStackTrace(); } Toolkit toolkit = Toolkit.getDefaultToolkit(); int width = toolkit.getScreenSize().width; int height = toolkit.getScreenSize().height; System.out.println(width + "x" + height); BufferedImage capture = robot.createScreenCapture(new Rectangle(width, height)); new ImageFrame(capture);// 截图后显示 setVisible(true); }); setVisible(true); } public static void main(String[] args) {new MainFrame(); }}ImageFrame显示启动类传来的全屏图片,按住鼠标左键拖动绘制矩形,松开鼠标按 Enter 键保存图片至剪切板,双击鼠标左键保存图片至剪切板并且提示保存至本地的 Windows 图片文件夹。import javax.imageio.ImageIO;import javax.swing.*;import java.awt.*;import java.awt.datatransfer.DataFlavor;import java.awt.datatransfer.Transferable;import java.awt.datatransfer.UnsupportedFlavorException;import java.awt.event.*;import java.awt.image.BufferedImage;import java.io.File;import java.io.IOException;import java.time.LocalDateTime;import java.time.format.DateTimeFormatter;public class ImageFrame extends JFrame { BufferedImage image; // 记录鼠标坐标 宽高 int x, y; int w, h; boolean flag = false; boolean clickState = false; Graphics graphics; public ImageFrame(BufferedImage image) { this.image = image; Toolkit toolkit = Toolkit.getDefaultToolkit(); Dimension screenSize = toolkit.getScreenSize(); int height = screenSize.height; int width = screenSize.width; setSize(width, height);// 无修饰边框 setUndecorated(true); setVisible(true);// 一旦显示整个面板就绘制图片 repaint(); addKeyListener(new KeyListener() { @Override public void keyTyped(KeyEvent e) { } @Override public void keyPressed(KeyEvent e) { } @Override public void keyReleased(KeyEvent e) {System.out.println(e.getKeyCode());// esc 退出 if (e.getKeyCode() == 27) {dispose(); }// Enter 键直接复制图片到剪切板 if (e.getKeyCode() == 10) { Robot robot = null; try {robot = new Robot(); BufferedImage screenCapture = robot.createScreenCapture(new Rectangle(x, y, w, h)); setClipboardImage(screenCapture); } catch (AWTException awtException) {awtException.printStackTrace(); } dispose();} } });// 绘制矩形 注册鼠标监听// 鼠标按下获取开始坐标 鼠标释放时获得终点坐标 addMouseListener(new MouseListener() { @Override public void mouseClicked(MouseEvent e) {// 鼠标双击截图保存到文件 if (flag && e.getClickCount() == 2) {// 保存矩形内截图 try {Robot robot = new Robot(); BufferedImage screenCapture = robot.createScreenCapture(new Rectangle(x, y, w, h)); setClipboardImage(screenCapture); JFileChooser chooser = new JFileChooser(); String dateString = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"));// 截图保存 Windows 用户文件夹 String imgPath = System.getProperties().getProperty("user.home") + "\\Pictures\\" + dateString + ".jpg"; chooser.setSelectedFile(new File(imgPath)); int xz = chooser.showSaveDialog(null); String absolutePath = null; if (xz == 0) {// 获得用户选择文件 File selectedFile = chooser.getSelectedFile(); absolutePath = selectedFile.getAbsolutePath(); System.out.println(absolutePath); }// 写入图片 if (absolutePath != null) ImageIO.write(screenCapture, "jpeg", new File(absolutePath)); } catch (AWTException | IOException awtException) {awtException.printStackTrace(); } dispose();} } @Override public void mousePressed(MouseEvent e) { clickState = true; if (!flag) {x = e.getXOnScreen(); y = e.getY();} } @Override public void mouseReleased(MouseEvent e) { clickState = false; if (!flag) {w = e.getX() - x; h = e.getY() - y; repaint(); flag = true; } } @Override public void mouseEntered(MouseEvent e) { } @Override public void mouseExited(MouseEvent e) {}}); addMouseMotionListener(new MouseMotionListener() { @Override public void mouseDragged(MouseEvent e) {if (clickState) { // 鼠标移动重绘矩形 w = e.getX() - x; h = e.getY() - y; repaint();} } @Override public void mouseMoved(MouseEvent e) {}}); } public static void main(String[] args) {new ImageFrame(null); } @Override public void paint(Graphics g) {super.paint(g); g.drawImage(image, 0, 0, null); graphics = g; g.drawRect(x, y, w, h); } /** * 设置图片到剪切板 * * @param image */ public static void setClipboardImage(final Image image) {Transferable trans = new Transferable() { @Override public DataFlavor[] getTransferDataFlavors() {return new DataFlavor[]{DataFlavor.imageFlavor}; } @Override public boolean isDataFlavorSupported(DataFlavor flavor) {return DataFlavor.imageFlavor.equals(flavor); } @Override public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {if (isDataFlavorSupported(flavor)) return image; throw new UnsupportedFlavorException(flavor); } }; Toolkit.getDefaultToolkit().getSystemClipboard().setContents(trans, null); }}

LayUI 几款不错的表格组件

treetable-lay树形表格 treeTable 实现了 layui 数据表格的大部分功能,并且在用法上与几乎数据表格一致, 支持懒加载、复选框联动(半选)、拖拽列宽、固定表头等功能。相对于 layui 标准表格,它多了展开表格行功能。演示地址:https://whvse.gitee.io/treetable-lay/demo/开发文档:https://gitee.com/whvse/treetable-lay/wikis/pageslayui-soul-table一款可以表头筛选,随意拖动列位置的表格控件表头筛选、自定义条件(支持前端筛选、后台筛选介绍请看 三、后台筛选 ) 拖动列调整顺序、隐藏显示列 excel 导出(根据筛选条件和列顺序导出) 子表(表中表、无限层级、子表同样支持前 3 个功能) 5. 拖动行 6. 右击快捷菜单 7. 合计栏支持固定列 8. 双击自适应列宽 9. 右侧固定列 列宽拖动改到单元格左侧项目地址:https://gitee.com/saodiyang/layui-soul-table更多组件 更多组件可以访问官方第三方组件库:https://fly.layui.com/extend/

Spring 注解方式校验前端输入

例如将前端传值封装为实体类 public class User { private String name; private Integer age; private String phone;} 传统方式使用 if 判断,麻烦并且参数过多需要写很多代码,使用 Spring 注解 public class User {@NotNull(message = " 传入姓名不能为 NULL") @NotEmpty(message = " 传入姓名不能为空 ") private String name; @Min(value = 1, message = " 传入年龄需大于 1 ") private Integer age; @Length(min = 11, max = 11, message = " 手机号需 11 位 ") private String phone;} 于此同时,我们还需要在对象入口处,加上注解 @Valid 来开启对传入 Student 对象的验证 ˂img src=https://gitee.com/cwayteam/blog/raw/master/image/2021/01/28/64820_59b2cd740e51ac387cb5dd2c00c7786477cf4fc8.png@1320w_442h.png alt="59b2cd740e51ac387cb5dd2c00c7786477cf4fc8.png@1320w_442h"˃ 为项目配置全局统一异常拦截器来格式化所有数据校验的返回结果 ˂img src=https://gitee.com/cwayteam/blog/raw/master/image/2021/01/28/37224_2f3d31fd54e2c08944a28c9644ed37d2dee296d3.png@1320w_506h.png alt="2f3d31fd54e2c08944a28c9644ed37d2dee296d3.png@1320w_506h"˃ 如上面代码所示,我们全局统一拦截了参数校验异常 MethodArgumentNotValidException,并仅仅只拿到对应异常的详细 Message 信息吐给前端,此时返回给前端的数据就清楚得多

Spring 注解方式校验前端输入

RK987 无线双模机械键盘使用说明

在线说明书:http://www.rkgaming.com/zh-CN/article.php?id=51. 键盘背部开关状态为 ON2. 短按 Fn+P , 此时指示灯闪烁三次(但无论切到蓝牙还是有线都是闪烁三次,因此看步骤 3)。3. 选择 Fn+Q/W/ E 其中一组进行配对 (比如按下 Fn+Q),此时键盘指示灯闪烁三次。(若无反应,表示未正确进入蓝牙模式,请重复步骤 2,因此这里步骤 3 可以看出步骤 2 是否成功切换到蓝牙)。4. 长按 Fn+P 3 秒进,此时 P 键持续闪烁,表示已进入配对模式5. 打开设备蓝牙并搜索连接名为“RK Bluebooth Keyboard“6. 连接成功后,P 键自动停止闪烁。 以此类推,重复以上步骤,Fn+Q/W/E 可同时设置并存储三组不同的蓝牙设备,并且可在不同设备之间任意切换。

RK987 无线双模机械键盘使用说明

Java 获取本机 ipv4/ipv6 地址

Java 获取本机 ip 地址 /ipv6 地址,非用户请求返回的 IP try {String hostAddress = InetAddress.getLocalHost().getHostAddress(); System.out.println("hostAddress:" + hostAddress); String hostName = InetAddress.getLocalHost().getHostName(); System.out.println("hostName:" + hostName); if (hostName.length() > 0) {InetAddress[] addrs = InetAddress.getAllByName(hostName); if (addrs.length > 0) {for (int i = 0; i < addrs.length; i++) {InetAddress address = addrs[i]; System.out.println("**********************"); System.out.println(address.getHostAddress()); if (address instanceof Inet6Address) {System.out.println("true6"); } else if(address instanceof Inet4Address){System.out.println("true4"); } else {System.out.println("unknown"); } System.out.println("**********************"); } } } } catch (UnknownHostException e) {e.printStackTrace(); } 或者 Enumeration<NetworkInterface> interfs = NetworkInterface.getNetworkInterfaces(); while (interfs.hasMoreElements()) {NetworkInterface interf = interfs.nextElement(); Enumeration<InetAddress> addres = interf.getInetAddresses(); while (addres.hasMoreElements()) {InetAddress in = addres.nextElement(); if (in instanceof Inet4Address) {System.out.println("v4:" + in.getHostAddress()); } else if (in instanceof Inet6Address) {System.out.println("v6:" + in.getHostAddress()); } } } 参考:https://blog.csdn.net/scugxl/article/details/47816117

原生 js 封装 ajax 操作

若使用原生 js 代码可能没 jquery 简洁,例如在 ajax 调用上,下列是用原生 js 实现对 ajax 的封装。一、什么是 ajax?定义 :Ajax(Asynchronous Java and XML 的缩写) 是一种异步请求数据的 web 开发技术,在不需要重新刷新页面的情况下,Ajax 通过异步请求加载后台数据,并在网页上呈现出来。作用:提高用户体验,减少网络数据的传输量二、ajax 常见运用场景表单验证是否登录成功、百度搜索下拉框提示和快递单号查询等等。三、Ajax 原理是什么Ajax 请求数据流程,其中最核心的依赖是浏览器提供的对象 xhr,它扮演的角色相当于秘书,使得浏览器可以发出 HTTP 请求与接收 HTTP 响应。浏览器接着做其他事情,等收到 XHR 返回来的数据再渲染页面四、ajax 涉及的知识点1、readyState:返回当前文档的载入状态 0-(未初始化)还没有调用 send()方法 1-(载入)已调用 send()方法,正在发送请求 2-(载入完成)send()方法执行完成,已经接收到全部响应内容 3-(交互)正在解析响应内容 4-(完成)响应内容解析完成,可以在客户端调用了 2、status:HTTP 状态码 1XX:信息性状态码 ,表示接收的请求正在处理 2XX:成功状态码 , 表示请求正常处理 3XX:重定向状态码 ,表示需要附加操作来完成请求 4XX:客户端错误状态 ,表示服务器无法处理请求 5XX:服务器错误状态 ,表示服务器处理请求出错3、get 和 post 的区别五、原生 JS 实现 ajax 请求<script> function ajax(options){options = options ||{}; // 调用函数时如果 options 没有指定,就给它赋值{}, 一个空的 Object options.type=(options.type || "GET").toUpperCase();/// 请求格式 GET、POST,默认为 GET options.dataType=options.dataType || "json"; // 响应数据格式,默认 json var params=formatParams(options.data);//options.data 请求的数据 var xhr; // 考虑兼容性 if(window.XMLHttpRequest){xhr=new XMLHttpRequest(); }else if(window.ActiveObject){// 兼容 IE6 以下版本 xhr=new ActiveXobject('Microsoft.XMLHTTP'); } // 启动并发送一个请求 if(options.type=="GET"){xhr.open("GET",options.url+"?"+params,true); xhr.send(null); }else if(options.type=="POST"){xhr.open("post",options.url,true); // 设置表单提交时的内容类型 //Content-type 数据请求的格式 xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded"); xhr.send(params); } // 设置有效时间 setTimeout(function(){if(xhr.readySate!=4){xhr.abort(); } },options.timeout) // 接收 // options.success 成功之后的回调函数 options.error 失败后的回调函数 //xhr.responseText,xhr.responseXML 获得字符串形式的响应数据或者 XML 形式的响应数据 xhr.onreadystatechange=function(){if(xhr.readyState==4){ var status=xhr.status; if(status>=200&& status<300 || status==304){options.success&&options.success(xhr.responseText,xhr.responseXML); }else{options.error&&options.error(status); } } } } // 格式化请求参数 function formatParams(data){var arr=[]; for(var name in data){arr.push(encodeURIComponent(name)+"="+encodeURIComponent(data[name])); } arr.push(("v="+Math.random()).replace(".","")); return arr.join("&"); } // 基本的使用实例 ajax({ url:"http://server-name/login", type:'post', data:{ username:'username', password:'password' }, dataType:'json', timeout:10000, contentType:"application/json", success:function(data){// 服务器返回响应,根据响应结果,分析是否登录成功}, // 异常处理 error:function(e){console.log(e); } })</script>参考:http://www.sohu.com/a/238246281_100109711https://www.cnblogs.com/lanyueboyu/p/8793352.html

原生 js 封装 ajax 操作

Springboot 定时任务的使用

Springboot 定时任务的使用 Spring 提供了@Scheduled 注解用于定时任务。也就是说一般需求可以无需第三方定时任务。开启定时任务 启动类加 @EnableScheduling 开启定时任务@SpringBootApplication@EnableScheduling // 开启定时任务public class MainApplication {public static void main(String[] args) {SpringApplication.run(MainApplication.class, args); }}执行方法@Componentpublic class Jobs { // 表示方法执行完成后 5 秒 @Scheduled(fixedDelay = 5000) public void fixedDelayJob() throws InterruptedException {System.out.println("fixedDelay 每隔 5 秒 " + new Date()); } // 表示每隔 3 秒 @Scheduled(fixedRate = 3000) public void fixedRateJob() {System.out.println("fixedRate 每隔 3 秒 " + new Date()); } // 表示每天 8 时 30 分 0 秒执行 @Scheduled(cron = "0 0,30 0,8 ? * ? ") public void cronJob() {System.out.println(new Date() + " ...>>cron"); }}第三种使用 cron 表达式可上网搜在线 cron 表达式,选择时间会自动生成,cron 说明* 第一位,表示秒,取值 0 -59* 第二位,表示分,取值 0 -59* 第三位,表示小时,取值 0 -23* 第四位,日期天 / 日,取值 1 -31* 第五位,日期月份,取值 1 -12* 第六位,星期,取值 1 -7,星期一,星期二...,注:不是第 1 周,第二周的意思 另外:1 表示星期天,2 表示星期一。* 第 7 为,年份,可以留空,取值 1970-2099(*)星号:可以理解为每的意思,每秒,每分,每天,每月,每年...(?)问号:问号只能出现在日期和星期这两个位置。(-)减号:表达一个范围,如在小时字段中使用“10-12”,则表示从 10 到 12 点,即 10,11,12(,)逗号:表达一个列表值,如在星期字段中使用“1,2,4”,则表示星期一,星期二,星期四(/)斜杠:如:x/y,x 是开始值,y 是步长,比如在第一位(秒) 0/15 就是,从 0 秒开始,每 15 秒,最后就是 0,15,30,45,60 另:*/y,等同于 0 /y举例0 0 3 * * ? 每天 3 点执行0 5 3 * * ? 每天 3 点 5 分执行0 5 3 ? * * 每天 3 点 5 分执行,与上面作用相同0 5/10 3 * * ? 每天 3 点的 5 分,15 分,25 分,35 分,45 分,55 分这几个时间点执行0 10 3 ? * 1 每周星期天,3 点 10 分 执行,注:1 表示星期天 0 10 3 ? * 1#3 每个月的第三个星期,星期天 执行,# 号只能出现在星期的位置 开启异步任务 在类上加 @EnableAsync 注解@SpringBootApplication@EnableScheduling // 开启定时任务@EnableAsync // 开启异步public class MainApplication {public static void main(String[] args) {SpringApplication.run(MainApplication.class, args); }}异步执行,很简单在方法上加 @Async 注解即可 @Async @Scheduled(cron = "0 0,30 0,8 ? * ? ") public void cronJob() {System.out.println(new Date() + " ...>>cron"); }基于接口 以上基于注解可能不灵活,有时将 cron 表达式存储在数据库就无法执行了,可以使用接口实现。依赖 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <version>2.0.4.RELEASE</version> </parent> <dependencies> <dependency><!-- 添加 Web 依赖 --> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency><!-- 添加 MySql 依赖 --> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency><!-- 添加 Mybatis 依赖 配置 mybatis 的一些初始化的东西 --> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.1</version> </dependency> <dependency><!-- 添加 mybatis 依赖 --> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.5</version> <scope>compile</scope> </dependency> </dependencies>执行本地 MySQL 创建表且插入一条数据DROP DATABASE IF EXISTS `socks`;CREATE DATABASE `socks`;USE `SOCKS`;DROP TABLE IF EXISTS `cron`;CREATE TABLE `cron` (`cron_id` varchar(30) NOT NULL PRIMARY KEY, `cron` varchar(30) NOT NULL );INSERT INTO `cron` VALUES ('1', '0/5 * * * * ?');具体代码:@Component@Configuration //1. 主要用于标记配置类,兼备 Component 的效果。@EnableScheduling // 2. 开启定时任务public class DynamicScheduleTask implements SchedulingConfigurer { @Mapper public interface CronMapper {@Select("select cron from cron limit 1") public String getCron();} @Autowired // 注入 mapper @SuppressWarnings("all") CronMapper cronMapper; /** * 执行定时任务. */ @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { taskRegistrar.addTriggerTask(//1. 添加任务内容(Runnable) () -> System.out.println(" 执行动态定时任务: " + LocalDateTime.now().toLocalTime()), //2. 设置执行周期(Trigger) triggerContext -> { //2.1 从数据库获取执行周期 String cron = cronMapper.getCron(); //2.2 合法性校验. if (StringUtils.isEmpty(cron)) {// Omitted Code ..} //2.3 返回执行周期(Date) return new CronTrigger(cron).nextExecutionTime(triggerContext); } ); }}参考:https://www.cnblogs.com/qdhxhz/p/9058418.htmlhttps://www.mmzsblog.cn/articles/2019/08/08/1565247960802.html

Java 比较器 Comparator 进行排序及获取最大最小值

Java 比较器 Comparator 使用 / 获取最大最小值 Comparable:强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的 compareTo 方法 被称为它的自然比较方法。只能在类中实现 compareTo() 一次,不能经常修改类的代码实现自己想要的排序。实现 此接口的对象列表(和数组)可以通过 Collections.sort(和 Arrays.sort)进行自动排序,对象可以用作有序映射中 的键或有序集合中的元素,无需指定比较器。 Comparator:强行对某个对象进行整体排序。可以将 Comparator 传递给 sort 方法(如 Collections.sort 或 Arrays.sort),从而允许在排序顺序上实现精确控制。还可以使用 Comparator 来控制某些数据结构(如有序 set 或 有序映射)的顺序,或者为那些没有自然顺序的对象 collection 提供排序。Comparator 使用 ArrayList<Integer> list = new ArrayList<Integer>();// 采用工具类 完成 往集合中添加元素 Collections.addAll(list, 5, 222, 1, 2);// 排序方法 Collections.sort(list);// 输出排序后值 第一个最小 末位最大 System.out.println(list);//[1, 2, 5, 222]// 为对象排序 ArrayList<String> list1 = new ArrayList<String>(); list1.add("cba"); list1.add("abc"); list1.add("bca"); Collections.sort(list1, new Comparator<String>() { @Override// 第一个参数代表较小的 第二个较大 public int compare(String o1, String o2) {return o1.charAt(0) - o2.charAt(0); } });// Lamada 改写 Collections.sort(list1, (o1, o2) -> o1.charAt(0) - o2.charAt(0)); Collections.sort(list1, Comparator.comparingInt(o -> o.charAt(0))); System.out.println(list1);//[abc, bca, cba]Comparable 使用Student 实体类import lombok.AllArgsConstructor;import lombok.Data;@Data@AllArgsConstructorpublic class Student implements Comparable<Student> { private String name; private int age; @Override public int compareTo(Student o) {return this.age - o.age;// 升序}}或者用 if 判断重写 @Override public int compareTo(Student o) { // 升序 if (this.age > o.age) {return 1;} else if (this.age < o.age){return -1;} else{return 0;} }测试// 创建四个学生对象 存储到集合中 ArrayList<Student> list = new ArrayList<Student>(); list.add(new Student("rose", 18)); list.add(new Student("jack", 16)); list.add(new Student("mark", 16)); list.add(new Student("mona", 20));/*让学生 按照年龄排序 升序*/ Collections.sort(list);// 要求 该 list 中元素类型 必须实现比较器 Comparable 接口 for (Student student : list) {System.out.println(student); }stream 获取最大 / 小值ArrayList<Integer> list = new ArrayList<>();Collections.addAll(list, 2, 5, 3, 8, 7, 9);Integer max = list.stream().max(Integer::compare).get();Integer min = list.stream().min(Integer::compare).get();System.out.println(" 最大 " + max + ", 最小 " + min); // 最大 9, 最小 2 针对与于对象,例如上述 Student 获取最大年龄跟最小年龄ArrayList<Student> list = new ArrayList<Student>();list.add(new Student("rose", 18));list.add(new Student("jack", 16));list.add(new Student("mark", 16));list.add(new Student("bob", 27));list.add(new Student("mona", 20));Student max = list.stream().max(Student::compareTo).get();Student min = list.stream().min(Student::compareTo).get();System.out.println(max);System.out.println(min);但是如果没有继承 Comparable 接口重写 compareTo 的话,就得代码比较年龄了 ArrayList<Student> list = new ArrayList<Student>(); list.add(new Student("rose", 18)); list.add(new Student("jack", 16)); list.add(new Student("mark", 15)); list.add(new Student("bob", 27)); list.add(new Student("mona", 20));// 取最大年龄 这里同样用 Comparator Student max = list.stream().max(new Comparator<Student>() { @Override public int compare(Student a, Student b) {if (a.getAge() > b.getAge()) {return 1;} else {return -1;} } }).get();// 取最小年龄 取较大值中最小值 这里也可以用 max 不过就要将 > 改为 < Student min = list.stream().min((a, b) -> {if (a.getAge() > b.getAge()) {return 1;} else {return -1;} }).get();// Lamada 简写 Student max1 = list.stream().max((a, b) -> a.getAge()-b.getAge()).get(); Student max2 = list.stream().max(Comparator.comparingInt(Student::getAge)).get();// 若跟参数 a b 顺序不对应的话无法简写成 comparingInt 但 max 这时就成取最小值; Student min1 = list.stream().max((a, b) -> b.getAge()-a.getAge()).get(); Student min2 = list.stream().min((a, b) -> a.getAge()-b.getAge()).get(); System.out.println(min); System.out.println(min1); System.out.println(min2); System.out.println(max); System.out.println(max1); System.out.println(max2);对 double 等类型比较 在 Student 实体类中加入 Double hign 身高属性,直接调用 Comparator.comparingDouble 方法,或者直接使用 Comparator.comparing 也可以。// 打印最高的学生 Student studentMax = list.stream().max(Comparator.comparingDouble(Student::getHign)).get(); Student studentMin = list.stream().min(Comparator.comparingDouble(Student::getHign)).get(); System.out.println(studentMax); System.out.println(studentMin); Student studentMax1 = list.stream().max((a, b) -> {if (a.getHign() > b.getHign()) {return 1;} else {return -1;} }).get(); Student studentMin1 = list.stream().min((a, b) -> {if (a.getHign() > b.getHign()) {return 1;} else {return -1;} }).get(); System.out.println(studentMax1); System.out.println(studentMin1); Student studentMax2 = list.stream().max(Comparator.comparing(Student::getHign)).get(); System.out.println(studentMax2);当然了,也可以用 sort 进行排序,对于 double 也可以使用上述策略// 年龄从低到高 list.sort((a, b) -> a.getAge() - b.getAge()); System.out.println(list);// 身高升序 list.sort(Comparator.comparing(Student::getHign)); System.out.println(list);// 身高降序 简而言之就是 o2.getHign()-o1.getHign()>0,但是 compare 方法只返回 int,强制类型转换会导致不精准 list.sort(new Comparator<Student>() { @Override public int compare(Student o1, Student o2) {if (o1.getHign() < o2.getHign()) {return 1;} else {return -1;} } }); System.out.println(list);stream 排序 用 sorted 即可Stream<Student> asc = list.stream().sorted((a,b)->a.getAge()-b.getAge());asc = list.stream().sorted(Comparator.comparingInt(Student::getAge));Stream<Student> desc = list.stream().sorted((a,b)->b.getAge()-a.getAge());System.out.println(asc.collect(Collectors.toList()));System.out.println(desc.collect(Collectors.toList()));

解决 idea 创建 springboot 项目载入配置过慢问题

其实很简单,不使用默认的 start.spring.io,而选择 Custom,再填入阿里云的:https://start.aliyun.com 即可

JAVA 实现收发 UDP 请求

Internet 协议集支持一个无连接的传输协议,该协议称为用户数据报协议(UDP,User Datagram Protocol)。UDP 为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据包的方法。 发送 import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;/* * 实现 UDP 协议的发送端: * 实现封装数据的类 java.net.DatagramPacket 将你的数据包装 * 实现数据传输类 java.net.DatagramSocket 将数据包发出去 * * 实现步骤: * 1. 创建 DatagramPacket 对象,封装数据,接收的地址和端口 * 2. 创建 DatagramSocket 对象, * 3. 调用 DatagramSocket 类的方法 send,发送数据包 * 4. 关闭资源 * * DatagramPacket 构造方法: * DatagramPacket(byte[] buf,int length,InetAddress address,int port) * * DatagramSocket 构造方法: * DatagramSocket() 空参数 * 方法:send(DatagramPacket d) * * */public class UDPSendTest {public static void main(String[] args) throws Exception {// 创建发送端 Socket 对象 DatagramSocket ds = new DatagramSocket();// 创建数据并打包 String s = "My name is happywindman"; byte[] bytes = s.getBytes(); int length = s.length(); InetAddress ip = InetAddress.getByName("127.0.0.1");// 根据自己主机的 ip 地址或者主机名 int port = 10086; DatagramPacket dp = new DatagramPacket(bytes, length, ip, port);// 发送数据 ds.send(dp);// 释放资源 ds.close();}} 接收 import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;/* * 实现 UDP 接收端 * 实现封装数据包 java.net.DatagramPacket 将数据接收 * 实现输出传输 java.net.DatagramSocket 接受数据包 * * 实现步骤: * 1. 创建 DatagramSocket 对象,绑定端口号 * 要和发送数据端口号一致 * 2. 创建字节数组,接受发来的数组 * 3. 创建数据包对象 DatagramPacket * 4. 调用 DatagramSocket 对象方法 receive(DatagramPacket dp) * 接受数据,数据放在数据包中 * 5. 拆包 * 发送的 IP 地址 * 数据包对象 DatagramPacket() 方法 getAddress() 获取的是发送端的 IP 地址对象 * 接受到的字节个数 * 数据包对象 DatagramPacket() 方法 getLength() * 发送方的端口号 * 数据包对象 DatagramPacket() 方法 getPort() * 6. 关闭资源 */public class UDPReceiveTest {public static void main(String[] args) throws Exception {// 创建接收端 Socket 对象 DatagramSocket ds = new DatagramSocket(10086);// 接收数据 byte[] bytes = new byte[1024]; int length = bytes.length; DatagramPacket dp = new DatagramPacket(bytes, length); ds.receive(dp); // 解析数据 获取地址 带斜杠 InetAddress address = dp.getAddress(); // 获取发送端的 IP 地址对象 String ip = address.getHostAddress(); // 获取发送的端口号 int port = dp.getPort(); byte[] data = dp.getData(); int len = dp.getLength();// 输出数据// System.out.println(address); System.out.println(String.format(" 发送方地址:%s:%s", ip, port)); System.out.println(new String(data, 0, len)); System.out.println(new String(bytes, 0, len)); ds.close();}}