likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 4

💥 简介

gitHub->star 28K

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

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

💌 由来

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

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

🐬 为什么使用Hutool?

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

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

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

🐚 包含组件

模块介绍
hutool-aopJDK动态代理封装,提供非IOC下的切面支持
hutool-bloomFilter布隆过滤,提供一些Hash算法的布隆过滤
hutool-cache简单缓存实现
hutool-core核心,包括Bean操作、日期、各种Util等
hutool-cron定时任务模块,提供类Crontab表达式的定时任务
hutool-crypto加密解密模块,提供对称、非对称和摘要算法封装
hutool-dbJDBC封装后的数据操作,基于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-jsonJSON实现
hutool-captcha图片验证码实现
hutool-poi针对POI中Excel和Word的封装
hutool-socket基于Java的NIO和AIO的Socket封装
hutool-jwtJSON Web Token (JWT)封装实现

🍓 安装

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.16</version>
</dependency>

🍎 使用场景

🍒 类型转换 Convert

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

日期转换

String a = "2017-05-06";
Date value = Convert.toDate(a);

集合转换

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

时间单位转换

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

long a = 4535345;

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

金额大小写转换

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

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之间的相互转换

//当前时间
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

String dateStr = "2017-03-01";
Date date = DateUtil.parse(dateStr);

一天的开始和结束

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);

日期偏移时、分、天

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);

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

//昨天
DateUtil.yesterday()
//明天
DateUtil.tomorrow()
//上周
DateUtil.lastWeek()
//下周
DateUtil.nextWeek()
//上个月
DateUtil.lastMonth()
//下个月
DateUtil.nextMonth()

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

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);

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

// "摩羯座"
String zodiac = DateUtil.getZodiac(Month.JANUARY.getValue(), 19);

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

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

🍅 IO操作

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 拷贝文件或目录

文件类型判断

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

🍉 URL工具

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);

执行方法


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 算法就是这种生成器

//参数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做工具化封装

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

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

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

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

//将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包。

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 解压

//将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 获取省份

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文件

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列表

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

定义Bean:

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

读取

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

输出:

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

生成CSV文件

//指定路径和编码
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 缩放图片 提供两种重载方法:其中一个是按照长宽缩放,另一种是按照比例缩放。

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

cut 剪裁图片

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

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

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等

ImgUtil.convert(FileUtil.file("e:/test2.png"), FileUtil.file("e:/test2Convert.jpg"));

gray 彩色转为黑白

ImgUtil.gray(FileUtil.file("d:/logo.png"), FileUtil.file("d:/result.png"));

pressText 添加文字水印

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 添加图片水印

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 旋转图片

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

flip 水平翻转图片

ImgUtil.flip(FileUtil.file("d:/logo.png"), FileUtil.file("d:/result.png"));

🐳 网络工具

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

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是否在指定范围内
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 线段干扰的验证码

//定义图形验证码的长和宽
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 圆圈干扰验证码

//定义图形验证码的长、宽、验证码字符数、干扰元素个数
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 扭曲干扰验证码

//定义图形验证码的长、宽、验证码字符数、干扰线宽度
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");

自定义验证码

// 自定义纯数字的验证码(随机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服务端

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客户端

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服务端

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客户端

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 这个工具能够为你的研发提效!

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