commons常用工具包的使用&guava工具类使用&hutool扫描类、http、加载指定jar包

===========commons-lang包======

   这个包中的很多工具类可以简化我们的操作,在这里简单的研究其中的几个工具类的使用。

1.StringUtils工具类

  可以判断是否是空串,是否为null,默认值设置等操作:

    /**
     * StringUtils
     */
    public static void test1() {
        System.out.println(StringUtils.isBlank("   "));// true----可以验证null, ""," "等
        System.out.println(StringUtils.isBlank("null"));// false
        System.out.println(StringUtils.isAllLowerCase("null"));// t
        System.out.println(StringUtils.isAllUpperCase("XXXXXX"));// t
        System.out.println(StringUtils.isEmpty(" "));// f---为null或者""返回true
        System.out.println(StringUtils.defaultIfEmpty(null, "default"));// 第二个参数是第一个为null或者""的时候的取值
        System.out.println(StringUtils.defaultIfBlank("    ", "default"));//// 第二个参数是第一个为null或者""或者"   "的时候的取值
    }

isBlank()  可以验证空格、null、"",如果是好几个空格也返回true

isEmpty验证不了空格,只有值为null和""返回true 

两者都验证不了"null"字符串,所以如果验证"null"还需要自己用equals进行验证。

结果:

true
false
true
true
false
default
default

简单的贴出几个源码便于记录:

    public static boolean isBlank(final CharSequence cs) {
        int strLen;
        if (cs == null || (strLen = cs.length()) == 0) {
            return true;
        }
        for (int i = 0; i < strLen; i++) {
            if (Character.isWhitespace(cs.charAt(i)) == false) {
                return false;
            }
        }
        return true;
    }

    public static boolean isEmpty(final CharSequence cs) {
        return cs == null || cs.length() == 0;
    }
    public static String defaultIfEmpty(String str, String defaultStr) {
        return StringUtils.isEmpty(str) ? defaultStr : str;
    }

 

  CharSequence是一个接口,String,StringBuffer,StringBuilder等都实现了此接口

public abstract interface CharSequence {
    public abstract int length();

    public abstract char charAt(int paramInt);

    public abstract CharSequence subSequence(int paramInt1, int paramInt2);

    public abstract String toString();
}

补充:StringUtils也可以将集合和数组转为String,并且以指定符号链接里面的数据

        List list = new ArrayList(2);
        list.add("张三");
        list.add("李四");
        list.add("王五");
        String list2str = StringUtils.join(list, ",");
        System.out.println(list2str);

结果:

  张三,李四,王五

有时候我们希望给拼接后的字符串都加上单引号,这个在拼接SQL  in条件的时候非常有用,例如:

        //需求:将逗号里面的内容都加上单引号
        String string = "111,222,333";
        string = "'"+string+"'";//字符串前后加'
        string = StringUtils.join(string.split(","),"','");//先按逗号分隔为数组,然后用','连接数组
        System.out.println(string);

结果:

'111','222','333'

 

  join方法也可以接收数组,可变参数等,也可以对数组进行拼接,如下:

        int[] numbers = { 1, 3, 5 };
        System.out.println(StringUtils.join(numbers, ','));

结果:

1,3,5

其重载方法如下:

 

补充:null和字符串"null"的区别

null在JVM中没有分配内存,引用中无任何东西,debug也看不到任何东西,"null"字符串是一个正常的字符串,在JVM分配内存而且可以看到东西

"null"字符串有东西

 

null无任何东西:

 

补充:String.format(format,Object)也可以对字符串进行格式化,例如在数字前面补齐数字

        int num = 50;
        String format = String.format("%0" + 5 + "d", num);
        System.out.println(format);

结果:

00050

 

补充:StringUtils也可以截取字符串,判断是否大小写等操作

        String string = "123_45_43_ss";
        System.out.println(StringUtils.isAllLowerCase(string));// 判断全部小写
        System.out.println(StringUtils.isAllUpperCase(string));// 判断全部大写
        System.out.println(StringUtils.substringAfter(string, "123"));// 截取123之后的
        System.out.println(StringUtils.substringBefore(string, "45"));// 截取45之前的
        System.out.println(StringUtils.substringBefore(string, "_"));// 截取第一个_之前的
        System.out.println(StringUtils.substringBeforeLast(string, "_"));// 截取最后一个_之前的
        System.out.println(StringUtils.substringAfter(string, "_"));// 截取第一个_之后的
        System.out.println(StringUtils.substringAfterLast(string, "_"));// 截取最后一个_之后的
        System.out.println(StringUtils.substringBetween("1234565432123456", "2", "6"));// 截取两个之间的(都找的是第一个)

结果:

false
false
_45_43_ss
123_
123
123_45_43
45_43_ss
ss
345

 

补充:StringUtils也可以将字符串分割为数组

package cn.xm.exam.test;

import org.apache.commons.lang.StringUtils;

public class test {

    public static void main(String[] args) {
        String t = "tttt";
        System.out.println(StringUtils.split(t, ","));
    }
}

结果:

