JAVA之反射知识点整理

Posted 大忽悠爱忽悠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JAVA之反射知识点整理相关的知识,希望对你有一定的参考价值。

概念

将类的各个组成部分封装成其他对象,这就是反射机制


好处

  1. 在程序运行过程中操作这些对象
  2. 可以解耦,提高程序的可扩展性

反射机制的含义及其功能

含义:

反射是被视为动态语言的关键,反射机制能够让程序在执行期间借助Reflection API获取任何类的内部信息,并能直接操作任意对象的内部属性和方法

功能:

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

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

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

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

生成动态代理


处理流程图

在这里插入图片描述


反射相关API

java.lang.Class : 代表一个类

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

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

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


Class类

反射可以得到的信息: 某个类的属性,方法和构造器,某个类到底实现了哪些接口.
对于每个类而言,JRE都为其保留一个不变的Class对象,
一个Class对象包含了特定某个类的相关信息

Class本身也是一个类

Class对象只能由系统建立对象

一个类在JVM中只会有一个实例

一个Class对象对应的是一个加载到JVM中的一个.class文件(每个类在编译后会生成一个.class文件)

每个类的实例都会记得自己是由哪个Class实例生成的

通过Class可以完整得到一个类中的完整结构


获取class对象的方式

1.class.forName(“全类名”): 将字节码文件加载进内存,返回class对象

多用于配置文件,将类名定义在配置文件中。 读取文件,加载类
全类名: 包名.类名
会抛出ClassNotFoundException的异常

2.类名.class: 通过类名的属性class获取
多用于参数传递

3.对象.getClass(): getClass()方法在Object类中定义

多用于对象的获取字节码的方式


注意

同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪种方式获取的class对象都是同一个


演示

package test;

public class main1 {
    public static void main(String[] args) throws Exception {
        //1.类全名
       Class cls1= Class.forName("test.main1");
        System.out.println(cls1);
        //类名.class
        Class cls2=main1.class;
        System.out.println(cls2);
        //对象.getClass()
        main1 m=new main1();
        Class cls3=m.getClass();
        System.out.println(cls3);
              //比较这三个对象
        System.out.println(cls1==cls2);
        System.out.println(cls1==cls3);
        System.out.println(cls2==cls2);
    }
    
}

在这里插入图片描述


获取一个类的父类和接口

package reflect;

public class main {
    public static void main(String[] args) throws Exception {
    Class cl1=Class.forName("reflect.stu");//获取指定类的Class实例
        System.out.println(cl1.getName());
    Class cl2=cl1.getSuperclass();//获取父类
        System.out.println(cl2.getName());
        //获取当前类的所有接口
        Class[] interfaces=cl1.getInterfaces();
        for(Class c:interfaces)
            System.out.println(c.getName());
    }
}

在这里插入图片描述


获取一个类的构造方法

         //获取当前类的共有构造方法
        Constructor[] con=cl1.getConstructors();
        for(Constructor c:con)
        {
            //getModifiers():  获取构造方法修饰符  返回1代表public 返回2代表private
            //getName(): 获取方法名称
            System.out.println("修饰符:"+c.getModifiers()+"  构造方法:"+c.getName());

            //获取构造函数的参数,参数有几个数组里面元素就有几个
            Class[] paramClass=c.getParameterTypes();
           for(Class c1:paramClass)
           {
               System.out.println("构造方法: "+c.getName()+ "  参数类型: "+c1.getName());
           }
        }


        System.out.println("--------------------------------------------------------");

        //获取当前类的所有构造方法,包括共有和私有构造方法
        Constructor[] con1=cl1.getDeclaredConstructors();
        for(Constructor c:con1)
        {
            //getModifiers():  获取构造方法修饰符  返回1代表public 返回2代表private
            System.out.println("修饰符: "+c.getModifiers()+"  构造方法:"+c.getName());

            Class[] paramClass=c.getParameterTypes();
            for(Class c1:paramClass)
            {
                System.out.println("构造方法: "+c.getName()+ "  参数类型: "+c1.getName());
            }
        }

在这里插入图片描述


通过反射创建一个对象

       Class cl1=Class.forName("reflect.stu");
       //相当于调用Stu类的无参构造方法
       Object obj=cl1.newInstance();
       stu s=(stu)obj;
       System.out.println(s.name+"  "+s.age);

        System.out.println("-----------------------------");
        //指定一个有两个参数,一个为String,一个为int的共有构造函数
        Constructor c=cl1.getConstructor(String.class,int.class);
        stu s2=(stu)c.newInstance("小朋友",19);//实例化对象,相当于调用对应的有参构造函数
        System.out.println(s2.name+"  "+s2.age);

        System.out.println("-----------------------------");
        //通过反射机制,强制调用私有的构造方法
        Constructor c3=cl1.getDeclaredConstructor(int.class);
        //解除私有的封装,下面就可以对这个私有的方法进行强制调用
          c3.setAccessible(true);
        stu s5=(stu)c3.newInstance(100);
       System.out.println(s5.name+"  "+s5.age);

在这里插入图片描述


获取一个类的方法

获取到类的所有共有方法

         Class cl1=stu.class;
         //获取到类的所有共有方法
        Method[] mt=cl1.getMethods();
        for(Method m:mt)
        {
            System.out.println("方法名: "+m.getName());
            System.out.println("返回值类型: "+m.getReturnType());
            System.out.println("修饰符: "+m.getModifiers());

            //获取方法的参数类型,是一个数组,方法有几个参数,数据就有几个
            Class[] pcs=m.getParameterTypes();
            if(pcs!=null&&pcs.length>0)
            {
                for(Class pc:pcs)
                {
                    System.out.println("参数类型: "+pc.getName());
                }
            }
        }

