知识点

深入理解反射

2019-01-15  本文已影响30人  Cherry300

众所周知,如果没有了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);

}

}

}

上一篇下一篇

猜你喜欢

热点阅读