Commons 组件学习笔记

目录

  • 目录
    • Commons 组件在apache官网有详细的资料深入学习的可以在官网查询API及相关资料
  • Commons 介绍
    • Commons Lang 组件简介
      • 数组元素的增加
      • AarryUtils类 数组元素删除
      • 生成随机字符串
      • 序列化与反序列化
      • 分数的常见运算
      • 整数取值的范围判断
    • Commons Math 组件简介
      • 绘制简单的直方图
      • 一元线性回归计算
      • 实数矩阵的运算
      • 复数的常见运算
      • T 分布常用计算
    • Commons IO 组件简介
      • 简化文件夹 删除
      • 简化文件夹 复制
      • 简化文件夹排序
      • 简化文件夹 过滤
      • 简化文件的读写操作
    • Commons BeanUtils组件简介
      • 设置JavaBean 简单属性
      • 设置JavaBean 级联属性
      • 动态生成JavaBean
      • 复制JavaBean 属性
      • 动态排序JavaBean
    • Commons DbUtils 组件简介
      • 优化JDBC 代码
      • 结果集与Bean 列表
    • Commons Codec 组件简介
      • 编写MD5查看器
      • 基于Base64编码
      • 基于Base64 解码
    • Commons Email 组件简介
      • 发送简单Email
      • 发送带附件的Email
    • Commons Configuration 组件简介
      • 读取XML 文件属性
    • Commons DBCP 组件简介

Commons 组件在apache官网有详细的资料.深入学习的可以在官网查询API.及相关资料.

Commons 介绍

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 组件比较多下面只是简单介绍了部分组件

Commons 组件学习笔记_第1张图片

Commons Lang 组件简介

数组元素的增加

Java语言中的数组并不好用:在创建时需要指定数组的长度,并且一旦创建完成则长度不能再发生变化。为了弥补这个不足,Java SE API中提供了ArrayList类。对于数组的超级粉丝,向您推荐使用Commons Lang组件。其中的ArrayUtils类对数组操作进行了增强,实现了向数组中增加元素的方法。本实例将演示如何使用这些方法.

Commons Lang下载地址

ArrayUtils类提供了对基本类型(例如int)、包装类型(例如Integer)和其他引用类型数组的支持。该类尝试优雅地处理null值。如果数组为null,并不会抛出异常。如果数组中某个元素为null,才会抛出异常。本实例使用的方法如表1所示:

Commons 组件学习笔记_第2张图片

编写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类来代替数组,该类的使用也是非常方便的。

AarryUtils类 数组元素删除

Commons 组件学习笔记_第3张图片

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所示:

Commons 组件学习笔记_第4张图片

该类还有一个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接口。该接口是一个标识接口,它没有定义任何方法。

Commons 组件学习笔记_第5张图片

利用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类,提供精确存储分数的功能。

Commons 组件学习笔记_第6张图片

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下载地址

在得到一组简单的统计学变量时,可以使用描述统计学对其进行初步分析,为下一步分析提供理论依据。Commons Math组件的DescriptiveStatistics类提供了相关方法。

DescriptiveStatistics类管理单变量的数据集并基于该数据集进行统计计算。windowSize属性用来设置数据集的大小,默认情况下是不限制数据集大小的。由于该类会保存所有的数据,所以对于数据量比较大的统计运算,推荐使用SummaryStatistics类。本实例使用的方法如表1所示:

Commons 组件学习笔记_第7张图片

编写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进行讲解,对于其他类型的使用是类似的。

Commons 组件学习笔记_第8张图片

编写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所示:

Commons 组件学习笔记_第9张图片

编写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所示:

Commons 组件学习笔记_第10张图片

编写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所示:

Commons 组件学习笔记_第11张图片

编写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 分布常用计算

正态分布是统计学中重要的分布,但是由于数据的方差不易计算,实际应用中更倾向使用T分布。Commons Math组件的TDistributionImpl类定义了T分布常用计算方法。

TDistributionImpl类定义了T分布运算的常用方法。本实例使用的方法如表1所示:

Commons 组件学习笔记_第12张图片

编写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 组件简介

Commons IO 下载地址

简化文件(夹) 删除

Java的中文件和文件夹统一使用File类管理,该类提供了delete()方法用来删除文件和空文件夹。Commons IO组件的FileDeleteStrategy类定义了删除非空文件夹的方法。
FileDeleteStrategy类定义了删除文件(夹)的常用方法。本实例使用的方法如表1所示:

Commons 组件学习笔记_第13张图片

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所示:

Commons 组件学习笔记_第14张图片

编写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所示:

Commons 组件学习笔记_第15张图片

编写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所示:

Commons 组件学习笔记_第16张图片

编写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所示:

Commons 组件学习笔记_第17张图片

编写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组件简介

Commons BeanUtils 下载地址

设置JavaBean 简单属性

为了实现面向对象的封装特性,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所示:

Commons 组件学习笔记_第18张图片

