【Java开发语言 13】第十三章 反射(Reflection理解Class类并实例化Class类对象-通过反射调用类的完整结构,父类,接口,构造器,方法,构造器创建对象-调用指定方法-动态代理)

本文详细介绍了Java的反射机制,包括Class类的使用、创建对象、调用方法和属性。通过反射,可以在运行时动态地获取类的信息并操作类的对象,包括获取类的构造器、方法和属性,以及创建对象、调用指定方法和属性。同时,文中还讨论了如何通过反射调用私有方法和属性,并展示了动态代理的应用场景。
摘要由CSDN通过智能技术生成

反射的思路

  • 看到形象的图片想到了抽象的名字
    在这里插入图片描述
  • 看到抽象的名字,在记忆中寻找匹配的形象的图片
    在这里插入图片描述
  • 看到一个类总结类名
    在这里插入图片描述
  • 看到一个类名总结类
    在这里插入图片描述

1 Java Reflection (反射) 概述

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

2 理解Class类并实例化Class类对象

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

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

  • 创建Class对象的四种方法
//=================创建Class对象的四种方法==============
        Class c0 = Person.class;//通过类名.class创建指定类的Class实例
        Clazz c1 = p.getClass();//通过一个类的实例对象.getClass()方法,获取对应 实例对象的类的Class实例
        try{
            
            //通过Class的静态方法forName(String className)来获取一个类的Class实例
            //forName(String className)方法中的参数是你要获取的Class实例的类的全路径(包名.类名)
            //最常用的
            Class c2 = Class.forName("chapter13.Person");//这个是获取Class实例的常用方式

        }catch(Exception e){
            e.pritnStackTrace();
        }
//第四种不常用不介绍

3 运行时创建类对象,通过反射调用类的完整结构

在这里插入图片描述

Class clazz = Class.forName("chapter13.Student");//通过包名.类名的字符串,调用Class.forName方法获取指定类的Class实例
Package p = clazz.getPackage();//获取类所在的包
System.out.println("获取类所在的包"+p.getName());//Package类也有getName()方法

  • 创建Person类作为父类,创建Student类继承父类并实现接口Move,Study
  • 在Student类中,创建不同权限的构造器、方法、属性
//Move接口
public interface Move {
    void moveTy();
}

//Study接口
public interface Study {
    void studyInfo();
}

//Person类

public class Person {
    String name;
    int age;
}
//Student类

public class Student extends Person implements Move,Study{
    public Student(){
        System.out.println("调用的是 public Student()");
    }

    public Student(int age){
        this.age = age;
        System.out.println("调用的是 public Student(int age)");
    }
    protected Student(String school){
        this.school = school;

        System.out.println("调用的是 protected Student(String school)");
    }

	Student(int id, int age){
        this.id = id;
    }
    
    private Student(String name, int age){
        this.name = name;
        this.age = age;
        System.out.println("调用的是 private Student(String name, int age)");

    }


    /**
     * 四种访问修饰符的范围复习:
     *           类内部 同一个包的类  子类  任何地方
     * private     ✔         ✘     ✘       ✘
     * 缺省         ✔         ✔     ✘       ✘
     * protected   ✔         ✔      ✔      ✘
     * public      ✔         ✔      ✔      ✔
     */

    public String school;
    private String privateField;
    int id;
    protected char sex;



    @Override
    public void moveTy() {
        System.out.println("骑自行车上学");
    }

    @Override
    public void studyInfo() {
        System.out.println("学习的是中学的知识");
    }


    public void showInfo(){
        System.out.println("学校是:"+this.school);
    }

    private void test(String name){
        System.out.println("这是私有方法private void test(String name)");

    }

    public String getSchool(){

        System.out.println("这是public String getSchool()方法");
        return this.school;
    }



    public void setInfo(String name, int age, int id){
        this.name = name;
        this.age = age;
        this.id= id;
        System.out.println("这是public void setInfo(String name, int age, int id)方法");
    }
    public void setInfo(String school){
        this.school = school;
        System.out.println("这个是public void setInfo(String school)方法");

    }

}

✔ 复习4种访问权限修饰符

在这里插入图片描述

