• java反射机制尚硅谷学习笔记


    Java反射机制

    Java Reflection

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

    Java反射机制提供的功能

    在运行时判断任意一个对象所属的类

    在运行时构造任意一个类的对象

    在运行时判断任意一个类所具有的成员变量和方法

    在运行时调用任意一个对象的成员变量和方法

    生成动态代理

    反射相关的主要API:

    java.lang.Class:代表一个类

    java.lang.reflect.Method:代表类的方法

    java.lang.reflect.Field:代表类的成员变量

    java.lang.reflect.Constructor:代表类的构造方法

    一、Class类

    Object类中的

    public final Class getClass()

    方法被所有子类集成,返回值的类型时一个Class类,此类是Java反射的源头

    对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定某个类的有关信息。

    Class对象只能由系统建立对象,一个Class对象对应的是一个加载到JVM中的一个.class文件,一个类在JVCM中只会有一个Class实例文件,此.class文件加载到内存以后,就是一个运行时类,存在缓存区。那么这个运行时类本身就是一个Class的实例!

    1.每个运行时类只加载一次!

    2.有了Class的实例以后,我们才能进行如下操作:

    • 创建对应的运行时类的对象
    • 获取对应的运行时类的完整结构(属性、方法、构造器、内部类、父类、所在的包、异常、注解...)
    • 调用对应的运行时类指定的结构(属性、方法、构造器)
    • 反射的应用:动态代理

    Class类的常用方法

    如何获取Class的实例(3种)

    @Test
    public void test() throws ClassNotFoundException{
      //1.调用运行时累本身的.class属性
      Class clazz = Person.class
      System.out.println(clazz.getName());//day19.Person
      Class clazz1 = String.class
      System.out.println(class1.getName());//java.lang.String
      
      //2.通过运行时类的对象获取
      Person p = new Person();
      Class clazz2 = p.getClass();
      System.out.println(clazz2.getName());//dya19.Person
      
      //3.通过Class的静态方法获取
      String className = "day19.Person";
      Class clazz3 = Class.forName(className);
      System.out.println(clazz3.getName());//day19.Person
      
      //4.(了解)通过类的加载器
      ClassLoader classLoader = this.getClass().getClassLoader();
      Class clazz4 = classLoader.loadClass(className);
      System.out.println(clazz4.getName());//day19.Person'
      
      System.out.println(clazz==clazz2);//true
      System.out.println(clazz==clazz3);//true
      System.out.println(clazz==clazz4);//true Person类就加载一次,获取的都是相同的Class类的实例
    }
    

    了解:类的加载过程

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

    类的生命周期:

    1、类的加载:

    • 通过类的全类名来获得二进制字节流
    • 将这个字节流所代表的静态存储结构转换成方法区运行时的数据结构
    • 并在堆内存区创建一个Java.lang.Class对象,作为对方法区中这个数据结构的访问入口

    2、连接

    • 校验:为了保证加载进来的字节流符合虚拟机规范,不会造成安全错误。(文件格式验证,元数据验证,字节码验证,符号引用验证)
    • 准备:为静态成员变量分配内存并设置默认的初始值(不是等于之后的真实值),这些变量所使用的内存都将在方法区中进行分配。假设定义类变量:public static int value=3;在准备阶段后value为0,将value=3的指令是存放于构造器方法中的,在初始化阶段才会执行。如果同时被final修饰则在准备阶段就会value赋值为3
    • 解析:将常量池内的符号引用替换为直接引用(也可能发生在初始化之后)
      • 初始化:先初始化父类,对static修饰的静态变量喝静态代码进行初始化,并初始化程序员设置的变量值
      • 生命周期结束:-执行了System.exit() 方法。-程序正常执行结束。-程序在执行过程中遇到了异常或错误而异常终止。-由于操作系统出现错误而导致java虚拟机进程终止。

    了解:ClassLoader

    类加载器是用来把(class)装载进内存的。JVM规范定义了两种类型的类加载器:启动类加载器(bootstrap)和用户自定义加载器(user-defined class loader)。JVM在运行时会产生3个类加载器组成的初始化加载器层次结构,如下图所示:

    @Test
    public void test1() throws ClassNotFoundException,IOException{
      //1.获取一个系统类加载器
      ClassLoader classLoader = ClassLoader.getSystemClassLoader();
      System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@6d06d69c
      //2.获取系统类加载器的父类加载器,即扩展类加载器
      classLoader = classLoader.getParent();
      System.out.println(classLoader);//sun.misc.Launcher$ExtClassLoader@2503dbd3
      //3.获取扩展类加载器的父类加载器,即引导类加载器
      classLoader = classLoader.getParent();
      System.out.println(classLoader);//null 引导类加载器时不能获取的
      //4.测试当前类由哪个类加载器进行加载
      classLoader = Class.forName("day19.TestReflection").getClassLoader();
      System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@6d06d69c 是系统类加载器
      //5.测试JDK提供的Object类由哪个类加载器加载
      classLoader = Class.forName("java.lang.Object").getClassLoader();
      System.out.println(classLoader);//null 说明是引导类加载器
      //6.关于类加载器的一个主要方法:getResourceAsStream(String str):获取类路径下的指定文件的输入流
      InputStream in = null;
      in = this.getClass().getClassLoader().getResourceAsSteam("jabc.properties");
      System.out.println(in);//java.io.BufferedInputStream@5b37e0d2
      byte[] b = new byte[100];
      in.read(b);
      String str = new String(b);
      System.out.println(str);//user=root 能获得文件内容
    }
    

    二、创建类对象并获取类的完整结构

    有了Class对象,能做什么?

    2.1.创建类的对象:

    调用Class对象的newInstance()方法

    要求:1)类必须有一个无参的构造器。2)类的构造器的访问权限需要足够。

    @Test
    public void test1() throws ClassNotFoundException,InstantiationException,IllegalAccessException{
      String className = "day19.Person";
      Class clazz = Class.forName(className);
      //创建对应的运行类的对象,要想创建成功,①要求对应的运行时类要有空参的构造器(类最好写一个空参的构造器)。②构造器的权限
    }
    

    难道没有无参的构造器就不能创建对象了吗?

    不是!只要在操作的时候明确的调用类中的构造方法,并将参数传递进去之后,才可以实例化操作。步骤如下:

    1)通过Class类的getDeclaredConstructor(Class…parameterTypes)取得本类的指定形参类型的构造器

    2)向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数

    3)通过Constructor实例化对象

    //1.根据全类名获取对应的Class对象
    String name = "atguigu.java.Person";
    Class clazz = null;
    clazz = Class.forName(name);
    //2.调用指定参数结构的构造器,生成Constructor的实例
    Constructor con = clazz.getConstructor(String.class,Integer.class);
    //3.通过Constructor的实例创建对应类的对象,并初始化类属性
    Person p2 = (Person) con.newInstance("Peter",20);
    System.out.println(p2);
    

    2.2.通过反射调用类的完整结构

    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<?> getReturnTyoe()取得全部的返回值

    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()

    public class TestOthers{
      //6.获取注解
      public void test6(){
        Class clazz = Person.class;
        Annotation[] anns = clazz.getAnnotations();
        for(Annotation a:anns){
          System.out.println(a);
        }
      }
      
      //5.获取所在的包
      @Test
      public void test5(){
        Class clazz = Person.class;
        Package pack = clazz.getPackage();
        System.out.println(pack);
      }
      
      //4.获取实现接口
      @Test
      public void test4(){
        Class clazz = Person.class;
        Class[] interfaces = clazz.getInterfaces();
        for(Class i:interfaces){
          System.out.println(i);
        }
      }
      
      //3.获取父类的泛型
      @Test
      public void test3(){
        Class clazz = Person.class;
        Tyep type1 = clazz.getGenericSuperclass();
        ParameterizedType param = (ParameterizedType)type1;
        Type[] ars = param.getActualTypeArguments();
        for(Type type:ars){
          System.out.println(((Class)type).getName());
        }
      }
      
      //4.获取带泛型的父类
      @Test
      public void test2(){
        Class clazz = Person.class;
        Type type1 = clazz.getGenericSuperclass();
        System.out.println(type1);
      }
      
      //1.获取运行时类的父类
      @Test
      public void test1(){
        Class clazz = Person.class;
        Class superclass = clazz.getSuperclass();
        System.out.println(superclass);
      }
    }
    
    public class TestMethod{
      //1.获取运行时类的方法
      @Test
      public void test1(){
        Class clazz = Person.class;
        //1.getMethods():获取运行时类及其父类中所有声明为public的方法
        Method[] m1 = clazz.getMethods();
        for(Method m:m1){
          System.out.println(m);
        }
        
        //2.getDeclaredMethods():获取运行时类本身声明的所有方法
        Method[] m2 = clazz.getDeclaredMethods();
        for(Method m:m2){
          System.out.println(m);
        }
      }
      
      //注解 权限修饰符 返回值类型 方法名 形参列表 异常
      @Test
      public void test2(){
        Class clazz = Person.class;
        Method[] m2 = clazz.getDeclaredMethods();
        for(Method m : m2){
          //1.注解
          Annotation[] ann = m.getAnnotations();
          for(Annotation a : ann){
            System.out.println(a);
          }
          //2.权限修饰符
          String str = Modifier.toString(m.getModifiers());
          System.out.print(str+" ");
          //3.返回值类型
          Class returnType = m.getReturnType();
          System.out.print(returnType.getName()+" ");
          //4.方法名
          System.out.print(m.getName()+" ");
          //5.形参列表
          System.out.print("(");
          Class[] params = m.getParameterTypes();
          for(int i=0;i<params.length;i++){
            System.out.print(params[i].getName()+" args-"+i+" ");
          }
          System.out.print(")");
          //6.异常类型
          Class[] exps = m.getExceptionTypes();
          if(exps.length!=0)
            System.out.print("throws ")
            for(int i=0;i<exps.length;i++){
              System.out.print(exps[i].getName());
            }
          System.out.println();
        }
      }
    }
    
    public class TestField{
      //获取运行时类的属性
      @Test
      public void test1(){
        Class clazz = Person.class;
        
        //1.getFields():只能获取到运行时类中及其父类中声明为public属性
        Field[] fields = clazz.getFields();
        for(int i=0;i<fields.length;i++){
          System.out.println(fields[i]);
        }//public java.lang.String day19.Person.name
        
        //2.getDeclaredFields()获取运行时类本身声明的所有属性(不能获取父类属性)
        Field[] fields1 = clazz.getDeclaredFields();
        for(Field f:fields1){
          System.out.println(f.getName());
          System.out.pirntln();
        }//name age
      }
      
      //权限修饰符 变量类型 变量名
      //获取属性的各个部分的内容
      public static void test2(){
        Class clazz=Person.class;
        Field[] fields1 = clazz.getDeclaredFields();
        for(Field f:fields1){
          //1.获取每个属性的权限修饰符
          int i = f.getModifiers();
          String str1 = Modifier.toString(i);
          System.out.print(str1+" ");
          
          //2.获取属性的变量类型
          Class type = f.getType();
          System.out.print(type.getName()+" ");
          
          //3.获取属性名
          System.out.print(f.getName());
          
          System.out.println();
        }
      }
    }
    
    public class TestConstructor{
      @Test
      public void test1() throws ClassNotFoundException,InstantiationException,IllegalAccessException{
        String className = "day19.Person";
        Class clazz = Class.forName(className);
        //创建对应的运行类的对象 要想创建成功,①要求对应的运行时类要有空参的构造器(类最好写一个空参的构造器)。②构造器的权限修饰符的权限要足够。
        Object obj = clazz.newInstance();//实际调用的是Person空参的构造器
        Person p = (Person)obj;
        System.out.println(p);//Person [name=null,age=0]
      }
      
      @Test
      public void test2() throws ClassNotFoundException{
        Class clazz = Class.forName("day19.Person");
        Constructor[] cons = clazz.getDeclaredConstructors();
        for(Constructor c:cons){
          System.out.peintln(c);
        }
      }
    }
    

    三、通过反射调用类中的指定方法、指定属性

    //调用运行时类中指定的属性
    @Test
    public void test3() throws NoSuchFieldException,SecurityException,InstantiationException,IllegalAccessException{
      Class clazz = Person.class;
      //1.获取指定的属性
      //getField(String filedName):获取运行时类中声明为public的指定属性名为fieldName的属性
      Field name = clazz.getField("name");
      //2.创建运行时类的对象
      Person p = (Person)clazz.newInstance();
      System.out.println(p);
      //将运行时类的指定的属性赋值
      name.set(p, "Jerry");
      System.out.println(p);
      System.out.println();
      
      //Field age = clazz.getField("age"); //私有类型的不可见
      //getDeclaredField(String fieldName):获取运行时类中指定的名为fieldName的属性
      Field age = clazz.getDeclaredField("age");
      //由于属性权限修饰符的限制,为了保证可以给属性赋值,需要在操作之前使得此属性可被操作。
      age.setAccessible(true);//不然辉出现IllegalAccessException
      age.set(p, 10);
      System.out.println(p);
    }
    
    //调用运行时类中指定的方法
    @Test
    public void test3() throws Exception{
      Class clazz = Person.class;
      
      //gertMethod(String methodName,Class ...params):获取运行时类中声明为public的指定的方法
      Method m1 = clazz.getMethod("show");
      Person p =(Person)clazz.newInstance();
      //调用指定的方法:Object invoke(Object obj, Object ...obj)
      Object returnVal = m1.invoke(p);
      System.out.println(returnVal);
      //对于运行时类中静态方法的调用
      Method m3 = clazz.getMethod("info");
      m3.invoke(Person.class);
      
      //getDeclaredMethod(String methodName,Class ...params):获取运行时类中指定的方法
      Method m4 = clazz.getDeclaredMethod("diaplay",String.class,Interger.class);
      m4.setAccessible(true);
      Object value = m4.invoke(p, "CHN", 10);
      System.out.println(value);
    }
    
    //调用指定的构造器,创建运行时类的对象
    @Test
    public void test3() throws Exception{
      String className = "day19.Person";
      Class clazz = Class.forName(className);
      Constructor cons = clazz.getDeclaredConstructor(String.class,int.class);
      cons.setAccessible(true);
      Person p = (Person)cons.newInstance("xxx",20);
      System.out.println(p);
    }
    

    Object invoke(Object obj, Object … args)

    说明:

    • Object对应原方法的返回值,若原方法无返回值,此时返回null
    • 若原方法为静态方法,此时形参Object obj可为null
    • 若原方法形参列表为空,则Object[] args为null
    • 若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法。

    调用指定属性

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

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

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

    在Field中:

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

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

    注:在类中属性都设置为private的前提下,在使用set()和get()时,首先要使用Field类中的setAccessible(true)方法将需要操作的属性设置为可以被外部访问。

    Public void setAccessible(true)访问私有属性时,让这个属性可见。

    Java动态代理

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

    动态代理使用场合:

    • 调试
    • 远程方法调用

    代理设计模式的原理:

    使用一个代理将对象包装起来,然后用该代理对象取代原始对象。

    任何对原始对象的调用都要通过代理。

    代理对象决定是否以及何时将方法调用转到原始对象上。

    静态代理

    //静态代理模式
    
    //接口
    interface ClothFactory{
      void productCloth();
    }
    
    //被代理类
    class NikeClothFactoy implements ClothFactory{
      
      @Override
      public void productCloth(){
        System.out.println("Nike工厂生产一批衣服");
      }
    }
    
    //代理类
    class ProxyFactory implements ClothFactory{
      ClothFactory cf;
      //创建代理类的对象时,实际传入被代理类的对象
      public ProxyFactory(ClothFactory cf){
        this.cf = cf;
      }
      
      @Override
      public void productCloth(){
        System.out.println("代理类开始执行,收代理费¥1000");
      }
    }
    
    public class TestClothProduct{
      public static void main(String[] args){
        NileClothFacoty nike = new NikeClothFactory();//创建被代理类的对象
        ProxyFactory proxyFactory = new ProxyFactory(nike);//创建代理类的对象
        proxyFactory.productCloth();
      }
    }
    
    //动态代理
    interface Subject{
      void action();
    }
    //被代理类
    class RealSubject implements Subject{
      @Override
      public void action(){
        System.out.println("我是被代理类,记得执行我!");
      }
    }
    
    //用来动态创建代理类
    class MyInvocationHandler implements InvocationHandler{
      Object obj;//实现了接口的被代理类的对象的声明
      //①给被代理的对象实例化②返回一个代理类的对象
      public Object blind(Object obj){
        this.obj = obj;
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),this);//类加载器,得到全部的接口,得到InvocationHandler接口的子类实例
      }
      //当通过代理类的对象发起对被重写的方法的调用时,都会转换为对如下的invoke方法的调用
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{//被代理的对象,要调用的方法,方法调用时所需要的参数
        //method方法的返回值是returnVal
        Object returnVal = method.invoke(obj, args);
        return returnVal;
      }
    }
    public class TestProxy{
      public static void main(String[] args){
        //1.被代理类的对象
        RealSubject real = new RealSubject();
        //2.创建一个实现了InvacationHandler接口的类的对象
        MyInvocationHandler handler = new MyInvocationHandler();
        //3.调用blind()方法,动态的返回一个同样实现了real所在类实现的接口Subject的代理类的对象
        Object obj = handler.blind(real);
        Subject sub = (Subject)obj;//此时sub就是代理类的对象
        sub.action();//转到对Invacationhandler接口的实现类的invoke()方法的调用
        
        //再举一例
        NikeClothFacoty nike = new NikeClothFacoty();
        ClothFactory proxyCloth = (ClothFactory)handler.blind(nike);//proxyCloth即为代理类的对象
        proxyCloth.productCloth();
      }
    }
    

    动态代理与AOP(Aspect Orient Programming)

    interface Dog{
      void info();
      void run();
    }
    class HuntingDog implements Dog{
      public void info(){
        System.out.println("我是一只猎狗");
      }
      public void run(){
        System.out.println("我奔跑迅速");
      }
    }
    class DogUtil{
      public void method1(){
        System.out.println("====模拟通用方法一====");
      }
      public void method2(){
        System.out.println("====模拟通用方法二====");
      }
    }
    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;
      }
    }
    class MyProxyFactory{
      //为指定target生成动态代理对象
      public static Object getProxy(Object target)
        throw Exception{
        //创建一个MyInvokationHandler对象
        MyInvocationHandler handler = new MyInvocationHandler();
        //为MyInvokationHandler设置target对象
        handler.setTarget(target);
        //创建、并返回一个动态代理对象
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),handler);
      }
    }
    public class Test{
      public static void main(Steing[] args)
        throw Exception{
        //创建一个原始的HuntingDog对象,作为target
        Dog target = new HuntingDog();
        //以指定的target来创建动态代理
        Dog dog = (Dog)MyProxyFactory.getProxy(target);
        dog.info();
        System.out.println();
        dog.run();
      }
    }
    

    执行结果:

    使用Proxy生成一个动态代理时,往往并不会凭空产生一个动态代理,这样没有太大的意义。通常都是为指定的目标对象生成动态代理,这种动态代理在AOP中被称为AOP代理,AOP代理可以替代目标对象,AOP代理包含了目标对象的全部方法。但AOP代理中的方法与目标对象的方法存在差异:AOP代理里的方法可以在执行目标方法之前、之后插入一些通用处理

    参考:https://blog.csdn.net/zxm1306192988/article/details/57084061

  • 相关阅读:
    人月神话阅读笔记01
    Map Reduce数据清洗及Hive数据库操作
    Hadoop实验六——MapReduce的操作
    假期第九周学习记录
    假期第八周学习记录
    假期第七周学习记录
    hadoop不在sudoers文件中。此事将被报告。 解决方法
    假期第六周学习记录
    2021寒假(22)
    2021寒假(21)
  • 原文地址:https://www.cnblogs.com/caoj/p/15912102.html
Copyright © 2020-2023  润新知