(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 级联属性

如果JavaBean的一个域是引用对象,而需要获得(修改)该引用对象的域时,会调用两次get(set)方法,代码显得十分麻烦。

本实例使用PropertyUtils类来完成获得和修改JavaBean属性的功能,主要应用的方法如表1所示:

PropertyUtils类

(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的优势又不方便创建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所示:

Commons 组件学习笔记_第19张图片

编写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组件对此提供了很好的支持。希望读者能够掌握本实例。

复制JavaBean 属性

在使用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组件的帮忙,一个方法就解决了。

动态排序JavaBean

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 组件简介

Commons DbUtils 下载地址

优化JDBC 代码

在使用JDBC的过程中,SQLException几乎处处可见。这不仅增加了代码量(要处理异常)而且影响代码的阅读(逻辑混乱)。Commons DbUtils组件提供了一些工具类来优化JDBC代码。本实例将演示如何使用它们实现向表格中添加数据,对于删除和修改,只要换成相应的SQL语句即可。

DbUtils类是一组JDBC工具集。本实例使用的方法如表1所示:

Commons 组件学习笔记_第20张图片

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类对其进行了处理,只需要调用其相关方法就不用处理异常了,这让代码看着更加简洁。

结果集与Bean 列表

使用JDBC进行查询得到的结果是一个ResultSet对象,该对象使用起来非常不方便。Commons DbUtils组件的handlers包提供了近10种方法对其转换。

使用JDBC进行查询得到的结果是一个ResultSet对象,该对象使用起来非常不方便。Commons DbUtils组件的handlers包提供了近10种方法对其转换。

DbUtils类是一组JDBC工具集。本实例使用的方法如表1所示:

Commons 组件学习笔记_第21张图片

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 组件简介

Commons Codec下载地址

编写MD5查看器

MD5可以为软件生成一个唯一的标识,防止软件在传播过程中遭到恶意修改。Commons Codec组件的DigestUtils类提供了对MD5、SHA等算法的支持。

DigestUtils类提供了很多工具方法来支持数字信息算法。本实例使用md5Hex()方法对输入流进行分析然后计算其MD5值,该方法的声明如下:

public static String md5Hex(InputStream data)throws IOException

参数说明:
?data:需要进行计算的数据流
设计过程
(1)继承JFrame编写一个窗体类,名称为“MD5Viewer”。
(2)设计MD5Viewer窗体类时用到的控件及说明如表1所示。

Commons 组件学习笔记_第22张图片

(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是网络上最常见的用于传输字节码的编码方式之一。采用Base64编码不仅能减少传出量,而且还可以起到简单的加密作用。Commons Codec组件的Base64类提供了对Base64编码和解码的支持。

Base64类提供由RFC 2045定义的编码和解码方法。本实例使用encodeBase64()方法来对给定的字符串进行编码,该方法的声明如下:

public static byte[] encodeBase64(byte[] binaryData)

参数说明:
?binaryData:需要编码的二进制数据

关于Base64的详细历史、算法、应用等可以参考相关资料。

(1)继承JFrame编写一个窗体类,名称为“Base64EncodingFrame”。
(2)设计Base64EncodingFrame窗体类时用到的控件及说明如表1所示。

Commons 组件学习笔记_第23张图片

(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是网络上最常见的用于传输字节码的编码方式之一。采用Base64编码不仅能减少传出量,而且还可以起到简单的加密作用。Commons Codec组件的Base64类提供了对Base64编码和解码的支持。

Base64类提供由RFC 2045定义的编码和解码方法。本实例使用decodeBase64 ()方法来对给定的字符串进行解码,该方法的声明如下:
public static byte[] decodeBase64(String base64String)
参数说明:
? base64String:包含Base64的字符串
( 1)继承JFrame编写一个窗体类,名称为“Base64DecodingFrame”。
(2)设计Base64DecodingFrame窗体类时用到的控件及说明如表1所示。

Commons 组件学习笔记_第24张图片

(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 组件简介

Commons Email下载地址

发送简单Email

Email是网络通信的常用方式之一。Java Mail是Java为了支持Email而开发的工具包,但是使用起来十分复制。Commons Email组件的SimpleEmail类也提供了发送Email的方法。

SimpleMail类用于发送没有附件的简单Email,本实例使用的方法如表1所示:

Commons 组件学习笔记_第25张图片

除了email.jar,发送邮件还需要两个额外的jar包,分别是activation.jar和mail.jar。

(1)继承JFrame编写一个窗体类,名称为“EmailSender”。
(2)设计EmailSender窗体类时用到的控件及说明如表1所示。

Commons 组件学习笔记_第26张图片

(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

Email是网络通信的常用方式之一。它不仅能传递文本信息,还可以用来发送文件、图片等,这些统称为附件。Commons Email组件的MultiPartEmail类提供了发送附件的功能。

MultiPartEmail用于发送有附件的简单Email,本实例使用的方法如表1所示:

Commons 组件学习笔记_第27张图片

除了email.jar,发送邮件还需要两个额外的jar包,分别是activation.jar和mail.jar。

Commons 组件学习笔记_第28张图片

(1)继承JFrame编写一个窗体类,名称为“EmailSender”。
(2)设计EmailSender窗体类时用到的控件及说明如表2所示。

Commons 组件学习笔记_第29张图片

(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下载地址

读取XML 文件属性

Commons Configuration组件提供了一个通用接口来使Java应用程序从不同来源读取配置信息。

XMLConfiguration类是用来解析XML文档的工具类,本实例使用的方法如表1所示

Commons 组件学习笔记_第30张图片

(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 组件简介

Commons DBCP 下载地址

由于DBCP 组件其他文章里面写过了,这里不再嗷述.放几张官方类图

其他组件类图官网都有提供这里不放了.

Commons 组件学习笔记_第31张图片

Commons 组件学习笔记_第32张图片

Commons 组件学习笔记_第33张图片

Commons 组件学习笔记_第34张图片

Commons 组件学习笔记_第35张图片

Commons 组件学习笔记_第36张图片

你可能感兴趣的:(Commons组件)