Java反射详解

2021-05-29 23:04

阅读:505

标签:变量   ring   编程   一段   methods   示例   执行方法   构造器   符号   

反射

Java反射机制概述

什么是反射

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

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

技术图片

动态语言与静态语言

动态语言

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

主要动态语言: Object-C. C#、JavaScript. PHP、Python、 Erlang

静态语言

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

关于Java的语言特性

Java不是动态语言,但Java可以称之为“准动态语言”。即Java有 ”定 的动态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性。Java的动态性让编程的时候更加灵活!

Java反射机制提供的功能

  • 在运往时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时获取泛型信息
  • 在运行时调用任意一个对象的成员变量和方法
  • 在运行时处理注解
  • 生成动态代理

反射相关的主要API

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

反射之前对Person类的操作

  1. 定义Person类
class Person{
    public int age;
    private String name;


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

    private Person(String name){
        this.name=name;
    }

    public Person() {
    }

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }

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

    public void show(){
        System.out.println("你好,我是"+this.name);
    }

    private String showNation(String nation){
        System.out.println("我的国籍是"+nation);
        return nation;
    }
}
  1. 我们可以访问其公有的属性和方法
public class Demo01 {
    public static void main(String[] args) {
        //1. 创建Person类对象
        Person p1=new Person(12,"Tom");

        //2. 通过对象,调用其内部的属性,方法
        p1.age=10;
        System.out.println(p1.toString());
        p1.show();
        //在Person类的内部,不可以通过Person类对象调用其内部的结构
        //比如:name、showNation()以及其私有的构造函数
    }
}

使用反射可以对一个Person类的操作

  1. 通过反射调用对应类的构造器
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    Class clazz=Person.class;//获取Person类的class类
    Constructor cons=clazz.getConstructor(int.class,String.class);//通过反射获取构造器
    Object obj=cons.newInstance(12,"Tom");//通过发射调用构造器创建对象
    System.out.println(obj.toString());

}

效果:

技术图片

  1. 通过反射访问类的属性与方法
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
    //通过反射,调用对象的指定的属性、方法
    Class clazz=Person.class;
    Constructor cons=clazz.getConstructor(int.class,String.class);
    Object obj=cons.newInstance(12,"Tom");
    System.out.println(obj.toString());
    Field name = clazz.getDeclaredField("age");
    name.set(obj,10);
    System.out.println(obj.toString());
}

效果:

技术图片

  1. 通过反射调用方法
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
    //通过反射,调用对象的指定的属性、方法
    Class clazz=Person.class;
    Constructor cons=clazz.getConstructor(int.class,String.class);
    Object obj=cons.newInstance(12,"Tom");
    System.out.println(obj.toString());
    Field name = clazz.getDeclaredField("age");
    name.set(obj,10);
    System.out.println(obj.toString());
    //反射调用方法
    Method show = clazz.getDeclaredMethod("show");
    show.invoke(obj);


}

效果:

技术图片

  1. 通过反射,可以调用Person类的私有结构的。比如:私有的构造器、方法、属性
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
    //通过反射,调用对象的指定的属性、方法
    Class clazz=Person.class;
    Constructor cons=clazz.getConstructor(int.class,String.class);
    Object obj=cons.newInstance(12,"Tom");
    System.out.println(obj.toString());
    Field name = clazz.getDeclaredField("age");
    name.set(obj,10);
    System.out.println(obj.toString());
    //反射调用方法
    Method show = clazz.getDeclaredMethod("show");
    show.invoke(obj);
    //通过反射。可以地哦啊用Person类的私有结构,比如私有的构造器、方法、属性
    Constructor constructor=clazz.getDeclaredConstructor(String.class);
    constructor.setAccessible(true);
    Person p2=(Person) constructor.newInstance("Jerry");
    System.out.println(p2);


}

效果:

技术图片

  1. 修改私有属性
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
    Person p1=new Person(12,"dreamcold");
    Class clazz=Person.class;
    //调用私有属性
    Field name = clazz.getDeclaredField("name");
    name.setAccessible(true);
    name.set(p1,"HanMeiMei");
    System.out.println(p1);
}

效果:

技术图片

  1. 调用私有方法
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
    Person p1=new Person(12,"dreamcold");
    Class clazz=Person.class;
    //调用私有属性
    Field name = clazz.getDeclaredField("name");
    name.setAccessible(true);
    name.set(p1,"HanMeiMei");
    System.out.println(p1);
    //调用私有方法
    Method showNation = clazz.getDeclaredMethod("showNation", String.class);
    showNation.setAccessible(true);
    showNation.invoke(p1,"中国");//相当于p1.showNation("中国")
    
}

效果:

技术图片

如何看待反射破坏封装性

  1. 疑问:通过直接new的方式或反射的方式都可以调用公共的结构,开发中到底用那个?
  • 建议:直接new的方式
  • 什么时候会使用:反射的方式。 反射的特征:动态性,比如servlet在接收浏览器路径的时候,根据不同的路径造不同对应的对象
  1. 疑问:反射机制与面向对象中的封装性是不是矛盾的?如何看待两个技术?
  • 不矛盾,封装性相当于建议你怎么调,但是实际还是可以通过反射来访问私有属性的

理解Class类并获取Class实例

关于java.lang.class的理解

  1. 类的加载过程:
    程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾),接着我们使用Java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。相当于将某个字节码文件加载到内存中。加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例。
  2. 万事万物皆对象?对象. xxx, File,URL,反射,前端、数据库操作,换句话说,Class的实例就对应着一个运行时类。

class实例的获取

加载到内存中的运行时类,会缓存- -定的时间。在此时间之内,我们可以通过不同的方式
来获取此运行时类。获取Class对象的三种方式:

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, ClassNotFoundException {
        //方式一:调用运行时类的属性: .class
        Class clazz1=Person.class;
        System.out.println(clazz1);
        //方式二:通过运行时类的对象
        Person p1=new Person();
        Class clazz2=p1.getClass();
        System.out.println(clazz2);
        //方式三:调用Class的静态方法: forName(String classPath)
        Class clazz3=Class.forName("com.dreamcold.thread.NumThread");
        System.out.println(clazz3);
        //方式四:使用类加载器: ClassLoader
        ClassLoader classLoader=Person.class.getClassLoader();
        Class claszz4=classLoader.loadClass("com.dreamcold.reflect.Person");
        System.out.println(claszz4);
        System.out.println(clazz1==claszz4);

    }

效果:

技术图片

那些可以有Class对象

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

Class c1 = object. class;
Class c2 = Comparable. class;
Class c3 = String[] .class;
Class c4 = int[][] . class;
Class c5 = ElemenitType. class;
Class c6 = Override. class;
Class c7 = int.class;
Class C8 = void.class;
Class c9 = Class. class;
int[] a = new int[10] ;
int[] b = new int[100] ;
Class c10 = a.getClass();
Class c11 =b. getClass();
//只要元素类型与维度一样,就是同一个Class
System. out. println(c10 == c11);

类的加载过程

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

技术图片

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

  • 链接:将Java类的二 进制代码合并到JVM的运行状态之中的过程。
    • 验证:确保加载的类信息符合JMM规范,例如:以cafe开头, 没有安全方面的问题
    • 准备:正式为类变量(static) 分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。
    • 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
  • 初始化:
    • 执行类构造器()方法的过程。类构造器()方法是由编译期自动收集类中
      所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造 器是构造类信
      息的,不是构造该类对象的构造器)。
    • 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类
      的初始化。
    • 虚拟机会保证一个类的()方 法在多线程环境中被正确加锁和同步。
pub1ic class ClassLoadingTest {
public static void main(String[] args) {
	System. out .print1n(A.m);
}
class A {
	static{
		m =300;
	}
	static int m = 100;
//第二步:链接结束后m=6
//第三步:初始化后,m的值由()方法执行决定
//这个A的类构造器()方法由类变量的赋值和静态代码块中的语句按照顺序合并产生,类似于
    (){
    m = 300;
    m =100;
	}
}

类的加载与ClassLoader的理解

类加载器的作用

技术图片

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

了解Classloader

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

技术图片

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

  • 扩展类加战器:负责e/ibext目录下的ar包或-java.ext.dirs指定目录下的ar包装入工作库系统类加载器:负责java- classpalh或

  • java.class path所指的目录下的类与jar包装入工作,是最常用的加戟器

package com.dreamcold.reflect;

public class ClassLoaderTest {
    public static void main(String[] args) {
        //对于自定义类,使用系统类加载器进行加载
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        System.out.println(classLoader);
        //调用系统类加载器的getParent():获取扩展类加载器
        ClassLoader parent = classLoader.getParent();
        System.out.println(parent);
        //引导类加载器主要负责加载java的核心类库,无法加载自定义类的。
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);
    }
}