[Ljava.lang.String;@5a24389c

看过深入理解JVM的都知道上面的[代表是一维数组类型,L代表是引用类型,后面的是String类型

 

补充:  isNoneBlank可以支持多个参数,甚至String数组,用来判断数组里的每一个字符串都是isNotBlank的。

 补充:StringUtils可以获取指定字符出现的次数

StringUtils.countMatches(str, ":")

 补充:StringUtils可以第N次某字符串出现的位置

StringUtils.ordinalIndexOf(str, ":", 2)

补充:StringUtils可以获取指定字符串之间的字符串,并自动截取为字符串数组

String[] substringBetweens = StringUtils.substringsBetween(str, ":", ":"); 

补充:StringUtils可以获取指定字符串之间的字符串(只取满足条件的前两个)

StringUtils.substringBetween(str2, "/")

补充:Stringutils可以左截取和右截取

        // 左右截取
        System.out.println(StringUtils.left("abc", 2));
        System.out.println(StringUtils.right("abc", 2));

结果:

ab
bc

查看源码:

    public static String left(String str, int len) {
        if (str == null) {
            return null;
        }
        if (len < 0) {
            return EMPTY;
        }
        if (str.length() <= len) {
            return str;
        }
        return str.substring(0, len);
    }

    public static String right(String str, int len) {
        if (str == null) {
            return null;
        }
        if (len < 0) {
            return EMPTY;
        }
        if (str.length() <= len) {
            return str;
        }
        return str.substring(str.length() - len);
    }

 

补充:StringUtils可以左右填充指定字符串

        // 左添加(默认添加空格)
        String leftPad = StringUtils.leftPad("2", 2);
        System.out.println(leftPad);
        String leftPad2 = StringUtils.leftPad("12", 3, "0");
        System.out.println(leftPad2);

        // 右添加(默认添加空格)
        String rightPad = StringUtils.rightPad("2", 2);
        System.out.println(rightPad);
        String rightPad2 = StringUtils.rightPad("12", 3, "0");
        System.out.println(rightPad2);

结果:

2
012
2
120

 补充:StringUtils可以忽略大小写判断equals以及contains等

import org.apache.commons.lang3.StringUtils;

public class Plaintest {

    public static void main(String[] args) {
        boolean equalsIgnoreCase = StringUtils.equalsIgnoreCase("AA", "aa");
        System.out.println(equalsIgnoreCase);

        boolean containsIgnoreCase = StringUtils.containsIgnoreCase("Abc", "BC");
        System.out.println(containsIgnoreCase);
    }
}

结果:

true
true

 

2.StringEscapeUtils----------转义字符串的工具类

        //1.防止sql注入------原理是将'替换为''
        System.out.println(org.apache.commons.lang.StringEscapeUtils.escapeSql("1' or '1' = '1"));
        //2.转义/反转义html
        System.out.println( org.apache.commons.lang.StringEscapeUtils.escapeHtml("<a>dddd</a>"));   //&lt;a&gt;dddd&lt;/a&gt;
        System.out.println(org.apache.commons.lang.StringEscapeUtils.unescapeHtml("&lt;a&gt;dddd&lt;/a&gt;"));  //<a>dddd</a>
        //3.转义/反转义JS
        System.out.println(org.apache.commons.lang.StringEscapeUtils.escapeJavaScript("<script>alert('1111')</script>"));   
        //4.把字符串转为unicode编码
        System.out.println(org.apache.commons.lang.StringEscapeUtils.escapeJava("中国"));   
        System.out.println(org.apache.commons.lang.StringEscapeUtils.unescapeJava("\u4E2D\u56FD"));  
        //5.转义JSON
        System.out.println(org.apache.commons.lang3.StringEscapeUtils.escapeJson("{name:'qlq'}"));

结果:

1'' or ''1'' = ''1
&lt;a&gt;dddd&lt;/a&gt;
<a>dddd</a>
<script>alert(\'1111\')<\/script>
\u4E2D\u56FD
中国
{name:'qlq'}

 

 补充:关于SQL注入做进一步解释。上面的防止SQL注入一般是对传入的条件进行转义,不可以对整条SQL进行转义,整条转义SQL执行的时候会报错。防止恶意SQL。例如:

万能用户名和密码如下:

        String username = "1' or '1' = '1";
        String password = "admin' or '1' = '1";

分别做转义和不做转义的情况如下:

        String sql = "select * from user where username = '" + username + "' and password = '" + password + "'";
        System.out.println(sql);

        String sql2 = "select * from user where username = '" + StringEscapeUtils.escapeSql(username)
                + "' and password = '" + StringEscapeUtils.escapeSql(password) + "'";
        System.out.println(sql2);

结果:(转义之后可以防止SQL注入,是将' 替换为 '' (一个单引号替换为两个连着的单引号。将''识别为普通的字符串而不是结束符,也就是转义后的username和password整体作为参数))

select * from user where username = '1' or '1' = '1' and password = 'admin' or '1' = '1'
select * from user where username = '1'' or ''1'' = ''1' and password = 'admin'' or ''1'' = ''1'

 

3.NumberUtils--------字符串转数据或者判断字符串是否是数字常用工具类

    /**
     * NumberUtils
     */
    public static  void test3(){
        System.out.println(NumberUtils.isNumber("231232.8"));//true---判断是否是数字
        System.out.println(NumberUtils.isDigits("2312332.5"));//false,判断是否是整数
        System.out.println(NumberUtils.toDouble(null));//如果传的值不正确返回一个默认值,字符串转double,传的不正确会返回默认值
        System.out.println(NumberUtils.createBigDecimal("333333"));//字符串转bigdecimal
    }

 

4.BooleanUtils------------判断Boolean类型工具类

    /**
     * BooleanUtils
     */
    public static  void test4(){
        System.out.println(BooleanUtils.isFalse(true));//false
        System.out.println(BooleanUtils.toBoolean("yes"));//true
        System.out.println(BooleanUtils.toBooleanObject(0));//false
        System.out.println(BooleanUtils.toStringYesNo(false));//no
        System.out.println(BooleanUtils.toBooleanObject("ok", "ok", "error", "null"));//true-----第一个参数是需要验证的字符串,第二个是返回true的值,第三个是返回false的值,第四个是返回null的值
    }

 

5.SystemUtils----获取系统信息(原理都是调用System.getProperty())

    /**
     * SystemUtils
     */
    public static  void test5(){
        System.out.println(SystemUtils.getJavaHome());
        System.out.println(SystemUtils.getJavaIoTmpDir());
        System.out.println(SystemUtils.getUserDir());
        System.out.println(SystemUtils.getUserHome());
        System.out.println(SystemUtils.JAVA_VERSION);
        System.out.println(SystemUtils.OS_NAME);
        System.out.println(SystemUtils.USER_TIMEZONE);
    }

 

6.DateUtils和DateFormatUtils可以实现字符串转date与date转字符串,date比较先后问题

  DateUtils也可以判断是否是同一天等操作。

package zd.dms.test;

import java.text.ParseException;
import java.util.Date;

import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.DateUtils;

public class PlainTest {
    public static void main(String[] args) {
        // DateFormatUtils----date转字符串
        Date date = new Date();
        System.out.println(DateFormatUtils.format(date, "yyyy-MM-dd hh:mm:ss"));// 小写的是12小时制
        System.out.println(DateFormatUtils.format(date, "yyyy-MM-dd HH:mm:ss"));// 大写的HH是24小时制

        // DateUtils ---加减指定的天数(也可以加减秒、小时等操作)
        Date addDays = DateUtils.addDays(date, 2);
        System.out.println(DateFormatUtils.format(addDays, "yyyy-MM-dd HH:mm:ss"));
        Date addDays2 = DateUtils.addDays(date, -2);
        System.out.println(DateFormatUtils.format(addDays2, "yyyy-MM-dd HH:mm:ss"));

        // 原生日期判断日期先后顺序
        System.out.println(addDays2.after(addDays));
        System.out.println(addDays2.before(addDays));

        // DateUtils---字符串转date
        String strDate = "2018-11-01 19:23:44";
        try {
            Date parseDateStrictly = DateUtils.parseDateStrictly(strDate, "yyyy-MM-dd HH:mm:ss");
            Date parseDate = DateUtils.parseDate(strDate, "yyyy-MM-dd HH:mm:ss");
            System.out.println(parseDateStrictly);
            System.out.println(parseDate);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}

结果:

2018-11-02 07:53:50
2018-11-02 19:53:50
2018-11-04 19:53:50
2018-10-31 19:53:50
false
true
Thu Nov 01 19:23:44 CST 2018
Thu Nov 01 19:23:44 CST 2018

 

7.StopWatch提供秒表的计时,暂停等功能

package cn.xm.exam.test;

import org.apache.commons.lang.time.StopWatch;

public class test implements AInterface, BInterface {
    public static void main(String[] args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        try {
            Thread.sleep(5 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        stopWatch.stop();
        System.out.println(stopWatch.getStartTime());// 获取开始时间
        System.out.println(stopWatch.getTime());// 获取总的执行时间--单位是毫秒
    }
}

 结果:

1541754863180
5001

 

8.以Range结尾的类主要提供一些范围的操作,包括判断某些字符,数字等是否在这个范围以内

        IntRange intRange = new IntRange(1, 5);
        System.out.println(intRange.getMaximumInteger());
        System.out.println(intRange.getMinimumInteger());
        System.out.println(intRange.containsInteger(6));
        System.out.println(intRange.containsDouble(3));

结果:

5
1
false
true

 

9.ArrayUtils操作数组,功能强大,可以合并,判断是否包含等操作

package cn.xm.exam.test;

import org.apache.commons.lang.ArrayUtils;

public class test implements AInterface, BInterface {
    public static void main(String[] args) {
        int array[] = { 1, 5, 5, 7 };
        System.out.println(array);

        // 增加元素
        array = ArrayUtils.add(array, 9);
        System.out.println(ArrayUtils.toString(array));

        // 删除元素
        array = ArrayUtils.remove(array, 3);
        System.out.println(ArrayUtils.toString(array));

        // 反转数组
        ArrayUtils.reverse(array);
        System.out.println(ArrayUtils.toString(array));

        // 查询数组索引
        System.out.println(ArrayUtils.indexOf(array, 5));

        // 判断数组中是否包含指定值
        System.out.println(ArrayUtils.contains(array, 5));

        // 合并数组
        array = ArrayUtils.addAll(array, new int[] { 1, 5, 6 });
        System.out.println(ArrayUtils.toString(array));
    }
}

结果:

[I@3cf5b814
{1,5,5,7,9}
{1,5,5,9}
{9,5,5,1}
1
true
{9,5,5,1,1,5,6}

 

补充:ArrayUtils可以将包装类型的数组转变为基本类型的数组。

package cn.xm.exam.test;

import org.apache.commons.lang.ArrayUtils;

public class test {
    public static void main(String[] args) {
        Integer integer[] = new Integer[] { 0, 1, 2 };
        System.out.println(integer.getClass());

        int[] primitive = ArrayUtils.toPrimitive(integer);
        System.out.println(primitive.getClass());
    }
}

结果:

class [Ljava.lang.Integer;
class [I

看过JVM的知道上面第一个代表是包装类型一维数组,而且是Integer包装类。L代表引用类型,[代表一维。

          第二个代表是基本数据类型int类型的一维数组。

 

补充:ArrayUtils也可以判断数组是否为null或者数组大小是否为0

    /**
     * <p>Checks if an array of Objects is empty or <code>null</code>.</p>
     *
     * @param array  the array to test
     * @return <code>true</code> if the array is empty or <code>null</code>
     * @since 2.1
     */
    public static boolean isEmpty(Object[] array) {
        return array == null || array.length == 0;
    }

 

ArrayUtils结合java.lang.reflect.Array工具类,可以满足数组的基本操作,Array的方法如下:

 补充:ArrayUtils.remove()是根据下标移除,也可以移除元素:从该数组中删除第一次出现的指定元素,返回一个新的数组。

        int array[] = { 1, 5, 5, 7 };
        System.out.println(ArrayUtils.toString(array));

        // 删除元素
        array = ArrayUtils.removeElement(array, 5);
        System.out.println(ArrayUtils.toString(array));

结果:

{1,5,5,7}
{1,5,7}

上面是删除第一个出现的元素,如果需要删除所有的,可以用:

        int array[] = { 1, 5, 5, 7 };
        System.out.println(ArrayUtils.toString(array));

        // 删除元素
        while (ArrayUtils.contains(array, 5)) {
            array = ArrayUtils.removeElement(array, 5);
        }
        System.out.println(ArrayUtils.toString(array));

结果:

{1,5,5,7}
{1,7}

 

8.  反射工具类的使用

一个普通的java:

package cn.xm.exam.test.p1;

public class Person {
    private String name;

    public static void staticMet(String t) {
        System.out.println(t);
    }

    public Person(String name) {
        this.name = name;
    }

    public String call(String string) {
        System.out.println(name);
        System.out.println(string);
        return string;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "test [name=" + name + "]";
    }
}

 

反射工具类操作:

package cn.xm.exam.test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.apache.commons.lang.reflect.ConstructorUtils;
import org.apache.commons.lang.reflect.FieldUtils;
import org.apache.commons.lang.reflect.MethodUtils;

import cn.xm.exam.test.p1.Person;

public class test {

    public static void main(String[] args) throws InstantiationException, IllegalAccessException,
            IllegalArgumentException, InvocationTargetException, NoSuchMethodException {
        // ConstructorUtils工具类的使用
        Constructor accessibleConstructor = ConstructorUtils.getAccessibleConstructor(Person.class, String.class);
        Person newInstance = (Person) accessibleConstructor.newInstance("test");
        System.out.println(newInstance.getClass());
        System.out.println(newInstance);

        // MethodUtils的使用
        Method accessibleMethod = MethodUtils.getAccessibleMethod(Person.class, "call", String.class);
        Object invoke = accessibleMethod.invoke(newInstance, "参数");
        System.out.println(invoke);
        // 调用静态方法
        MethodUtils.invokeStaticMethod(Person.class, "staticMet", "静态方法");

        // FieldUtils 暴力获取私有变量(第三个参数表示是否强制获取)---反射方法修改元素的值
        Field field = FieldUtils.getField(Person.class, "name", true);
        field.setAccessible(true);
        System.out.println(field.getType());
        field.set(newInstance, "修改后的值");
        System.out.println(newInstance.getName());
    }
}

结果:

class cn.xm.exam.test.p1.Person
test [name=test]
test
参数
参数
静态方法
class java.lang.String
修改后的值

9.  EqualsBuilder 可以用于拼接多个条件进行equals比较

例如:

        EqualsBuilder equalsBuilder = new EqualsBuilder();
        Integer integer1 = new Integer(1);
        Integer integer2 = new Integer(1);

        String string1 = "111";
        String string2 = "111";
        equalsBuilder.append(integer1, integer2);
        equalsBuilder.append(string1, string2);
        System.out.println(equalsBuilder.isEquals());

结果:

true

 查看源码:(append的时候判断两个元素是否相等,如果equals已经等于false就直接返回)

    /**
     * <p>Test if two <code>Object</code>s are equal using their
     * <code>equals</code> method.</p>
     *
     * @param lhs  the left hand object
     * @param rhs  the right hand object
     * @return EqualsBuilder - used to chain calls.
     */
    public EqualsBuilder append(Object lhs, Object rhs) {
        if (isEquals == false) {
            return this;
        }
        if (lhs == rhs) {
            return this;
        }
        if (lhs == null || rhs == null) {
            this.setEquals(false);
            return this;
        }
        Class lhsClass = lhs.getClass();
        if (!lhsClass.isArray()) {
            // The simple case, not an array, just test the element
            isEquals = lhs.equals(rhs);
        } else if (lhs.getClass() != rhs.getClass()) {
            // Here when we compare different dimensions, for example: a boolean[][] to a boolean[] 
            this.setEquals(false);
        }
        // 'Switch' on type of array, to dispatch to the correct handler
        // This handles multi dimensional arrays of the same depth
        else if (lhs instanceof long[]) {
            append((long[]) lhs, (long[]) rhs);
        } else if (lhs instanceof int[]) {
            append((int[]) lhs, (int[]) rhs);
        } else if (lhs instanceof short[]) {
            append((short[]) lhs, (short[]) rhs);
        } else if (lhs instanceof char[]) {
            append((char[]) lhs, (char[]) rhs);
        } else if (lhs instanceof byte[]) {
            append((byte[]) lhs, (byte[]) rhs);
        } else if (lhs instanceof double[]) {
            append((double[]) lhs, (double[]) rhs);
        } else if (lhs instanceof float[]) {
            append((float[]) lhs, (float[]) rhs);
        } else if (lhs instanceof boolean[]) {
            append((boolean[]) lhs, (boolean[]) rhs);
        } else {
            // Not an array of primitives
            append((Object[]) lhs, (Object[]) rhs);
        }
        return this;
    }

补充:利用EqualsBuilder和HashCodeBuilder进行重写equals方法和hasCode方法:

package cn.qlq;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;

public class User implements Cloneable {

    private int age;
    private String name, sex;

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }

        if (obj == this) {
            return true;
        }

        if (!(obj instanceof User)) {
            return false;
        }

        final User tmpUser = (User) obj;
        return new EqualsBuilder().appendSuper(super.equals(obj)).append(name, tmpUser.getName()).isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder().append(name).toHashCode();
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "User [age=" + age + ", name=" + name + ", sex=" + sex + "]";
    }

}

 

10.  RandomStringUtils可用于生成随机数和随机字符串 

        // 第一个参数表示生成位数,第二个表示是否生成字母,第三个表示是否生成数字
        System.out.println(RandomStringUtils.random(15, true, false));

        // 长度、起始值、结束值、是否使用字母、是否生成数字
        System.out.println(RandomStringUtils.random(15, 5, 129, true, false));

        System.out.println(RandomStringUtils.random(22));

        // 从指定字符串随机生成
        System.out.println(RandomStringUtils.random(15, "abcdefgABCDEFG123456789"));

        // 从字母中抽取
        System.out.println(RandomStringUtils.randomAlphabetic(15));

        // 从数字抽取
        System.out.println(RandomStringUtils.randomNumeric(15));

        // ASCII between 32 and 126 =内部调用(random(count, 32, 127, false,false);)
        System.out.println(RandomStringUtils.randomAscii(15));

结果:

JDibosuEOUepHtO
LXrzlRaANphURKS
ဧ큵䳵᩠K훸쟌ᚪ惥㈤ꃣ嚶爆䴄毟鰯韭;䡶ꑉ凷訩
5F5D919d77fEEA9
oTmXgAbiZWFUDRc
843164814767664
p(_xsQIp/&<Jc$Z

 

11.RandomUtils可用于生成一定范围内整数、float、boolean等值。最新版的commons-lang包还可以提供上限、下限

    public static void main(String[] args) {
        List<Object> result = new ArrayList<>();
        for (int i = 0; i < 30; i++) {
            result.add(RandomUtils.nextInt());
        }
        System.out.println(StringUtils.join(result, ","));

        result = new ArrayList<>();
        for (int i = 0; i < 30; i++) {
            result.add(RandomUtils.nextInt(20));
        }
        System.out.println(StringUtils.join(result, ","));

        result = new ArrayList<>();
        for (int i = 0; i < 30; i++) {
            result.add(org.apache.commons.lang3.RandomUtils.nextInt(0, 10));
        }
        System.out.println(StringUtils.join(result, ","));

        result = new ArrayList<>();
        for (int i = 0; i < 30; i++) {
            result.add(org.apache.commons.lang3.RandomUtils.nextLong(5L, 6L));
        }
        System.out.println(StringUtils.join(result, ","));

        result = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            result.add(org.apache.commons.lang3.RandomUtils.nextFloat(0.5F, 0.6F));
        }
        System.out.println(StringUtils.join(result, ","));
    }

结果:

391357754,492392032,492524087,666171631,473008086,2089602614,1303315335,494646254,1863562131,182849529,207273461,1857831948,1197203156,864149196,956426242,1263147526,2070274937,931371426,478106765,838690870,723564037,100543521,319440652,1438255942,1495754097,1537242017,1161118057,534187007,957222284,553366099
2,7,5,10,3,1,19,1,19,11,7,13,10,14,9,2,10,14,1,9,8,1,1,8,3,13,9,18,4,6
2,5,7,9,5,1,3,6,7,7,3,5,3,7,3,6,8,4,2,9,8,3,6,5,9,7,1,9,9,4
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5
0.52966917,0.52869964,0.58883214,0.53288007,0.5808929,0.56509304,0.5022589,0.53946894,0.5280939,0.5391899

 

12. Validate工具类可以提供对参数的校验,不符合会抛出异常。可以用于参数校验

    public static void main(String[] args) {
        // 判断是否在指定范围内
        Validate.inclusiveBetween(0, 20, 15);

        // 判断是否不在指定范围内
        Validate.exclusiveBetween(0, 20, 35);

        // 判断表达式是否为true,不是抛出IllegalArgumentException异常
        Validate.isTrue(1 == 2, " 1不等于2");

        // 验证状态。 第一个表达式为true抛出非法状态IllegalStateException异常
        User user = null;
        Validate.validState(user != null, "user is null");

        // 验证非空,如果为空,会抛出NPE
        Validate.notNull(user, "用户为空");
    }

 

13.ToStringBuilder可以用于获取对象,可以理解为正确打印bean的属性值。并且接受第二个参数打印风格

        long[] array = { 1L, 2L };
        System.out.println(array);
        System.out.println(ToStringBuilder.reflectionToString(array));
        System.out.println(ToStringBuilder.reflectionToString(array, ToStringStyle.SHORT_PREFIX_STYLE));
        System.out.println(ToStringBuilder.reflectionToString(array, ToStringStyle.NO_CLASS_NAME_STYLE));

结果:

[J@53e25b76
[J@53e25b76[{1,2}]
long[][{1,2}]
[{1,2}]

 

14. SerializationUtils可以用于序列化和反序列化,也可以用于深复制

    public static void main(String[] args) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("key1", "value1");

        // 深复制
        HashMap<String, Object> clonedmap = (HashMap) SerializationUtils.clone(map);
        clonedmap.put("key2", "value2");

        System.out.println(map);
        System.out.println(clonedmap);

        // 序列化
        byte[] serialize = SerializationUtils.serialize(map);
        // 反序列化
        Object deserialize = SerializationUtils.deserialize(serialize);
        System.out.println(deserialize);
    }

结果:

{key1=value1}
{key1=value1, key2=value2}
{key1=value1}

 

查看深复制原理,也是基于流的深复制,源码如下:

    public static Object clone(Serializable object) {
        return deserialize(serialize(object));
    }

    public static byte[] serialize(Serializable obj) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
        serialize(obj, baos);
        return baos.toByteArray();
    }

    public static void serialize(Serializable obj, OutputStream outputStream) {
        if (outputStream == null) {
            throw new IllegalArgumentException("The OutputStream must not be null");
        }
        ObjectOutputStream out = null;
        try {
            // stream closed in the finally
            out = new ObjectOutputStream(outputStream);
            out.writeObject(obj);
            
        } catch (IOException ex) {
            throw new SerializationException(ex);
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException ex) {
                // ignore close exception
            }
        }
    }

    public static Object deserialize(byte[] objectData) {
        if (objectData == null) {
            throw new IllegalArgumentException("The byte[] must not be null");
        }
        ByteArrayInputStream bais = new ByteArrayInputStream(objectData);
        return deserialize(bais);
    }

 

========commons-collections包中的常用的工具类==

1. CollectionUtils工具类用于操作集合,  isEmpty () 方法最有用   (commons-collections包中的类)

package cn.xm.exam.test;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.commons.collections.CollectionUtils;

public class test {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        list.add("str1");
        list.add("str2");

        List<String> list1 = new ArrayList<String>();
        list1.add("str1");
        list1.add("str21");

        // 判断是否有任何一个相同的元素
        System.out.println(CollectionUtils.containsAny(list, list1));

        // 求并集(自动去重)
        List<String> list3 = (List<String>) CollectionUtils.union(list, list1);
        System.out.println(list3);

        // 求交集(两个集合中都有的元素)
        Collection intersection = CollectionUtils.intersection(list, list1);
        System.out.println("intersection->" + intersection);

        // 求差集(并集去掉交集,也就是list中有list1中没有,list1中有list中没有)
        Collection intersection1 = CollectionUtils.disjunction(list, list1);
        System.out.println("intersection1->" + intersection1);

        // 获取一个同步的集合
        Collection synchronizedCollection = CollectionUtils.synchronizedCollection(list);

        // 验证集合是否为null或者集合的大小是否为0,同理有isNouEmpty方法
        List list4 = null;
        List list5 = new ArrayList<>();
        System.out.println(CollectionUtils.isEmpty(list4));
        System.out.println(CollectionUtils.isEmpty(list5));
    }
}

结果:

true
[str2, str21, str1]
intersection->[str1]
intersection1->[str2, str21]
true
true

 

补充:此工具类还可以向集合中加数组元素

        List<String> list = new ArrayList<>();
        String s[] = { "1", "2" };
        CollectionUtils.addAll(list, s);
        list.add("3");
        System.out.println(list);

结果:

[1, 2, 3]

 

2.   MapUtils工具类,isEmpty最有用(commons-collections包中的类)

  可以用于map判断null和size为0,也可以直接获取map中的值为指定类型,没有的返回null

package cn.xm.exam.test;

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.NumberUtils;

import ognl.MapElementsAccessor;

public class test {
    public static void main(String[] args) {
        Map map = null;
        Map map2 = new HashMap();
        Map map3 = new HashMap<>();
        map3.put("xxx", "xxx");
        // 检验为empty可以验证null和size为0的情况
        System.out.println(MapUtils.isEmpty(map));
        System.out.println(MapUtils.isEmpty(map2));
        System.out.println(MapUtils.isEmpty(map3));

        String string = MapUtils.getString(map3, "eee");
        String string2 = MapUtils.getString(map3, "xxx");
        Integer integer = MapUtils.getInteger(map3, "xxx");
        System.out.println("string->" + string);
        System.out.println("string2->" + string2);
        System.out.println("integer->" + integer);
        System.out.println(integer == null);
    }
}

结果:

true
true
false
INFO: Exception: java.text.ParseException: Unparseable number: "xxx"
string->null
string2->xxx
integer->null
true

 

 MapUtils.isEmpty根踪源码:

    public static boolean isEmpty(Map map) {
        return (map == null || map.isEmpty());
    }

 

map.isEmpty()代码查看hashmap:
    public boolean isEmpty() {
        return size == 0;
    }

 

补充:MapUtils也可以获取值作为String,获取不到取默认值:

        //获取字符串,如果获取不到可以返回一个默认值
        String string3 = MapUtils.getString(map3, "eee","没有值");

 

 查看源码:

    /**
     *  Looks up the given key in the given map, converting the result into
     *  a string, using the default value if the the conversion fails.
     *
     *  @param map  the map whose value to look up
     *  @param key  the key of the value to look up in that map
     *  @param defaultValue  what to return if the value is null or if the
     *     conversion fails
     *  @return  the value in the map as a string, or defaultValue if the 
     *    original value is null, the map is null or the string conversion
     *    fails
     */
    public static String getString( Map map, Object key, String defaultValue ) {
        String answer = getString( map, key );
        if ( answer == null ) {
            answer = defaultValue;
        }
        return answer;
    }

 

 

补充:网上总结的常见工具类的使用

一. org.apache.commons.io.IOUtils

closeQuietly:关闭一个IO流、socket、或者selector且不抛出异常,通常放在finally块
toString:转换IO流、 Uri、 byte[]为String
copy:IO流数据复制,从输入流写到输出流中,最大支持2GB
toByteArray:从输入流、URI获取byte[]
write:把字节. 字符等写入输出流
toInputStream:把字符转换为输入流
readLines:从输入流中读取多行数据,返回List<String>
copyLarge:同copy,支持2GB以上数据的复制
lineIterator:从输入流返回一个迭代器,根据参数要求读取的数据量,全部读取,如果数据不够,则失败

 

二. org.apache.commons.io.FileUtils

deleteDirectory:删除文件夹
readFileToString:以字符形式读取文件内容
deleteQueitly:删除文件或文件夹且不会抛出异常
copyFile:复制文件
writeStringToFile:把字符写到目标文件,如果文件不存在,则创建
forceMkdir:强制创建文件夹,如果该文件夹父级目录不存在,则创建父级
write:把字符写到指定文件中
listFiles:列举某个目录下的文件(根据过滤器)
copyDirectory:复制文件夹
forceDelete:强制删除文件

 

三. org.apache.commons.lang.StringUtils

isBlank:字符串是否为空 (trim后判断)
isEmpty:字符串是否为空 (不trim并判断)
equals:字符串是否相等
join:合并数组为单一字符串,可传分隔符
split:分割字符串
EMPTY:返回空字符串
trimToNull:trim后为空字符串则转换为null
replace:替换字符串

 

四. org.apache.http.util.EntityUtils

toString:把Entity转换为字符串
consume:确保Entity中的内容全部被消费。可以看到源码里又一次消费了Entity的内容,假如用户没有消费,那调用Entity时候将会把它消费掉
toByteArray:把Entity转换为字节流
consumeQuietly:和consume一样,但不抛异常
getContentCharset:获取内容的编码

 

五. org.apache.commons.lang3.StringUtils

isBlank:字符串是否为空 (trim后判断)
isEmpty:字符串是否为空 (不trim并判断)
equals:字符串是否相等
join:合并数组为单一字符串,可传分隔符
split:分割字符串
EMPTY:返回空字符串
replace:替换字符串
capitalize:首字符大写

 

六. org.apache.commons.io.FilenameUtils

getExtension:返回文件后缀名
getBaseName:返回文件名,不包含后缀名
getName:返回文件全名
concat:按命令行风格组合文件路径(详见方法注释)
removeExtension:删除后缀名
normalize:使路径正常化
wildcardMatch:匹配通配符
seperatorToUnix:路径分隔符改成unix系统格式的,即/
getFullPath:获取文件路径,不包括文件名
isExtension:检查文件后缀名是不是传入参数(List<String>)中的一个

 

七. org.springframework.util.StringUtils

hasText:检查字符串中是否包含文本
hasLength:检测字符串是否长度大于0
isEmpty:检测字符串是否为空(若传入为对象,则判断对象是否为null)
commaDelimitedStringToArray:逗号分隔的String转换为数组
collectionToDelimitedString:把集合转为CSV格式字符串
replace 替换字符串
delimitedListToStringArray:相当于split
uncapitalize:首字母小写
collectionToDelimitedCommaString:把集合转为CSV格式字符串
tokenizeToStringArray:和split基本一样,但能自动去掉空白的单词

 

八. org.apache.commons.lang.ArrayUtils

contains:是否包含某字符串
addAll:添加整个数组
clone:克隆一个数组
isEmpty:是否空数组
add:向数组添加元素
subarray:截取数组
indexOf:查找某个元素的下标
isEquals:比较数组是否相等
toObject:基础类型数据数组转换为对应的Object数组

 

九. org.apache.commons.lang.StringEscapeUtils

参考十五:
org.apache.commons.lang3.StringEscapeUtils


十. org.apache.http.client.utils.URLEncodedUtils

format:格式化参数,返回一个HTTP POST或者HTTP PUT可用application/x-www-form-urlencoded字符串
parse:把String或者URI等转换为List<NameValuePair>

 

十一. org.apache.commons.codec.digest.DigestUtils

md5Hex:MD5加密,返回32位字符串
sha1Hex:SHA-1加密
sha256Hex:SHA-256加密
sha512Hex:SHA-512加密
md5:MD5加密,返回16位字符串

 

十二. org.apache.commons.collections.CollectionUtils

isEmpty:是否为空
select:根据条件筛选集合元素
transform:根据指定方法处理集合元素,类似List的map()
filter:过滤元素,雷瑟List的filter()
find:基本和select一样
collect:和transform 差不多一样,但是返回新数组
forAllDo:调用每个元素的指定方法
isEqualCollection:判断两个集合是否一致

 

十三. org.apache.commons.lang3.ArrayUtils

contains:是否包含某个字符串
addAll:添加整个数组
clone:克隆一个数组
isEmpty:是否空数组
add:向数组添加元素
subarray:截取数组
indexOf:查找某个元素的下标
isEquals:比较数组是否相等
toObject:基础类型数据数组转换为对应的Object数组

 

十四. org.apache.commons.beanutils.PropertyUtils

getProperty:获取对象属性值
setProperty:设置对象属性值
getPropertyDiscriptor:获取属性描述器
isReadable:检查属性是否可访问
copyProperties:复制属性值,从一个对象到另一个对象
getPropertyDiscriptors:获取所有属性描述器
isWriteable:检查属性是否可写
getPropertyType:获取对象属性类型

 

十五. org.apache.commons.lang3.StringEscapeUtils

unescapeHtml4:转义html
escapeHtml4:反转义html
escapeXml:转义xml
unescapeXml:反转义xml
escapeJava:转义unicode编码
escapeEcmaScript:转义EcmaScript字符
unescapeJava:反转义unicode编码
escapeJson:转义json字符
escapeXml10:转义Xml10
这个现在已经废弃了,建议使用commons-text包里面的方法。

 

十六. org.apache.commons.beanutils.BeanUtils

copyPeoperties:复制属性值,从一个对象到另一个对象
getProperty:获取对象属性值
setProperty:设置对象属性值
populate:根据Map给属性复制
copyPeoperty:复制单个值,从一个对象到另一个对象
cloneBean:克隆bean实例


  现在你只要了解了以上16种最流行的工具类方法,你就不必要再自己写工具类了,不必重复造轮子。大部分工具类方法通过其名字就能明白其用途,如果不清楚的,可以看下别人是怎么用的,或者去网上查询其用法。
  另外,工具类,根据阿里开发手册,包名如果要使用util不能带s,工具类命名为 XxxUtils(参考spring命名)

补充:其实google的guava工具包也提供了许多工具类,包括缓存以及io、集合、并发操作相关的工具类,也有限流相关的工具类

1.简单的创建一个带元素的集合

        List<Object> objects = Lists.newArrayList();
        System.out.println("===1");

        ArrayList<String> strings = Lists.newArrayList("1", "2", "3");
        System.out.println(strings);

2.限流器的简单使用(并发包下面实用的工具类)

package guava;

import com.google.common.util.concurrent.RateLimiter;

public class Test {

    public static void main(String[] args) throws InterruptedException {
        // 默认每秒钟一个
        RateLimiter rateLimiter = RateLimiter.create(1);
        System.out.println(rateLimiter.tryAcquire());
        System.out.println(rateLimiter.tryAcquire());

        Thread.sleep(1 * 1000);
        System.out.println(rateLimiter.tryAcquire());
    }
}

结果:

true
false
true

这里补充下,主要的限流算法有两种:

  漏桶算法(Leaky Bucket):水(请求)先进入到漏桶里,漏桶以一定的速度出水(接口有响应速率),当水流入速度过大会直接溢出(访问频率超过接口响应速率),然后就拒绝请求,可以看出漏桶算法能强行限制数据的传输速率.

  令牌桶算法(Token Bucket)和 Leaky Bucket 效果一样但方向相反的算法,更加容易理解.随着时间流逝,系统会按恒定1/QPS时间间隔(如果QPS=100,则间隔是10ms)往桶里加入Token(想象和漏洞漏水相反,有个水龙头在不断的加水),如果桶已经满了就不再加了.新请求来临时,会各自拿走一个Token,如果没有Token可拿了就阻塞或者拒绝服务.

Guava RateLimiter提供了令牌桶算法实现:平滑突发限流(SmoothBursty)和平滑预热限流(SmoothWarmingUp)实现。

(1) 平滑突发限流:

    public static void main(String[] args) throws InterruptedException {
        //QPS = 3,每秒允许3个请求
        RateLimiter limiter = RateLimiter.create(3);
        //limiter.acquire() 返回获取token的耗时,以秒为单位
        log.info("" + limiter.acquire());
        log.info("" + limiter.acquire());
        log.info("" + limiter.acquire());
        log.info("" + limiter.acquire());
    }

结果:

2021-02-03 22:08:46.322 | cmdb - INFO | main | com.xm.ggn.test.guava.GuavaTest | line:18 - 0.0
2021-02-03 22:08:46.654 | cmdb - INFO | main | com.xm.ggn.test.guava.GuavaTest | line:19 - 0.311978
2021-02-03 22:08:46.978 | cmdb - INFO | main | com.xm.ggn.test.guava.GuavaTest | line:20 - 0.32445
2021-02-03 22:08:47.313 | cmdb - INFO | main | com.xm.ggn.test.guava.GuavaTest | line:21 - 0.333521

(2) 平滑突发限流测试2:

    public static void main(String[] args) throws InterruptedException {
        // QPS = 3,每秒允许3个请求
        RateLimiter limiter = RateLimiter.create(3);
        log.info("limiter: " + limiter.getClass());
        // 一次性消费3个令牌
        log.info("" + limiter.acquire(3));
        // limiter.acquire(6)将等待差不多1秒桶中才能有令牌
        log.info("" + limiter.acquire(6));
        // 经过2s才可以拿到
        log.info("" + limiter.acquire(9));
        // 经过3s才拿到
        log.info("" + limiter.acquire(1));
    }

结果:

2021-02-03 22:16:06.480 | cmdb - INFO | main | com.xm.ggn.test.guava.GuavaTest | line:17 - limiter: class com.google.common.util.concurrent.SmoothRateLimiter$SmoothBursty
2021-02-03 22:16:06.501 | cmdb - INFO | main | com.xm.ggn.test.guava.GuavaTest | line:19 - 0.0
2021-02-03 22:16:07.482 | cmdb - INFO | main | com.xm.ggn.test.guava.GuavaTest | line:21 - 0.972153
2021-02-03 22:16:09.542 | cmdb - INFO | main | com.xm.ggn.test.guava.GuavaTest | line:22 - 1.99175
2021-02-03 22:16:12.474 | cmdb - INFO | main | com.xm.ggn.test.guava.GuavaTest | line:23 - 2.931438

(3)平滑突发限流测试3:

    public static void main(String[] args) throws InterruptedException {
        // QPS = 3,每秒允许3个请求
        RateLimiter limiter = RateLimiter.create(3);
        log.info("limiter: " + limiter.getClass());
        // 一次性消费3个令牌, 造成同一s内其他访问不到
        log.info("" + limiter.tryAcquire(3));

        log.info("" + limiter.tryAcquire(1));
        log.info("" + limiter.tryAcquire(1));
        log.info("" + limiter.tryAcquire(1));
    }

结果:

2021-02-03 22:20:12.608 | cmdb - INFO | main | com.xm.ggn.test.guava.GuavaTest | line:17 - limiter: class com.google.common.util.concurrent.SmoothRateLimiter$SmoothBursty
2021-02-03 22:20:12.622 | cmdb - INFO | main | com.xm.ggn.test.guava.GuavaTest | line:19 - true
2021-02-03 22:20:12.623 | cmdb - INFO | main | com.xm.ggn.test.guava.GuavaTest | line:20 - false
2021-02-03 22:20:12.623 | cmdb - INFO | main | com.xm.ggn.test.guava.GuavaTest | line:21 - false
2021-02-03 22:20:12.623 | cmdb - INFO | main | com.xm.ggn.test.guava.GuavaTest | line:22 - false

从这里看出来,acquire(long)  会等令牌桶内有足够令牌后取取令牌,返回值是获取令牌需要的时间。

      tryAcquire(1) 是判断此时容器内是否有能获取到令牌,不阻塞,返回值是boolean

(4) SmoothWarmingUp 平滑预热限流测试

        //permitsPerSecond:每秒新增的令牌数  warmupPeriod:从冷启动速率过渡到平均速率的时间间隔
        //系统冷启动后慢慢的趋于平均固定速率(即刚开始速率慢一些,然后慢慢趋于我们设置的固定速率)
        RateLimiter limiter = RateLimiter.create(10, 1000, TimeUnit.MILLISECONDS);
        for (int i = 0; i < 10; i++) {
            //获取一个令牌
            System.out.println(limiter.acquire(1));
        }

结果:

0.0

0.275105

0.23306

0.200281

0.160285

0.120186

0.100082

0.100037

0.099945

0.099792

(5). RateLimiter 也可以指定尝试获取的时间和单位, limiter 会自动计算,如果在指定时间获取不到会立即返回false

public static void main(String[] args) throws InterruptedException {
        // 每秒钟2个
        RateLimiter rateLimiter = RateLimiter.create(1);
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                while (true) {
                    boolean b = rateLimiter.tryAcquire(1, 2, TimeUnit.SECONDS);
                    PrintUtils.printWithTime(String.valueOf(b));
                    if (!b) {
                        try {
                            Thread.sleep(1 * 1000);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            }).start();
        }
    }

结果:

Thread-1    15:40:44    true
Thread-1    15:40:44    false
Thread-3    15:40:44    true
Thread-1    15:40:45    false
Thread-2    15:40:45    true
Thread-1    15:40:46    false
Thread-3    15:40:46    true
Thread-1    15:40:47    false
Thread-2    15:40:47    true
Thread-1    15:40:48    false
Thread-3    15:40:48    true
Thread-1    15:40:49    false

3. Join连接器

    @Test
    public void joinerListTest() {
        List<String> lists = Lists.newArrayList("1", "2", "3", null, "4", "5");
        // Joiner 连接器
        // 跳过null元素
        System.out.println(Joiner.on("--").skipNulls().join(lists));
        // 给空值一个默认值
        System.out.println(Joiner.on("--").useForNull("dv").join(lists));
        // 如果直接join,有null元素会报NPE
//        System.out.println(Joiner.on(",").join(lists));

        // 连接Map
        Map<Integer, String> maps = Maps.newHashMap();
        maps.put(1, "嘻嘻");
        maps.put(2, "哈哈");
        String result = Joiner.on(",").withKeyValueSeparator(":").join(maps);
        System.out.println(result);
        System.out.println(maps);
    }

结果:

1--2--3--4--5
1--2--3--dv--4--5
1:嘻嘻,2:哈哈
{1=嘻嘻, 2=哈哈}

4. Splitter 拆分器

    @Test
    public void splitterListTest() {
        String test = " 1,2,3,, ,4    ";
        List<String> lists = Splitter.on(",").splitToList(test);
        System.out.println(lists);

        // 拆分去除前后空格
        List<String> lists2 = Splitter.on(",").trimResults().splitToList(test);
        System.out.println(lists2);

        // 去除拆分出来空的字符串
        List<String> lists3 = Splitter.on(",").omitEmptyStrings().splitToList(test);
        System.out.println(lists3);
    }

结果:

[ 1, 2, 3, , , 4 ]
[1, 2, 3, , , 4]
[ 1, 2, 3, , 4 ]

5.  Multimap 的用法--存元素自动存入集合

        Multimap<String,User> bMultimap = ArrayListMultimap.create();
        bMultimap.put("1", user11);
        bMultimap.put("1", user11);
        bMultimap.put("2", user21);
        System.out.println(bMultimap);

结果:

{1=[User(username=user11, deptId=1), User(username=user11, deptId=1)], 2=[User(username=user21, deptId=2)]}

6. Guava 缓存工具类的使用

package guava;

import com.google.common.cache.*;

import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;

/**
 * 1. 基于存活时间的缓存清除策略
 * expireAfterWrite 写缓存后多久过期
 * expireAfterAccess 读写缓存后多久过期
 * 2. 基于容量的清除策略
 * 通过CacheBuilder.maximumSize(long)方法可以设置Cache的最大容量数,当缓存数量达到或接近该最大值时,Cache将清除掉那些最近最少使用的缓存
 * 3. 基于权重的清除 策略
 * 使用CacheBuilder.weigher(Weigher)指定一个权重函数,并且用CacheBuilder.maximumWeight(long)指定最大总重。如每一项缓存所占据的内存空间大小都不一样,可以看作它们有不同的“权重”(weights),作为执行清除策略时优化回收的对象
 * 4. 手动清除
 * 清除单个key:Cache.invalidate(key)
 * 批量清除key:Cache.invalidateAll(keys)
 * 清除所有缓存项:Cache.invalidateAll(
 */
public class CacheTest {

    public static void main(String[] args) throws Exception {
        loadingCacheTest();
    }

    private static void localManualCache() throws InterruptedException {
        // LocalManualCache 用法
        Cache<Integer, Integer> numCache = CacheBuilder.newBuilder()
                .expireAfterWrite(5, TimeUnit.MINUTES)
                .build();

        System.out.println(numCache.getIfPresent(1));
        Thread.sleep(1000);
        System.out.println(numCache.getIfPresent(1));
        Thread.sleep(1000);
        numCache.put(1, 5);
        System.out.println(numCache.getIfPresent(1));
        // console: null null 5
    }

    private static void loadingCacheTest() throws java.util.concurrent.ExecutionException, InterruptedException {
        //    使用自定义ClassLoader加载数据,置入内存中。从LoadingCache中获取数据时,若数据存在则直接返回;若数据不存在,则根据ClassLoader的load方法加载数据至内存,然后返回该数据. put方法不受影响
        LoadingCache<Integer, Integer> numCache = CacheBuilder.newBuilder().
                expireAfterWrite(5L, TimeUnit.MINUTES).
                maximumSize(5000L).
                // 添加删除key 的监听器(手动put 的时候覆盖key 也会触发监听器)
                 removalListener(new RemovalListener<Integer, Integer>() {
                    @Override
                    public void onRemoval(RemovalNotification<Integer, Integer> removalNotification) {
                        System.out.println("删除key: " + removalNotification.getKey() + ", value: " + removalNotification.getValue());
                    }
                }).build(new CacheLoader<Integer, Integer>() {
                    @Override
                    public Integer load(Integer key) throws Exception {
                        System.out.println("no cache");
                        return key * 5;
                    }
                });

        System.out.println(numCache.get(1));
        Thread.sleep(1000);
        System.out.println(numCache.get(1));
        Thread.sleep(1000);
        numCache.put(1, 6);
        System.out.println(numCache.get(1));

        // 上面 console: no cache/ 5 / 5 / 删除key: 1, value: 5/ 6

        numCache.invalidate(1); // 单个清除
        System.out.println(numCache.get(3));
        ConcurrentMap<Integer, Integer> integerIntegerConcurrentMap = numCache.asMap(); // 转为map
        System.out.println(integerIntegerConcurrentMap);
    }
}

补充:guava 提供了刷新的机制

  注意不是后面有定时任务去获取,而是到达refresh 时间之后,第一次请求进来之后会去load。 

(1). 例子一:异步获取

package org.example;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class GuavaCacheTest {

    private final static long EXPIRE = 6;

    private final static long REFRESH = 3;

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        LoadingCache<String, String> build = CacheBuilder.newBuilder()
                // 我们在构建缓存时可以为缓存设置一个合理大小初始容量,由于Guava的缓存使用了分离锁的机制,扩容的代价非常昂贵。所以合理的初始容量能够减少缓存容器的扩容次数。
                .initialCapacity(40)
                // Guava提供了设置并发级别的api,使得缓存支持并发的写入和读取。同ConcurrentHashMap类似Guava cache的并发也是通过分离锁实现。在一般情况下,将并发级别设置为服务器cpu核心数是一个比较不错的选择。
                .concurrencyLevel(5)
                // 过期时间。 写入后指定时间过期
                .expireAfterWrite(EXPIRE, TimeUnit.SECONDS)
                // 在进行缓存定时刷新时,我们需要指定缓存的刷新间隔,和一个用来加载缓存的CacheLoader,当达到刷新时间间隔后,下一次获取缓存时,会调用CacheLoader的load方法刷新缓存。例如构建个刷新频率为10分钟的缓存:
                // 需要注意不是定时任务去获取,而是达到时间之后下次拿才会load
                // 比如说我们的过期时间是6s, 刷新时间是3s。 当一个key前3s来拿不会load;第3之后s过来拿会先返回目前存在的,然后异步load之后去更新
                .refreshAfterWrite(REFRESH, TimeUnit.SECONDS)
                .build(CacheLoader.asyncReloading(
                        new CacheLoader<String, String>() {
                            private int num = 0;

                            @Override
                            public String load(String key) throws Exception {
                                PrintUtils.printWithTime("load\t" + key);
                                // 模拟获取需要1 s
                                Thread.sleep(1 * 1000);
//                        // 缓存加载逻辑
                                return key + (num++);
                            }
                        }, Executors.newFixedThreadPool(10)));

        // 第一次加载
        String s1 = build.get("1");
        PrintUtils.printWithTime("s1:\t" + s1);
        PrintUtils.printWithTime("s10:\t" + build.get("1"));

        Thread.sleep(4 * 1000);
        String s2 = build.get("1");
        PrintUtils.printWithTime("s2:\t" + s2);

        Thread.sleep(1 * 1000);
        PrintUtils.printWithTime("s20:\t" + build.get("1"));
    }
}

结果:

main    20:44:02    load    1
main    20:44:03    s1:    10
main    20:44:03    s10:    10
pool-1-thread-1    20:44:08    load    1
main    20:44:08    s2:    10
main    20:44:09    s20:    11

(2). 例子二: 同步获取

package org.example;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class GuavaCacheTest {

    private final static long EXPIRE = 6;

    private final static long REFRESH = 3;

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        LoadingCache<String, String> build = CacheBuilder.newBuilder()
                // 我们在构建缓存时可以为缓存设置一个合理大小初始容量,由于Guava的缓存使用了分离锁的机制,扩容的代价非常昂贵。所以合理的初始容量能够减少缓存容器的扩容次数。
                .initialCapacity(40)
                // Guava提供了设置并发级别的api,使得缓存支持并发的写入和读取。同ConcurrentHashMap类似Guava cache的并发也是通过分离锁实现。在一般情况下,将并发级别设置为服务器cpu核心数是一个比较不错的选择。
                .concurrencyLevel(5)
                // 过期时间。 写入后指定时间过期
                .expireAfterWrite(EXPIRE, TimeUnit.SECONDS)
                // 在进行缓存定时刷新时,我们需要指定缓存的刷新间隔,和一个用来加载缓存的CacheLoader,当达到刷新时间间隔后,下一次获取缓存时,会调用CacheLoader的load方法刷新缓存。例如构建个刷新频率为10分钟的缓存:
                // 需要注意不是定时任务去获取,而是达到时间之后下次拿才会load
                // 比如说我们的过期时间是6s, 刷新时间是3s。 当一个key前3s来拿不会load;第3之后s过来拿会先同步去拿
                .refreshAfterWrite(REFRESH, TimeUnit.SECONDS)
                .build(new CacheLoader<String, String>() {
                    private int num = 0;

                    @Override
                    public String load(String key) throws Exception {
                        PrintUtils.printWithTime("load\t" + key);
                        // 模拟获取需要1 s
                        Thread.sleep(1 * 1000);
//                        // 缓存加载逻辑
                        return key + (num++);
                    }
                });

        // 第一次加载
        String s1 = build.get("1");
        PrintUtils.printWithTime("s1:\t" + s1);
        PrintUtils.printWithTime("s10:\t" + build.get("1"));

        Thread.sleep(4 * 1000);
        String s2 = build.get("1");
        PrintUtils.printWithTime("s2:\t" + s2);

        Thread.sleep(1 * 1000);
        PrintUtils.printWithTime("s20:\t" + build.get("1"));
    }
}

结果:

main    20:47:19    load    1
main    20:47:21    s1:    10
main    20:47:21    s10:    10
main    20:47:25    load    1
main    20:47:26    s2:    11
main    20:47:27    s20:    11

7. Guava 提供的布隆过滤器

    public static void main(String[] args) throws Exception {
        int total = 1000000; // 总数量
        BloomFilter<CharSequence> bf =
                BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), total);
        // 初始化 1000000 条数据到过滤器中
        for (int i = 0; i < total; i++) {
            bf.put("" + i);
        }
        // 判断值是否存在过滤器中
        int count = 0;
        for (int i = 0; i < total + 10000; i++) {
            if (bf.mightContain("" + i)) {
                count++;
            }
        }
        System.out.println("已匹配数量 " + count);
    }

8. Lists 可以拆分大集合为多个小集合

    public static void main(String[] args) {
        ArrayList<Integer> integers2 = Lists.newArrayList(11, 21, 31);
        List<List<Integer>> partition = Lists.partition(integers2, 2);
        System.out.println(partition);
    }

结果:

[[11, 21], [31]]

9. 线程工具类

    ThreadFactory build = new ThreadFactoryBuilder().setNameFormat("my-thread-%d").build();
        for (int i = 0; i < 100; i++) {
            System.out.println(build.newThread(new Runnable() {
                @Override
                public void run() {
                }
            }).getName());
        }

 

 结果:

my-thread-0
my-thread-1
my-thread-2
..

 10. 关于event bus 消息总线使用

使用说明:

(1). 只有通过 @Subscribe 注解才会被注册进EventBus

(2). 监听方法只能有1个参数,当有多个监听方法时,参数类型一样并行消费;不同参类型对应不对消费事件

(3). 单线程中处理

测试:

1. 主要类:

package google;

import com.google.common.eventbus.EventBus;

public class EventBusCenter {

    private static EventBus eventBus = new EventBus();

    private EventBusCenter() {
    }

    public static EventBus getInstance(){
        return eventBus;
    }

    /**注册监听*/
    public static void register(Object listener){
        eventBus.register(listener);
    }

    /**取消注册*/
    public static void unregister(Object listener){
        eventBus.unregister(listener);
    }

    /**发布事件*/
    public static void post(Object event){
        eventBus.post(event);
    }

}


package google;

import com.google.common.eventbus.Subscribe;
import org.example.PrintUtils;

public class OrderSuccessListener {

    @Subscribe
    public void orderSubscribe(OrderInfo event) {
        // 订单业务监听
        PrintUtils.printWithTime("订单支付成功处理事件: " + event);
    }

    @Subscribe
    public void ortherSubscribe(OrderInfo event) {
        // 其它业务监听
        PrintUtils.printWithTime("订单支付成功其它处理事件: " + event);
    }

}


package google;

import com.google.common.eventbus.Subscribe;
import org.example.PrintUtils;

public class OrderFailListener {

    @Subscribe
    public void orderSubscribe(OrderFailInfo event) {
        // 订单业务监听
        PrintUtils.printWithTime("订单支付失败处理事件: " + event);
    }

    @Subscribe
    public void ortherSubscribe(String event) {
        // 其它业务监听
        PrintUtils.printWithTime("订单支付失败其它处理事件: " + event);
    }
}


package google;

import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class OrderInfo {

    private long orderId;

    private String orderName;
}


package google;


import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class OrderFailInfo {

    private long orderId;

    private String orderName;

    private String msg;
}

2. 测试代码:

package google;

import org.example.PrintUtils;

public class Client {

    public static void main(String[] args) {
        OrderSuccessListener successListener = new OrderSuccessListener();
        OrderFailListener failListener = new OrderFailListener();
        EventBusCenter.register(successListener);
        EventBusCenter.register(failListener);

        OrderInfo order = OrderInfo.builder().orderId(111L).orderName("订单名称(success)xxx").build();
        PrintUtils.printWithTime("=====支付成功=======");
        EventBusCenter.post(order);

        PrintUtils.printWithTime("=====支付失败=======");
        OrderFailInfo orderFail = OrderFailInfo.builder().orderId(111L).orderName("订单名称(fail)xxx").msg("支付失败").build();
        EventBusCenter.post(orderFail);
        PrintUtils.printWithTime("=====记录支付失败原因=======");
        EventBusCenter.post("用户取消订单");
    }
}

/**
main    19:52:05    =====支付成功=======
main    19:52:05    订单支付成功处理事件: OrderInfo(orderId=111, orderName=订单名称(success)xxx)
main    19:52:05    订单支付成功其它处理事件: OrderInfo(orderId=111, orderName=订单名称(success)xxx)
main    19:52:05    =====支付失败=======
main    19:52:05    订单支付失败处理事件: OrderFailInfo(orderId=111, orderName=订单名称(fail)xxx, msg=支付失败)
main    19:52:05    =====记录支付失败原因=======
main    19:52:05    订单支付失败其它处理事件: 用户取消订单
*/

  

补充:关于在cloud分布式环境中BO、VO、DTO之间属性的拷贝可以使用Spring自带的BeanUtils,也可以使用dozermapper

1.关于Spring自带的BeanUtils的用法

        User user = new User();
        user.setUserName("zhangsan");
        user.setFullname("张三");
        Address address = new Address();
        address.setAddress("这是什么地址");
        address.setCodes(Lists.newArrayList("1", "2", "3"));
        user.setAddresses(Lists.newArrayList(address));
        user.setAddress(address);

        UserBO userBO = new UserBO();
        BeanUtils.copyProperties(user, userBO);
        System.out.println("====123456===");
        System.out.println(userBO);

里面是使用内省来操作的,根据变量的名称以及类型进行拷贝

自己扩展的拷贝集合等方法:

package cn.qz.template.util;

import org.springframework.beans.BeanUtils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 乔利强
 * @date 2022/3/11 14:45
 * @description 基于Spring的BeanUtils 扩展的转换集合的方法, 下面的方法都是浅拷贝。 包括父类 BeanUtils 拷贝相关方法也是浅拷贝
 */
public class CustomBeanUtils extends BeanUtils {

    private CustomBeanUtils() {
    }

    public static <T> T copyToBean(Object bean, Class<T> targetType) {
        return copyToBean(bean, targetType, null);
    }

    /**
     * 拷贝单个对象
     *
     * @param bean
     * @param targetType
     * @param <T>
     * @return
     */
    public static <T> T copyToBean(Object bean, Class<T> targetType, String... ignoreProperties) {
        T target = instantiateClass(targetType);
        copyProperties(bean, target, ignoreProperties);
        return target;
    }

    public static <T> List<T> copyToList(Collection<?> collection, Class<T> targetType) {
        return copyToList(collection, targetType, null);
    }

    /**
     * 拷贝集合
     *
     * @param collection
     * @param targetType
     * @param ignoreProperties
     * @param <T>
     * @return
     */
    public static <T> List<T> copyToList(Collection<?> collection, Class<T> targetType, String... ignoreProperties) {
        if (null == collection || collection.isEmpty()) {
            return new ArrayList<>(0);
        }

        return collection.stream().map((source) -> {
            final T target = instantiateClass(targetType);
            copyProperties(source, target, ignoreProperties);
            return target;
        }).collect(Collectors.toList());
    }

}

2.使用dozer

  dozer是git上一个项目,git地址: https://github.com/DozerMapper/dozer

简单实用:

(1)pom

<dependency>
    <groupId>com.github.dozermapper</groupId>
    <artifactId>dozer-core</artifactId>
    <version>6.5.0</version>
</dependency>

(2)代码使用:

package beanutils;

import com.github.dozermapper.core.DozerBeanMapperBuilder;
import com.github.dozermapper.core.Mapper;
import com.google.common.collect.Lists;

import java.util.HashMap;

public class Client {

    public static void main(String[] args) {
        User user = new User();
        user.setUserName("zhangsan");
        user.setFullname("张三");
        Address address = new Address();
        address.setAddress("这是什么地址");
        address.setCodes(Lists.newArrayList("1", "2", "3"));
        user.setAddresses(Lists.newArrayList(address));
        user.setAddress(address);

        // 属性拷贝
        Mapper defaultMapper = DozerBeanMapperBuilder.buildDefault();
        UserBO map = defaultMapper.map(user, UserBO.class);
        System.out.println("====123===");
        System.out.println(map);

        //直接转map
        HashMap map1 = defaultMapper.map(user, HashMap.class);
        System.out.println("====456===");
        System.out.println(map1);
    }
}

2021-02-03 22:13:37.888 | cmdb - INFO | main | com.xm.ggn.test.guava.GuavaTest | line:17 - limiter: class com.google.common.util.concurrent.SmoothRateLimiter$SmoothBursty2021-02-03 22:13:37.904 | cmdb - INFO | main | com.xm.ggn.test.guava.GuavaTest | line:19 - 0.02021-02-03 22:13:38.890 | cmdb - INFO | main | com.xm.ggn.test.guava.GuavaTest | line:21 - 0.9774312021-02-03 22:13:39.219 | cmdb - INFO | main | com.xm.ggn.test.guava.GuavaTest | line:22 - 0.3255752021-02-03 22:13:39.555 | cmdb - INFO | main | com.xm.ggn.test.guava.GuavaTest | line:23 - 0.330091

补充:guava切割器和连接器用法

    public static void main(String[] args) {
        /**
         * 切割器
         * omitEmptyStrings 代表为空的跳过,比如",a,,,b,c,," 切割后为 ["a", "b", "c"]
         */
        Splitter splitter = Splitter.on(",").omitEmptyStrings();
        Set<String> collect = splitter.splitToStream("1,2,3,,4,5").collect(Collectors.toSet());
        System.out.println(collect);
        // map 切割器。比如 1-2#@#2-3#@#3-4 切割为map。
        Splitter.MapSplitter mapSplitter = Splitter.on("#@#").omitEmptyStrings().withKeyValueSeparator("-");
        Map<String, String> map = mapSplitter.split("1-2#@#2-3#@#3-4");
        System.out.println(map);
        System.out.println("======");

        /**
         * 连接器
         * 集合和map 连接器
         */
        String joinedStr = Joiner.on("#@#").join(collect);
        System.out.println(joinedStr);
        Joiner.MapJoiner mapJoiner = Joiner.on("#@#").withKeyValueSeparator("-");
        String mapStr = mapJoiner.join(map);
        System.out.println(mapStr);
    }

结果:

[1, 2, 3, 4, 5]
{1=2, 2=3, 3=4}
======
1#@#2#@#3#@#4#@#5
1-2#@#2-3#@#3-4

====== hutool 扫描 

1. 有时候需要反射,然后一个接口,多个实现,根据不同的类名称选择不同的实现,然后反射进行调用

import cn.hutool.core.util.ClassUtil;
。。。
        // 根据父类找指定包下的实现或子类
        Set<Class<?>> classes = ClassUtil.scanPackageBySuper("qz.test.impl", IInterface.class);
        System.out.println(classes);
        for (Class clazz : classes) {
            IInterface iInterface = (IInterface) clazz.newInstance();
            iInterface.test1();
        }
        System.out.println("======");

        // 根据包扫描,扫描出来按自己的规则过滤
        Set<Class<?>> classes1 = ClassUtil.scanPackage("qz.test", tmp -> IInterface.class.isAssignableFrom(tmp) && !IInterface.class.equals(tmp));
        System.out.println(classes1);

        // 根据包以及指定的注解进行扫描. @MyAno 注解需要声明 @Retention(RetentionPolicy.RUNTIME) 运行时可见
        System.out.println("======");
        Set<Class<?>> classes2 = ClassUtil.scanPackageByAnnotation("qz.test", MyAno.class);
        System.out.println(classes2);

2. hutool 发送http 请求

package cn.qz.template.util;

import cn.hutool.http.ContentType;
import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.google.common.collect.Lists;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class HttpUtils {

    public static void main(String[] args) {
        Map<String, Object> param = new HashMap<>();
        param.put("param1", "111222");
        Map<String, List<String>> header = new HashMap<>();
        header.put("header1", Lists.newArrayList("111222"));

//        HttpRequest request = HttpUtil.createGet("http://127.0.0.1:8090/").form(param).header(header);
//        System.out.println(get("http://127.0.0.1:8090/xcx/test1", param, header));
//        System.out.println(postJson("http://127.0.0.1:8090/xcx/test1", header, ""));
        System.out.println(post("http://127.0.0.1:8090/xcx/test1", param, header));
//        HttpUtil.post("http://127.0.0.1:8090/xcx/test1", param);
    }

    public static String get(String url, Map<String, Object> param, Map<String, List<String>> headers) {
        if (headers == null) {
            headers = new HashMap<>();
        }
        HttpRequest request = HttpUtil.createGet(url).form(param).header(headers);
        String body = request.execute().body();
        return body;
    }

    /**
     * 发送post请求
     *
     * @param url
     * @param param
     * @param headers
     * @return
     */
    public static String post(String url, Map<String, Object> param, Map<String, List<String>> headers) {
        if (headers == null) {
            headers = new HashMap<>();
        }
        HttpRequest request = HttpUtil.createPost(url).form(param).header(headers);
        String body = request.execute().body();
        return body;
    }

    /**
     * 发送post 请求方法
     *
     * @param url
     * @param headers
     * @param json
     * @return
     */
    public static String postJson(String url, Map<String, List<String>> headers, String json) {
        // post 请求的json也写在body 中
        if (headers == null) {
            headers = new HashMap<>();
        }
        headers.put(Header.CONTENT_TYPE.toString(), Lists.newArrayList(ContentType.JSON.toString()));
        HttpRequest request = HttpUtil.createPost(url)
                .body(json)
                .header(headers);
        String body = request.execute().body();
        return body;
    }

    public static <T> T convert(String str, Class<T> clazz) {
        return JSONUtil.toBean(str, clazz);
    }
}

 3. 类加载器

  指定目录加载jar包,可以递归加载,使用的主要类是: JarClassLoader。

  JarClassLoader 是指定目录或者指定jar file 进行加载, 不会主动去加载类,加载之后用loadClass 的时候会找到类,相当于将相关的jar 包加到classpath 路径。

1》. 准备两个类,分别打到两个jar包

(1). User 类
package cn.qz;

public class User {
    static {
    System.out.println("user loaded~~~");
    }

    public User() {
    System.out.println("user create");
    }

    public void method1() {
       System.out.println("method1");
    }

}
(2). Client 类
package cn.qz2;

import cn.qz.User;

public class Client {

    public static void test() {
        User user = new User();
        System.out.println();
        user.method1();
        System.out.println();
        System.out.println(user.getClass().getClassLoader());
    }
}

2》 打包后放于两个包, 分布如下

/Users/xxx/Desktop/lib
xxx lib % ls -R
client    user

./client:
client.jar

./user:
user.jar

3》 hutool 测试

  也可以用返回的 classLoader 进行加载。

package org.example;

import cn.hutool.core.lang.JarClassLoader;

import java.io.File;
import java.lang.reflect.Method;
import java.net.URLClassLoader;

public class PlainTest {

    public static void main(String[] args) throws Exception {
        URLClassLoader classLoader = JarClassLoader.loadJarToSystemClassLoader(new File("/Users/xxx/Desktop/lib/"));
//        Class<?> aClass = PlainTest.class.getClassLoader().loadClass("cn.qz2.Client");
//        System.out.println(aClass);
        Class<?> aClass = classLoader.loadClass("cn.qz2.Client");
        Method test = aClass.getDeclaredMethod("test", null);
        test.invoke(null, null);
        System.out.println("======");
        System.out.println(aClass.getClassLoader());
    }

}

结果:

user loaded~~~
user create

method1

sun.misc.Launcher$AppClassLoader@18b4aac2
======
sun.misc.Launcher$AppClassLoader@18b4aac2

可以看到上面用  classLoader.loadClass 加载class 的时候不会触发static 静态代码块的执行。 执行其方法的时候会触发static 代码块的执行。

4》可以看到默认使用的是 sun.misc.Launcher$AppClassLoader, load 方法使用的是hutool 自定义的loader

package org.example;

import cn.hutool.core.lang.JarClassLoader;

import java.io.File;
import java.lang.reflect.Method;
import java.net.URLClassLoader;

public class PlainTest {

    public static void main(String[] args) throws Exception {
        URLClassLoader classLoader = JarClassLoader.load(new File("/Users/xxx/Desktop/lib/"));
//        PlainTest.class.getClassLoader().loadClass("cn.qz2.Client");
        Class<?> aClass = classLoader.loadClass("cn.qz2.Client");
        System.out.println(aClass);
        Method test = aClass.getDeclaredMethod("test", null);
        test.invoke(null, null);
        System.out.println("======");
        System.out.println(aClass.getClassLoader());
    }

}

结果

class cn.qz2.Client
user loaded~~~
user create

method1

cn.hutool.core.lang.JarClassLoader@78c03f1f
======
cn.hutool.core.lang.JarClassLoader@78c03f1f

5》如果用load 方法,应用classloader 加载会报错

package org.example;

import cn.hutool.core.lang.JarClassLoader;

import java.io.File;
import java.lang.reflect.Method;
import java.net.URLClassLoader;

public class PlainTest {

    public static void main(String[] args) throws Exception {
        URLClassLoader classLoader = JarClassLoader.load(new File("/Users/xxx/Desktop/lib/"));
        Class<?> aClass = PlainTest.class.getClassLoader().loadClass("cn.qz2.Client");
//        Class<?> aClass = classLoader.loadClass("cn.qz2.Client");
        System.out.println(aClass);
        Method test = aClass.getDeclaredMethod("test", null);
        test.invoke(null, null);
        System.out.println("======");
        System.out.println(aClass.getClassLoader());
    }

}

结果报错:

Exception in thread "main" java.lang.ClassNotFoundException: cn.qz2.Client
    at java.net.URLClassLoader.findClass(URLClassLoader.java:387)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
    at org.example.PlainTest.main(PlainTest.java:13)

6》只加载client 目录,代码会报错

package org.example;

import cn.hutool.core.lang.JarClassLoader;

import java.io.File;
import java.lang.reflect.Method;
import java.net.URLClassLoader;

public class PlainTest {

    public static void main(String[] args) throws Exception {
        URLClassLoader classLoader = JarClassLoader.load(new File("/Users/xxx/Desktop/lib/client/"));
//        Class<?> aClass = PlainTest.class.getClassLoader().loadClass("cn.qz2.Client");
        Class<?> aClass = classLoader.loadClass("cn.qz2.Client");
        System.out.println(aClass);
        Method test = aClass.getDeclaredMethod("test", null);
        test.invoke(null, null);
        System.out.println("======");
        System.out.println(aClass.getClassLoader());
    }

}

结果:

class cn.qz2.Client
Exception in thread "main" Disconnected from the target VM, address: '127.0.0.1:54695', transport: 'socket'
java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.example.PlainTest.main(PlainTest.java:17)
Caused by: java.lang.NoClassDefFoundError: cn/qz/User
    at cn.qz2.Client.test(Client.java:8)
    ... 5 more
Caused by: java.lang.ClassNotFoundException: cn.qz.User
    at java.net.URLClassLoader.findClass(URLClassLoader.java:387)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
    ... 6 more

5. 环境下打包MAC 下需要设置变量名为大写, 且用export 命令:

export CLASSPATH=/Users/xxx/Desktop/lib/user/user.jar
javac ./cn/qz2/*  (编译所有文件,支持通配符)
jar cvf ./client.jar ./    // 打包

 

posted @ 2018-08-13 11:44  QiaoZhi  阅读(15036)  评论(4编辑  收藏  举报