不同的修饰符及其对应符号:private: 2,缺省default: 0,protected: 4,public: 1

✔ Declared:不含Declared的方法只能返回public修饰的方法或属性(包含基类的),含Declared的方法可以返回所有访问权限的方法或属性(不包含基类)

  • 例如:得到所有构造器
    Constructor c1 = clazz.getConstructor(int.class);//参数类型为Class,返回public的Constructor类对象
    Constructor c2 = clazz.getDeclaredConstructor(String.class);//参数类型为Class,返回Constructor类对象

在这里插入图片描述

在这里插入图片描述

  • 得到所有方法
    Method[] ms = clazz.getMethods();//获取到类的所有的公有public方法
    Method[] ms = clazz.getDeclaredMethods();//获取类的所有方法,包括公有和私有

    • getMethods() 获取的是类的所有共有方法,这就包括自身的 所有public 方法,和从基类继承的、从接口实现的所有public方法。
    • getDeclaredMethods()获取的是类的所有方法包含4种不同访问权限包括private私有类。但不包含基类(即父类,Object类等继承过的类)的方法。
  • 得到所有属性
    Field[] fs = clazz.getFields();//获取类的公有的属性,包括从父类继承的
    Field[] fs = clazz.getDeclaredFields();//获取类的所有属性包括私有,不包括从父类继承的

3.1 获取当前对象类的父类、所有接口(Class)

在这里插入图片描述

注意:此处获得的父类Person,子类Student,类实现的接口Move,Study都是作为Class类的对象返回得到的

  • 实例化Class对象,并获取所继承的父类,获取当前类的所有接口

Class superClass = clazz.getSuperclass();//获取父类
Class[] interfaces = clazz.getInterfaces();//获取当前类的所有接口
c.getName()//获取单个接口名

public static void main(String[] args) {
        try{
            Class clazz = Class.forName("chapter13.Student");//通过包名.类名的字符串,调用Class.forName方法获取指定类的Class实例
            Class superClass = clazz.getSuperclass();//获取父类
            System.out.println("父类:"+superClass.getName());

            Class[] interfaces = clazz.getInterfaces();//获取当前类的所有接口
            for(Class c: interfaces){
                System.out.println("接口:"+c.getName());
            }

        }catch(Exception e){
            e.printStackTrace();
        }
    }

在这里插入图片描述

3.2 通过反射获取类的全部构造器及修饰符、构造方法名、参数类型。

在这里插入图片描述

