深入理解反射
众所周知,如果没有了java的反射,那么java中的框架将不复存在,所以当我们学好了反射以后对我们日后学习框架是有很多的帮助的,下面,我把我学反射的一些总结分享给大家。
刚接触反射的时候感觉它真是太帅了,尤其是暴力反射我感觉真的很牛x(下面我会介绍的),其实反射说白了点讲,就是通过java中给我们提供的一些类和方法,将我们类的字段、方法以及各种成分映射成相应的它们给我们提供的类和方法。
Class类为反射的基石,下面我将介绍三种方式获得Class对象的方式:
//以String类为例,获得其字节码对象,首先我们定义一个String对象
String str = "getClass";
//第一种方式
Class stringClassOne = str.getClass();
//第二种方式
Class stringClassTwo = String.class;
//第三种方式,forName()方法的参数必须是包名和类名
Class stringClassThree = Class.forName("java.lang.String");
//但是,当我们通过上面的方式获得了字节码对象以后,我们可以怎么用这个对象呢?
String str = "reflect";
//这个方法判断当前的类型是否为原始类型
System.out.println(str.getClass().isPrimitive());
//我们可以看出Integer和int的字节码文件并不是同一份,但是Integer.Type得到的是原始类型的字节码文件
System.out.println(Integer.class == int.class);
System.out.println(Integer.TYPE == int.class);
//通过Class对象看是否为一个数组类型
System.out.println(new int[2].getClass().isArray());
//通过Class对象得到当前对象的类型
System.out.println("The class of " + str +" is " + str.getClass().getName());
//但是上面的getName()这个方法有几点我们应该注意
//首先,如果此类对象表示一个基本类型或 void,则返回的名字是一个与该基本类型或 void所对应的 Java语言关键字相同的 String。
System.out.println(int.class.getName());
System.out.println(void.class.getName());
/*
* 如果此类对象表示一个数组类,则名字的内部形式为:表示该数组嵌套深度的一个或多个 '[' 字符加元素类型名。元素类型名的编码如下:
* 元素类型名 编码
boolean Z
byte B
char C
class or interface Lclassname;
double D
float F
int I
long J
short S
*/
System.out.println(new Object[3].getClass().getName());
System.out.println(new int[3][4][5][6][7][8][9].getClass().getName());
//下面,我们可以通过Class的实例对象创建一个对应的类的实例对象:
@SuppressWarnings("unchecked")
//得到String类型的Class对象
Class<String> stringClass = (Class<String>)Class.forName("java.lang.String");
//通过这个Class对象得到一个构造方法,但是构造方法参数类型并不相同,所以我们给这个方法一个参数
Constructor<String> con = stringClass.getConstructor(char[].class);
//用这个构造方法创建一个String类型的实例,参数要与上面给的参数类型相对应
String newValue = con.newInstance(new char[]{'j','a','v','a'});
System.out.println(newValue);
//我们也可以通过Class的实例对象创建一个String类型的实例
//这种方法是用缓存一个无参数的构造方法实现的,所以,由此看出,反射是比较消耗性能的
String str = stringClass.newInstance();
System.out.println(str.length());
下面我们得到对象身上的字段:
public class ClassTest {
public static void main(String[] args) throws Exception {
Point point = new Point(13,14,"javareflect","man");
@SuppressWarnings("unchecked")
Class<Point> pointClass = (Class<Point>) Class.forName("com.java.day01.ClassTest$Point");
//因为y字段是public的,所以我们可以通过这种方式访问
Field yField = pointClass.getField("y");
int y = (Integer) yField.get(point);
System.out.println(y);
//通过下面的这种方式访问私有的字段
Field xField = pointClass.getDeclaredField("x");
xField.setAccessible(true);
int x = (Integer) xField.get(point);
System.out.println(x);
//我们也可以将其私有的字段更改,通过上面这种反射的方式,是不是很帅气!!!
System.out.println(point);
Field[] fields = pointClass.getDeclaredFields();
for(Field field : fields) {
if(field.getType() == String.class) {
field.setAccessible(true);
field.set(point, field.get(point).toString().replace('a', '$'));
}
}
System.out.println(point);
}
static class Point {
private int x;
public int y;
private String name;
private String sex;
public Point(int x, int y, String name, String sex) {
super();
this.x = x;
this.y = y;
this.name = name;
this.sex = sex;
}
@Override
public String toString() {
return "Point [name=" + name + ", sex=" + sex + "]";
}
}
}
下面我们得到对象身上的方法:
String str = "javareflect";
Class<String> strClass = (Class<String>) str.getClass();
//通过Class实例得到String对象的方法
Method method = strClass.getMethod("charAt", int.class);
//方法与对象是没有关系的,它是属于类的,是方法作用在某个对象身上
char printVal = (Character)method.invoke(str, 2);
System.out.println(printVal);
深入理解关于方法的反射
public class ClassTest {
public static void main(String[] args) throws Exception {
//我们通过run配置,给主方法的数组赋值,从而得到其他类的类名,然后运行这个类的主函数
String className = args[0];
Method mainMethod = Class.forName(className).getMethod("main", String[].class);
/*
* 由于jdk1.5之前并没有可变参数这一特性,以前invoke的第二个参数是一个Object数组类型,而高的jdk版本
* 必须向下兼容,所以当我们new一个引用类型的数组时,java会把他认为是一个Object类型的数组,从而导致程序
* 出错,所以我们自己new一个Object数组将正确的参数类型给放进去,这样程序就不会出错了,但是基本类型并没有继承
* Object类型,所以基本类型的数组并不存在这样的问题。
*/
mainMethod.invoke(null, new Object[] {new String[] {"hxl","znb"}});
Method m1 = Class.forName(className).getMethod("say",char[].class);
m1.invoke(null, new char[] {12});
}
}
class TestMain {
public static void main(String[] args) {
for(String arg : args) {
System.out.println(arg);
}
}
public static void say(char[] i) {
System.out.println("i am primitive type test");
}
}
关于数组的反射:
public class ClassTest {
public static void main(String[] args) throws Exception {
//利用Array类实现数组的反射
Object obj = new int[] {12,14};
//将obj这个不知道的类型打印输出
if(obj.getClass().isArray()) {
int length = Array.getLength(obj);
for(int i = 0;i < length;i++) {
Object arrVal = Array.get(obj, i);
System.out.println(arrVal);
}
} else {
System.out.println(obj);
}
}
}