在这里插入图片描述
在这里插入图片描述

获取到类的所有方法,包括共有和私有方法,不包括继承方法

         Class cl1=stu.class;
         //获取到类的所有共有方法
        Method[] mt=cl1.getDeclaredMethods();
        for(Method m:mt)
        {
            System.out.println("方法名: "+m.getName());
            System.out.println("返回值类型: "+m.getReturnType());
            System.out.println("修饰符: "+m.getModifiers());

            //获取方法的参数类型,是一个数组,方法有几个参数,数据就有几个
            Class[] pcs=m.getParameterTypes();
            if(pcs!=null&&pcs.length>0)
            {
                for(Class pc:pcs)
                {
                    System.out.println("参数类型: "+pc.getName());
                }
            }
        }

在这里插入图片描述


获取一个类的属性和包

获取类的所有公有属性,包含父类的公有属性

        stu s = new stu();
        Class c = s.getClass();
        //获取类的所有公有属性,包含父类的公有属性
        Field[] fs = c.getFields();
        for (Field f : fs)
        {
            System.out.println("修饰符: "+f.getModifiers());
            System.out.println("属性的类型: "+f.getType());
            System.out.println("属性的名称: "+f.getName());
        }

在这里插入图片描述

获取类的所有属性,包括私有,但不包括继承至父类的属性

        stu s = new stu();
        Class c = s.getClass();
        //获取类的所有属性
        Field[] fs = c.getDeclaredFields();
        for (Field f : fs)
        {
            System.out.println("修饰符: "+f.getModifiers());
            System.out.println("属性的类型: "+f.getType());
            System.out.println("属性的名称: "+f.getName());
        }

在这里插入图片描述

获取包名

    stu s = new stu();
        Class c = s.getClass();
        //获取类所在包的包名
        Package p=c.getPackage();
        System.out.println(p.getName());

在这里插入图片描述


反射机制调用指定方法

//注意:下面不论是反射调用show还是display方法,都调用obj对象的方法,obj对象实际上就是stu对象
      Class c=Class.forName("reflect.stu");
        Constructor con=c.getConstructor();//获取无参构造
        Object obj=con.newInstance();
      //调用display公有的无参方法,无返回值
        Method m=c.getMethod("display");
        m.invoke(obj);

        System.out.println("--------------------");

        //调用公有的有参有返回值的重载dispaly
        Method m1=c.getMethod("display",String.class);
        String ret=(String) m1.invoke(obj,"大忽悠");
        System.out.println("返回值打印:"+ret);

        System.out.println("--------------------");

        //调用私有无参无返回值的方法
        m=c.getDeclaredMethod("show");
        //解除私有的封装,下面可以强制调用私有的方法
        m.setAccessible(true);
        m.invoke(obj);

        System.out.println("--------------------");

        //调用私有有参有返回值的重载show方法
        m1=c.getDeclaredMethod("show",String.class);
        //解除私有的封装,下面可以强制调用私有的方法
        m1.setAccessible(true);
        ret=(String) m1.invoke(obj,"小朋友");
        System.out.println("返回值打印:"+ret);

在这里插入图片描述


反射机制调用指定属性

            Class c=Class.forName("reflect.stu");
            //反射创建一个对象
            stu s=(stu)c.newInstance();//默认调用无参构造
            //获取名称为name的属性
        Field f=c.getField("name");
        //将对象s的name属性设置为大忽悠
        f.set(s,"大忽悠");
        //获取对象s的name属性值
        String name=(String) f.get(s);
        System.out.println("name属性值: "+name);

      System.out.println("-----------------");

      //获取私有属性
      f=c.getDeclaredField("age");
      //解除私有的封装,下面就可以强制的调用这个属性
      f.setAccessible(true);
      //设置私有属性
      f.set(s,18);
      System.out.println("私有属性age值: "+f.get(s));

在这里插入图片描述


JAVA动态代理

在这里插入图片描述

其实动态代理很好理解,就是有些情况下我们不能直接对目标类进行一个访问,这个时候就需要中间商,也就是代理对象,我们通过Proxy里的newProxyInstance,指定目标对象的父类接口,它就会根据这个接口里的抽象方法,隐含的在底层实现一个代理类,这个时候肯定有人会问,那实现了代理类,里面重写的方法咋办?,,其实代理类并没有重写接口里的那些方法,是直接通过反射对目标对象里的方法进行的调用,在此基础之上,还可以添加一些额外的非业务逻辑代码,例如日志信息、验证啊啥的,下面是我话的一张草图,希望有助于大家理解。

在这里插入图片描述

举例:

一个java项目,有100个类,每个类有10个方法,一共1000个方法
现在要求在每个方法执行前后加上两句话

testDemo接口 :

package reflect;

public interface testDemo {
    void test1();
    void test2();
}

test实现testDemo接口:

package reflect;

public class test implements testDemo{
    @Override
    public void test1()
    {
        System.out.println("test1方法执行中...");
    }

    @Override
    public void test2() {
        System.out.println("test2方法执行中....");
    }
}

proxyDemo实现代理类接口:

package reflect;

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


public class proxyDemo implements InvocationHandler {
    //被代理的对象
    Object obj;
    proxyDemo( Object obj)
    {
        this.obj=obj;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(method.getName()+"开始执行");
        //执行指定代理对象的指定方法
        Object ret=method.invoke(obj,args);//不管代理对象的方法有无返回值都接收一下,然后返回
        System.out.println(method.getName()+"执行完毕");
        return ret;
    }
}

主函数:

package reflect;

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

public class main 关于JAVA 反射 基础知识/编码经验的一些总结

java 反射代码片段

笔记之_java整理设计模式

Java开发知识之JAVA的反射

Java基础知识之反射机制简介

java反射