注意:
1.此处获得的构造器(构造方法)都是作为Constructor类的对象返回得到的,而获取的构造器的参数们为Class类型对象
2.获得的方法为Method类
3.获得的属性为Field类

  • 获取类的全部构造器及修饰符、构造方法名、参数类型
 public static void main(String[] args) {
        try{
         
            //---------------------------------获取当前类public的构造器-------------------------------------------------
            Constructor[] pcons = clazz.getConstructors();//返回的是一个数组,public的构造方法的数组;
            for(Constructor c: pcons){
                System.out.println("------------------------");
                //输出所有构造方法的名称;直接c输出整个方法名包含修饰符、返回值、参数类型
                System.out.println("构造方法名称:"+c.getName());//getName获取方法名称
                System.out.println("构造方法:"+c.getName()+"的修饰符是:"+c.getModifiers());//getModifiers获取方法修饰符
                //getModifiers取得方法的修饰,返回数字1代表public,返回数字2代表private,缺省是0,protected是4
            }

            //-------------------------获取当前类所有的构造器-----通过构造方法Constructor类返回该构造器的参数数组--------------------------------------
            System.out.println();
            System.out.println("获取当前类所有的构造器-----通过构造方法Constructor类返回参数数组");
            Constructor[] cons = clazz.getDeclaredConstructors();//返回此Class对象类声明的所有构造方法
            for(Constructor c: cons){
                System.out.println("------------------------");

                //输出所有构造方法的名称;直接c输出整个方法名包含修饰符、返回值、参数类型
                System.out.println("构造方法名称:"+c.getName());//getName获取方法名称
                System.out.println("构造方法:"+c.getName()+"的修饰符是:"+c.getModifiers());//getModifiers获取方法修饰符

                Class[] parmClazz = c.getParameterTypes();//获取该构造方法的参数类型,有几个参数,数组的元素就有几个
                for(Class pc: parmClazz){
                    System.out.println("构造方法"+c.getName()+"的参数类型:"+pc.getName());
                }
            }
            
 		}catch(Exception e){
            e.printStackTrace();
        }

  • 运行结果
  • 对应4个构造器
  • 不同的修饰符及其对应符号:private2,缺省default0,protected4,public1

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

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

3.3 获取类的全部方法及方法名、返回值类型、修饰符、方法的参数类型们。获取类的全部属性及属性名、属性类型、修饰符。

在这里插入图片描述

在这里插入图片描述
注意:返回的类的方法都为Method类对象,返回的类的属性都为Field类对象

  • 获取类的全部方法及方法名、返回值类型、修饰符、方法的参数类型们
 //--------------------------------------获取类的所有方法Methods------------------------------------------------
	try{
            //Method[] ms = clazz.getMethods();//获取到类的所有的公有public方法
            Method[] ms = clazz.getDeclaredMethods();//获取类的所有方法,包括公有和私有
            for (Method m : ms) {//输出每一个方法
                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 p : pcs) {
                        System.out.println(p.getName());
                    }
                }
                System.out.println("---------------------------");
            }
      }catch(Exception e){
            e.printStackTrace();
      }
  • 运行结果
    • getMethods() 获取的是类的所有共有方法,这就包括自身的 所有public 方法,和从基类继承的、从接口实现的所有public方法。
    • getDeclaredMethods()获取的是类的所有方法包含4种不同访问权限包括private私有类。但不包含基类(即父类,Object类等继承过的类)的方法。

getMethods()得到很多基类public方法
getDeclaredMethods得到的方法里没有test方法,因为test方法为private
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • 获取类的全部属性及属性名、属性类型、修饰符
//--------------------------------------获取类的所有属性Field-----------------------------------------------
	try{
            /*
             * 加了Declared就不能输出基类的属性,但是可以输出所有类型的
             */
            Field[] fs = clazz.getFields();//获取类的公有的属性,包括从父类继承的
//            Field[] fs = clazz.getDeclaredFields();//获取类的所有属性包括私有,不包括从父类继承的
            for (Field f:fs){
                System.out.println("-----------------------------");
                System.out.println("修饰符:"+f.getModifiers());
                System.out.println("属性类型:"+f.getType());
                System.out.println("属性名称"+f.getName());
            }

      }catch(Exception e){
            e.printStackTrace();
      }
  • 运行结果
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

由图可见,2private,0缺省default,4protected,1public

4 通过反射创建对象 -> Class对象.newInstance();Constructor对象.newInstance(参数.class);

  • 通过反射创建对象,调用四种不同的构造器
    • 注意:只有public的无参构造是直接用该Class类对象.newInstance()创建,其他都是先得到构造器(根据参数类型.class转为Class类,作为调构造器的参数,来区分调哪个构造器)再构造器.newInstance(参数)创建该类对象
    • 如果是调用私有private构造器创建对象,Declared强制获得构造器后进行,构造器.setAccessible(true)来解除封装,再构造器.newInstance(参数);
    • 获得protected和default类型的构造器无需解除封装,但需Declared

例如:

Student obj = (Student)clazz.newInstance();//调无参构造直接构造对象

Constructor c1 = clazz.getConstructor(int.class);//参数类型为Class,返回Constructor类对象
Student stu2 = (Student)c1.newInstance(36);//填入该构造器参数

