Java 反射

2021/7/26 22:06:58

本文主要是介绍Java 反射,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

一  反射的概述

  1. 什么是反射?在程序运行中分析类的一种能力 

 反射能做什么?(1)分析类:加载并初始化一个类,查看类的所有属性和方法 (2)查看并使用对象:查看一 个对象的所有属性和方法,使用对象的任意属性和方法 

  1. 反射的应用场景:(1)构造通用的工具类时 (2)搭建具有高度灵活性和扩展性的系统框架

二  类加载器(ClassLoader)

  1. 作用:负责将类的字节码文件(.class文件)加载到内存中,并生成对应的Class文件
  2. Class对象:一个后缀名为 .java的源文件对应一个后缀名为 .class的字节码文件,一个字节码文件又对应一个Class对象
  3. 类加载时机:
    (1)创建类的实例:
            Student stu = new Student(); //使用类加载器,将Student类的字节码文件加载到内存中
    注:如果再次使用该方法创建Student类对象,类加载器将不再加载该类的字节码文件,因为一个类的字节码文件只会被加载一次
    (2)访问类的静态成员时:
            Calendar.getInstance();  //使用类加载器,将Calendar类的字节码文件加载到内存中
    (3)初始化类的子类:
            class User extends Person{}
            User user = new User();  //使用类加载器,先加载父类Person类的文件,再加载子类User类文件
    (4)反射方式创建类的Class对象:
            Class clazz = Class.forname("类的正名");
            类的正名:包名 + 类名:如 javatest.Test
    总结:第一次使用类中的成员时,类加载器就会将该类的字节码文件加载到内存中

 三  获取Class对象的方式

1 使用Object类中的getClass()方法:Class clazz = 对象名.getClass();
2 类的静态属性:Class clazz = 类名.class;
3 Class类的静态方法:Class clazz = Class.forName("类的正名");

