文本操作
CSV文件处理工具-CsvUtil
介绍
逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本)。
Hutool针对此格式,参考FastCSV项目做了对CSV文件读写的实现(Hutool实现完全独立,不依赖第三方)
CsvUtil
是CSV工具类,主要封装了两个方法:
getReader 用于对CSV文件读取
getWriter 用于生成CSV文件
这两个方法分别获取CsvReader
对象和CsvWriter
,从而独立完成CSV文件的读写。
使用
读取CSV文件
读取为CsvRow
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());
}
CsvRow
对象还记录了一些其他信息,包括原始行号等。
读取为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"}
);
乱码问题
CSV文件本身为一种简单文本格式,有编码区分,你可以使用任意编码。
但是当使用Excel读取CSV文件时,如果你的编码与系统编码不一致,会出现乱码的情况,解决方案如下:
可以将csv文本编码设置为与系统一致,如Windows下可以设置GBK
可以增加BOM头来指定编码,这样Excel可以自动识别bom头的编码完成解析。
Unicode编码转换工具-UnicodeUtil
介绍
此工具主要针对类似于\\u4e2d\\u6587
这类Unicode字符做一些特殊转换。
使用
字符串转Unicode符
//第二个参数true表示跳过ASCII字符(只跳过可见字符)
String s = UnicodeUtil.toUnicode("aaa123中文", true);
//结果aaa123\\u4e2d\\u6587
Unicode转字符串
String str = "aaa\\U4e2d\\u6587\\u111\\urtyu\\u0026";
String res = UnicodeUtil.toString(str);
//结果aaa中文\\u111\\urtyu&
由于\\u111
为非Unicode字符,因此原样输出。
可复用字符串生成器-StrBuilder
介绍
在JDK提供的StringBuilder
中,拼接字符串变得更加高效和灵活,但是生成新的字符串需要重新构建StringBuilder
对象,造成性能损耗和内存浪费,因此Hutool提供了可复用的StrBuilder
。
使用
StrBuilder
和StringBuilder
使用方法基本一致,只是多了reset
方法可以重新构建一个新的字符串而不必开辟新内存。
StrBuilder builder = StrBuilder.create();
builder.append("aaa").append("你好").append('r');
//结果:aaa你好r
多次构建字符串性能测试
我们模拟创建1000000次字符串对两者性能对比,采用TimeInterval
计时:
//StringBuilder
TimeInterval timer = DateUtil.timer();
StringBuilder b2 = new StringBuilder();
for(int i =0; i< 1000000; i++) {
b2.append("test");
b2 = new StringBuilder();
}
Console.log(timer.interval());
//StrBuilder
TimeInterval timer = DateUtil.timer();
StrBuilder builder = StrBuilder.create();
for(int i =0; i< 1000000; i++) {
builder.append("test");
builder.reset();
}
Console.log(timer.interval());
测试结果为:
StringBuilder: 39ms
StrBuilder : 20ms
性能几乎翻倍。也欢迎用户自行测试。
字符串切割-StrSplitter
由来
在Java的String对象中提供了split方法用于通过某种字符串分隔符来把一个字符串分割为数组。但是有的时候我们对这种操作有不同的要求,默认方法无法满足,这包括:
分割限制分割数
分割后每个字符串是否需要去掉两端空格
是否忽略空白项
根据固定长度分割
通过正则分隔
因此,StrSplitter
应运而生。StrSplitter
中全部为静态方法,方便快捷调用。
方法
基础方法
split
切分字符串,众多可选参数,返回结果为ListsplitToArray
切分字符串,返回结果为数组splitByRegex
根据正则切分字符串splitByLength
根据固定长度切分字符串
栗子:
String str1 = "a, ,efedsfs, ddf";
//参数:被切分字符串,分隔符逗号,0表示无限制分片数,去除两边空格,忽略空白项
List<String> split = StrSplitter.split(str1, ',', 0, true, true);
特殊方法
splitPath
切分字符串,分隔符为"/"splitPathToArray
切分字符串,分隔符为"/",返回数组
注解
注解工具-AnnotationUtil
介绍
封装了注解获取等方法的工具类。
使用
方法介绍
注解获取相关方法:
getAnnotations
获取指定类、方法、字段、构造等上的注解列表getAnnotation
获取指定类型注解getAnnotationValue
获取指定注解属性的值
例子:
我们定义一个注解:
// Retention注解决定MyAnnotation注解的生命周期
@Retention(RetentionPolicy.RUNTIME)
// Target注解决定MyAnnotation注解可以加在哪些成分上,如加在类身上,或者属性身上,或者方法身上等成分
@Target({ ElementType.METHOD, ElementType.TYPE })
public @interface AnnotationForTest {
/**
* 注解的默认属性值
*
* @return 属性值
*/
String value();
}
给需要的类加上注解:
@AnnotationForTest("测试")
public static class ClassWithAnnotation{
}
获取注解中的值:
// value为"测试"
Object value = AnnotationUtil.getAnnotationValue(ClassWithAnnotation.class, AnnotationForTest.class);
注解属性获取相关方法:
getRetentionPolicy
获取注解类的保留时间,可选值 SOURCE(源码时),CLASS(编译时),RUNTIME(运行时),默认为 CLASSgetTargetType
获取注解类可以用来修饰哪些程序元素,如 TYPE, METHOD, CONSTRUCTOR, FIELD, PARAMETER 等isDocumented
是否会保存到 Javadoc 文档中isInherited
是否可以被继承,默认为 false
更多方法见API文档:
https://apidoc.gitee.com/loolly/hutool/cn/hutool/core/annotation/AnnotationUtil.html
比较器
概述
介绍
各种比较器(Comparator)实现和封装
提供的比较器
其它比较器
ReverseComparator
反转比较器,排序时提供反序VersionComparator
版本比较器,支持如:1.3.20.8,6.82.20160101,8.5a/8.5c等版本形式PropertyComparator
Bean属性比较器,通过Bean的某个属性来对Bean对象进行排序IndexedComparator
按照数组的顺序正序排列,数组的元素位置决定了对象的排序先后ComparatorChain
比较器链。此链包装了多个比较器,最终比较结果按照比较器顺序综合多个比较器结果。PinyinComparator
按照GBK拼音顺序对给定的汉字字符串排序。
比较工具-CompareUtil
介绍
在JDK提供的比较器中,对于null
的比较没有考虑,Hutool封装了相关比较,可选null是按照最大值还是最小值对待。
// 当isNullGreater为true时,null始终最大,此处返回的compare > 0
int compare = CompareUtil.compare(null, "a", true);
// 当isNullGreater为false时,null始终最小,此处返回的compare < 0
int compare = CompareUtil.compare(null, "a", false);
版本比较器-VersionComparator
介绍
版本比较器用于比较版本号,支持的格式包括:
x.x.x(1.3.20)
x.x.yyyyMMdd(6.82.20160101)
带字母的版本(8.5a/8.5c)
带V的版本(V8.5)
使用
// -1
int compare = VersionComparator.INSTANCE.compare("1.12.1", "1.12.1c");
// 1
int compare = VersionComparator.INSTANCE.compare("V0.0.20170102", "V0.0.20170101");
异常
异常工具-ExceptionUtil
介绍
针对异常封装,例如包装为RuntimeException
。
方法
包装异常
假设系统抛出一个非Runtime异常,我们需要包装为Runtime异常,那么:
IORuntimeException e = ExceptionUtil.wrap(new IOException(), IORuntimeException.class);
获取入口方法
StackTraceElement ele = ExceptionUtil.getRootStackElement();
// main
ele.getMethodName();
异常转换
如果我们想把异常转换指定异常为来自或者包含指定异常,那么:
IOException ioException = new IOException();
IllegalArgumentException argumentException = new IllegalArgumentException(ioException);
IOException ioException1 = ExceptionUtil.convertFromOrSuppressedThrowable(argumentException, IOException.class, true);
其他方法
getMessage
获得完整消息,包括异常名wrapRuntime
使用运行时异常包装编译异常getCausedBy
获取由指定异常类引起的异常isCausedBy
判断是否由指定异常类引起stacktraceToString
堆栈转为完整字符串
其它方法见API文档:
https://apidoc.gitee.com/dromara/hutool/cn/hutool/core/exceptions/ExceptionUtil.html
其它异常封装
介绍
针对Hutool中常见异常封装
异常类
DependencyException
依赖异常StatefulException
带有状态码的异常UtilException
工具类异常NotInitedException
未初始化异常ValidateException
验证异常
数学
数学相关-MathUtil
介绍
此工具是NumberUtil的一个补充,NumberUtil偏向于简单数学计算的封装,MathUtil偏向复杂数学计算。
方法
排列
arrangementCount
计算排列数arrangementSelect
排列选择(从列表中选择n个排列)
组合
combinationCount
计算组合数,即C(n, m) = n!/((n-m)! * m!)combinationSelect
组合选择(从列表中选择n个组合)
线程和并发
线程工具-ThreadUtil
由来
并发在Java中算是一个比较难理解和容易出问题的部分,而并发的核心在线程。好在从JDK1.5开始Java提供了concurrent
包可以很好的帮我们处理大部分并发、异步等问题。
不过,ExecutorService
和Executors
等众多概念依旧让我们使用这个包变得比较麻烦,如何才能隐藏这些概念?又如何用一个方法解决问题?ThreadUtil
便为此而生。
原理
Hutool使用GlobalThreadPool
持有一个全局的线程池,默认所有异步方法在这个线程池中执行。
方法
ThreadUtil.execute
直接在公共线程池中执行线程
ThreadUtil.newExecutor
获得一个新的线程池
ThreadUtil.execAsync
执行异步方法
ThreadUtil.newCompletionService
创建CompletionService,调用其submit方法可以异步执行多个任务,最后调用take方法按照完成的顺序获得其结果。若未完成,则会阻塞。
ThreadUtil.newCountDownLatch
新建一个CountDownLatch,一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
ThreadUtil.sleep
挂起当前线程,是Thread.sleep
的封装,通过返回boolean值表示是否被打断,而不是抛出异常。
ThreadUtil.safeSleep
方法是一个保证挂起足够时间的方法,当给定一个挂起时间,使用此方法可以保证挂起的时间大于或等于给定时间,解决Thread.sleep
挂起时间不足问题,此方法在Hutool-cron的定时器中使用保证定时任务执行的准确性。
ThreadUtil.getStackTrace
此部分包括两个方法:
getStackTrace
获得堆栈列表getStackTraceElement
获得堆栈项
其它
createThreadLocal
创建本地线程对象interupt
结束线程,调用此方法后,线程将抛出InterruptedException异常waitForDie
等待线程结束. 调用Thread.join()
并忽略 InterruptedExceptiongetThreads
获取JVM中与当前线程同组的所有线程getMainThread
获取进程的主线程
异步工具类-AsyncUtil
由来
在JDK8
中,提供了CompletableFuture
进行异步执行
使用场景如异步调用微服务、异步查询数据库、异步运算大量数据等
方法
AsyncUtil.waitAll
等待所有任务执行完毕
AsyncUtil.waitAny
等待任意一个任务执行完毕
AsyncUtil.get
获取异步任务结果
自定义线程池-ExecutorBuilder
由来
在JDK中,提供了Executors
用于创建自定义的线程池对象ExecutorService
,但是考虑到线程池中存在众多概念,这些概念通过不同的搭配实现灵活的线程管理策略,单独使用Executors
无法满足需求,所以构建了ExecutorBuilder
。
概念
corePoolSize
初始池大小maxPoolSize
最大池大小(允许同时执行的最大线程数)workQueue
队列,用于存储未执行的任务handler
当线程阻塞(block)时的异常处理器,所谓线程阻塞即线程池和等待队列已满,无法处理线程时采取的策略
线程池对待线程的策略
如果池中任务数 < corePoolSize -> 放入立即执行
如果池中任务数 > corePoolSize -> 放入队列等待
队列满 -> 新建线程立即执行
执行中的线程 > maxPoolSize -> 触发handler(RejectedExecutionHandler)异常
workQueue线程池策略
SynchronousQueue
它将任务直接提交给线程而不保持它们。当运行线程小于maxPoolSize
时会创建新线程,否则触发异常策略LinkedBlockingQueue
默认无界队列,当运行线程大于corePoolSize
时始终放入此队列,此时maxPoolSize
无效。当构造LinkedBlockingQueue对象时传入参数,变为有界队列,队列满时,运行线程小于maxPoolSize
时会创建新线程,否则触发异常策略ArrayBlockingQueue
有界队列,相对无界队列有利于控制队列大小,队列满时,运行线程小于maxPoolSize
时会创建新线程,否则触发异常策略
使用
默认线程池
策略如下:
初始线程数为corePoolSize指定的大小
没有最大线程数限制
默认使用LinkedBlockingQueue,默认队列大小为1024(最大等待数1024)
当运行线程大于corePoolSize放入队列,队列满后抛出异常
ExecutorService executor = ExecutorBuilder builder = ExecutorBuilder.create()..build();
单线程线程池
初始线程数为 1
最大线程数为 1
默认使用LinkedBlockingQueue,默认队列大小为1024
同时只允许一个线程工作,剩余放入队列等待,等待数超过1024报错
ExecutorService executor = ExecutorBuilder.create()//
.setCorePoolSize(1)//
.setMaxPoolSize(1)//
.setKeepAliveTime(0)//
.build();
更多选项的线程池
初始5个线程
最大10个线程
有界等待队列,最大等待数是100
ExecutorService executor = ExecutorBuilder.create()
.setCorePoolSize(5)
.setMaxPoolSize(10)
.setWorkQueue(new LinkedBlockingQueue<>(100))
.build();
特殊策略的线程池
初始5个线程
最大10个线程
它将任务直接提交给线程而不保持它们。当运行线程小于maxPoolSize时会创建新线程,否则触发异常策略
ExecutorService executor = ExecutorBuilder.create()
.setCorePoolSize(5)
.setMaxPoolSize(10)
.useSynchronousQueue()
.build();
高并发测试-ConcurrencyTester
由来
很多时候,我们需要简单模拟N个线程调用某个业务测试其并发状况,于是Hutool提供了一个简单的并发测试类——ConcurrencyTester。
使用
ConcurrencyTester tester = ThreadUtil.concurrencyTest(100, () -> {
// 测试的逻辑内容
long delay = RandomUtil.randomLong(100, 1000);
ThreadUtil.sleep(delay);
Console.log("{} test finished, delay: {}", Thread.currentThread().getName(), delay);
});
// 获取总的执行时间,单位毫秒
Console.log(tester.getInterval());
图片
图片工具-ImgUtil
介绍
针对awt中图片处理进行封装,这些封装包括:缩放、裁剪、转为黑白、加水印等操作。
方法介绍
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
按照行列剪裁切片(将图片分为10行和10列)
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"));
图片编辑器-Img
介绍
针对awt中图片处理进行封装,这些封装包括:缩放、裁剪、转为黑白、加水印等操作。
方法介绍
图像切割
// 将face.jpg切割为原型保存为face_radis.png
Img.from(FileUtil.file("e:/pic/face.jpg"))
.cut(0, 0, 200)//
.write(FileUtil.file("e:/pic/face_radis.png"));
图片压缩
图片压缩只支持Jpg文件。
Img.from(FileUtil.file("e:/pic/1111.png"))
.setQuality(0.8)//压缩比率
.write(FileUtil.file("e:/pic/1111_target.jpg"));
网络
网络工具-NetUtil
由来
在日常开发中,网络连接这块儿必不可少。日常用到的一些功能,隐藏掉部分IP地址、绝对和相对路径的转换等等。
介绍
NetUtil
工具中主要的方法包括:
longToIpv4
根据long值获取ipv4地址ipv4ToLong
根据ip地址计算出long型的数据isUsableLocalPort
检测本地端口可用性isValidPort
是否为有效的端口isInnerIP
判定是否为内网IPlocalIpv4s
获得本机的IP地址列表toAbsoluteUrl
相对URL转换为绝对URLhideIpPart
隐藏掉IP地址的最后一部分为 * 代替buildInetSocketAddress
构建InetSocketAddressgetIpByHost
通过域名得到IPisInner
指定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);
更多方法请见:
URL生成器-UrlBuilder
由来
在JDK中,我们可以借助URL
对象完成URL的格式化,但是无法完成一些特殊URL的解析和处理,例如编码过的URL、不标准的路径和参数。在旧版本的hutool中,URL的规范完全靠字符串的替换来完成,不但效率低,而且处理过程及其复杂。于是在5.3.1之后,加入了UrlBuilder类,拆分URL的各个部分,分别处理和格式化,完成URL的规范化。
按照Uniform Resource Identifier的标准定义,URL的结构如下:
[scheme:]scheme-specific-part[#fragment]
[scheme:][//authority][path][?query][#fragment]
[scheme:][//host:port][path][?query][#fragment]
按照这个格式,UrlBuilder将URL分成scheme、host、port、path、query、fragment部分,其中path和query较为复杂,又使用UrlPath
和UrlQuery
分别封装。
使用
相比URL
对象,UrlBuilder更加人性化,例如:
URL url = new URL("www.hutool.cn")
此时会报java.net.MalformedURLException: no protocol
的错误,而使用UrlBuilder则会有默认协议:
// 输出 http://www.hutool.cn/
String buildUrl = UrlBuilder.create().setHost("www.hutool.cn").build();
完整构建
// https://www.hutool.cn/aaa/bbb?ie=UTF-8&wd=test
String buildUrl = UrlBuilder.create()
.setScheme("https")
.setHost("www.hutool.cn")
.addPath("/aaa").addPath("bbb")
.addQuery("ie", "UTF-8")
.addQuery("wd", "test")
.build();
中文编码
当参数中有中文时,自动编码中文,默认UTF-8编码,也可以调用setCharset
方法自定义编码。
// https://www.hutool.cn/s?ie=UTF-8&ie=GBK&wd=%E6%B5%8B%E8%AF%95
String buildUrl = UrlBuilder.create()
.setScheme("https")
.setHost("www.hutool.cn")
.addPath("/s")
.addQuery("ie", "UTF-8")
.addQuery("ie", "GBK")
.addQuery("wd", "测试")
.build();
解析
当有一个URL字符串时,可以使用of
方法解析:
UrlBuilder builder = UrlBuilder.ofHttp("www.hutool.cn/aaa/bbb/?a=张三&b=%e6%9d%8e%e5%9b%9b#frag1", CharsetUtil.CHARSET_UTF_8);
// 输出张三
Console.log(builder.getQuery().get("a"));
// 输出李四
Console.log(builder.getQuery().get("b"));
我们发现这个例子中,原URL中的参数a是没有编码的,b是编码过的,当用户提供此类混合URL时,Hutool可以很好的识别并全部decode,当然,调用build()之后,会全部再encode。
特殊URL解析
有时候URL中会存在&
这种分隔符,谷歌浏览器会将此字符串转换为&
使用,Hutool中也同样如此:
String urlStr = "https://mp.weixin.qq.com/s?__biz=MzI5NjkyNTIxMg==&mid=100000465&idx=1";
UrlBuilder builder = UrlBuilder.ofHttp(urlStr, CharsetUtil.CHARSET_UTF_8);
// https://mp.weixin.qq.com/s?__biz=MzI5NjkyNTIxMg==&mid=100000465&idx=1
Console.log(builder.build());
UrlBuilder主要应用于http模块,在构建HttpRequest时,用户传入的URL五花八门,为了做到最好的适应性,减少用户对URL的处理,使用UrlBuilder完成URL的规范化。
源码编译
源码编译工具-CompilerUtil
介绍
JDK提供了JavaCompiler
用于动态编译java源码文件,然后通过类加载器加载,这种动态编译可以让Java有动态脚本的特性,Hutool针对此封装了对应工具。
使用
首先我们将编译需要依赖的class文件和jar文件打成一个包:
// 依赖A,编译B和C
final File libFile = ZipUtil.zip(FileUtil.file("lib.jar"),
new String[]{"a/A.class", "a/A$1.class", "a/A$InnerClass.class"},
new InputStream[]{
FileUtil.getInputStream("test-compile/a/A.class"),
FileUtil.getInputStream("test-compile/a/A$1.class"),
FileUtil.getInputStream("test-compile/a/A$InnerClass.class")
});
开始编译:
final ClassLoader classLoader = CompilerUtil.getCompiler(null)
// 被编译的源码文件
.addSource(FileUtil.file("test-compile/b/B.java"))
// 被编译的源码字符串
.addSource("c.C", FileUtil.readUtf8String("test-compile/c/C.java"))
// 编译依赖的库
.addLibrary(libFile)
.compile();
加载编译好的类:
final Class<?> clazz = classLoader.loadClass("c.C");
// 实例化对象c
Object obj = ReflectUtil.newInstance(clazz);