添加一个非常优秀的代码库。redis和Mysql放到了外边。

This commit is contained in:
法然
2022-12-05 11:53:08 +08:00
parent 012fc7a16d
commit ea02f1c503
342 changed files with 18929 additions and 570 deletions

View File

@@ -11,9 +11,10 @@
* [ ] 设计模式
* [ ] Java基础教程Java的基本语法和使用及原理晚上自学第一周学完
* [X] Java语言基础。语言语法。20200830
* [ ] Java标注库开发.(自动拆装箱、String、集合类、枚举类、IO类、反射、动态代理、序列化、注解、泛型、单元测试、正则表达式、工具库、异常、日期时间、编码方式)
* [x] Java标注库.(自动拆装箱、String枚举类、反射、动态代理、序列化、注解、泛型、单元测试、正则表达式、工具库、异常、日期时间、编码方式)
* [x] Java集合类
* [x] JavaIO与网络编程网络基础、Socket编程
* [ ] Java并发编程线程、锁、同步、并发包
* [ ] Java网络编程网络基础、Socket编程
* [ ] Java基本原理。JVM底层的原理和技术。内存结构、垃圾回收
* [ ] Java架构模式。面向对象和设计模式
* [ ] Java网站开发JavaWeb相关的技术知识。
@@ -72,4 +73,11 @@
> 一份可以参考的文档。其中大数据、网络安全和扩展篇不学。
> ![](image/2022-10-27-20-26-36.png)
> ![](image/2022-10-27-20-26-36.png)
> 找到了两个很不得了的东西。如果有时间,可以把这两个文档全部重新看一遍。完全有书籍的质量的基础知识介绍。常看常新。自己接下来的笔记就从这上边找吧。
>
> 入门小站。https://rumenz.com/
> BestJavaer。https://github.com/crisxuan/bestJavaer
>

View File

@@ -1,31 +0,0 @@
数据结构分为
* 线性数据结构
* 树型数据结构
* 图型数据结构
C++中的容器分为(都是线性的)
* 顺序容器
* array 数组
* vector向量
* list 链表
* 关联容器
* map 映射
* set 集合
* 容器适配器
* stack 栈
* queue 队列
Java中的容器分为都是线性的
* 集合collection
* List
* Queue
* Map
* Set
![](image/2022-11-08-10-51-54.png)
![](image/2022-11-08-10-54-19.png)

View File

@@ -2,6 +2,10 @@
### 概览
`java.lang.Object`类是Java语言中的根类即所有类的父类。它中描述的所有方法子类都可以使用。在对象实例化的时候最终找的父类就是Object。
如果一个类没有特别指定父类, 那么默认则继承自Object类。
```java
public native int hashCode()
@@ -354,3 +358,38 @@ CloneConstructorExample e2 = new CloneConstructorExample(e1);
e1.set(2, 222);
System.out.println(e2.get(2)); // 2
```
## 2 Objects工具类
Objects类是对象工具类它里面的的方法都是用来操作对象的。
### equals方法
在**JDK7**添加了一个Objects工具类它提供了一些方法来操作对象它由一些静态的实用方法组成这些方法是null-save空指针安全的或null-tolerant容忍空指针的用于计算对象的hashcode、返回对象的字符串表示形式、比较两个对象。
在比较两个对象的时候Object的equals方法容易抛出空指针异常而Objects类中的equals方法就优化了这个问题。方法如下
* `public static boolean equals(Object a, Object b)`:判断两个对象是否相等。
我们可以查看一下源码,学习一下:
```java
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
```
### isNull
`static boolean isNull(Object obj)` 判断对象是否为null如果为null返回true。
```java
Student s1 = null;
Student s2 = new Student("蔡徐坤", 22);
// static boolean isNull(Object obj) 判断对象是否为null,如果为null返回true
System.out.println(Objects.isNull(s1)); // true
System.out.println(Objects.isNull(s2)); // false
```

View File

