Java反射知识小结
反射知识在我近期的开发中用到的不过,所以知识点也不是很清楚。今天补习了一下反射部分内容,在这里做一个小结。
-
Java反射的概念
反射含义:可以获取正在运行的Java对象。 -
Java反射的功能
1)可以判断运行时对象所属的类
2)可以判断运行时对象所具有的成员变量和方法
3)通过反射甚至可以调用到private的方法
4)生成动态代理 -
Java反射机制
- 在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意一个方法;
这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。 - Java反射机制主要提供了以下功能:
在运行时判断任意一个对象所属的类;
在运行时构造任意一个类的对象;
在运行时判断任意一个类所具有的成员变量和方法;
在运行时调用任意一个对象的方法;
生成动态代理。
- 在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
-
实现Java反射的类
1) Class:它表示正在运行的Java应用程序中的类和接口
2) Field:提供有关类或接口的属性信息,以及对它的动态访问权限
3) Constructor:提供关于类的单个构造方法的信息以及对它的访问权限
4) Method:提供关于类或接口中某个方法信息
注意:Class类是Java反射中最重要的一个功能类,所有获取对象的信息(包括:方法/ 属性/构造方法/访问权限)都需要它来实现。 -
编写Java反射程序的步骤:
1) 必须首先获取一个类的Class对象
例如(推荐第一种):
Class c1 = Test.class;
Class c2 = Class.forName(“com.reflection.Test”);
Class c3 = new Test().getClass();
2) 然后分别调用Class对象中的方法来获取一个类的属性/方法/构造方法的结构
注意:如果要能够正常的获取类中方法/属性/构造方法应该重点掌握如下的反射类
Field
Constructor
Method
常用方法
//首先要获取一个类的Class对象
Class c1 = TestReflection.class;
或:Class<Bean> c1 = (Class<Bean>) Class.forName(className);
或:Class<Bean> c1 = (Class<Bean>) new Bean().getClass();
//生成一个实例
Bean b = (Bean)c1.newInstance();
//获取指定的包名
String package01 = c1.getPackage().getName();
//获取类的修饰符
int mod = c1.getModifiers();
//获取指定类的完全限定名
String className = c1.getName();
//获取指定类的父类
Class superClazz = c1.getSuperclass();
//获取实现的接口
Class[] interfaces = c1.getInterfaces();
//获取指定类的所有成员变量:(类或接口所声明的所有字段,public, private, protected ,但不包括从基类继承的字段)
Field[] fields = c1.getDeclaredFields();
for (Field field : fields) {
//获取每个字段的访问修饰符
modifier = Modifier.toString(field.getModifiers());
//获取字段的数据类型所对应的Class对象
Class type = field.getType();
//获取字段名
String name = field.getName();
//如果是数组类型则需要特别处理
if (type.isArray()) {
String arrType = type.getComponentType().getName() +"[]";
System.out.println("" + modifier + " " + arrType + " "+ name + ";");
} else {
System.out.println("" + modifier + " " + type + " " + name + ";");
}
}
//获取指定类的指定成员变量(前者为全部,后者仅为公有,但包含基类)
Field field = c1.getDeclaredField("mScroller");
Field field = c1.getField("mScroller");
实例(获取指定类的指定成员变量):
初始化ViewPager时,利用获得指定成员变量,来反射修改滑动速度。
public class ViewPagerScroller extends Scroller {
// 设置滑动速度
private int mScrollDuration = 2000;
public void setScrollDuration(int duration){
this.mScrollDuration = duration;
}
public ViewPagerScroller(Context context) {
super(context);
}
public ViewPagerScroller(Context context, Interpolator interpolator) {
super(context, interpolator);
}
public ViewPagerScroller(Context context, Interpolator interpolator, boolean flywheel) {
super(context, interpolator, flywheel);
}
@Override
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
super.startScroll(startX, startY, dx, dy, mScrollDuration);
}
@Override
public void startScroll(int startX, int startY, int dx, int dy) {
super.startScroll(startX, startY, dx, dy, mScrollDuration);
}
public void initViewPagerScroll(ViewPager viewPager) {
try {
//在这里使用了反射,获得ViewPager类的指定成员变量
Field mScroller = ViewPager.class.getDeclaredField("mScroller");
mScroller.setAccessible(true);
//将ViewPager的实例,即传入作为形参的viewPager对象,也就是主界面程序中的mViewPager对象,中的成员变量mScroller设置为this
mScroller.set(viewPager, this);
} catch(Exception e) {
e.printStackTrace();
}
}
}
//在主界面程序中使用ViewPager的时候,这样初始化:
mViewPager = (ShowViewPager) getActivity().findViewById(R.id.viewpager);
ViewPagerScroller viewPagerScroller = new ViewPagerScroller(getActivity());
viewPagerScroller.initViewPagerScroll(mViewPager);
获取指定类的所有方法的两种方式:
public Method[] getMethods():返回某个类的所有public方法,包括从基类继承的、从接口实现的所有public方法。
public Method[] getDeclaredMethods():返回某个类自身声明的所有方法(public, private, protected),包括从所实现接口的方法,但不包括继承的方法。
getMethod获取指定方法的用法举例:
- 执行某对象的方法
public Object invokeMethod(Object owner, String methodName, Object[] args) throws Exception {
//首先得到这个对象的Class
Class ownerClass = owner.getClass();
//配置参数的Class数组,作为寻找Method的条件
Class[] argsClass = new Class[args.length];
for (int i = 0, j = args.length; i < j; i++) {
argsClass[i] = args[i].getClass();
}
//得到要执行的Method
Method method = ownerClass.getMethod(methodName,argsClass);
//执行该Method.invoke方法的参数是执行这个方法的对象owner,和参数数组args。可以这么理解:owner对象中带有参数args的method方法。返回值是Object,也既是该方法的返回值。
return method.invoke(owner, args);
}
- 执行某个类的静态方法
public Object invokeStaticMethod(String className, String methodName,
Object[] args) throws Exception {
Class ownerClass = Class.forName(className);
Class[] argsClass = new Class[args.length];
for (int i = 0, j = args.length; i < j; i++) {
argsClass[i] = args[i].getClass();
}
Method method = ownerClass.getMethod(methodName,argsClass);
//invoke的一个参数是null,因为这是静态方法,不需要借助实例运行
return method.invoke(null, args);
}
解释一下getMethod(String name, Class<?>... parameterTypes)方法中的两个参数:
- 第一个参数是方法名,第二个参数是该方法的参数类型数组
- 因为存在同方法名不同参数这种情况,所以只有同时指定方法名和参数类型才能唯一确定一个方法。
如一个函数: int test(int a, String str);
对应的getMethod方法:
getMethod("test",int.class, String.class);
或:getMethod("test",new Class[]{ int.class, String.class } );
method.invoke(Object receiver, Object... args)
就是最后一步:执行改类的指定方法了。
需要注意的是其第二个参数,不同于getMethod的第二个参数。
- 前者是该方法参数的实际的值。
- 后者是该方法参数的实际的值的参数类型。
invoke方法的返回值即为该方法实际的返回值类型,包装成Object,可以向下强转。
已上面的例子为例引出一个疑问: