• 【Java反射机制 01】


    一、含义:

    Java反射机制是在运行状态中,对于任意一个类(class文件),都能够知道这个类的所有属性和方法;
    对于任意一个对象,都能够调用它的任意一个方法和属性
    这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制

    动态获取类中信息,就是Java反射
    可以理解为对类的解剖

     

     反射必须要有接口和配置文件两大元素。

    二、 字节码文件对象

    想要对一个类文件进行解剖,只要获取到该类字节码文件对象即可

    |-- 用于描述字节码的类叫class Class{

      提供获取字节码文件中的内容。比如名称,字段,构造函数,一般函数

    }

    该类就可以获取字节码文件中的所有内容,那么反射,就是依靠该类完成的。

     三、获取Class对象的三种方式

    package com.FansheReflect;
    
    import com.JavaFanShe.Person;
    
    /**
     * @Author wufq
     * @Date 2020/9/8 16:38
     */
    public class ReflectDemo {
        public static void main(String[] args) throws ClassNotFoundException {
    //        getClassobject_1();
    //        getClassobject_2();
            getClassobject_3();
    
        }
    
        /*
        方式三:通过给定类的字符串名称就可以获取类,更为扩展
        可是用Class类中的方法完成。
        该方法就是forName
        这种方法只需要名称即可,更方便,扩展性更强
        * */
    
        public static void getClassobject_3() throws ClassNotFoundException {
            String className = "com.JavaFanShe.Person";//
            Class classs = Class.forName(className);  //以下片段返回命名为java.lang.Thread的类的运行时Class描述符,所以定义的类字符串是完整的带包名的类名称
            System.out.println(classs);
        }
    
        /*
        * 方式一:获取字节码对象的方式:
        * 1.object类中的getClass方法
        * 想要这种方式,必须要明确具体的类,并创建对象
        * 麻烦!!!
        * */
    
        public static void getClassobject_1(){
            Person p = new Person();
            Class aClass=p.getClass(); //调用person的缺省构造器    -->Person run...
    
            Person p1 = new Person();
            Class p1Class= p1.getClass();//调用person的缺省构造器  -->Person run...
    
            System.out.println(aClass==p1Class);
        }
    
        /*方式二:
        * 任何数据类型都具备一个静态的属性.class来获取其对应的class对象
        * 相对简单,但是还是要明确用到类中的静态成员
        * */
        private static void getClassobject_2() {
    
            Class classz = Person.class;
            Class classz1 = Person.class;
            System.out.println(classz==classz1);
        }
    }

    四、获取Class中的构造函数

    |-- 寻找类名称文件,并加载进内存,生成class对象

    |-- 通过getConstructor(Class<?>... parameterTypes)方法获取指定的构造方法,返回Constructor对象

    |-- 通过newInstance()方法实现对象的生成

    备注:如果调用缺省构造器的话,就不需要获取指定的构造方法,,直接实现方法的生成

    package com.FansheReflect;
    
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    
    /**
     * @Author wufq
     * @Date 2020/9/9 15:46
     */
    public class ReflectDemo01 {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
    
            //creatNewObject_2();
            //creatNewObject();
            creatNewObject_1();
    
        }
    
        public static void creatNewObject(){
    
            //早期初始化对象-->缺省构造器、构造方法调用
            com.JavaFanShe.Person p = new com.JavaFanShe.Person();
    
            com.JavaFanShe.Person p1 = new com.JavaFanShe.Person("小强",23);
    
        }
    
        //现在:通过该名称类文件地址  -->缺省构造器、构造方法调用
        public static void creatNewObject_1() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
    
            String Naame ="com.JavaFanShe.Person";
            //寻找该名称类文件,并加载进内存,并产生class对象
            Class classs = Class.forName(Naame);
            //如何产生对象呢?
            Object obj =classs.newInstance();
    
        }
    
        public static void creatNewObject_2() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
    
            String Naame ="com.JavaFanShe.Person";
            //寻找该名称类文件,并加载进内存,并产生class对象
            Class classs = Class.forName(Naame);
            //获取到了指定对象的构造方法
            Constructor con =classs.getConstructor(String.class,int.class);
            //真实产生对象
            Object obj = con.newInstance("小强",22);
    
        }
    
    }
    
    =====执行结果=====
    Person param run...小强::22
    Person run...
    Person param run...小强::23
    Person run...、获取Class中的构造函数 wu

    五、获取Class中的字段

    |-- 寻找类名称文件,并加载进内存,生成class对象

    |-- class对象调用getField()  -->只能获取共有的

      class对象调用getDeclaredField("age");  -->只获取本类,包含私有

    |-- 对私有字段的访问取消权限检查。暴力访问  

    field= clazz.getDeclaredField("age");

    |-- 产生真实的对象:

    field.setAccessible(true);

    |-- 给对象进行赋值和获取

       Object obj = clazz.newInstance();
    
            field.set(obj,89);
            Object o = field.get(obj);
            System.out.println(o);

    --------->完整的代码<-----------

    package com.FansheReflect;
    
    import java.lang.reflect.Field;
    
    /**
     * @Author wufq
     * @Date 2020/9/15 14:21
     */
    public class ReflectDemo02 {
        public static void main(String[] args) throws NoSuchFieldException, ClassNotFoundException, InstantiationException, IllegalAccessException {
            getFileDemo();
        }
    
        /*
        * 获取字节码文件中的字段*/
        public static void getFileDemo() throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException {
            Class clazz = Class.forName("com.JavaFanShe.Person");
            Field field=null;
            /*//getField()返回公共成员字段,而Person对象里面的age是一个私有属性,所以不适合
            file=clazz.getField("age");
            System.out.println(file);//java.lang.NoSuchFieldException: age*/
    
            //只能获取本类,包含私有
            field= clazz.getDeclaredField("age");
    
            //对私有字段的访问取消权限检查。暴力访问
            field.setAccessible(true);
    
            //真实产生对象
            Object obj = clazz.newInstance();
    
            field.set(obj,89);
            Object o = field.get(obj);
            System.out.println(o);
            
        }
    }
    
    ====执行结果====
    Person run...
    89

    六、获取Class中的方法

    1、 获取无参方法

    2、 获取有参方法

    |-- 寻找类名称对象,并加载进内存,返回class对象

    |-- 调用getMethod()方法:无参和有参的区别在于:参数用String.class   int.class

    |-- 生成对象

    |-- 执行目标方法:invoke

    package com.FansheReflect;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Method;
    
    /**
     * @Author wufq
     * @Date 2020/9/15 16:35
     * 获取class类的方法  -->单独获取一个方法(无参/有参)
     */
    public class ReflectDemo04 {
        public static void main(String[] args) throws Exception {
    //        getMethodDemo_1();
            getMethodDemo_2();
        }
        //有参方法
        public static void getMethodDemo_2()  throws Exception {
            Class clazz = Class.forName("com.JavaFanShe.Person");
            Method method= clazz.getMethod("paramMethod",String.class,int.class);
            Object obj = clazz.newInstance();
            method.invoke(obj,"小强",23);
        }
    
        //无参方法
        public static void getMethodDemo_1() throws Exception {
            Class clazz = Class.forName("com.JavaFanShe.Person");
            Method method=clazz.getMethod("show",null);//调用无参方法
            Object obj =clazz.newInstance();//产生真实的对象
            method.invoke(obj,null);//用来执行某个的对象的目标方法
    
            //调用构造方法
            Constructor con= clazz.getConstructor(String.class,int.class);
            Object obj_1 = con.newInstance("小明",22);
    
        }
    
    }

    七、反射练习

    电脑运行:在电脑主板的基础上新增加声卡和网卡

    思路:

    |--  有可能在电脑主板的基础上,新增加网卡、声卡....

    |--  所以需要把新增加的网卡、声卡做成接口,然后每新增加一个就继承接口,已实现接口的功能

    |-- 在电脑主板上写一个方法(usePCI)实现接口

    |-- 在测试时,调用usePCI接口实现声卡、网卡的功能

      这样做的优势:即使新增加功能,只需要新增加的功能实现了接口就能被调用

      劣势:每次添加声卡、网卡这样的设备都需要修改代码传递一个新创建的对象

    问题:能不能不修改对象就可以完成这个动作

    解决方法:不能new完成,通过修改配置文件,然后获取其class文件,在内部实现创建对象的动作。

    ----------->接口:PCI<------------

    package com.FansheReflectTest;
    
    /**
     * @Author wufq
     * @Date 2020/9/15 17:34
     */
    public interface PCI {
        void open();
        void close();
    }

    ----------->声卡/网卡类:SoundCard/WangCard  继承实现接口<------------

    package com.FansheReflectTest;
    
    /**
     * @Author wufq
     * @Date 2020/9/15 17:32
     */
    public class SoundCard implements PCI{
        public void open(){
            System.out.println("open....");
        }
    
        public void close(){
            System.out.println("close....");
        }
    }
    package com.FansheReflectTest;
    
    /**
     * @Author wufq
     * @Date 2020/9/15 17:40
     */
    public class WangCard  implements PCI{
        public void open(){
            System.out.println("net open....");
        }
    
        public void close(){
            System.out.println("net close....");
        }
    }

    ----------->主板类:Mainboard<------------

    package com.FansheReflectTest;
    
    /**
     * @Author wufq
     * @Date 2020/9/15 17:18
     */
    public class Mainboard {
        public void run(){
            System.out.println("main bpard run...");
        }
    
        public void usePCI(PCI p){ //采用接口的好处是,即使新增加功能,只需要新增加的功能实现了接口就能被调用
            if(p!=null){
                p.close();
                p.open();
            }
        }
    }

    ----------->测试类:ReflectTest<------------

    package com.FansheReflectTest;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.util.Properties;
    
    /**
     * @Author wufq
     * @Date 2020/9/15 17:16
     *
     * 电脑运行
     * --->采用接口的好处是,即使新增加功能,只需要新增加的功能实现了接口就能被调用
     * ---> 缺点:每次添加设备都需要修改代码传递一个新创建的对象
     *      能不能不修改对象就可以完成这个动作。
     *      --->不用new完成,而是获取其class文件,在内部实现创建对象的动作。
     */
    public class ReflectTest {
        public static void main(String[] args){
            Mainboard mb = new Mainboard();
            mb.run();
    
    
            /*mb.usePCI(new SoundCard());
            mb.usePCI(new WangCard());*/
    
    
            try {
                File configFile = new File("pic.properties");
                Properties prop = new Properties();
                FileInputStream fis = new FileInputStream(configFile);
                prop.load(fis);
    
                for(int x=0;x<prop.size();x++){
                    String pciName = prop.getProperty("pci"+(x+1));
                    Class clazz = Class.forName(pciName);
                    PCI pci=(PCI) clazz.newInstance();
    
                    mb.usePCI(pci);
                }
                fis.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        }
    }
     try {}catch{}   整个这一段代码都可以封装

    ----------->配置文件:
    pic.properties<------------
    pci1=com.FansheReflectTest.SoundCard
    pci2=com.FansheReflectTest.WangCard

    后面如果新增加设备,只需要在修改配置文件里面的名称即可

  • 相关阅读:
    webpack 入门
    javascript 函数重载另一种实现办法
    5个python爬虫教材,让小白也有爬虫可写,含视频教程!
    简书模拟登陆缺陷!!!
    Python操作Mongodb
    【爬虫系列之一】爬虫开发环境的搭建
    CentOS7.4,anaconda3,python3.6,tensorflow环境下gdal的编译和问题解决
    返回Json格式结果
    json扩展
    EF中使用SQL语句或存储过程
  • 原文地址:https://www.cnblogs.com/frankruby/p/13639934.html
Copyright © 2020-2023  润新知