Java反射

Java反射(Reflection)


A. Class类

1.概念

class

  • class(包括interface)本质是数据类型(Type)
  • 无继承关系的数据类型无法赋值
String s = new Double(899.98); //无法赋值,Java是强类型语言,编译器报错

Class

  • class/interface的数据类型Class
  • 每加载一个class,JVM都为其创建一个Class类型的实例,并关联起来
  • JVM持有的每个Class实例都指向一个数据类型(class/interface)
  • 一个Class实例包含了该class的完整信息

小结

  • JVM为每个加载的class创建对应的Class实例,并在实例中保存该class的所有信息
  • 如果获取了某个Class实例,则可以获取到该实例的对应的class的所有信息
  • 通过Class实例获取class信息的方法称为反射(Reflection)

2.如何获取一个class的Class实例

  1. Type.class

  2. getClass()

  3. Class.forName

3.Class实例在JVM中是唯一的

  • 可以用==比较两个Class实例

4.反射的目的

  • 当获得某个Object实例时,就可以获取该Object的class实例信息

5.从Class实例获取class信息

  • getName() // 获取完整类名
  • getSimpleName() // 获取类名
  • getPackage() //获取包名

6.从Class实例判断class类型

  • isInterface()
  • isEnum()
  • isArarry()
  • isPrimitive() //判断是不是基本类型,虽然基本类型变量本身不是Class,但JVM内部会创建包装类型

7.创建class实例

  • newInstance() //调用无参数的构造方法创建一个新的实例,有参数的无法调用

8.利用JVM动态加载class的特性,可以在运行期根据条件加载不同的实现类(或叫控制加载class)

// Commons Logging优先使用Log4j

LogFactory factroy;

if (isClassPresent("org.apache.logging.log4j.Logger")) {
    factory = createLog4j();
} else {
   factroy = createJdkLog(); 
}

boolean isClassPresent(String name) {
    try{
        Class.forName(name);
        return true;
    }catch (Exception e){
        return false;
    }
}

B.Field 访问字段

1.通过Class实例获取field信息

  • getField(name) // 获取某个public的field(包括父类)
  • getDeclaredField(name) // 获取当前类的某个field(不包括父类)
  • getFields() // 获取所以public的field (包括父类)
  • getDeclaredFields() // 获取当前类的所有field(不包括父类)

2.通过Field实例获取field信息

  • getName() //获得field名称
  • getType() //获得field字段定义的类型
  • getModifiers() //获取field字段的修饰符

3.Field实例获取或设置某个对象的字段

  • get(Object instance) // 获取一个实例的该字段的值
  • set(Object instance , Object fieldValue) // 设置某个实例的字段值
Integer n = new Integer(123);
Class cls = n.getClass();
Field f = cls.getDeclaredField("value");
f.get(n); // 123, 相当于n.value
f.set(n, 456); //相当于n.value = 456

4.设置setAccessible(true)来访问非public字段

  • setAccessible(true)可能会失败:

    • 定义了SecurityManager

    • SecurityManager 的规则阻止对Field设置accessible:

      例如,规则应用于所有java和javax开头的package的类

C. Method 调用方法

1.通过Class实例获取method信息

  • getMethod(name,Class…) //获取某个public的method(包括父类)
  • getDeclaredMethod(name, Class…) //获取当前类的某个method(不包括父类)
  • getMethods() // 获取所有public的method(包括父类)
  • getDeclaredMethods() // 获取当前类的所有method(不包括父类)
  • 注意:,Class…表示可变参数。Java定义中可变参数就是数组参数,但调用起来时不用写成数组形式

2.通过Method获取method信息

  • getNmae() //获取方法名称
  • getReturnType() // 获取方法返回 类型
  • getParameterTypes() //获取方法参数类型
  • getModifiers()// 获取方法修饰符

2.1调用无参数的Method

  • Object invoke(Object obj)
Integer n = new Integer(123);
Class cls = n.getClass();
Method m = cls.getMethod("toString");
String s = (String) m.invoke(n); 
// "123" ,相当于 String s = n.toString();

2.2调用带参数的Method

Object invoke(Object obj, Object… args)