效果:

技术图片

Properties:用来读取配置文件

  1. 读取配置文件的方式一
package com.dreamcold.reflect;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;

public class Demo02 {
    public static void main(String[] args) throws IOException {
        Properties pros=new Properties();
        FileInputStream fis=new FileInputStream("classload.properties");
        pros.load(fis);
        String name = pros.getProperty("name");
        String age=pros.getProperty("age");
        System.out.println("name = "+name+",password = "+age);
    }
}

效果:

技术图片

  1. 读取配置文件的方式二
package com.dreamcold.reflect;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class Demo02 {
    public static void main(String[] args) throws IOException {
        Properties pros=new Properties();
//        FileInputStream fis=new FileInputStream("classload.properties");
//        pros.load(fis);
        ClassLoader classLoader=ClassLoaderTest.class.getClassLoader();
        InputStream resourceAsStream = classLoader.getResourceAsStream("classload.properties");
        pros.load(resourceAsStream);
        String name = pros.getProperty("name");
        String age=pros.getProperty("age");
        System.out.println("name = "+name+",password = "+age);
    }
}

效果:

技术图片

原因:配置文件默认识别为:当前module的src下,配置文件放在如下的位置:

技术图片

再次运行:

技术图片

创建运行时类的对象

newInstance():调用此方法,创建对应的运行时类的对象

要想此方法正常的创建运行时类的对象,要求:

  1. 运行时类必须提供空参的构造器
  2. 空参的构造器的访问权限得够。通常,设置为public。

注意:

在javabean中要求提供一public 的空参构造器。原因:

  1. 便于通过反射,创建运行时类的对象
  2. 便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器
package com.dreamcold.reflect;

public class NewInstanceTest {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException {
        Class claz=Person.class;
        Object o = claz.newInstance();
        System.out.println(o.toString());
    }
}

效果:

技术图片

举例反射是动态语言的关键

该例子会随机创建对应的不同对象,体现了反射的动态性

package com.dreamcold.reflect;

import java.util.Random;

