Java反射机制-框架设计的灵魂

11 篇文章 0 订阅

概述

运行时状态中,可以获取程序或者程序集的每一个类型的成员或成员信息。核心在于可以运行时动态加载类或者调用方法/属性,不需要事先知道运行对象是谁。

用途

  1. 使用IDE 时,输入一个累活对象,调用它的属性或者方法时,输入 . IDE就自动列出类的方法或属性,这就是java 的反射用途之一。
  2. 在框架设计时,模块间不能相互依赖,这是就需要使用反射获取其他模块的类对象。
  3. 某些类的私有方法(属性)无法直接调用,但是又必须使用时,可以使用反射机制获取类对象的私有方法(属性)并调用。

类的加载过程

Java 文件从编码完成到执行经过两个过程:

**编译:**通过 javac 命令编译成字节码,也就是 .class 文件。

**运行:**把编译生成的 .class 文件交给 JVM 执行。

**类的加载过程:**JVM 虚拟机将 .class 文件中的信息加载到内存中,解析生成对应的 Class 对象,作为这个类的数据访问入口。同一个类,在 内存中只存在一个 Class 对象

也就是说JVM并不是一次性把所有类都加载到内存中,而是第一次执行到这个类的时候加载,且只加载一次。

Class 类的实例表示正在运行的Java应用程序中的类和接口。

Class 没有公共的构造方法。

反射的使用

1、获取Class 对象

反射获取 Class 对象有三种方法:

  1. 调用对象的 getClass() 方法:

    String str = new String();
    Class<?> cls = str.getClass();
    
  2. 直接获取对象(包括基本数据类型)的 class 属性:

    Class<?> cls = int.class;
    
  3. 使用 Class.forName 方法:

    Class<?> cls = Class.forName("类名");
    

在反射里,一般使用第3种方法,第一种已经获取对象,没必要反射。第二种已经知道类型,需要引入包名,比较麻烦。第3种知道类的全提名即可。

2、创建实例

  1. 可以使用Class 对象的newInstance()方法来创建Class对象应用类的实例。

    Class<?> cls = String.class;
    Object oj = cls.newIntance();
    
  2. 可以通过获取Constructor对象的newInstance()方法。这种方式可以指定构造类实例。

    Class<?> cls = String.class;
    // 获取带String 类型参数的构造器
    Contructor c = cls.getConstructor(String.class);
    //调用构造器创建实例。 
    Object o = c.newInstance("124");
    
  3. 如果对象有getInstance()方法,也可以通过调用该方法获取对象。如何反射调用方法往下看。

3、获取方法

反射获取某个Class对象的方法有几种方法:

  1. getDeclaredMethods 返回类或接口声明的所有方法,包括publicprotecteddefaultprivate方法,但是不包括继承父类的方法。

    // 源码如下
    public Method[] getDeclaredMethods() throws SecurityException {
            Method[] result = getDeclaredMethodsUnchecked(false);
            for (Method m : result) {
                // Throw NoClassDefFoundError if types cannot be resolved.
                m.getReturnType();
                m.getParameterTypes();
            }
            return result;
        }
    

    测试示例如下:

    public class TestReflection {
    
        public static void main(String[] args) throws IllegalAccessException, InstantiationException {
            Class<?> c =  MethodCalss.class;
            Object o = c.newInstance();
            Method[] methods = c.getDeclaredMethods();
            System.out.println("*** getDeclaredMethods *** 返回所有声明的方法,但是不返回继承父类的方法");
            for (Method m :methods) {
                System.out.println(m);
            }
        }
    
        static class MethodCalss{
    			  private String testStr="we";				
    				public String testPublic = "hh";
    
            public String getABC(){
                return "ABC";
            }
    
            String getAnyStr(String s){
                return s;
            }
        }
    }
     输出结果如下
    
    > Task :lib:TestReflection.main()
    *** getDeclaredMethods *** 返回所有声明的方法,但是不返回继承父类的方法
    public java.lang.String com.newtypegames.lib.TestReflection$MethodCalss.getABC()
    java.lang.String com.newtypegames.lib.TestReflection$MethodCalss.getAnyStr(java.lang.String)
    

    以上输出结果只有声明的方法,没有父类的方法。

  2. getMethods 获取所有 public 方法,包括父类的public 方法。(只有公共方法,private等其他的关键字声明的方法不可以获取)

    示例及运行结果如下:

    System.out.println("*** getMethods *** 返回所有公共的方法,包括继承父类的公共方法");
            Method[] methods1 = c.getMethods();
    
            for (Method m :methods1) {
                System.out.println(m);
            }
    

    输出结果不包括default 声明的 getAnyStr() 方法,但包括继承父类 ObjecttoString() 等方法。

    *** getMethods *** 返回所有公共的方法,包括继承父类的公共方法
    public java.lang.String com.newtypegames.lib.TestReflection$MethodCalss.getABC()
    public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
    public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
    public final void java.lang.Object.wait() throws java.lang.InterruptedException
    public boolean java.lang.Object.equals(java.lang.Object)
    public java.lang.String java.lang.Object.toString()
    public native int java.lang.Object.hashCode()
    public final native java.lang.Class java.lang.Object.getClass()
    public final native void java.lang.Object.notify()
    public final native void java.lang.Object.notifyAll()
    
  3. getMethod 获取指定方法,第一个参数是需要获取的方法名,第二个参数是方法参数的数据类型。

    Method method = c.getMethod("getAnyStr", String.class);
    

4、获取成员变量

获取成员变量和获取方法类似。

// 获取指定的已声明的成员变量,但不包括父类的成员变量
 Field testStr = c.getDeclaredField("testStr");
 System.out.println("*** method ***" + testStr);

// 输出结果
> Task :lib:TestReflection.main()
*** Field ***private java.lang.String com.newtypegames.lib.TestReflection$MethodCalss.testStr

// 获取指定的公共成员变量,包括父类的公共变量
Field testStr = c.getField("testStr");

// 获取所有的public 变量,包括父类的public 变量
Field[] fields = c.getFields();

// 获取所有声明的(public\private\default\protected) 变量
Field[] declaredFields = c.getDeclaredFields();

5、调用方法

调用 invoke 方法。

// 第一个参数是 实例化后的对象,第二个参数是 用于调用方法的参数
public native Object invoke(Object obj, Object... args)
            throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;

反射的弊端

  1. 反射会消耗一定系统资源,如果没有必要,尽量少用反射。
  2. 反射调用方法可以忽略权限检查,可能会破坏封装性导致安全问题。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值