Constructor c3 = clazz.getDeclaredConstructor(String.class,int.class);//指定获取有两个参数(String,int)的构造方法
c3.setAccessible(true);//解除私有的封装,下面就可以对这个私有方法强制调用
Student stu4 = (Student)c3.newInstance("张三",24);//填入构造器对应的参数

			Class clazz = Class.forName("chapter13.Student");//通过包名.类名的字符串,调用Class.forName方法获取指定类的Class实例

            try{
                //-------------------用Class对象.newInstance();调用无参构造器返回对应类的Object------------------------------
                //相当于调用Student类的无参公有的构造方法,public Student()
                Object obj = clazz.newInstance();//无参,返回Object对象
                //得到一个Student对象,需要强转到原本类型
                Student stu1 =(Student)obj;
                System.out.println("该public无参构造的Student对象学校为:"+stu1.school);//无参构造,school无值


                //------------------用Constructor对象.newInstance(参数.class);调用public有参构造器返回对应类的Object------------------------------------
                Constructor c1 = clazz.getConstructor(int.class);//参数类型为Class,返回Constructor类对象
                Student stu2 = (Student)c1.newInstance(36);//通过newInstance实例化对象,相当于调用protected Student(String school)
                System.out.println("该public有参构造的Student对象年龄为:"+stu2.age);


                //------------------用Constructor对象.newInstance(参数.class);调用procected有参构造器返回对应类的Object------------------------------------
                //指定获取有一个参数并且为String类型的protected的构造方法
                Constructor c2 = clazz.getDeclaredConstructor(String.class);//参数类型为Class,返回Constructor类对象
                Student stu3 = (Student)c2.newInstance("第一中学");//通过newInstance实例化对象,相当于调用protected Student(String school)
                System.out.println("该protected有参构造的Student对象学校为:"+stu3.school);//该有参构造的Student对象学校为:第一中学

 				//-------------------用Constructor对象.newInstance(参数.class);调用default有参构造返回对应类的Object---------------------------------------
                Constructor c3 = clazz.getDeclaredConstructor(int.class, int.class);
                Student stu4 = (Student)c3.newInstance(202856,34);
                System.out.println("该default有参构造的Student对象学号为:"+stu4.id+"年龄为:"+stu4.age);

                //------------------用Constructor对象.newInstance(参数.class);调用private有参构造器返回对应类的Object------------------------------------
                //通过反射机制,可以强制的调用私有的构造方法
                Constructor c3 = clazz.getDeclaredConstructor(String.class,int.class);//指定获取有两个参数(String,int)的构造方法

                //需要加上这一句,解除封装
                c3.setAccessible(true);//解除私有的封装,下面就可以对这个私有方法强制调用

                //直接写报错java.lang.IllegalAccessException: Class chapter13.Test1 can not access a member of class chapter13.Student with modifiers "private"
                //因为是私有构造
                Student stu4 = (Student)c3.newInstance("张三",24);
                System.out.println("该private有参构造的Student对象姓名为为:"+stu4.name+",年龄为:"+stu4.age);//该有参构造的Student对象学校为:第一中学


            }catch(Exception e){
                e.printStackTrace();
            }

  • 运行结果
    在这里插入图片描述

在这里插入图片描述

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

在这里插入图片描述
在这里插入图片描述
如果是私有属性
Field f1 = clazz.getDeclaredField("privateField");//获取该class实例对应类中对应名称的对象
f1.setAccessible(true);//解除私有的封装,下面就可以强制的调用这个属性

//=============================通过反射调用类的指定方法、指定属性============================

try{
            System.out.println("=============================通过反射调用类的指定方法============================");
            //----------------------------------通过反射调用指定方法---------------------------------------------

            /**
             * 注意:下面不论是反射调用setInfo还是test方法
             * 都调用的obj对象的方法,obj对象实际上就是Student对象
             */
            Constructor con = clazz.getConstructor();//获取无参构造
            Object obj = con.newInstance();//使用无参构造创建对象

            Method m =  clazz.getMethod("setInfo",String.class,int.class,int.class);//得到名称叫setInfo,参数是String,int,int的方法
            m.invoke(obj,"张三",23,20165678);//参数1是需要实例化的对象,后面的参数是调用当前的方法实际参数

            //如果想要调用一个私有方法
            Method m1 = clazz.getDeclaredMethod("test",String.class);//获取方法名为test,参数为1个String类型的方法
            m1.setAccessible(true);//解除私有封装,下面可以强制调用私有的方法
            m1.invoke(obj,"李四");//私有的

            //调用一个重载方法
            Method m2= clazz.getMethod("setInfo",String.class);
            m2.invoke(obj,"第一中学");

            //有返回值的方法
            Method m3 = clazz.getMethod("getSchool");//这是获取方法名为getSchool并且没有参数的方法
            String school= (String)m3.invoke(obj);//调用有返回值但是没有参数的方法;返回的是Object类型的,强转一下
            System.out.println(school);

            //----------------------------------通过反射调用指定方法属性---------------------------------------------

            System.out.println("=============================通过反射调用类的指定属性============================");

            //反射创建一个对象
            Constructor con2 = clazz.getConstructor();//利用无参构造创建一个对象
            Student stu = (Student)con2.newInstance();//通过构造方法创建对象
            Field f = clazz.getField("school");//获取名称为school的属性

            f.set(stu,"第三中学");
            String school2 = (String)f.get(stu);//获取stu对象的school属性的值
            System.out.println(school2);

            //如果是私有属性
            Field f1 = clazz.getDeclaredField("privateField");
            f1.setAccessible(true);//解除私有的封装,下面就可以强制的调用这个属性
            f1.set(stu,"测试私有属性");
            System.out.println(f1.get(stu));

        }catch(Exception e){
            e.printStackTrace();
        }

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

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