四  通过反射方式获取构造方法并使用

  1. Constructor<T>:构造器对象 :即其对应类的构造方法
  2. 通过Class对象获取构造器对象
    1 getConstructor​(Class<?>... parameterTypes):根据参数列表,获取对应的构造器对象(即对应类的构造方法)仅限公共的构造方法。
    其中Class<?>...:可变的参数,代表Class类型的数组, ?:通配符,代表不确定的任意类型,根据构造方法进行对应,即Class<?>... parameterTypes 代表参数类别
    2 getDeclaredConstructor​(Class<?>... parameterTypes):根据参数列表,获取对应的构造器对象,可获得私有的构造方法
    3 getConstructors​():直接获取此类所有的构造方法(不报含私有方法)
    4 getDeclaredConstructors​:直接获取此类所有的构造方法
    
    例:Class<?>... parameterTypes
    若参数为 String name , 则为clazz.getConstructor(String.class);
    若参数为 int age , 则为clazz.getConstructor(int.class);
  3. Constructor的常用方法
    1 getName​(): 获取构造函数名:以字符串形式返回此构造函数的名称 
    2 newInstance​(Object... initargs): 根据此构造函数和指定参数创建对象,返回为Object类型对象,需要下转型
    3 在调用私有构造方法创建对象是需要结合:
      Constructor对象名.setAccessible(boolean flag); + Constructor对象名.newInstance​(Object... initargs);
      当flag为true是,开始暴力反射,即可以调用私有构造方法
  4. 代码演示:
    Person类
    package javatest;
    
    //Person类
    public class Person {
        //公共的无参构造
        public Person() {}
    
        //公共的带参构造
        public Person(String name){
            System.out.println(name);
        }
    
        //私有的的带参构造
        private Person(int age){
            System.out.println(age);
        }
    }
    
    
    测试类:
    import java.lang.reflect.Constructor;
    
    public class Test {
        public static void main(String[] args) throws Exception {
            //需求通过反射的方式创建Person类型的对象并使用其构造方法
            //若不需要反射:可直接 Person p = new Person();
    
            //1.获取Person类的字节码文件对象
            Class clazz = Class.forName("javatest.Person"); //文件可能不存在,抛出异常
            //或Class clazz = Person.class; //通过;类名也可得到CLass对象
    
            //2.根据第一步,获取到的字节码文件对象获取指定的构造器对象
            //2.1获取公共的无参构造
            Constructor con1 = clazz.getConstructor(); //可能不存在该类型参数列表的构造方法,所以要抛出异常
            System.out.print("获取公共的无参构造: ");
            System.out.println(con1);
            //2.2获取公共的有参构造
            Constructor con2 = clazz.getConstructor(String.class); //参数为String类型,需要String.class作为参数
            System.out.print("获取公共的有参构造: ");
            System.out.println(con2);
            //2.3获取私有的有参构造
            Constructor con3 = clazz.getDeclaredConstructor(int.class); //参数为int类型,需要int .class作为参数
            System.out.print("获取私有的有参构造:  ");
            System.out.println(con3);
            //2.4直接获取所有的公共构造
            System.out.println("-------------------------------");
            System.out.print("直接获取所有的公共构造: ");
            Constructor[] cons = clazz.getConstructors();//获取所有公共构造
            //遍历cons数组
            for (Constructor con : cons) {
                System.out.println(con);
            }
    
            //3.根据构造器对象和参数,创建对应的Person类对象
            System.out.println("-------------------------------");
            //首先根据获取构造器名字
            System.out.print("获取构造器名字: ");
            System.out.println(con2.getName());
            //根据con2构造器创建对象
            System.out.print("通过公共带参构造方法,创建对象,并输出参数:");
            Person p = (Person) con2.newInstance("张三");//默认为Object类型,通过下转型(强制转换)得到Person对象
            //通过有无地址值,确定是否存在
            System.out.print("p的地址为: ");
            System.out.println(p);
        }
    }
  5. 结果展示:
  6. 总结:通过”全类名“先获得Class对象,根据Class对象获得Constructor对象(构造器对象)通过Constructor对象可以创建Person类对象

  通过反射方式获取成员方法并使用

  1. Method:方法对象:及其对应类的成员方法
  2. 通过Class对象获取方法
    1 getMethod(String name, Class<?>...parameterTypes):返回一个Method对象,仅公共成员方法
    其中:name方法名, Class<?>...parameterTypes方法的参数列表
    2 getDeclaredMethod(String, Class<?>...):返回一个Method对象,可获取私有方法
    3 getMethod():返回此类中的所有(不含私有)方法数组
    4 getDeclaredMethods():返回此类中的所有方法数组
    注:getMethod()得到父类的方法,即一定会得到Object类中的方法
  3.  Method的常用方法
    1 getName():放回方法名
    2 invoke( Object obj, Object...args):在指定对象上调用此方法,参数为args
    其中:obj代表对象名 , 返回值为Object类型 
    3 在调用私有方法是需要结合:
      Method对象名.setAccessible(boolean flag); + Method对象名.invoke( Object obj, Object...args);
      当flag为true是,开始暴力反射,即可以调用私有方法
  4. 代码演示
    Person类:
    package javatest;
    
    //Person类
    public class Person {
        //公共的无参方法
        public void show1(){
            System.out.println("我是公共的无参方法");
        }
    
        //公共的有参方法
        public void show2(int a){
            System.out.println("我是公共的有参方法,参数为" + a);
        }
    
        //私有的有参方法
        private int show3(int a, int b){
            System.out.println("我是私有的有参方法,参数和为" + (a + b));
            return (a + b);
        }
    }
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Method;
    
    public class Test {
        public static void main(String[] args) throws Exception {
            //需求通过反射的方式获取Person类的成员方法并调用
            //若不需要反射:可直接 Person p = new Person();
            //1.获取Person类的字节码文件对象
            Class clazz = Class.forName("javatest.Person"); //文件可能不存在,抛出异常
            //或Class clazz = Person.class; //通过;类名也可得到CLass对象
    
            //2.获取该类的构造器对象,然后创建Person对象
            Constructor con = clazz.getConstructor(); //获得系统提供的无参构造方法
            Person person = (Person) con.newInstance(); //向下转型,获得Person对象
    
            //3.获取该类的成员方法对象,然后调用此方法
            //3.1调用公共的无参方法
            Method method1 = clazz.getMethod("show1");
            //打印方法
            System.out.println(method1);
            //打印方法名
            System.out.println(method1.getName());
            //调用此方法
            method1.invoke(person); //person作为对象进行调用
            System.out.println("-----------------------------");
            //3.2调用公共的有参方法
            Method method2 = clazz.getMethod("show2", int.class);
            //调用此方法
            method2.invoke(person,2); //person作为对象进行调用, arge参数
            System.out.println("-----------------------------");
            //3.2调用私有的有参方法
            Method method3 = clazz.getDeclaredMethod("show3", int.class, int.class);
            //调用此方法
            method3.setAccessible(true);
            int sum = (int)method3.invoke(person,1,2); //person作为对象进行调用
            System.out.println("sum:" + sum);
            System.out.println("-----------------------------");
            //3.4一次性调用所有的公共方法
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                System.out.println(method);  //会得到Object类的方法
            }
        }
    }
    
  5. 结果演示:可以看出只有俩个为Person类的公共方法,其余为Object类方法
  6.  总结:通过”全类名“先获得Class对象,根据Class对象获得Constructor对象(构造器对象)通过构造器创建Person类对象,通过Class对象获取方法,再通过method类方法结合Person类对象调用方法

   通过反射方式获取成员变量并使用

  1. Field对象:域(属性、成员变量)对象,及其对应类的成员变量
  2. 通过Class对象获取属性
    1 getField(String name):返回一个Field对象,仅公共属性
    其中:name:属性名
    2 getDeclaredField(String name):返回一个Field对象,可获取私有属性
    3 getField():返回此类中的所有属性(不含私有)方法数组
    4 getDeclaredFields():返回此类中的所有属性方法数组
    注:getMethod()得到父类的方法,即一定会得到Object类中的方法
  3.  Field的常用方法
    1 set(Object obj, Object value):设置obj对象的指定属性值为value
    2 setAccessible(boolean flag):将此属性的可访问性设置为指定布尔值
  4. 代码演示
    Person类:
    package javatest;
    
    //Person类
    public class Person {
        //公有的属性
        public String name;
        //私有的属性
        private int age;
    
        //重写toString方法为了方便打印各个属性值
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    
    public class Test {
        public static void main(String[] args) throws Exception{
            //需求:通过反射获取成员变量并使用
            //1.获取Person类的字节码文件对象
            Class clazz = Class.forName("javatest.Person");
            //或:Class clazz = Person.class;
    
            //2.通过字节码文件对象获取构造器对象,然后创建Person类对象
            Constructor con = clazz.getConstructor();
            Person person = (Person)con.newInstance();
            //合并版 Person person = (Person)clazz.getConstructor().newInstance(); 链式编程
    
            //3.设置Person类对象的属性值
            Field field1 = clazz.getField("name");
            field1.set(person, "张三");
            Field age = clazz.getDeclaredField("age");
            age.setAccessible(true); //将私有属性设置为可设置权限
            age.set(person, 1);
            System.out.println(person);
        }
    }
  5. 结果演示:可以看出只有俩个为Person类的公共方法,其余为Object类方法
  6.  总结:通过”全类名“先获得Class对象,根据Class对象获得Constructor对象(构造器对象)通过构造器创建Person类对象,通过Class对象获取方法,再通过method类方法结合Person类对象调用方法


这篇关于Java 反射的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程