什么是反射
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
套用别人的一句话说,反射就是将java类中的各种成分映射成相应的java类。
反射的作用
判断一个对象的类
取出类的modifiers,数据成员,方法,构造器,和超类.运行时构造类的实例
取得和设定对象数据成员的值。
在运行时刻调用动态对象的方法。
反射在框架里应用的很多,很多工具类底层也是反射机制实现的。如javaBean,自动调用get和set方法也是使用了反射/
反射的简单使用
下面是我最近复习反射机制的笔记
反射的包是java.lang.reflect。
下面是接下来测试需要用到的类:
package com.Howard.test02;
import java.io.Serializable;
/**
* 用于java反射的测试类
* @author Howard
* 2017年2月17日
*/
public class PointTest implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private int x;
public int y;
public String str_1 = "alibaba";
public String str_2 = "basketball";
public String str_3 = "tomorrow";
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public PointTest(int x, int y) {
super();
this.x = x;
this.y = y;
}
public PointTest() {
super();
}
@Override
public String toString() {
return "PointTest [x=" + x + ", y=" + y + ", str_1=" + str_1 + ", str_2=" + str_2 + ", str_3=" + str_3 + "]";
}
}
反射获取包名和类名
(测试类的main方法调用我就省略了,以下同,代码有注释,我就不多写了)
/**
* 获取类名以及包名
*/
public static void getNameAndPackage(){
System.out.println(PointTest.class.getName());
//com.Howard.test02.PointTest
}
运行结果:
com.Howard.test02.PointTest
三种获取获取class字节码文件的方式
/**
* 三种获取class字节码的方式
* 相关比较
* @throws ClassNotFoundException
*/
public static void getClazz() throws ClassNotFoundException{
Class<?> clazz_1 = PointTest.class;
Class<?> clazz_2 = new PointTest().getClass();
//抛ClassNotFoundException异常
Class<?> clazz_3 = Class.forName("com.Howard.test02.PointTest");
System.out.println(clazz_1.getName());
System.out.println(clazz_2.getName());
System.out.println(clazz_3.getName());
//比较
System.out.println(clazz_1 == clazz_2);
System.out.println(clazz_2 == clazz_3);
String s = "abc";
//isPrimitive()是否为基本类型
System.out.println("string是否为基本类型:"+s.getClass().isPrimitive());
System.out.println(int.class == Integer.class);
System.out.println(int.class == Integer.TYPE);
System.out.println("int是否为基本类型:"+int.class.isPrimitive());
System.out.println("int[]是否为基本类型:"+int[].class.isPrimitive());
System.out.println("int[]是否为数组:"+int[].class.isArray());
}
运行结果:
获取父类以及实现的接口
/**
* 获取父类以及接口
* @throws Exception
*/
public static void getSuperClass() throws Exception{
Class<?> clazz = Class.forName("com.Howard.test02.PointTest");
//获取父类
Class<?> superclass = clazz.getSuperclass();
Class<?>[] interfaces = clazz.getInterfaces();
System.out.println("父类:"+superclass.getName());
System.out.println("实现的接口:");
for(int i = 0;i<interfaces.length;i++){
System.out.println(interfaces[i]+" ");
}
/*
父类:java.lang.Object
实现的接口:
interface java.io.Serializable
*/
}
运行结果:
获取构造方法以及利用构造方法实例化对象
/**
* 获取构造方法并通过构造方法new出实例对象
* @throws Exception
*/
public static void getConstructor() throws Exception{
//String str = new String("abc");
//传入参数类型
Constructor<String> constructor_1 = String.class.getConstructor(StringBuffer.class);
//通过构造器来实例化对象
//如果上面得到的Constructor没有明确泛型,下面就必须强制转换
//参数必须明确new出类型 直接用字符串是会报argument type mismatch异常
String str = constructor_1.newInstance(new StringBuffer("abc"));
System.out.println(str.charAt(2));
}
运行结果:
获取成员变量
/**
* 获取成员变量Field
* 注意private的获取方式
* @throws Exception
*/
public static void getField() throws Exception{
PointTest poit = new PointTest(5, 7);
Field fieldY = poit.getClass().getField("y");
//这时得到的fieldY只是一个变量 并不能说它的值为5
System.out.println(fieldY);
System.out.println(fieldY.getType());
//得到值必须指定哪个具体对象
System.out.println(fieldY.get(poit));
System.out.println(fieldY.getInt(poit));
//使用下面的方法获取private的变量x,会认为看不到这个变量 在获取具体信息时会报java.lang.NoSuchFieldException: x
// Field fieldX = poit.getClass().getField("x");
Field fieldX = poit.getClass().getDeclaredField("x");
//获取权限修饰符
int a = fieldX.getModifiers();
System.out.println(Modifier.toString(a));
//使用getDeclaredField获取x后,在获取值时仍会报xxx with modifiers "private"
//private 无访问权限 所以通过下面的方法先强制访问
fieldX.setAccessible(true);
System.out.println(fieldX.getType());
System.out.println(fieldX.get(poit));
}
运行结果:
获取类的方法method
/**
* 获取方法method并执行
* @throws Exception
*/
public static void getMethod() throws Exception{
//反射实现如下语句
// String str = "abc";
// str.charAt(2);
String str = "abc";
//第一个参数 方法名 第2~n个参数 参数类型
Method methodCharAt = String.class.getMethod("charAt", int.class);
System.out.println(methodCharAt.invoke(str, 2));
System.out.println(methodCharAt.invoke(str, new Object[]{2}));
}
运行结果:
获取main方法并执行
这里我另创建一个类,含有main方法。
package com.Howard.test02;
/**
* 用于反射测试主函数
* @author Howard
* 2017年2月17日
*/
public class ArgumentsTest {
public static void main(String[] args) {
System.out.println("test!!!!");
if(args.length>0){
System.out.println(args[0]);
}
}
}
下面的方法就是用于通过反射获取上面类的方法并进行相关操作
/**
* 获取com.Howard.test02.ArgumentsTest的main方法并传参数执行
* @throws Exception
*/
public static void TestMain() throws Exception{
ArgumentsTest.main(new String[]{});
System.out.println(ArgumentsTest.class.getName());
Method method = ArgumentsTest.class.getMethod("main", String[].class);
//下面的参数这样写会报错java.lang.NoSuchMethodException
//因为jdk会自动拆箱为多个参数,但是该方法只要一个数组参数
//method.invoke(null, new String[]{"123","234"});
//下面这样写会把参数包成一个数组
method.invoke(null, new Object[]{new String[]{"123","234"}});
method.invoke(null, (Object)new String[]{"123","234"});
}
运行结果:
数组的反射以及数组与Object的关系
/**
* 数组与Object的关系以及数组的反射
*/
public static void testArray(){
int[] a1 = new int[]{1,2,3,};
int[] a2 = new int[4];
int[][] a3 = new int[2][3];
String[] a4 = new String[]{"a","b","c","d"};
System.out.println(a1.getClass() == a2.getClass());
//下面三者无法比较
// System.out.println(a2.getClass() == a3.getClass());
// System.out.println(a3.getClass() == a4.getClass());
// System.out.println(a4.getClass() == a1.getClass());
//[I [表示数组 I表示int
System.out.println(a1.getClass().getName());
System.out.println(a3.getClass().getName());
System.out.println(a1.getClass().getSuperclass().getName());
System.out.println(a2.getClass().getSuperclass().getName());
//引伸
Object aObj1 = a1;
Object aObj2 = a4;
Object aObj3 = a3;
//下面这个编译就过不去
// Object[] aObj4 = a1;
Object[] aObj5 = a3;
Object[] aObj6 = a4;
//下面的结果是:
//[I@15db9742
//[Ljava.lang.String;@6d06d69c
System.out.println(a1);
System.out.println(a4);
//如果想看到数组里面的具体值。使用arrays工具类:
//结果:
//[[I@15db9742]
//[a, b, c, d]
//asList接收的是T...a
//根据上面Object测试结果int数组是被当作Object的,而string数组可以被当作Object数组的,所以会有下面的结果
System.out.println(Arrays.asList(a1));
System.out.println(Arrays.asList(a4));
}
运行结果:
反射的简单应用
1、将字符串中所有的b改为z
/**
* 练习
* 将PointTest中string类型的值中b改为z
* @throws Exception
*/
public static void practiceChangeString() throws Exception{
PointTest poit = new PointTest();
System.out.println("old:"+poit);
//获取所有成员变量
Field[] fields = poit.getClass().getDeclaredFields();
for(Field field:fields){
//字节码比较 用==
if(field.getType() == String.class){
String oldvalue =(String)field.get(poit);
String newvalue = oldvalue.replace("b", "z");
field.set(poit, newvalue);
}
}
System.out.println("new:"+poit);
}
运行结果:
2、反射判断是否为数组,如果是 输出数组各个值
/**
* 数组反射 获取类并判断是否为数组 如果是 输出各个数
* @param obj
*/
public static void practiceArray(Object obj){
Class clazz = obj.getClass();
System.out.println(clazz.getName());
if(clazz.isArray()){
System.out.println(clazz.isArray());
int len = Array.getLength(obj);
for(int i = 0;i<len;i++){
System.out.println(Array.get(obj, i));
}
}else{
System.out.println(obj);
}
}
这里有必要说下我main调用传的参数:
practiceArray("abc");
practiceArray(new String[]{"1","2","3"});
运行结果: