------------ android培训、java培训、java博客、java学习型技术博客、期待与您交流! --------------
Java中的反射:
内存里面的每一份字节码就是一个Class类的的实例对象。
java虚拟机里面每一个类只保存一份字节码。
得到字节码(Class类)的方式有三种:
类名.class;
对象名.getClass();
Class.forName("类名"):如果要加载的类字节码已经被加载过,那么就直接返回那一份字节码;
如果要加载的类字节码没有被加载过,那么就用类加载器去加载,把加载进来的字节码缓存到虚拟机里面;
java里面有9个预定义的Class实例对象(8个基本数据类型和void)。
int.class = Integer.TYPE;
int.class.isPrimitive();-----true //判断class类是否为基本数据类型。
int[].class.isArray();-----true //判断Class类是否为数组类。
总之,只要是在源程序中出现的类型,都有各自的Class实例对象,如:int[], void 等。
字节码之间的比较应该用“==”来比较,当然,用equals()来比较也是可以的,只是从意义上来说,应该用“==”来比较比较合理。
因为一种类型的字节码在虚拟机里面是只有一份的,所以,用“==”来比较比较合理。如:field1.getType() == String.class;
Class类中有个方法isAssignableFrom(Class<?> cls)判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同,
或是否是其超类或超接口。如果是则返回 true;否则返回 false。
如果该 Class 表示一个基本类型,且指定的 Class 参数正是该 Class 对象,则该方法返回 true;否则返回 false。
如:Number.class.isAssignableFrom(Integer.class);返回true。
反射:把java类中的各种成分映射成相应的java类。
其实就是通过一个类的Class对象(字节码)把类中的各种成分映射成相应的java类的对象来使用。
Constructor类,Field类,Method类。
Class类中的方法newInstance()方法是在方法内部先得到默认的构造方法,然后用该构造方法来创建实例对象。其内部用到了缓存机制来保存默认构造方法的实例对象。
Constructor类的简单使用见如下例子:
package com.heima.exam;
import java.lang.reflect.Constructor;
public class RefConstructorTest {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
Constructor con = Class.forName("java.lang.String").getConstructor(StringBuffer.class);
String str = (String) con.newInstance(new StringBuffer("haha"));
System.out.println(str);
}
}
Field实例对象只代表类字节码里面的一个变量,并不代表某一对象里面的变量本身具体的值。我们需要调用Field类中的方法get(Object obj)
(注意:此方法返回类型是Object类型,很多时候我们需要做类型强制转换。)或其他对应的方法才能获得具体实例对象所对应的此字段的值。
在对象里面声明为非public的的成员变量需要用getDeclaredField(String name)才能得到Class对象中的此变量名所对应的Field实例对象。
然后如果需要访问此Field实例对象所对应的具体的值,那么需要调用Field父类的方法setAccessible(true)来指示反射的对象在使用时应该取消Java语言访问检查,
然后才能访问具体对象所对应的此字段的值。
Field对象的使用见如下例子:
package com.heima.exam;
import java.lang.reflect.Field;
public class RefChangeStringValue {
public static void changeStringValue(Object obj) {
Field[] fields = obj.getClass().getDeclaredFields();
for(Field field : fields) {
if(field.getType() == String.class) {
try {
field.setAccessible(true);
field.set(obj, ((String)field.get(obj)).replace('b', 'a'));
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
TestClass tv = new TestClass();
changeStringValue(tv);
System.out.println(tv);
}
}
class TestClass {
public String str1 = "ball";
protected String str2 = "basketball";
String str3 = "hello";
private String str4 = "bell";
int i = 10;
public String toString() {
return str1 + ":" + str2 + ":" + str3 + ":" + str4 + ":" + i;
}
}
静态方法的反射调用时,invoke()方法中的对象参数可以是null,如:methodTest.invoke(null, "hello"))。
java1.5以后的虚拟机为了兼容1.4以前的版本,对方法的反射调用时所传输的数组参数进行解包操作,如:method1.invoke(null, new String[] {"aa", "bb"});
java虚拟机会自动的解开数组里面的元素来作为调用方法的参数,此时并不是我们所想象的那样了,而实际是method1.invoke(null, "aa", "bb");这样了。
我们只能用如下方式进行调用:Test.method1((Object)new String[] {"aa", "bb"});或如下所说的另一种方案。
对接收数组参数的成员方法进行反射的2种解决方案:new Object(arrays[]),(object)arrays[]。
Method类的简单使用见如下例子:
package com.heima.exam;
import java.lang.reflect.Method;
public class RefMethodTest {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
String str = "hello";
Method methodCharAt = String.class.getMethod("charAt", int.class);
System.out.println(methodCharAt.invoke(str, 0));
Method methodMin = Math.class.getMethod("min", int.class, int.class);
System.out.println(methodMin.invoke(null, 3, 20));
ClassTest1.main(new String[] {"aa", "bb"});
Method methodMain = Class.forName("com.heima.exam.ClassTest1").getMethod("main", String[].class);
methodMain.invoke(null, (Object)new String[] {"hello", "world"});
methodMain.invoke(null, new Object[] {new String[] {"hello", "world"}});
}
}
class ClassTest1 {
public static void main(String[] args) {
for(String arg : args) {
System.out.println(arg);
}
}
}
数组类型:具有相同类型和相同维数的数组共享同一份字节码,即同一个Class类。
Arrays是一个对数组操作的工具类,里面有很多的方法能对数组进行各种操作,如:Arrays.asList(T... a),能将一个数组转换成List返回。
Arrays中的asList(T... a)方法为了兼容JDK1.4版本,如果传进去的参数是一个Object数组的话,那么就把数组里面的元素装进一个集合里面,并返回这个List集合,
但是如果传进去的参数是一个基本类型的数组的话,那么就把这个基本类型的数组作为一个元素装进集合里面并返回。
如:Arrays.asList(new String[] {"aa", "ss", "dd"});那么转换后的List集合里面有3个元素,分别是"aa", "ss", "dd";
但是:Arrays.asList(new int[] {1, 5, 9});这样的话,转换后的List集合里面就只有一个元素,这个元素就是new int[] {1, 5, 9}这个数组本身。
Array工具类用于完成对数组的反射操作,Array类里面的所有方法都是静态方法。如:Array.getLength(obj); Array.get(obj, index);
数组反射的简单应用程序片段如下:
public void printObject(Object obj) {
if(obj.getClass().isArray()) {
for(int i = 0; i < Array.getLength(obj); i++) {
System.out.println(Array.get(obj, i));
}
} else {
System.out.println(obj);
}
}