6 动态代理

在这里插入图片描述
在这里插入图片描述
如果有100个java类,每个类有10个方法,总共1000个方法,都需要在方法前后增加打印开始和结束的输出。
这里是两个类

  • 接口ITestDemo
public interface ITestDemo {
    void test1();
    void test2();

}
  • 类TestDemoImp1实现接口ITestDemo
public class TestDemoImp1 implements ITestDemo {
    @Override
    public void test1() {
        System.out.println("执行test()方法");
    }

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

  • 类TestDemoImp2实现接口ITestDemo
public class TestDemoImp2 implements ITestDemo{
    @Override
    public void test1() {
        System.out.println("TestDemoImp2类的test1()");
    }

    @Override
    public void test2() {
        System.out.println("TestDemoImp2类的test2()");

    }
}

  • 动态代理类
    • 需要实现InvocationHandler接口和public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 方法
    • 其中有参构造,参数为被代理的类Object,属性Object类型的obj在invoke方法中作为参数调用原类的方法
* @Description: 动态代理类
 */
public class ProxyDemo implements InvocationHandler {
    Object obj;//被代理的对象
    public ProxyDemo(Object obj){
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println(method.getName()+"方法开始执行");
        Object result = method.invoke(this.obj, args);//执行的是指定对象的指定的方法;传入参数调用该对象的该方法
        System.out.println(method.getName()+"方法执行完毕");
        return result;
    }
}
  • 动态代理,前后对比
public static void main(String[] args) {
        //接口中定义了两个方法,实例化类对象
        ITestDemo demoImp = new TestDemoImp1();//继承接口的类对象



        /**
         * 注意:如果一个对象想要通过Proxy.newProxyInstance方法被代理,
         * 那么这个对象的类一定要有相应的接口
         * 就像本例中的ITestDemo接口和实现类TestDemoImp1
         */
        demoImp.test1();
        demoImp.test2();//调用对象的两个方法


        /**
         * 需求:
         * 在执行test1和test2方法时需要加入一些东西
         * 在执行方法前打印test1或test2开始执行
         * 在执行方法后打印test1或test2执行完毕
         * 打印的方法名要和当时调用方法保持一致
         */

        InvocationHandler handler = new ProxyDemo(demoImp);//传入要同意修改的类所在的对象为参数作为被代理对象,然后构造代理对象
        /**
         * Proxy.newProxyInstance(ClassLoader, interfaces, h)
         * 参数1是代理对象的类加载器
         * 参数2是被代理的对象的接口
         * 参数3是代理对象
         * 返回的值是成功被代理后的对象
         */
        //返回的值就是成功被代理后对象,返回的是Object类型,需要根据具体情况去转换类型
        ITestDemo t = (ITestDemo)Proxy.newProxyInstance(handler.getClass().getClassLoader(),demoImp.getClass().getInterfaces(),handler);
        t.test1();
        t.test2();

        //测试:代理前的
        ITestDemo t2 = new TestDemoImp2();
        t2.test2();
        demoImp.test1();

    }

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值