@@ -34,7 +34,19 @@ Integer i = new Interger(1);
Double d = Double.valueOf(3.14);
```
### 包装器类转换
### 装箱拆箱
* 装箱Boxing: 将基本类型转化为包装器类型 包装器类.valueOf(基本数据类型变量或常量)。装箱共享内存。
* 拆箱unBoxing将包装器类型转化为基本数据类型XX.XXXvalue();拆箱也共享内存
装箱操作
```java
Integer i = Integer.valueOf(10);//10是基本数据类型i是包装器类型
int n = i.intValue();//i是包装器类型n是包装器类型
```
拆箱操作
```java
Boolean.booleanValue()
@@ -47,17 +59,18 @@ Float.floatValue()
Double.doubleValue()
```
* 装箱Boxing: 将基本类型转化为包装器类型 包装器类.valueOf(基本数据类型变量或常量)。装箱共享内存。
* 拆箱unBoxing将包装器类型转化为基本数据类型XX.XXXvalue();拆箱也共享内存
可以自动进行拆箱装箱
```java
Integer i = Integer.valueOf(10);//10是基本数据类型i是包装器类型
int n = i.intValue();//i是包装器类型n是包装器类型
Integer i = 4;//自动装箱。相当于Integer i = Integer.valueOf(4);
i = i + 5;//等号右边将i对象转成基本数值(自动拆箱) i.intValue() + 5;
//加法运算完成后,再次装箱,把基本数值转成对象。
```
### 包装器类
> 注意事项
* 对象一旦赋值,其值不能在改变。
* ++/--自增自减运算符只能对基本数据类型操作
* 集合中只能存放帮装起类型的对象
* 集合中只能存放包装器类型的对象
@@ -175,4 +188,45 @@ System.out.println(m == n); // true
7. toLowerCase()
指定字母的小写形式
8. toString()
返回字符的字符串形式字符串的长度仅为1
返回字符的字符串形式字符串的长度仅为1
## 2 Integer类
- Integer类概述
包装一个对象中的原始类型 int 的值
- Integer类构造方法及静态方法
| 方法名 | 说明 |
| --------------------------------------- | -------------------------------------- |
| public Integer(int value) | 根据 int 值创建 Integer 对象(过时) |
| public Integer(String s) | 根据 String 值创建 Integer 对象(过时) |
| public static Integer valueOf(int i) | 返回表示指定的 int 值的 Integer 实例 |
| public static Integer valueOf(String s) | 返回保存指定String值的 Integer 对象 |
- 示例代码
```java
public class IntegerDemo {
public static void main(String[] args) {
//public Integer(int value):根据 int 值创建 Integer 对象(过时)
Integer i1 = new Integer(100);
System.out.println(i1);
//public Integer(String s):根据 String 值创建 Integer 对象(过时)
Integer i2 = new Integer("100");
//Integer i2 = new Integer("abc"); //NumberFormatException
System.out.println(i2);
System.out.println("--------");
//public static Integer valueOf(int i):返回表示指定的 int 值的 Integer 实例
Integer i3 = Integer.valueOf(100);
System.out.println(i3);
//public static Integer valueOf(String s)返回保存指定String值的Integer对象
Integer i4 = Integer.valueOf("100");
System.out.println(i4);
}
}
```

View File

@@ -198,7 +198,7 @@ String s5 = new String("Runoob"); // String 对象创建
* String(byte数组,起始下标,长度)
* String(StringBuffer buffer)
* String(StringBuilder builder)
### 关键方法
### 字符串拼接
* 字符串链接concat和+号的功能类似。
@@ -207,6 +207,7 @@ String s5 = new String("Runoob"); // String 对象创建
"Hello," + " runoob" + "!"
```
### 格式化方法
* 创建格式化字符串printf和format两个方法
```java
@@ -216,13 +217,13 @@ System.out.printf("浮点型变量的值为 " +
"is %s", floatVar, intVar, stringVar);
```
```
String fs;
fs = String.format("浮点型变量的值为 " +
"%f, 整型变量的值为 " +
" %d, 字符串变量的值为 " +
" %s", floatVar, intVar, stringVar);
```
### 带正则表达式的方法
* boolean matches(String regex)告知此字符串是否匹配给定的正则表达式。
* String replaceAll(String regex, String replacement)使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。
* String replaceFirst(String regex, String replacement) 使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。
* String[] split(String regex)根据给定正则表达式的匹配拆分此字符串。
* String[] split(String regex, int limit)根据匹配给定的正则表达式来拆分此字符串。
### 其他方法
* char charAt(int index)返回指定索引处的 char 值。
@@ -250,14 +251,11 @@ fs = String.format("浮点型变量的值为 " +
* int lastIndexOf(String str)返回指定子字符串在此字符串中最右边出现处的索引。
* int lastIndexOf(String str, int fromIndex) 返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索。
* int length()返回此字符串的长度。
* boolean matches(String regex)告知此字符串是否匹配给定的正则表达式。
* boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len)测试两个字符串区域是否相等。
* boolean regionMatches(int toffset, String other, int ooffset, int len)测试两个字符串区域是否相等。
* String replace(char oldChar, char newChar)返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。
* String replaceAll(String regex, String replacement)使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。
* String replaceFirst(String regex, String replacement) 使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。
* String[] split(String regex)根据给定正则表达式的匹配拆分此字符串。
* String[] split(String regex, int limit)根据匹配给定的正则表达式来拆分此字符串。
* boolean startsWith(String prefix)测试此字符串是否以指定的前缀开始。
* boolean startsWith(String prefix, int toffset)测试此字符串从指定索引开始的子字符串是否以指定前缀开始。
* CharSequence subSequence(int beginIndex, int endIndex) 返回一个新的字符序列,它是此序列的一个子序列。
@@ -290,6 +288,10 @@ StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不
* 由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。
StringBuffer的内部实现方式和String不同.
* StringBuffer在进行字符串处理时不生成新的对象在内存使用上要优于String类。所以在实际使用时如果经常需要对一个字符串进行修改例如插入、删除等操作使用StringBuffer要更加适合一些。
* String:在String类中没有用来改变已有字符串中的某个字符的方法由于不能改变一个java字符串中的某个单独字符所以在JDK文档中称String类的对象是不可改变的。然而不可改变的字符串具有一个很大的优点:编译器可以把字符串设为共享的。
### 使用
```java
@@ -343,3 +345,185 @@ public class RunoobTest{
## 4 字符串格式化
利用了Formatter类。printf、printfln等方法原理一直。
字符串的格式化相当于将字符串按照指定的格式进行toString(),一般有两种形式:
```java
//使用指定的格式字符串和参数返回一个格式化字符串。
public static String format(String format, Object... args) {
return new Formatter().format(format, args).toString();
}
//使用指定的语言环境、格式字符串和参数返回一个格式化字符串。
public static String format(Locale l, String format, Object... args) {
return new Formatter(l).format(format, args).toString();
}
```
* 实例
```
String fs;
fs = String.format("浮点型变量的值为 " +
"%f, 整型变量的值为 " +
" %d, 字符串变量的值为 " +
" %s", floatVar, intVar, stringVar);
```
### 数据转化符
| 数据类型 | 说明 | 转化形式 |
|------|------------|------------|
| %s | 字符串类型 | “string” |
| %c | 字符类型 | A |
| %b | 布尔类型 | true/false |
| %o | 整数类型(八进制) | 111 |
| %d | 整数类型(十进制) | 17 |
| %x | 整数类型(十六进制) | 11 |
| %f | 浮点类型(基本) | 66.66 |
| %e | 指数类型 | 1.11e+5 |
| %a | 浮点类型(十六进制) | FF.22 |
| %h | 散列码 | 11 |
| %% | 百分比类型 | 17% |
| %n | 换行符 |
| %tx | 日期与时间类型 |
```java
public class Format01 {
public static void main(String[] args) {
System.out.println(String.format("字符串:%s", "String"));
System.out.println(String.format("字符:%c", 'M'));
System.out.println(String.format("布尔类型:%b", 'M'>'A'));
System.out.println(String.format("八进制整数类型:%o", 17));
System.out.println(String.format("十进制整数类型:%d", 17));
System.out.println(String.format("十六进制整数类型:%x", 17));
System.out.println(String.format("基本浮点类型:%f", 99.1));
System.out.println(String.format("指数类型:%e", 100.111111111));
System.out.println(String.format("十六进制浮点类型:%a", 17.111111));
System.out.println(String.format("散列码:%h", 17));
System.out.println(String.format("百分比类型17%%"));
System.out.print(String.format("换行符:%n", 17));
}
}
```
### 格式化控制符
| 标志 | 说明 | 示例 | 输出 |
|---|---|---|---|
| + | 为正数添加符号 | (“正数:%+f”,11.11)) | 正数:+11.110000 |
| - | 左对齐 | (“左对齐:%-5d”,11) | 左对齐11 |
| 0 | 整数前面补0 | (“数字前面补0%04d”,11) | 数字前面补00011 |
| , | 对数字分组 | (“按,对数字分组:%,d”,111111111) | 按,对数字分组111,111,111 |
| 空格 | 数字前面补空格 | (“空格:% 4d”,11) | 空格: 11 |
| | 包含负数 | (“使用括号包含负数:%(f”,-11.11) | 使用括号包含负数:(11.110000) |
| # | 浮点数包含小数八进制包含0十六进制包含0x |
| < | 格式化前一个转换符描述的参数 | (“格式化前描述的参数:%f转化后%❤️.3f”,111.1111111) | 格式化前描述的参数111.111111转化后111.111 |
| $ | 被格式化的参数索引 | (“被格式化的参数索引:%1
```java
public class formatString {
public static void main(String[] args) {
System.out.println(String.format("正数:%+f",11.11));
System.out.println(String.format("右对齐:%+10d",11));
System.out.println(String.format("左对齐:%-5d",11));
System.out.println(String.format("数字前面补0%044d",11));
System.out.println(String.format("空格:% 4d",11));
System.out.println(String.format("按,对数字分组:%,d",111111111));
System.out.println(String.format("使用括号包含负数:%(f",-11.11));
System.out.println(String.format("浮点数包含小数点:%#f",11.1));
System.out.println(String.format("八进制包含0%#o",11));
System.out.println(String.format("十六进制包含0x%#x",11));
System.out.println(String.format("格式化前描述的参数:%f转化后%<3.3f",111.1111111));
System.out.println(String.format("被格式化的参数索引:%1$d,%2$s",11,"111.1111111"));
}
}
```
### 日期格式化符
| 转换符 | 说明 | 示例 |
|---|---|---|
| c | 全部时间日期 | 星期四 十二月 17 13:11:35 CST 2020 |
| F | 年-月-日格式 | 2020-12-17 |
| D | 月/日/年格式 | 12/17/20 |
| r | HH:MM:SS PM格式12时制 | 01:11:35 下午 |
| T | HH:MM:SS格式24时制 | 13:11:35 |
| R | HH:MM格式24时制 | 13:11 |
```java
public class formatDate {
public static void main(String[] args) {
Date date = new Date();
System.out.println(String.format("全部时间日期:%tc",date));
System.out.println(String.format("年-月-日格式:%tF",date));
System.out.println(String.format("月/日/年格式:%tD",date));
System.out.println(String.format("HH:MM:SS PM格式12时制%tr",date));
System.out.println(String.format("HH:MM:SS格式24时制%tT",date));
System.out.println(String.format("HH:MM格式24时制%tR",date));
}
}
```
### 时间格式化符
| 转换符 | 说明 | 示例 |
|---|---|---|
| H | 2位数字24时制的小时不足2位前面补0 | 13 |
| l | 2位数字12时制的小时 | 1 |
| k | 2位数字24时制的小时 | 13 |
| M | 2位数字的分钟 | 33 |
| L | 3位数字的毫秒 | 745 |
| S | 2位数字的秒 | 33 |
| N | 9位数字的毫秒数 | 745000000 |
| p | Locale.US,"小写字母的上午或下午标记(英) | 下午 |
| Z | 时区缩写字符串 | CST |
| z | 相对于GMT的RFC822时区的偏移量 | +0800 |
| s | 1970-1-1 00:00:00 到现在所经过的秒数 | 1608183213 |
| Q | 1970-1-1 00:00:00 到现在所经过的毫秒数 | 1608183213745 |
```java
public class formatTime {
public static void main(String[] args) {
Date date = new Date();
System.out.println(String.format("2位数字24时制的小时不足2位前面补0%tH", date));
System.out.println(String.format("2位数字12时制的小时%tl", date));
System.out.println(String.format("2位数字24时制的小时%tk", date));
System.out.println(String.format("2位数字的分钟%tM", date));
System.out.println(String.format("3位数字的毫秒%tL", date));
System.out.println(String.format("2位数字的秒%tS", date));
System.out.println(String.format("9位数字的毫秒数%tN", date));
System.out.println(String.format("时区缩写字符串:%tZ", date));
System.out.println(String.format("相对于GMT的RFC822时区的偏移量%tz", date));
System.out.println(String.format("Locale.US,\"小写字母的上午或下午标记(英)%tp", date));
System.out.println(String.format("1970-1-1 00:00:00 到现在所经过的秒数:%ts", date));
System.out.println(String.format("1970-1-1 00:00:00 到现在所经过的毫秒数:%tQ", date));
}
}
```
### 类型转换
* 其他类型转字符串
```java
1.String s=""+i;
2.String s=Integer.toString(i);
3.String s=String.valueOf(i);
```
* 字符串转其他类型
```java
1.int i=Integer.parsenInt(s);
2.int i=Integer.valueOf(s).intValue();
```

View File

@@ -1,28 +0,0 @@
## 1 概述
### Math类
Java 的 Math 包含了用于执行基本数学运算的属性和方法,如初等指数、对数、平方根和三角函数。
### Math中的常量
* Math.PI 记录的圆周率
* Math.E 记录e的常量
### Math中的函数
* Math.abs 求绝对值
* Math.sin 正弦函数 Math.asin 反正弦函数
* Math.cos 余弦函数 Math.acos 反余弦函数
* Math.tan 正切函数 Math.atan 反正切函数 Math.atan2 商的反正切函数
* Math.toDegrees 弧度转化为角度 Math.toRadians 角度转化为弧度
* Math.ceil 得到不小于某数的最大整数
* Math.floor 得到不大于某数的最大整数
* Math.IEEEremainder 求余
* Math.max 求两数中最大
* Math.min 求两数中最小
* Math.sqrt 求开方
* Math.pow 求某数的任意次方, 抛出ArithmeticException处理溢出异常
* Math.exp 求e的任意次方
* Math.log10 以10为底的对数
* Math.log 自然对数
* Math.rint 求距离某数最近的整数(可能比某数大,也可能比它小)
* Math.round 同上返回int型或者long型上一个函数返回double型
* Math.random 返回01之间的一个随机数

View File

@@ -0,0 +1,71 @@
## 1 Math
### Math类
Java 的 Math 包含了用于执行基本数学运算的属性和方法,如初等指数、对数、平方根和三角函数。
### Math中的常量
* Math.PI 记录的圆周率
* Math.E 记录e的常量
### Math中的函数
三角函数
* Math.sin 正弦函数 Math.asin 反正弦函数
* Math.cos 余弦函数 Math.acos 反余弦函数
* Math.tan 正切函数 Math.atan 反正切函数 Math.atan2 商的反正切函数
* Math.toDegrees 弧度转化为角度 Math.toRadians 角度转化为弧度
舍入函数
* Math.abs 求绝对值
* Math.ceil 得到不小于某数的最大整数
* Math.floor 得到不大于某数的最大整数
* Math.IEEEremainder 求余
* Math.max 求两数中最大
* Math.min 求两数中最小
* Math.round 同上返回int型或者long型上一个函数返回double型
指数幂计算
* Math.sqrt 求开方
* Math.pow 求某数的任意次方, 抛出ArithmeticException处理溢出异常
* Math.exp 求e的任意次方
* Math.log10 以10为底的对数
* Math.log 自然对数
* Math.rint 求距离某数最近的整数(可能比某数大,也可能比它小)
随机数
* Math.random 返回01之间的一个随机数
## 2 BigDecimal类
### 概述
| 相关内容 | 具体描述 |
| -------- | :----------------------------------------------------------- |
| 包 | java.math 使用时需要导包 |
| 类声明 | public class BigDecimal extends Number implements Comparable<BigDecimal> |
| 描述 | BigDecimal类提供了算术缩放操作舍入比较散列和格式转换的操作。提供了更加精准的数据计算方式 |
### 构造方法
| 构造方法名 | 描述 |
| ---------------------- | ----------------------------------------------- |
| BigDecimal(double val) | 将double类型的数据封装为BigDecimal对象 |
| BigDecimal(String val) | 将 BigDecimal 的字符串表示形式转换为 BigDecimal |
注意:推荐使用第二种方式,第一种存在精度问题;
### 常用方法
BigDecimal类中使用最多的还是提供的进行四则运算的方法如下
| 方法声明 | 描述 |
| -------------------------------------------- | -------- |
| public BigDecimal add(BigDecimal value) | 加法运算 |
| public BigDecimal subtract(BigDecimal value) | 减法运算 |
| public BigDecimal multiply(BigDecimal value) | 乘法运算 |
| public BigDecimal divide(BigDecimal value) | 触发运算 |
注意对于divide方法来说如果除不尽的话就会出现java.lang.ArithmeticException异常。此时可以使用divide方法的另一个重载方法
> BigDecimal divide(BigDecimal divisor, int scale, int roundingMode): divisor除数对应的BigDecimal对象scale:精确的位数roundingMode取舍模式
> 小结Java中小数运算有可能会有精度问题如果要解决这种精度问题可以使用BigDecimal

View File

@@ -47,7 +47,7 @@ Java使用以下三种方法来比较两个日期
## 2 日期格式化SimpleDateFormat
### 格式化的方法
### 格式化的方法format
SimpleDateFormat 是一个以语言环境敏感的方式来格式化和分析日期的类。SimpleDateFormat 允许你选择任何用户自定义日期时间格式来运行。
```java
@@ -65,7 +65,27 @@ public class DateDemo {
}
```
### 格式化编码
### 格式化方法parse
```java
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 把String转换成Date对象
*/
public class Demo04DateFormatMethod {
public static void main(String[] args) throws ParseException {
DateFormat df = new SimpleDateFormat("yyyy年MM月dd日");
String str = "2020年6月11日";
Date date = df.parse(str);
System.out.println(date);
}
}
```
### SimpleDateFormat格式化编码
| 字母 | 描述 | 示例 |
|---|---|---|
@@ -90,7 +110,7 @@ public class DateDemo {
| ' | 文字定界符 | Delimiter |
| " | 单引号 | ` |
### printf格式化日期
### Printf&String.format格式化日期
printf 方法可以很轻松地格式化时间和日期。使用两个字母格式,它以 %t 开头并且以下面表格中的一个字母结尾。
@@ -174,4 +194,144 @@ public class DiffDemo {
## 4 Calendar类
> 日后补充把
## 概念
`java.util.Calendar`是日历类在Date后出现替换掉了许多Date的方法。该类将所有可能用到的时间信息封装为静态成员变量方便获取。日历类就是方便获取各个时间属性的。
## 获取方式
Calendar为抽象类由于语言敏感性Calendar类在创建对象时并非直接创建而是通过静态方法创建返回子类对象如下
Calendar静态方法
* `public static Calendar getInstance()`:使用默认时区和语言环境获得一个日历
例如:
```java
import java.util.Calendar;
public class Demo06CalendarInit {
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
}
}
```
## 常用方法
根据Calendar类的API文档常用方法有
- `public int get(int field)`:返回给定日历字段的值。
- `public void set(int field, int value)`:将给定的日历字段设置为给定值。
- `public abstract void add(int field, int amount)`:根据日历的规则,为给定的日历字段添加或减去指定的时间量。
- `public Date getTime()`返回一个表示此Calendar时间值从历元到现在的毫秒偏移量的Date对象。
Calendar类中提供很多成员常量代表给定的日历字段
| 字段值 | 含义 |
| ------------ | ------------------------------------- |
| YEAR | 年 |
| MONTH | 月从0开始可以+1使用 |
| DAY_OF_MONTH | 月中的天(几号) |
| HOUR | 时12小时制 |
| HOUR_OF_DAY | 时24小时制 |
| MINUTE | 分 |
| SECOND | 秒 |
| DAY_OF_WEEK | 周中的天周几周日为1可以-1使用 |
### get/set方法
get方法用来获取指定字段的值set方法用来设置指定字段的值代码使用演示
```java
import java.util.Calendar;
public class CalendarUtil {
public static void main(String[] args) {
// 创建Calendar对象
Calendar cal = Calendar.getInstance();
// 获取年
int year = cal.get(Calendar.YEAR);
// 获取月
int month = cal.get(Calendar.MONTH) + 1;
// 获取日
int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH);
System.out.print(year + "年" + month + "月" + dayOfMonth + "日");
}
}
```
```java
import java.util.Calendar;
public class Demo07CalendarMethod {
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
// 设置年
cal.set(Calendar.YEAR, 2020);
System.out.print(year + "年" + month + "月" + dayOfMonth + "日"); // 2020年6月11日
}
}
```
### add方法
add方法可以对指定日历字段的值进行加减操作如果第二个参数为正数则加上偏移量如果为负数则减去偏移量。代码如
```java
import java.util.Calendar;
public class CalendarMethod {
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
// 获取年
int year = cal.get(Calendar.YEAR);
// 获取月
int month = cal.get(Calendar.MONTH) + 1;
// 获取日
int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH);
// 2020年6月11日
System.out.println(year + "年" + month + "月" + dayOfMonth + "日");
// 使用add方法
// 加2天
cal.add(Calendar.DAY_OF_MONTH, 2);
// 减3年
cal.add(Calendar.YEAR, -3);
// 获取年
int year1 = cal.get(Calendar.YEAR);
// 获取月
int month1 = cal.get(Calendar.MONTH) + 1;
// 获取日
int dayOfMonth1 = cal.get(Calendar.DAY_OF_MONTH);
// 2017年6月13日;
System.out.println(year1 + "年" + month1 + "月" + dayOfMonth1 + "日");
}
}
```
### getTime方法
Calendar中的getTime方法并不是获取毫秒时刻而是拿到对应的Date对象。
```java
import java.util.Calendar;
import java.util.Date;
public class Demo09CalendarMethod {
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
Date date = cal.getTime();
System.out.println(date); // Thu Jun 11 09:37:57 CST 2020
}
}
```
> 小贴士:
>
> 西方星期的开始为周日,中国为周一。
>
> 在Calendar类中月份的表示是以0-11代表1-12月。
>
> 日期是有大小关系的,时间靠后,时间越大。

View File

@@ -1,2 +1,304 @@
## Java 正则表达式
> https://blog.csdn.net/m0_62618110/article/details/123704869
# Java 正则表达式
## 0 概述
### 简介
正则表达式regex是一个字符串由字面值字符和特殊符号组成是用来描述匹配一个字符串集合的模式可以用来匹配、查找字符串。
正则表达式的两个主要作用:
* 查找:在字符串中查找符合固定模式的子串
* 匹配:整个字符串是否符合某个格式
在匹配和查找的基础上,实现替换、分割等操作。
### 基本实例
```java
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexMatches
{
public static void main( String[] args ){
// 按指定模式在字符串查找
String line = "This order was placed for QT3000! OK?";
String pattern = "(\\D*)(\\d+)(.*)";
// 创建 Pattern 对象
Pattern r = Pattern.compile(pattern);
// 现在创建 matcher 对象
Matcher m = r.matcher(line);
if (m.find( )) {
System.out.println("Found value: " + m.group(0) );
System.out.println("Found value: " + m.group(1) );
System.out.println("Found value: " + m.group(2) );
System.out.println("Found value: " + m.group(3) );
} else {
System.out.println("NO MATCH");
}
}
}
```
## 1 正则表达式语法
* 在其他语言中,`\\`表示:我想要在正则表达式中插入一个普通的(字面上的)反斜杠,请不要给它任何特殊的意义。
* 在 Java 中,`\\` 表示:我要插入一个正则表达式的反斜线,所以其后的字符具有特殊的意义。
* 不要在重复词符中使用空白。如B{3,6} ,不能写成 B{3, 6}。空格也是有含义的。
* 可以使用括号来将模式分组。(ab){3}匹配ababab , 而ab{3} 匹配 abbb。
| 字符 | 匹配 | 示例 |
|---|---|---|
| . | 任意单个字符,除换行符外 | jav.匹配java |
| [ ] | [ ] 中的任意一个字符 | java匹配j[abc]va |
| - | [ ] 内表示字符范围 | java匹配[a-z]av[a-g] |
| ^ | 在[ ]内的开头,匹配除[ ]内的字符之外的任意一个字符 | java匹配j[^b-f]va |
| | | 或 | x|y匹配x或y |
| \ | 将下一字符标记为特殊字符、文本、反向引用或八进制转义符 | \(匹配( |
| $ | 匹配输入字符串结尾的位置。如果设置了 RegExp 对象的 Multiline 属性,$ 还会与"\n"或"\r"之前的位置匹配。 | ;$匹配位于一行及外围的;号 |
| * | 零次或多次匹配前面的字符 | zo*匹配zoo或z |
| + | 一次或多次匹配前面的字符 | zo+匹配zo或zoo |
| ? | 零次或一次匹配前面的字符 | zo?匹配z或zo |
| p{n} | n 是非负整数。正好匹配 n 次 | o{2}匹配food中的两个o |
| p{n,} | n 是非负整数。至少匹配 n 次 | o{2}匹配foood中的所有o |
| p{n,m} | M 和 n 是非负整数,其中 n &lt;= m。匹配至少 n 次,至多 m 次 | o{1,3}匹配fooood中的三个o |
| \p{P} | 一个标点字符 !"#$%&amp;'()*+,-./:;&lt;=&gt;?@[\]^_'{|}~ | J\p{P}a匹配J?a |
| \b | 匹配一个字边界 | va\b匹配java中的va但不匹配javar中的va |
| \B | 非字边界匹配 | va\B匹配javar中的va但不匹配java中的va |
| \d | 数字字符匹配 | 1[\\d]匹配13 |
| \D | 非数字字符匹配 | [\\D]java匹配Jjava |
| \w | 单词字符 | java匹配[\\w]ava |
| \W | 非单词字符 | $java匹配[\\W]java |
| \s | 空白字符 | Java 2匹配Java\\s2 |
| \S | 非空白字符 | java匹配 j[\\S]va |
| \f | 匹配换页符 | 等效于\x0c和\cL |
| \n | 匹配换行符 | 等效于\x0a和\cJ |
### 分组说明
```
正则表达式-字符类
- 语法示例:
1. \[abc\]代表a或者b或者c字符中的一个。
2. \[^abc\]代表除a,b,c以外的任何字符。
3. [a-z]代表a-z的所有小写字符中的一个。
4. [A-Z]代表A-Z的所有大写字符中的一个。
5. [0-9]代表0-9之间的某一个数字字符。
6. [a-zA-Z0-9]代表a-z或者A-Z或者0-9之间的任意一个字符。
7. [a-dm-p]a 到 d 或 m 到 p之间的任意一个字符。
正则表达式-逻辑运算符
- 语法示例:
1. &&:并且
2. | :或者
正则表达式-预定义字符
- 语法示例:
1. "." 匹配任何字符。
2. "\d":任何数字[0-9]的简写;
3. "\D":任何非数字\[^0-9\]的简写;
4. "\s" 空白字符:[ \t\n\x0B\f\r] 的简写
5. "\S" 非空白字符:\[^\s\] 的简写
6. "\w":单词字符:[a-zA-Z_0-9]的简写
7. "\W":非单词字符:\[^\w\]
正则表达式-数量词
- 语法示例:
1. X? : 0次或1次
2. X* : 0次到多次
3. X+ : 1次或多次
4. X{n} : 恰好n次
5. X{n,} : 至少n次
6. X{n,m}: n到m次(n和m都是包含的)
正则表达式-分组括号()
```
## 2 基本概念
### Patter类和Matcher类
Pattern 类:
pattern 对象是一个正则表达式的编译表示。Pattern 类没有公共构造方法。要创建一个 Pattern 对象,你必须首先调用其公共静态编译方法,它返回一个 Pattern 对象。该方法接受一个正则表达式作为它的第一个参数。
Matcher 类:
Matcher 对象是对输入字符串进行解释和匹配操作的引擎。与Pattern 类一样Matcher 也没有公共构造方法。你需要调用 Pattern 对象的 matcher 方法来获得一个 Matcher 对象。
### 捕获组
1. 捕获组是把多个字符当成一个单独单元进行处理的方法,它通过对括号内的字符分组来创建。
```
捕获组通过从左到右计算其括号来编号。
例如:在表达式((A)(B(C))) 中,存在四个这样的组:
((A)(B(C)))
(A)
(B(C))
(C)
```
2. 捕获组可以通过调用matcher对象的groupCount方法来查看表达式有多少个分组。groupCount方法返回一个int值来表示matcher对象当前有多少个捕获组
3. 还有一个特殊的组零group(0)它代表整个表达式。该组不包括在groupCount的返回值中
4. 以 (?) 开头的组是纯的非捕获 组,它不捕获文本,也不针对组合计进行计数。
## 3 Matcher用法
### 索引方法
1. public int start()
返回以前匹配的初始索引。
2. public int start(int group)
返回在以前的匹配操作期间,由给定组所捕获的子序列的初始索引
3. public int end()
返回最后匹配字符之后的偏移量。
4. public int end(int group)
返回在以前的匹配操作期间,由给定组所捕获子序列的最后字符之后的偏移量。
```java
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexMatches
{
private static final String REGEX = "\\bcat\\b";
private static final String INPUT =
"cat cat cat cattie cat";
public static void main( String[] args ){
Pattern p = Pattern.compile(REGEX);
Matcher m = p.matcher(INPUT); // 获取 matcher 对象
int count = 0;
while(m.find()) {
count++;
System.out.println("Match number "+count);
System.out.println("start(): "+m.start());
System.out.println("end(): "+m.end());
}
}
}
```
### 匹配和查找方法
1. public boolean lookingAt()
尝试将从区域开头开始的输入序列与该模式匹配。开头匹配。
2. public boolean find()
尝试查找与该模式匹配的输入序列的下一个子序列。
3. public boolean find(int start
重置此匹配器,然后尝试查找匹配该模式、从指定索引开始的输入序列的下一个子序列。
4. public boolean matches()
尝试将整个区域与模式匹配。全局匹配。
```java
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexMatches
{
private static final String REGEX = "foo";
private static final String INPUT = "fooooooooooooooooo";
private static final String INPUT2 = "ooooofoooooooooooo";
private static Pattern pattern;
private static Matcher matcher;
private static Matcher matcher2;
public static void main( String[] args ){
pattern = Pattern.compile(REGEX);
matcher = pattern.matcher(INPUT);
matcher2 = pattern.matcher(INPUT2);
System.out.println("Current REGEX is: "+REGEX);
System.out.println("Current INPUT is: "+INPUT);
System.out.println("Current INPUT2 is: "+INPUT2);
System.out.println("lookingAt(): "+matcher.lookingAt());
System.out.println("matches(): "+matcher.matches());
System.out.println("lookingAt(): "+matcher2.lookingAt());
}
}
```
### 替换方法
1. public Matcher appendReplacement(StringBuffer sb, String replacement)
实现非终端添加和替换步骤。
2. public StringBuffer appendTail(StringBuffer sb)
实现终端添加和替换步骤。
3. public String replaceAll(String replacement)
替换模式与给定替换字符串相匹配的输入序列的每个子序列。
4. public String replaceFirst(String replacement)
替换模式与给定替换字符串匹配的输入序列的第一个子序列。
5. public static String quoteReplacement(String s)
返回指定字符串的字面替换字符串。这个方法返回一个字符串就像传递给Matcher类的appendReplacement 方法一个字面字符串一样工作。
```java
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexMatches
{
private static String REGEX = "dog";
private static String INPUT = "The dog says meow. " +
"All dogs say meow.";
private static String REPLACE = "cat";
public static void main(String[] args) {
Pattern p = Pattern.compile(REGEX);
// get a matcher object
Matcher m = p.matcher(INPUT);
INPUT = m.replaceAll(REPLACE);
System.out.println(INPUT);
}
}
```
```java
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexMatches
{
private static String REGEX = "a*b";
private static String INPUT = "aabfooaabfooabfoobkkk";
private static String REPLACE = "-";
public static void main(String[] args) {
Pattern p = Pattern.compile(REGEX);
// 获取 matcher 对象
Matcher m = p.matcher(INPUT);
StringBuffer sb = new StringBuffer();
while(m.find()){
m.appendReplacement(sb,REPLACE);
}
m.appendTail(sb);
System.out.println(sb.toString());
}
}
```
## 4 String自带的正则表达式功能
见String

View File

@@ -0,0 +1,47 @@
## 2 Random
### 简介
在 Java中要生成一个指定范围之内的随机数字有两种方法一种是调用 Math 类的 random() 方法,一种是使用 Random 类。
Random():该构造方法使用一个和当前系统时间对应的数字作为种子数,然后使用这个种子数构造 Random 对象。
Random(long seed):使用单个 long 类型的参数创建一个新的随机数生成器。
Random 类提供的所有方法生成的随机数字都是均匀分布的,也就是说区间内部的数字生成的概率是均等的
### 实例
```java
package cn.itcast.demo1;
import java.util.Random;//使用时需要先导包
import java.util.Scanner;
public class RAndom {
public static void main(String[] args) {
Random r = new Random();//以系统自身时间为种子数
int i = r.nextInt();
System.out.println("i"+i);
Scanner sc =new Scanner(System.in);
int j = sc.nextInt();
Random r2 = new Random(j);//自定义种子数
Random r3 = new Random(j);//这里是为了验证上方的注意事项Random类是伪随机相同种子数相同次数产生的随机数相同
int num = r2.nextInt();
int num2 = r3.nextInt();
System.out.println("num"+num);
System.out.println("num2"+num2);
}
}
```
### 常用方法
| random.nextInt() | 返回值为整数,范围是int类型范围 |
|---|---|
| random.nextLong() | 返回值为长整型范围是long类型的范围 |
| random.nextFloat() | 返回值为小数,范围是[0,0.1] |
| random.nextDouble() | 返回值为小数,范围是[0,0.1] |
| random.nextBoolean | 返回值为boolean值true和false概率相同 |
| radom.nextGaussian() | 返回值为呈高斯(“正态”)分布的 double 值,其平均值是 0.0,标准差是 1.0 |

View File

@@ -0,0 +1,59 @@
## 1 System
### 比较有用的方法
```java
static void setIn(InputStream in) // 标准输入的重定向
static void setOut(PrintStream out) // 标准输出的重定向
static void setErr(PrintStream err) // 标准错误的重定向
/******************************/
static Map<String,String> getenv() // 返回所有的环境变量的键值对
static String getenv(String name) // 返回特定环境变量的值
/******************************/
static Properties getProperties() // 返回所有的系统属性
static String getProperty(String key) // 返回特定的系统属性的值
/******************************/
static void setProperties(Properties props) // 设置所有的系统属性
static String setProperty(String key, String value) // 设置特定的系统属性的值
/******************************/
static long currentTimeMillis() // 获取当前系统时间,用毫秒表示,从 1970 年开始。HotSpot VM 中可用
```
### arraycopy(…)方法
arraycopy(…)方法将指定原数组中的数据从指定位置复制到目标数组的指定位置。
```java
static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
```
```java
package com.ibelifly.commonclass.system;
public class Test1 {
public static void main(String[] args) {
int[] arr={23,45,20,67,57,34,98,95};
int[] dest=new int[8];
System.arraycopy(arr,4,dest,4,4);
for (int x:dest) {
System.out.print(x+" ");
}
}
}
```
### exit(int status)方法
exit(int status)方法用于终止当前运行的Java虚拟机。如果参数是0表示正常退出JVM如果参数非0表示异常退出JVM。
```java
package com.ibelifly.commonclass.system;
public class Test4 {
public static void main(String[] args) {
System.out.println("程序开始了");
System.exit(0); //因为此处已经终止当前运行的Java虚拟机故不会执行之后的代码
System.out.println("程序结束了");
}
}
```

View File

@@ -1,20 +0,0 @@
## Arrays类中的方法
### 方法概述
* 给数组赋值:通过 fill 方法。
* 对数组排序:通过 sort 方法,按升序。
* 比较数组:通过 equals 方法比较数组中元素值是否相等。
* 查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法操作。
### 具体方法
* public static int binarySearch(Object[] a, Object key)
用二分查找算法在给定数组中搜索给定值的对象(Byte,Int,double等)。数组在调用前必须排序好的。如果查找值包含在数组中,则返回搜索键的索引;否则返回 (-(插入点) - 1)。
* public static boolean equals(long[] a, long[] a2)
如果两个指定的 long 型数组彼此相等,则返回 true。如果两个数组包含相同数量的元素并且两个数组中的所有相应元素对都是相等的则认为这两个数组是相等的。换句话说如果两个数组以相同顺序包含相同的元素则两个数组是相等的。同样的方法适用于所有的其他基本数据类型ByteshortInt等
* public static void fill(int[] a, int val)
将指定的 int 值分配给指定 int 型数组指定范围中的每个元素。同样的方法适用于所有的其他基本数据类型ByteshortInt等
* public static void sort(Object[] a)
对指定对象数组根据其元素的自然顺序进行升序排列。同样的方法适用于所有的其他基本数据类型ByteshortInt等

View File

@@ -1,140 +0,0 @@
String与StringBuffer的区别
简单地说就是一个变量和常量的关系。StringBuffer对象的内容可以修改而String对象一旦产生后就不可以被修改重新赋值其实是两个对象。
StringBuffer的内部实现方式和String不同StringBuffer在进行字符串处理时不生成新的对象在内存使用上要优于String类。所以在实际使用时如果经常需要对一个字符串进行修改例如插入、删除等操作使用StringBuffer要更加适合一些。
String:在String类中没有用来改变已有字符串中的某个字符的方法由于不能改变一个java字符串中的某个单独字符所以在JDK文档中称String类的对象是不可改变的。然而不可改变的字符串具有一个很大的优点:编译器可以把字符串设为共享的。
StringBuffer:StringBuffer类属于一种辅助类可预先分配指定长度的内存块建立一个字符串缓冲区。这样使用StringBuffer类的append方法追加字符
比 String使用 + 操作符添加字符 到 一个已经存在的字符串后面有效率得多。因为使用 +
操作符每一次将字符添加到一个字符串中去时字符串对象都需要寻找一个新的内存空间来容纳更大的字符串这无凝是一个非常消耗时间的操作。添加多个字符也就意味着要一次又一次的对字符串重新分配内存。使用StringBuffer类就避免了这个问题。
StringBuffer是线程安全的在多线程程序中也可以很方便的进行使用但是程序的执行效率相对来说就要稍微慢一些。
StringBuffer的常用方法
StringBuffer类中的方法要偏重于对字符串的变化例如追加、插入和删除等这个也是StringBuffer和String类的主要区别。
1、append方法
public StringBuffer append(boolean b)
该方法的作用是追加内容到当前StringBuffer对象的末尾类似于字符串的连接。调用该方法以后StringBuffer对象的内容也发生改变例如
StringBuffer sb = new StringBuffer(“abc”);
sb.append(true);
则对象sb的值将变成”abctrue”。
使用该方法进行字符串的连接将比String更加节约内容例如应用于数据库SQL语句的连接例如
StringBuffer sb = new StringBuffer();
String user = “test”;
String pwd = “123”;
sb.append(“select \* from userInfo where username=“)
.append(user)
.append(“ and pwd=”)
.append(pwd);
这样对象sb的值就是字符串“select \* from userInfo where username=test and
pwd=123”。
2、deleteCharAt方法
public StringBuffer deleteCharAt(int index)
该方法的作用是删除指定位置的字符,然后将剩余的内容形成新的字符串。例如:
StringBuffer sb = new StringBuffer(“Test”);
sb. deleteCharAt(1);
该代码的作用删除字符串对象sb中索引值为1的字符也就是删除第二个字符剩余的内容组成一个新的字符串。所以对象sb的值变为”Tst”。
还存在一个功能类似的delete方法
public StringBuffer delete(int start,int end)
该方法的作用是删除指定区间以内的所有字符包含start不包含end索引值的区间。例如
StringBuffer sb = new StringBuffer(“TestString”);
sb. delete (1,4);
该代码的作用是删除索引值1(包括)到索引值4(不包括)之间的所有字符剩余的字符形成新的字符串。则对象sb的值是”TString”。
3、insert方法
public StringBuffer insert(int offset, String s)
该方法的作用是在StringBuffer对象中插入内容然后形成新的字符串。例如
StringBuffer sb = new StringBuffer(“TestString”);
sb.insert(4,“false”);
该示例代码的作用是在对象sb的索引值4的位置插入字符串false形成新的字符串则执行以后对象sb的值是”TestfalseString”。
4、reverse方法
public StringBuffer reverse()
该方法的作用是将StringBuffer对象中的内容反转然后形成新的字符串。例如
StringBuffer sb = new StringBuffer(“abc”);
sb.reverse();
经过反转以后对象sb中的内容将变为”cba”。
5、setCharAt方法
public void setCharAt(int index, char ch)
该方法的作用是修改对象中索引值为index位置的字符为新的字符ch。例如
StringBuffer sb = new StringBuffer(“abc”);
sb.setCharAt(1,D);
则对象sb的值将变成”aDc”。
6、trimToSize方法
public void trimToSize()
该方法的作用是将StringBuffer对象的中存储空间缩小到和字符串长度一样的长度减少空间的浪费。
7、构造方法
StringBuffer s0=new StringBuffer();分配了长16字节的字符缓冲区
StringBuffer s1=new StringBuffer(512);分配了512字节的字符缓冲区
8、获取字符串的长度: length()
StringBuffer s = new StringBuffer("www");
int i=s.length();
m.返回字符串的一部分值
substring(int start) //返回从start下标开始以后的字符串
substring(int start,int end) //返回从start到 end-1字符串
9.替换字符串
replace(int start,int end,String str)
s.replace(0,1,"qqq");
10.转换为不变字符串:toString()。

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

@@ -0,0 +1,142 @@
package cn.aofeng.demo.aspectj;
/**
* 模拟业务方法将被Aspectj织入代码增加功能。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class BusinessService {
public long add(int a, int b) {
return a+b;
}
public long add(int a, int b, int... other) {
long result = a + b;
for (int i : other) {
result += i;
}
return result;
}
public String join(String first, String... appends) {
if (null == first) {
throw new IllegalArgumentException("first is null");
}
StringBuilder buffer = new StringBuilder();
buffer.append(first);
for (String str : appends) {
buffer.append(str);
}
return buffer.toString();
}
public String addPrefix(String src) {
if (null == src) {
throw new IllegalArgumentException("src is null");
}
return "-->"+src;
}
public static void printLine(char style) {
if ('=' == style) {
System.out.println("========================================================================================");
} else if ('-' == style) {
System.out.println("----------------------------------------------------------------------------------------");
} else {
System.out.println(" ");
}
}
public static void main(String[] args) {
final BusinessService bs = new BusinessService();
System.out.println("1、执行方法add(int, int)");
RunMethod rm = new RunMethod() {
@Override
public void run() {
long result = bs.add(1, 2);
System.out.println(">>> 结果:" + result);
}
};
rm.execute();
System.out.println("2、执行方法add(int, int, int...)");
rm = new RunMethod() {
@Override
public void run() {
long result = bs.add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
System.out.println(">>> 结果:" + result);
}
};
rm.execute();
System.out.println("3、执行方法join(String, String...)");
rm = new RunMethod() {
@Override
public void run() {
String str = bs.join("first", "-second", "-third");
System.out.println(">>> 结果:" + str);
}
};
rm.execute();
System.out.println("4、执行方法join(String, String...)");
rm = new RunMethod() {
@Override
public void run() {
String str = bs.join(null, "-second", "-third");
System.out.println(">>> 结果:" + str);
}
};
rm.execute();
System.out.println("5、执行方法addPrefix(String)");
rm = new RunMethod() {
@Override
public void run() {
String str = bs.addPrefix("原字符串");
System.out.println(">>> 结果:" + str);
}
};
rm.execute();
System.out.println("6、执行方法addPrefix(String)");
rm = new RunMethod() {
@Override
public void run() {
String str = bs.addPrefix(null);
System.out.println(">>> 结果:" + str);
}
};
rm.execute();
}
public static abstract class RunMethod {
private char _style = '=';
public void execute() {
printLine(_style);
try {
run();
} catch (Exception e) {
e.printStackTrace(System.err);
}
printLine(_style);
printLine(' ');
}
public abstract void run();
}
}

View File

@@ -0,0 +1,122 @@
package cn.aofeng.demo.aspectj;
import org.apache.commons.lang.ArrayUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 拦截器。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
@Aspect
public class BusinessServiceInterceptor {
private static Logger _logger = LoggerFactory.getLogger(BusinessServiceInterceptor.class);
private char _style = '-';
/**
* <pre>
* Before通知不能修改方法传入的参数。
* </pre>
*/
@Before("execution(public * cn.aofeng.demo.aspectj.BusinessService.add(..))")
public void beforeAdd(JoinPoint joinPoint) {
_logger.info( String.format("拦截到方法:%s, 传入参数:%s",
joinPoint.getSignature().getName(),
ArrayUtils.toString(joinPoint.getArgs()) ) );
BusinessService.printLine(_style);
}
/**
* <pre>
* After通知不能修改方法的返回值。
* 如果被拦截的方法抛出异常,拦截代码仍然正常执行。
* </pre>
*/
@After("execution(public * cn.aofeng.demo.aspectj.BusinessService.join(..))")
public void beforeAddSupportMultiArgs(JoinPoint joinPoint) {
_logger.info("Signature.name"+joinPoint.getSignature().getName());
_logger.info("Args" + ArrayUtils.toString(joinPoint.getArgs()));
_logger.info("Target" + ArrayUtils.toString(joinPoint.getTarget()));
_logger.info("This" + ArrayUtils.toString(joinPoint.getThis()));
_logger.info("Kind" + ArrayUtils.toString(joinPoint.getKind()));
_logger.info("SourceLocation" + ArrayUtils.toString(joinPoint.getSourceLocation()));
// 试图修改传入的参数
joinPoint.getArgs()[0] = "100";
BusinessService.printLine(_style);
}
/**
* <pre>
* AfterReturning通知不能修改方法的返回值。
* 如果被拦截的方法抛出异常,拦截代码不再执行。
* </pre>
*/
@AfterReturning(pointcut="execution(public * cn.aofeng.demo.aspectj.BusinessService.join(..))", returning="result")
public void afterReturnAdd(JoinPoint joinPoint, Object result) {
_logger.info( String.format("拦截到方法:%s, 传入参数:%s, 执行结果:%s",
joinPoint.getSignature().getName(),
ArrayUtils.toString(joinPoint.getArgs()),
result) );
// 试图修改返回值
result = "hello, changed";
BusinessService.printLine(_style);
}
/**
* <pre>
* 只在被拦截的方法抛出异常时才执行。
* </pre>
*/
@AfterThrowing(pointcut="execution(public * cn.aofeng.demo.aspectj.BusinessService.join(..))", throwing="ex")
public void afterThrowingAdd(JoinPoint joinPoint, Exception ex) {
_logger.info( String.format("拦截到方法:%s, 传入参数:%s",
joinPoint.getSignature().getName(),
ArrayUtils.toString(joinPoint.getArgs()) ) );
if (null != ex) {
_logger.info("拦截到异常:", ex);
}
BusinessService.printLine(_style);
}
/**
* <pre>
* {@link ProceedingJoinPoint}只能在Around通知中使用。
* Around通知可以修改被拦截方法的传入参数和返回值。
* </pre>
*/
@Around("execution(public * cn.aofeng.demo.aspectj.BusinessService.addPrefix(..))")
public Object afterAround(ProceedingJoinPoint joinPoint) throws Throwable {
_logger.info( String.format("拦截到方法:%s, 传入参数:%s",
joinPoint.getSignature().getName(),
ArrayUtils.toString(joinPoint.getArgs()) ) );
Object result = null;
try {
result = joinPoint.proceed();
_logger.info("执行结果:" + result);
} catch (Throwable e) {
throw e;
} finally {
BusinessService.printLine(_style);
}
return result;
}
}

View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="JavaTutorial" default="runNormal" basedir="../../../../../">
<property name="project.src.dir" value="${basedir}/src" />
<property name="project.lib.dir" value="${basedir}/lib" />
<property name="project.conf.dir" value="${basedir}/conf" />
<property name="project.tmp.dir" value="${basedir}/tmp" />
<property name="project.target.dir" value="${basedir}/classes" />
<path id="project.classpath">
<fileset dir="${project.lib.dir}">
<include name="*.jar" />
</fileset>
</path>
<taskdef resource="org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties">
<classpath refid="project.classpath" />
</taskdef>
<target name="prepare">
<delete dir="${project.target.dir}" />
<mkdir dir="${project.target.dir}"/>
<copy todir="${project.target.dir}">
<fileset dir="${project.conf.dir}">
<include name="**/*.properties" />
<include name="**/*.xml" />
</fileset>
</copy>
</target>
<target name="compileWithIajc" depends="prepare">
<echo message="compile with iajc" />
<iajc destdir="${project.target.dir}" sourceroots="${project.src.dir}" source="1.8" target="1.8" encoding="UTF-8">
<classpath refid="project.classpath" />
</iajc>
</target>
<!-- 运行AspectJ编译时织入功能的代码 -->
<target name="runNormal" depends="compileWithIajc">
<java classname="cn.aofeng.demo.aspectj.BusinessService">
<classpath refid="project.classpath" />
<classpath location="${project.target.dir}" />
</java>
</target>
<target name="compileWithJavac" depends="prepare">
<echo message="compile with javac" />
<javac destdir="${project.target.dir}" srcdir="${project.src.dir}" source="1.8" target="1.8"
encoding="UTF-8" debug="true" includeantruntime="false">
<classpath refid="project.classpath" />
</javac>
</target>
<!-- 载入代码时织入功能LTW -->
<target name="runLTW" depends="compileWithJavac">
<java classname="cn.aofeng.demo.aspectj.BusinessService" fork="true">
<jvmarg value="-Daj.weaving.verbose=true" />
<jvmarg value="-javaagent:${project.lib.dir}/aspectjweaver-1.8.10.jar"/>
<classpath refid="project.classpath" />
<classpath location="${project.target.dir}" />
</java>
</target>
</project>

View File

@@ -0,0 +1,205 @@
package cn.aofeng.demo.dbutils;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.MapHandler;
import org.apache.commons.dbutils.handlers.MapListHandler;
import org.apache.log4j.Logger;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
/**
* Apache DbUtils使用示例。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class DbUtilsDemo {
private static Logger _logger = Logger.getLogger(DbUtilsDemo.class);
/**
* 创建JDBC连接池。
*
* @param pros 数据库连接信息里面包含4个键值对。
* <pre>
* jdbcDriver => JDBC驱动类名称
* url => 数据库JDBC连接地址
* user => 连接数据库的用户名
* password => 连接数据库的密码
* <pre>
* @return 连接池对象
*/
private DataSource createDataSource(Properties pros) {
MysqlDataSource ds = new MysqlDataSource();
ds.setURL(pros.getProperty("url"));
ds.setUser(pros.getProperty("user"));
ds.setPassword(pros.getProperty("password"));
return ds;
}
/**
* 将查询结果集转换成Bean列表返回。
*
* @param ds JDBC连接池
*/
public void queryBeanList(DataSource ds) {
String sql = "select userId, userName, gender, age from student";
QueryRunner run = new QueryRunner(ds);
ResultSetHandler<List<Student>> handler = new BeanListHandler<Student>(Student.class);
List<Student> result = null;
try {
result = run.query(sql, handler);
} catch (SQLException e) {
_logger.error("获取JDBC连接出错或执行SQL出错", e);
}
if (null == result) {
return;
}
for (Student student : result) {
System.out.println(student);
}
}
/**
* 将查询结果转换成Bean返回。
*
* @param ds JDBC连接池
*/
public void queryBean(DataSource ds, int userId) {
String sql = "select userId, userName, gender, age from student where userId=?";
QueryRunner run = new QueryRunner(ds);
ResultSetHandler<Student> handler = new BeanHandler<Student>(Student.class);
Student result = null;
try {
result = run.query(sql, handler, userId);
} catch (SQLException e) {
_logger.error("获取JDBC连接出错或执行SQL出错", e);
}
System.out.println(result);
}
/**
* 将查询结果集转换成键值对列表返回。
*
* @param ds JDBC连接池
*/
public void queryMapList(DataSource ds) {
String sql = "select userId, userName, gender, age from student";
QueryRunner run = new QueryRunner(ds);
ResultSetHandler<List<Map<String, Object>>> handler = new MapListHandler();
List<Map<String, Object>> result = null;
try {
result = run.query(sql, handler);
} catch (SQLException e) {
_logger.error("获取JDBC连接出错或执行SQL出错", e);
}
if (null == result) {
return;
}
for (Map<String, Object> map : result) {
System.out.println(map);
}
}
/**
* 将查询结果集转换成键值对列表返回。
*
* @param ds JDBC连接池
* @param userId 用户编号
*/
public void queryMap(DataSource ds, int userId) {
String sql = "select userId, userName, gender, age from student where userId=?";
QueryRunner run = new QueryRunner(ds);
ResultSetHandler<Map<String, Object>> handler = new MapHandler();
Map<String, Object> result = null;
try {
result = run.query(sql, handler, userId);
} catch (SQLException e) {
_logger.error("获取JDBC连接出错或执行SQL出错", e);
}
System.out.println(result);
}
/**
* 将查询结果集转换成键值对列表返回。
*
* @param ds JDBC连接池
*/
public void queryCustomHandler(DataSource ds) {
String sql = "select userId, userName, gender, age from student";
// 新实现一个ResultSetHandler
ResultSetHandler<List<Student>> handler = new ResultSetHandler<List<Student>>() {
@Override
public List<Student> handle(ResultSet resultset)
throws SQLException {
List<Student> result = new ArrayList<Student>();
while (resultset.next()) {
Student student = new Student();
student.setUserId(resultset.getInt("userId"));
student.setUserName(resultset.getString("userName"));
student.setGender(resultset.getString("gender"));
student.setAge(resultset.getInt("age"));
result.add(student);
}
return result;
}
};
QueryRunner run = new QueryRunner(ds);
List<Student> result = null;
try {
result = run.query(sql, handler);
} catch (SQLException e) {
_logger.error("获取JDBC连接出错或执行SQL出错", e);
}
if (null == result) {
return;
}
for (Student student : result) {
System.out.println(student);
}
}
public static void main(String[] args) {
Properties pros = new Properties();
pros.put("jdbcDriver", "com.mysql.jdbc.Driver");
pros.put("url", "jdbc:mysql://192.168.56.102:19816/test?useUnicode=true&characterEncoding=UTF8");
pros.put("user", "uzone");
pros.put("password", "uzone");
DbUtilsDemo demo = new DbUtilsDemo();
DataSource ds = demo.createDataSource(pros);
demo.queryBeanList(ds);
demo.queryBean(ds, 1);
demo.queryBean(ds, 9);
demo.queryMapList(ds);
demo.queryMap(ds, 3);
demo.queryMap(ds, 9);
demo.queryCustomHandler(ds);
}
}

View File

@@ -0,0 +1,226 @@
Apache DbUtils 使用教程
===
用JDBC编程时需要关注和处理的内容非常多而且很容易造成连接资源没有释放导致泄漏的问题。一个普通的查询操作其处理过程如下
1. 创建Connection。
1. 创建Statement。
1. 执行SQL生成ResultSet历遍ResultSet中的所有行记录提取列数据并转换成所需的对象。
1. 关闭ResultSet。
1. 关闭Statement。
1. 关闭Connection。
`Apache DbUtils`对JDBC操作进行了轻量地封装解决了两个问题
1. 自动创建和释放连接资源,不再有泄漏问题。
2. 自动将Result转换成对象。填入不同的`ResultSetHandler`可将ResultSet转换成不同的对象。
使得代码更加简洁和健壮,让你将精力更多地投入到业务处理中。
预备
---
* MySQL数据库
* commons-dbutils-1.5.jar
* mysql-connector-java-5.1.22-bin.jar
创建表和初始化数据
---
1、表结构。
```sql
CREATE TABLE `student` (
`userId` int(11) NOT NULL,
`userName` varchar(30) NOT NULL,
`gender` char(1) NOT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`userId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
```
2、初始化数据。
| userId | userName | gender | age |
| :--- | :--- | :--- | :--- |
| 1 | 张三 | M | 20 |
| 2 | 李四 | M | 21 |
| 3 | 王五 | M | 22 |
| 4 | 小明 | M | 6 |
| 5 | 小丽 | F | 9 |
编写查询代码
---
###1、将查询结果转换成POJO对象列表。
1定义一个与表student相对应的对象Student[源代码下载](https://raw.githubusercontent.com/aofeng/JavaDemo/master/src/cn/aofeng/demo/dbutils/Student.java))。
2使用`BeanListHandler`将查询结果转换成POJO对象列表。
```java
public void queryBeanList(DataSource ds) {
String sql = "select userId, userName, gender, age from student";
QueryRunner run = new QueryRunner(ds);
ResultSetHandler<List<Student>> handler = new BeanListHandler<Student>(Student.class);
List<Student> result = null;
try {
result = run.query(sql, handler);
} catch (SQLException e) {
_logger.error("获取JDBC连接出错或执行SQL出错", e);
}
if (null == result) {
return;
}
for (Student student : result) {
System.out.println(student);
}
}
```
执行queryBeanList方法控制台输出如下信息
```
Student [userId=1, userName=张三, gender=M, age=20]
Student [userId=2, userName=李四, gender=M, age=21]
Student [userId=3, userName=王五, gender=M, age=22]
Student [userId=4, userName=小明, gender=M, age=6]
Student [userId=5, userName=小丽, gender=F, age=9]
```
###2、将查询结果转换成一个POJO象。
1定义一个与表student相对应的对象Student[源代码下载](https://raw.githubusercontent.com/aofeng/JavaDemo/master/src/cn/aofeng/demo/dbutils/Student.java))。
2使用`BeanHandler`将查询结果转换成一个POJO对象。
```java
public void queryBean(DataSource ds, int userId) {
String sql = "select userId, userName, gender, age from student where userId=?";
QueryRunner run = new QueryRunner(ds);
ResultSetHandler<Student> handler = new BeanHandler<Student>(Student.class);
Student result = null;
try {
result = run.query(sql, handler, userId);
} catch (SQLException e) {
_logger.error("获取JDBC连接出错或执行SQL出错", e);
}
System.out.println(result);
}
```
执行queryBean方法控制台输出如下信息
```
Student [userId=1, userName=张三, gender=M, age=20]
```
###3、将查询结果转换成Map对象列表。
1使用`MapListHandler`将ResultSet转换成Map对象列表。
```java
public void queryMapList(DataSource ds) {
String sql = "select userId, userName, gender, age from student";
QueryRunner run = new QueryRunner(ds);
ResultSetHandler<List<Map<String, Object>>> handler = new MapListHandler();
List<Map<String, Object>> result = null;
try {
result = run.query(sql, handler);
} catch (SQLException e) {
_logger.error("获取JDBC连接出错或执行SQL出错", e);
}
if (null == result) {
return;
}
for (Map<String, Object> map : result) {
System.out.println(map);
}
}
```
执行queryMapList方法控制台输出如下信息
```
{age=20, userId=1, gender=M, userName=张三}
{age=21, userId=2, gender=M, userName=李四}
{age=22, userId=3, gender=M, userName=王五}
{age=6, userId=4, gender=M, userName=小明}
{age=9, userId=5, gender=F, userName=小丽}
```
###4、将查询结果转换成一个Map对象。
1使用`MapHandler`将ResultSet转换成一个Map对象。
```java
public void queryMap(DataSource ds, int userId) {
String sql = "select userId, userName, gender, age from student where userId=?";
QueryRunner run = new QueryRunner(ds);
ResultSetHandler<Map<String, Object>> handler = new MapHandler();
Map<String, Object> result = null;
try {
result = run.query(sql, handler, userId);
} catch (SQLException e) {
_logger.error("获取JDBC连接出错或执行SQL出错", e);
}
System.out.println(result);
}
```
执行queryMap方法控制台输出如下信息
```
{age=22, userId=3, gender=M, userName=王五}
```
###5、自定义ResultSetHandler。
`Apache DbUtils`自带的各种`ResultSetHandler`已经可以满足绝大部分的查询需求,如果有一些特殊的要求满足不了,可以自己实现一个。
```java
public void queryCustomHandler(DataSource ds) {
String sql = "select userId, userName, gender, age from student";
// 新实现一个ResultSetHandler
ResultSetHandler<List<Student>> handler = new ResultSetHandler<List<Student>>() {
@Override
public List<Student> handle(ResultSet resultset)
throws SQLException {
List<Student> result = new ArrayList<Student>();
while (resultset.next()) {
Student student = new Student();
student.setUserId(resultset.getInt("userId"));
student.setUserName(resultset.getString("userName"));
student.setGender(resultset.getString("gender"));
student.setAge(resultset.getInt("age"));
result.add(student);
}
return result;
}
};
QueryRunner run = new QueryRunner(ds);
List<Student> result = null;
try {
result = run.query(sql, handler);
} catch (SQLException e) {
_logger.error("获取JDBC连接出错或执行SQL出错", e);
}
if (null == result) {
return;
}
for (Student student : result) {
System.out.println(student);
}
}
```
执行queryCustomHandler方法控制台输出如下信息
```
Student [userId=1, userName=张三, gender=M, age=20]
Student [userId=2, userName=李四, gender=M, age=21]
Student [userId=3, userName=王五, gender=M, age=22]
Student [userId=4, userName=小明, gender=M, age=6]
Student [userId=5, userName=小丽, gender=F, age=9]
```
附录
---
* [DEMO完整源代码](https://github.com/aofeng/JavaDemo/edit/master/src/cn/aofeng/demo/dbutils)
参考资料
---
* [commons-dbutils example](http://commons.apache.org/proper/commons-dbutils/examples.html)

View File

@@ -0,0 +1,86 @@
package cn.aofeng.demo.dbutils;
/**
* 表student的POJO对象。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class Student {
private Integer userId;
private String userName;
private String gender;
private Integer age;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((age == null) ? 0 : age.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Student)) {
return false;
}
Student other = (Student) obj;
if (age == null) {
if (other.age != null) {
return false;
}
} else if (!age.equals(other.age)) {
return false;
}
return true;
}
@Override
public String toString() {
return "Student [userId=" + userId + ", userName=" + userName
+ ", gender=" + gender + ", age=" + age + "]";
}
}

View File

@@ -0,0 +1,258 @@
用EasyMock生成模拟对象进行单元测试 Testing with EasyMock
===
当项目达到一定规模时会根据业务或功能进行模块化,这时研发团队在协作时会碰到一个问题:如何让相互依赖的各模块并行开发?常用的办法是:各模块之间有清晰的边界,模块之间的交互通过接口。在设计阶段定义模块的接口,然后各个模块开发时遵循接口的定义进行实现和调用。
OK并行开发的问题解决了。但不同的模块因其复杂度和工作量不同进度不一致。当研发同学完成所负责的模块时但依赖的模块还没有完成开发不好进行测试。
这里就讲如何用EasyMock生成Mock Object模拟所依赖模块的接口来完成单元测试。
![EasyMock](http://img1.ph.126.net/wErY4T7Ne-KeyB66As_PLA==/6608670713840748025.gif)
预备
---
* easymock-3.2.jar
* easymockclassextension-3.2.jar
* cglib-2.2.2.jar
* objenesis-1.2.jar
* asm-3.1.jar
* asm-commons-3.1.jar
* asm-util-3.1.jar
* gson-2.2.4.jar // *用于处理JSON*
上面的jar文件均可从[MAVEN仓库](http://mvnrepository.com)下载。
使用EasyMock的五部曲
---
1、引入EasyMock。
```java
import static org.easymock.EasyMock.*;
```
2、创建Mock Object。
```java
mock = createMock(InterfaceOrClass.class);
```
3、设置Mock Object的行为和预期结果。
```java
mock.doSomeThing();
expectLastCall().times(1); // 至少要调用一次doSomeThing方法
```
或者
```java
// 模拟抛出异常
expect(mock.getSomeThing(anyString()))
.andThrow(new IOException("单元测试特意抛的异常"));
// 模拟返回指定的数据
expect(mock.getSomeThing(anyString()))
.andReturn("abcd");
```
4、设置Mock Object变成可用状态。
```java
replay(mock);
```
5、运行单元测试代码会调用到Mock Object的方法
```java
// 如果是校验调用次数就要用到verify方法
verify(mock);
```
实践
---
### 业务代码
[源码下载](https://raw.githubusercontent.com/aofeng/JavaDemo/master/src/cn/aofeng/demo/easymock/UserService.java)
```java
package cn.aofeng.demo.easymock;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Map;
import org.apache.log4j.Logger;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import cn.aofeng.demo.jetty.HttpGet;
/**
* 用户相关服务。如:获取用户昵称。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class UserService {
private static Logger _logger = Logger.getLogger(UserService.class);
private HttpGet _httpGet = new HttpGet();
/**
* 根据用户的账号ID获取昵称。
*
* @param accountId 用户的账号ID
* @return 如果账号ID有效且请求成功返回昵称否则返回默认的昵称"用户xxx"。
*/
public String getNickname(String accountId) {
String targetUrl = "http://192.168.56.102:8080/user?method=getNickname&accountId="+accountId;
String response = null;
try {
response = _httpGet.getSomeThing(targetUrl);
} catch (IOException e) {
_logger.error("获取用户昵称时出错,账号ID:"+accountId, e);
}
if (null != response) {
// 响应数据结构示例:{"nickname":"张三"}
Type type = new TypeToken<Map<String, String>>() {}.getType();
Map<String, String> data = new Gson().fromJson(response, type);
if (null != data && data.containsKey("nickname")) {
return data.get("nickname");
}
}
return "用户"+accountId;
}
protected void setHttpGet(HttpGet httpGet) {
this._httpGet = httpGet;
}
}
```
[源码下载](https://raw.githubusercontent.com/aofeng/JavaDemo/master/src/cn/aofeng/demo/jetty/HttpGet.java)
```java
package cn.aofeng.demo.jetty;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import org.apache.commons.io.IOUtils;
/**
* HTTP GET请求。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class HttpGet {
public String getSomeThing(String urlStr) throws IOException {
URL url = new URL(urlStr);
HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
urlConn.setConnectTimeout(3000);
urlConn.setRequestMethod("GET");
urlConn.connect();
InputStream ins = null;
try {
if (200 == urlConn.getResponseCode()) {
ins = urlConn.getInputStream();
ByteArrayOutputStream outs = new ByteArrayOutputStream(1024);
IOUtils.copy(ins, outs);
return outs.toString("UTF-8");
}
} catch (IOException e) {
throw e;
} finally {
IOUtils.closeQuietly(ins);
}
return null;
}
}
```
### 单元测试代码
[源码下载](https://raw.githubusercontent.com/aofeng/JavaDemo/master/src/cn/aofeng/demo/easymock/UserServiceTest.java)
```java
package cn.aofeng.demo.easymock;
import static org.junit.Assert.*;
import java.io.IOException;
import static org.easymock.EasyMock.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import cn.aofeng.demo.jetty.HttpGet;
/**
* {@link UserService}的单元测试用例。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class UserServiceTest {
private HttpGet _mock = createMock(HttpGet.class);
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
reset(_mock);
}
/**
* 测试用例:获取用户昵称 <br/>
* 前置条件:
* <pre>
* 网络请求时发生IO异常
* </pre>
*
* 测试结果:
* <pre>
* 返回默认的用户昵称"用户xxx"
* </pre>
*/
@Test
public void testGetNickname4OccursIOError() throws IOException {
// 设置Mock
expect(_mock.getSomeThing(anyString()))
.andThrow(new IOException("单元测试特意抛的异常"));
replay(_mock);
UserService us = new UserService();
us.setHttpGet(_mock);
String nickname = us.getNickname("123456");
verify(_mock); // 校验mock
assertEquals("用户123456", nickname); // 检查返回值
}
/**
* 测试用例:获取用户昵称 <br/>
* 前置条件:
* <pre>
* 1、网络请求成功。
* 2、响应状态码为200且响应内容符合接口定义{\"nickname\":\"张三\"})。
* </pre>
*
* 测试结果:
* <pre>
* 返回"张三"
* </pre>
*/
@Test
public void testGetNickname4Success() throws IOException {
// 设置Mock
_mock.getSomeThing(anyString());
expectLastCall().andReturn("{\"nickname\":\"张三\"}");
expectLastCall().times(1);
replay(_mock);
UserService us = new UserService();
us.setHttpGet(_mock);
String nickname = us.getNickname("123456");
verify(_mock); // 校验方法的调用次数
assertEquals("张三", nickname); // 校验返回值
}
}
```

View File

@@ -0,0 +1,56 @@
package cn.aofeng.demo.easymock;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Map;
import org.apache.log4j.Logger;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import cn.aofeng.demo.jetty.HttpGet;
/**
* 用户相关服务。如:获取用户昵称。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class UserService {
private static Logger _logger = Logger.getLogger(UserService.class);
private HttpGet _httpGet = new HttpGet();
/**
* 根据用户的账号ID获取昵称。
*
* @param accountId 用户的账号ID
* @return 如果账号ID有效且请求成功返回昵称否则返回默认的昵称"用户xxx"。
*/
public String getNickname(String accountId) {
String targetUrl = "http://192.168.56.102:8080/user?method=getNickname&accountId="+accountId;
String response = null;
try {
response = _httpGet.getSomeThing(targetUrl);
} catch (IOException e) {
_logger.error("获取用户昵称时出错,账号ID:"+accountId, e);
}
if (null != response) {
// 响应数据结构示例:{"nickname":"张三"}
Type type = new TypeToken<Map<String, String>>() {}.getType();
Map<String, String> data = new Gson().fromJson(response, type);
if (null != data && data.containsKey("nickname")) {
return data.get("nickname");
}
}
return "用户"+accountId;
}
protected void setHttpGet(HttpGet httpGet) {
this._httpGet = httpGet;
}
}

View File

@@ -0,0 +1,87 @@
package cn.aofeng.demo.easymock;
import static org.junit.Assert.*;
import java.io.IOException;
import static org.easymock.EasyMock.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import cn.aofeng.demo.jetty.HttpGet;
/**
* {@link UserService}的单元测试用例。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class UserServiceTest {
private HttpGet _mock = createMock(HttpGet.class);
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
reset(_mock);
}
/**
* 测试用例:获取用户昵称 <br/>
* 前置条件:
* <pre>
* 网络请求时发生IO异常
* </pre>
*
* 测试结果:
* <pre>
* 返回默认的用户昵称"用户xxx"
* </pre>
*/
@Test
public void testGetNickname4OccursIOError() throws IOException {
// 设置Mock
expect(_mock.getSomeThing(anyString()))
.andThrow(new IOException("单元测试特意抛的异常"));
replay(_mock);
UserService us = new UserService();
us.setHttpGet(_mock);
String nickname = us.getNickname("123456");
verify(_mock); // 校验mock
assertEquals("用户123456", nickname); // 检查返回值
}
/**
* 测试用例:获取用户昵称 <br/>
* 前置条件:
* <pre>
* 1、网络请求成功。
* 2、响应状态码为200且响应内容符合接口定义{\"nickname\":\"张三\"})。
* </pre>
*
* 测试结果:
* <pre>
* 返回"张三"
* </pre>
*/
@Test
public void testGetNickname4Success() throws IOException {
// 设置Mock
_mock.getSomeThing(anyString());
expectLastCall().andReturn("{\"nickname\":\"张三\"}");
expectLastCall().times(1);
replay(_mock);
UserService us = new UserService();
us.setHttpGet(_mock);
String nickname = us.getNickname("123456");
verify(_mock); // 校验方法的调用次数
assertEquals("张三", nickname); // 校验返回值
}
}

View File

@@ -0,0 +1,54 @@
package cn.aofeng.demo.encrypt;
import static cn.aofeng.demo.util.LogUtil.log;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import org.apache.commons.codec.binary.Base64;
/**
* AES加密与解密。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class AES extends EncryptAndDecrypt {
public final String encryptType = "AES/CBC/PKCS5Padding";
public final String algorithmParam = "abcdefgh12345678";
public final String key = "abcdefgh_1234567";
public void execute(String data) throws InvalidKeyException,
IllegalBlockSizeException, BadPaddingException,
UnsupportedEncodingException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidAlgorithmParameterException {
SecretKey secretKey = createSecretKey("AES", key);
byte[] secretData = encrypt(encryptType, secretKey, data,
algorithmParam);
log("使用%s加密后的数据", encryptType);
log(Base64.encodeBase64String(secretData));
String srcStr = decrypt(encryptType, secretKey, secretData,
algorithmParam);
log("解密后的数据:\n%s", srcStr);
}
public static void main(String[] args) throws UnsupportedEncodingException,
InvalidKeyException, IllegalBlockSizeException,
BadPaddingException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidAlgorithmParameterException {
String data = "炎黄汉字english,do it,abcdefghijklmnopqrstuvwxyz,0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ, ~!@#$%^&*()_+=-";
log("待加密的数据:\n%s", data);
AES aes = new AES();
aes.execute(data);
}
}

View File

@@ -0,0 +1,51 @@
package cn.aofeng.demo.encrypt;
import static cn.aofeng.demo.util.LogUtil.log;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import org.apache.commons.codec.binary.Base64;
/**
* Blowfish加密解密。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class Blowfish extends EncryptAndDecrypt {
public final String encryptType = "Blowfish";
public final String key = "abcdefgh_1234567";
public void execute(String data) throws InvalidKeyException,
IllegalBlockSizeException, BadPaddingException,
UnsupportedEncodingException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidAlgorithmParameterException {
SecretKey secretKey = createSecretKey(encryptType, key);
byte[] secretData = encrypt(encryptType, secretKey, data);
log("使用%s加密后的数据", encryptType);
log(Base64.encodeBase64String(secretData));
String srcStr = decrypt(encryptType, secretKey, secretData);
log("解密后的数据:\n%s", srcStr);
}
public static void main(String[] args) throws UnsupportedEncodingException,
InvalidKeyException, IllegalBlockSizeException,
BadPaddingException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidAlgorithmParameterException {
String data = "炎黄汉字english,do it,abcdefghijklmnopqrstuvwxyz,0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ, ~!@#$%^&*()_+=-";
log("待加密的数据:\n%s", data);
Blowfish bf = new Blowfish();
bf.execute(data);
}
}

View File

@@ -0,0 +1,125 @@
package cn.aofeng.demo.encrypt;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
/**
* 加密与解密。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class EncryptAndDecrypt {
public final static String CHARSET = "utf8";
/**
* 创建安全密钥。
*
* @param encryptType
* 加密方式AESBlowfish。详情查看<a href="https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Cipher">Java Cryptography Architecture Standard Algorithm Name Documentation</a>
* @param keyStr
* 密钥明文
* @return 安全密钥
* @throws UnsupportedEncodingException
* 不支持指定的字符集编码
*/
public SecretKey createSecretKey(String encryptType, String keyStr)
throws UnsupportedEncodingException {
byte[] secretKeyData = keyStr.getBytes(CHARSET);
SecretKeySpec sks = new SecretKeySpec(secretKeyData, encryptType);
return sks;
}
/**
* 加密数据。
*
* @param encryptType 加密方式AESBlowfish。详情查看<a href="https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Cipher">Java Cryptography Architecture Standard Algorithm Name Documentation</a>
* @param secretKey 密钥
* @param srcData 待加密的源数据
* @return 加密后的二进制数据(字节数组)
* @see #encrypt(String, SecretKey, String, String)
*/
public byte[] encrypt(String encryptType, SecretKey secretKey,
String srcData) throws InvalidKeyException,
IllegalBlockSizeException, BadPaddingException,
UnsupportedEncodingException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidAlgorithmParameterException {
return encrypt(encryptType, secretKey, srcData, null);
}
/**
* 加密数据。
*
* @param encryptType 加密类型AES/CBC/PKCS5Padding
* @param secretKey 密钥
* @param srcData 待加密的源数据
* @param algorithmParam 某些加密算法的附加参数
* @return 加密后的二进制数据(字节数组)
*/
public byte[] encrypt(String encryptType, SecretKey secretKey,
String srcData, String algorithmParam) throws InvalidKeyException,
IllegalBlockSizeException, BadPaddingException,
UnsupportedEncodingException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidAlgorithmParameterException {
Cipher encrpyt = Cipher.getInstance(encryptType);
if (null == algorithmParam) {
encrpyt.init(Cipher.ENCRYPT_MODE, secretKey);
} else {
IvParameterSpec iv = new IvParameterSpec(
algorithmParam.getBytes(CHARSET));
encrpyt.init(Cipher.ENCRYPT_MODE, secretKey, iv);
}
byte[] secretData = encrpyt.doFinal(srcData.getBytes(CHARSET));
return secretData;
}
/**
* 解密数据。
*
* @param decryptType 解密方式AESBlowfish。详情查看<a href="https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Cipher">Java Cryptography Architecture Standard Algorithm Name Documentation</a>
* @param secretKey 密钥
* @param secretData 待解密的数据
* @return 解密后的数据
* @see #decrypt(String, SecretKey, byte[], String)
*/
public String decrypt(String decryptType, SecretKey secretKey,
byte[] secretData) throws InvalidKeyException,
IllegalBlockSizeException, BadPaddingException,
UnsupportedEncodingException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidAlgorithmParameterException {
return decrypt(decryptType, secretKey, secretData, null);
}
public String decrypt(String decryptType, SecretKey secretKey,
byte[] secretData, String algorithmParam)
throws InvalidKeyException, IllegalBlockSizeException,
BadPaddingException, UnsupportedEncodingException,
NoSuchAlgorithmException, NoSuchPaddingException,
InvalidAlgorithmParameterException {
Cipher decrypt = Cipher.getInstance(decryptType);
if (null == algorithmParam) {
decrypt.init(Cipher.DECRYPT_MODE, secretKey);
} else {
IvParameterSpec iv = new IvParameterSpec(
algorithmParam.getBytes(CHARSET));
decrypt.init(Cipher.DECRYPT_MODE, secretKey, iv);
}
byte[] srcData = decrypt.doFinal(secretData);
String srcStr = new String(srcData, CHARSET);
return srcStr;
}
}

View File

@@ -0,0 +1,53 @@
package cn.aofeng.demo.encrypt;
import static cn.aofeng.demo.util.LogUtil.log;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import org.apache.commons.codec.binary.Base64;
/**
* HMAC-SHA1签名算法。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class HmacSha1 {
public final String encryptType = "HmacSHA1";
public final String key = "abcdefgh_1234567";
public void execute(String data) throws UnsupportedEncodingException,
NoSuchAlgorithmException, InvalidKeyException {
EncryptAndDecrypt ead = new EncryptAndDecrypt();
byte[] srcData = data.getBytes(EncryptAndDecrypt.CHARSET);
SecretKey secretKey = ead.createSecretKey(encryptType, key); // 生成密钥对象
Mac mac = Mac.getInstance(encryptType);
mac.init(secretKey);
byte[] result = mac.doFinal(srcData);
log("使用%s签名后的数据", encryptType);
log(Base64.encodeBase64String(result));
}
public static void main(String[] args) throws InvalidKeyException,
UnsupportedEncodingException, IllegalBlockSizeException,
BadPaddingException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidAlgorithmParameterException {
String data = "炎黄汉字english,do it,abcdefghijklmnopqrstuvwxyz,0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ, ~!@#$%^&*()_+=-";
log("待签名的数据:\n%s", data);
HmacSha1 hs = new HmacSha1();
hs.execute(data);
}
}

View File

@@ -0,0 +1,70 @@
package cn.aofeng.demo.encrypt;
import static cn.aofeng.demo.util.LogUtil.log;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
/**
* 加密解密性能比较。
*
* @author <a href="mailto:nieyong@ucweb.com">聂勇</a>
*/
public class PerformanceCompare extends EncryptAndDecrypt {
public void blowfishPerformence(String data)
throws UnsupportedEncodingException, InvalidKeyException,
IllegalBlockSizeException, BadPaddingException,
NoSuchAlgorithmException, NoSuchPaddingException,
InvalidAlgorithmParameterException {
Blowfish bf = new Blowfish();
SecretKey secretKey = createSecretKey(bf.encryptType, bf.key);
long startTime = System.currentTimeMillis();
for (int j = 0; j < 100000; j++) {
bf.encrypt(bf.encryptType, secretKey, data+j);
}
long endTime = System.currentTimeMillis();
long usedTime = endTime - startTime;
log("使用%s进行%d次加密消耗时间%d毫秒", bf.encryptType, 100000, usedTime);
}
public void aesPerformence(String data)
throws UnsupportedEncodingException, InvalidKeyException,
IllegalBlockSizeException, BadPaddingException,
NoSuchAlgorithmException, NoSuchPaddingException,
InvalidAlgorithmParameterException {
AES aes = new AES();
SecretKey secretKey = createSecretKey("AES", aes.key);
long startTime = System.currentTimeMillis();
for (int j = 0; j < 100000; j++) {
aes.encrypt(aes.encryptType, secretKey, data+j,
aes.algorithmParam);
}
long endTime = System.currentTimeMillis();
long usedTime = endTime - startTime;
log("使用%s进行%d次加密消耗时间%d毫秒", aes.encryptType, 100000, usedTime);
}
public static void main(String[] args) throws InvalidKeyException,
UnsupportedEncodingException, IllegalBlockSizeException,
BadPaddingException, NoSuchAlgorithmException,
NoSuchPaddingException, InvalidAlgorithmParameterException {
String data = "炎黄汉字english,do it,abcdefghijklmnopqrstuvwxyz,0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ, ~!@#$%^&*()_+=-";
log("待加密的数据:\n%s", data);
PerformanceCompare pc = new PerformanceCompare();
// AES
pc.aesPerformence(data);
// Blowfish
pc.blowfishPerformence(data);
}
}

View File

@@ -0,0 +1,38 @@
package cn.aofeng.demo.eventdriver_improve;
/**
* 事件驱动调用示例
* @author 聂勇 <a href="mailto:aofengblog@163.com">aofengblog@163.com</a>
*/
public class ClientMain {
public static void main(String[] args) {
EventManagement eventManagement = new EventManagement();
eventManagement.addListener("read", new HelloWorldListener());
eventManagement.addListener("write", new SimpleListener());
EventSource eventSource = new EventSource(eventManagement);
eventSource.fire(new Event("read", "this is a read event"));
eventSource.fire(new Event("write", "this is a write event"));
}
public static class HelloWorldListener implements EventListener {
@Override
public void execute(Event event) {
System.out.println("监听器:"+this + "接收到事件,事件类型是:"
+ event.getEventType() + ", 事件附带的数据:" + event.getData());
}
}
public static class SimpleListener implements EventListener {
@Override
public void execute(Event event) {
System.out.println("监听器:"+this + "接收到事件,事件类型是:"
+ event.getEventType() + ", 事件附带的数据:" + event.getData());
}
}
}

View File

@@ -0,0 +1,28 @@
package cn.aofeng.demo.eventdriver_improve;
/**
* 事件
*
* @author aofeng <aofengblog@163.com>
*/
public class Event {
// 事件附带的数据
private Object data;
// 事件类型
private String eventType;
public Event(String eventType, Object obj){
this.eventType = eventType;
this.data = obj;
}
public Object getData() {
return this.data;
}
public String getEventType() {
return this.eventType;
}
}

View File

@@ -0,0 +1,16 @@
package cn.aofeng.demo.eventdriver_improve;
/**
* 事件监听器(监听一个或多个事件并进行具体的处理)
*
* @author aofeng <aofengblog@163.com>
*/
public interface EventListener {
/**
* 处理事件
*
* @param event 事件
*/
public void execute(Event event);
}

View File

@@ -0,0 +1,64 @@
package cn.aofeng.demo.eventdriver_improve;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 事件管理器。负责
*
* @author aofeng <aofengblog@163.com>
*/
public class EventManagement {
private Map<String, List<EventListener>> map = new HashMap<String, List<EventListener>>();
public EventManagement(){
}
/**
* 向指定事件添加一个监听器
*
* @param eventType 事件类型
* @param listener 事件监听器
* @return 添加成功返回true添加失败返回false
*/
public boolean addListener(String eventType, EventListener listener){
List<EventListener> listeners = map.get(eventType);
if (null == listeners) {
listeners = new ArrayList<EventListener>();
}
boolean result = listeners.add(listener);
map.put(eventType, listeners);
return result;
}
/**
* 移除事件的某一个监听器
*
* @param eventType 事件类型
* @param listener 事件监听器
* @return 移除成功返回true移除失败返回false
*/
public boolean removeListener(String eventType, EventListener listener){
List<EventListener> listeners = map.get(eventType);
if (null != listeners) {
return listeners.remove(listener);
}
return false;
}
/**
* 获取指定事件的监听器
*
* @param eventType 事件类型
* @return 如果指定的事件没有监听器返回null否则返回监听器列表
*/
public List<EventListener> getEventListeners(String eventType) {
return map.get(eventType);
}
}

View File

@@ -0,0 +1,38 @@
package cn.aofeng.demo.eventdriver_improve;
import java.util.List;
/**
* 事件源(事件发送者)
*
* @author aofeng <aofengblog@163.com>
*/
public class EventSource {
// 事件管理器
private EventManagement eventManagement;;
public EventSource(EventManagement eventManagement){
this.eventManagement = eventManagement;
}
/**
* 派发事件
*
* @param data 事件
*/
public void fire(Event event) {
if (null == event) {
return;
}
List<EventListener> listeners = eventManagement.getEventListeners(event.getEventType());
if (null == listeners) {
return;
}
for (EventListener listener : listeners) {
listener.execute(event);
}
}
}

View File

@@ -0,0 +1,33 @@
package cn.aofeng.demo.eventdriver_normal;
/**
* 事件驱动调用示例
* @author aofeng <aofengblog@163.com>
*/
public class ClientMain {
public static void main(String[] args) {
EventSource eventSource = new EventSource();
eventSource.addListener(new HelloWorldListener());
eventSource.addListener(new SimpleListener());
eventSource.fire("hello, world!");
}
public static class HelloWorldListener implements EventListener {
@Override
public void execute(Event event) {
System.out.println("监听器:"+this + "接收到事件,事件附带的数据:" + event.getData());
}
}
public static class SimpleListener implements EventListener {
@Override
public void execute(Event event) {
System.out.println("监听器:"+this + "接收到事件,事件附带的数据:" + event.getData());
}
}
}

View File

@@ -0,0 +1,17 @@
package cn.aofeng.demo.eventdriver_normal;
/**
* 事件
* @author aofeng <aofengblog@163.com>
*/
public class Event {
private Object data;
public Event(Object obj){
this.data = obj;
}
public Object getData() {
return data;
}
}

View File

@@ -0,0 +1,16 @@
package cn.aofeng.demo.eventdriver_normal;
/**
* 事件监听器(监听一个或多个事件并进行具体的处理)
*
* @author aofeng <aofengblog@163.com>
*/
public interface EventListener {
/**
* 处理事件
*
* @param event 事件
*/
public void execute(Event event);
}

View File

@@ -0,0 +1,47 @@
package cn.aofeng.demo.eventdriver_normal;
import java.util.ArrayList;
import java.util.List;
/**
* 事件源(事件发送者)
*
* @author aofeng <aofengblog@163.com>
*/
public class EventSource {
private List<EventListener> listeners = new ArrayList<EventListener>();
public EventSource() {
}
/**
* 添加事件监听器
*
* @param listener 事件监听器
*/
public boolean addListener(EventListener listener) {
return listeners.add(listener);
}
/**
* 移除事件监听器
*
* @param listener 移除事件监听器
*/
public boolean removeListener(EventListener listener) {
return listeners.remove(listener);
}
/**
* 派发事件
*
* @param data 事件
*/
public void fire(Object data) {
for (EventListener listener : listeners) {
listener.execute(new Event(data));
}
}
}

View File

@@ -0,0 +1,85 @@
/**
* 创建时间2016-8-5
*/
package cn.aofeng.demo.httpclient;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.fluent.Request;
import org.apache.http.client.fluent.Response;
import org.apache.http.message.BasicNameValuePair;
import org.apache.log4j.Logger;
import cn.aofeng.demo.httpclient.server.SimpleHttpServer;
/**
* 使用Fluent API快速地进行简单的HTTP的请求和响应处理启动{@link SimpleHttpServer}作为请求的服务端。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class FluentApi {
private static Logger _logger = Logger.getLogger(FluentApi.class);
private static String _targetHost = "http://127.0.0.1:8888";
private static String _charset = "utf-8";
/**
* HTTP GET请求关闭Keep-Alive。
*
* @param targetUrl 请求的地址
* @param charset 将响应流转换成字符串时使用的编码
*/
public static void get(String targetUrl, String charset) {
Response response = null;
try {
response = Request.Get(targetUrl).setHeader("Connection", "close").execute();
String content = response.returnContent().asString(Charset.forName(charset));
_logger.info(content);
} catch (ClientProtocolException e) {
_logger.error("协议问题", e);
} catch (IOException e) {
_logger.error("连接服务器或读取响应出错", e);
}
}
/**
* HTTP POST请求默认开启Keep-Alive表单数据使用utf-8编码。
*
* @param targetUrl 请求的地址
* @param charset 请求表单内容处理和将响应流转换成字符串时使用的编码
*/
public static void post(String targetUrl, String charset) {
List<NameValuePair> form = new ArrayList<NameValuePair>();
form.add(new BasicNameValuePair("hello", ""));
form.add(new BasicNameValuePair("gogogo", "走走走"));
Response response = null;
try {
response = Request.Post(targetUrl)
.bodyForm(form, Charset.forName(charset))
.execute();
String content = response.returnContent().asString(Charset.forName(charset));
_logger.info(content);
} catch (ClientProtocolException e) {
_logger.error("协议问题", e);
} catch (IOException e) {
_logger.error("连接服务器或读取响应出错", e);
}
}
/**
* @param args
*/
public static void main(String[] args) {
get(_targetHost+"/get", _charset);
post(_targetHost+"/post", _charset);
}
}

View File

@@ -0,0 +1,110 @@
/**
* 创建时间2016-2-23
*/
package cn.aofeng.demo.httpclient;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.FileEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;
/**
* HttpClient的基本操作。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class HttpClientBasic {
private static Logger _logger = Logger.getLogger(HttpClientBasic.class);
private static String _targetHost = "http://127.0.0.1:8888";
private static String _charset = "utf-8";
public void get() throws URISyntaxException, ClientProtocolException, IOException {
CloseableHttpClient client = HttpClients.createDefault();
HttpGet get = new HttpGet(_targetHost+"/get");
CloseableHttpResponse response = client.execute(get);
processResponse(response);
}
public void post() throws ClientProtocolException, IOException {
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("chinese", "中文"));
params.add(new BasicNameValuePair("english", "英文"));
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params, _charset);
CloseableHttpClient client = HttpClients.createDefault();
HttpPost post = new HttpPost(_targetHost+"/post");
post.addHeader("Cookie", "character=abcdefghijklmnopqrstuvwxyz; sign=abc-123-jkl-098");
post.setEntity(entity);
CloseableHttpResponse response = client.execute(post);
processResponse(response);
}
public void sendFile(String filePath) throws UnsupportedOperationException, IOException {
CloseableHttpClient client = HttpClients.createDefault();
HttpPost post = new HttpPost(_targetHost+"/file");
File file = new File(filePath);
FileEntity entity = new FileEntity(file, ContentType.create(ContentType.TEXT_PLAIN.getMimeType(), _charset));
post.setEntity(entity);
CloseableHttpResponse response = client.execute(post);
processResponse(response);
}
private void processResponse(CloseableHttpResponse response)
throws UnsupportedOperationException, IOException {
try {
// 获取响应头
Header[] headers = response.getAllHeaders();
for (Header header : headers) {
_logger.info(header.getName() + ":" + header.getValue());
}
// 获取状态信息
StatusLine sl =response.getStatusLine();
_logger.info( String.format("ProtocolVersion:%s, StatusCode:%d, Desc:%s",
sl.getProtocolVersion().toString(), sl.getStatusCode(), sl.getReasonPhrase()) );
// 获取响应内容
HttpEntity entity = response.getEntity();
_logger.info( String.format("ContentType:%s, Length:%d, Encoding:%s",
null == entity.getContentType() ? "" : entity.getContentType().getValue(),
entity.getContentLength(),
null == entity.getContentEncoding() ? "" : entity.getContentEncoding().getValue()) );
_logger.info(EntityUtils.toString(entity, _charset));
// _logger.info( IOUtils.toString(entity.getContent(), _charset) ); // 大部分情况下效果与上行语句等同,但实现上的编码处理不同
} finally {
response.close();
}
}
/**
* @param args
*/
public static void main(String[] args) throws Exception {
HttpClientBasic basic = new HttpClientBasic();
// basic.get();
// basic.post();
basic.sendFile("/devdata/projects/open_source/mine/JavaTutorial/LICENSE");
}
}

View File

@@ -0,0 +1,9 @@
#HTTP
##HTTP客户端
###代码
* [使用Fluent API发起HTTP请求Get和Post](cn/aofeng/demo/httpclient/FluentApi.java)
* [使用HttpClient发起HTTP请求Get, Post和上传文件](cn/aofeng/demo/httpclient/FluentApi.java)
##HTTP服务端
###代码
* [使用JDK中的API建立简单的HTTP Server](src/cn/aofeng/demo/httpclient/SimpleHttpServer.java)

View File

@@ -0,0 +1,71 @@
/**
* 创建时间2016-8-18
*/
package cn.aofeng.demo.httpclient.server;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
/**
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public abstract class AbstractHandler implements HttpHandler {
private static Logger _logger = Logger.getLogger(AbstractHandler.class);
protected String _charset = "utf-8";
public AbstractHandler(String charset) {
this._charset = charset;
}
/**
* 处理请求头。
*/
public void handleHeader(HttpExchange httpEx) {
InetSocketAddress remoteAddress = httpEx.getRemoteAddress();
_logger.info("收到来自"+remoteAddress.getAddress().getHostAddress()+":"+remoteAddress.getPort()+"的请求");
URI rUri = httpEx.getRequestURI();
_logger.info("请求地址:"+rUri.toString());
String method = httpEx.getRequestMethod();
_logger.info("请求方法:"+method);
Headers headers = httpEx.getRequestHeaders();
Set<Entry<String, List<String>>> headerSet = headers.entrySet();
_logger.info("请求头:");
for (Entry<String, List<String>> header : headerSet) {
_logger.info(header.getKey()+":"+header.getValue());
}
}
/**
* 处理响应。
*/
public void handleResponse(HttpExchange httpEx, String content)
throws UnsupportedEncodingException, IOException {
String rc = "冒号后面是收到的请求,原样返回:"+content;
byte[] temp = rc.getBytes(_charset);
Headers outHeaders = httpEx.getResponseHeaders();
outHeaders.set("ABC", "123");
httpEx.sendResponseHeaders(200, temp.length);
OutputStream outs = httpEx.getResponseBody();
outs.write(temp);
IOUtils.closeQuietly(outs);
}
}

View File

@@ -0,0 +1,66 @@
/**
* 创建时间2016-8-18
*/
package cn.aofeng.demo.httpclient.server;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
/**
* 二进制处理器。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class BinaryHandler extends AbstractHandler implements HttpHandler {
private static Logger _logger = Logger.getLogger(BinaryHandler.class);
private String _dir = "/home/nieyong/temp/JavaTutorial";
public BinaryHandler(String charset) {
super(charset);
}
@Override
public void handle(HttpExchange httpEx) throws IOException {
super.handleHeader(httpEx);
handleRequest(httpEx);
String content = "收到一个二进制的请求";
super.handleResponse(httpEx, content);
}
/**
* 处理请求。
* @throws IOException
*/
public String handleRequest(HttpExchange httpEx) throws IOException {
OutputStream outs = null;
InputStream ins = null;
try {
File file = new File(_dir, ""+System.currentTimeMillis());
if (!file.exists()) {
file.createNewFile();
}
outs = new FileOutputStream(file);
ins = httpEx.getRequestBody();
IOUtils.copyLarge(ins, outs);
} catch (Exception e) {
_logger.error("read request or write file occurs error", e);
} finally {
IOUtils.closeQuietly(ins);
IOUtils.closeQuietly(outs);
}
return null;
}
}

View File

@@ -0,0 +1,50 @@
/**
* 创建时间2016-8-18
*/
package cn.aofeng.demo.httpclient.server;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
/**
* 字符串处理器。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class CharacterHandler extends AbstractHandler implements HttpHandler {
static Logger _logger = Logger .getLogger(CharacterHandler.class);
public CharacterHandler(String charset) {
super(charset);
}
@Override
public void handle(HttpExchange httpEx) throws IOException {
super.handleHeader(httpEx);
String content = handleRequest(httpEx);
super.handleResponse(httpEx, content);
}
/**
* 处理请求。
*/
public String handleRequest(HttpExchange httpEx)
throws UnsupportedEncodingException, IOException {
InputStream ins = httpEx.getRequestBody();
String content = URLDecoder.decode(
IOUtils.toString(ins, _charset), _charset);
_logger.info("请求内容:"+content);
IOUtils.closeQuietly(ins);
return content;
}
}

View File

@@ -0,0 +1,41 @@
/**
* 创建时间2016-8-5
*/
package cn.aofeng.demo.httpclient.server;
import java.io.IOException;
import java.net.InetSocketAddress;
import org.apache.log4j.Logger;
import com.sun.net.httpserver.HttpServer;
/**
* 简单的HTTP Server。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class SimpleHttpServer {
private static Logger _logger = Logger.getLogger(SimpleHttpServer.class);
private static String _charset = "utf-8";
/**
* @param args
*/
public static void main(String[] args) {
int port = 8888;
try {
HttpServer server = HttpServer.create(new InetSocketAddress(port), 128);
server.createContext("/get", new CharacterHandler(_charset));
server.createContext("/post", new CharacterHandler(_charset));
server.createContext("/file", new BinaryHandler(_charset));
server.start();
_logger.info("http server already started, listen port:"+port);
} catch (IOException e) {
_logger.error("", e);
}
}
}

View File

@@ -0,0 +1,53 @@
package cn.aofeng.demo.io;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* 多线程网络echo服务。每接收到一个新连接都新建一个线程处理连接关闭后线程随之销毁。
*
* @author <a href="mailto:aofengblog@163.com">NieYong</a>
*/
public class MultiThreadEchoServer {
private final static Logger logger = Logger.getLogger(MultiThreadEchoServer.class.getName());
/**
* @param args [0]-监听端口
*/
public static void main(String[] args) {
if (args.length != 1) {
System.err.println("无效的参数。使用示例:");
System.err.println(" java cn.aofeng.demo.io.MultiThreadEchoServer 9090");
System.exit(-1);
}
int port = Integer.parseInt(args[0]);
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(port));
if (logger.isLoggable(Level.INFO)) {
logger.info("多线程网络echo服务启动完毕监听端口" +port);
}
while (true) {
// 接收新的客户端连接
Socket socket = serverSocket.accept();
if (logger.isLoggable(Level.INFO)) {
logger.info("收到一个新的连接客户端IP"+socket.getInetAddress().getHostAddress()+"客户端Port"+socket.getPort());
}
// 新建一个线程处理Socket连接
Thread thread = new Thread(new Worker(socket));
thread.start();
}
} catch (IOException e) {
logger.log(Level.SEVERE, "处理网络连接出错", e);
}
}
}

View File

@@ -0,0 +1,55 @@
package cn.aofeng.demo.io;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* 线程池网络echo服务。每接收到一个新连接都由线程池中的空闲线程处理连接关闭后释放线程不会销毁线程仍在线程池中
*
* @author <a href="mailto:aofengblog@163.com">NieYong</a>
*/
public class ThreadPoolEchoServer {
private final static Logger logger = Logger.getLogger(MultiThreadEchoServer.class.getName());
/**
* @param args [0]-监听端口
*/
public static void main(String[] args) {
if (args.length != 1) {
System.err.println("无效的参数。使用示例:");
System.err.println(" java cn.aofeng.demo.io.ThreadPoolEchoServer 9090");
System.exit(-1);
}
int port = Integer.parseInt(args[0]);
ExecutorService threadpool = Executors.newFixedThreadPool(5);
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(port));
if (logger.isLoggable(Level.INFO)) {
logger.info("线程池网络echo服务启动完毕监听端口" +port);
}
while (true) {
// 接收新的客户端连接
Socket socket = serverSocket.accept();
if (logger.isLoggable(Level.INFO)) {
logger.info("收到一个新的连接客户端IP"+socket.getInetAddress().getHostAddress()+"客户端Port"+socket.getPort());
}
// 将Socket连接交给线程池处理
threadpool.submit(new Worker(socket));
}
} catch (IOException e) {
logger.log(Level.SEVERE, "处理网络连接出错", e);
}
}
}

View File

@@ -0,0 +1,77 @@
package cn.aofeng.demo.io;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* 处理客户端Socket连接的工作线程。
*
* @author 聂勇 <a href="mailto:aofengblog@163.com">aofengblog@163.com</a>
*/
public class Worker implements Runnable {
private final static Logger logger = Logger.getLogger(MultiThreadEchoServer.class.getName());
// 字符集编码
private final static String CHAR_SET = "utf8";
// 行结束符
private final static String CRLF = "\r\n";
private Socket socket = null;
public Worker(Socket socket) {
this.socket = socket;
}
public void setSocket(Socket socket) {
this.socket = socket;
}
public void close(Closeable c) {
if (null != c) {
try {
c.close();
} catch (IOException e) {
// ingore
}
}
}
@Override
public void run() {
if (null == socket || socket.isClosed()) {
logger.warning("无效的Socket连接" + socket);
return;
}
String lineEnd = CRLF;
try {
BufferedReader reader = new BufferedReader(
new InputStreamReader(
socket.getInputStream()));
OutputStream outs = socket.getOutputStream();
String line = null;
while ( null != (line = reader.readLine()) ) {
// 客户端退出
if ("quit".equalsIgnoreCase(line) || "exit".equalsIgnoreCase(line)) {
break;
}
outs.write(line.getBytes(CHAR_SET));
outs.write(lineEnd.getBytes(CHAR_SET));
}
close(reader);
close(outs);
} catch (IOException e) {
logger.log(Level.SEVERE, "读取网络连接数据出错", e);
}
}
}

View File

@@ -0,0 +1,25 @@
package cn.aofeng.demo.java.lang.instrument;
import java.lang.instrument.Instrumentation;
import org.apache.commons.lang.StringUtils;
import cn.aofeng.demo.util.LogUtil;
/**
* Instrument入口类。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class FirstInstrumentation {
public static void premain(String options, Instrumentation ins) {
if (StringUtils.isBlank(options)) {
LogUtil.log("instrument without options");
} else {
LogUtil.log("instrument with options:%s", options);
}
ins.addTransformer(new FirstTransformer());
}
}

View File

@@ -0,0 +1,26 @@
/**
*
*/
package cn.aofeng.demo.java.lang.instrument;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import cn.aofeng.demo.util.LogUtil;
/**
* 只输出问候语不进行字节码修改的Class转换器。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class FirstTransformer implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
LogUtil.log(">>> %s", className);
return null;
}
}

View File

@@ -0,0 +1,14 @@
package cn.aofeng.demo.java.lang.instrument;
/**
* Instrumentation启动类。 *
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class Hello {
public static void main(String[] args) {
// nothing
}
}

View File

@@ -0,0 +1,40 @@
# 一、Instrumentation入门
* [FirstTransformer.java](FirstTransformer.java) 处理字节码由类FirstInstrumentation执行
* [FirstInstrumentation.java](FirstInstrumentation.java) instrumentation入口类由javaagent载入执行
* [build.xml](build.xml) Ant脚本负责编译、打包和运行
在当前目录下执行命令:
```bash
ant
```
输出信息如下:
> [java] instrument with options:"Hello, Instrumentation"
> [java] >>> java/lang/invoke/MethodHandleImpl
> [java] >>> java/lang/invoke/MethodHandleImpl$1
> [java] >>> java/lang/invoke/MethodHandleImpl$2
> [java] >>> java/util/function/Function
> [java] >>> java/lang/invoke/MethodHandleImpl$3
> [java] >>> java/lang/invoke/MethodHandleImpl$4
> [java] >>> java/lang/ClassValue
> [java] >>> java/lang/ClassValue$Entry
> [java] >>> java/lang/ClassValue$Identity
> [java] >>> java/lang/ClassValue$Version
> [java] >>> java/lang/invoke/MemberName$Factory
> [java] >>> java/lang/invoke/MethodHandleStatics
> [java] >>> java/lang/invoke/MethodHandleStatics$1
> [java] >>> sun/misc/PostVMInitHook
> [java] >>> sun/usagetracker/UsageTrackerClient
> [java] >>> java/util/concurrent/atomic/AtomicBoolean
> [java] >>> sun/usagetracker/UsageTrackerClient$1
> [java] >>> sun/usagetracker/UsageTrackerClient$4
> [java] >>> sun/usagetracker/UsageTrackerClient$3
> [java] >>> java/io/FileOutputStream$1
> [java] >>> sun/launcher/LauncherHelper
> [java] >>> cn/aofeng/demo/java/lang/instrument/Hello
> [java] >>> sun/launcher/LauncherHelper$FXHelper
> [java] >>> java/lang/Class$MethodArray
> [java] >>> java/lang/Void
> [java] >>> java/lang/Shutdown
> [java] >>> java/lang/Shutdown$Lock

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="Instrumentation" default="run" basedir="../../../../../../../">
<property name="project.src.dir" value="${basedir}/src" />
<property name="project.lib.dir" value="${basedir}/lib" />
<property name="project.conf.dir" value="${basedir}/conf" />
<property name="project.target.dir" value="${basedir}/classes" />
<property name="project.dist.dir" value="${basedir}/dist" />
<path id="project.classpath">
<fileset dir="${project.lib.dir}">
<include name="*.jar" />
</fileset>
</path>
<target name="prepare">
<delete dir="${project.target.dir}" />
<mkdir dir="${project.target.dir}"/>
<copy todir="${project.target.dir}">
<fileset dir="${project.conf.dir}">
<include name="**/*.properties" />
<include name="**/*.xml" />
</fileset>
</copy>
<delete dir="${project.dist.dir}" />
<mkdir dir="${project.dist.dir}" />
</target>
<target name="compileWithJavac" depends="prepare">
<echo message="compile with javac" />
<javac destdir="${project.target.dir}" srcdir="${project.src.dir}" source="1.8" target="1.8"
encoding="UTF-8" debug="true" includeantruntime="false">
<classpath refid="project.classpath" />
</javac>
</target>
<target name="package" depends="compileWithJavac">
<jar destfile="${project.dist.dir}/hello.jar">
<fileset dir="${project.target.dir}">
<include name="cn/aofeng/demo/java/lang/instrument/*.class" />
</fileset>
<manifest>
<attribute name="Premain-Class" value="cn.aofeng.demo.java.lang.instrument.FirstInstrumentation" />
</manifest>
</jar>
</target>
<target name="run" depends="package">
<java classname="cn.aofeng.demo.java.lang.instrument.Hello" fork="true">
<classpath refid="project.classpath" />
<classpath location="${project.target.dir}" />
<jvmarg value="-javaagent:${project.dist.dir}/hello.jar=&quot;Hello, Instrumentation&quot;" />
</java>
</target>
</project>

View File

@@ -0,0 +1,143 @@
package cn.aofeng.demo.java.lang.reflect;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**
* 通过反射获取类的构造方法、字段、方法和注解等信息。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class ClassAnalyze {
final static String PREFIX = "==========";
final static String SUFFIX = PREFIX;
private static void parseClass(Class<?> claz) {
// 注解
parseAnnotation(claz);
// 类
StringBuilder buffer = new StringBuilder(32)
.append( Modifier.toString(claz.getModifiers()) )
.append(' ')
.append(claz.getName());
log(buffer.toString());
}
private static void parseConstructor(Constructor<?> c) {
// 注解
parseAnnotation(c);
// 构造方法
StringBuilder buffer = new StringBuilder(32)
.append( parseMember(c) )
.append('(');
// 参数
Class<?>[] params = c.getParameterTypes();
for (int index = 0; index < params.length; index++) {
buffer.append(params[index].getName());
if (index!=params.length-1) {
buffer.append(", ");
}
}
buffer.append(')');
log(buffer.toString());
}
private static void parseMethod(Method method) {
// 注解
parseAnnotation(method);
// 方法
StringBuilder buffer = new StringBuilder(32)
.append( parseMember(method) )
.append('(');
// 参数
Class<?>[] params = method.getParameterTypes();
for (int index = 0; index < params.length; index++) {
buffer.append(params[index].getName());
if (index!=params.length-1) {
buffer.append(", ");
}
}
buffer.append(')');
log(buffer.toString());
}
private static void parseField(Field field) {
// 注解
parseAnnotation(field);
// 字段
StringBuilder buffer = parseMember(field);
log(buffer.toString());
}
/**
* 解析方法、字段或构造方法的信息。
* @param member 方法、字段或构造方法
* @return 修饰符和名称组成的字符串。
*/
private static StringBuilder parseMember(Member member) {
StringBuilder buffer = new StringBuilder()
.append(Modifier.toString(member.getModifiers()))
.append(' ')
.append(member.getName());
return buffer;
}
/**
* 解析注解信息。
*/
private static void parseAnnotation(AnnotatedElement ae) {
Annotation[] ans = ae.getDeclaredAnnotations();
for (Annotation annotation : ans) {
log(annotation.toString());
}
}
public static void log(String msg, Object... param) {
System.out.println( String.format(msg, param) );
}
public static void main(String[] args) throws ClassNotFoundException {
if (args.length != 1) {
log("无效的输入参数!");
log("示例:");
log("java cn.aofeng.demo.java.lang.reflect.ClassAnalyze java.util.HashMap");
}
Class<?> claz = Class.forName(args[0]);
log("%s类%s", PREFIX, SUFFIX);
parseClass(claz);
log("%s构造方法%s", PREFIX, SUFFIX);
Constructor<?>[] cs = claz.getDeclaredConstructors();
for (Constructor<?> constructor : cs) {
parseConstructor(constructor);
}
log("%s字段%s", PREFIX, SUFFIX);
Field[] fields = claz.getDeclaredFields();
for (Field field : fields) {
parseField(field);
}
log("%s方法%s", PREFIX, SUFFIX);
Method[] methods = claz.getDeclaredMethods();
for (Method method : methods) {
parseMethod(method);
}
}
}

View File

@@ -0,0 +1,36 @@
package cn.aofeng.demo.java.lang.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import cn.aofeng.demo.util.LogUtil;
/**
* 通过反射使用构造方法创建对象实例。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class CreateInstance {
public static void main(String[] args) throws InstantiationException, IllegalAccessException,
NoSuchMethodException, SecurityException,
IllegalArgumentException, InvocationTargetException {
Class<Man> claz = Man.class;
// 调用默认的public构造方法
Man man = claz.newInstance();
LogUtil.log(man.toString());
// 调用带参数的protected构造方法
Constructor<Man> manC = claz.getDeclaredConstructor(String.class);
man = manC.newInstance("aofeng");
LogUtil.log(man.toString());
// 调用带参数的private构造方法
manC = claz.getDeclaredConstructor(String.class, int.class);
manC.setAccessible(true);
man = manC.newInstance("NieYong", 32);
LogUtil.log(man.toString());
}
}

View File

@@ -0,0 +1,37 @@
package cn.aofeng.demo.java.lang.reflect;
import java.lang.reflect.Field;
import static cn.aofeng.demo.util.LogUtil.log;
/**
* 通过反射设置字段。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class InvokeField {
public static void main(String[] args) throws NoSuchFieldException,
SecurityException, IllegalArgumentException, IllegalAccessException {
Man man = new Man();
Class<?> claz = man.getClass();
log("==========设置public字段的值==========");
log("height的值:%d", man.height);
Field field = claz.getField("height");
field.setInt(man, 175);
log("height的值:%d", man.height);
log("==========设置private字段的值==========");
log("power的值:%d", man.getPower());
field = claz.getDeclaredField("power");
field.setAccessible(true);
field.setInt(man, 100);
log("power的值:%d", man.getPower());
log("==========获取private字段的值==========");
int power = field.getInt(man);
log("power的值:%d", power);
}
}

View File

@@ -0,0 +1,84 @@
package cn.aofeng.demo.java.lang.reflect;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import static cn.aofeng.demo.util.LogUtil.log;
/**
* 通过反射调用方法。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class InvokeMethod {
/**
* 调用父类的方法。
*
* @param claz
* 类
* @param man
* 类对应的实例
*/
private static void invokeParentMethod(Class<Man> claz, Man man)
throws NoSuchMethodException, IllegalAccessException,
InvocationTargetException {
// 调用父类的public方法
Method method = claz.getMethod("setName", String.class);
method.invoke(man, "NieYong");
log(man.toString());
method = claz.getMethod("getName");
Object result = method.invoke(man);
log("name:%s", result);
// 调用父类的private方法
method = claz.getSuperclass().getDeclaredMethod("reset");
method.setAccessible(true);
result = method.invoke(man);
log(man.toString());
}
/**
* 调用自身的方法。
*
* @param claz
* 类
* @param man
* 类对应的实例
*/
private static void invokeSelfMethod(Class<Man> claz, Man man)
throws NoSuchMethodException, IllegalAccessException,
InvocationTargetException {
man.setName("XiaoMing");
// 调用自身的private方法
Method method = claz.getDeclaredMethod("setPower", int.class);
method.setAccessible(true);
method.invoke(man, 99);
log("power:%d", man.getPower());
// 调用自身的public方法
log("%s is marry:%s", man.getName(), (man.isMarry() ? "Yes" : "No"));
method = claz.getDeclaredMethod("setMarry", boolean.class);
method.invoke(man, true);
log("%s is marry:%s", man.getName(), (man.isMarry() ? "Yes" : "No"));
// 调用静态方法可将实例设置为null因为静态方法属于类
Man a = new Man("张三");
Man b = new Man("李四");
method = claz.getMethod("fight", Man.class, Man.class);
method.invoke(null, a, b); //
}
public static void main(String[] args) throws NoSuchMethodException,
SecurityException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException,
InstantiationException {
Class<Man> claz = Man.class;
Man man = claz.newInstance();
invokeParentMethod(claz, man);
invokeSelfMethod(claz, man);
}
}

View File

@@ -0,0 +1,63 @@
package cn.aofeng.demo.java.lang.reflect;
import cn.aofeng.demo.json.gson.Person;
import cn.aofeng.demo.util.LogUtil;
/**
* 男人。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class Man extends Person {
private boolean marry;
private int power;
public int height;
public Man() {
super();
LogUtil.log("%s的默认构造方法被调用", Man.class.getName());
}
protected Man(String name) {
super(name, 0);
LogUtil.log("%s带name参数的构造方法被调用", Man.class.getName());
}
@SuppressWarnings("unused")
private Man(String name, int age) {
super(name, age);
LogUtil.log("%s带name和age参数的构造方法被调用", Man.class.getName());
}
public static void fight(Man a, Man b) {
String win = "unkown";
if (a.power > b.power) {
win = a.getName();
} else if (b.power > a.power) {
win = a.getName();
}
LogUtil.log("%s vs %s, fight result:%s", a.getName(), b.getName(), win);
}
public boolean isMarry() {
return marry;
}
public void setMarry(boolean marry) {
this.marry = marry;
}
public int getPower() {
return power;
}
@SuppressWarnings("unused")
private void setPower(int power) {
this.power = power;
}
}

View File

@@ -0,0 +1,74 @@
package cn.aofeng.demo.java.lang.serialization;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
/**
* 自定义序列化和反序列化。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class Man implements Externalizable {
private String manName;
private int manAge;
private transient String password;
public Man() {
// nothing
}
public Man(String name, int age) {
this.manName = name;
this.manAge = age;
}
public Man(String name, int age, String password) {
this.manName = name;
this.manAge = age;
this.password = password;
}
public String getManName() {
return manName;
}
public void setManName(String manName) {
this.manName = manName;
}
public int getManAge() {
return manAge;
}
public void setManAge(int manAge) {
this.manAge = manAge;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(manName);
out.writeInt(manAge);
out.writeObject(password);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
manName = (String) in.readObject();
manAge = in.readInt();
password = (String) in.readObject();
}
}

View File

@@ -0,0 +1,79 @@
package cn.aofeng.demo.java.lang.serialization;
import java.io.Serializable;
/**
* 默认序列化和
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class People implements Serializable {
private static final long serialVersionUID = 6235620243018494633L;
private String name;
private int age;
private transient String address;
private static String sTestNormal;
private static transient String sTestTransient;
public People(String name) {
this.name = name;
}
public People(String name, int age) {
this.name = name;
this.age = age;
}
public People(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getsTestNormal() {
return sTestNormal;
}
public void setsTestNormal(String sTestNormal) {
People.sTestNormal = sTestNormal;
}
public String getsTestTransient() {
return sTestTransient;
}
public void setsTestTransient(String sTestTransient) {
People.sTestTransient = sTestTransient;
}
}

View File

@@ -0,0 +1,126 @@
package cn.aofeng.demo.java.lang.serialization;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 关键字 transient 测试。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class TransientDemo {
private static Logger _logger = LoggerFactory.getLogger(TransientDemo.class);
private String _tempFileName = "TransientDemo";
/**
* 将对象序列化并保存到文件。
*
* @param obj 待序列化的对象
*/
public void save(Object obj) {
ObjectOutputStream outs = null;
try {
outs = new ObjectOutputStream(
new FileOutputStream( getTempFile(_tempFileName) ));
outs.writeObject(obj);
} catch (IOException e) {
_logger.error("save object to file occurs error", e);
} finally {
IOUtils.closeQuietly(outs);
}
}
/**
* 从文件读取内容并反序列化成对象。
*
* @return {@link People}对象。如果读取文件出错 或 对象类型转换失败返回null。
*/
public <T> T load() {
ObjectInputStream ins = null;
try {
ins = new ObjectInputStream(
new FileInputStream( getTempFile(_tempFileName)) );
return ((T) ins.readObject());
} catch (IOException e) {
_logger.error("load object from file occurs error", e);
} catch (ClassNotFoundException e) {
_logger.error("load object from file occurs error", e);
} finally {
IOUtils.closeQuietly(ins);
}
return null;
}
private File getTempFile(String filename) {
return new File(getTempDir(), filename);
}
private String getTempDir() {
return System.getProperty("java.io.tmpdir");
}
private void displayPeople(People people) {
if (null == people) {
return;
}
String template = "People[name:%s, age:%d, address:%s, sTestNormal:%s, sTestTransient:%s]";
System.out.println( String.format(template, people.getName(), people.getAge(),
people.getAddress(), people.getsTestNormal(), people.getsTestTransient()));
}
private void displayMan(Man man) {
if (null == man) {
return;
}
String template = "Man[manName:%s, manAge:%d, password:%s]";
System.out.println( String.format(template, man.getManName(), man.getManAge(), man.getPassword()) );
}
/**
* @param args
*/
public static void main(String[] args) {
System.out.println(">>> Serializable测试");
TransientDemo demo = new TransientDemo();
People people = new People("张三", 30, "中国广州");
people.setsTestNormal("normal-first");
people.setsTestTransient("transient-first");
System.out.println("序列化之前的对象信息:");
demo.displayPeople(people);
demo.save(people);
// 修改静态变量的值
people.setsTestNormal("normal-second");
people.setsTestTransient("transient-second");
People fromLoad = demo.load();
System.out.println("反序列化之后的对象信息:");
demo.displayPeople(fromLoad);
System.out.println("");
System.out.println(">>> Externalizable测试");
Man man = new Man("李四", 10, "假密码");
System.out.println("序列化之前的对象信息:");
demo.displayMan(man);
demo.save(man);
Man manFromLoadMan = demo.load();
System.out.println("反序列化之后的对象信息:");
demo.displayMan(manFromLoadMan);
}
}

View File

@@ -0,0 +1,20 @@
package cn.aofeng.demo.java.rmi;
/**
* 性别。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class Gender {
/**
* 男。
*/
public final static char MALE = 'M';
/**
* 女。
*/
public final static char FEMALE = 'F';
}

View File

@@ -0,0 +1,60 @@
package cn.aofeng.demo.java.rmi;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class RmiClient {
private static Logger _logger = LoggerFactory.getLogger(RmiClient.class);
private static String assembleUrl(String hostUrl, String bindName) {
if (StringUtils.isBlank(hostUrl) || StringUtils.isBlank(bindName)) {
return null;
}
String host = hostUrl.endsWith("/") ? hostUrl.substring(0, hostUrl.length()-1) : hostUrl;
String name = bindName.startsWith("/") ? bindName.substring(1) : bindName;
return host + "/" + name;
}
/**
* @param args
* <ul>
* <li>[0]待连接的RMI主机。如rmi://192.168.56.102:9999</li>
* <li>[1]服务名称。如UserService</li>
* </ul>
*/
public static void main(String[] args) throws MalformedURLException, RemoteException, NotBoundException {
String host = args[0];
String serviceName = args[1];
String url = assembleUrl(host, serviceName);
UserService userService = (UserService) Naming.lookup(url);
String userId = "10000";
User user = userService.findById(userId);
_logger.info("身份证号为{}的用户信息{}", userId, user);
userId = "10001";
user = userService.findById(userId);
_logger.info("身份证号为{}的用户信息{}", userId, user);
String userName = "小明";
user = userService.findByName(userName);
_logger.info("姓名为{}的用户信息{}", userName, user);
userName = "张三";
user = userService.findByName(userName);
_logger.info("姓名为{}的用户信息{}", userName, user);
}
}

View File

@@ -0,0 +1,53 @@
package cn.aofeng.demo.java.rmi;
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* RMI服务端。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class RmiServer {
private static Logger _logger = LoggerFactory.getLogger(RmiServer.class);
public static Registry createRegistry(int port) {
Registry registry = null;
try {
registry = LocateRegistry.createRegistry(port);
} catch (RemoteException e) {
_logger.info( String.format("注册端口%s失败", port), e);
}
return registry;
}
/**
* @param args [0]:绑定端口
* @throws RemoteException
*/
public static void main(String[] args) throws RemoteException {
int port = Integer.parseInt(args[0]);
UserService userService = new UserServiceImpl();
Registry registry = createRegistry(port);
if (null == registry) {
System.exit(0);
}
String bindName = "UserService";
try {
registry.bind(bindName, userService);
} catch (AlreadyBoundException e) {
_logger.info("服务{}已经绑定过", bindName);
}
_logger.info("RMI Server started, listen port:{}", port);
}
}

View File

@@ -0,0 +1,170 @@
package cn.aofeng.demo.java.rmi;
import java.io.Serializable;
/**
* 用户信息。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class User implements Serializable {
private static final long serialVersionUID = 7616705579045104892L;
/**
* 身份证号。
*/
private String id;
/**
* 姓名。
*/
private String name;
/**
* 性别M-男F-女。
*/
private char gender;
/**
* 出生日期。以毫秒存储。
*/
private long birthday;
/**
* 国家。
*/
private String country;
/**
* 省/州。
*/
private String province;
/**
* 市/区。
*/
private String city;
/**
* 街道详细地址。
*/
private String address;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
public long getBirthday() {
return birthday;
}
public void setBirthday(long birthday) {
this.birthday = birthday;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((country == null) ? 0 : country.hashCode());
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
User other = (User) obj;
if (country == null) {
if (other.country != null)
return false;
} else if (!country.equals(other.country))
return false;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
StringBuilder buffer = new StringBuilder(256)
.append("User [id=").append(id)
.append(", name=").append(name)
.append(", gender=").append(gender)
.append(", birthday=").append(birthday)
.append(", country=").append(country)
.append(", province=").append(province)
.append(", city=").append(city)
.append(", address=").append(address)
.append("]");
return buffer.toString();
}
}

View File

@@ -0,0 +1,19 @@
package cn.aofeng.demo.java.rmi;
import java.rmi.Remote;
import java.rmi.RemoteException;
/**
* 用户信息服务。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public interface UserService extends Remote {
public User findByName(String name) throws RemoteException;
public User findById(String id) throws RemoteException;
public boolean add(User user) throws RemoteException;
}

View File

@@ -0,0 +1,70 @@
package cn.aofeng.demo.java.rmi;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import org.apache.commons.lang.StringUtils;
/**
* 用户信息服务。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class UserServiceImpl extends UnicastRemoteObject implements UserService {
public UserServiceImpl() throws RemoteException {
super();
}
private static final long serialVersionUID = -9134952963637302483L;
@Override
public User findByName(String name) throws RemoteException {
if (StringUtils.isBlank(name)) {
return null;
}
if ("小明".equals(name)) {
return createUser("10000", "小明", Gender.MALE);
}
return null;
}
@Override
public User findById(String id) throws RemoteException {
if (StringUtils.isBlank(id)) {
return null;
}
if ("10000".equals(id)) {
return createUser("10000", "小丽", Gender.FEMALE);
}
return null;
}
@Override
public boolean add(User user) throws RemoteException {
if (null == user || StringUtils.isBlank(user.getId()) || StringUtils.isBlank(user.getName())) {
return false;
}
return true;
}
private User createUser(String id, String name, char gender) {
User user = new User();
user.setId(id);
user.setName(name);
user.setGender(gender);
user.setBirthday(System.currentTimeMillis());
user.setCountry("中国");
user.setProvince("广东");
user.setCity("广州");
user.setAddress("xxx区xxx街道xxx号");
return user;
}
}

View File

@@ -0,0 +1,78 @@
package cn.aofeng.demo.java.util.concurret;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import cn.aofeng.demo.util.DateUtil;
/**
* {@link ScheduledExecutorService}的使用示例:<br>
* 定时任务1执行过程中会抛出异常。<br>
* 定时任务2执行过程中不会抛出异常。<br>
* <br>
* 目的检测java.util.concurrent.ScheduledExecutorService在执行定时任务的过程中任务内抛出异常没有捕捉时在下一次执行时间到来时是否可以正常执行。<br>
* 测试的JDK版本1.6.xx。<br>
* 结果通过相比java.util.Timer完善地解决了定时任务抛异常的问题。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class ScheduledExecutorServiceDemo {
public static void main(String[] args) {
ScheduledExecutorService timer = Executors.newScheduledThreadPool(2);
long delay = computeDelay();
timer.schedule(new ThrowExceptionTask(timer), delay, TimeUnit.MILLISECONDS);
timer.schedule(new NotThrowExceptionTask(timer), delay, TimeUnit.MILLISECONDS);
System.out.println("主线程的功能执行完毕");
}
private static long computeDelay() {
Date now = new Date();
Date nextMinute = DateUtil.getNextMinute();
long delay = nextMinute.getTime() - now.getTime();
return delay;
}
static class ThrowExceptionTask implements Runnable {
private ScheduledExecutorService _timer;
public ThrowExceptionTask(ScheduledExecutorService timer) {
this._timer = timer;
}
@Override
public void run() {
try {
System.out.println("任务名:ThrowExceptionTask, 当前时间:"+DateUtil.getCurrentTime());
throw new IllegalArgumentException();
} finally {
_timer.schedule(new ThrowExceptionTask(_timer), computeDelay(), TimeUnit.MILLISECONDS);
}
}
} // end of ThrowExceptionTask
static class NotThrowExceptionTask implements Runnable {
private ScheduledExecutorService _timer;
public NotThrowExceptionTask(ScheduledExecutorService timer) {
this._timer = timer;
}
@Override
public void run() {
try {
System.out.println("任务名:NotThrowExceptionTask, 当前时间:"+DateUtil.getCurrentTime());
} finally {
_timer.schedule(new NotThrowExceptionTask(_timer), computeDelay(), TimeUnit.MILLISECONDS);
}
}
} // end of NotThrowExceptionTask
}

View File

@@ -0,0 +1,103 @@
package cn.aofeng.demo.java.util.forkjoin;
import java.util.Arrays;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.TimeUnit;
/**
* Fork/Join练习。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class HelloForkJoin extends RecursiveTask<Long> {
private static final long serialVersionUID = -2386438994963147457L;
private int[] _intArray;
private int _threshold = 10;
private long _result = 0;
public HelloForkJoin(int[] intArray) {
this._intArray = intArray;
}
@Override
protected Long compute() {
if (null == _intArray || _intArray.length <= 0) {
return 0L;
}
if (_intArray.length <= _threshold) { // 如果数组长度小于等于指定的值,执行累加操作
for (int item : _intArray) {
_result += item;
}
System.out.println( String.format("线程:%s累加数组%s中的值结果%d", Thread.currentThread(), Arrays.toString(_intArray), _result) );
} else { // 如果数组长度大于指定的值,做任务分解
int[] temp = new int[_threshold];
System.arraycopy(_intArray, 0, temp, 0, _threshold);
HelloForkJoin subTask1 = new HelloForkJoin(temp);
subTask1.fork();
if (_intArray.length - _threshold > 0) {
int remain[] = new int[_intArray.length - _threshold];
System.arraycopy(_intArray, _threshold, remain, 0, remain.length);
HelloForkJoin task = new HelloForkJoin(remain);
task.fork();
_result += task.join();
}
_result += subTask1.join();
return _result;
}
return _result;
}
public static void main(String[] args) throws InterruptedException {
int count = 10000;
serialCompute(count);
parallelCompute(count);
}
/**
* 使用fork/join并行计算数字累加。
*
* @param count 数字个数从1开始
* @throws InterruptedException
*/
private static void parallelCompute(int count)
throws InterruptedException {
int[] numbers = new int[count];
for (int i = 0; i < count; i++) {
numbers[i] = i+1;
}
ForkJoinPool pool = new ForkJoinPool(4);
HelloForkJoin task = new HelloForkJoin(numbers);
long startTime = System.currentTimeMillis();
pool.submit(task);
pool.shutdown();
pool.awaitTermination(10, TimeUnit.SECONDS);
System.out.println( String.format("并行计算结果:%d耗时%d毫秒", task._result, System.currentTimeMillis()-startTime) );
}
/**
* 使用for循环串行计算数字累加。
*
* @param count 数字个数从1开始
*/
private static void serialCompute(int count) {
long startTime = System.currentTimeMillis();
int sum = 0;
for (int i = 0; i < count; i++) {
sum += (i+1);
}
System.out.println( String.format("串行计算结果:%d耗时%d毫秒", sum, System.currentTimeMillis()-startTime) );
}
}

View File

@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<class-diagram version="1.1.6" icons="true" always-add-relationships="false" generalizations="true" realizations="true"
associations="true" dependencies="false" nesting-relationships="true">
<interface id="1" language="java" name="java.util.concurrent.Future" project="JavaDemo"
file="/devdata/java/jdk/jdk1.7.0_60/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="334" y="85"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="true" protected="false" private="false" static="false"/>
<operations public="true" package="true" protected="false" private="false" static="false"/>
</display>
</interface>
<class id="2" language="java" name="java.util.concurrent.FutureTask" project="JavaDemo"
file="/devdata/java/jdk/jdk1.7.0_60/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="521" y="477"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="true" protected="false" private="false" static="false"/>
<operations public="true" package="true" protected="false" private="false" static="false"/>
</display>
</class>
<interface id="3" language="java" name="java.util.concurrent.RunnableFuture" project="JavaDemo"
file="/devdata/java/jdk/jdk1.7.0_60/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="521" y="280"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="true" protected="false" private="false" static="false"/>
<operations public="true" package="true" protected="false" private="false" static="false"/>
</display>
</interface>
<interface id="4" language="java" name="java.util.concurrent.RunnableScheduledFuture" project="JavaDemo"
file="/devdata/java/jdk/jdk1.7.0_60/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="130" y="410"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="true" protected="false" private="false" static="false"/>
<operations public="true" package="true" protected="false" private="false" static="false"/>
</display>
</interface>
<interface id="5" language="java" name="java.util.concurrent.ScheduledFuture" project="JavaDemo"
file="/devdata/java/jdk/jdk1.7.0_60/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="131" y="272"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="true" protected="false" private="false" static="false"/>
<operations public="true" package="true" protected="false" private="false" static="false"/>
</display>
</interface>
<interface id="6" language="java" name="java.util.concurrent.Delayed" project="JavaDemo"
file="/devdata/java/jdk/jdk1.7.0_60/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="131" y="119"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="true" protected="false" private="false" static="false"/>
<operations public="true" package="true" protected="false" private="false" static="false"/>
</display>
</interface>
<interface id="7" language="java" name="java.lang.Runnable" project="JavaDemo"
file="/devdata/java/jdk/jdk1.7.0_60/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="521" y="119"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="true" protected="false" private="false" static="false"/>
<operations public="true" package="true" protected="false" private="false" static="false"/>
</display>
</interface>
<generalization id="8">
<end type="SOURCE" refId="4"/>
<end type="TARGET" refId="5"/>
</generalization>
<generalization id="9">
<end type="SOURCE" refId="3"/>
<end type="TARGET" refId="7"/>
</generalization>
<generalization id="10">
<end type="SOURCE" refId="4"/>
<end type="TARGET" refId="3"/>
</generalization>
<generalization id="11">
<end type="SOURCE" refId="5"/>
<end type="TARGET" refId="6"/>
</generalization>
<generalization id="12">
<end type="SOURCE" refId="5"/>
<end type="TARGET" refId="1"/>
</generalization>
<generalization id="13">
<end type="SOURCE" refId="3"/>
<end type="TARGET" refId="1"/>
</generalization>
<realization id="14">
<end type="SOURCE" refId="2"/>
<end type="TARGET" refId="3"/>
</realization>
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
accessors="true" visibility="true">
<attributes public="false" package="true" protected="false" private="false" static="false"/>
<operations public="true" package="true" protected="false" private="false" static="false"/>
</classifier-display>
<association-display labels="true" multiplicity="true"/>
</class-diagram>

View File

@@ -0,0 +1,106 @@
package cn.aofeng.demo.java.util.future;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.apache.log4j.Logger;
/**
* Future、Callable练习。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class HelloFuture {
private static Logger _logger = Logger.getLogger(HelloFuture.class);
/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
executeSingleTask();
executeBatchTask();
}
/**
* 执行单个异步任务:<br>
* 1、用submit方法向线程池提交一个任务。<br>
* 2、获取结果时指定了超时时间。<br><br>
* 结果超时后调用者收到TimeoutException但实际上任务还在继续执行。
* @throws InterruptedException
*/
private static void executeSingleTask() throws InterruptedException {
ExecutorService threadpool = Executors.newFixedThreadPool(6);
try {
Future<Integer> f = threadpool.submit(createCallable(5 * 1000));
Object result = f.get(3, TimeUnit.SECONDS);
System.out.println("单个任务的执行结果:"+result);
} catch (Exception e) {
_logger.error("线程执行任务时出错", e);
} finally {
threadpool.shutdown();
threadpool.awaitTermination(10, TimeUnit.SECONDS);
}
}
/**
* 执行多个异步任务:<br>
* 1、用invokeAll方法向线程池提交多个任务并指定了执行的超时时间。<br><br>
* 结果超时后未执行完成的任务被取消在调用Future的get方法时取消的任务会抛出CancellationException执行完成的任务可获得结果。
* @throws InterruptedException
*/
private static void executeBatchTask() throws InterruptedException {
ExecutorService threadpool = Executors.newFixedThreadPool(6);
List<Callable<Integer>> tasks = new ArrayList<Callable<Integer>>();
tasks.add(createCallable(2000));
tasks.add(createCallable(5000));
tasks.add(createCallable(2500));
long startTime = System.currentTimeMillis();
try {
List<Future<Integer>> fs = threadpool.invokeAll(tasks, 3, TimeUnit.SECONDS);
int result = 0;
for (Future<Integer> f : fs) {
try{
result += f.get();
} catch(CancellationException ce) {
// nothing
}
}
System.out.println("执行三个任务共耗时:" + (System.currentTimeMillis() - startTime) + "毫秒");
System.out.println("三个任务的执行结果汇总:"+result);
} catch (Exception e) {
_logger.error("线程执行任务时出错", e);
} finally {
threadpool.shutdown();
threadpool.awaitTermination(10, TimeUnit.SECONDS);
}
}
/**
* 创建需要长时间执行的任务模拟对象。
* @param sleepTimes 线程休眠时间(单位:毫秒)
* @return {@link Callable}对象
*/
private static Callable<Integer> createCallable(final int sleepTimes) {
Callable<Integer> c = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Thread.sleep(sleepTimes);
System.out.println(Thread.currentThread().getName() + ": I'm working");
return 9;
}
};
return c;
}
}

View File

@@ -0,0 +1,139 @@
<?xml version="1.0" encoding="UTF-8"?>
<class-diagram version="1.1.6" icons="true" always-add-relationships="false" generalizations="true" realizations="true"
associations="true" dependencies="false" nesting-relationships="true">
<interface id="1" language="java" name="java.util.Map" project="JavaDemo"
file="/devdata/java/jdk/jdk1.6.0_45/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="417" y="179"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="true" package="true" protected="false" private="false" static="true"/>
</display>
</interface>
<class id="2" language="java" name="java.util.AbstractMap" project="JavaDemo"
file="/devdata/java/jdk/jdk1.6.0_45/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="417" y="423"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</class>
<class id="3" language="java" name="java.util.HashMap" project="JavaDemo"
file="/devdata/java/jdk/jdk1.6.0_45/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="824" y="422"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="false"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</class>
<class id="4" language="java" name="java.util.Hashtable" project="JavaDemo"
file="/devdata/java/jdk/jdk1.6.0_45/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="152" y="312"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</class>
<class id="5" language="java" name="java.util.IdentityHashMap" project="JavaDemo"
file="/devdata/java/jdk/jdk1.6.0_45/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="206" y="423"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</class>
<class id="6" language="java" name="java.util.LinkedHashMap" project="JavaDemo"
file="/devdata/java/jdk/jdk1.6.0_45/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="824" y="283"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</class>
<class id="7" language="java" name="java.util.EnumMap" project="JavaDemo"
file="/devdata/java/jdk/jdk1.6.0_45/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="329" y="528"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</class>
<class id="8" language="java" name="java.util.TreeMap" project="JavaDemo"
file="/devdata/java/jdk/jdk1.6.0_45/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="499" y="526"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</class>
<class id="9" language="java" name="java.util.WeakHashMap" project="JavaDemo"
file="/devdata/java/jdk/jdk1.6.0_45/jre/lib/rt.jar" binary="true" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="609" y="374"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" accessors="true"
visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="false" package="false" protected="false" private="false" static="true"/>
</display>
</class>
<generalization id="10">
<end type="SOURCE" refId="9"/>
<end type="TARGET" refId="2"/>
</generalization>
<generalization id="11">
<end type="SOURCE" refId="8"/>
<end type="TARGET" refId="2"/>
</generalization>
<realization id="12">
<end type="SOURCE" refId="5"/>
<end type="TARGET" refId="1"/>
</realization>
<generalization id="13">
<end type="SOURCE" refId="3"/>
<end type="TARGET" refId="2"/>
</generalization>
<realization id="14">
<end type="SOURCE" refId="4"/>
<end type="TARGET" refId="1"/>
</realization>
<realization id="15">
<end type="SOURCE" refId="2"/>
<end type="TARGET" refId="1"/>
</realization>
<realization id="16">
<end type="SOURCE" refId="3"/>
<end type="TARGET" refId="1"/>
</realization>
<generalization id="17">
<end type="SOURCE" refId="7"/>
<end type="TARGET" refId="2"/>
</generalization>
<realization id="18">
<end type="SOURCE" refId="9"/>
<end type="TARGET" refId="1"/>
</realization>
<generalization id="19">
<end type="SOURCE" refId="6"/>
<end type="TARGET" refId="3"/>
</generalization>
<generalization id="20">
<end type="SOURCE" refId="5"/>
<end type="TARGET" refId="2"/>
</generalization>
<realization id="21">
<end type="SOURCE" refId="6"/>
<end type="TARGET" refId="1"/>
</realization>
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
accessors="true" visibility="true">
<attributes public="true" package="true" protected="false" private="false" static="true"/>
<operations public="true" package="true" protected="false" private="false" static="true"/>
</classifier-display>
<association-display labels="true" multiplicity="true"/>
</class-diagram>

View File

@@ -0,0 +1,47 @@
package cn.aofeng.demo.java.util.timer;
import java.util.Timer;
import java.util.TimerTask;
import cn.aofeng.demo.util.DateUtil;
/**
* {@link Timer}的使用示例:<br>
* 定时任务1执行过程中会抛出异常。<br>
* 定时任务2执行过程中不会抛出异常。<br>
* <br>
* 目的检测java.util.Timer在执行定时任务的过程中任务内抛出异常没有捕捉时在下一次执行时间到来时是否可以正常执行。<br>
* 测试的JDK版本1.6.xx。<br>
* 结果不通过定时任务抛出异常时整个Timer中止其他定时任务也中止。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class TimerDemo {
public static void main(String[] args) {
Timer timer = new Timer("aofeng-timer-demo");
timer.schedule(new ThrowExceptionTask(), DateUtil.getNextMinute(), 60*1000);
timer.schedule(new NotThrowExceptionTask(), DateUtil.getNextMinute(), 60*1000);
}
static class ThrowExceptionTask extends TimerTask {
@Override
public void run() {
System.out.println("任务名:ThrowExceptionTask, 当前时间:"+DateUtil.getCurrentTime());
throw new IllegalArgumentException();
}
} // end of ThrowExceptionTask
static class NotThrowExceptionTask extends TimerTask {
@Override
public void run() {
System.out.println("任务名:NotThrowExceptionTask, 当前时间:"+DateUtil.getCurrentTime());
}
} // end of NotThrowExceptionTask
}

View File

@@ -0,0 +1,64 @@
package cn.aofeng.demo.jdbc;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* JDBC常用方法集。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class JDBCUtils {
private static Logger _logger = LoggerFactory.getLogger(JDBCUtils.class);
public static void close(ResultSet rs) {
if (null != rs) {
try {
rs.close();
} catch (SQLException e) {
_logger.error("close resultset occurs error", e);
}
}
}
public static void close(Statement stmt) {
if (null != stmt) {
try {
stmt.close();
} catch (SQLException e) {
_logger.error("close statement occurs error", e);
}
}
}
public static void close(Connection conn) {
if (null != conn) {
try {
conn.close();
} catch (SQLException e) {
_logger.error("close connection occurs error", e);
}
}
}
public static void showResultSetInfo(ResultSet rs) throws SQLException {
ResultSetMetaData rsMeta = rs.getMetaData();
int colCount = rsMeta.getColumnCount();
_logger.info("total columns:{}", colCount);
for (int i = 1; i <= colCount; i++) {
_logger.info("column name:{}, label:{}, type:{}, typeName:{}",
rsMeta.getColumnName(i),
rsMeta.getColumnLabel(i),
rsMeta.getColumnType(i),
rsMeta.getColumnTypeName(i));
}
}
}

View File

@@ -0,0 +1,82 @@
package cn.aofeng.demo.jdbc;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import cn.aofeng.demo.tree.PrettyTree;
import cn.aofeng.demo.tree.PrettyTree.Node;
/**
* JDBC元数据使用示例。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class MetaDataExample {
private static Logger _logger = LoggerFactory.getLogger(MetaDataExample.class);
/**
* @param args
*/
public static void main(String[] args) {
String url = "jdbc:mysql://192.168.56.102:19816/ucgc_sdk?useUnicode=true&characterEncoding=UTF8";
Properties info = new Properties();
info.put("user", "uzone");
info.put("password", "uzone");
Connection conn = null;
try {
conn = DriverManager.getConnection(url, info);
DatabaseMetaData dbMeta = conn.getMetaData();
showCatalogs(dbMeta);
} catch (SQLException e) {
_logger.error("get connection occurs error", e);
} finally {
JDBCUtils.close(conn);
}
}
private static void showCatalogs(DatabaseMetaData dbMeta) throws SQLException {
ResultSet catalogsRs = null;
try {
catalogsRs = dbMeta.getCatalogs();
// JDBCUtils.showResultSetInfo(catalogsRs);
while (catalogsRs.next()) {
String catalog = catalogsRs.getString("TABLE_CAT");
showTables(dbMeta, catalog);
}
} finally {
JDBCUtils.close(catalogsRs);
}
}
/**
* 采用树状结构输出Catalog和所有归属于它的表的名称。
*/
private static void showTables(DatabaseMetaData dbMeta, String catalog) throws SQLException {
ResultSet tablesRs = null;
Node root = new Node(catalog);
try {
tablesRs = dbMeta.getTables(catalog, null, null, null);
// JDBCUtils.showResultSetInfo(tablesRs);
while (tablesRs.next()) {
root.add(new Node(tablesRs.getString("TABLE_NAME")));
}
} finally {
JDBCUtils.close(tablesRs);
}
StringBuilder buffer = new StringBuilder(256);
PrettyTree pt = new PrettyTree();
pt.renderRoot(root, buffer);
System.out.print(buffer);
}
}

View File

@@ -0,0 +1,42 @@
package cn.aofeng.demo.jetty;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import org.apache.commons.io.IOUtils;
/**
* 抓取页面内容。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class HttpGet {
public String getSomeThing(String urlStr) throws IOException {
URL url = new URL(urlStr);
HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
urlConn.setConnectTimeout(3000);
urlConn.setRequestMethod("GET");
urlConn.connect();
InputStream ins = null;
try {
if (200 == urlConn.getResponseCode()) {
ins = urlConn.getInputStream();
ByteArrayOutputStream outs = new ByteArrayOutputStream(1024);
IOUtils.copy(ins, outs);
return outs.toString("UTF-8");
}
} catch (IOException e) {
throw e;
} finally {
IOUtils.closeQuietly(ins);
}
return null;
}
}

View File

@@ -0,0 +1,55 @@
package cn.aofeng.demo.jetty;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* {@link HttpGet}的单元测试用例。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class HttpGetTest {
private HttpServerMock _mock;
private HttpGet _httpGet = new HttpGet();
@Before
public void setUp() throws Exception {
_mock = new HttpServerMock();
}
@After
public void tearDown() throws Exception {
if (null != _mock) {
_mock.stop();
}
}
/**
* 用例响应状态码为200且有响应内容。
*/
@Test
public void testGetSomeThing4Success() throws Exception {
String response = "Hello, The World!";
_mock.start(response, "text/plain");
String content = _httpGet.getSomeThing("http://localhost:9191/hello");
assertEquals(response, content);
}
/**
* 用例响应状态码为非200。
*/
@Test
public void testGetSomeThing4Fail() throws Exception {
String response = "Hello, The World!";
_mock.start(response, "text/plain", 500);
String content = _httpGet.getSomeThing("http://localhost:9191/hello");
assertNull(content);
}
}

View File

@@ -0,0 +1,90 @@
package cn.aofeng.demo.jetty;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
/**
* HTTP服务器MOCK可用于单元测试时模拟HTTP服务器的响应。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class HttpServerMock {
public final static int DEFAULT_PORT = 9191;
public final static String DEFAULT_CONTENT_TYPE = "application/json";
public final static int DEFAULT_STATUS_CODE=HttpServletResponse.SC_OK;
private Server _httpServer;
private int _port;
public HttpServerMock() {
_port = DEFAULT_PORT;
}
public HttpServerMock(int port) {
_port = port;
}
/**
* 启动Jetty服务器。默认的响应status code为"200"content type为"application/json"。
* @param content 响应内容
*/
public void start(String content) throws Exception {
start(content, DEFAULT_CONTENT_TYPE, DEFAULT_STATUS_CODE);
}
/**
* 启动Jetty服务器。默认的响应status code为"200"。
* @param content 响应内容
* @param contentType 响应内容的MIME类型
*/
public void start(String content, String contentType) throws Exception {
start(content, contentType, DEFAULT_STATUS_CODE);
}
/**
* 启动Jetty服务器。
* @param content 响应内容
* @param contentType 响应内容的MIME类型
* @param statuCode 响应状态码
*/
public void start(String content, String contentType,
int statuCode) throws Exception {
_httpServer = new Server(_port);
_httpServer.setHandler(createHandler(content, contentType, statuCode));
_httpServer.start();
}
/**
* 停止Jetty服务器。
*/
public void stop() throws Exception {
if (null != _httpServer) {
_httpServer.stop();
_httpServer = null;
}
}
private Handler createHandler(final String content, final String contentType,
final int statusCode) {
return new AbstractHandler() {
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request,
HttpServletResponse response) throws IOException, ServletException {
response.setContentType(contentType);
response.setStatus(statusCode);
baseRequest.setHandled(true);
response.getWriter().print(content);
}
};
}
}

View File

@@ -0,0 +1,216 @@
使用Jetty实现Http Server Mock作单元测试
===
在研发过程中发现许多模块对外部系统的调用并没有沉淀成专门的客户端代码中直接调用URL发送请求和获取响应内容导致做单元测试的时候非常麻烦。
特别是对已有系统做改造的时候,要先写单元测试用例,保证后续的重构和修改不偏离原来的业务需求。
这样问题就来了:该如何模拟外部系统返回各种正常和异常的响应内容呢?
办法总是有的这里就用Jetty实现Http Server Mock模拟服务端返回各种响应数据。
预备
---
* [JUnit 4.11](http://junit.org/)
* [Jetty 7.6.9](http://www.eclipse.org/jetty/)
业务示例代码
---
[源码下载](https://raw.githubusercontent.com/aofeng/JavaDemo/master/src/cn/aofeng/demo/jetty/HttpGet.java)
```java
package cn.aofeng.demo.jetty;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import org.apache.commons.io.IOUtils;
/**
* 抓取页面内容。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class HttpGet {
public String getSomeThing(String urlStr) throws IOException {
URL url = new URL(urlStr);
HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
urlConn.setConnectTimeout(3000);
urlConn.setRequestMethod("GET");
urlConn.connect();
InputStream ins = null;
try {
if (200 == urlConn.getResponseCode()) {
ins = urlConn.getInputStream();
ByteArrayOutputStream outs = new ByteArrayOutputStream(1024);
IOUtils.copy(ins, outs);
return outs.toString("UTF-8");
}
} catch (IOException e) {
throw e;
} finally {
IOUtils.closeQuietly(ins);
}
return null;
}
}
```
用Jetty实现的Http Server Mock
---
[源码下载](https://raw.githubusercontent.com/aofeng/JavaDemo/master/src/cn/aofeng/demo/jetty/HttpServerMock.java)
```java
package cn.aofeng.demo.jetty;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
/**
* HTTP服务器MOCK可用于单元测试时模拟HTTP服务器的响应。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class HttpServerMock {
public final static int DEFAULT_PORT = 9191;
public final static String DEFAULT_CONTENT_TYPE = "application/json";
public final static int DEFAULT_STATUS_CODE=HttpServletResponse.SC_OK;
private Server _httpServer;
private int _port;
public HttpServerMock() {
_port = DEFAULT_PORT;
}
public HttpServerMock(int port) {
_port = port;
}
/**
* 启动Jetty服务器。默认的响应status code为"200"content type为"application/json"。
* @param content 响应内容
*/
public void start(String content) throws Exception {
start(content, DEFAULT_CONTENT_TYPE, DEFAULT_STATUS_CODE);
}
/**
* 启动Jetty服务器。默认的响应status code为"200"。
* @param content 响应内容
* @param contentType 响应内容的MIME类型
*/
public void start(String content, String contentType) throws Exception {
start(content, contentType, DEFAULT_STATUS_CODE);
}
/**
* 启动Jetty服务器。
* @param content 响应内容
* @param contentType 响应内容的MIME类型
* @param statuCode 响应状态码
*/
public void start(String content, String contentType,
int statuCode) throws Exception {
_httpServer = new Server(_port);
_httpServer.setHandler(createHandler(content, contentType, statuCode));
_httpServer.start();
}
/**
* 停止Jetty服务器。
*/
public void stop() throws Exception {
if (null != _httpServer) {
_httpServer.stop();
_httpServer = null;
}
}
private Handler createHandler(final String content, final String contentType,
final int statusCode) {
return new AbstractHandler() {
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request,
HttpServletResponse response) throws IOException, ServletException {
response.setContentType(contentType);
response.setStatus(statusCode);
baseRequest.setHandled(true);
response.getWriter().print(content);
}
};
}
}
```
单元测试代码
---
[源码下载](https://raw.githubusercontent.com/aofeng/JavaDemo/master/src/cn/aofeng/demo/jetty/HttpGetTest.java)
```java
package cn.aofeng.demo.jetty;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* {@link HttpGet}的单元测试用例。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class HttpGetTest {
private HttpServerMock _mock;
private HttpGet _httpGet = new HttpGet();
@Before
public void setUp() throws Exception {
_mock = new HttpServerMock();
}
@After
public void tearDown() throws Exception {
if (null != _mock) {
_mock.stop();
}
}
/**
* 用例响应状态码为200且有响应内容。
*/
@Test
public void testGetSomeThing4Success() throws Exception {
String response = "Hello, The World!";
_mock.start(response, "text/plain");
String content = _httpGet.getSomeThing("http://localhost:9191/hello");
assertEquals(response, content);
}
/**
* 用例响应状态码为非200。
*/
@Test
public void testGetSomeThing4Fail() throws Exception {
String response = "Hello, The World!";
_mock.start(response, "text/plain", 500);
String content = _httpGet.getSomeThing("http://localhost:9191/hello");
assertNull(content);
}
}
```

View File

@@ -0,0 +1,45 @@
package cn.aofeng.demo.json.gson;
import com.google.gson.Gson;
/**
* 数组的反序列化。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class ArrayDeserialize {
public <T> T deserialize(String json, Class<T> claz) {
Gson gson = new Gson();
return gson.fromJson(json, claz);
}
public static void main(String[] args) {
ArrayDeserialize ad = new ArrayDeserialize();
// 整型数组
String intArrJson = "[9,7,5]";
int[] intArr = ad.deserialize(intArrJson, int[].class);
System.out.println("---------- 整型数组 ----------");
for (int i : intArr) {
System.out.println(i);
}
// 字符串数组
String strArrJson = "[\"张三\",\"李四\",\"王五\"]";
String[] strArr = ad.deserialize(strArrJson, String[].class);
System.out.println("---------- 字符串数组 ----------");
for (String str : strArr) {
System.out.println(str);
}
// 对象数组
String objArrJson = "[{\"name\":\"小明\",\"age\":10},{\"name\":\"马丽\",\"age\":9}]";
Person[] objArr = ad.deserialize(objArrJson, Person[].class);
System.out.println("---------- 对象数组 ----------");
for (Person person : objArr) {
System.out.println(person);
}
}
}

View File

@@ -0,0 +1,41 @@
package cn.aofeng.demo.json.gson;
import com.google.gson.Gson;
/**
* 数组的序列化。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class ArraySerialize {
public void serialize(Object[] arr) {
Gson gson = new Gson();
System.out.println( gson.toJson(arr) );
}
public static void main(String[] args) {
ArraySerialize as = new ArraySerialize();
// 整型对象数组
Integer[] intArr = new Integer[3];
intArr[0] = 9;
intArr[1] = 7;
intArr[2] = 5;
as.serialize(intArr);
// 字符串数组
String[] names = new String[3];
names[0] = "张三";
names[1] = "李四";
names[2] = "王五";
as.serialize(names);
// 对象数组
Person[] persons = new Person[2];
persons[0] = new Person("小明", 10);
persons[1] = new Person("马丽", 9);
as.serialize(persons);
}
}

View File

@@ -0,0 +1,55 @@
package cn.aofeng.demo.json.gson;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
/**
* 集合的反序列化。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class CollectionDeserialize {
public <T> T deserialize(String json, Type type) {
Gson gson = new Gson();
return gson.fromJson(json, type);
}
public static void main(String[] args) {
CollectionDeserialize cd = new CollectionDeserialize();
//整型List
String intListJson = "[9,8,0]";
List<Integer> intList = cd.deserialize( intListJson,
new TypeToken<List<Integer>>(){}.getType() );
System.out.println("---------- 整型List ----------");
for (Integer obj : intList) {
System.out.println(obj);
}
// 字符串Set
String strSetJson = "[\"Best\",\"World\",\"Hello\"]";
Set<String> strSet = cd.deserialize( strSetJson,
new TypeToken<Set<String>>(){}.getType() );
System.out.println("---------- 字符串Set ----------");
for (String str : strSet) {
System.out.println(str);
}
// Map
String objMapJson = "{\"xiaomin\":{\"name\":\"小明\",\"age\":21},\"marry\":{\"name\":\"马丽\",\"age\":20}}";
Map<String, Person> objMap = cd.deserialize( objMapJson,
new TypeToken<Map<String, Person>>(){}.getType() );
System.out.println("---------- Map ----------");
for (Entry<String, Person> entry : objMap.entrySet()) {
System.out.println(entry);
}
}
}

View File

@@ -0,0 +1,54 @@
package cn.aofeng.demo.json.gson;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.gson.Gson;
/**
* 集合的序列化。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class CollectionsSerialize {
public void serialize(Collection<?> c) {
Gson gson = new Gson();
System.out.println( gson.toJson(c) );
}
public void serialize(Map<?, ?> map) {
Gson gson = new Gson();
System.out.println( gson.toJson(map) );
}
public static void main(String[] args) {
CollectionsSerialize cs = new CollectionsSerialize();
// 整型List
List<Integer> intList = new ArrayList<Integer>();
intList.add(9);
intList.add(8);
intList.add(0);
cs.serialize(intList);
// 字符串Set
Set<String> strSet = new HashSet<String>();
strSet.add("Hello");
strSet.add("World");
strSet.add("Best");
cs.serialize(strSet);
// Map
Map<String, Person> objMap = new HashMap<String, Person>();
objMap.put("marry", new Person("马丽", 20));
objMap.put("xiaomin", new Person("小明", 21));
cs.serialize(objMap);
}
}

View File

@@ -0,0 +1,46 @@
package cn.aofeng.demo.json.gson;
import java.lang.reflect.Type;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
/**
* 自定义反序列化。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class CustomDeserialize {
public static void main(String[] args) {
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Person.class, new PersonDeserializer());
Gson gson = builder.create();
String json = "{\"PersonName\":\"aofeng\",\"PersonAge\":32}";
Person obj = gson.fromJson(json, Person.class);
System.out.println(obj); // 输出结果Person [name=aofeng, age=32]
}
public static class PersonDeserializer implements JsonDeserializer<Person> {
@Override
public Person deserialize(JsonElement jsonEle, Type type,
JsonDeserializationContext context)
throws JsonParseException {
JsonObject jo = jsonEle.getAsJsonObject();
String name = jo.get("PersonName").getAsString();
int age = jo.get("PersonAge").getAsInt();
Person obj = new Person(name, age);
return obj;
}
} // end of PersonDeserializer
}

View File

@@ -0,0 +1,42 @@
package cn.aofeng.demo.json.gson;
import java.lang.reflect.Type;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
/**
* 自定义序列化。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class CustomSerialize {
public static void main(String[] args) {
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Person.class, new PersonSerializer());
Gson gson = builder.create();
Person obj = new Person("aofeng", 32);
System.out.println( gson.toJson(obj) ); // 输出结果:{"PersonName":"aofeng","PersonAge":32}
}
public static class PersonSerializer implements JsonSerializer<Person> {
@Override
public JsonElement serialize(Person obj, Type type,
JsonSerializationContext context) {
JsonObject jo = new JsonObject();
jo.addProperty("PersonName", obj.getName());
jo.addProperty("PersonAge", obj.getAge());
return jo;
}
} // end of PersonSerializer
}

View File

@@ -0,0 +1,50 @@
package cn.aofeng.demo.json.gson;
/**
* 简单的Java对象。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class Person {
private String name;
private int age;
public Person() {
// nothing
}
@SuppressWarnings("unused")
private void reset() {
name = null;
age = 0;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}

View File

@@ -0,0 +1,39 @@
package cn.aofeng.demo.json.gson;
import com.google.gson.Gson;
/**
* Java简单对象的序列化与反序列化。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class SimpleObjectSerialize {
/**
* 序列化将Java对象转换成JSON字符串。
*/
public void serialize(Person person) {
Gson gson = new Gson();
System.out.println( gson.toJson(person) );
}
/**
* 反序列化将JSON字符串转换成Java对象。
*/
public void deserialize(String json) {
Gson gson = new Gson();
Person person = gson.fromJson(json, Person.class);
System.out.println( person );
}
public static void main(String[] args) {
SimpleObjectSerialize ss = new SimpleObjectSerialize();
Person person = new Person("NieYong", 33);
ss.serialize(person);
String json = " {\"name\":\"AoFeng\",\"age\":32}";
ss.deserialize(json);
}
}

View File

@@ -0,0 +1,63 @@
package cn.aofeng.demo.misc;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
/**
* 获取本机IP和主机名以及Java环境信息。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class GetHostInfo {
/**
* @param args
* @throws UnknownHostException
* @throws SocketException
*/
public static void main(String[] args) throws UnknownHostException, SocketException {
InetAddress address = InetAddress.getLocalHost();
System.out.println("计算机名:" + address.getHostName());
Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces();
while (nis.hasMoreElements()) {
StringBuilder buffer = new StringBuilder();
NetworkInterface ni = nis.nextElement();
buffer.append("网卡:").append(ni.getName());
buffer.append(" 绑定IP");
Enumeration<InetAddress> ias = ni.getInetAddresses();
int count = 0;
while (ias.hasMoreElements()) {
InetAddress ia = ias.nextElement();
if (count > 0) {
buffer.append(", ");
}
buffer.append(ia.getHostAddress());
}
System.out.println(buffer.toString());
}
System.out.println("Java环境信息:");
System.out.println("---------------------------------------------------:");
Properties pros = System.getProperties();
Set<Entry<Object, Object>> javaEnums = pros.entrySet();
for (Entry<Object, Object> entry : javaEnums) {
System.out.println(entry.getKey() + " : " + entry.getValue());
}
System.out.println("");
System.out.println("系统环境信息:");
System.out.println("---------------------------------------------------:");
Map<String, String> envs = System.getenv();
Set<Entry<String, String>> envSet = envs.entrySet();
for (Entry<String, String> entry : envSet) {
System.out.println(entry.getKey() + " : " + entry.getValue());
}
}
}

View File

@@ -0,0 +1,50 @@
package cn.aofeng.demo.mockito;
/**
* 商品信息。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class Commodity {
private String id;
private String name;
private int type;
public Commodity() {
// nothing
}
public Commodity(String id, String name, int type) {
this.id = id;
this.name = name;
this.type = type;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
}

View File

@@ -0,0 +1,20 @@
package cn.aofeng.demo.mockito;
import java.util.List;
/**
* 商品存储层。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class CommodityDao {
public Commodity queryById(String id) {
return null;
}
public List<Commodity> queryByType(int type) {
return null;
}
}

View File

@@ -0,0 +1,70 @@
package cn.aofeng.demo.mockito;
/**
* 用户信息。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class User {
private long id;
private String name;
private int sex;
private int age;
private String address;
public User() {
// nothing
}
public User(long userId, String userName, int age) {
this.id = userId;
this.name = userName;
this.age = age;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}

View File

@@ -0,0 +1,19 @@
package cn.aofeng.demo.mockito;
import java.util.List;
/**
* 用户信息存储层。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class UserDao {
public User queryById(long userId) {
return null;
}
public List<User> queryByName(String userName) {
return null;
}
}

View File

@@ -0,0 +1,54 @@
package cn.aofeng.demo.mockito;
/**
* 用户服务。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class UserService {
/** 成人年龄分界线 */
private final static int ADULT_AGE = 18;
private UserDao _userDao;
private CommodityDao _commodityDao;
public boolean isAdult(long userId) {
User user = _userDao.queryById(userId);
if (null == user || user.getAge() < ADULT_AGE) {
return false;
}
return true;
}
public boolean buy(long userId, String commodityId) {
if (! isAdult(userId)) {
return false;
}
Commodity commodity = _commodityDao.queryById(commodityId);
if (null == commodity) {
return false;
}
// 省略余下的处理逻辑
return true;
}
public UserDao getUserDao() {
return _userDao;
}
public void setUserDao(UserDao userDao) {
this._userDao = userDao;
}
public CommodityDao getCommodityDao() {
return _commodityDao;
}
public void setCommodityDao(CommodityDao commodityDao) {
this._commodityDao = commodityDao;
}
}

View File

@@ -0,0 +1,66 @@
package cn.aofeng.demo.mockito;
import static org.junit.Assert.*;
import org.junit.Test;
import static org.mockito.Mockito.*;
/**
* {@link UserService}的单元测试用例。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class UserServiceTest {
private UserService _userService = new UserService();
@Test
public void testIsAdult4UserExist() {
long userId = 123;
User user = new User(userId, "张三", 19);
// 大于18岁的测试用例
UserDao daoMock = mock(UserDao.class);
when(daoMock.queryById(userId)).thenReturn(user); // 设置行为和对应的返回值
_userService.setUserDao(daoMock); // 设置mock
assertTrue(_userService.isAdult(userId)); // 校验结果
// 等于18岁的测试用例
User user2 = new User(userId, "李四", 18);
when(daoMock.queryById(userId)).thenReturn(user2);
_userService.setUserDao(daoMock);
assertTrue(_userService.isAdult(userId));
// 小于18岁的测试用例
User user3 = new User(userId, "王五", 17);
when(daoMock.queryById(userId)).thenReturn(user3);
_userService.setUserDao(daoMock);
assertFalse(_userService.isAdult(userId));
}
@Test
public void testIsAdult4UserNotExist() {
// 用户不存在的测试用例
long userId = 123;
UserDao daoMock = mock(UserDao.class);
when(daoMock.queryById(userId)).thenReturn(null); // 设置行为和对应的返回值
_userService.setUserDao(daoMock); // 设置mock
assertFalse(_userService.isAdult(userId)); // 校验结果
}
@Test
public void testBuy() {
long userId = 12345;
UserDao daoMock = mock(UserDao.class);
when(daoMock.queryById(anyLong())).thenReturn(new User(userId, "张三", 19));
String commodityId = "S01A10009823";
CommodityDao commodityDao = mock(CommodityDao.class);
when(commodityDao.queryById(anyString())).thenReturn(new Commodity(commodityId, "xxx手机", 1));
_userService.setUserDao(daoMock);
_userService.setCommodityDao(commodityDao);
assertTrue(_userService.buy(userId, commodityId));
}
}

View File

@@ -0,0 +1,62 @@
package cn.aofeng.demo.mybatis;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import cn.aofeng.demo.mybatis.dao.MonitNotifyHistoryDao;
import cn.aofeng.demo.mybatis.entity.MonitNotifyHistory;
/**
*
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class MyBatisClient {
private static Logger _logger = LoggerFactory.getLogger(MyBatisClient.class);
public static void main(String[] args) throws IOException {
String resource = "mybatis-config.xml";
InputStream ins = Resources.getResourceAsStream(resource);
SqlSessionFactory ssFactory = new SqlSessionFactoryBuilder().build(ins);
MonitNotifyHistoryDao dao = new MonitNotifyHistoryDao(ssFactory);
// 插入一条记录
long recordId = 999;
MonitNotifyHistory record = new MonitNotifyHistory();
record.setRecordId(recordId);
record.setMonitId(9999);
record.setAppId(99);
record.setNotifyTarget(1);
record.setNotifyType(1);
record.setNotifyContent("通知内容测试");
record.setStatus(0);
record.setCreateTime(1492061400000L);
int count = dao.insert(record);
_logger.info("insert complete, effects {} rows", count);
// 查询记录
MonitNotifyHistory entity = dao.selectByPrimaryKey(recordId);
_logger.info("query record id {}, result: {}", recordId, entity);
// 更新一个字段再执行查询
entity.setRetryTimes(99);
count = dao.updateByPrimaryKeySelective(entity);
_logger.info("update complete, record id:{}, effects {} rows", recordId, count);
entity = dao.selectByPrimaryKey(recordId);
_logger.info("query record id {}, result: {}", recordId, entity);
// 删除记录后再执行查询
count = dao.deleteByPrimaryKey(recordId);
_logger.info("delete complete, record id {}, effects {} rows", recordId, count);
entity = dao.selectByPrimaryKey(recordId);
_logger.info("query record id {}, result: {}", recordId, entity);
}
}

View File

@@ -0,0 +1,5 @@
* [MyBatisClien.java](MyBatisClient.java) 入口类。读取配置文件生成SqlSessionFactory使用DAO操作表。
* [mybatis-config.xml](../../../../../conf/mybatis-config.xml) mybatis配置文件
* [MonitNotifyHistoryDao](dao/MonitNotifyHistoryDao.java) DAO类
* [MonitNotifyHisto.java](entity/MonitNotifyHistory.java) 实体类
* [MonitNotifyHistoryMapper.xml](mapper/MonitNotifyHistoryMapper.xml) SQL模板映射配置文件

View File

@@ -0,0 +1,79 @@
package cn.aofeng.demo.mybatis.dao;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import cn.aofeng.demo.mybatis.entity.MonitNotifyHistory;
/**
* 监控通知历史CURD。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class MonitNotifyHistoryDao {
private SqlSessionFactory _ssFactory = null;
public MonitNotifyHistoryDao(SqlSessionFactory factory) {
this._ssFactory = factory;
}
private String assembleStatement(String id) {
return "cn.aofeng.demo.mybatis.dao.MonitNotifyHistoryMapper."+id;
}
private void close(SqlSession session) {
if (null != session) {
session.close();
}
}
public int deleteByPrimaryKey(Long recordId) {
SqlSession session = _ssFactory.openSession(true);
int result = 0;
try {
session.delete(assembleStatement("deleteByPrimaryKey"), recordId);
} finally {
close(session);
}
return result;
}
public int insert(MonitNotifyHistory record) {
SqlSession session = _ssFactory.openSession(true);
int result = 0;
try {
result = session.insert(assembleStatement("insertSelective"), record);
} finally {
close(session);
}
return result;
}
public MonitNotifyHistory selectByPrimaryKey(Long recordId) {
SqlSession session = _ssFactory.openSession();
MonitNotifyHistory result = null;
try {
result = session.selectOne(assembleStatement("selectByPrimaryKey"), recordId);
} finally {
close(session);
}
return result;
}
public int updateByPrimaryKeySelective(MonitNotifyHistory record) {
SqlSession session = _ssFactory.openSession(true);
int result = 0;
try {
session.update(assembleStatement("updateByPrimaryKeySelective"), record);
} finally {
close(session);
}
return result;
}
}

View File

@@ -0,0 +1,105 @@
package cn.aofeng.demo.mybatis.entity;
/**
* 监控通知历史实体类。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class MonitNotifyHistory {
private Long recordId;
private Integer monitId;
private Integer appId;
private Integer notifyType;
private Integer notifyTarget;
private String notifyContent;
private Integer status;
private Integer retryTimes;
private Long createTime;
public Long getRecordId() {
return recordId;
}
public void setRecordId(Long recordId) {
this.recordId = recordId;
}
public Integer getMonitId() {
return monitId;
}
public void setMonitId(Integer monitId) {
this.monitId = monitId;
}
public Integer getAppId() {
return appId;
}
public void setAppId(Integer appId) {
this.appId = appId;
}
public Integer getNotifyType() {
return notifyType;
}
public void setNotifyType(Integer notifyType) {
this.notifyType = notifyType;
}
public Integer getNotifyTarget() {
return notifyTarget;
}
public void setNotifyTarget(Integer notifyTarget) {
this.notifyTarget = notifyTarget;
}
public String getNotifyContent() {
return notifyContent;
}
public void setNotifyContent(String notifyContent) {
this.notifyContent = notifyContent;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public Integer getRetryTimes() {
return retryTimes;
}
public void setRetryTimes(Integer retryTimes) {
this.retryTimes = retryTimes;
}
public Long getCreateTime() {
return createTime;
}
public void setCreateTime(Long createTime) {
this.createTime = createTime;
}
@Override
public String toString() {
return "MonitNotifyHistory [recordId=" + recordId + ", monitId=" + monitId + ", appId=" + appId + ", notifyType=" + notifyType + ", notifyTarget="
+ notifyTarget + ", notifyContent=" + notifyContent + ", status=" + status + ", retryTimes=" + retryTimes + ", createTime=" + createTime + "]";
}
}

View File

@@ -0,0 +1,139 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.aofeng.demo.mybatis.dao.MonitNotifyHistoryMapper">
<resultMap id="BaseResultMap" type="cn.aofeng.demo.mybatis.entity.MonitNotifyHistory">
<id column="record_id" jdbcType="BIGINT" property="recordId" />
<result column="monit_id" jdbcType="INTEGER" property="monitId" />
<result column="app_id" jdbcType="TINYINT" property="appId" />
<result column="notify_type" jdbcType="TINYINT" property="notifyType" />
<result column="notify_target" jdbcType="INTEGER" property="notifyTarget" />
<result column="notify_content" jdbcType="VARCHAR" property="notifyContent" />
<result column="status" jdbcType="TINYINT" property="status" />
<result column="retry_times" jdbcType="TINYINT" property="retryTimes" />
<result column="create_time" jdbcType="BIGINT" property="createTime" />
</resultMap>
<select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
select record_id, monit_id, app_id, notify_type, notify_target, notify_content, status,
retry_times, create_time
from monit_notify_history
where record_id = #{recordId,jdbcType=BIGINT}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
delete from monit_notify_history
where record_id = #{recordId,jdbcType=BIGINT}
</delete>
<insert id="insert" parameterType="cn.aofeng.demo.mybatis.entity.MonitNotifyHistory">
insert into monit_notify_history (record_id, monit_id, app_id,
notify_type, notify_target, notify_content,
status, retry_times, create_time
)
values (#{recordId,jdbcType=BIGINT}, #{monitId,jdbcType=INTEGER}, #{appId,jdbcType=TINYINT},
#{notifyType,jdbcType=TINYINT}, #{notifyTarget,jdbcType=INTEGER}, #{notifyContent,jdbcType=VARCHAR},
#{status,jdbcType=TINYINT}, #{retryTimes,jdbcType=TINYINT}, #{createTime,jdbcType=BIGINT}
)
</insert>
<insert id="insertSelective" parameterType="cn.aofeng.demo.mybatis.entity.MonitNotifyHistory">
insert into monit_notify_history
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="recordId != null">
record_id,
</if>
<if test="monitId != null">
monit_id,
</if>
<if test="appId != null">
app_id,
</if>
<if test="notifyType != null">
notify_type,
</if>
<if test="notifyTarget != null">
notify_target,
</if>
<if test="notifyContent != null">
notify_content,
</if>
<if test="status != null">
status,
</if>
<if test="retryTimes != null">
retry_times,
</if>
<if test="createTime != null">
create_time,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="recordId != null">
#{recordId,jdbcType=BIGINT},
</if>
<if test="monitId != null">
#{monitId,jdbcType=INTEGER},
</if>
<if test="appId != null">
#{appId,jdbcType=TINYINT},
</if>
<if test="notifyType != null">
#{notifyType,jdbcType=TINYINT},
</if>
<if test="notifyTarget != null">
#{notifyTarget,jdbcType=INTEGER},
</if>
<if test="notifyContent != null">
#{notifyContent,jdbcType=VARCHAR},
</if>
<if test="status != null">
#{status,jdbcType=TINYINT},
</if>
<if test="retryTimes != null">
#{retryTimes,jdbcType=TINYINT},
</if>
<if test="createTime != null">
#{createTime,jdbcType=BIGINT},
</if>
</trim>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="cn.aofeng.demo.mybatis.entity.MonitNotifyHistory">
update monit_notify_history
<set>
<if test="monitId != null">
monit_id = #{monitId,jdbcType=INTEGER},
</if>
<if test="appId != null">
app_id = #{appId,jdbcType=TINYINT},
</if>
<if test="notifyType != null">
notify_type = #{notifyType,jdbcType=TINYINT},
</if>
<if test="notifyTarget != null">
notify_target = #{notifyTarget,jdbcType=INTEGER},
</if>
<if test="notifyContent != null">
notify_content = #{notifyContent,jdbcType=VARCHAR},
</if>
<if test="status != null">
status = #{status,jdbcType=TINYINT},
</if>
<if test="retryTimes != null">
retry_times = #{retryTimes,jdbcType=TINYINT},
</if>
<if test="createTime != null">
create_time = #{createTime,jdbcType=BIGINT},
</if>
</set>
where record_id = #{recordId,jdbcType=BIGINT}
</update>
<update id="updateByPrimaryKey" parameterType="cn.aofeng.demo.mybatis.entity.MonitNotifyHistory">
update monit_notify_history
set monit_id = #{monitId,jdbcType=INTEGER},
app_id = #{appId,jdbcType=TINYINT},
notify_type = #{notifyType,jdbcType=TINYINT},
notify_target = #{notifyTarget,jdbcType=INTEGER},
notify_content = #{notifyContent,jdbcType=VARCHAR},
status = #{status,jdbcType=TINYINT},
retry_times = #{retryTimes,jdbcType=TINYINT},
create_time = #{createTime,jdbcType=BIGINT}
where record_id = #{recordId,jdbcType=BIGINT}
</update>
</mapper>

View File

@@ -0,0 +1,55 @@
package cn.aofeng.demo.netty40x.echo;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.log4j.Logger;
/**
* Echo客户端。
*
* @author <a href="mailto:aofengblog@163.com">聂勇</a>
*/
public class EchoClient {
private static Logger _logger = Logger.getLogger(EchoClient.class);
public void start(String host, int port) {
EventLoopGroup worker = new NioEventLoopGroup(1);
Bootstrap bootstrap = new Bootstrap();
try {
bootstrap.group(worker)
.channel(NioSocketChannel.class)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000)
.handler(new EchoClientHandler())
.remoteAddress(host, port);
ChannelFuture cf = bootstrap.connect().sync();
cf.channel().closeFuture().sync();
} catch (Exception e) {
_logger.error("occurs error", e);
} finally {
worker.shutdownGracefully();
}
}
/**
* @param args
*/
public static void main(String[] args) {
if (args.length != 2) {
_logger.error("Invalid arguments。Usagejava cn.aofeng.demo.netty40x.echo.EchoClient 192.168.56.102 8080");
System.exit(-1);
}
String host = args[0];
int port = NumberUtils.toInt(args[1], 8080);
EchoClient client = new EchoClient();
client.start(host, port);
}
}

Some files were not shown because too many files have changed in this diff Show More