java程序员必备开发包【小而美的开发工具-HU Tool】

💥 简介

gitHub->star 28K

Hutool是一个功能丰富且易用的Java工具库,通过诸多实用工具类的使用,旨在帮助开发者快速、便捷地完成各类开发任务。 这些封装的工具涵盖了字符串、数字、集合、编码、日期、文件、IO、加密、数据库JDBC、JSON、HTTP客户端等一系列操作, 可以满足各种不同的开发需求。

💌 由来

Hutool = Hu + tool ,是原公司项目底层代码剥离后的开源库,"Hu"是公司名称的表示,tool表示工具。Hutool谐音"糊涂",一方面简洁易懂,一方面寓意"难得糊涂"。

🐬 为什么使用Hutool?

Hutool的目标是使用一个工具方法代替一段复杂代码,从而最大限度的避免"复制粘贴"代码的问题,彻底改变我们写代码的方式。

以计算MD5为例: 【以前】打开搜索引擎 -> 搜"Java MD5加密" -> 打开某篇博客-> 复制粘贴 -> 改改好用 【现在】引入Hutool -> SecureUtil.md5()

Hutool的存在就是为了减少代码搜索成本,避免网络上参差不齐的代码出现导致的bug。

🐚 包含组件

模块 介绍
hutool-aop JDK动态代理封装,提供非IOC下的切面支持
hutool-bloomFilter 布隆过滤,提供一些Hash算法的布隆过滤
hutool-cache 简单缓存实现
hutool-core 核心,包括Bean操作、日期、各种Util等
hutool-cron 定时任务模块,提供类Crontab表达式的定时任务
hutool-crypto 加密解密模块,提供对称、非对称和摘要算法封装
hutool-db JDBC封装后的数据操作,基于ActiveRecord思想
hutool-dfa 基于DFA模型的多关键字查找
hutool-extra 扩展模块,对第三方封装(模板引擎、邮件、Servlet、二维码、Emoji、FTP、分词等)
hutool-http 基于HttpUrlConnection的Http客户端封装
hutool-log 自动识别日志实现的日志门面
hutool-script 脚本执行封装,例如Javascript
hutool-setting 功能更强大的Setting配置文件和Properties封装
hutool-system 系统参数调用封装(JVM信息等)
hutool-json JSON实现
hutool-captcha 图片验证码实现
hutool-poi 针对POI中Excel和Word的封装
hutool-socket 基于Java的NIO和AIO的Socket封装
hutool-jwt JSON Web Token (JWT)封装实现

🍓 安装

xml 复制代码
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.16</version>
</dependency>

🍎 使用场景

🍒 类型转换 Convert

在Java开发中我们要面对各种各样的类型转换问题,尤其是从命令行获取的用户参数、从HttpRequest获取的Parameter等等,这些参数类型多种多样,我们怎么去转换他们呢? 常用的办法是先整成String 然后调用XXX.parseXXX方法 还要承受转换失败的风险 不得不加一层try catch 这个小小的过程混迹在业务代码中会显得非常难看和臃肿。

日期转换

java 复制代码
String a = "2017-05-06";
Date value = Convert.toDate(a);

集合转换

java 复制代码
Object[] a = {"a", "你", "好", "", 1};
List<?> list = Convert.toList(a);

时间单位转换

Convert.convertTime方法主要用于转换时长单位,比如一个很大的毫秒,我想获得这个毫秒数对应多少分

java 复制代码
long a = 4535345;

//结果为:75
long minutes = Convert.convertTime(a, TimeUnit.MILLISECONDS, TimeUnit.MINUTES);

金额大小写转换

面对财务类需求,Convert.digitToChinese将金钱数转换为大写形式:

java 复制代码
double a = 67556.32;

//结果为:"陆万柒仟伍佰伍拾陆元叁角贰分"
String digitUppercase = Convert.digitToChinese(a);

🍑 日期时间

日期时间包是Hutool的核心包之一,提供针对JDK中Date和Calendar对象的封装,封装对象如下:

  • DateUtil 针对日期时间操作提供一系列静态方法
  • DateTime 提供类似于Joda-Time中日期时间对象的封装,继承自Date类,并提供更加丰富的对象方法。
  • FastDateFormat 提供线程安全的针对Date对象的格式化和日期字符串解析支持。此对象在实际使用中并不需要感知,相关操作已经封装在DateUtil和DateTime的相关方法中。
  • DateBetween 计算两个时间间隔的类,除了通过构造新对象使用外,相关操作也已封装在DateUtil和DateTime的相关方法中。 TimeInterval 一个简单的计时器类,常用于计算某段代码的执行时间,提供包括毫秒、秒、分、时、天、周等各种单位的花费时长计算,对象的静态构造已封装在DateUtil中。
  • DatePattern 提供常用的日期格式化模式,包括String类型和FastDateFormat两种类型。

Date、long、Calendar之间的相互转换

java 复制代码
//当前时间
Date date = DateUtil.date();
//当前时间
Date date2 = DateUtil.date(Calendar.getInstance());
//当前时间
Date date3 = DateUtil.date(System.currentTimeMillis());
//当前时间字符串,格式:yyyy-MM-dd HH:mm:ss
String now = DateUtil.now();
//当前日期字符串,格式:yyyy-MM-dd
String today= DateUtil.today();

字符串转日期

DateUtil.parse方法会自动识别一些常用格式,包括:

yyyy/MM/dd HH:mm:ss yyyy.MM.dd HH:mm:ss yyyy年MM月dd日 HH时mm分ss秒 yyyy-MM-dd yyyy/MM/dd yyyy.MM.dd HH:mm:ss HH时mm分ss秒 yyyy-MM-dd HH:mm yyyy-MM-dd HH:mm:ss yyyy-MM-dd HH:mm:ss.SSS yyyyMMddHHmmss yyyyMMddHHmmssSSS yyyyMMdd EEE, dd MMM yyyy HH:mm:ss z EEE MMM dd HH:mm:ss zzz yyyy yyyy-MM-dd'T'HH:mm:ss'Z' yyyy-MM-dd'T'HH:mm:ss.SSS'Z' yyyy-MM-dd'T'HH:mm:ssZ yyyy-MM-dd'T'HH:mm:ss.SSSZ

java 复制代码
String dateStr = "2017-03-01";
Date date = DateUtil.parse(dateStr);

一天的开始和结束

java 复制代码
String dateStr = "2017-03-01 22:33:23";
Date date = DateUtil.parse(dateStr);

//一天的开始,结果:2017-03-01 00:00:00
Date beginOfDay = DateUtil.beginOfDay(date);

//一天的结束,结果:2017-03-01 23:59:59
Date endOfDay = DateUtil.endOfDay(date);

日期偏移时、分、天

java 复制代码
String dateStr = "2017-03-01 22:33:23";
Date date = DateUtil.parse(dateStr);

//结果:2017-03-03 22:33:23
Date newDate = DateUtil.offset(date, DateField.DAY_OF_MONTH, 2);

//常用偏移,结果:2017-03-04 22:33:23
DateTime newDate2 = DateUtil.offsetDay(date, 3);

//常用偏移,结果:2017-03-01 19:33:23
DateTime newDate3 = DateUtil.offsetHour(date, -3);

昨天、明天、上周、下周、上个月

java 复制代码
//昨天
DateUtil.yesterday()
//明天
DateUtil.tomorrow()
//上周
DateUtil.lastWeek()
//下周
DateUtil.nextWeek()
//上个月
DateUtil.lastMonth()
//下个月
DateUtil.nextMonth()

日期时间差(计算两个时间相差天数、小时)

java 复制代码
String dateStr1 = "2017-03-01 22:33:23";
Date date1 = DateUtil.parse(dateStr1);

String dateStr2 = "2017-04-01 23:33:23";
Date date2 = DateUtil.parse(dateStr2);

//相差一个月,31天
long betweenDay = DateUtil.between(date1, date2, DateUnit.DAY);

获取星座、属相、平年闰年

java 复制代码
// "摩羯座"
String zodiac = DateUtil.getZodiac(Month.JANUARY.getValue(), 19);

// "狗"
String chineseZodiac = DateUtil.getChineseZodiac(1994);
//年龄
DateUtil.ageOfNow("1990-01-30");