Integer n = new Integer(123);
Class cls = n.getClass();
Method m = cls.getMethod("compareTo",Integer.class);
Integer s = (Integer) m.invoke(n,456); 
// "-1" ,相当于 int r = n.compareTo(456);

3.设置setAccessible(true)来访问非public方法

  • setAccessible(true)可能会失败:

    • 定义了SecurityManager

    • SecurityManager 的规则阻止对Method设置accessible:

      例如,规则应用于所有java和javax开头的package的类

4.从父类获取的Method,作用于子类实例时

  • 实际调用方法是子类覆写的方法
  • 保证了多态的正确性
class Person{
    public void hello(){
        System.out.println("Person:hello");
    }
}

class Student extend Person{
    public void hello(){
        System.out.println("Student:hello");
    }
}

Method m = Person.class.getMethod("hello");
m.invoke(new Student());
// 相当于如下
// Person p = new Student();
// p.hello();

D. Constructor 调用构造函数

1.Constructor对象包含一个构造方法的所有信息,可以创建一个实例(通过Class实例可以获取Constructor信息)

注意,Class.newInstance()只能调用public无参数构造方法;Constructor总是当前类定义的构造方法,Constructor和继承是没有关系的,所以也不可能获得父类的构造方法

  • getConstructor(Class… ) //获取某个public的Constructor
  • getDeclaredConstructor(Class… ) //获取某个Constructor
  • getConstructors(Class… ) //获取所有public的Constructor
  • getDeclaredConstructors(Class… ) //获取所有Constructor

2.通过Constructor实例可以创建一个实例对象

  • newInstance(Object… parameters)
Class cls = Student.class;
Constructor c = cls.getDeclaredConstructor(String.class, int.class);
//c.setAccessible(true);
Student s = (Student) c.newInstance("Yang",27);
s.hello();

3.设置setAccessible(true)来访问非public构造方法

  • setAccessible(true)可能会失败:

    • 定义了SecurityManager

    • SecurityManager 的规则阻止对Constructor设置accessible:

      例如,规则应用于所有java和javax开头的package的类

E.获取继承关系

1.getSuperclass() 获取父类对象

  • 当拿到一个实例的时,可以获取父类的Class

    • getSuperclass() //通过Class实例可以获取父类对象

      注意

      • 当父类为Object时,返回的是null
      • 当父类为interface时,返回是null
Class s = Integer.class.getSuperclass(); // Number.class
Object.class.getSuperclass(); //null
Runnable.class.getSuperclass(); //null

2.getInterfaces() 获取直接实现的接口

  • 如果当前类实现interface,可以调用getInterfaces()获得直接实现的interface对象
    • Class[] getInterfaces() // 获得直接实现的interfaces数组
    • 不包括间接实现的interface
    • 没有interface的class返回空数组
Class[] ifs = Integer.class.getInterfaces(); // [Comparable.class]
Class[] ifs = java.util.ArrayList.class.getInterfaces(); 
//[List.Class, RandomAccess.class, Cloneable.class, Serializaable.class]
Class[] ifs = Math.class.getinterfaces(); // [] ,没有实现任何接口
Class[] ifs = java.util.List.class.getInterfaces(); //[Collection.class]
  • 如果要获取当前类所有的interface(直接实现和间接实现的interface),使用***递归法***来实现
Class s = Student.class;

printAllInterfaces(s);

void printAllInterfaces(Class cls){
    System.out.println(cls.getSimpleName() +":");
    _printAllInterfaces(cls);
    System.out.println();
}

void _printAllInterfaces(Class cls){
    Class[] ifs = cls.getInterfaces();
    for(Class f : ifs){
        System.out.println(f.getSimpleName());
        System.out.print(",");
        _printAllInterfaces(f);
    }
    Class sup = cls.getSuperclass();
    if(sup != null){
         _printAllInterfaces(sup);
    }
}

3.isAssignableFrom() 判断向上转型是否成立

  • boolean isAssignableFrom(Class)
Number.class.isAssignableFrom(Integer.class); // true
Integer.class.isAssignableFrom(Number.class); // false

the end.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值