java 反射

序言

java是面向对象的语言,把程序代码比作一辆车,车子有自己的颜色、车型号、品牌这些属性,也有正常行驶、倒车、停泊这些功能操作。正常情况下,我们需要为车子配备一个司机,然后按照行为准则规范行驶。那么反射是什么呢?无人驾驶。Class就是行驶规则。

反射的概念:

反射就是获取类的字节码文件,可以使我们在运行状态获取任何一个类的方法和属性。对于任意一个对象,都能够调用它的任意方法和属性。

Class获取 

一个类在内存中只有个一个class对象

类被加载后。类的整个结构都会封装在class对象中

1. 通过 Object.getClass()
        //	这种方法不适合基本类型如 int、float 等等
		Car car = new Car();
		Class cls = car.getClass();

2. 通过 .class 标识
	   Class cls = Car.class;

3. 通过 Class.forName(包名+类名) 方法
try {
	Class cls = Class.forName("com.frank.test.Car");
} catch (ClassNotFoundException e) {
	e.printStackTrace();
}

4. 创建对象

    Car car = (Car)cls.newInstance();//必须要有无惨构造器
    //构造器

    Constructor constructor = cls.getDeclredControct(参数类型)
    Car car2 = constructor =cls.newInstance("","")
       

Class 的名字

Class cls=....
//得到包名+类型 如果是数组({[+包名+类名)内部类(包名+类型$内部类名)
cls.getName();

//得到类名
cls.getSimpleName();

//返回的也是全限定类名,但是对于内部类,不用 $ 开头,而用 .。
cls.getCanonicalName();

Class 获取修饰符

Java 开发中定义一个类,往往是要通过许多修饰符来配合使用的。它们大致分为 4 类。

  • 用来限制作用域,如 public、protected、priviate。
  • 用来提示子类复写,abstract。
  • 用来标记为静态类 static。
  • 注解

java 反射提供了 API 去获取这些修饰符。

package com.frank.test;

public abstract class TestModifier {

}

如果定义一个类,名字为 TestModifier,被 public 和 abstract 修饰,调用 Class.getModifiers() 方法就是了,它返回的是一个 int 数值。

System.out.println("modifiers value:"+TestModifier.class.getModifiers());
System.out.println("modifiers :"+Modifier.toString(TestModifier.class.getModifiers()));

打印结果是:
modifiers value:1025
modifiers :public abstract

获取 Class 的成员

一个类的成员包括属性、方法, 构造方法。对应到 Class 中就是 Field、Method、Constructor。

Field获取

1,获取单个属性
//获取的是 Class 中被 private 修饰的属性,不能获取父类的属性
public Field getDeclaredField(String name)throws NoSuchFieldException,
                              SecurityException;

//获取的是非私有属性,并且 getField() 在当前 Class 获取不到时会向祖先类获取。
public Field getField(String name)throws NoSuchFieldException,
                      SecurityException

2,获取所有的属性。
//获取所有的属性,但不包括从父类继承下来的属性
public Field[] getDeclaredFields() throws SecurityException {}

//获取自身的所有的 public 属性,包括从父类继承下来的。
public Field[] getFields() throws SecurityException {

1,获取Field的名   fiele.getName,,

2,获取 Field 的类型,通过 2 个方法:

//法能够获取到泛型类型 eg List<Car>   java.util.List<xxx.Car>
public Type getGenericType() {}

eg:List<Car>   java.util.List
public Class<?> getType() {}

3,获取Field 也有很多修饰符。同 Class 一样,通过 getModifiers() 方法就可以轻松获取。Modifier.toString(fiele.getModifiers())

public int getModifiers() {}

4,获取Field 值与赋值 如果是私有属性。必须 field.setAccessible(true); 表示可以访问

Field 这个类定义了一系列的 get 方法来获取不同类型的值。

public Object get(Object obj);//如果是静态属性 object是null

public int getInt(Object obj);//以此类推。可以float类型数据,没有getString

Field 又定义了一系列的 set 方法用来对其自身进行赋值。

public void set(Object obj, Object value);

public void getInt(Object obj,int value);

获取 Method
类或者接口中的方法对应到 Class 就是 Method。Method跟Field 类似,parameterTypes 是方法对应的参数

//获取单个方法。
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)

public Method getMethod(String name, Class<?>... parameterTypes)

//获取所有方法
public Method[] getDeclaredMethods() throws SecurityException

public Method[] getMethods() throws SecurityException

方法由下面几个要素构成:

  • 方法名
  • 方法参数
  • 方法返回值
  • 方法的修饰符
  • 方法可能会抛出的异常

1, 获取方法名,通过 getName() 。

2,获取方法参数,有三个基本方法

public Parameter[] getParameters() {}

返回的是一个 Parameter 数组,在反射中 Parameter 对象就是用来映射方法中的参数。经常使用的方法有:

Parameter.java

// 获取参数名字
public String getName() {}

// 获取参数类型
public Class<?> getType() {}

// 获取参数的修饰符
public int getModifiers() {}