//是否闰年
DateUtil.isLeapYear(2017);

🍅 IO操作

java 复制代码
BufferedInputStream in = FileUtil.getInputStream("d:/test.txt");
BufferedOutputStream out = FileUtil.getOutputStream("d:/test2.txt");
long copySize = IoUtil.copy(in, out, IoUtil.DEFAULT_BUFFER_SIZE);

NIO,提供了copyByNIO方法

readBytes 返回byte数组(读取图片等) readHex 读取16进制字符串 readObj 读取序列化对象(反序列化) readLines 按行读取

仿Linux文件处理

ls 列出目录和文件 touch 创建文件,如果父目录不存在也自动创建 mkdir 创建目录,会递归创建每层目录 del 删除文件或目录(递归删除,不判断是否为空),这个方法相当于Linux的delete命令 copy 拷贝文件或目录

文件类型判断

java 复制代码
File file = FileUtil.file("d:/test.jpg");
String type = FileTypeUtil.getType(file);
//输出 jpg则说明确实为jpg文件
Console.log(type);

🍉 URL工具

java 复制代码
URLUtil.normalize
String url = "http://www.hutool.cn//aaa/bbb";
// 结果为:http://www.hutool.cn/aaa/bbb
String normalize = URLUtil.normalize(url);

url = "http://www.hutool.cn//aaa/\\bbb?a=1&b=2";
// 结果为:http://www.hutool.cn/aaa/bbb?a=1&b=2
normalize = URLUtil.normalize(url);

URLEncoder.encode

String body = "366466 - 副本.jpg";
// 结果为:366466%20-%20%E5%89%AF%E6%9C%AC.jpg
String encode = URLUtil.encode(body);

URLUtil.decode 封装URLDecoder.decode,将%开头的16进制表示的内容解码。
URLUtil.getPath 获得path部分 URI -> http://www.aaa.bbb/search?scope=ccc&q=ddd PATH -> /search
URLUtil.toURI 转URL或URL字符串为URI。

🍇 反射

获取某个类的所有方法 Method\[\] methods = ReflectUtil.getMethods(ExamInfoDict.class); 获取某个类的指定方法 Method method = ReflectUtil.getMethod(ExamInfoDict.class, "getId"); 构造对象 ReflectUtil.newInstance(ExamInfoDict.class);

执行方法

java 复制代码
class TestClass {
    private int a;

    public int getA() {
        return a;
    }

    public void setA(int a) {
        this.a = a;
    }
}
TestClass testClass = new TestClass();
ReflectUtil.invoke(testClass, "setA", 10);

🥗 命令行工具

获取Windows下可以获取网卡信息。 String str = RuntimeUtil.execForStr("ipconfig");

🥝 分布式ID生成

分布式系统中,有一些需要使用全局唯一ID的场景,有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。 Twitter的Snowflake 算法就是这种生成器

java 复制代码
//参数1为终端ID
//参数2为数据中心ID
Snowflake snowflake = IdUtil.getSnowflake(1, 1);
long id = snowflake.nextId();

//简单使用
long id = IdUtil.getSnowflakeNextId();
String id = snowflake.getSnowflakeNextIdStr();

🍋 高效压缩

Java中,对文件、文件夹打包,压缩是一件比较繁琐的事情,我们常常引入Zip4j进行此类操作。但是很多时候,JDK中的zip包就可满足我们大部分需求。

ZipUtil针对java.util.zip做工具化封装

压缩解压操作可以一个方法搞定,并且自动处理文件和目录的问题,不再需要用户判断,压缩后的文件也会自动创建文件,自动创建父目录,大大简化的压缩解压的复杂度。

打包到当前目录(可以打包文件,也可以打包文件夹,根据路径自动判断)

java 复制代码
//将aaa目录下的所有文件目录打包到d:/aaa.zip
ZipUtil.zip("d:/aaa");

指定打包后保存的目的地,自动判断目标是文件还是文件夹

java 复制代码
//将aaa目录下的所有文件目录打包到d:/bbb/目录下的aaa.zip文件中
// 此处第二个参数必须为文件,不能为目录
ZipUtil.zip("d:/aaa", "d:/bbb/aaa.zip");

//将aaa目录下的所有文件目录打包到d:/bbb/目录下的ccc.zip文件中
ZipUtil.zip("d:/aaa", "d:/bbb/ccc.zip");

多文件或目录压缩。可以选择多个文件或目录一起打成zip包。

java 复制代码
ZipUtil.zip(FileUtil.file("d:/bbb/ccc.zip"), false, 
    FileUtil.file("d:/test1/file1.txt"),
    FileUtil.file("d:/test1/file2.txt"),
    FileUtil.file("d:/test2/file1.txt"),
    FileUtil.file("d:/test2/file2.txt")
);

ZipUtil.unzip 解压

java 复制代码
//将test.zip解压到e:\\aaa目录下,返回解压到的目录
File unzip = ZipUtil.unzip("E:\\aaa\\test.zip", "e:\\aaa");

🌽 身份信息获取

IdcardUtil现在支持大陆15位、18位身份证,港澳台10位身份证。

isValidCard 验证身份证是否合法 convert15To18 身份证15位转18位 getBirthByIdCard 获取生日 getAgeByIdCard 获取年龄 getYearByIdCard 获取生日年 getMonthByIdCard 获取生日月 getDayByIdCard 获取生日天 getGenderByIdCard 获取性别 getProvinceByIdCard 获取省份

java 复制代码
String ID_18 = "321083197812162119";
String ID_15 = "150102880730303";

//是否有效
boolean valid = IdcardUtil.isValidCard(ID_18);
boolean valid15 = IdcardUtil.isValidCard(ID_15);

//转换
String convert15To18 = IdcardUtil.convert15To18(ID_15);
Assert.assertEquals(convert15To18, "150102198807303035");

//年龄
DateTime date = DateUtil.parse("2017-04-10");
        
int age = IdcardUtil.getAgeByIdCard(ID_18, date);
Assert.assertEquals(age, 38);

int age2 = IdcardUtil.getAgeByIdCard(ID_15, date);
Assert.assertEquals(age2, 28);

//生日
String birth = IdcardUtil.getBirthByIdCard(ID_18);
Assert.assertEquals(birth, "19781216");

String birth2 = IdcardUtil.getBirthByIdCard(ID_15);
Assert.assertEquals(birth2, "19880730");

//省份
String province = IdcardUtil.getProvinceByIdCard(ID_18);
Assert.assertEquals(province, "江苏");

String province2 = IdcardUtil.getProvinceByIdCard(ID_15);
Assert.assertEquals(province2, "内蒙古");

🥒 CSV文件处理

读取CSV文件

java 复制代码
CsvReader reader = CsvUtil.getReader();
//从文件中读取CSV数据
CsvData data = reader.read(FileUtil.file("test.csv"));
List<CsvRow> rows = data.getRows();
//遍历行
for (CsvRow csvRow : rows) {
    //getRawList返回一个List列表,列表的每一项为CSV中的一个单元格(既逗号分隔部分)
    Console.log(csvRow.getRawList());
}

读取为Bean列表

java 复制代码
首先测试的CSV:test_bean.csv:
姓名,gender,focus,age
张三,男,无,33
李四,男,好对象,23
王妹妹,女,特别关注,22

定义Bean:

java 复制代码
// lombok注解
@Data
private static class TestBean{
    // 如果csv中标题与字段不对应,可以使用alias注解设置别名
    @Alias("姓名")
    private String name;
    private String gender;
    private String focus;
    private Integer age;
}

读取

java 复制代码
final CsvReader reader = CsvUtil.getReader();
//假设csv文件在classpath目录下
final List<TestBean> result = reader.read(
ResourceUtil.getUtf8Reader("test_bean.csv"), TestBean.class);

输出:

java 复制代码
CsvReaderTest.TestBean(name=张三, gender=男, focus=无, age=33)
CsvReaderTest.TestBean(name=李四, gender=男, focus=好对象, age=23)
CsvReaderTest.TestBean(name=王妹妹, gender=女, focus=特别关注, age=22)

生成CSV文件