public class Demo03 {
    /**
     * 创建- -个指定类的对象。
     * classPath:指定类的全类名
     * @param args
     * @throws ClassNotFoundException
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        int num=new Random().nextInt(3);//0,1,2
        String classPath="";
        switch (num){
            case 0:
                classPath="java.util.Date";
                break;
            case 1:
                classPath="java.sql.Date";
                break;
            case 2:
                classPath="com.dreamcold.reflect.Person";
                break;
        }
        try {
            Object obj=newInstance(classPath);
            System.out.println(obj);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private static Object newInstance(String classPath) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        return Class.forName(classPath).newInstance();
    }
}

获取运行时类的完整结构

前提准备

反射提供了很多丰富的接口来帮助我们获取类的相应信息:

Person类

package com.dreamcold.reflect2;

@MyAnnoation("hi")
public class Person extends Creature implements  Comparable ,MyInterface{
    private String name;
    int age;
    public int id;

    @MyAnnoation("abc")
    public Person(){

    }

    private Person(String name){
        this.name=name;
    }

    public Person(String name,int age){
        this.name=name;
        this.age=age;
    }
    @MyAnnoation
    private String show(String nation){
        System.out.println("我的国籍是: "+nation);
        return nation;
    }

    public String display(String insterest){
        return insterest;
    }

    @Override
    public void info() {
        System.out.println("我是一个人");
    }

    @Override
    public int compareTo(String o) {
        return 0;
    }
}

自定义注解MyAnnoation

package com.dreamcold.reflect2;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.LOCAL_VARIABLE;

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnoation {
    String value() default "hello";
}

自定义接口MyInterface

package com.dreamcold.reflect2;

public interface MyInterface {
    void info();
}

父类Creature

package com.dreamcold.reflect2;

import java.io.Serializable;

public class Creature implements Serializable {
    private char gender;
    public double weight;

    private void breath(){
        System.out.println("生物呼吸");
    }

    public void eat(){
        System.out.println("生物吃东西");
    }
}

目录的完整结构:

技术图片

获取运行时类的属性结构

示例一:获取类的所有的public方法

package com.dreamcold.reflect2;

import java.lang.reflect.Field;

public class Demo01 {
    public static void main(String[] args){
        Class clazz=Person.class;

         //获取属性结构
        //getFields():获取当前运行时类及其父类中声明为public访问权限的属性

        Field[] fields = clazz.getFields();
        for (Field f:fields){
            System.out.println(f);
        }
    }
}

效果:

技术图片

示例二:获取类的所有声明方法,不包括父类方法

package com.dreamcold.reflect2;

import java.lang.reflect.Field;

public class Demo01 {
    public static void main(String[] args){
        Class clazz=Person.class;
        //getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类 中声明的属性总

        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field f: declaredFields){
            System.out.println(f);
        }
    }
}

示例三:获取当前类的中属性的访问修饰、数据类型、变量名

package com.dreamcold.reflect2;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class Demo02 {
    public static void main(String[] args) {
        Class clazz=Person.class;
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field f:declaredFields){
            //1. 权限修饰符
            int modifiers = f.getModifiers();
            System.out.println(Modifier.toString(modifiers));
            //2.数据类型
            Class type=f.getType();
            System.out.println(type);
            //3.变量名
            String name = f.getName();
            System.out.println(name);

        }

    }
}

效果:

技术图片

获取运行时类的方法属性

示例一:获取类中的所有的方法(包括父类)

package com.dreamcold.reflect2;

import java.lang.reflect.Method;

public class Demo03 {
    public static void main(String[] args) {
        Class clazz=Person.class;
		//getMethods():获取当前运行时类及其所有父类中声明为public权限的方法
        Method[] methods = clazz.getMethods();
        for (Method m:methods){
            System.out.println(m);
        }
    }
}

效果:

技术图片

示例二:获取当前运行时类中声明的所有方法。(不包 含父类中声明的) |

package com.dreamcold.reflect2;

import java.lang.reflect.Method;

public class Demo04 {
    public static void main(String[] args) {
        Class clazz=Person.class;
        //getDeclaredMethods(): 获取当前运行时类中声明的所有方法。(不包 含父类中声明的) |

        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method m:declaredMethods){
            System.out.println(m);
        }
    }
}

效果:

技术图片

示例三:获取方法的访问修饰符、返回值类型、方法名

package com.dreamcold.reflect2;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class Demo05 {
    public static void main(String[] args) {
        Class clazz=Person.class;
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for(Method m:declaredMethods){
            //注解
            Annotation[] annotations = m.getAnnotations();
            for (Annotation a:annotations){
                System.out.println(a);
            }
            //访问修饰符
            System.out.println(Modifier.toString(m.getModifiers())+"\t");

            //返回值类型
            System.out.println(m.getReturnType().getName()+"\t");
            //函数名
            System.out.println(m.getName()+"\t");
            //参数列表
            System.out.print("(");
            Class[] parameterTypes = m.getParameterTypes();
            if (parameterTypes!=null){
                for (int i=0;i0){
                for (int i = 0; i 

效果:

技术图片

获取运行时类的构造器

示例一:getConstructors():获取当前运行时类中声明为public的构造器

package com.dreamcold.reflect2;

import java.lang.reflect.Constructor;

public class Demo06 {
    public static void main(String[] args){
        Class clazz=Person.class;
        Constructor[] constructors = clazz.getConstructors();
        for (Constructor c:constructors){
            System.out.println(c);
        }

    }
}

效果:

package com.dreamcold.reflect2;

import java.lang.reflect.Constructor;

public class Demo06 {
    public static void main(String[] args){
        Class clazz=Person.class;
        Constructor[] constructors = clazz.getConstructors();
        for (Constructor c:constructors){
            System.out.println(c);
        }

    }
}

效果:

技术图片

示例二:getDeclaredConstructors():获取当前运行时类中声明的所有的构造器

package com.dreamcold.reflect2;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class Demo06 {
    public static void main(String[] args){
        Class clazz=Person.class;

        Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
        for (Constructor constructor:declaredConstructors){
            System.out.println(constructor);
        }

    }
}

效果:

技术图片

获取运行时的父类

示例一:获取运行时的父类

package com.dreamcold.reflect2;

public class Demo07 {
    public static void main(String[] args) {
        Class clazz=Person.class;
        Class superclass = clazz.getSuperclass();
        System.out.println(superclass);
    }
}

效果:

技术图片

示例二:获取运行的时候带泛型的父类

package com.dreamcold.reflect2;

import java.lang.reflect.Type;

public class Demo08 {
    public static void main(String[] args) {
        Class clazz=Person.class;
        Type genericSuperclass = clazz.getGenericSuperclass();
        System.out.println(genericSuperclass);

    }
}

效果:

技术图片

示例三:获取运行的时候带泛型的父类,带的参数的类型

package com.dreamcold.reflect2;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public class Demo08 {
    public static void main(String[] args) {
        Class clazz=Person.class;
        //获取泛型的类型
        Type genericSuperclass = clazz.getGenericSuperclass();
        ParameterizedType paramType=(ParameterizedType)genericSuperclass;
        Type[] actualTypeArguments = paramType.getActualTypeArguments();
        System.out.println(actualTypeArguments[0].getTypeName());

    }
}

效果:

技术图片

获取运行时类的接口

  1. 获取运行时类和父类的接口
package com.dreamcold.reflect2;

public class Demo09 {
    public static void main(String[] args) {
        Class clazz=Person.class;
        Class[] interfaces = clazz.getInterfaces();
        for (Class c:interfaces){
            System.out.println(c);
        }

        System.out.println();

        Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
        for (Class c:interfaces1){
            System.out.println(c);
        }
    }
}

效果:

技术图片

获取运行时所在的包

package com.dreamcold.reflect2;

import java.lang.annotation.Annotation;

public class Demo10 {
    public static void main(String[] args) {
        Class clazz=Person.class;
        Package aPackage = clazz.getPackage();
        System.out.println(aPackage);


    }
}

效果:

技术图片

获取运行时类声明的注解

package com.dreamcold.reflect2;

import java.lang.annotation.Annotation;

public class Demo10 {
    public static void main(String[] args) {
        Class clazz=Person.class;
        Annotation[] annotations = clazz.getAnnotations();
        for (Annotation annotation:annotations){
            System.out.println(annotation);
        }

    }
}

结果:

技术图片

调用运行时类的指定

调用运行时类的属性

调用运行时类中指定的结构:属性、方法、构造器

示例一:获取和设置运行时类中的属性

package com.dreamcold.reflect2;

import java.lang.reflect.Field;

public class Demo11 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
        Class clazz=Person.class;
        //创建运行时的对象
        Person p =(Person) clazz.newInstance();
        //获取指定的属性
        Field id=clazz.getField("id");
        //设置当前属性的值
        //set():参数
        //参数1指明那个对象的属性,参数2:将此属性设置为多少
        id.set(p,1001);
        //获取当前属性的值,要求运行时类中的属性声明为public
        //通常不采用此方法
        int pId = (int)id.get(p);
        System.out.println(pId);


    }
}

效果:

技术图片

示例二:获取和设置运行时类中的属性(实际开发中使用)

package com.dreamcold.reflect2;

import java.lang.reflect.Field;

public class Demo12 {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchFieldException {
        Class clazz=Person.class;
        //创建运行时的对象
        Person p =(Person) clazz.newInstance();
        //getDeclaredField获取运行时类中的指定变量名的属性
        Field name = clazz.getDeclaredField("name");
        //java.lang.IllegalAccessException,由于权限不够,在set的时候报错,只有设置为public的属性才可以访问
        //强制设置其可以访问后可以得到结果
        name.setAccessible(true);
        name.set(p,"Tom");
        System.out.println(name.get(p));
    }
}

效果:

技术图片

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

示例一:调用类中的非静态方法

package com.dreamcold.reflect2;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Demo13 {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class clazz=Person.class;
        //创建运行时的对象
        Person p =(Person) clazz.newInstance();
        //1. 获取指定的某个方法getDeclaredMethod,指明获取的方法的名称 参数2:指明获取方法的形参列表中的class
        Method show = clazz.getDeclaredMethod("show", String.class);
        //java.lang.IllegalAccessException会被报出,如果不是public方法的情况下,所以要设置可访问
        //invoke()的返回值即为对应方法的返回值
        show.setAccessible(true);
        //2. invoke():参数1 方法的调用者 参数2:给方法形参赋值的实参
        show.invoke(p,"china");


    }
}

效果:

技术图片

示例二:调用类中的非静态方法,在Person类中加入一个静态方法

private static void showDesc(){
    System.out.println("我是一个可爱的人");
}

Test.java

package com.dreamcold.reflect2;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Demo14 {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class clazz=Person.class;
        //创建运行时的对象
        Person p =(Person) clazz.newInstance();
        Method showDesc = clazz.getDeclaredMethod("showDesc");
        showDesc.setAccessible(true);
        Object invoke = showDesc.invoke(null);
        System.out.println(invoke);//null

    }
}

效果:

技术图片

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

package com.dreamcold.reflect2;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Demo15 {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class clazz=Person.class;
        //创建运行时的对象
        Person p =(Person) clazz.newInstance();
        //1.获取指定的构造器
        //getDeclaredConstructor():参数:指明构造器的参数列表
        Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class);
        //2.保证此构造器是可访问的
        declaredConstructor.setAccessible(true);
        //3.调用此构造器创建运行时类的对象
        Person tom =(Person) declaredConstructor.newInstance("Tom");
        System.out.println(tom.toString());

    }
}

效果:

技术图片

反射的应用:动态代理

代理设计模式的原理

  • 使用一个代理将对原包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。
  • 之前为大家讲解过代理机制的操作,属于静态代理,特征是代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。最好可以通过一个代理类完成全部的代理功能。
  • 动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。
  • 动态代理使用场合:
    • 调试
    • 远程方法调用
  • 动态代理相比于静态代理的优点:抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统--的处理众多的方法。

静态代理的实现

为什么叫静态代理:特点:代理类和被代理类在编译期间,就确定下来了。

package com.dreamcold.reflect2;

/**
 * 静态代理举例
 */
interface ClothFactory{
    void produceCloth();
}
//代理类
class ProxyClothFactory implements ClothFactory{

