Apache Commons是一个Apache项目,专注于可重用Java组件的所有方面。
Apache Commons项目由三部分组成:
Commons Proper - 可重用Java组件的存储库。
Commons Sandbox - 用于Java组件开发的工作区。
Commons Dormant - 当前不活动的组件的存储库。
Commons Proper致力于一个主要目标:创建和维护可重用的Java组件。Commons Proper是一个协作和共享的场所,Apache Apache社区的开发人员可以共同合作,共同分享Apache项目和Apache用户的项目。
Commons开发人员将努力确保其组件对其他库的依赖性最小,从而可以轻松部署这些组件。此外,Commons组件将尽可能保持其接口的稳定性,因此Apache用户(包括其他Apache项目)可以实现这些组件,而无需担心将来的更改。
Commons 组件下载地址
Commons 组件比较多下面只是简单介绍了部分组件
Java语言中的数组并不好用:在创建时需要指定数组的长度,并且一旦创建完成则长度不能再发生变化。为了弥补这个不足,Java SE API中提供了ArrayList类。对于数组的超级粉丝,向您推荐使用Commons Lang组件。其中的ArrayUtils类对数组操作进行了增强,实现了向数组中增加元素的方法。本实例将演示如何使用这些方法.
Commons Lang下载地址
ArrayUtils类提供了对基本类型(例如int)、包装类型(例如Integer)和其他引用类型数组的支持。该类尝试优雅地处理null值。如果数组为null,并不会抛出异常。如果数组中某个元素为null,才会抛出异常。本实例使用的方法如表1所示:
编写ArrayUtilsTest类,在该类的main()方法中,实现了向数组的不同位置增加元素和合并数组的操作,代码如下:
public class ArrayUtilsTest {
public static void main(String[] args) {
int[] array0 = new int[5]; //创建长度为5的int类型数组
Arrays.fill(array0, 8); //将数组中的元素全部初始化为8
System.out.println("数组中的元素是:");
System.out.println(Arrays.toString(array0)); //输出数组中的全部元素
System.out.println("在数组的最后增加元素10");
int[] array1 = ArrayUtils.add(array0, 10); //在数组的最后增加元素10
System.out.println("数组中的元素是:");
System.out.println(Arrays.toString(array1)); //输出新数组中的全部元素
System.out.println("在数组的开头增加元素10");
int[] array2 = ArrayUtils.add(array0, 0, 10); //在数组的开头增加元素10
System.out.println("数组中的元素是:");
System.out.println(Arrays.toString(array2)); //输出新数组中的全部元素
System.out.println("将新生成的两个数组合并");
int[] array3 = ArrayUtils.addAll(array1, array2);//合并新生成的两个数组
System.out.println("数组中的元素是:");
System.out.println(Arrays.toString(array3)); //输出新数组中的全部元素
}
}
}
ArrayUtils类并没有改变Java的基本语法,即数组长度增加的原因是使用反射机制创建了一个新的数组。推荐读者查看该类的源代码深入学习。其实完全可以使用ArrayList类来代替数组,该类的使用也是非常方便的。
public class ArrayUtilsTest {
public static void main(String[] args) {
int[] array0 = { 1, 2, 3, 2, 1 };//在定义数组时实现初始化
System.out.println("数组中的元素是:");
System.out.println(Arrays.toString(array0));//输出数组中的全部元素
System.out.println("删除最后一个元素");
int[] array1 = ArrayUtils.remove(array0, 4);//删除索引为4的元素
System.out.println("数组中的元素是:");
System.out.println(Arrays.toString(array1));//输出新数组中的全部元素
System.out.println("删除元素2");
int[] array2 = ArrayUtils.removeElement(array0, 2);//删除元素2
System.out.println("数组中的元素是:");
System.out.println(Arrays.toString(array2));//输出新数组中的全部元素
}
}
在用户注册和登录等操作时,为了防止用户使用软件进行恶意操作(例如批量注册用户),通常会要求用户输出一些随机的字符串。Commons Lang组件的RandomStringUtils类提供了生成随机字符串的方法。
RandomStringUtils类提供了多种生成随机字符串的方法。本实例使用的方法如表1所示:
该类还有一个random()方法,该方法使用的字符取自全部字符集,会出现乱码的情况,因此不要使用。
编写RandomStringUtilsTest类,在该类的main()方法中,输出了4类随机字符串,代码如下:
public class RandomStringUtilsTest {
public static void main(String[] args) {
System.out.println("生成长度为5的由字母组成的字符串");
String randomString = RandomStringUtils.randomAlphabetic(5);//获得随机字符串
System.out.println(randomString);
System.out.println("生成长度为5的由字母和数字组成的字符串");
randomString = RandomStringUtils.randomAlphanumeric(5); //获得随机字符串
System.out.println(randomString);
System.out.println("生成长度为5的由ASCII编码在32~126间字符组成的字符串");
randomString = RandomStringUtils.randomAscii(5); //获得随机字符串
System.out.println(randomString);
System.out.println("生成长度为5的由数字组成的字符串");
randomString = RandomStringUtils.randomNumeric(5); //获得随机字符串
System.out.println(randomString);
}
}
有些“智能化”的软件能识别文本形式的字符串,因此程序中很少直接使用文本字符串。通常将字符串转换成图片形式,还可以在图片中增加一些不规则的直线,防止恶意软件的识别。
当需要保存对象的状态时,可以考虑使用序列化。Java SE API中提供了对序列化的支持,但是使用起来十分不方便。Commons Lang组件的SerializationUtils类提供了简化的序列化与反序列化的方法。
进行序列化的类要实现IO包中的Serializable接口。该接口是一个标识接口,它没有定义任何方法。
利用FileInputStream可以将对象的序列化结果写入文件,利用FileOutputStream可以从文件中读取。
)编写Student类,在该类中定义了两个域:id表示学生序号,name表示学生姓名。为这两个域提供了get和set方法,并且重写了toString()方法。代码如下:
public class Student implements Serializable {
private static final long serialVersionUID = -8396517822004869094L;
private int id;//表示学生的序号
private String name;//表示学生的姓名
public int getId() {//获得学生的序号
return id;
}
public void setId(int id) {//设置学生的序号
this.id = id;
}
public String getName() {//获得学生的姓名
return name;
}
public void setName(String name) {//设置学生的姓名
this.name = name;
}
@Override
public String toString() {
return "学生id:" + id + ",学生姓名:" + name;
}
}
(2)编写SerializationUtilsTest类,在该类的main()方法中,首先创建了student对象,然后将其序列化。再反序列化,然后输出该对象,代码如下:
public class SerializationUtilsTest {
public static void main(String[] args) {
Student student = new Student();//创建student对象
student.setId(10);//初始化id属性
student.setName("江湖好深");// 初始化name属性
System.out.println("将student对象序列化成byte数组");
byte[] studentByte = SerializationUtils.serialize(student);//将对象转换成byte数组
System.out.println("输出序列化数组:");
System.out.println(Arrays.toString(studentByte));//输出byte数组
System.out.println("将student对象序列化到本地文件");
FileOutputStream out = null;// 创建文件输出流对象
try {
out = new FileOutputStream(new File("d:\\student.txt").getAbsoluteFile());
} catch (FileNotFoundException e) {
e.printStackTrace();
}
SerializationUtils.serialize(student, out);//将对象写入到student.txt文件
System.out.println("文件生成成功!");
System.out.println("从本地文件反序列化student对象");
FileInputStream in = null;// 创建文件输入流对象
try {
in = new FileInputStream(new File("d:\\student.txt").getAbsoluteFile());
} catch (FileNotFoundException e) {
e.printStackTrace();
}
Student newStudent = (Student) SerializationUtils.deserialize(in);//读入对象
System.out.println("查看student对象的属性");
System.out.println(newStudent);
}
}
在克隆对象时,如果存在多个引用类型的域,则深度克隆时还要考虑这些域的域。因此利用重写clone方法来实现深度克隆十分麻烦。序列化提供了克隆对象的简便途径,只要对应的类是可序列化的即可。而这种方法的缺点是序列化的速度明显慢于clone()方法,读者需要在复杂性与效率之间做个合适的选择。
Java API中并没有对分数运算提供良好的支持,通常是使用float和double类型处理分数,这样很容易引起误差。Commons Lang组件的Fraction类提供了分数运算的方法。
Fraction类继承了Number类,提供精确存储分数的功能。
Fraction类还包括一些静态域表示一些常见的分数,例如TWO_THIRDS表示2/3。
编写FractionTest类,在该类的main()方法中演示了关于分数的基本运算,代码如下:
public class FractionTest {
public static void main(String[] args) {
Fraction fraction1 = Fraction.getFraction(1, 3);// 创建小数1/3
Fraction fraction2 = Fraction.getFraction(1, 5);// 创建小数1/5
Fraction addition = fraction1.add(fraction2);// 计算1/3 + 1/5
System.out.println("1/3 + 1/5 = " + addition);
Fraction subtraction = fraction1.subtract(fraction2);// 计算1/3 - 1/5
System.out.println("1/3 - 1/5 = " + subtraction);
Fraction multiplication = fraction1.multiplyBy(fraction2);// 计算1/3 * 1/5
System.out.println("1/3 * 1/5 = " + multiplication);
Fraction division = fraction1.divideBy(fraction2);// 计算1/3 / 1/5
System.out.println("1/3 / 1/5 = " + division);
Fraction invert = fraction1.invert();// 计算1/3的倒数
System.out.println("1/3的倒数是:" + invert);
Fraction pow = fraction1.pow(2); // 计算1/3的平方
System.out.println("1/3的平方是:" + pow);
}
}
很多数学问题都涉及到数值的取值范围,例如求定积分等。如果使用普通方法要判断两次,数的上限和下限。Commons Lang组件的IntRange类提供了更加合理的方法。
IntRange表示一个包含端点的整数区间。
编写IntRangeTest类,在该类的main()方法中,输出了区间的上下限等信息,代码如下:
public class IntRangeTest {
public static void main(String[] args) {
IntRange range = new IntRange(-5, 5); //创建一个从-5到5的区间
System.out.println("区间中的全部整数是:");
System.out.println(Arrays.toString(range.toArray()));//输出区间中的全部整数
System.out.print("0是否在区间中:");
System.out.println(range.containsInteger(0)); //判断0是否在区间中
System.out.print("区间的上限是:");
System.out.println(range.getMaximumInteger()); //输出区间的上限
System.out.print("区间的下限是:");
System.out.println(range.getMinimumInteger()); //输出区间的下限
System.out.print("区间的字符串表示是:");
System.out.println(range.toString()); //输出区间的数学表示形式
}
}
区间是数学中的基本概念。IntRange类提供了更有意义的区间表示和使用方式。在编程中可以用来确定点的位置,求解方程组等。
Commons Math下载地址
在得到一组简单的统计学变量时,可以使用描述统计学对其进行初步分析,为下一步分析提供理论依据。Commons Math组件的DescriptiveStatistics类提供了相关方法。
DescriptiveStatistics类管理单变量的数据集并基于该数据集进行统计计算。windowSize属性用来设置数据集的大小,默认情况下是不限制数据集大小的。由于该类会保存所有的数据,所以对于数据量比较大的统计运算,推荐使用SummaryStatistics类。本实例使用的方法如表1所示:
编写DescriptiveStatisticsTest类,在该类的main()方法中,输出了简单的数理统计信息,代码如下:
public class DescriptiveStatisticsTest {
public static void main(String[] args) {
DescriptiveStatistics ds = new DescriptiveStatistics(10);
for (int i = 0; i < 10; i++) {
ds.addValue(new Random().nextInt(10));// 向数据集中添加10个小于10的随机变量
}
System.out.println("数据集中的全部元素:");
System.out.println(Arrays.toString(ds.getValues()));
System.out.println("数据集的算数平均数是:" + ds.getMean());
System.out.println("数据集的几何平均数是:" + ds.getGeometricMean());
System.out.println("数据集的方差是:" + ds.getVariance());
System.out.println("数据集的标准方差是:" + ds.getStandardDeviation());
System.out.println("数据集的和是:" + ds.getSum());
System.out.println("数据集的平方和是:" + ds.getSumsq());
System.out.println("数据集的最大值是:" + ds.getMax());
System.out.println("数据集的最小值是:" + ds.getMin());
System.out.println("数据集的中位数是:" + ds.getPercentile(50));
System.out.println("数据集的偏度是:" + ds.getSkewness());
System.out.println("数据集的峰度是:" + ds.getKurtosis());
}
}
DescriptiveStatistics类的使用
描述统计学可以对数据进行初步的处理,其中涉及大量的统计学量,如果读者自己编程还是有些麻烦的。使用DescriptiveStatistics类能直接获得这些统计学量,能大幅度简化开发,这样就可以集中精力解决核心问题了。
描述统计学获得的数据对一般人来说太抽象了。通过绘制直方图可以对数据分布有个直观的了解。Commons Math组件的Frequency类提供了相关方法。
Frequency支持多种类型,本实例选择int进行讲解,对于其他类型的使用是类似的。
编写FrequencyTest类,在该类的main()方法中,制作了简单的直方图,代码如下:
public class FrequencyTest {
public static void main(String[] args) {
Frequency frequency = new Frequency();
for (int i = 0; i < 100; i++) {
frequency.addValue(new Random().nextInt(10));// 增加100个小于10的随机数
}
System.out.println("频度分布直方图");
for (int i = 0; i < 10; i++) {// 对于0~9每个数值绘制直方图
System.out.print("数值" + i + "的频度:");
for (int j = 0; j < frequency.getCount(i); j++) {// 输入不同个星号来表示不同的频度
System.out.print("*");
}
System.out.println("\t" + frequency.getCumFreq(i));// 输出累计频度
}
}
}
Frequency类的使用
除了绘制简单的直方图,还可以使用该类绘制更为复杂、更为常用的直方图。结合GUI的知识,可以绘制出类似Excel中的直方图。
对于给定的平面上的点集,如果其分布形状类似于直线,则可以进行一元线性回归分析。Commons Math组件的SimpleRegression类提供了相关方法。
SimpleRegression类使用最小二乘法求解y = intercept + slope * x。intercept和slope的标准差可以表示为ANOVA、r-square和Pearson’s r统计量。本实例使用的方法如表1所示:
编写SimpleRegressionTest类,在该类的main()方法中,实现了简单一元线性回归分析,代码如下:
public class SimpleRegressionTest {
public static void main(String[] args) {
double[][] data = { { 54, 61 }, { 66, 80 }, { 68, 62 }, { 76, 86 }, { 78, 84 }, { 82, 76 }, { 85, 85 }, { 87, 82 }, { 90, 88 }, { 94, 82 }, { 90, 88 }, { 94, 96 } };
SimpleRegression regression = new SimpleRegression();
regression.addData(data);// 增加要分析的数据
System.out.println("斜率是:" + regression.getSlope());
System.out.println("斜率标准差是:" + regression.getSlopeStdErr());
System.out.println("截距是:" + regression.getIntercept());
System.out.println("截距标准差是:" + regression.getInterceptStdErr());
System.out.println("误差平方和是:" + regression.getSumSquaredErrors());
}
}
一元线性回归是统计学中常用的分析方法,目前已经利用最小二乘法计算出最优解。SimpleRegression类就是实现了该方法来简化程序员编程的。
矩阵是线性代数的基础,其常用运算包括矩阵的加法、减法、乘法、转置等。Commons Math组件的RealMatrix接口定义了这些方法。
RealMatrix接口定义了矩阵的常用方法。本实例使用的方法如表1所示:
编写RealMatrixTest类,在该类的main()方法中,演示了如何进行矩阵的四则运算。代码如下:
public class RealMatrixTest {
public static void main(String[] args) {
double[][] matrixData1 = { { 1, 2 }, { 3, 4 } };
RealMatrix m = new Array2DRowRealMatrix(matrixData1);// 利用二维数组初始化矩阵
System.out.println("矩阵m中的元素:");
System.out.println(Arrays.deepToString(m.getData()));// 利用工具类输出矩阵中元素
double[][] matrixData2 = { { 1, 2 }, { 3, 4 } };
RealMatrix n = new Array2DRowRealMatrix(matrixData2);// 利用二维数组初始化矩阵
System.out.println("矩阵n中的元素:");
System.out.println(Arrays.deepToString(n.getData()));// 利用工具类输出矩阵中元素
RealMatrix addition = m.add(n);// 进行矩阵加法运算
System.out.println("矩阵addition中的元素:");
System.out.println(Arrays.deepToString(addition.getData()));
RealMatrix subtraction = m.subtract(n);// 进行矩阵减法运算
System.out.println("矩阵subtraction中的元素:");
System.out.println(Arrays.deepToString(subtraction.getData()));
RealMatrix multiplication = m.multiply(n);// 进行矩阵乘法运算
System.out.println("矩阵multiplication中的元素:");
System.out.println(Arrays.deepToString(multiplication.getData()));
RealMatrix transposition = m.multiply(n);// 进行矩阵转置运算
System.out.println("矩阵m转置后新矩阵中的元素:");
System.out.println(Arrays.deepToString(transposition.getData()));
}
}
由于矩阵运算的特殊性,编程实现很复杂的,而且也是不必要的。使用RealMatrix接口的实现类,就可以帮助实现矩阵的常见运算,大大简化了编程。
由于负数不能开平方等原因,对实数集进行了扩展,引入了虚数的概念,两者并称为复数。Commons Math组件的Complex类定义了复数运算的方法。
Complex类定义了复数运算的常用方法。本实例使用的方法如表1所示:
编写ComplexTest类,在该类的main()方法中,演示了复数的四则运算。代码如下:
public class ComplexTest {
public static void main(String[] args) {
Complex complex1 = new Complex(1.0, 3.0); // 复数的初始化
System.out.println("复数complex1是:" + getComplex(complex1));
Complex complex2 = new Complex(2.0, 5.0); // 复数的初始化
System.out.println("复数complex2是:" + getComplex(complex2));
Complex addition = complex1.add(complex2); // 复数的加法运算
System.out.println("加法运算的结果是:" + getComplex(addition));
Complex subtraction = complex1.subtract(complex2); // 复数的减法运算
System.out.println("减法运算的结果是:" + getComplex(subtraction));
Complex multiplication = complex1.multiply(complex2); // 复数的乘法运算
System.out.println("乘法运算的结果是:" + getComplex(multiplication));
Complex division = complex1.divide(complex2); // 复数的除法运算
System.out.println("除法运算的结果是:" + getComplex(division));
}
public static String getComplex(Complex complex) {// 自定义输出复数的方法
return complex.getReal() + "+" + complex.getImaginary() + "i";
}
}
Complex类不仅提供了基本的四则运算还有三角运算、反三角运算、指数运算、共轭运算等。只要读者了解相关的数学背景,这些方法是很容易使用的。
正态分布是统计学中重要的分布,但是由于数据的方差不易计算,实际应用中更倾向使用T分布。Commons Math组件的TDistributionImpl类定义了T分布常用计算方法。
TDistributionImpl类定义了T分布运算的常用方法。本实例使用的方法如表1所示:
编写TDistributionImplTest类,在该类的main()方法中输出了几个常见的T分布值。代码如下:
public class TDistributionImplTest {
public static void main(String[] args) throws MathException {
TDistributionImpl t = new TDistributionImpl(5);// 新建一个自由度为5的T分布
System.out.println("当前T分布的自由度:" + t.getDegreesOfFreedom());
double upperTail = t.cumulativeProbability(0.7267);
System.out.println("计算域大于0.7267的置信度:" + upperTail);
System.out.println("计算0点的概率密度:" + t.density(0));
double domain = t.inverseCumulativeProbability(0.75);
System.out.println("计算置信度大于0.75的域值:" + domain);
}
}
虽然应用中T分布非常常用,但是其计算异常复杂,通常需要专业的数学软件来完成。TDistributionImpl实现了T分布的常用计算,使用起来也很方便,简化了代码的编写。
Commons IO 下载地址
Java的中文件和文件夹统一使用File类管理,该类提供了delete()方法用来删除文件和空文件夹。Commons IO组件的FileDeleteStrategy类定义了删除非空文件夹的方法。
FileDeleteStrategy类定义了删除文件(夹)的常用方法。本实例使用的方法如表1所示:
FileDeleteStrategy没有提供构造方法用来实例化,它仅提供了两个域FORCE与NORMAL。
编写FileDeleteStrategyTest类,在该类的main()方法中,演示了删除文件(夹)的常用方式,代码如下:
public class FileDeleteStrategyTest {
public static void main(String[] args) {
File rootFile = new File("d:\\IT水深\\知识如浩瀚宇宙");// 创建要删除的文件夹对象
System.out.println("获得所有文件的绝对路径:");
File[] list = rootFile.listFiles();
for (File file : list) {
System.out.println(file.getAbsolutePath());// 输出文件夹中的所有文件的绝对路径
}
FileDeleteStrategy strategy = FileDeleteStrategy.NORMAL;// 使用普通删除策略
System.out.println("以普通策略删除非空文件夹d:\\IT水深:");
try {
strategy.delete(new File("d:\\IT水深"));
System.out.println("文件夹删除成功!");// 如果删除成功则提示删除成功
} catch (IOException e) {
System.out.println("文件夹删除失败!");// 如果删除失败则提示删除失败
}
strategy = FileDeleteStrategy.FORCE;// 使用强制删除策略
System.out.println("以强制策略删除非空文件夹d:\\IT水深:");
try {
strategy.delete(new File("d:\\IT水深"));
System.out.println("文件夹删除成功!");// 如果删除成功则提示删除成功
} catch (IOException e) {
System.out.println("文件夹删除失败!");// 如果删除失败则提示删除失败
}
}
}
对于非空文件夹,通常删除起来十分不方便,先要删除其中所有的文件,再依次删除空文件夹。如果使用FileDeleteStrategy类就很简单了,一个方法调用就解决问题了。
Java的中文件和文件夹统一使用File类管理,该类并没有提供文件(夹)复制的相关方法。Commons IO组件的FileUtils类定义了复制文件(夹)的方法。
FileUtils类定义了复制文件(夹)的常用方法。本实例使用的方法如表1所示:
编写FileUtilsTest类,在该类的main()方法中,演示了复制文件(夹)的常用方式,代码如下:
public class FileUtilsTest {
public static void main(String[] args) throws IOException {
File srcDir = new File("D:\\IT水深");
File destDir = new File("E:\\IT水深");
List list = new ArrayList();
System.out.println("复制之前文件夹中的文件:");
getFilePath(list, destDir);
for (String string : list) {
System.out.println(string);// 输出复制前文件夹中所有文件
}
System.out.println();
System.out.println("复制之后文件夹中的文件:");
FileUtils.copyDirectory(srcDir, destDir);
getFilePath(list, destDir);
for (String string : list) {
System.out.println(string); // 输出复制后文件夹中所有文件
}
}
// 获得rootFile文件夹中所有文件的绝对路径并将其保存在list中
private static List getFilePath(List list, File rootFile) {
File[] files = rootFile.listFiles();
for (File file : files) {
if (file.isDirectory()) {
getFilePath(list, file);
} else {
list.add(file.getAbsolutePath().replace("\\", File.separator));
}
}
return list;
}
}
复制文件通常的思路是先创建文件,在利用流将数据写入到新创建的文件。对于文件夹的复制更加复杂。如果使用FileUtils类就很简单了,一个方法调用就解决问题了。
文件的排序通常涉及到以下几个属性的比较:名称、大小、项目类型和修改时间。Commons IO组件的comparator包对每个属性定义了一个类,用来简化文件的排序。
SizeFileComparator类定义了一些字段代表不同的排序方式。详细说明如表1所示:
编写SizeFileComparatorTest类,在该类的main()方法中,演示了排序文件(夹)的常用方式,代码如下:
public class SizeFileComparatorTest {
@SuppressWarnings("unchecked")
public static void main(String[] args) throws IOException {
File rootFile = new File("D:\\IT水深");// 创建一个文件夹对象
File[] files = rootFile.listFiles();// 获得该文件夹中所有文件(夹)
System.out.println("文件(夹)的原始排序:");
for (File file : files) {
System.out.print(file.getName() + "\t");// 输出文件夹中文件(夹)的名称
}
System.out.println();
Arrays.sort(files, SizeFileComparator.SIZE_COMPARATOR);// 对files数组进行排序
System.out.println("文件(夹)的SIZE_COMPARATOR排序:");
for (File file : files) {
System.out.print(file.getName() + "\t");// 输出文件夹中文件(夹)的名称
}
System.out.println();
Arrays.sort(files, SizeFileComparator.SIZE_REVERSE); // 对files数组进行排序
System.out.println("文件(夹)的SIZE_REVERSE排序:");
for (File file : files) {
System.out.print(file.getName() + "\t");// 输出文件夹中文件(夹)的名称
}
System.out.println();
Arrays.sort(files, SizeFileComparator.SIZE_SUMDIR_COMPARATOR);
System.out.println("文件(夹)的SIZE_SUMDIR_COMPARATOR排序:");
for (File file : files) {
System.out.print(file.getName() + "\t");// 输出文件夹中文件(夹)的名称
}
System.out.println();
Arrays.sort(files, SizeFileComparator.SIZE_SUMDIR_REVERSE);
System.out.println("文件(夹)的SIZE_SUMDIR_REVERSE排序:");
for (File file : files) {
System.out.print(file.getName() + "\t");// 输出文件夹中文件(夹)的名称
}
}
}
假设文件夹中有大量不同的文件,而你又仅对某种类型的文件感兴趣,就可以使用文件的过滤功能。Commons IO组件的filefilter包提供了大量与过滤相关的实现类。
SizeFileFilter类能根据指定的文件大小实现过滤功能,可以过滤掉小于指定数值的文件或者不小于指定数值的文件。构造方法的详细说明如表1所示:
编写SizeFileFilterTest类,在该类的main()方法中,过滤掉文件夹中文件大小小于1M的文件,代码如下:
public class SizeFileFilterTest {
public static void main(String[] args) {
File dir = new File("d:\\明日科技");// 创建一个文件夹对象
System.out.println("过滤前文件夹中的文件:");
File[] files = dir.listFiles();// 获得该文件夹中所有文件和子文件夹
for (File file : files) {// 输出文件夹中文件的名字和大小
System.out.println(file.getName() + "的大小是:" + file.length());
}
System.out.println("过滤后文件夹中的文件:");
String[] fileNames = dir.list(new SizeFileFilter(1024 * 1024));// 过滤掉<1M的文件
for (int i = 0; i < fileNames.length; i++) {
System.out.println(fileNames[i]);
}
}
}
filefilter包还提供了很多类来支持根据文件可读、可写、隐藏等属性过滤文件,还可以使用通配符等。
Java IO操作中会出现大量的IOException,需要对其捕获或抛出。Commons IO组件的IOUtils对此进行了封装,并提供了大量简化IO操作的方法
IOUtils类为input/output操作提供静态工具方法。该类中与读流有关的方法都已经被缓冲了。所以不需要使用BufferedRead。缓冲的大小是4kb,经测试这是效率最高的。该类的方法并不是及时关闭流的,这意味着你需要手动关闭。其常用方法如表1所示:
编写IOUtilsTest类,在该类的main()方法中先向文件中写入5个字符串,然后输出。代码如下:
public class IOUtilsTest {
@SuppressWarnings("unchecked")
public static void main(String[] args) {
FileOutputStream out = null;
FileInputStream in = null;
try {
out = new FileOutputStream("d:\\明日科技.txt"); // 创建文件输出流对象
in = new FileInputStream("d:\\明日科技.txt"); // 创建文件输入流对象
System.out.println("向文件中写入5个随机字符串");
for (int i = 0; i < 5; i++) {// 向文件中写入5个随机字符串
IOUtils.write(RandomStringUtils.randomAlphanumeric(5) + "\n", out);
}
System.out.println("输出文件中的随机字符串");
List list = IOUtils.readLines(in);// 从文件中读取字符串
for (String string : list) {
System.out.println(string);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
IOUtils.closeQuietly(out); //释放资源
IOUtils.closeQuietly(in); //释放资源
}
}
}
IOUtils除了上面提到的方法外还有其他有用的方法,例如将InputStream转换成字节数组、字符数组,将Reader转换成字节数组、字符数组等,请读者参考API文档。
Commons BeanUtils 下载地址
为了实现面向对象的封装特性,Java程序员经常使用JavaBean。即将类的所有域设置成私有的,然后对每个域提供get和set方法来获得和修改域的值。本实例演示在不能事先获得JavaBean的对象和要获得(修改)的域时,如何用BeanUtils组件实现动态获得和修改JavaBean属性的功能。
BeanUtils组件需要和Logging、Collections组件一起使用。用户可以到Apache官网上下载应用jar包。
JavaBean的属性类型可以分成三类,标准JavaBean规范支持的Simple、Indexed和BeanUtils包支持的Mapped,这些类型的概要说明如下:
q Simple:用来存储单一值,例如Java基本类型int、引用类型String等
q Indexed:用来存储一组相同类型的数据,使用从0开始的整数索引。例如Java数组、列表
q Mapped:用来存储一组键值对,利用String类型的键可以获得相应的值。例如Java映射
本实例使用PropertyUtils类来完成获得和修改JavaBean属性的功能,主要应用的方法如表1所示:
(1)编写Employee类,该类定义了3个域:name表示员工姓名,phoneNumber表示员工手机号码,address表示员工的地址,并且提供了相应的get和set方法。代码如下:
public class Employee {
private String name;//表示员工的姓名
private String[] phoneNumber = new String[10];//表示员工的手机
private Map address = new HashMap();//表示员工的地址
public String getName() {//获得员工的姓名
return name;
}
public void setName(String name) {//修改员工的姓名
this.name = name;
}
public String[] getPhoneNumber() {//获得员工的手机
return phoneNumber;
}
public void setPhoneNumber(String[] phoneNumber) {//修改员工的手机
this.phoneNumber = phoneNumber;
}
public Map getAddress() {//获得员工的地址
return address;
}
public void setAddress(Map address) {//修改员工的地址
this.address = address;
}
}
对于Indexed和Mapped类型要先赋值,否则会出现空指针异常。
(2)编写Test类进行测试,在main()方法中先创建了employee对象,并输出对该对象赋值前后对象的属性值。代码如下:
public class Test {
public static void main(String[] args) {
Employee employee = new Employee();// 获得一个Employee对象
// 获得Employee对象的属性值,由于事先并未对其赋值,所以应该为空
String name = employee.getName();
String phoneNumber = employee.getPhoneNumber()[0];
String address = employee.getAddress().get("home");
// 输出刚获得的属性值
System.out.println("设置属性值之前:");
System.out.println("name属性:" + name);
System.out.println("phoneNumber属性的第一个值:" + phoneNumber);
System.out.println("address属性home键所对应的值:" + address);
try {// 使用PropertyUtils类的相关方法对Employee对象的域赋值
PropertyUtils.setSimpleProperty(employee, "name", "高手如云");
PropertyUtils.setIndexedProperty(employee, "phoneNumber", 0, "1234567");
PropertyUtils.setMappedProperty(employee, "address", "home", "中国");
// 获得Employee对象的属性值,由于刚刚对其赋值,所以应该为不空
name = (String) PropertyUtils.getSimpleProperty(employee, "name");
phoneNumber = (String) PropertyUtils.getIndexedProperty(employee, "phoneNumber", 0);
address = (String) PropertyUtils.getMappedProperty(employee, "address", "home");
// 输出刚获得的属性值
System.out.println("设置属性值之后:");
System.out.println("name属性:" + name);
System.out.println("phoneNumber属性的第一个值:" + phoneNumber);
System.out.println("address属性home键所对应的值:" + address);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
使用PropertyUtils类获得属性值都是Object类型的,要赋值的话不要忘记类型转换。
Java语言提供了工具类,可以在运行时检查、确认JavaBean的属性和方法。再利用反射可以动态调用方法。然而,这些API不易使用,而且程序员也不必要学这些东西。使用PropertyUtils类的方法就可以直接实现这个功能了,大大简化了开发。
如果JavaBean的一个域是引用对象,而需要获得(修改)该引用对象的域时,会调用两次get(set)方法,代码显得十分麻烦。
本实例使用PropertyUtils类来完成获得和修改JavaBean属性的功能,主要应用的方法如表1所示:
(1)编写Employee类,该类定义了3个域:name表示员工姓名,phoneNumber表示员工手机号码,address表示员工的地址,并且提供了相应的get和set方法。代码如下:
public class Employee {
private String name;//表示员工的姓名
private String[] phoneNumber = new String[10];//表示员工的手机
private Map address = new HashMap();//表示员工的地址
public String getName() {//获得员工的姓名
return name;
}
public void setName(String name) {//修改员工的姓名
this.name = name;
}
public String[] getPhoneNumber() {//获得员工的手机
return phoneNumber;
}
public void setPhoneNumber(String[] phoneNumber) {//修改员工的手机
this.phoneNumber = phoneNumber;
}
public Map getAddress() {//获得员工的地址
return address;
}
public void setAddress(Map address) {//修改员工的地址
this.address = address;
}
}
对于Indexed和Mapped类型要先赋值,否则会出现空指针异常。
(2)编写Company类,该类定义了一个域:employee代表公司的员工,并对该域提供了get和set方法。代码如下:
public class Company {
private Employee employee = new Employee();//实例化公司的员工
public Employee getEmployee() {//获得公司的员工
return employee;
}
public void setEmployee(Employee employee) {//修改公司的员工
this.employee = employee;
}
}
(3)编写Test类进行测试,在该类的main()方法中为对象的级联属性赋值并输出赋值后的结果。代码如下:
public class Test {
public static void main(String[] args) {
Company company = new Company();
try {//设置级联属性并输出
PropertyUtils.setNestedProperty(company, "employee.name", "高手如云");
PropertyUtils.setNestedProperty(company, "employee.phoneNumber[0]",
"1234567");
PropertyUtils.setNestedProperty(company, "employee.address(home)", "中国");
System.out.println("获得设置的级联属性:");
String name = (String) PropertyUtils.getNestedProperty(company,
"employee.name");
String phoneNumber = (String) PropertyUtils.getNestedProperty(company,
"employee.phoneNumber[0]");
String address = (String) PropertyUtils.getNestedProperty(company,
"employee.address(home)");
System.out.println("employee.name = " + name);
System.out.println("employee.phoneNumber[0] = " + phoneNumber);
System.out.println("employee.address(home) = " + address);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
对于级联属性,通常的做法是利用若干个get、set方法来获得和设置一个域的值,BeanUtils组件的PropertyUtils类提供方法来简化这种无聊的代码,使程序的可读性更强,推荐读者使用。
如果希望使用JavaBean的优势又不方便创建JavaBean对象(例如对象的属性值会动态发生变化),则可以使用BeanUtils相关工具类动态生成JavaBean。
DynaProperty类是用于描述独立的DynaBean属性的。本实例主要使用了它的两个非空构造方法,其说明如下:
public DynaProperty(String name)
参数说明:
? name:初始化的属性的名称
public DynaProperty(String name,Class type,Class contentType)
参数说明:
? name:初始化的属性的名称
? type:指定的属性的类型
? contentType:Indexed或Mapped类型属性的元素类型
BasicDynaClass最小化的实现了DynaClass接口,它可以作为编写更加专业的DynaClass接口实现类的基础。本实例使用了它的一个非空构造方法,其说明如下:
public BasicDynaClass(String name,Class dynaBeanClass,DynaProperty[] properties)
参数说明:
? name:DynaBean类的名字
? dynaBeanClass:DynaBean的实现类,null代表实现类为BasicDynaBean
? properties:新JavaBean所支持的属性
DynaBean接口用来支持属性名字、类型和值可以动态修改的JavaBean。实现该接口的类在BeanUtils组件中可以当JavaBean使用。本实例使用的方法如表1所示:
编写Test类,在该类的main()方法中创建了一个动态Bean。对其属性赋值之后输出,代码如下:
public class Test {
public static void main(String[] args) {
DynaProperty[] properties = new DynaProperty[3];// 声明保存3个属性值的数组
// 指定属性名称和类型
properties[0] = new DynaProperty("name", String.class);
properties[1] = new DynaProperty("phoneNumber", String[].class, String.class);
properties[2] = new DynaProperty("address", Map.class, String.class);
BasicDynaClass dynaClass = new BasicDynaClass("employee", null, properties);
DynaBean employee = null;
try {
employee = dynaClass.newInstance();// 获得DynaBean的实例
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
// 为属性赋值
employee.set("name", "明日科技");
employee.set("phoneNumber", new String[10]);// 索引类型要先初始化
employee.set("phoneNumber", 0, "1234567");
employee.set("address", new HashMap());// 映射类型要先初始化
employee.set("address", "home", "中国");
String name = (String) employee.get("name");
String phoneNumber = (String) employee.get("phoneNumber", 0);
String address = (String) employee.get("address", "home");
// 输出属性值
System.out.println("新建JavaBean的name属性:" + name);
System.out.println("新建JavaBean的phoneNumber属性的第一个值:" + phoneNumber);
System.out.println("新建JavaBean的address属性home键所对应的值:" + address);
}
}
对于DynaBean类型,可以使用其get和set方法来获得和修改属性值。
当JavaBean的属性不确定时,要想使用只能采用动态创建的方法。BeanUtils组件对此提供了很好的支持。希望读者能够掌握本实例。
在使用Hibernate等ORM框架操作数据库时,其操作和返回的对象都是JavaBean。如果使用普通的方式复制JavaBean属性将会调用大量的get和set方法。
BeanUtils利用反射机制提供了操作bean的方法,本实例使用了其copyProperties()方法来实现Bean属性的复制操作,该方法的声明如下:
public static void copyProperties(Object dest,Object orig)throws IllegalAccessException,InvocationTargetException
参数说明:
? dest:目标对象
? orig:源对象
这个方法的复制是浅复制,即对于引用对象复制的效果是两者都指向了同一对象。
(1)编写Employee类,该类定义了3个域:name表示员工姓名,phoneNumber表示员工手机号码,address表示员工的地址,并且提供了相应的get和set方法。代码如下:
public class Employee {
private String name;//表示员工的姓名
private String[] phoneNumber = new String[10];//表示员工的手机
private Map address = new HashMap();//表示员工的地址
public String getName() {//获得员工的姓名
return name;
}
public void setName(String name) {//修改员工的姓名
this.name = name;
}
public String[] getPhoneNumber() {//获得员工的手机
return phoneNumber;
}
public void setPhoneNumber(String[] phoneNumber) {//修改员工的手机
this.phoneNumber = phoneNumber;
}
public Map getAddress() {//获得员工的地址
return address;
}
public void setAddress(Map address) {//修改员工的地址
this.address = address;
}
}
注意:对于Indexed和Mapped类型要先赋值,否则会出现空指针异常。
(2)编写Test类进行测试,在main()方法中,首先创建了两个Employee对象,然后对其中一个赋值,对另外一个复制,然后输出两个的域。代码如下:
public class Test {
public static void main(String[] args) {
Employee employee1 = new Employee();// 声明Employee变量
Employee employee2 = new Employee();// 声明Employee变量
try {
// 为employee1赋值
PropertyUtils.setSimpleProperty(employee1, "name", "明日科技");
PropertyUtils.setIndexedProperty(employee1, "phoneNumber", 0, "1234567");
PropertyUtils.setMappedProperty(employee1, "address", "home", "中国");
BeanUtils.copyProperties(employee2, employee1);// 将employee1复制到employee2
// 获得employee2的属性值
String name = (String) PropertyUtils.getSimpleProperty(employee2, "name");
String phoneNumber = (String) PropertyUtils.getIndexedProperty(employee2, "phoneNumber", 0);
String address = (String) PropertyUtils.getMappedProperty(employee2, "address", "home");
// 输出employee2的属性值
System.out.println("复制属性值之后:");
System.out.println("name属性:" + name);
System.out.println("phoneNumber属性的第一个值:" + phoneNumber);
System.out.println("address属性home键所对应的值:" + address);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
通常复制JavaBean的属性先要将一个bean的属性值用get方法取出,再用set方法存入到另一个bean。如果只有一两个属性值还可以忍受,如果三个以上就比较痛苦了。还好有BeanUtils组件的帮忙,一个方法就解决了。
Java中如果对对象排序可以考虑实现Comparable接口,但是需要排序的属性一旦指定就不能再修改了。BeanUtils组件提供了对JavaBean动态排序的支持,即可以在运行时指定排序的属性。
BeanComparator通过指定的属性来比较两个bean。它也可以用来比较级联属性、索引属性、映射属性和组合属性等。BeanComparator默认把指定的bean属性传递给ComparableComparator。如果比较的属性值可能有空值,那么应该传递一个合适的Comparator或ComparatorChain给构造方法。
技巧:利用Collections组件的ComparatorUtils类可以实现含有空值的排序,请用户参考相关的API。
(1)编写Employee类,该类定义了3个域:id表示员工的序号、name表示员工的姓名、salary表示员工的薪水,并且提供了相应的get和set方法。代码如下:
public class Employee {
private int id;//表示员工的序号
private String name;//表示员工的姓名
private double salary;//表示员工的薪水
//省略get和set方法
@Override
public String toString() {
return "员工编号:" + id + ",员工姓名:" + name + ",员工工资:" + salary;
}
}
(2)编写Test类,在该类的main()方法中创建了3个Employee对象并进行初始化,然后使用salary域进行排序,代码如下:
public class Test {
@SuppressWarnings("unchecked")
public static void main(String[] args) {
Employee employee1 = new Employee();// 创建employee1对象并初始化
employee1.setId(1);
employee1.setName("IBM");
employee1.setSalary(10000);
Employee employee2 = new Employee();// 创建employee2对象并初始化
employee2.setId(2);
employee2.setName("Oracle");
employee2.setSalary(1000);
Employee employee3 = new Employee();// 创建employee3对象并初始化
employee3.setId(3);
employee3.setName("Sun");
employee3.setSalary(100);
List list = new ArrayList();// 创建list对象并保存全部员工对象
list.add(employee1);
list.add(employee2);
list.add(employee3);
System.out.println("排序前:");
for (Employee employee : list) {
System.out.println(employee);// 输出所有对象
}
Collections. sort(list, new BeanComparator("salary"));// 进行排序
System.out.println("按工资排序后:");
for (Employee employee : list) {
System.out.println(employee);// 输出所有对象
}
}
}
BeanComparator实现了Comparator接口,利用反射根据指定的属性值来排序。使用该类的方法比自己实现该功能要好很多,希望读者认真掌握。
Commons DbUtils 下载地址
在使用JDBC的过程中,SQLException几乎处处可见。这不仅增加了代码量(要处理异常)而且影响代码的阅读(逻辑混乱)。Commons DbUtils组件提供了一些工具类来优化JDBC代码。本实例将演示如何使用它们实现向表格中添加数据,对于删除和修改,只要换成相应的SQL语句即可。
DbUtils类是一组JDBC工具集。本实例使用的方法如表1所示:
DbUtils只是对JDBC进行了简单的封装,它并不是ORM框架。
(1)创建users表,该表包括了3个字段:id用来做标识列,username用来保存用户名,password用来保存密码。代码如下:
create table users (
id int auto_increment primary key,
username varchar(20),
password varchar(20)
)
(2)编写QueryRunnerTest类,在该类中定义了3个方法:getConnection()用户获得数据库连接,operate()方法用于操作数据库,main()方法用于测试。代码如下:
public class QueryRunnerTest {
// 定义JDBC相关参数
private static String URL = "jdbc:mysql://localhost:3306/db_database18";
private static String DRIVER = "com.mysql.jdbc.Driver";
private static String USERNAME = "mr";
private static String PASSWORD = "mingrisoft";
private static Connection conn;
public static Connection getConnection() {// 用于获得数据库连接的工具方法
try {
DbUtils.loadDriver(DRIVER);// 加载驱动
conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);// 建立连接
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
public static int operate(String sql, Object... params) {// 用于执行有参数的SQL语句
int result = 0;
QueryRunner runner = new QueryRunner();
try {
result = runner.update(getConnection(), sql, params);// 执行SQL语句
} catch (SQLException e) {
e.printStackTrace();
} finally {
DbUtils.closeQuietly(conn);// 关闭连接
}
return result;
}
public static void main(String[] args) {
String sql = "insert into users(username, password) values (?, ?)";
Object[] params = { "mrsoft", "Java" };
operate(sql, params);// 向数据库中插入一条数据
}
}
使用普通JDBC代码,加载驱动、关闭连接等操作都要处理异常的,但是DbUtils类对其进行了处理,只需要调用其相关方法就不用处理异常了,这让代码看着更加简洁。
使用JDBC进行查询得到的结果是一个ResultSet对象,该对象使用起来非常不方便。Commons DbUtils组件的handlers包提供了近10种方法对其转换。
使用JDBC进行查询得到的结果是一个ResultSet对象,该对象使用起来非常不方便。Commons DbUtils组件的handlers包提供了近10种方法对其转换。
DbUtils类是一组JDBC工具集。本实例使用的方法如表1所示:
DbUtils只是对JDBC进行了简单的封装,它并不是ORM框架。
(1)创建users表,该表包括了3个字段:id用来做标识列,username用来保存用户名,password用来保存密码。代码如下:
create table users (
id int auto_increment primary key,
username varchar(20),
password varchar(20)
)
(2)针对users表,编写User类。在该类中定义了3个域分别和表中的字段相对应。并且为这些自动提供了get和set方法,代码如下:
public class User {
private int id;
private String username;
private String password;
//省略get和set方法
@Override
public String toString() {
return "序号:" + id + ",用户名:" + username + ",密码:" + password;
}
}
(3)编写QueryRunnerTest类,在该类中定义了3个方法:getConnection()用于创建数据库连接,query()方法用于查询,main()方法用于进行测试。代码如下:
public class QueryRunnerTest {
// 定义JDBC相关参数
private static String URL = "jdbc:mysql://localhost:3306/db_database18";
private static String DRIVER = "com.mysql.jdbc.Driver";
private static String USERNAME = "mr";
private static String PASSWORD = "mingrisoft";
private static Connection conn;
public static Connection getConnection() {// 用于获得数据库连接的工具方法
try {
DbUtils.loadDriver(DRIVER);// 加载驱动
conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);// 建立连接
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
public static List query(String sql) {// 用来将查询结果转换成bean列表的工具方法
QueryRunner qr = new QueryRunner();
List list = null;
try {
list = qr.query(getConnection(), sql, new BeanListHandler(User.class));
} catch (SQLException e) {
e.printStackTrace();
} finally {
DbUtils.closeQuietly(conn);// 关闭连接
}
return list;
}
public static void main(String[] args) {
System.out.println("表users中的全部数据如下:");
List list = query("select * from users");// 查询users表中全部数据
for (User user : list) {
System.out.println(user);
}
}
}
本实例演示了如何将结果集转换成Bean列表,handlers包还支持很多格式的转换,例如转换成Object数组、Object数组列表等等。
Commons Codec下载地址
MD5可以为软件生成一个唯一的标识,防止软件在传播过程中遭到恶意修改。Commons Codec组件的DigestUtils类提供了对MD5、SHA等算法的支持。
DigestUtils类提供了很多工具方法来支持数字信息算法。本实例使用md5Hex()方法对输入流进行分析然后计算其MD5值,该方法的声明如下:
public static String md5Hex(InputStream data)throws IOException
参数说明:
?data:需要进行计算的数据流
设计过程
(1)继承JFrame编写一个窗体类,名称为“MD5Viewer”。
(2)设计MD5Viewer窗体类时用到的控件及说明如表1所示。
(3)编写按钮激活事件监听器调用的do_fileButton_actionPerformed ()方法,该方法是在类中自定义的,主要用途是实现选择文件并计算MD5。代码如下:
protected void do_fileButton_actionPerformed(ActionEvent arg0) {
JFileChooser fileChooser = new JFileChooser();// 创建文件选择器
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);// 让文件选择器只能选择文件
fileChooser.setMultiSelectionEnabled(false);// 不能选择多个文件
int result = fileChooser.showOpenDialog(this);
if (result == JFileChooser.APPROVE_OPTION) {
File selectFile = fileChooser.getSelectedFile();// 获得用户选择的文件
textField.setText(selectFile.getName ());// 显示选择文件的名称
FileInputStream in = null;
try {
in = new FileInputStream(selectFile);// 获得文件输入流
} catch (FileNotFoundException e) {
e.printStackTrace();
}
try {
md5 = DigestUtils.md5Hex(in);// 计算MD5值,并保存在域变量md5中
} catch (IOException e) {
e.printStackTrace();
}
}
}
Java的security包也提供了计算MD5的方法,但是没有DigestUtils类使用方便。该类还提供了多种其他形式的MD5计算、SHA计算等,请读者参考其API文档。
Base64是网络上最常见的用于传输字节码的编码方式之一。采用Base64编码不仅能减少传出量,而且还可以起到简单的加密作用。Commons Codec组件的Base64类提供了对Base64编码和解码的支持。
Base64类提供由RFC 2045定义的编码和解码方法。本实例使用encodeBase64()方法来对给定的字符串进行编码,该方法的声明如下:
public static byte[] encodeBase64(byte[] binaryData)
参数说明:
?binaryData:需要编码的二进制数据
关于Base64的详细历史、算法、应用等可以参考相关资料。
(1)继承JFrame编写一个窗体类,名称为“Base64EncodingFrame”。
(2)设计Base64EncodingFrame窗体类时用到的控件及说明如表1所示。
(3)编写按钮激活事件监听器调用的do_button_actionPerformed ()方法,该方法是在类中自定义的,主要用途是使用Base64编码用户输入的字符串。代码如下:
protected void do_button_actionPerformed(ActionEvent arg0) {
String sourceString = message1TextArea.getText();// 获得用户要编码的字符串
if (sourceString.length() == 0) {// 如果字符串长度为0则提示用户重新输入
JOptionPane.showConfirmDialog(this, "请输入要编码的字符串", "",
JOptionPane.WARNING_MESSAGE);
return;
}
Base64 base64 = new Base64();
byte[] encodeBytes = base64.encode(sourceString.getBytes());// 进行编码
message2TextArea.setText(new String(encodeBytes));// 显示编码后结果
}
Base64编码还可以应用于下载资源的路径,迅雷在下载资源是的路径就使用Base64编码。需要了解的是该编码的破解方式非常容易,所以只能用来“防君子”,不能用来“防小人”!
Base64是网络上最常见的用于传输字节码的编码方式之一。采用Base64编码不仅能减少传出量,而且还可以起到简单的加密作用。Commons Codec组件的Base64类提供了对Base64编码和解码的支持。
Base64类提供由RFC 2045定义的编码和解码方法。本实例使用decodeBase64 ()方法来对给定的字符串进行解码,该方法的声明如下:
public static byte[] decodeBase64(String base64String)
参数说明:
? base64String:包含Base64的字符串
( 1)继承JFrame编写一个窗体类,名称为“Base64DecodingFrame”。
(2)设计Base64DecodingFrame窗体类时用到的控件及说明如表1所示。
(3)编写按钮激活事件监听器调用的do_button_actionPerformed ()方法,该方法是在类中自定义的,主要用途是使用Base64解码用户输入的字符串。代码如下:
protected void do_button_actionPerformed(ActionEvent arg0) {
String sourceString = message1TextArea.getText();// 获得用户要解码的字符串
// 省略校验代码
Base64 base64 = new Base64();
byte[] encodeBytes = base64.decode(sourceString); // 进行解码
message2TextArea.setText(new String(encodeBytes));// 显示解码后结果
}
编程实现Base64的算法比较麻烦,使用Base64类就非常容易了。如果读者对于Base64类的底层实现感兴趣,请参考Base64类的源代码。
Commons Email下载地址
Email是网络通信的常用方式之一。Java Mail是Java为了支持Email而开发的工具包,但是使用起来十分复制。Commons Email组件的SimpleEmail类也提供了发送Email的方法。
SimpleMail类用于发送没有附件的简单Email,本实例使用的方法如表1所示:
除了email.jar,发送邮件还需要两个额外的jar包,分别是activation.jar和mail.jar。
(1)继承JFrame编写一个窗体类,名称为“EmailSender”。
(2)设计EmailSender窗体类时用到的控件及说明如表1所示。
(3)编写按钮激活事件监听器调用的do_button_actionPerformed ()方法,该方法是在类中自定义的,主要用途是校验用户输入的信息和发送邮件。主要代码如下:
protected void do_button_actionPerformed(ActionEvent arg0) {
String hostName = hostTextField.getText(); //获得服务器地址
String toEmail = toEmailTextField.getText();//获得收件人邮箱
String fromEmail = fromEmailTextField.getText();// 获得发件人邮箱
String username = usernameTextField.getText();//获得用户名
String password = passwordTextField.getText();//获得密码
String title = titleTextField.getText();//获得邮件标题
String content = contentTextArea.getText();//获得邮件内容
// 省略校验代码
SimpleEmail email = new SimpleEmail();
email.setHostName(hostName);//设置服务器地址
try {
email.addTo(toEmail);//设置收件人邮箱
email.setFrom(fromEmail);//设置发件人邮箱
} catch (EmailException e) {
e.printStackTrace();
}
email.setAuthentication(username, password);//设置用户名和密码
email.setSubject(title);//设置邮件主题
try {
email.setMsg(content);//设置邮件内容
email.send();// 发送邮件
} catch (EmailException e) {
e.printStackTrace();
}
}
SimpleEmail类继承Email类,用于发送简单的文本邮件。该类将发送邮件的功能处理的十分完美,只要填好必须的参数就可以了,使用起来比Java Mail容易很多。
Email是网络通信的常用方式之一。它不仅能传递文本信息,还可以用来发送文件、图片等,这些统称为附件。Commons Email组件的MultiPartEmail类提供了发送附件的功能。
MultiPartEmail用于发送有附件的简单Email,本实例使用的方法如表1所示:
除了email.jar,发送邮件还需要两个额外的jar包,分别是activation.jar和mail.jar。
(1)继承JFrame编写一个窗体类,名称为“EmailSender”。
(2)设计EmailSender窗体类时用到的控件及说明如表2所示。
(3)编写按钮激活事件监听器调用的do_attachButton_actionPerformed ()方法,该方法是在类中自定义的,主要用途是获得用户选择的附件并添加附件的信息。代码如下:
protected void do_attachButton_actionPerformed(ActionEvent arg0) {
JFileChooser fileChooser = new JFileChooser(); //创建文件选择器
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);//设置文件选择器只能选择文件
fileChooser.setMultiSelectionEnabled(false); //设置文件选择器只能选择单个文件
int result = fileChooser.showOpenDialog(this);
if (result == JFileChooser.APPROVE_OPTION) {
File selectFile = fileChooser.getSelectedFile();// 获得用户选择的文件
attachTextField.setText(selectFile.getAbsolutePath());
attachment = new EmailAttachment(); //创建附件对象
attachment.setDescription("附件"); //设置附件描述
attachment.setDisposition(EmailAttachment.ATTACHMENT);//设置附件类型
attachment.setName(selectFile.getName());//设置附件名称
attachment.setPath(selectFile.getAbsolutePath());//设置附件绝对路径
}
}
(4)编写按钮激活事件监听器调用的do_button_actionPerformed ()方法,该方法是在类中自定义的,主要用途是校验用户输入的信息和发送邮件。代码如下:
protected void do_button_actionPerformed(ActionEvent arg0) {
String hostName = hostTextField.getText(); //获得服务器地址
String toEmail = toEmailTextField.getText(); //获得收件人邮箱
String fromEmail = fromEmailTextField.getText(); //获得发件人邮箱
String username = usernameTextField.getText(); //获得用户名
String password = passwordTextField.getText(); //获得密码
String title = titleTextField.getText(); //获得邮件主题
String content = contentTextArea.getText();//获得邮件内容
// 省略文件校验代码
MultiPartEmail email = new MultiPartEmail();
if (attachment != null) {
try {
email.attach(attachment);// 如果附件不是空就增加附件
} catch (EmailException e) {
e.printStackTrace();
}
}
email.setHostName(hostName);//设置服务器地址
try {
email.addTo(toEmail); //设置收件人邮箱
email.setFrom(fromEmail);//设置发件人邮箱
} catch (EmailException e) {
e.printStackTrace();
}
email.setAuthentication(username, password);//设置发件人信息
email.setSubject(title); //设置邮件主题
try {
email.setMsg(content); //设置邮件内容
email.send();//发送邮件
} catch (EmailException e) {
e.printStackTrace();
}
}
邮件的附件可以用来传递更多的信息,使用MultiPartEmail类可以大幅度简化发送包含附件的邮件。只需要简单设置一下EmailAttachment对象的信息即可。
Commons Configuration下载地址
Commons Configuration组件提供了一个通用接口来使Java应用程序从不同来源读取配置信息。
XMLConfiguration类是用来解析XML文档的工具类,本实例使用的方法如表1所示
(1)编写一个简单的XML文件Book,用来进行测试,代码如下:
<路飞>
<Java图书>
<书名>Java从入门到精通(第3版)书名>
<作者>索隆,娜美作者>
<出版社>海贼王出版社出版社>
<ISBN>9787302227465ISBN>
<价格>5亿贝利价格>
<页数>533页数>
<出版时间>2010-7-1出版时间>
Java图书>
路飞>
(2)编写XMLConfigurationTest类,在该类的main()方法中输出了XML文件的节点信息,代码如下:
public class XMLConfigurationTest {
public static void main(String[] args) throws ConfigurationException {
URL resource = new XMLConfigurationTest().getClass().getResource("Book.xml");
XMLConfiguration config = new XMLConfiguration(resource);
String bookName = config.getString("Java图书.书名"); //获得书名
String author = config.getString("Java图书.作者"); //获得作者
String press = config.getString("Java图书.出版社"); //获得出版社
String ISBN = config.getString("Java图书.ISBN"); //获得ISBN
double price = config.getDouble("Java图书.价格"); //获得价格
int pages = config.getInt("Java图书.页数");//获得页数
String time = config.getString("Java图书.出版时间"); //获得出版时间
System.out.println("图书信息");
System.out.println("书名:" + bookName);
System.out.println("作者:" + author);
System.out.println("出版社:" + press);
System.out.println("ISBN:" + ISBN);
System.out.println("价格:" + price + "元");
System.out.println("页数:" + pages);
System.out.println("出版时间:" + time);
}
}
级联属性使用.分隔,例如“Java图书.出版社”。根元素可以忽略不写。
Commons Configuration组件不仅支持XML,还支持属性文件、Windows INI文件、属性列表文件、JNDI文件、JDBC数据源、系统属性、Applet参数和Servlet参数等,还可以将不同来源的属性进行组合.
Commons DBCP 下载地址
由于DBCP 组件其他文章里面写过了,这里不再嗷述.放几张官方类图
其他组件类图官网都有提供这里不放了.