java 复制代码
//指定路径和编码
CsvWriter writer = CsvUtil.getWriter("e:/testWrite.csv", CharsetUtil.CHARSET_UTF_8);
//按行写出
writer.write(
    new String[] {"a1", "b1", "c1"}, 
    new String[] {"a2", "b2", "c2"}, 
    new String[] {"a3", "b3", "c3"}
);

🤡 图片工具

scale 缩放图片 提供两种重载方法:其中一个是按照长宽缩放,另一种是按照比例缩放。

java 复制代码
ImgUtil.scale(
    FileUtil.file("d:/face.jpg"), 
    FileUtil.file("d:/face_result.jpg"), 
    0.5f//缩放比例
);

cut 剪裁图片

java 复制代码
ImgUtil.cut(
    FileUtil.file("d:/face.jpg"), 
    FileUtil.file("d:/face_result.jpg"), 
    new Rectangle(200, 200, 100, 100)//裁剪的矩形区域
);

slice 按照行列剪裁切片(将图片分为20行和20列)

java 复制代码
ImgUtil.slice(FileUtil.file("e:/test2.png"), FileUtil.file("e:/dest/"), 10, 10);

convert 图片类型转换 支持GIF->JPG、GIF->PNG、PNG->JPG、PNG->GIF(X)、BMP->PNG等

java 复制代码
ImgUtil.convert(FileUtil.file("e:/test2.png"), FileUtil.file("e:/test2Convert.jpg"));

gray 彩色转为黑白

java 复制代码
ImgUtil.gray(FileUtil.file("d:/logo.png"), FileUtil.file("d:/result.png"));

pressText 添加文字水印

java 复制代码
ImgUtil.pressText(//
    FileUtil.file("e:/pic/face.jpg"), //
    FileUtil.file("e:/pic/test2_result.png"), //
    "版权所有", Color.WHITE, //文字
    new Font("黑体", Font.BOLD, 100), //字体
    0, //x坐标修正值。 默认在中间,偏移量相对于中间偏移
    0, //y坐标修正值。 默认在中间,偏移量相对于中间偏移
    0.8f//透明度:alpha 必须是范围 [0.0, 1.0] 之内(包含边界值)的一个浮点数字
);

pressImage 添加图片水印

java 复制代码
ImgUtil.pressImage(
    FileUtil.file("d:/picTest/1.jpg"), 
    FileUtil.file("d:/picTest/dest.jpg"), 
    ImgUtil.read(FileUtil.file("d:/picTest/1432613.jpg")), //水印图片
    0, //x坐标修正值。 默认在中间,偏移量相对于中间偏移
    0, //y坐标修正值。 默认在中间,偏移量相对于中间偏移
    0.1f
);

rotate 旋转图片

java 复制代码
// 旋转180度
BufferedImage image = ImgUtil.rotate(ImageIO.read(FileUtil.file("e:/pic/366466.jpg")), 180);
ImgUtil.write(image, FileUtil.file("e:/pic/result.png"));

flip 水平翻转图片

java 复制代码
ImgUtil.flip(FileUtil.file("d:/logo.png"), FileUtil.file("d:/result.png"));

🐳 网络工具

在日常开发中,网络连接这块儿必不可少。日常用到的一些功能,隐藏掉部分IP地址、绝对相对路径的转换等等。

java 复制代码
longToIpv4 根据long值获取ip v4地址
ipv4ToLong 根据ip地址计算出long型的数据
isUsableLocalPort 检测本地端口可用性
isValidPort 是否为有效的端口
isInnerIP 判定是否为内网IP
localIpv4s 获得本机的IP地址列表
toAbsoluteUrl 相对URL转换为绝对URL
hideIpPart 隐藏掉IP地址的最后一部分为 * 代替
buildInetSocketAddress 构建InetSocketAddress
getIpByHost 通过域名得到IP
isInner 指定IP的long是否在指定范围内
java 复制代码
String ip= "127.0.0.1";
long iplong = 2130706433L;

//根据long值获取ip v4地址
String ip= NetUtil.longToIpv4(iplong);


//根据ip地址计算出long型的数据
long ip= NetUtil.ipv4ToLong(ip);

//检测本地端口可用性
boolean result= NetUtil.isUsableLocalPort(6379);