    private ClothFactory factory;//就用被代理类的对象进行实例化

    public ProxyClothFactory(ClothFactory clothFactory){
        this.factory=clothFactory;
    }

    @Override
    public void produceCloth() {
        System.out.println("代理工厂做一些准备工作");
        factory.produceCloth();
        System.out.println("代理工厂还要做一些后续的收尾工作");
    }
}

//被代理类
class NikeClothFactory implements ClothFactory{
    @Override
    public void produceCloth() {
        System.out.println("Nike工厂生产一批运动服");
    }
}


public class StaticProxyTest {
    public static void main(String[] args) {
        //创建被代理的对象
        NikeClothFactory nike=new NikeClothFactory();
        //代理类的创建
        ClothFactory proxyClothFactory = new ProxyClothFactory(nike);
        proxyClothFactory.produceCloth();

    }
}

效果:

技术图片

动态代理的实现

package com.dreamcold.reflect2;

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

interface Human{
    String getBelief();
    void eat(String food);
}

//被代理类
class SuperMan implements Human{
    @Override
    public String getBelief() {
        return "I believe I can fly!";
    }

    @Override
    public void eat(String food) {
        System.out.println("我喜欢吃"+food);
    }
}

/**
 * 要想实现动态代理,需要解决什么问题?
 * 问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类以及对象?
 * 问题二:当通过代理类的对象调用方法的时候,如何动态的调用被代理类中的同名方法
 */

class  MyInvocationHandler implements InvocationHandler {

