Java反射学习笔记

#Java反射学习笔记

Class类

万事万物皆对象,但是在Java中有两样不是对象

  1. 静态的成员:静态成员不是属于对象,是属于类的。
  2. 普通的数据类型:普通的数据类型有封装类可以将普通数据类型封装为对象。

类是谁的对象?
类是对象,是java.lang.Class类的实例对象。

任何一个类都是Class的实例对象,Class类的实例对象有三种表示方法:

  1. 通过 类.class,可以获得该类的类类型,等价于任何一个类都有一个隐含的静态成员变量class
    Class t1 = Test.class;
  2. 已知该类的对象,通过 类对象.getClass() 可以获得该类的类类型。
    Class t2 = test1.getClass();
  3. 通过 Class.forname(“类的全称”) 可以获得该类的类类型
    Class t3 = Class.forName(“com.reflect.test”)

可以通过类的类类型创建该类的对象实例
Test test = (Test)t1.newInstance();

动态加载类

编译时刻加载类是静态加载类,运行时刻加载类是动态加载类

  • new 创建对象是静态加载类,在编译时刻就需要加载所有的可能使用到的类
  • Class.forName(“类的全称”)不仅表示了类类型,还代表了动态加载类,在运行时刻加载

功能性的类一般使用动态加载,并统一功能性接口(标准),并让功能性的类统一implements该接口,这样就不用重新编译,就可以动态的加新的功能。 这种方法一般可应用于软件在线升级,不需要删除软件重新编译。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//动态加载类,在运行时刻加载
Class c = Class.forName(arg[0]);
//通过类类型,创建该类对象
OfficeAble oa = (OfficeAble)c.newInstance();
oa.start();

//编写一个标准接口
interface OfficeAble
{
public void start();
}

//让word和EXCEL类实现oa接口
class Excel implements OfficeAble
{
····
}

获取方法信息

基本的数据类型,void关键字等都存在类类型
Class c = 基类.class (int,String,double,void等)

Class类的基本API操作的

  1. class.getName();可以获取类的名称
  2. class.getSimpleName();不包含包名的类的名称
  3. class.getMethods();获取类的public方法集合,包括继承来的
  4. class.getDeclaredMethods();获取的是所有该类自己声明的方法,不问访问权限

Method类提供了一些操作方法的方法

  1. Method.getName()得到方法的名称
  2. Method.getReturnType()得到该方法的返回值类型的类类型(class),如int.class String.class
  3. Method.getParameterTypes()获得参数列表类型的类类型数组class[],如参数为(int,int)则得到(int.class ,int class)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    public class ClassUtil {
    /**
    * 打印类的信息,包括类的成员函数、成员变量(只获取成员函数)
    * @param obj 该对象所属类的信息
    */
    public static void printClassMethodMessage(Object obj){
    //要获取类的信息 首先要获取类的类类型
    Class c = obj.getClass();//传递的是哪个子类的对象c就是该子类的类类型
    //获取类的名称
    System.out.println("类的名称是:"+c.getName());
    /*
    * Method类,方法对象
    * 一个成员方法就是一个Method对象
    * getMethods()方法获取的是所有的public的函数,包括父类继承而来的
    * getDeclaredMethods()获取的是所有该类自己声明的方法,不问访问权限(继承的就没有了)
    */
    Method[] ms = c.getMethods();//c.getDeclaredMethods()
    for(int i = 0; i < ms.length;i++){
    //得到方法的返回值类型的类类型
    Class returnType = ms[i].getReturnType();
    System.out.print(returnType.getName()+" ");
    //得到方法的名称
    System.out.print(ms[i].getName()+"(");
    //获取参数类型--->得到的是参数列表的类型的类类型
    Class[] paramTypes = ms[i].getParameterTypes();
    for (Class class1 : paramTypes) {
    System.out.print(class1.getName()+",");
    }
    System.out.println(")");
    }
    }

获取成员变量构造函数信息

Field类封装了关于成员变量的操作

  1. getFields()方法获取的是所有的public的成员变量的信息
  2. getDeclaredFields()方法获取的是该类自己声明的成员变量的信息
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    /*
    * 成员变量也是对象
    * java.lang.reflect.Field
    * Field类封装了关于成员变量的操作
    * getFields()方法获取的是所有的public的成员变量的信息
    * getDeclaredFields()方法获取的是该类自己声明的成员变量的信息
    */
    Class c = obj.getClass();
    Field[] fs = c.getFields();
    Field[] fs = c.getDeclaredFields();
    for (Field field : fs){
    //得到成员变量的类型的类类型
    Class fieldType = field.getType();
    String typeName = fieldType.getName();
    //得到成员变量的名字
    String fieldName = field.getName();
    System.out.println(typeName + " " + fieldName);
    }

Constructor类封装了构造函数的信息

  1. getConstructors()获取所有的public的构造函数
  2. getDeclaredConstructors()获取所有自己声明的构造函数(构造函数都是自己声明的,所以即得到了所有构造函数)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    /*
    * 构造函数也是对象
    * java.lang.Constructor
    * Constructor类封装了构造函数的信息
    * getConstructors()获取所有的public的构造函数
    * getDeclaredConstructors()获取所有的构造函数
    */
    Class c = obj.getClass();
    //Constructor[] cs = c.getConstructors();
    Constructor[] cs = c.getDeclaredConstructors();
    for(Constructor constructor : cs){
    //获取构造函数的参数列表--->得到的是参数列表的类类型
    System.out.println(constructor.getName() + "(";
    Class[] paramTypes = constructor.getParameterTypes();
    for(Class class1 : paramTypes){
    System.out.print(class1.getName()+ "," );
    }
    System.out.print(")");
    }

Class类还有很多获取类的信息的方法,总之,想要获取类的信息,就一定要先获取类的类类型

方法反射的基本操作

获取方法:方法的名称和方法的参数列表才能唯一决定某个方法。
方法反射的操作:method.invoke(对象,参数列表);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//1.获取一个方法就是获取类的信息,获取类的信息首先要获取类的类类型
Test test1 = new Test();
Class c = test1.getClass();
/*
*2.获取方法 通过名称和参数列表来决定
* getMethod 获取的是public的方法
* getDelcaredMethod 获取的是自己声明的方法
*/
//Method m = c.getMethod("print", new Class[]{int.class, int.class});
Method m = c.getMethod("print", int.class, int.class);

//方法的反射操作:用m对象来进行方法调用 和 test1.print调用效果完全相同
//方法如果没有返回值返回null,有返回值返回具体的返回值
m.invoke(test1, new Object[]{10, 20});
m.invoke(test1, 10, 20)

通过反射了解集合泛型的本质

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
ArrayList list1 = new ArrayList();
ArrayList<String> list2 = new ArrayList<String>()

Class c1 = list1.getClass();
Class c2 = list2.getClass();
System.out.println(c1 == c2);//True
//反射的操作都是编译之后的操作

/*
* c1==c2结果返回true说明编译之后集合的泛型失去泛型化的
* Java中集合的泛型,是防止错误输入的,只在编译阶段有效,
* 绕过编译就无效了
* 验证:可以通过方法的反射来操作,绕过编译
*/
Method m = c2.getMethod("add", Object.class);
m.invoke(list2, 20);//绕过编译操作就绕过了泛型
System.out.println(list2.size());
System.out.println(list2);//20
//注意不能使用for-each来遍历,会有类型转换错误