JAVA反射是一个非常重要的概念,很多框架都是通过反射来完成的.!
在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法?答案是肯定的。这种动态获取类的信息,以及动态调用对象的方法的功能来自于Java语言的反射(Reflection)机制。Java反射机制主要提供了以下功能:
1,在运行时判断任意一个对象所属的类;
2,在运行时构造任意一个类的对象;
3,在运行时判断任意一个类所具有的成员变量和方法;
4,在运行时调用任意一个对象的方法;
5,生成动态代理。
在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中。
1,Class类:代表一个类。
2,Field类:代表类的成员变量(成员变量也称为类的属性)。
3,Method类:代表类的方法。
4,Constructor类:代表类的构造方法。
5,Array类:提供了动态创建数组,以及访问数组元素的静态方法。
当真正需要使用一个类时系统才会装入它 java.lang.Class是Java应用程序在运行时装入类和接口的实例 Class只能由系统建立对象我们可以通过Object的getClass()方法來取得某个对象对应Class对象
我们知道每一个类被加载之后,系统就会为该类生成一个对应的Class对象,通过该Class对象我们就可以访问到JVM中的这个类了,
java程序获取Class对象有三种方式:
1>使用Class类的forName()静态方法,该方法传入字符串参数,该字符串是某个类的全限定类名!
2>调用该类的class属性来获取该类对应的Class对象,例如Dog.class
3>调用某个对象的getClass方法,,该方法是Object类的一个方法,所以Java对象都可以调用这个方法.
我们可以通过API来查看Class提供了那些方法来来获取类的信息:
- 通过反射来获取类的基本信息:
下面我们就通过一个简单的程序来详细说Class对象来获取对应的详细信息
:
//使用2个注释修饰该类
@SuppressWarnings(value="unchecked")
@Deprecated
public class ClassTest
{
//为该类定义一个私有的构造器
private ClassTest()
{
}
//定义一个有参数的构造器
public ClassTest(String name)
{
System.out.println("执行有参数的构造器");
}
//定义一个无参数的info方法
public void info()
{
System.out.println("执行无参数的info方法");
}
//定义一个有参数的info方法
public void info(String str)
{
System.out.println("执行有参数的info方法"
+ ",其实str参数值:" + str);
}
//定义一个测试用的内部类
class Inner
{
}
public static void main(String[] args)
throws Exception
{
//下面代码可以获取ClassTest对应的Class
Class<ClassTest> clazz = ClassTest.class;
//获取该Class对象所对应类的全部构造器
Constructor[] ctors = clazz.getDeclaredConstructors();
System.out.println("ClassTest的全部构造器如下:");
for (Constructor c : ctors)
{
System.out.println(c);
}
//获取该Class对象所对应类的全部public构造器
Constructor[] publicCtors = clazz.getConstructors();
System.out.println("ClassTest的全部public构造器如下:");
for (Constructor c : publicCtors)
{
System.out.println(c);
}
//获取该Class对象所对应类的全部public方法
Method[] mtds = clazz.getMethods();
System.out.println("ClassTest的全部public方法如下:");
for (Method md : mtds)
{
System.out.println(md);
}
//获取该Class对象所对应类的指定方法
System.out.println("ClassTest里带一个字符串参数的info方法为:"
+ clazz.getMethod("info" , String.class));
//获取该Class对象所对应类的上的全部注释
Annotation[] anns = clazz.getAnnotations();
System.out.println("ClassTest的全部Annotattion如下:");
for (Annotation an : anns)
{
System.out.println(an);
}
System.out.println("该Class元素上的@SuppressWarnings注释为:"
+ clazz.getAnnotation(SuppressWarnings.class));
//获取该Class对象所对应类的全部内部类
Class<?>[] inners = clazz.getDeclaredClasses();
System.out.println("ClassTest的全部内部类如下:");
for (Class c : inners)
{
System.out.println(c);
}
//使用Class.forName方法加载ClassTest的Inner内部类
Class inClazz = Class.forName("com.zx.ClassTest$Inner");
//通过getDeclaringClass()访问该类所在的外部类
System.out.println("inClazz对应类的外部类为:" +
inClazz.getDeclaringClass());
System.out.println("ClassTest的包为:" + clazz.getPackage());
System.out.println("ClassTest的父类为:" + clazz.getSuperclass());
}
}
2 使用反射生成并操作对象:
通过反射来生成对象有两种方式:
1>
使用Class对象newInstance()方法来创建,该中方式需要该类有默认的构造器
实际上newInstance()方法它是同过默认的构造器创建实例的
2>先使用Class对象获取指定的Constructor对象,在调用Contructor对象的newInstance()方法来创建实例的
下面程序就实现一个简单的
对象池
该对象池通过配置文件 读取name和value对,然后创建这些对象,这有点像spring容器,然后把这些创建的对象放进HashMap中.
配置文件如下:
public class ObjectPoolFactory
{
//定义一个对象池,前面是对象名,后面是实际对象
private Map<String ,Object> objectPool =
new HashMap<String , Object>();
//定义一个创建对象的方法,
//该方法只要传入一个字符串类名,程序可以根据该类名生成Java对象
private Object createObject(String clazzName)
throws InstantiationException , IllegalAccessException
,ClassNotFoundException
{
//根据字符串来获取对应的Class对象
Class<?> clazz =Class.forName(clazzName);
//使用clazz对应类的默认构造器创建实例
return clazz.newInstance();
}
//该方法根据指定文件来初始化对象池,
//它会根据配置文件来创建对象
public void initPool(String fileName)
throws InstantiationException , IllegalAccessException
,ClassNotFoundException
{
FileInputStream fis = null;
try
{
fis = new FileInputStream(fileName);
Properties props = new Properties();
props.load(fis);
for (String name : props.stringPropertyNames())
{
//每取出一对属性名-属性值对,就根据属性值创建一个对象
//调用createObject创建对象,并将对象添加到对象池中
objectPool.put(name ,
createObject(props.getProperty(name)));
}
}
catch (IOException ex)
{
System.out.println("读取" + fileName + "异常");
}
finally
{
try
{
if (fis != null)
{
fis.close();
}
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
}
public Object getObject(String name)
{
//从objectPool中取出指定name对应的对象。
return objectPool.get(name);
}
public static void main(String[] args)
throws Exception
{
ObjectPoolFactory pf = new ObjectPoolFactory();
pf.initPool("obj.txt");
System.out.println(pf.getObject("a"));
}
}
3 通过反射调用方法
通过invoke(Object obj,Object...args)
public class TestRef {
public static void main(String args[]) throws NoSuchMethodException,
IllegalAccessException, InvocationTargetException {
Foo foo = new Foo("这个一个Foo对象!");
Class clazz = foo.getClass();
Method m1 = clazz.getDeclaredMethod("outInfo");
Method m2 = clazz.getDeclaredMethod("setMsg", String.class);
Method m3 = clazz.getDeclaredMethod("getMsg");
m1.invoke(foo);
m2.invoke(foo, "重新设置msg信息!");
String msg = (String) m3.invoke(foo);
System.out.println(msg);
}
}
4, 通过反射访问属性:
通过Class对象的getFields()和getField()方法可以获取该类所包括的全部属性
如:
class Person
{
private String name;
private int age;
public String toString()
{
return "Person [ name:" + name +
" , age:" + age + " ]";
}
}
public class FieldTest
{
public static void main(String[] args)
throws Exception
{
//创建一个Person对象
Person p = new Person();
//获取Person类对应的Class对象
Class<Person> personClazz = Person.class;
//获取Person类名为name的属性
//使用getDeclaredField,表明可获取各种访问控制符的field
Field nameField = personClazz.getDeclaredField("name");
//设置通过反射访问该Field时取消访问权限检查
nameField.setAccessible(true);
//调用set方法为p对象的指定Field设置值
nameField.set(p , "Yeeku.H.Lee");
//获取Person类名为age的属性
Field ageField = personClazz.getDeclaredField("age");
//设置通过反射访问该Field时取消访问权限检查
ageField.setAccessible(true);
//调用setInt方法为p对象的指定Field设置值
ageField.setInt(p , 30);
System.out.println(p);
}
}
5 通过反射操作数组:
下面程序测试了,如果使用Array来生成数组,为指定数组元素复制,并获取指定数组元素的方式:
public class ArrayTest1
{
public static void main(String args[])
{
try
{
//创建一个元素类型为String ,长度为10的数组
Object arr = Array.newInstance(String.class, 10);
//依次为arr数组中index为5、6的元素赋值
Array.set(arr, 5, "Struts2权威指南");
Array.set(arr, 6, "ROR敏捷开发最佳实践");
//依次取出arr数组中index为5、6的元素的值
Object book1 = Array.get(arr , 5);
Object book2 = Array.get(arr , 6);
//输出arr数组中index为5、6的元素
System.out.println(book1);
System.out.println(book2);
}
catch (Throwable e)
{
System.err.println(e);
}
}
}
在上面的程序基础上,我们想生成一个三维数组怎么办呢?
public class ArrayTest2
{
public static void main(String args[])
{
/*创建一个三维数组。
根据前面介绍数组时讲的:三维数组也是一维数组,是数组元素是
二维数组的一维数组,因此可以认为arr是长度为3的一维数组
*/
Object arr = Array.newInstance(String.class, 3, 4, 10);
//获取arr数组中index为2的元素,应该是二维数组
Object arrObj = Array.get(arr, 2);
//使用Array为二维数组的数组元素赋值。
//二维数组的数组元素是一维数组,所以传入Array set方法的第三个参数是
//第三个参数是一维数组。
Array.set(arrObj , 2 , new String[]
{
"Struts2权威指南",
"轻量级J2EE企业应用实战"
});
//获取arrObj数组中index为3的元素,应该是一维数组。
Object anArr = Array.get(arrObj, 3);
Array.set(anArr , 8 , "ROR敏捷开发最佳实践");
//将arr强制类型转换为三维数组
String[][][] cast = (String[][][])arr;
//获取cast三维数组中指定元素的值
System.out.println(cast[2][3][8]);
System.out.println(cast[2][2][0]);
System.out.println(cast[2][2][1]);
}
}
6, 通过反射来获取泛型的信息:
下面是一个完整的实例程序:详细介绍了怎么获取泛型的信息:
public class GenericTest
{
private Map<String , Integer> score;
public static void main(String[] args)
throws Exception
{
Class<GenericTest> clazz = GenericTest.class;
Field f = clazz.getDeclaredField("score");
//直接使用getType()取出Field类型只对普通类型的Field有效
Class<?> a = f.getType();
System.out.println("score的类型是:" + a);
//获得Field实例f的泛型类型
Type gType = f.getGenericType();
//如果gType类型是ParameterizedType对象
if(gType instanceof ParameterizedType)
{
//强制类型转换
ParameterizedType pType = (ParameterizedType)gType;
//获取原来类型
Type rType = pType.getRawType();
System.out.println("原始类型是:" + rType);
//取得泛型类型的泛型参数
Type[] tArgs = pType.getActualTypeArguments();
System.out.println("泛型类型是:");
for (int i = 0; i < tArgs.length; i++)
{
System.out.println("第" + i + "个泛型类型是:" + tArgs[i]);
}
}
else
{
System.out.println("获取泛型类型出错!");
}
}
}
总结:
我们通过代码的方式讲解了Class Method Field Contructor Type ParameterizedType等类和接口的用法.
---------- 期待与您的交流 Android培训 Java培训 期待与您的交流-----