一起来学反射(下)

简介: 一起来学反射

5. 获取运行类的完整实例

5.1 通过反射获取运行时类的完整结构

Fileds、Menthod、Constructor、Superclass、Interface、Annotation


实现的全部接口


所继承的父类


全部的构造器


全部的方法


全部的Field


使用反射可以取得:


1.实现的全部接口

public Class<?>[] getInterfaces()

确定此对象所表示的类或接口实现的接口。


2.所继承的父类

public Class<? Super T> getSuperclass()

返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的


Class。


3.全部的构造器

public Constructor[] getConstructors()

返回此 Class 对象所表示的类的所有public构造方法。


public Constructor[] getDeclaredConstructors()返回此 Class 对象表示的类声明的所有构造方法。

Constructor类中:


取得修饰符: public int getModifiers();


取得方法名称: public String getName();


取得参数的类型:public Class<?>[] getParameterTypes();


4.全部的方法

public Method[] getDeclaredMethods()

返回此Class对象所表示的类或接口的全部方法


public Method[] getMethods()

返回此Class对象所表示的类或接口的public的方法


Method类中:


public Class<?> getReturnType()取得全部的返回值


public Class<?>[] getParameterTypes()取得全部的参数


public int getModifiers()取得修饰符


public Class<?>[] getExceptionTypes()取得异常信息


5.全部的Field

public Field[] getFields()

返回此Class对象所表示的类或接口的public的Field。


public Field[] getDeclaredFields() 返回此Class对象所表示的类或接口的全部Field。

Field方法中:


public int getModifiers() 以整数形式返回此Field的修饰符


public Class<?> getType() 得到Field的属性类型


public String getName() 返回Field的名称。


6.Annotation相关

get Annotation(Class annotationClass)


getDeclaredAnnotations()


7.泛型相关

获取父类泛型类型:Type getGenericSuperclass()


泛型类型:ParameterizedType


获取实际的泛型类型参数数组:getActualTypeArguments()


8.类所在的包

Package getPackage()


6. 调用运行时类的的指定结构

6.1 调用指定方法

通过反射,调用类中的方法,通过Method类完成。步骤:


1.通过Class类的**getMethod(String name,Class…parameterTypes)**方法取得一个Method对象,并设置此方法操作时所需要的参数类型。


2.之后使用**Object invoke(Object obj, Object[] args)**进行调用,并向方法中传递要设置的obj对象的参数信息。


image-20221002220609925.png

Object invoke(Object obj, Object … args)


说明:


1.Object 对应原方法的返回值,若原方法无返回值,此时返回null


2.若原方法若为静态方法,此时形参Object obj可为null


3.若原方法形参列表为空,则Object[] args为null


4.若原方法声明为private,则需要在调用此invoke()方法前,显式调用


方法对象的setAccessible(true)方法,将可访问private的方法。


6.2 调用指定属性

在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的set()和get()方法就可以完成设置和取得属性内容的操作。


public Field getField(String name) 返回此Class对象表示的类或接口的指定的public的Field。


public Field getDeclaredField(String name)返回此Class对象表示的类或接口的


指定的Field。


在Filed方法中 :


public Object get(Object obj) 取得指定对象obj上此Field的属性内容


public void set(Object obj,Object value) 设置指定对象obj上此Field的属性内容


6.3 关于setAccessible方法的使用

Method和Filed、Construtor对象都有setAccessible()方法。


setAccessible启动和禁用访问安全检查的开关。


参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。


提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true。

使得原本无法访问的私有成员也可以访问

参数值为false则指示反射的对象应该实施Java语言访问检查


7. 反射的应用 : 动态代理

7.2 初始代理模式

代理设计模式的原理:

使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。


静态代理,特征是代理类和目标


对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。最好可以通过一个代理类完成全部的代理功能。


动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。


动态代理使用场合:


调试

远程方法调用

动态代理相比于静态代理的优点:


抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法。


7.2 Java动态代理相关的API

