java反射机制之Method invoke执行调用方法例子
来源:互联网 发布:办公室软件2013 编辑:程序博客网 时间:2024/05/14 15:23
java反射机制之Method invoke执行调用方法例子
昨天在群里跟大家讨论了下java反射调用可变参数的问题,这个问题起因是我们需要反射调用另一个部门提供的方法,我同事说java不能反射调用可变参数的方法,于是我写了个demo证明了他这个观点的错误。但是测试过程中,有一点我不明白,就是反射调用可变参数的方法时,为什么一定要保证传入的参数数组长度为1,在群里跟大家讨论了很多,没有得到确切的答案,参照网上大牛写的东西和我自己跟源码的过程,记录如下:
1.两个类,一个父类,一个子类
1
package
com.reflect.test;
2
3
public
class
BaseObject {
4
5
public
void
getObjectName(){
6
System.out.println(
"BaseObject"
);
7
}
8
9
}
01
package
com.reflect.test;
02
03
public
class
SubObject
extends
BaseObject{
04
@Override
05
public
void
getObjectName() {
06
System.out.println(
"SubObject"
);
07
}
08
public
void
getParamsLength(String...params){
09
System.out.println(
"param's length is:"
+params.length);
10
}
11
public
void
getParamsLength(String param1,String param2){
12
System.out.println(param1 +
"-"
+ param2);
13
}
14
}
2.测试类,主要测试重载方法的调用、可变参数方法的调用、定参方法的调用
01
package
com.reflect.test;
02
03
import
java.lang.reflect.Method;
04
05
public
class
ReflectTest {
06
07
private
static
final
String BASE_OBJECT_PATH =
"com.reflect.test.BaseObject"
;
08
private
static
final
String SUB_OBJECT_PATH =
"com.reflect.test.SubObject"
;
09
10
public
static
void
main(String[] args)
throws
Exception{
11
12
Class<?> bClazz = Class.forName(BASE_OBJECT_PATH);
13
Class<?> sClazz = Class.forName(SUB_OBJECT_PATH);
14
15
Object bObj = bClazz.newInstance();
//父类实例
16
Object sObj = sClazz.newInstance();
//子类实例
17
18
//1.反射调用子类父类的重载方法
19
//多态+动态绑定
20
Method bMethod = bClazz.getDeclaredMethod(
"getObjectName"
);
21
bMethod.invoke(bObj);
//父类的bMethod调用父类的getObjectName()
22
bMethod.invoke(sObj);
//父类的bMethod调用子类的getObjectName();
23
24
Method sMethod = sClazz.getDeclaredMethod(
"getObjectName"
);
25
//不符合多态和动态绑定
26
//sMethod.invoke(bObj);//sMethod调用父类的getObjectName(),会报错:java.lang.IllegalArgumentException: object is not an instance of declaring class
27
sMethod.invoke(sObj);
28
29
//2.反射调用可变参数的方法
30
Method changeMethod = sClazz.getDeclaredMethod(
"getParamsLength"
, String[].
class
);
31
//可变参数必须这样封装,因为java反射内部实现做了参数个数为1的判断,如果参数长度不为1,则会抛出异常
32
String[] strParams = {
"a"
,
"b"
,
"c"
};
33
Object[] cParams = {strParams};
34
changeMethod.invoke(sObj, cParams);
35
36
//3.反射调用固定长度参数的方法
37
Method unChangeMethod1 = sClazz.getDeclaredMethod(
"getParamsLength"
, String.
class
,String.
class
);
38
unChangeMethod1.invoke(sObj,
"Hello"
,
"Java"
);
39
//也可以写成这样
40
Class<?>[] clazzs = {String.
class
,String.
class
};
41
Method unChangeMethod2 = sClazz.getDeclaredMethod(
"getParamsLength"
, clazzs);
42
unChangeMethod2.invoke(sObj,
"Hello"
,
"Java"
);
43
//下面的这种调用形式也是可以的,不过会报警告
44
//String[] params1 = {"Hello","Java"};
45
//unChangeMethod1.invoke(sObj, params1);
46
}
47
}
下面是JDK里面Method 的invoke方法的源码
从代码中可以看出,先检查 AccessibleObject的override属性是否为true(override属性默认为false)。AccessibleObject是Method,Field,Constructor的父类,可调用setAccessible方法改变,如果设置为true,则表示可以忽略访问权限的限制,直接调用。
如果不是ture,则要进行访问权限检测。用Reflection的quickCheckMemberAccess方法先检查是不是public的,如果不是再用Reflection.getCallerClass()方法获得到调用这个方法的Class,然后做是否有权限访问的校验,校验之后缓存一次,以便下次如果还是这个类来调用就不用去做校验了,直接用上次的结果。
01
@CallerSensitive
02
public
Object invoke(Object obj, Object... args)
03
throws
IllegalAccessException, IllegalArgumentException,
04
InvocationTargetException
05
{
06
if
(!override) {
07
if
(!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
08
// Until there is hotspot @CallerSensitive support
09
// can't call Reflection.getCallerClass() here
10
// Workaround for now: add a frame getCallerClass to
11
// make the caller at stack depth 2
12
Class<?> caller = getCallerClass();
13
checkAccess(caller, clazz, obj, modifiers);
14
}
15
}
16
MethodAccessor ma = methodAccessor;
// read volatile
17
if
(ma ==
null
) {
18
ma = acquireMethodAccessor();
19
}
20
return
ma.invoke(obj, args);
21
}
22
23
//验证的代码,securityCheckCache就是JDK做的缓存
24
volatile
Object securityCheckCache;
25
26
void
checkAccess(Class<?> caller, Class<?> clazz, Object obj,
int
modifiers)
27
throws
IllegalAccessException
28
{
29
if
(caller == clazz) {
// quick check
30
return
;
// ACCESS IS OK
31
}
32
Object cache = securityCheckCache;
// read volatile
33
Class<?> targetClass = clazz;
34
if
(obj !=
null
35
&& Modifier.isProtected(modifiers)
36
&& ((targetClass = obj.getClass()) != clazz)) {
37
// Must match a 2-list of { caller, targetClass }.
38
if
(cache
instanceof
Class[]) {
39
Class<?>[] cache2 = (Class<?>[]) cache;
40
if
(cache2[
1
] == targetClass &&
41
cache2[
0
] == caller) {
42
return
;
// ACCESS IS OK
43
}
44
// (Test cache[1] first since range check for [1]
45
// subsumes range check for [0].)
46
}
47
}
else
if
(cache == caller) {
48
// Non-protected case (or obj.class == this.clazz).
49
return
;
// ACCESS IS OK
50
}
51
52
// If no return, fall through to the slow path.
53
slowCheckMemberAccess(caller, clazz, obj, modifiers, targetClass);
54
}
然后就是调用MethodAccessor的invoke方法了。
调用MethodAccessor的invoke方法。每个Method对象包含一个root对象,root对象里持有一个MethodAccessor对象。这个对象由ReflectionFactory方法生成,ReflectionFactory对象在Method类中是static final的由native方法实例化。代码片段如下;
01
//Method类中的代码片段,生成MethodAccessor
02
private
volatile
MethodAccessor methodAccessor;
03
private
Method root;
04
private
MethodAccessor acquireMethodAccessor() {
05
// First check to see if one has been created yet, and take it
06
// if so
07
MethodAccessor tmp =
null
;
08
if
(root !=
null
) tmp = root.getMethodAccessor();
09
if
(tmp !=
null
) {
10
methodAccessor = tmp;
11
}
else
{
12
// Otherwise fabricate one and propagate it up to the root
13
tmp = reflectionFactory.newMethodAccessor(
this
);
14
setMethodAccessor(tmp);
15
}
16
17
return
tmp;
18
}
19
20
// reflectionFactory在父类AccessibleObject中定义,代码片段如下:
21
static
final
ReflectionFactory reflectionFactory =
22
AccessController.doPrivileged(
23
new
sun.reflect.ReflectionFactory.GetReflectionFactoryAction());
ReflectionFactory生成MethodAccessor:如果noInflation的属性为true则直接返回MethodAccessorGenerator创建的一个MethodAccessor,否则返回DelegatingMethodAccessorImpl,并将他与一个NativeMethodAccessorImpl互相引用。但DelegatingMethodAccessorImpl执行invoke方法的时候又委托给NativeMethodAccessorImpl了。代码片段如下:
01
public
MethodAccessor newMethodAccessor(Method paramMethod) {
02
checkInitted();
03
04
if
(noInflation) {
05
return
new
MethodAccessorGenerator().generateMethod(paramMethod.getDeclaringClass(), paramMethod.getName(), paramMethod.getParameterTypes(), paramMethod.getReturnType(), paramMethod.getExceptionTypes(), paramMethod.getModifiers());
06
}
07
08
NativeMethodAccessorImpl localNativeMethodAccessorImpl =
new
NativeMethodAccessorImpl(paramMethod);
09
10
DelegatingMethodAccessorImpl localDelegatingMethodAccessorImpl =
new
DelegatingMethodAccessorImpl(localNativeMethodAccessorImpl);
11
12
localNativeMethodAccessorImpl.setParent(localDelegatingMethodAccessorImpl);
13
return
localDelegatingMethodAccessorImpl;
14
}
MethodAccessor实现有两个版本,一个是Java实现的,另一个是native code实现的。Java实现的版本在初始化时需要较多时间,但长久来说性能较好;native版本正好相反,启动时相对较快,但运行时间长了之后速度就比不过Java版了。这是HotSpot的优化方式带来的性能特性,同时也是许多虚拟机的共同点:跨越native边界会对优化有阻碍作用,它就像个黑箱一样让虚拟机难以分析也将其内联,于是运行时间长了之后反而是托管版本的代码更快些。 为了权衡两个版本的性能,Sun的JDK使用了“inflation”的技巧:让Java方法在被反射调用时,开头若干次使用native版,等反射调用次数超过阈值时则生成一个专用的MethodAccessor实现类,生成其中的invoke()方法的字节码,以后对该Java方法的反射调用就会使用Java版。
看下NativeMethodAccessorImpl 中的invoke方法:
代码片段如下:
01
package
sun.reflect;
02
03
import
java.lang.reflect.InvocationTargetException;
04
import
java.lang.reflect.Method;
05
06
class
NativeMethodAccessorImpl
extends
MethodAccessorImpl
07
{
08
private
Method method;
09
private
DelegatingMethodAccessorImpl parent;
10
private
int
numInvocations;
11
12
NativeMethodAccessorImpl(Method paramMethod)
13
{
14
this
.method = paramMethod;
15
}
16
17
public
Object invoke(Object paramObject, Object[] paramArrayOfObject)
18
throws
IllegalArgumentException, InvocationTargetException
19
{
20
if
(++
this
.numInvocations > ReflectionFactory.inflationThreshold()) {
21
MethodAccessorImpl localMethodAccessorImpl = (MethodAccessorImpl)
new
MethodAccessorGenerator().generateMethod(
this
.method.getDeclaringClass(),
this
.method.getName(),
this
.method.getParameterTypes(),
this
.method.getReturnType(),
this
.method.getExceptionTypes(),
this
.method.getModifiers());
22
23
this
.parent.setDelegate(localMethodAccessorImpl);
24
}
25
26
return
invoke0(
this
.method, paramObject, paramArrayOfObject);
27
}
28
29
void
setParent(DelegatingMethodAccessorImpl paramDelegatingMethodAccessorImpl) {
30
this
.parent = paramDelegatingMethodAccessorImpl;
31
}
32
33
private
static
native
Object invoke0(Method paramMethod, Object paramObject, Object[] paramArrayOfObject);
34
}
调用natiave方法invoke0执行方法调用.
注意这里有一个计数器numInvocations,每调用一次方法+1,当比 ReflectionFactory.inflationThreshold(15)大的时候,用MethodAccessorGenerator创建一个MethodAccessor,并把之前的DelegatingMethodAccessorImpl引用替换为现在新创建的。下一次DelegatingMethodAccessorImpl就不会再交给NativeMethodAccessorImpl执行了,而是交给新生成的java字节码的MethodAccessor
每次NativeMethodAccessorImpl.invoke()方法被调用时,都会增加一个调用次数计数器,看超过阈值没有;一旦超过,则调用MethodAccessorGenerator.generateMethod()来生成Java版的MethodAccessor的实现类,并且改变DelegatingMethodAccessorImpl所引用的MethodAccessor为Java版。后续经由DelegatingMethodAccessorImpl.invoke()调用到的就是Java版的实现了。
注意到关键的invoke0()方法是个native方法。它在HotSpot VM里是由JVM_InvokeMethod()函数所支持的,是用C写的
为了验证这个结论,我故意写出一个非法参数,循环调用16次并catch下异常,结果如下:从结果中看出,前15次都是调用NativeMethodAccessorImpl,第16次开始就是调用DelegatingMethodAccessorImpl了。
01
java.lang.IllegalArgumentException: wrong number of arguments
02
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
03
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
57
)
04
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
43
)
05
at java.lang.reflect.Method.invoke(Method.java:
606
)
06
at com.reflect.test.ReflectTest.main(ReflectTest.java:
44
)
07
java.lang.IllegalArgumentException: wrong number of arguments
08
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
09
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
57
)
10
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
43
)
11
at java.lang.reflect.Method.invoke(Method.java:
606
)
12
at com.reflect.test.ReflectTest.main(ReflectTest.java:
44
)
13
java.lang.IllegalArgumentException: wrong number of arguments
14
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
15
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
57
)
16
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
43
)
17
at java.lang.reflect.Method.invoke(Method.java:
606
)
18
at com.reflect.test.ReflectTest.main(ReflectTest.java:
44
)
19
java.lang.IllegalArgumentException: wrong number of arguments
20
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
21
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
57
)
22
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
43
)
23
at java.lang.reflect.Method.invoke(Method.java:
606
)
24
at com.reflect.test.ReflectTest.main(ReflectTest.java:
44
)
25
java.lang.IllegalArgumentException: wrong number of arguments
26
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
27
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
57
)
28
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
43
)
29
at java.lang.reflect.Method.invoke(Method.java:
606
)
30
at com.reflect.test.ReflectTest.main(ReflectTest.java:
44
)
31
java.lang.IllegalArgumentException: wrong number of arguments
32
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
33
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
57
)
34
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
43
)
35
at java.lang.reflect.Method.invoke(Method.java:
606
)
36
at com.reflect.test.ReflectTest.main(ReflectTest.java:
44
)
37
java.lang.IllegalArgumentException: wrong number of arguments
38
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
39
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
57
)
40
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
43
)
41
at java.lang.reflect.Method.invoke(Method.java:
606
)
42
at com.reflect.test.ReflectTest.main(ReflectTest.java:
44
)
43
java.lang.IllegalArgumentException: wrong number of arguments
44
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
45
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
57
)
46
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
43
)
47
at java.lang.reflect.Method.invoke(Method.java:
606
)
48
at com.reflect.test.ReflectTest.main(ReflectTest.java:
44
)
49
java.lang.IllegalArgumentException: wrong number of arguments
50
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
51
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
57
)
52
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
43
)
53
at java.lang.reflect.Method.invoke(Method.java:
606
)
54
at com.reflect.test.ReflectTest.main(ReflectTest.java:
44
)
55
java.lang.IllegalArgumentException: wrong number of arguments
56
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
57
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
57
)
58
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
43
)
59
at java.lang.reflect.Method.invoke(Method.java:
606
)
60
at com.reflect.test.ReflectTest.main(ReflectTest.java:
44
)
61
java.lang.IllegalArgumentException: wrong number of arguments
62
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
63
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
57
)
64
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
43
)
65
at java.lang.reflect.Method.invoke(Method.java:
606
)
66
at com.reflect.test.ReflectTest.main(ReflectTest.java:
44
)
67
java.lang.IllegalArgumentException: wrong number of arguments
68
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
69
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
57
)
70
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
43
)
71
at java.lang.reflect.Method.invoke(Method.java:
606
)
72
at com.reflect.test.ReflectTest.main(ReflectTest.java:
44
)
73
java.lang.IllegalArgumentException: wrong number of arguments
74
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
75
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
57
)
76
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
43
)
77
at java.lang.reflect.Method.invoke(Method.java:
606
)
78
at com.reflect.test.ReflectTest.main(ReflectTest.java:
44
)
79
java.lang.IllegalArgumentException: wrong number of arguments
80
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
81
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
57
)
82
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
43
)
83
at java.lang.reflect.Method.invoke(Method.java:
606
)
84
at com.reflect.test.ReflectTest.main(ReflectTest.java:
44
)
85
java.lang.IllegalArgumentException: wrong number of arguments
86
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
87
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
57
)
88
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
43
)
89
at java.lang.reflect.Method.invoke(Method.java:
606
)
90
at com.reflect.test.ReflectTest.main(ReflectTest.java:
44
)
91
java.lang.IllegalArgumentException
92
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
93
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
43
)
94
at java.lang.reflect.Method.invoke(Method.java:
606
)
95
at com.reflect.test.ReflectTest.main(ReflectTest.java:
44
)
下面看看java版的DelegatingMethodAccessorImpl的实现:
01
package
sun.reflect;
02
03
import
java.lang.reflect.InvocationTargetException;
04
05
class
DelegatingMethodAccessorImpl
extends
MethodAccessorImpl
06
{
07
private
MethodAccessorImpl delegate;
08
09
DelegatingMethodAccessorImpl(MethodAccessorImpl paramMethodAccessorImpl)
10
{
11
setDelegate(paramMethodAccessorImpl);
12
}
13
14
public
Object invoke(Object paramObject, Object[] paramArrayOfObject)
15
throws
IllegalArgumentException, InvocationTargetException
16
{
17
return
this
.delegate.invoke(paramObject, paramArrayOfObject);
18
}
19
20
void
setDelegate(MethodAccessorImpl paramMethodAccessorImpl) {
21
this
.delegate = paramMethodAccessorImpl;
22
}
23
24
25
package
sun.reflect;
26
27
public
class
GeneratedMethodAccessor1
extends
MethodAccessorImpl {
28
public
GeneratedMethodAccessor1() {
29
super
();
30
}
31
32
public
Object invoke(Object obj, Object[] args)
33
throws
IllegalArgumentException, InvocationTargetException {
34
// prepare the target and parameters
35
if
(obj ==
null
)
throw
new
NullPointerException();
36
try
{
37
A target = (A) obj;
38
if
(args.length !=
1
)
throw
new
IllegalArgumentException();
39
String arg0 = (String) args[
0
];
40
}
catch
(ClassCastException e) {
41
throw
new
IllegalArgumentException(e.toString());
42
}
catch
(NullPointerException e) {
43
throw
new
IllegalArgumentException(e.toString());
44
}
45
// make the invocation
46
try
{
47
target.foo(arg0);
48
}
catch
(Throwable t) {
49
throw
new
InvocationTargetException(t);
50
}
51
}
52
}
if (args.length != 1) throw new IllegalArgumentException();这一句就能解释我之前的疑问了,这块会判断参数数组的长度,如果长度不等于1,就会抛出非法参数的异常。
而且MethodAccessor会做强制类型转换再进行方法调用,但父类强制转化成子类的的时候就会报错类型不匹配错误了,所以如果变量的引用声明是父但实际指向的对象是子,那么这种调用也是可以的。
转载自:http://www.zuidaima.com/share/1843292395834368.htm
- java反射机制之Method invoke执行调用方法例子
- java反射机制之Method invoke执行调用方法例子
- java反射机制之Method invoke执行调用方法例子
- java反射机制之Method invoke执行调用方法例子
- 说下java反射机制之Method invoke执行调用方法例子
- java反射机制--Method.invoke方法
- 常用java反射机制 和 Method.invoke
- Java反射机制 以及 method.invoke运用
- java反射机制以及Method.invoke解释
- Java反射机制及Method.invoke详解
- Java反射机制及Method.invoke详解
- java中的反射--method的invoke方法
- java 反射中的method.invoke()方法详解
- java反射机制 + Method.invoke解释 getMethod + 反射理解
- java 反射机制 之 getMethod获取公有方法 getDeclaredMethod获取所有方法 然后invoke执行其所有方法
- java反射机制学习笔记与Method类invoke方法jvm底层优化方案
- java-Java反射机制及Method.invoke详解
- java反射Method中的如何调用任意方法,即invoke()的使用
- 学习iOS的第七天
- 双机高可用、负载均衡、MySQL(读写分离、主从自动切换)架构设计
- Activity生命周期
- 测试udp 端口是否开放
- Java算法——求1到100的偶数和,奇数和
- java反射机制之Method invoke执行调用方法例子
- 你可能不清楚的Java细节(1)--为什么Boolean的hashCode()方法返回值是1231或1237
- MVC+Easyui+EF datagrid数据编辑(5)
- Jmeter分布式测试(二)
- JSP 发送邮件
- 2016年公益
- 数据库学习记录--复索引
- 基于Ofbiz的Web框架
- 通过命令初始化Git管理