//是否为有效的端口
boolean result= NetUtil.isValidPort(6379);

//隐藏掉IP地址
 String result =NetUtil.hideIpPart(ip);

🐉 图形验证码

由于对验证码需求量巨大,在Hutool中加入验证码生成和校验功能

验证码功能位于cn.hutool.captcha包中,核心接口为ICaptcha,此接口定义了以下方法:

createCode 创建验证码,实现类需同时生成随机验证码字符串和验证码图片 getCode 获取验证码的文字内容 verify 验证验证码是否正确,建议忽略大小写 write 将验证码写出到目标流中 其中write方法只有一个OutputStream,ICaptcha实现类可以根据这个方法封装写出到文件等方法。 AbstractCaptcha为一个ICaptcha抽象实现类,此类实现了验证码文本生成、非大小写敏感的验证、写出到流和文件等方法,通过继承此抽象类只需实现createImage方法定义图形生成规则即可。

LineCaptcha 线段干扰的验证码

java 复制代码
//定义图形验证码的长和宽
LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(200, 100);

//图形验证码写出,可以写出到文件,也可以写出到流
lineCaptcha.write("d:/line.png");
//输出code
Console.log(lineCaptcha.getCode());
//验证图形验证码的有效性,返回boolean值
lineCaptcha.verify("1234");

//重新生成验证码
lineCaptcha.createCode();
lineCaptcha.write("d:/line.png");
//新的验证码
Console.log(lineCaptcha.getCode());
//验证图形验证码的有效性,返回boolean值
lineCaptcha.verify("1234");

CircleCaptcha 圆圈干扰验证码

java 复制代码
//定义图形验证码的长、宽、验证码字符数、干扰元素个数
CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(200, 100, 4, 20);
//CircleCaptcha captcha = new CircleCaptcha(200, 100, 4, 20);
//图形验证码写出,可以写出到文件,也可以写出到流
captcha.write("d:/circle.png");
//验证图形验证码的有效性,返回boolean值
captcha.verify("1234");

ShearCaptcha 扭曲干扰验证码

java 复制代码
//定义图形验证码的长、宽、验证码字符数、干扰线宽度
ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(200, 100, 4, 4);
//ShearCaptcha captcha = new ShearCaptcha(200, 100, 4, 4);
//图形验证码写出,可以写出到文件,也可以写出到流
captcha.write("d:/shear.png");
//验证图形验证码的有效性,返回boolean值
captcha.verify("1234");

自定义验证码

java 复制代码
// 自定义纯数字的验证码(随机4位数字,可重复)
RandomGenerator randomGenerator = new RandomGenerator("0123456789", 4);
LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(200, 100);
lineCaptcha.setGenerator(randomGenerator);
// 重新生成code
lineCaptcha.createCode();
ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(200, 45, 4, 4);
// 自定义验证码内容为四则运算方式
captcha.setGenerator(new MathGenerator());
// 重新生成code
captcha.createCode();

🌾 网络socket

NIO服务端

java 复制代码
NioServer server = new NioServer(8080);
server.setChannelHandler((sc)->{
    ByteBuffer readBuffer = ByteBuffer.allocate(1024);
    try{
        //从channel读数据到缓冲区
        int readBytes = sc.read(readBuffer);
        if (readBytes > 0) {
            //Flips this buffer.  The limit is set to the current position and then
            // the position is set to zero,就是表示要从起始位置开始读取数据
            readBuffer.flip();
            //eturns the number of elements between the current position and the  limit.
            // 要读取的字节长度
            byte[] bytes = new byte[readBuffer.remaining()];
            //将缓冲区的数据读到bytes数组
            readBuffer.get(bytes);
            String body = StrUtil.utf8Str(bytes);
            Console.log("[{}]: {}", sc.getRemoteAddress(), body);
            doWrite(sc, body);
        } else if (readBytes < 0) {
            IoUtil.close(sc);
        }
    } catch (IOException e){
        throw new IORuntimeException(e);
    }
});
server.listen();
public static void doWrite(SocketChannel channel, String response) throws IOException {
    response = "收到消息:" + response;
    //将缓冲数据写入渠道,返回给客户端
    channel.write(BufferUtil.createUtf8(response));
}