Proxy : 专门完成代理的操作类,是所有动态代理的父类。通过此类可以为一个或多个接口动态生成实现类。

提供用于创建动态代理类和动态代理的静态方法。


7.3 动态代理的相关步骤

1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法,以完成代理的具体操作。

image-20221002222234496.png


2.创建被代理的类以及接口


image-20221002222306625.png

3.通过Proxy的静态方法


newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建一个Subject接口代理。

RealSubject target = new RealSubject();
// Create a proxy to wrap the original implementation
DebugProxy proxy = new DebugProxy(target);
// Get a reference to the proxy through the Subject interface
Subject sub = (Subject) Proxy.newProxyInstance(
Subject.class.getClassLoader(),new Class[] { Subject.class }, proxy);


4.通过 Subject代理调用RealSubject实现类的方法


String info = sub.say(“Peter", 24);
System.out.println(info);


7.4 动态代理与AOP(Aspect Orient Progranmming


image-20221002222450521.png

image-20221002222450521.png

96134ba17fd92c58a28b1fdcfdd97b40.png


改进后的说明:


代码段1、代码段2、代码段3和深色代码段分离开了,但代码段1、2、3又和一个特定的方法A耦合了!最理想的效果是:代块1、2、3既可以执行方法A,又无须在程序中以硬编码的方式直接调用深色代码的方法


代码示例 :

public interface Dog{
    void info();
    void run();
}
public class HuntingDog implements Dog{
public void info(){
  System.out.println("我是一只猎狗");
}
public void run(){
  System.out.println("我奔跑迅速");
  }
}
public class DogUtil{
public void method1(){
  System.out.println("=====模拟通用方法一=====");
}
public void method2(){
  System.out.println("=====模拟通用方法二=====");
  } 
}
public class MyInvocationHandler implements InvocationHandler{
// 需要被代理的对象
private Object target;
public void setTarget(Object target){
this.target = target;}
// 执行动态代理对象的所有方法时,都会被替换成执行如下的invoke方法
public Object invoke(Object proxy, Method method, Object[] args)
throws Exception{
DogUtil du = new DogUtil();
// 执行DogUtil对象中的method1。
du.method1();
// 以target作为主调来执行method方法
Object result = method.invoke(target , args);
// 执行DogUtil对象中的method2。
du.method2();
return result;}}
public class MyProxyFactory{
// 为指定target生成动态代理对象
public static Object getProxy(Object target)
throws Exception{
// 创建一个MyInvokationHandler对象
MyInvokationHandler handler = 
new MyInvokationHandler();
// 为MyInvokationHandler设置target对象
handler.setTarget(target);
// 创建、并返回一个动态代理对象
return 
Proxy.newProxyInstance(target.getClass().getClassLoader()
, target.getClass().getInterfaces() , handler);
} }
public class Test{
public static void main(String[] args) 
throws Exception{
// 创建一个原始的HuntingDog对象,作为target
Dog target = new HuntingDog();
// 以指定的target来创建动态代理
Dog dog = (Dog)MyProxyFactory.getProxy(target);
dog.info();
dog.run();
  } 
}

image-20221002222911897.png

765ba63c70cf6d361a008b134e031279.png

8.反射的基本应用

8.1 获取类型的详细信息

可以获取:包、修饰符、类型名、父类(包括泛型父类)、父接口(包括泛型父接口)、成员(属性、构造器、方法)、注解(类上的、方法上的、属性上的)

示例代码获取常规信息:

package com.atguigu.reflect;
package com.atguigu.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class TestClassInfo {
    public static void main(String[] args) throws Exception {
        //1、先得到某个类型的Class对象
        Class clazz = String.class;
        //比喻clazz好比是镜子中的影子
        //2、获取类信息
        //(1)获取包对象,即所有java的包,都是Package的对象
        Package pkg = clazz.getPackage();
        System.out.println("包名:" + pkg.getName());
        //(2)获取修饰符
        //其实修饰符是Modifier,里面有很多常量值
        /*
         * 0x是十六进制
         * PUBLIC           = 0x00000001;  1    1
         * PRIVATE          = 0x00000002;  2  10
         * PROTECTED        = 0x00000004;  4  100
         * STATIC           = 0x00000008;  8  1000
         * FINAL            = 0x00000010;  16 10000
         * ...
         *
         * 设计的理念,就是用二进制的某一位是1,来代表一种修饰符,整个二进制中只有一位是1,其余都是0
         *
         * mod = 17          0x00000011
         * if ((mod & PUBLIC) != 0)  说明修饰符中有public
         * if ((mod & FINAL) != 0)   说明修饰符中有final
         */
        int mod = clazz.getModifiers();
        System.out.println("类的修饰符有:" + Modifier.toString(mod));
        //(3)类型名
        String name = clazz.getName();
        System.out.println("类名:" + name);
        //(4)父类,父类也有父类对应的Class对象
        Class superclass = clazz.getSuperclass();
        System.out.println("父类:" + superclass);
        //(5)父接口们
        System.out.println("父接口们:");
        Class[] interfaces = clazz.getInterfaces();
        for (Class iter : interfaces) {
            System.out.println(iter);
        }
        //(6)类的属性,  你声明的一个属性,它是Field的对象
/*    Field clazz.getField(name)  根据属性名获取一个属性对象,但是只能得到公共的
    Field[] clazz.getFields();  获取所有公共的属性
    Field clazz.getDeclaredField(name)  根据属性名获取一个属性对象,可以获取已声明的
    Field[] clazz.getDeclaredFields() 获取所有已声明的属性
    */
//        Field valueField = clazz.getDeclaredField("value");
//    System.out.println("valueField = " +valueField);
        System.out.println("------------------------------");
        System.out.println("成员如下:");
        System.out.println("属性有:");
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field field : declaredFields) {
            //修饰符、数据类型、属性名
            int modifiers = field.getModifiers();
            System.out.println("属性的修饰符:" + Modifier.toString(modifiers));
            String name2 = field.getName();
            System.out.println("属性名:" + name2);
            Class<?> type = field.getType();
            System.out.println("属性的数据类型:" + type);
        }
        System.out.println("-------------------------");
        //(7)构造器们
        System.out.println("构造器列表:");
        Constructor[] constructors = clazz.getDeclaredConstructors();
        for (int i=0; i<constructors.length; i++) {
            Constructor constructor = constructors[i];
            System.out.println("第" + (i+1) +"个构造器:");
            //修饰符、构造器名称、构造器形参列表  、抛出异常列表
            int modifiers = constructor.getModifiers();
            System.out.println("构造器的修饰符:" + Modifier.toString(modifiers));
            String name2 = constructor.getName();
            System.out.println("构造器名:" + name2);
            //形参列表
            System.out.println("形参列表:");
            Class[] parameterTypes = constructor.getParameterTypes();
            for (Class parameterType : parameterTypes) {
                System.out.println(parameterType);
            }
            //异常列表
            System.out.println("异常列表:");
            Class<?>[] exceptionTypes = constructor.getExceptionTypes();
            for (Class<?> exceptionType : exceptionTypes) {
                System.out.println(exceptionType);
            }
            System.out.println();
        }
        System.out.println("---------------------------------");
        //(8)方法们
        System.out.println("方法列表:");
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (int i=0; i<declaredMethods.length; i++) {
            Method method = declaredMethods[i];
            System.out.println("第" + (i+1) +"个方法:");
            //修饰符、返回值类型、方法名、形参列表 、异常列表
            int modifiers = method.getModifiers();
            System.out.println("方法的修饰符:" + Modifier.toString(modifiers));
            Class<?> returnType = method.getReturnType();
            System.out.println("返回值类型:" + returnType);
            String name2 = method.getName();
            System.out.println("方法名:" + name2);
            //形参列表
            System.out.println("形参列表:");
            Class[] parameterTypes = method.getParameterTypes();
            for (Class parameterType : parameterTypes) {
                System.out.println(parameterType);
            }
            //异常列表
            System.out.println("异常列表:");
            Class<?>[] exceptionTypes = method.getExceptionTypes();
            for (Class<?> exceptionType : exceptionTypes) {
                System.out.println(exceptionType);
            }
            System.out.println();
        }
    }
}

8.2 创建任意引用类型的对象

两种方式:


1、直接通过Class对象来实例化(要求必须有公共的无参构造)


2、通过获取构造器对象来进行实例化


方式一的步骤:


(1)获取该类型的Class对象(2)创建对象


方式二的步骤:


(1)获取该类型的Class对象(2)获取构造器对象(3)创建对象


如果构造器的权限修饰符修饰的范围不可见,也可以调用setAccessible(true)


示例代码:

package com.atguigu.reflect;
import org.junit.Test;
import java.lang.reflect.Constructor;
public class TestCreateObject {
    @Test
    public void test1() throws Exception{
//        AtGuiguClass obj = new AtGuiguClass();//编译期间无法创建
        Class<?> clazz = Class.forName("com.atguigu.ext.demo.AtGuiguClass");
        //clazz代表com.atguigu.ext.demo.AtGuiguClass类型
        //clazz.newInstance()创建的就是AtGuiguClass的对象
        Object obj = clazz.newInstance();
        System.out.println(obj);
    }
    @Test
    public void test2()throws Exception{
        Class<?> clazz = Class.forName("com.atguigu.ext.demo.AtGuiguDemo");
        //java.lang.InstantiationException: com.atguigu.ext.demo.AtGuiguDemo
        //Caused by: java.lang.NoSuchMethodException: com.atguigu.ext.demo.AtGuiguDemo.<init>()
        //即说明AtGuiguDemo没有无参构造,就没有无参实例初始化方法<init>
        Object stu = clazz.newInstance();
        System.out.println(stu);
    }
    @Test
    public void test3()throws Exception{
        //(1)获取Class对象
        Class<?> clazz = Class.forName("com.atguigu.ext.demo.AtGuiguDemo");
        /*
         * 获取AtGuiguDemo类型中的有参构造
         * 如果构造器有多个,我们通常是根据形参【类型】列表来获取指定的一个构造器的
         * 例如:public AtGuiguDemo(String title, int num)
         */
        //(2)获取构造器对象
        Constructor<?> constructor = clazz.getDeclaredConstructor(String.class,int.class);
        //(3)创建实例对象
        // T newInstance(Object... initargs)  这个Object...是在创建对象时,给有参构造的实参列表
        Object obj = constructor.newInstance("尚硅谷",2022);
        System.out.println(obj);
    }
}

8.3 操作任意类型的属性

(1)获取该类型的Class对象


Class clazz = Class.forName(“包.类名”);


(2)获取属性对象


Field field = clazz.getDeclaredField(“属性名”);


(3)如果属性的权限修饰符不是public,那么需要设置属性可访问


field.setAccessible(true);


(4)创建实例对象:如果操作的是非静态属性,需要创建实例对象


Object obj = clazz.newInstance(); //有公共的无参构造


Object obj = 构造器对象.newInstance(实参…);//通过特定构造器对象创建实例对象


(4)设置属性值


field.set(obj,“属性值”);


如果操作静态变量,那么实例对象可以省略,用null表示


(5)获取属性值


Object value = field.get(obj);


如果操作静态变量,那么实例对象可以省略,用null表示


示例代码:


package com.atguigu.reflect;
public class Student {
    private int id;
    private String name;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}
package com.atguigu.reflect;
import java.lang.reflect.Field;
public class TestField {
    public static void main(String[] args)throws Exception {
        //1、获取Student的Class对象
        Class clazz = Class.forName("com.atguigu.reflect.Student");
        //2、获取属性对象,例如:id属性
        Field idField = clazz.getDeclaredField("id");
        //3、如果id是私有的等在当前类中不可访问access的,我们需要做如下操作
        idField.setAccessible(true);
        //4、创建实例对象,即,创建Student对象
        Object stu = clazz.newInstance();
        //5、获取属性值
        /*
         * 以前:int 变量= 学生对象.getId()
         * 现在:Object id属性对象.get(学生对象)
         */
        Object value = idField.get(stu);
        System.out.println("id = "+ value);
        //6、设置属性值
        /*
         * 以前:学生对象.setId(值)
         * 现在:id属性对象.set(学生对象,值)
         */
        idField.set(stu, 2);
        value = idField.get(stu);
        System.out.println("id = "+ value);
    }
}

8.4 调用任意类型的方法


(1)获取该类型的Class对象


Class clazz = Class.forName(“包.类名”);


(2)获取方法对象


Method method = clazz.getDeclaredMethod(“方法名”,方法的形参类型列表);


(3)创建实例对象


Object obj = clazz.newInstance();


(4)调用方法


Object result = method.invoke(obj, 方法的实参值列表);


如果方法的权限修饰符修饰的范围不可见,也可以调用setAccessible(true)


如果方法是静态方法,实例对象也可以省略,用null代替


示例代码:


package com.atguigu.reflect;
import org.junit.Test;
import java.lang.reflect.Method;
public class TestMethod {
    @Test
    public void test()throws Exception {
        // 1、获取Student的Class对象
        Class<?> clazz = Class.forName("com.atguigu.reflect.Student");
        //2、获取方法对象
        /*
         * 在一个类中,唯一定位到一个方法,需要:(1)方法名(2)形参列表,因为方法可能重载
         *
         * 例如:void setName(String name)
         */
        Method setNameMethod = clazz.getDeclaredMethod("setName", String.class);
        //3、创建实例对象
        Object stu = clazz.newInstance();
        //4、调用方法
        /*
         * 以前:学生对象.setName(值)
         * 现在:方法对象.invoke(学生对象,值)
         */
        Object setNameMethodReturnValue = setNameMethod.invoke(stu, "张三");
        System.out.println("stu = " + stu);
        //setName方法返回值类型void,没有返回值,所以setNameMethodReturnValue为null
        System.out.println("setNameMethodReturnValue = " + setNameMethodReturnValue);
        Method getNameMethod = clazz.getDeclaredMethod("getName");
        Object getNameMethodReturnValue = getNameMethod.invoke(stu);
        //getName方法返回值类型String,有返回值,getNameMethod.invoke的返回值就是getName方法的返回值
        System.out.println("getNameMethodReturnValue = " + getNameMethodReturnValue);//张三
    }
    @Test
    public void test02()throws Exception{
        Class<?> clazz = Class.forName("com.atguigu.ext.demo.AtGuiguClass");
        Method printInfoMethod = clazz.getMethod("printInfo", String.class);
        //printInfo方法是静态方法
        printInfoMethod.invoke(null,"尚硅谷");
    }
}
目录
相关文章
|
11月前
反射
何为反射?在运行状态时,对于任何一个类,都能够动态获取这个类得所有方法和属性(私有,公有,受保护),都能够调用任意一个方法和属性。
34 0
|
3月前
|
API C# 数据库
C#反射详解
C#反射详解
34 0
|
8月前
|
Java 数据库连接 数据库
JVAVEE反射
JVAVEE反射
40 0
|
11月前
|
安全 数据可视化 IDE
反射(二)什么是反射
反射(二)什么是反射
46 0
|
存储 Java 索引
反射理解
个人对反射的学习理解与代码
55 0
|
缓存 前端开发 JavaScript
一起来学反射(上)
一起来学反射
87 0
|
Java
反射总结
反射总结
84 0
反射之IllegalAccessException、NoSuchFieldException
本文目录 1. IllegalAccessException 2. NoSuchFieldException
394 0