java反射机制

本文详细介绍了Java反射机制,包括Object类中的getClass()方法作为反射起点,如何实例化Class对象,以及通过getConstructors()获取构造方法。强调了在使用反射实例化对象时,必须确保类中存在无参构造器。此外,还展示了如何获取类的接口、父类和其他信息。最后提到了Java动态代理的实现,涉及Proxy类和InvocationHandler接口。
摘要由CSDN通过智能技术生成

Java反射机制

在这里插入图片描述

1. Object类的支持

  • 在Object类中定义了一下方法,此方法将被所有子类继承
    • public final Class getClass()
    • 以上的方法的返回值类型是一个“Class”类,实际上此类是Java反射机制的源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称
  • 正常方式
    • 引入需要的“包.类”名称 -----> 通过new实例化 -----> 取得实例对象
  • 反射方式
    • 实例化对象 -----> getClass()方法 -----> 得到完整的“包.类“名称

2. 实例化Class类对象

  • 实例化Class类对象的三种方法:
    • 通过forName()方法 (重点掌握)
    • 类.class
    • 对象.getClass()
  • 从实际角度看,如果要使用反射进行对象的实例化操作,最好在类中存在无参构造
//Person类

package com.lgt.Demo2;

public class Person {
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

<注意>

该类中写了两个构造方法,后面在调用有参构造函数时,Constructors[]数组的下标;

无参构造 cons[0] 有参cons[1]

package com.lgt.Demo2;

/**
 * @Author LGT
 * @Date 2021/7/10 15:03
 * @Version 1.0
 */
public class InstanceDemo1 {
    public static void main(String[] args) {
        Class<?> c = null;
        try{
            c = Class.forName("com.lgt.Demo2.Person");
        }
        catch (ClassNotFoundException e)
        {
            e.printStackTrace();
        }
        Person person = null;
        try {
            person = (Person) c.newInstance();
        }
        catch (Exception e){
            e.printStackTrace();
        }
        person.setName("曲小檀");
        person.setAge(23);
        System.out.println(person);
    }
}

  • getConstructors
    public Constructor<?>[] getConstructors()
                                     throws SecurityException
    
    返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。如果该类没有公共构造方法,或者该类是一个数组类,或者该类反映一个基本类型或 void,则返回一个长度为 0 的数组。 注意,此方法返回 Constructor<T> 对象的数组(即取自此类构造方法的数组)时,此方法的返回类型是 Constructor<?>[],不是 预期的 Constructor<T>[]。此少量信息的返回类型是必需的,因为从此方法返回之后,该数组可能被修改以保存不同类的 Constructor 对象,而这将违反 Constructor<T>[] 的类型保证。 
    
    返回:
    表示此类公共构造方法的 Constructor 对象数组 
    抛出: 
    SecurityException - 如果存在安全管理器 s,并满足下列任一条件: 
    调用 s.checkMemberAccess(this, Member.PUBLIC) 拒绝访问该类中的构造方法 
    调用者的类加载器不同于也不是当前类的类加载器的一个祖先,并且对 s.checkPackageAccess() 的调用拒绝访问该类的包 
    
package com.lgt.Demo2;

import java.lang.reflect.Constructor;

/**
 * @Author LGT
 * @Date 2021/7/10 16:01
 * @Version 1.0
 */
public class InstanceDemo2 {
    public static void main(String[] args) {
        Class<?> c = null;
        try{
            c = Class.forName("com.lgt.Demo2.Person");
        }
        catch (ClassNotFoundException e){
            e.printStackTrace();
        }
        Person person = null;
        Constructor<?>[] cons = c.getConstructors();
        try{
            person = (Person) cons[1].newInstance("李光泰",23);
        }
        catch (Exception e){
            e.printStackTrace();
        }
        System.out.println(person);
    }
}

总结:
  • 在使用Class实例化对象的时候,必须保证类中存在一个无参构造,否则无法使用
  • 如果想调用有参构造进行对象的实例化操作,则必须使用Constructor类完成,在操作的时候需要明确的调用类中的公祖奥方法,并将参数传递进去之后才能进行实例化操作,操作步骤如下:
    1. 通过Class类中的getConstructors()取得本类中的全部构造方法。
    2. 向构造方法中传递一个对象数组进去,里面包含了构造方法中所需的哥哥参数
    3. 通过Constructor实例化对象

3. 反射机制深入-取得类结构

通过反射机制可以获得一个类的完整结构

java.lang.reflect 类 AccessibleObject
java.lang.Object
  java.lang.reflect.AccessibleObject
  • 所有已实现的接口:

    AnnotatedElement

  • 直接已知子类:

    Constructor, Field, Method

    • Constructor: 表示类中的构造方法
    • Filed: 表示类中的属性
    • Method: 表示类中的方法

3.1 取得类所实现的全部接口

使用Class类中的 getInterface()方法

  • public Class[] getInterface()
  • 此方法返回的是Class类的对象数组,之后可以直接利用Class类中的getName()输出

3.2 取得类所继承的父类

一个类可以实现多个接口,但只能继承一个父类;要想取得一个类的父类,可以直接通过Class类中的 getSuperclass() 方法

3.3 取得类中的其他信息

public static void main(String[] args) {
        Class<?> c = null;
        try{
            c = Class.forName("com.lgt.GetInterfaceDemo.FruitImpl");
        }catch(ClassNotFoundException e){
            e.printStackTrace();
        }
        FruitImpl fruit = null;
        Constructor<?>[] cons = c.getConstructors();//取得所有的构造方法
    
        Class<?>[] ins = c.getInterfaces();//取得实现的所有接口
        //获取全部的构造方法
        System.out.println("--------- 构造方法  -----------");
        for(int i = 0;i< cons.length;i++){
            //注意,直接通过getModifiers()获取的修饰符是数字,需要转换
            //System.out.println(cons[i].getModifiers());//获取访问权限
            int mo = cons[i].getModifiers();//获取访问修饰符
            System.out.print(Modifier.toString(mo)+" ");//转换为String
            System.out.print(cons[i].getName());
            Class<?>[] parameter = cons[i].getParameterTypes();//得到构造方法的全部参数
            System.out.print("("+" ");
            for(int n = 0;n<parameter.length;n++){
                System.out.print(parameter[n].getName()+" args");
                if(n!=parameter.length-1)
                    System.out.print(" , ");
            }
            System.out.print(")");
            System.out.println();
        }
        System.out.println("--------- 实现的所有接口 -----------");
        for(int i = 0;i< ins.length;i++){
            System.out.println(ins[i].getName());
        }
    }
//输出结果
--------- 构造方法  -----------
public com.lgt.GetInterfaceDemo.FruitImpl( java.lang.String args , float args)
public com.lgt.GetInterfaceDemo.FruitImpl( java.lang.String args)
public com.lgt.GetInterfaceDemo.FruitImpl( )
--------- 实现的所有接口 -----------
com.lgt.GetInterfaceDemo.Fruit

Process finished with exit code 0
  • 注意
    • 直接取出的访问修饰符是数字,需要使用Modifier类进行转换

4. 动态代理

4.1 Proxy类

  • 类加载器
    • 在Proxy类中的newProxyInstance()方法需要一个ClassLoader类的实例,ClassLoader实际上对应的是类加载器,在java中主要有以下三类类加载器

      • Bootstrap ClassLoader (不常见)

      • Extension ClassLoader (用来进行扩展类的加载,一般对应的是 “jre/lib/ext” 目录中的类)

      • Application ClassLoader (加载classpath指定的类,是最为常用的一种类加载器)
    • 可以通过 对象.getClass().getClassLoader().getClass().getName() 获取类加载器的名称

4.2 动态代理实现 ———— 定义MyInvocationHandler的类

package com.lgt.Demo3;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @Author LGT
 * @Date 2021/7/12 13:04
 * @Version 1.0
 */
public class MyInvocationHandler implements InvocationHandler {
    private Object obj; //真实主题

    //绑定真实操作主题
    public Object bind(Object obj){
        this.obj =obj;
        //取得代理对象
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this);
    }

    //动态调用方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object temp = method.invoke(this.obj,args);//调用方法传入真实的主题和参数
        return temp;
    }
}

public class test {
    public static void main(String[] args) {
    //    静态代理
        //Chinese ch = new Chinese();
        //StaticProxy staticProxy = new StaticProxy();
        //staticProxy.setHuman(ch);
        //staticProxy.say();

    //    动态代理(1)
    //    Human human = (Human) new MyInvocationHandler().bind(new Chinese());
    //    human.say();
	
        //    动态代理(2)
        Chinese chinese = new Chinese();
        MyInvocationHandler mi = new MyInvocationHandler();
        Human human = (Human) mi.bind(chinese);
        human.say();
    }

总结:
  • Proxy----------------提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类
  • InvocationHandler-----调用处理程序并返回结果
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值