NIO客户端

java 复制代码
NioClient client = new NioClient("127.0.0.1", 8080);
client.setChannelHandler((sc)->{
    ByteBuffer readBuffer = ByteBuffer.allocate(1024);
    //从channel读数据到缓冲区
    int readBytes = sc.read(readBuffer);
    if (readBytes > 0) {
        //Flips this buffer.  The limit is set to the current position and then
        // the position is set to zero,就是表示要从起始位置开始读取数据
        readBuffer.flip();
        //returns the number of elements between the current position and the  limit.
        // 要读取的字节长度
        byte[] bytes = new byte[readBuffer.remaining()];
        //将缓冲区的数据读到bytes数组
        readBuffer.get(bytes);
        String body = StrUtil.utf8Str(bytes);
        Console.log("[{}]: {}", sc.getRemoteAddress(), body);
    } else if (readBytes < 0) {
        sc.close();
    }
});
client.listen();
client.write(BufferUtil.createUtf8("你好。\n"));
client.write(BufferUtil.createUtf8("你好2。"));
// 在控制台向服务器端发送数据
Console.log("请输入发送的消息:");
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()) {
    String request = scanner.nextLine();
    if (request != null && request.trim().length() > 0) {
        client.write(BufferUtil.createUtf8(request));
    }
}

AIO服务端

java 复制代码
AioServer aioServer = new AioServer(8899);
aioServer.setIoAction(new SimpleIoAction() {
    
    @Override
    public void accept(AioSession session) {
        StaticLog.debug("【客户端】:{} 连接。", session.getRemoteAddress());
        session.write(BufferUtil.createUtf8("=== Welcome to Hutool socket server. ==="));
    }
    
    @Override
    public void doAction(AioSession session, ByteBuffer data) {
        Console.log(data);
        
        if(false == data.hasRemaining()) {
            StringBuilder response = StrUtil.builder()//
                    .append("HTTP/1.1 200 OK\r\n")//
                    .append("Date: ").append(DateUtil.formatHttpDate(DateUtil.date())).append("\r\n")//
                    .append("Content-Type: text/html; charset=UTF-8\r\n")//
                    .append("\r\n")
                    .append("Hello Hutool socket");//
            session.writeAndClose(BufferUtil.createUtf8(response));
        }else {
            session.read();
        }
    }
}).start(true);

AIO客户端

java 复制代码
AioClient client = new AioClient(new InetSocketAddress("localhost", 8899), new SimpleIoAction() {
    
    @Override
    public void doAction(AioSession session, ByteBuffer data) {
        if(data.hasRemaining()) {
            Console.log(StrUtil.utf8Str(data));
            session.read();
        }
        Console.log("OK");
    }
});

client.write(ByteBuffer.wrap("Hello".getBytes()));
client.read();

client.close();

🎉 总结

HU Tool GitHub下载地址 Hu Tool 中文文档查看地址

Hutool是一个Java工具包类库,对文件、流、加密解密、转码、正则、线程、XML等JDK方法进行封装,组成各种Util工具 类

希望Hu Tool 这个工具能够为你的研发提效!

相关推荐
葫芦和十三43 分钟前
图解 MongoDB 22|读写关注:持久性与一致性的档位选择
后端·mongodb·agent
葫芦和十三7 小时前
图解 MongoDB 21|选举与 failover:Primary 是怎么选出来的
后端·mongodb·agent
GetcharZp8 小时前
26k Star 开源内网穿透神器 NetBird,一分钟实现全球设备互联!
后端
考虑考虑8 小时前
Mybatis实现批量插入
java·后端·mybatis
咖啡八杯9 小时前
GoF设计模式——中介者模式
java·后端·spring·设计模式
lizhongxuan11 小时前
多Agent之间的区别
后端
青石路13 小时前
记一次多JDK版本问题的排查,一坑套一坑,差点没爬上来
java
杨充13 小时前
1.面向对象设计思想
后端
IT_陈寒14 小时前
Java的Date类又坑了我一次,改用时间戳真香
前端·人工智能·后端
systemPro14 小时前
2.6亿条设备数据,历史查询从超时到50ms,我做了什么
后端