• JAVA【反射机制】


    目录

    一、反射机制概述

     二、理解Class类并获取Class的实例

    获取class实例的方式

    三、类的加载与ClassLoader的理解 

    四、创建运行时类的对象

    五、获取运行时类的完整结构

    获取上述运行时类的属性结构

     获取权限修饰符,数据类型,变量名

     获取运行时类中的方法

     获取方法的具体结构

     获取构造器结构

     获取运行时类的父类 

     获取运行时类带泛型的父类

    获取运行时类实现的接口

     获取运行时类所在的包

     获取运行时类声明的注释

    六、调用运行时类的指定结构

     1.如何操作运行时类中的指定的属性

     2.如何操作运行时类中的指定的方法

     七、动态代理


    一、反射机制概述

    Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。


    加载完类之后, 在堆内存的方法区中就产生了一个Class类型的对象( 一个类只有一个Class对象) , 这个对象就包含了完整的类的结构信息。 我们可以通过这个对象看到类的结构。 这个对象就像一面镜子, 透过这个镜子看到类的结构, 所以, 我们形象的称之为: 反射。

     1、动态语言
    是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构。
    主要动态语言: Object-C、 C#、 JavaScript、 PHP、 Python、 Erlang。


    2、静态语言
    与动态语言相对应的, 运行时结构不可变的语言就是静态语言。如Java、 C、C++。


    补充:动态语言 vs 静态语言
    Java不是动态语言, 但Java可以称之为“准动态语言” 。 即Java有一定的动态性, 我们可以利用反射机制、 字节码操作获得类似动态语言的特性。

     Java反射机制提供的功能
            在运行时判断任意一个对象所属的类
            在运行时构造任意一个类的对象
            在运行时判断任意一个类所具有的成员变量和方法
            在运行时获取泛型信息
            在运行时调用任意一个对象的成员变量和方法
            在运行时处理注解
            生成动态代理

    反射相关地API

    java.lang.Class:代表一个类
            java.lang.reflect.Method:代表类的方法
            java.lang.reflect.Field:代表类的成员变量
            java.lang.reflect.Constructor:代表类的构造器 

     二、理解Class类并获取Class的实例

    创建一个person类

    1. public class Person {
    2. private String name;
    3. public int age;
    4. @Override
    5. public String toString() {
    6. return "Person{" +
    7. "name='" + name + '\'' +
    8. ", age=" + age +
    9. '}';
    10. }
    11. public String getName() {
    12. return name;
    13. }
    14. public void setName(String name) {
    15. this.name = name;
    16. }
    17. public int getAge() {
    18. return age;
    19. }
    20. public void setAge(int age) {
    21. this.age = age;
    22. }
    23. public Person(String name, int age) {
    24. this.name = name;
    25. this.age = age;
    26. }
    27. private Person(String name) {
    28. this.name = name;
    29. }
    30. public Person() {
    31. System.out.println("Person()");
    32. }
    33. public void show(){
    34. System.out.println("你好,我是催逝员");
    35. }
    36. private String showNation(String nation){
    37. System.out.println("我的国籍是:" + nation);
    38. return nation;
    39. }
    40. }

    反射之前对于person的操作

    1. //反射之前,对于Person的操作
    2. @Test
    3. public void test1() {
    4. //1.创建Person类的对象
    5. Person p1 = new Person("催逝员", 12);
    6. //2.通过对象,调用其内部的属性、方法
    7. p1.age = 10;
    8. System.out.println(p1.toString());
    9. p1.show();
    10. //在Person类外部,不可以通过Person类的对象调用其内部私有结构。
    11. //比如:name、showNation()以及私有的构造器
    12. }

     使用反射机制之后对于person的操作

    1. //反射之后,对于Person的操作
    2. @Test
    3. public void test2() throws Exception{
    4. //反射的源头,只有当创建了如下面这行代码中的clazz之后才能使用反射
    5. //从这里我们可以看出类的本身也是一个对象,这正好验证了我们所说的王五皆对象
    6. Class clazz = Person.class;
    7. //1.通过反射,创建Person类的对象
    8. //获取构造器
    9. Constructor cons = clazz.getConstructor(String.class,int.class);
    10. Object obj = cons.newInstance("催逝员", 42);
    11. Person p = (Person) obj;
    12. System.out.println(p.toString());
    13. //2.通过反射,调用对象指定的属性、方法
    14. //调用属性
    15. Field age = clazz.getDeclaredField("age");
    16. age.set(p,52);
    17. System.out.println(p.toString());
    18. //调用方法
    19. Method show = clazz.getDeclaredMethod("show");
    20. show.invoke(p);
    21. System.out.println("*******************************");
    22. //通过反射,可以调用Person类的私有结构的。比如:私有的构造器、方法、属性
    23. //调用私有的构造器
    24. Constructor cons1 = clazz.getDeclaredConstructor(String.class);
    25. cons1.setAccessible(true);
    26. Person p1 = (Person) cons1.newInstance("催逝员");
    27. System.out.println(p1);
    28. //调用私有的属性
    29. Field name = clazz.getDeclaredField("name");
    30. name.setAccessible(true);
    31. name.set(p1,"王大队长");
    32. System.out.println(p1);
    33. //调用私有的方法
    34. Method showNation = clazz.getDeclaredMethod("showNation", String.class);
    35. showNation.setAccessible(true);
    36. String nation = (String) showNation.invoke(p1,"中国");//相当于String nation = p1.showNation("中国")
    37. System.out.println(nation);
    38. }

    什么情况下我们需要用反射?

    这里我们举一个例子:比方说我们有一个服务器正在运行,提供给用户访问一个具体的网页。用户可能需要登录或者是注册,我们在开服务器之前是不知道用户要干什么的。当用户给我们发送URL,解析之后知道用户想要使用登录功能,在这个时候,就通过反射造login类的方法,如果是想注册,就创建一个注册的对象。(这里的过程都是动态的,不是直接在Java代码中写死一定要创建哪一个对象的)

    既然反射性可以获取私有结构,那么反射机制和面向对象的封装性是否矛盾?

    封装性表示的是建议你去调用那些公有的,私有的功能一般是被公有的方法所调用的。

    而反射是能不能调用的问题。封装性是建议你不要调用私有的方法,但是如果真的想要调用,也是可以调用的

    获取class实例的方式

    1. 关于java.lang.Class类的理解
    2. 1.类的加载过程:
    3. 程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。
    4. 接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件
    5. 加载到内存中。此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此
    6. 运行时类,就作为Class的一个实例。
    7. 2.换句话说,Class的实例就对应着一个运行时类。
    8. 3.加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式
    9. 来获取此运行时类。
    1. @Test
    2. public void test3() throws ClassNotFoundException {
    3. //方式一:调用运行时类的属性:.class
    4. Class clazz1 = Person.class;
    5. System.out.println(clazz1);
    6. //方式二:通过运行时类的对象,调用getClass()
    7. Person p1 = new Person();
    8. Class clazz2 = p1.getClass();
    9. System.out.println(clazz2);
    10. //方式三:调用Class的静态方法:forName(String classPath)
    11. Class clazz3 = Class.forName("com.zhuyuan.java.Person");
    12. // clazz3 = Class.forName("java.lang.String");
    13. System.out.println(clazz3);
    14. System.out.println(clazz1 == clazz2);
    15. System.out.println(clazz1 == clazz3);
    16. //方式四:使用类的加载器:ClassLoader (了解)
    17. ClassLoader classLoader = ReflectionTest.class.getClassLoader();
    18. Class clazz4 = classLoader.loadClass("com.zhuyuan.java.Person");
    19. System.out.println(clazz4);
    20. System.out.println(clazz1 == clazz4);
    21. }

     那些类型可以有class对象?

    (1) class:外部类, 成员(成员内部类, 静态内部类), 局部内部类, 匿名内部类
    (2) interface: 接口
    (3) []:数组
    (4) enum:枚举
    (5) annotation:注解@interface
    (6) primitive type:基本数据类型
    (7) void

    1. //Class实例可以是哪些结构的说明:
    2. @Test
    3. public void test4(){
    4. Class c1 = Object.class;
    5. Class c2 = Comparable.class;
    6. Class c3 = String[].class;
    7. Class c4 = int[][].class;
    8. Class c5 = ElementType.class;
    9. Class c6 = Override.class;
    10. Class c7 = int.class;
    11. Class c8 = void.class;
    12. Class c9 = Class.class;
    13. int[] a = new int[10];
    14. int[] b = new int[100];
    15. Class c10 = a.getClass();
    16. Class c11 = b.getClass();
    17. // 只要数组的元素类型与维度一样,就是同一个Class
    18. System.out.println(c10 == c11);
    19. }

     

    三、类的加载与ClassLoader的理解 

     类的加载过程

    当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化。

    类的加载(load):将类的class文件读入内存,并为之创建一个java.lang.Class对象。此过程由类加载器完成。

    类的链接(link):将类的二进制数据合并到JRE中

    类的初始化(initialize):JVM负责对类进行初始化

    加载:

            将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时
    数据结构,然后生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问
    入口(即引用地址)。所有需要访问和使用类数据只能通过这个Class对象。这个加载的
    过程需要类加载器参与。

    链接:

            将Java类的二进制代码合并到JVM的运行状态之中的过程。
            验证:确保加载的类信息符合JVM规范,例如:以cafe开头,没有安全方面的问题
            准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存
    都将在方法区中进行分配。
            解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。

    初始化:
            执行类构造器()方法的过程。 类构造器()方法是由编译期自动收集类中
    所有类变量的赋值动作和静态代码块中的语句合并产生的。 (类构造器是构造类信
    息的,不是构造该类对象的构造器) 。
            当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类
    的初始化。
            虚拟机会保证一个类的()方法在多线程环境中被正确加锁和同步。 

    类的加载器的作用:

    类加载的作用: 将class文件字节码内容加载到内存中, 并将这些静态数据转换成方法区的运行时数据结构, 然后在堆中生成一个代表这个类的java.lang.Class对象, 作为方法区中类数据的访问入口。
    类缓存: 标准的JavaSE类加载器可以按要求查找类, 但一旦某个类被加载到类加载器中, 它将维持加载(缓存) 一段时间。 不过JVM垃圾回收机制可以回收这些Class对象

    了解classloader

    类加载器作用是用来把类(class)装载进内存的。 JVM 规范定义了如下类型的类的加载器。

    引导类加载器:用C++编写的,是JVM自带的类加载器, 负责Java平台核心库,用来装载核心类库。该加载器无法直接获取。

    扩展类加载器:负责jre/lib/ext目录下的jar包或 –D java.ext.dirs 指定目录下的jar包装入工作库

    系统类加载器:负责java –classpath 或 –Djava.class.path所指的目录下的类与jar包装入工
    作 ,是最常用的加载器

    1. @Test
    2. public void test1(){
    3. //对于自定义类,使用系统类加载器进行加载
    4. ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
    5. System.out.println(classLoader);
    6. //调用系统类加载器的getParent():获取扩展类加载器
    7. ClassLoader classLoader1 = classLoader.getParent();
    8. System.out.println(classLoader1);
    9. //调用扩展类加载器的getParent():无法获取引导类加载器
    10. //引导类加载器主要负责加载java的核心类库,无法加载自定义类的。
    11. ClassLoader classLoader2 = classLoader1.getParent();
    12. System.out.println(classLoader2);
    13. ClassLoader classLoader3 = String.class.getClassLoader();
    14. System.out.println(classLoader3);
    15. }

    使用classloader读取配置文件。 

    1. @Test
    2. public void test2() throws Exception {
    3. Properties pros = new Properties();
    4. //此时的文件默认在当前的module下。
    5. //读取配置文件的方式一:
    6. // FileInputStream fis = new FileInputStream("jdbc.properties");
    7. // FileInputStream fis = new FileInputStream("src\\jdbc1.properties");
    8. // pros.load(fis);
    9. //读取配置文件的方式二:使用ClassLoader
    10. //配置文件默认识别为:当前module的src下
    11. ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
    12. InputStream is = classLoader.getResourceAsStream("jdbc1.properties");
    13. pros.load(is);
    14. String user = pros.getProperty("user");
    15. String password = pros.getProperty("password");
    16. System.out.println("user = " + user + ",password = " + password);
    17. }

    四、创建运行时类的对象

    1. @Test
    2. public void test1() throws IllegalAccessException, InstantiationException {
    3. Class clazz = Person.class;
    4. /*
    5. newInstance():调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参的构造器。
    6. 要想此方法正常的创建运行时类的对象,要求:
    7. 1.运行时类必须提供空参的构造器
    8. 2.空参的构造器的访问权限得够。通常,设置为public。
    9. 在javabean中要求提供一个public的空参构造器。原因:
    10. 1.便于通过反射,创建运行时类的对象
    11. 2.便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器
    12. */
    13. //从我们结果来看,我们的newInstance在这里调用了我们Person的空参构造器
    14. Person obj = clazz.newInstance();
    15. System.out.println(obj);
    16. }

     反射的动态性

    在下面的代码中,我们并没有直接指明我们生成的对象是什么,而是通过一个随机的方式生成不同的类。

    1. //体会反射的动态性
    2. @Test
    3. public void test2(){
    4. for(int i = 0;i < 5;i++){
    5. int num = new Random().nextInt(3);//0,1,2
    6. String classPath = "";
    7. switch(num){
    8. case 0:
    9. classPath = "java.util.Date";
    10. break;
    11. case 1:
    12. classPath = "java.lang.Object";
    13. break;
    14. case 2:
    15. classPath = "com.zhuyuan.java.Person";
    16. break;
    17. }
    18. try {
    19. Object obj = getInstance(classPath);
    20. System.out.println(obj);
    21. } catch (Exception e) {
    22. e.printStackTrace();
    23. }
    24. }
    25. }
    26. /*
    27. 创建一个指定类的对象。
    28. classPath:指定类的全类名
    29. */
    30. public Object getInstance(String classPath) throws Exception {
    31. Class clazz = Class.forName(classPath);
    32. return clazz.newInstance();
    33. }

    五、获取运行时类的完整结构

    创建一个复杂的people类

    1. public class Creature implements Serializable {
    2. private char gender;
    3. public double weight;
    4. private void breath(){
    5. System.out.println("生物呼吸");
    6. }
    7. public void eat(){
    8. System.out.println("生物吃东西");
    9. }
    10. }
    1. public interface MyInterface {
    2. void info();
    3. }

    1. @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
    2. @Retention(RetentionPolicy.RUNTIME)
    3. public @interface MyAnnotation {
    4. String value() default "hello";
    5. }
    1. @MyAnnotation(value="hi")
    2. public class Person extends Creature implements Comparable,MyInterface{
    3. private String name;
    4. int age;
    5. public int id;
    6. public Person(){}
    7. @MyAnnotation(value="abc")
    8. private Person(String name){
    9. this.name = name;
    10. }
    11. Person(String name,int age){
    12. this.name = name;
    13. this.age = age;
    14. }
    15. @MyAnnotation
    16. private String show(String nation){
    17. System.out.println("我的国籍是:" + nation);
    18. return nation;
    19. }
    20. public String display(String interests,int age) throws NullPointerException,ClassCastException{
    21. return interests + age;
    22. }
    23. @Override
    24. public void info() {
    25. System.out.println("我是一个人");
    26. }
    27. @Override
    28. public int compareTo(String o) {
    29. return 0;
    30. }
    31. private static void showDesc(){
    32. System.out.println("我是一个可爱的人");
    33. }
    34. @Override
    35. public String toString() {
    36. return "Person{" +
    37. "name='" + name + '\'' +
    38. ", age=" + age +
    39. ", id=" + id +
    40. '}';
    41. }
    42. }

    获取上述运行时类的属性结构

    1. @Test
    2. public void test1(){
    3. Class clazz = Person.class;
    4. //获取属性结构
    5. //getFields():获取当前运行时类及其父类中声明为public访问权限的属性
    6. Field[] fields = clazz.getFields();
    7. for(Field f : fields){
    8. System.out.println(f);
    9. }
    10. System.out.println();
    11. //getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
    12. Field[] declaredFields = clazz.getDeclaredFields();
    13. for(Field f : declaredFields){
    14. System.out.println(f);
    15. }
    16. }

     获取权限修饰符,数据类型,变量名

    1. //权限修饰符 数据类型 变量名
    2. @Test
    3. public void test2(){
    4. Class clazz = Person.class;
    5. Field[] declaredFields = clazz.getDeclaredFields();
    6. for(Field f : declaredFields){
    7. //1.权限修饰符
    8. int modifier = f.getModifiers();
    9. System.out.print(Modifier.toString(modifier) + "\t");
    10. //2.数据类型
    11. Class type = f.getType();
    12. System.out.print(type.getName() + "\t");
    13. //3.变量名
    14. String fName = f.getName();
    15. System.out.print(fName);
    16. System.out.println();
    17. }
    18. }

     获取运行时类中的方法

    1. @Test
    2. public void test1(){
    3. Class clazz = Person.class;
    4. //getMethods():获取当前运行时类及其所有父类中声明为public权限的方法
    5. Method[] methods = clazz.getMethods();
    6. for(Method m : methods){
    7. System.out.println(m);
    8. }
    9. System.out.println();
    10. //getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)
    11. Method[] declaredMethods = clazz.getDeclaredMethods();
    12. for(Method m : declaredMethods){
    13. System.out.println(m);
    14. }
    15. }

     

     获取方法的具体结构

    权限修饰符,返回值类型,方法名(参数类型1 形参名1,……)throws xxxException{}

    1. @Test
    2. public void test2(){
    3. Class clazz = Person.class;
    4. Method[] declaredMethods = clazz.getDeclaredMethods();
    5. for(Method m : declaredMethods){
    6. //1.获取方法声明的注解
    7. Annotation[] annos = m.getAnnotations();
    8. for(Annotation a : annos){
    9. System.out.println(a);
    10. }
    11. //2.权限修饰符
    12. System.out.print(Modifier.toString(m.getModifiers()) + "\t");
    13. //3.返回值类型
    14. System.out.print(m.getReturnType().getName() + "\t");
    15. //4.方法名
    16. System.out.print(m.getName());
    17. System.out.print("(");
    18. //5.形参列表
    19. Class[] parameterTypes = m.getParameterTypes();
    20. if(!(parameterTypes == null && parameterTypes.length == 0)){
    21. for(int i = 0;i < parameterTypes.length;i++){
    22. //如果是最后一个的话就不要打印出逗号了。
    23. if(i == parameterTypes.length - 1){
    24. System.out.print(parameterTypes[i].getName() + " args_" + i);
    25. break;
    26. }
    27. System.out.print(parameterTypes[i].getName() + " args_" + i + ",");
    28. }
    29. }
    30. System.out.print(")");
    31. //6.抛出的异常
    32. Class[] exceptionTypes = m.getExceptionTypes();
    33. if(exceptionTypes.length > 0){
    34. System.out.print("throws ");
    35. for(int i = 0;i < exceptionTypes.length;i++){
    36. if(i == exceptionTypes.length - 1){
    37. System.out.print(exceptionTypes[i].getName());
    38. break;
    39. }
    40. System.out.print(exceptionTypes[i].getName() + ",");
    41. }
    42. }
    43. System.out.println();
    44. }
    45. }

     获取构造器结构

    1. @Test
    2. public void test1(){
    3. Class clazz = Person.class;
    4. //getConstructors():获取当前运行时类中声明为public的构造器
    5. Constructor[] constructors = clazz.getConstructors();
    6. for(Constructor c : constructors){
    7. System.out.println(c);
    8. }
    9. System.out.println();
    10. //getDeclaredConstructors():获取当前运行时类中声明的所有的构造器
    11. Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
    12. for(Constructor c : declaredConstructors){
    13. System.out.println(c);
    14. }
    15. }

     获取运行时类的父类 

    1. @Test
    2. public void test2(){
    3. Class clazz = Person.class;
    4. Class superclass = clazz.getSuperclass();
    5. System.out.println(superclass);
    6. }

     获取运行时类带泛型的父类

    1. @Test
    2. public void test3(){
    3. Class clazz = Person.class;
    4. Type genericSuperclass = clazz.getGenericSuperclass();
    5. System.out.println(genericSuperclass);
    6. }

     获取运行时类的带泛型的父类的泛型

    1. @Test
    2. public void test4(){
    3. Class clazz = Person.class;
    4. Type genericSuperclass = clazz.getGenericSuperclass();
    5. //带参数的类型
    6. ParameterizedType paramType = (ParameterizedType) genericSuperclass;
    7. //获取泛型类型
    8. Type[] actualTypeArguments = paramType.getActualTypeArguments();
    9. // System.out.println(actualTypeArguments[0].getTypeName());
    10. System.out.println(((Class)actualTypeArguments[0]).getName());
    11. }

    获取运行时类实现的接口

    1. @Test
    2. public void test5(){
    3. Class clazz = Person.class;
    4. Class[] interfaces = clazz.getInterfaces();
    5. for(Class c : interfaces){
    6. System.out.println(c);
    7. }
    8. System.out.println();
    9. //获取运行时类的父类实现的接口
    10. Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
    11. for(Class c : interfaces1){
    12. System.out.println(c);
    13. }
    14. }

     

     获取运行时类所在的包

    1. @Test
    2. public void test6(){
    3. Class clazz = Person.class;
    4. Package pack = clazz.getPackage();
    5. System.out.println(pack);
    6. }

     获取运行时类声明的注释

    1. @Test
    2. public void test7(){
    3. Class clazz = Person.class;
    4. Annotation[] annotations = clazz.getAnnotations();
    5. for(Annotation annos : annotations){
    6. System.out.println(annos);
    7. }
    8. }

    六、调用运行时类的指定结构

     1.如何操作运行时类中的指定的属性

    方式一: 采用实例化验一个对象之后使用getfield的方式,但是这种方式获取到的属性必须是public属性的,比较具有局限性,所以不常用

    1. @Test
    2. public void testField() throws Exception {
    3. Class clazz = Person.class;
    4. //创建运行时类的对象
    5. Person p = (Person) clazz.newInstance();
    6. //获取指定的属性:要求运行时类中属性声明为public
    7. //通常不采用此方法
    8. Field id = clazz.getField("id");
    9. /*
    10. 设置当前属性的值
    11. set():参数1:指明设置哪个对象的属性 参数2:将此属性值设置为多少
    12. */
    13. id.set(p,1001);
    14. /*
    15. 获取当前属性的值
    16. get():参数1:获取哪个对象的当前属性值
    17. */
    18. int pId = (int) id.get(p);
    19. System.out.println(pId);
    20. }

     方式二:下面的方法在声明了setAccessible之后就能够保证是可访问的,不管是不是公有的类,所以用途比第一种方法更加广泛,推荐使用。

    1. @Test
    2. public void testField1() throws Exception {
    3. Class clazz = Person.class;
    4. //创建运行时类的对象
    5. Person p = (Person) clazz.newInstance();
    6. //1. getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
    7. Field name = clazz.getDeclaredField("name");
    8. //2.保证当前属性是可访问的
    9. name.setAccessible(true);
    10. //3.获取、设置指定对象的此属性值
    11. name.set(p,"催逝员");
    12. System.out.println(name.get(p));
    13. }

     2.如何操作运行时类中的指定的方法

    1. @Test
    2. public void testMethod() throws Exception {
    3. Class clazz = Person.class;
    4. //创建运行时类的对象
    5. Person p = (Person) clazz.newInstance();
    6. /*
    7. 1.获取指定的某个方法
    8. getDeclaredMethod():参数1 :指明获取的方法的名称 参数2:指明获取的方法的形参列表
    9. */
    10. Method show = clazz.getDeclaredMethod("show", String.class);
    11. //2.保证当前方法是可访问的
    12. show.setAccessible(true);
    13. /*
    14. 3. 调用方法的invoke():参数1:方法的调用者 参数2:给方法形参赋值的实参
    15. invoke()的返回值即为对应类中调用的方法的返回值。
    16. */
    17. Object returnValue = show.invoke(p,"CHN"); //String nation = p.show("CHN");
    18. System.out.println(returnValue);
    19. System.out.println("*************如何调用静态方法*****************");
    20. // private static void showDesc()
    21. Method showDesc = clazz.getDeclaredMethod("showDesc");
    22. showDesc.setAccessible(true);
    23. //如果调用的运行时类中的方法没有返回值,则此invoke()返回null
    24. // Object returnVal = showDesc.invoke(null);
    25. Object returnVal = showDesc.invoke(Person.class);
    26. System.out.println(returnVal);//null
    27. }

     调用运行时类中指定的构造器

    1. @Test
    2. public void testConstructor() throws Exception {
    3. Class clazz = Person.class;
    4. //private Person(String name)
    5. /*
    6. 1.获取指定的构造器
    7. getDeclaredConstructor():参数:指明构造器的参数列表
    8. */
    9. Constructor constructor = clazz.getDeclaredConstructor(String.class);
    10. //2.保证此构造器是可访问的
    11. constructor.setAccessible(true);
    12. //3.调用此构造器创建运行时类的对象
    13. Person per = (Person) constructor.newInstance("催逝员");
    14. System.out.println(per);
    15. }

     七、动态代理

            使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。
            之前为大家讲解过代理机制的操作,属于静态代理,特征是代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。 最好可以通过一个代理类完成全部的代理功能

    下面是一个简单的静态代理

    特点:代理类和被代理类在编译期间就被确定下来了。

    1. interface ClothFactory{
    2. void produceCloth();
    3. }
    4. //代理类
    5. class ProxyClothFactory implements ClothFactory{
    6. private ClothFactory factory;//用被代理类对象进行实例化
    7. public ProxyClothFactory(ClothFactory factory){
    8. this.factory = factory;
    9. }
    10. @Override
    11. public void produceCloth() {
    12. System.out.println("代理工厂做一些准备工作");
    13. factory.produceCloth();
    14. System.out.println("代理工厂做一些后续的收尾工作");
    15. }
    16. }
    17. //被代理类
    18. class NikeClothFactory implements ClothFactory{
    19. @Override
    20. public void produceCloth() {
    21. System.out.println("Nike工厂生产一批运动服");
    22. }
    23. }
    24. public class StaticProxyTest {
    25. public static void main(String[] args) {
    26. //创建被代理类的对象
    27. ClothFactory nike = new NikeClothFactory();
    28. //创建代理类的对象
    29. ClothFactory proxyClothFactory = new ProxyClothFactory(nike);
    30. proxyClothFactory.produceCloth();
    31. }
    32. }

    动态代理的简单举例

    1. interface Human{
    2. String getBelief();
    3. void eat(String food);
    4. }
    5. //被代理类
    6. class SuperMan implements Human{
    7. @Override
    8. public String getBelief() {
    9. return "I believe I can fly!";
    10. }
    11. @Override
    12. public void eat(String food) {
    13. System.out.println("我喜欢吃" + food);
    14. }
    15. }
    16. class HumanUtil{
    17. public void method1(){
    18. System.out.println("====================通用方法一====================");
    19. }
    20. public void method2(){
    21. System.out.println("====================通用方法二====================");
    22. }
    23. }
    24. /*
    25. 要想实现动态代理,需要解决的问题?
    26. 问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。
    27. 问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。
    28. */
    29. class ProxyFactory{
    30. //调用此方法,返回一个代理类的对象。解决问题一
    31. public static Object getProxyInstance(Object obj){//obj:被代理类的对象
    32. MyInvocationHandler handler = new MyInvocationHandler();
    33. handler.bind(obj);
    34. //使用反射创建一个被代理类的对象,第一个参数为类的加载器,第二个参数为接口,
    35. return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
    36. }
    37. }
    38. class MyInvocationHandler implements InvocationHandler{
    39. private Object obj;//需要使用被代理类的对象进行赋值
    40. public void bind(Object obj){
    41. this.obj = obj;
    42. }
    43. //当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke()
    44. //将被代理类要执行的方法a的功能就声明在invoke()中
    45. @Override
    46. //第一个参数为代理类的对象,第二个参数为代理类的对象所调用的方法
    47. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    48. HumanUtil util = new HumanUtil();
    49. util.method1();
    50. //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
    51. //obj:被代理类的对象
    52. Object returnValue = method.invoke(obj,args);
    53. util.method2();
    54. //上述方法的返回值就作为当前类中的invoke()的返回值。
    55. return returnValue;
    56. }
    57. }
    58. public class ProxyTest {
    59. public static void main(String[] args) {
    60. SuperMan superMan = new SuperMan();
    61. //proxyInstance:代理类的对象
    62. Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
    63. //当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法
    64. String belief = proxyInstance.getBelief();
    65. System.out.println(belief);
    66. proxyInstance.eat("四川麻辣烫");
    67. System.out.println("*****************************");
    68. NikeClothFactory nikeClothFactory = new NikeClothFactory();
    69. ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory);
    70. proxyClothFactory.produceCloth();
    71. }
    72. }

     

     

  • 相关阅读:
    C语言IO操作fread/fwrite/fflush
    qt实现sqlite数据库文件与csv文件相互转换
    国内Api行业,可以内卷到什么程度?
    结构体(Struct)
    robotframework+selenium+ride测试环境搭建和如何把环境迁移到不能连接互联网的环境
    AWS入门实践-S3 精细化权限控制
    Vue 2 生命周期与 Vue 3 生命周期:介绍与差别对比
    【校招VIP】计算机网络之TCP/IP模型归纳
    2012年认证杯SPSSPRO杯数学建模B题(第一阶段)减缓热岛效应全过程文档及程序
    LVS负载均衡群集
  • 原文地址:https://blog.csdn.net/weixin_62684026/article/details/125988716