    //需要使用被代理类的对象进行赋值,因为invoke方法要作用在一个对象上,所以在这里要声明
    private Object obj;

    public void bind(Object obj){
        this.obj=obj;
    }

    //当我们通过代理类对象调用方法a的时候,就会自动调用如下的方法invoke()
    //将被代理类要执行的方法a的功能就声明在invoke()中
    //第一个参数是代理类的对象
    //第二个是代理类的对象调用的方法

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //method:即为代理类对象调用的方法方法,此方法也就作为了被代理对象要刁颖的方法
        //obj:被代理类的对象
        Object returnValue = method.invoke(obj, args);
        //上述方法的返回值就作为当前类中的invoke()的返回值
        return returnValue;
    }
}

class ProxyFactory{

    //调用词方法,返回一个代理类的对象
    public static Object getProxyInstance(Object obj){//obj:被代理的类
        //使用Proxy类的newProxyInstance方法来创建代理类的对象
        //第一个参数是类的加载器
        //第二个参数是接口,也就是代理类和被代理类共同要实现的接口
        //第三个参数是叫做InvocationHandler,是解决以上两个问题中第二个问题的
        MyInvocationHandler myInvocationHandler=new MyInvocationHandler();
        //相当于对obj的赋值
        myInvocationHandler.bind(obj);
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),myInvocationHandler);
    }
}

