要学反射,先要了解Class这个类,Class是所有Java类的一个总称,Class的实例中存储的是一个类的字节码,获取Class的实例有三种方式:
System.class
new Date().getClass()
Class.forName(“java.lang.String”);
Java不允许使用Class cla = new Class()这种方式获得一个Class的新实例,因为Class的构造方法是私有的,看一段源代码:
这段源码里说的很明白,只有JVM才可以创建一个Class对象。
这段源码里说的很明白,只有JVM才可以创建一个Class对象。
那么这三种获取Class实例的方式有什么区别呢?
@Test
public void test1() throws ClassNotFoundException{
String str = "abc";
Class cla1 = str.getClass();
Class cla2 = String.class;
Class cla3 = Class.forName("java.lang.String");
System.out.println(cla1==cla2);
System.out.println(cla2==cla3);
}
输出结果为:
根据这结果我们可以推论出,用这三种方式获得的Class实例是一模一样的,但是在实际的开发中我们更多的是使用第三种方式来获得一个Class实例,比如spring框架,我们先在配置文件中写好类名,然后在程序运行的过程中动态加载,获得该类的实例,再执行方法(spring的工作原理基本就这样)。
System.out.println(String.class.isPrimitive());//false
System.out.println(int.class.isPrimitive());//true,判断是否为基本类型
System.out.println(int.class==Integer.class);//false,Integer是类,而int是基本类型
System.out.println(int[].class.isArray());//true
那么反射是什么?
这是别人总结的“反射就是把Java类中的各种成分映射成相应的Java类(比如 属性–>Field、方法–>Method、构造方法–>Contructor、包–>Package)”。
拿到这些相应的Java类之后该怎么用?这是反射学习的重点。
1.Constructor类
1.1 如果想调用一个类的默认无参构造方法,有以下两种方式:
方式一,直接实例化一个Class(这种方式只能调用无参构造方法):
@Test
public void test2(){
try {
Class c = Class.forName("lenve.test.Utils");
c.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
输出结果:
Utils.java
package lenve.test;
public class Utils {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int add(int a, int b) {
return a + b;
}
public Utils(String str) {
System.out.println(str);
}
public Utils(int a, int b) {
System.out.println(a + b);
}
public Utils() {
System.out.println("this is default constructor!");
}
}
方式二,先获得一个Constructor类,再根据这个类调用无参构造方法。
@Test
public void test3(){
try {
/**
Class.newInstance() 只能够调用无参的构造函数,即默认的构造函数;
Constructor.newInstance() 可以根据传入的参数,调用任意构造构造函数。
*/
Class c = Class.forName("lenve.test.Utils");
Constructor constructor = c.getConstructor(null);
Utils util = (Utils) constructor.newInstance();
System.out.println(util.add(3, 4));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
输出结果:
1.2 调用一个有参的构造方法
@Test
public void test4(){
try {
Class c = Class.forName("lenve.test.Utils");
//根据参数的类型来确定调用的是哪一个构造方法
Constructor constructor = c.getConstructor(String.class);
//传入该构造方法需要的参数
constructor.newInstance("today is a good day!");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
输出结果:
2.获得Field并查看相应的实例对象的值,细节都已经在注释中说明:
@Test
public void test5(){
try {
Point p1 = new Point(3, 7);
Field fY = p1.getClass().getField("y");
//fY是字节码中Field对象的一个实例,并不属于某个具体的实例,因此它的值不是7
//这样才是得到p1中y的值
System.out.println(fY.get(p1));
//因为x是私有的,所以不能通过下面的方式获得
// Field fX = p1.getClass().getField("x");
//正确的获得方式应该是这样的
Field fX = p1.getClass().getDeclaredField("x");
//上面的方式拿到x后并不能获得其值,还需要做如下处理
fX.setAccessible(true);
System.out.println(fX.get(p1));
} catch (NoSuchFieldException | SecurityException
| IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
3.获取一个类中的所有String类型的属性,如果该字段的值中有’a’,则全部替换为’b’:
@Test
public void test6(){
Point p = new Point(3, 4);
changeValue(p);
System.out.println(p);
}
private void changeValue(Object obj) {
try {
Field[] fields = obj.getClass().getFields();
for(Field field:fields){
//因为对一个Java类来说,它只有一个字节码,是单例的,所以用==比较就可以了
if(field.getType()==String.class){
String oldStr = (String) field.get(obj);
String newStr = oldStr.replace('a', 'b');
field.set(obj, newStr);
}
}
} catch (SecurityException | IllegalArgumentException
| IllegalAccessException e) {
e.printStackTrace();
}
}
Point.java
public class Point {
private int x;
public int y;
public String username = "zhangsan";
public String password = "lisi";
public Point(int x,int y) {
this.x = x;
this.y = y;
}
@Override
public String toString() {
return "Point [username=" + username +