当然,有时候我们不需要参数的名字,只要参数的类型就好了,通过 Method 中下面的方法获取。

Method.java

// 获取所有的参数类型
public Class<?>[] getParameterTypes() {}

// 获取所有的参数类型,包括泛型
public Type[] getGenericParameterTypes() {}

3, Method 获取返回值类型

// 获取返回值类型
public Class<?> getReturnType() {}

// 获取返回值类型包括泛型
public Type getGenericReturnType() {}

4, Method 获取修饰符
public int getModifiers() {}

5,Method 获取异常类型

public Class<?>[] getExceptionTypes() {}

public Type[] getGenericExceptionTypes() {}

6 ,Method 方法的执行 如果是私有方法。必须 field.setAccessible(true); 表示可以访问

这个应该是整个反射机制的核心内容了,很多时候运用反射目的其实就是为了以常规手段执行 Method。

public Object invoke(Object obj, Object... args) {}

Method 调用 invoke() 的时候,存在许多细节:

invoke() 方法中第一个参数 Object 实质上是 Method 所依附的 Class 对应的类的实例,如果这个方法是一个静态方法,那么 ojb 为 null,后面的可变参数 Object 对应的自然就是参数。

invoke() 返回的对象是 Object,所以实际上执行的时候要进行强制转换。

在对 Method 调用 invoke() 的时候,如果方法本身会抛出异常,那么这个异常就会经过包装,由 Method 统一抛出 InvocationTargetException。而通过 InvocationTargetException.getCause() 可以获取真正的异常。

eg
 Method methodSetAge =cls.getMethod("setAge",int.class);
 methodSetAge.invoke(student,100);
 //得到返回值
 Method methodGetName=cls.getMethod("getName");
 String name = (String) methodGetName.invoke(student);

获取 构造器Constructor

Constructor 不能从父类继承,所以就没有办法通过 getConstructor() 获取到父类的 Constructor。

Constructor 同 Method 差不多,但是它特别的地方在于,它能够创建一个对象。

在 Java 反射机制中有两种方法可以用来创建类的对象实例:Class.newInstance() 和 Constructor.newInstance()。官方文档建议开发者使用后面这种方法,下面是原因。

Class.newInstance() 只能调用无参的构造方法,而 Constructor.newInstance() 则可以调用任意的构造方法。
Class.newInstance() 通过构造方法直接抛出异常,而 Constructor.newInstance() 会把抛出来的异常包装到 InvocationTargetException 里面去,这个和 Method 行为一致。
Class.newInstance() 要求构造方法能够被访问,而 Constructor.newInstance() 却能够访问 private 修饰的构造器。

/*获取构造方法*/
//获取构造方法,包括没写无参的构造方法
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
//获取构造方法。如果没有写无参的构造方法,不可以使用
public Constructor<T> getConstructor(Class<?>... parameterTypes)

/*获取所有的构造方法*/
//已写的构造方法+默认的构造方法
public Constructor<?>[] getDeclaredConstructors() throws SecurityException 
//已经写出来的构造方法
public Constructor<?>[] getConstructors() throws SecurityException 



eg
        Class cls = Student.class;
        Constructor[] constructors = cls.getConstructors();
        Constructor[] constructors1 = cls.getDeclaredConstructors();
           for(int i=0;i<constructors.length;i++){
               err("不包括默认构造器=="+constructors[i].toString());
            }
   
          for(int i=0;i<constructors1.length;i++){   
                err("包括默认构造器=="+constructor1.toString());
            }
     

        try {
            Constructor constr=cls.getConstructor(boolean.class);

                Student s= (Student) constr.newInstance(true);
             err("带参数=="+s.getName()+"---"+s.getAge());
            Constructor constr2=cls.getDeclaredConstructor();
            Student s2= (Student) constr2.newInstance();
            err("***带参数=="+s2.getName()+"---"+s2.getAge());
            Constructor constr1 =cls.getConstructor();

            Student s1 = (Student) constr1.newInstance();
            err("no带参数=="+s1.getName()+"---"+s1.getAge());
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();

        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

总结
Java 中的反射是非常规编码方式。
Java 反射机制的操作入口是获取 Class 文件。 有 Class.forName()、 .class 和 Object.getClass() 3 种。
获取 Class 对象后还不够,需要获取它的 Members,包含 Field、Method、Constructor。
Field 操作主要涉及到类别的获取,及数值的读取与赋值。
Method 算是反射机制最核心的内容,通常的反射都是为了调用某个 Method 的 invoke() 方法。
通过 Class.newInstance() 和 Constructor.newInstance() 都可以创建类的对象实例,但推荐后者。因为它适应于任何构造方法,而前者只会调用可见的无参数的构造方法。
数组和枚举可以被看成普通的 Class 对待。
 

Android Hook动态代理机制详解_艾阳Blog的博客-CSDN博客_android hook代理

细说反射,Java 和 Android 开发者必须跨越的坎_frank909的博客-CSDN博客
 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值