public class ProxyTest {
    public static void main(String[] args) {
        //创建代理类对象
        SuperMan superMan = new SuperMan();
        //proxyInstance:代理类的对象
        Human proxyInstance =(Human) ProxyFactory.getProxyInstance(superMan);
        ////当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法
        System.out.println(proxyInstance.getBelief());;
        proxyInstance.eat("四川麻辣烫");
    }
}

效果:

技术图片

动态代理与AOP

前面介绍的Proxy和InvocationHandler,很难看出这种动态代理的优势,下面介绍-种更实用的动态代理机制

技术图片

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

中间灰色的方法可以想象是动态的,想放那个方法放那个方法

技术图片

package com.dreamcold.reflect2;

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

interface Human{
    String getBelief();
    void eat(String food);
}

//被代理类
class SuperMan implements Human{
    @Override
    public String getBelief() {
        return "I believe I can fly!";
    }

    @Override
    public void eat(String food) {
        System.out.println("我喜欢吃"+food);
    }
}

//用来体现AOP
class HumanUtil{
    public void method1(){
        System.out.println("通用方法一");
    }

    public void method2(){
        System.out.println("通用方法二");
    }
}

/**
 * 要想实现动态代理,需要解决什么问题?
 * 问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类以及对象?
 * 问题二:当通过代理类的对象调用方法的时候,如何动态的调用被代理类中的同名方法
 */

class  MyInvocationHandler implements InvocationHandler {

    //需要使用被代理类的对象进行赋值,因为invoke方法要作用在一个对象上,所以在这里要声明
    private Object obj;

    public void bind(Object obj){
        this.obj=obj;
    }

    //当我们通过代理类对象调用方法a的时候,就会自动调用如下的方法invoke()
    //将被代理类要执行的方法a的功能就声明在invoke()中
    //第一个参数是代理类的对象
    //第二个是代理类的对象调用的方法

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        HumanUtil humanUtil=new HumanUtil();
        humanUtil.method1();

        //method:即为代理类对象调用的方法方法,此方法也就作为了被代理对象要刁颖的方法
        //obj:被代理类的对象
        Object returnValue = method.invoke(obj, args);
        //上述方法的返回值就作为当前类中的invoke()的返回值
        humanUtil.method2();
        return returnValue;
    }
}

class ProxyFactory{

    //调用词方法,返回一个代理类的对象
    public static Object getProxyInstance(Object obj){//obj:被代理的类
        //使用Proxy类的newProxyInstance方法来创建代理类的对象
        //第一个参数是类的加载器
        //第二个参数是接口,也就是代理类和被代理类共同要实现的接口
        //第三个参数是叫做InvocationHandler,是解决以上两个问题中第二个问题的
        MyInvocationHandler myInvocationHandler=new MyInvocationHandler();
        //相当于对obj的赋值
        myInvocationHandler.bind(obj);
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),myInvocationHandler);
    }
}

public class ProxyTest {
    public static void main(String[] args) {
        //创建代理类对象
        SuperMan superMan = new SuperMan();
        //proxyInstance:代理类的对象
        Human proxyInstance =(Human) ProxyFactory.getProxyInstance(superMan);
        ////当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法
        System.out.println(proxyInstance.getBelief());;
        proxyInstance.eat("四川麻辣烫");
    }
}

效果:

我们发现在原来每个执行的方法前后都加上了对应的HumanUtil中的方法执行

技术图片

Java反射详解

标签:变量   ring   编程   一段   methods   示例   执行方法   构造器   符号   

原文地址:https://www.cnblogs.com/mengxiaoleng/p/14759731.html


评论


亲,登录后才可以留言!