Java反射机制

Posted Rocksta

tags:

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

一. 反射概述

什么是反射?

  • 反射允许对封装类的字段,方法和构造函数的信息进行编程访问
  • 这里的字段指的就是成员变量,方法指的就是成员方法,构造函数指的就是构造方法
  • 反射:反射是在运行时获取类的字节码文件对象,然后可以解析类中的成员变量,构造方法以及成员方法,反射允许对成员变量、成员方法和构造方法的信息进行编程访问!
  • 反射的核心思想和关键就是:得到编译后的字节码文件对象,也就是class文件对象。
  •  反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意属性和方法;这种运行时动态获取类信息以及动态调用类中成分的能力称为Java语言的反射机制!

  • 反射允许对成员变量、成员方法和构造方法的信息进行编程访问
  • 反射就可以把它理解成是一个人,这个人它可以把成员变量、成员方法以及构造方法,  挨个的都获取出来,并对它们进行操作!

获取出来有什么用呢?

  • 当然有用了,比如IDEA里面自动提示的功能,就是利用反射来实现的。
  • 我们在程序中创建了一个对象,利用这个对象去调用方法,或者是调用成员变量,那么IDEA就会利用反射把这个类里面能调用的所有的方法,所有的成员变量获取出来并进行展示!
  • 还有我们在创建对象或者是调用方法的时候,方法的形参给忘了,可以按Ctrl + P加以提示,此时IDEA就是利用反射去获取这个方法上所有的形参并展示出来!
  • 还有当我们在一个类里面定义了私有的构造方法,那么在测试类当中,IDEA给出的自动提示就不让去调用那个私有的构造方法,这是怎么做到的呢?
    就是利用反射,利用反射获取到该类的构造方法的权限修饰符,然后一看是private的,IDEA给出的自动提示就不会提示该私有的构造方法参数!

=======================================================================

问题1:说白了反射就是可以从类里面拿东西,拿什么?

  • 拿成员变量、构造方法、成员方法,把它们挨个的拿出来,这就是反射!

问题2:既然都是拿东西,为什么不用IO流而要用反射呢?

  • IO流是从上往下一行一行的去读,当我们读到构造方法和普通成员方法的时候,怎么区分?很难了把,如果想着用返回值区分,那么成员变量和局部变量你怎么区分呢?还得通过上下文去区分,非常的麻烦!

  • 反射甚至可以利用获取出来的构造方法去创建对象!
  • 反射还能够运行获取出来的方法!
  • 在反射面前,一切都是赤裸裸的。
  • 利用反射,我们就可以获取到类里面所有的信息!

  • 反射的第一步永远都是先获取Class类对象,如此才可以解析类中的全部成分!
  • 在获取的时候,不是从Java文件当中去获取的,而是从Class字节码文件当中去获取的!
  • Class类就是用来描述字节码文件的!
  • 同一个类编译后只产生一份class文件!

Class是一种类:

  1. 获取class类对象的三种方式:

  • java文件:就是我们自己编写的java代码。
  • 字节码文件:就是通过java文件编译之后的class文件(是在硬盘上真实存在的,用眼睛能看到的)
  • 字节码文件对象:当class文件加载到内存之后,虚拟机自动创建出来的对象。
  • ​ 这个对象里面至少包含了:构造方法,成员变量,成员方法。
  • 而我们的反射获取的是什么?字节码文件对象,这个对象在内存中是唯一的。
  •  

package com.gch.d2_reflect_class;

public class Student 
   

package com.gch.d2_reflect_class;

/**
   目标:反射的第一步,获取class对象,也就是类对象
   一个类编译后只产生一份class文件
   获取class对象的三种方式:
 */
public class Test 
    public static void main(String[] args) throws Exception
        // 1.调class类中的一个静态方法:forName(String className===>全限名/全类名:包名 + 类名)
        // 最为常用的
        Class c1 = Class.forName("com.gch.d2_reflect_class.Student");
        System.out.println(c1); // class com.gch.d2_reflect_class.Student,本质就是Student.class

        // 2.类名.class
        // 一般更多的是当作参数进行传递(比如多线程的synchronized的同步锁对象用的就是class对象)
        Class c2 = Student.class;
        System.out.println(c1);// class com.gch.d2_reflect_class.Student

        // 3. java.lang.Object下的一个方法,对象.getClass(),获取对象对应类的Class对象
        // 当我们已经有了这个类的对象时,才可以使用
        Student s = new Student();
        Class c3 = s.getClass();
        System.out.println(c3); // class com.gch.d2_reflect_class.Student
    

  • 在Java当中,有一个思想叫做万物皆对象
  • 比如字节码文件可以看作是Class这个类的对象
  • 同理,构造方法也可以把它看作是一个对象,在Java当中定义了一个类叫做Constructor,那么这个类就是用来描述构造方法的!同理这个类的对象,就表示构造方法的对象
  • 在Java当中,有一个类叫Field,它是用来描述成员变量的,所以这个类的对象就表示成员变量的对象。
  • 第三个成员,方法,在Java当中有一个类叫Method,用来描述成员方法,同理,这个类的对象就是成员方法的对象!

 2. 反射获取构造方法

  • Declared就把它理解成权限的意思,有了Declared我就可以获取到所有的构造方法,包括私有的构造方法我都能获取到!

package com.gch.d3_reflect_constructor;

public class Student 
    private String name;
    private int age;

    public Student() 
        System.out.println("无参构造器执行!");
    

    public Student(String name)
        this.name = name;
    

    protected Student(int age)
        this.age = age;
    

    private Student(String name, int age) 
        System.out.println("有参构造器执行!");
        this.name = name;
        this.age = age;
    

    /**
     * 获取
     * @return name
     */
    public String getName() 
        return name;
    

    /**
     * 设置
     * @param name
     */
    public void setName(String name) 
        this.name = name;
    

    /**
     * 获取
     * @return age
     */
    public int getAge() 
        return age;
    

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) 
        this.age = age;
    

    public String toString() 
        return "Studentname = " + name + ", age = " + age + "";
    

package com.gch.d3_reflect_constructor;

import java.lang.reflect.Constructor;
import java.lang.reflect.Parameter;

/**
   目标:反射获取Constructor构造方法
 */
public class MyReflectDemo 
    public static void main(String[] args) throws Exception 
        /*
           Class类中用于获取构造方法的方法
                Constructor<?>[] getConstructors()
                Constructor<?>[] getDeclaredConstructors()
                Constructor<T> getConstructor(Class<?>... parameterTypes)
                Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

            Constructor类中用于创建对象的方法
                 T newInstance(Object... initargs)
                 public void  setAccessible(boolean flag)
         */

        // 1.获取class字节码文件的对象
        Class clazz = Class.forName("com.gch.d3_reflect_constructor.Student");

        // 2.获取构造方法
        // 获取所有的公共的构造方法
        Constructor[] cons1 = clazz.getConstructors();
        for (Constructor con : cons1) 
            System.out.println(con);
        

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

        // 获取所有的构造方法
        Constructor[] cons2 = clazz.getDeclaredConstructors();
        for (Constructor con : cons2) 
            System.out.println(con);
        

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

        // 获取到空参的构造方法
        Constructor con3 = clazz.getDeclaredConstructor();
        System.out.println(con3);
        System.out.println();

        // 获取到指定参数的构造方法,这个参数要跟构造方法里面的参数要保持一致才是可以的
        Constructor con4 = clazz.getDeclaredConstructor(String.class);
        System.out.println(con4);
        System.out.println();
        // 在Java当中,int也是有对应字节码文件的
        Constructor con5 = clazz.getDeclaredConstructor(int.class);
        System.out.println(con5);

        System.out.println("====================================");
        // getDeclared只是让你看到这个构造,但是无法利用它直接创建对象
        Constructor con6 = clazz.getDeclaredConstructor(String.class,int.class);
        System.out.println(con6);
        // 获取该构造方法的权限修饰符,返回值类型是int
        int modifiers = con6.getModifiers();
        System.out.println(modifiers); // 2
        System.out.println("================================");
        // con6.getParameterCount(); 获取到参数的个数
//        con6.getParameterTypes(); 获取到参数的类型
        // 获取到这个构造方法所有的参数
        Parameter[] parameters = con6.getParameters();
        for (Parameter parameter : parameters) 
            System.out.println(parameter); // 表示构造方法的每一个参数
        
        // 表示临时取消权限校验,如果遇到私有构造器,可以暴力反射,表示权限被打开
        con6.setAccessible(true); // 暴力反射
        // 利用获取到的构造方法去创建对象,要传递参数,参数的属性要跟构造方法的参数保持一致
        Student stu = (Student) con6.newInstance("张三",24);
        System.out.println(stu);
    

 3. 反射获取成员变量

package com.gch.d4_reflect_field;

public class Student 
   private String name; // 姓名
   private int age; // 年龄
   public String gender; // 性别


    public Student() 
    

    public Student(String name, int age, String gender) 
        this.name = name;
        this.age = age;
        this.gender = gender;
    

    /**
     * 获取
     * @return name
     */
    public String getName() 
        return name;
    

    /**
     * 设置
     * @param name
     */
    public void setName(String name) 
        this.name = name;
    

    /**
     * 获取
     * @return age
     */
    public int getAge() 
        return age;
    

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) 
        this.age = age;
    

    /**
     * 获取
     * @return gender
     */
    public String getGender() 
        return gender;
    

    /**
     * 设置
     * @param gender
     */
    public void setGender(String gender) 
        this.gender = gender;
    

    public String toString() 
        return "Studentname = " + name + ", age = " + age + ", gender = " + gender + "";
    

package com.gch.d4_reflect_field;

import java.lang.reflect.Field;

public class MyReflectDemo 
    public static void main(String[] args) throws Exception 
        /*
            Class类中用于获取成员变量的方法:
                Field[] getFields()	返回所有公共成员变量对象的数组(只能拿public的)
                Field[] getDeclaredFields()	返回所有成员变量对象的数组,存在就能拿到
                Field getField(String name)	返回单个公共成员变量对象(只能拿public的)
                Field getDeclaredField(String name)	返回单个成员变量对象,存在就能拿到

            Filed类中用于创建对象的方法:
                void set(Object obj,Object value):赋值
                Object get(Object obj):  获取值
         */

        // 1.获取class字节码文件的对象
        Class clazz = Class.forName("com.gch.d4_reflect_field.Student");

        // 2.获取成员变量
        // 获取所有的公共的成员变量
        Field[] fields = clazz.getFields();
        for (Field field : fields) 
            System.out.println(field); // public java.lang.String com.gch.d4_reflect_field.Student.gender
        
        System.out.println();
        // 获取所有的成员变量
        Field[] fields2 = clazz.getDeclaredFields();
        for (Field field : fields2) 
            System.out.println(field); // private int com.gch.d4_reflect_field.Student.age
        
        System.out.println();
        // 获取单个的公共成员变量
        Field gender = clazz.getField("gender");
        System.out.println(gender); // public java.lang.String com.gch.d4_reflect_field.Student.gender
        System.out.println();
        // 获取单个的全部成员变量
        Field name = clazz.getDeclaredField("name");
        System.out.println(name);

        // 获取权限修饰符
        int modifiers = name.getModifiers();
        System.out.println(modifiers); // 2
        // 获取成员变量的名字
        String nameStr = name.getName();
        System.out.println(nameStr); // name
        // 获取到成员变量的数据类型
        Class nameType = name.getType();
        System.out.println(nameType); // class java.lang.String
        System.out.println();

        // 获取成员变量记录的值
        // 要获取值,首先要创建一个对象
        Student s = new Student("张三",24,"男");
        // 表示临时取消访问权限,暴力反射,表示权限被打开了
        name.setAccessible(true);
        String nameValue = (String) name.get(s);
//        Object nameValue = name.get(s);
        System.out.println(nameValue); // 张三

        // 修改对象里面记录的值
        // 参数1:修改哪个对象里面的值  参数2:修改成什么
        name.set(s,"李四");
        System.out.println(s); // Studentname = 李四, age = 24, gender = 男
    

 4. 反射获取成员方法

package com.gch.d5_reflect_method;

import java.io.IOException;

public class Student 
    private String name;
    private int age;


    public Student() 
    

    public Student(String name, int age) 
        this.name = name;
        this.age = age;
    

    /**
     * 获取
     * @return name
     */
    public String getName() 
        return name;
    

    /**
     * 设置
     * @param name
     */
    public void setName(String name) 
        this.name = name;
    

    /**
     * 获取
     * @return age
     */
    public int getAge() 
        return age;
    

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) 
        this.age = age;
    

    public void sleep()
        System.out.println("睡觉");
    

    private String eat(String something) throws IOException,NullPointerException,ClassCastException 
        System.out.println("在吃" + something);
        return "奥里给";
    

    private void eat(String something,int a)
        System.out.println("在吃" + something);
    


    public String toString() 
        return "Studentname = " + name + ", age = " + age + "";
    

package com.gch.d5_reflect_method;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

/**
   目标:利用反射获取成员方法
 */
public class MyReflectDemo 
    public static void main(String[] args) throws Exception 
        /*
         反射获取类的Method方法对象:
         1、Method getMethod(String name,Class...args);
             根据方法名和参数类型获得对应的方法对象,只能获得public的

         2、Method getDeclaredMethod(String name,Class...args);
             根据方法名和参数类型获得对应的方法对象,包括private的

         3、Method[] getMethods();
             获得类中的所有成员方法对象,返回数组,只能获得public修饰的且包含父类的

         4、Method[] getDeclaredMethods();
            获得类中的所有成员方法对象,返回数组,只获得本类申明的方法。

    Method的方法执行:
        Object invoke(Object obj, Object... args)
          参数一:触发的是哪个对象的方法执行。
          参数二: args:调用方法时传递的实际参数

      获取方法的修饰符
      获取方法的名字
      获取方法的形参
      获取方法的返回值
      获取方法的抛出的异常
         */

        // 1.获取class字节码文件的对象
        Class clazz = Class.forName("com.gch.d5_reflect_method.Student");

        // 2.获取里面所有的方法对象
        // 获取所有的公共成员方法对象(包含父类中所有的公共方法)
        Method[] methods1 = clazz.getMethods();
        for (Method method : methods1) 
            System.out.println(method);
        
        System.out.println();

        // 获取里面所有的方法对象(不能获取父类的,但是可以获取本类中私有的方法)
        Method[] methods2 = clazz.getDeclaredMethods();
        for (Method method : methods2) 
            System.out.println(method);
        

        // 获取单个的公共成员方法对象
        // 参数1:方法名  参数2:参数的形参

        // 获取指定的单个的所有成员方法对象
        Method m = clazz.getDeclaredMethod("eat", String.class);
        System.out.println(m); // private void com.gch.d5_reflect_method.Student.eat(java.lang.String)

        // 获取方法的修饰符
        int modifiers = m.getModifiers();
        System.out.println(modifiers); // 2

        // 获取方法的名字
        String name = m.getName();
        System.out.println(name); // eat

        // 获取方法的形参
        // 获取到方法参数的个数
        int count = m.getParameterCount();
        System.out.println(count); // 1
        // 获取到参数的类型
       Class[] types = m.getParameterTypes();
        for (Class type : types) 
            System.out.println(type); // class java.lang.String
        
        // 获取到参数的对象
        Parameter[] parameters = m.getParameters();
        for (Parameter parameter : parameters) 
            System.out.println(parameter); // java.lang.String arg0
        

        // 获取到方法抛出的异常
        Class[] exceptionTypes = m.getExceptionTypes();
        for (Class exceptionType : exceptionTypes) 
            System.out.println(exceptionType); // class java.io.IOException
//                                                class java.lang.NullPointerException
//                                                class java.lang.ClassCastException
        

        // 方法运行
     /*   Method的方法执行:invoke方法就可以运行你获取出来的方法
        Object invoke(Object obj, Object... args):运行方法
        参数一:表示方法的调用者,用obj对象调用该方法
        参数二: args:方法的形参,表示在调用方法时传递的实际参数(如果没有就不写)
        返回值:方法的返回值(如果没有就不写)*/
        Student s = new Student();
        // 表示临时取消访问权限,暴力反射,表示权限被打开
        m.setAccessible(true);
        // 参数一s:表示方法的调用者
        // 参数二"稀饭":表示在调用方法的时候传递的实际参数
        String result = (String) m.invoke(s,"稀饭"); // 在吃稀饭
       // 获取到方法的返回值(要方法运行之后才能获取到方法的返回值)
        System.out.println(result); // 奥里给
    

面试题:

你觉得反射好不好?好,有两个方向

  1. 第一个方向:反射可以无视修饰符访问类中的内容。但是这种操作在开发中一般不用,都是框架底层来用的。
  2. 第二个方向:反射可以跟配置文件结合起来使用,动态的创建对象并动态的调用方法。

 练习1:利用反射保存任意对象数据

package com.gch.d6_reflect_test;

public class Student 
    private String name;
    private int age;
    private char gender; // 性别
    private double height;
    private String hobby;


    public Student() 
    

    public Student(String name, int age, char gender, double height, String hobby) 
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.height = height;
        this.hobby = hobby;
    

    /**
     * 获取
     * @return name
     */
    public String getName() 
        return name;
    

    /**
     * 设置
     * @param name
     */
    public void setName(String name) 
        this.name = name;
    

    /**
     * 获取
     * @return age
     */
    public int getAge() 
        return age;
    

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) 
        this.age = age;
    

    /**
     * 获取
     * @return gender
     */
    public char getGender() 
        return gender;
    

    /**
     * 设置
     * @param gender
     */
    public void setGender(char gender) 
        this.gender = gender;
    

    /**
     * 获取
     * @return height
     */
    public double getHeight() 
        return height;
    

    /**
     * 设置
     * @param height
     */
    public void setHeight(double height) 
        this.height = height;
    

    /**
     * 获取
     * @return hobby
     */
    public String getHobby() 
        return hobby;
    

    /**
     * 设置
     * @param hobby
     */
    public void setHobby(String hobby) 
        this.hobby = hobby;
    

    public String toString() 
        return "Studentname = " + name + ", age = " + age + ", gender = " + gender + ", height = " + height + ", hobby = " + hobby + "";
    

package com.gch.d6_reflect_test;

public class Teacher 
    private String name;
    private double salary; // 工资,薪水
    
    public Teacher() 
    

    public Teacher(String name, double salary) 
        this.name = name;
        this.salary = salary;
    

    /**
     * 获取
     * @return name
     */
    public String getName() 
        return name;
    

    /**
     * 设置
     * @param name
     */
    public void setName(String name) 
        this.name = name;
    

    /**
     * 获取
     * @return salary
     */
    public double getSalary() 
        return salary;
    

    /**
     * 设置
     * @param salary
     */
    public void setSalary(double salary) 
        this.salary = salary;
    

    public String toString() 
        return "Teachername = " + name + ", salary = " + salary + "";
    

package com.gch.d6_reflect_test;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.Writer;
import java.lang.reflect.Field;

/**
   目标:对于任意一个对象,都可以把对象所有的字段名和值,保存到文件中去
 */
public class MyReflectDemo 
    public static void main(String[] args) throws Exception 
        Student s = new Student("小A",23,'女',167.5,"睡觉");
        Teacher t = new Teacher("播妞",10000);

        saveObject(s);
        System.out.println();
        saveObject(t);
    

    /**
     * 把对象里面所有的成员变量名和值保存到本地文件当中
     * @param obj
     */
    public static void saveObject(Object obj) throws Exception 
        // 1.获取类的字节码文件的对象:对象都有了,直接对象.getClass()即可,我真笨!
        Class clazz = obj.getClass();

        // 2.创建IO流
        BufferedWriter bw = new BufferedWriter(new FileWriter("day13-reflect-demo\\\\src\\\\a.txt",true)); // 追加数据管道

        // 3.获取到所有的成员变量对象
        Field[] fields = clazz.getDeclaredFields();

        for (Field field : fields) 
           // 一律暴力破解,临时取消访问取消,表示访问权限被打开
            field.setAccessible(true);
            // 获取成员变量的名字
            String name = field.getName();
            // 获取成员变量的值
            Object value = field.get(obj);
            // 写出数据
            bw.write(name + " = " + value);
            // 换行
            bw.newLine();
        
        // 关闭IO流,释放资源
        bw.close();
    

 练习2:利用反射动态的创建对象和运行方法

package com.gch.d7_reflect_test;

public class Student 
    private String name;
    private int age;


    public Student() 
    

    public Student(String name, int age) 
        this.name = name;
        this.age = age;
    

    public void study()
        System.out.println("学生在学习!");
    

    /**
     * 获取
     * @return name
     */
    public String getName() 
        return name;
    

    /**
     * 设置
     * @param name
     */
    public void setName(String name) 
        this.name = name;
    

    /**
     * 获取
     * @return age
     */
    public int getAge() 
        return age;
    

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) 
        this.age = age;
    

    public String toString() 
        return "Studentname = " + name + ", age = " + age + "";
    

package com.gch.d7_reflect_test;

public class Teacher 
    private String name;
    private double salary; // 薪水


    public Teacher() 
    

    public Teacher(String name, double salary) 
        this.name = name;
        this.salary = salary;
    

    public void teach()
        System.out.println("老师在教书!");
    

    /**
     * 获取
     * @return name
     */
    public String getName() 
        return name;
    

    /**
     * 设置
     * @param name
     */
    public void setName(String name) 
        this.name = name;
    

    /**
     * 获取
     * @return salary
     */
    public double getSalary() 
        return salary;
    

    /**
     * 设置
     * @param salary
     */
    public void setSalary(double salary) 
        this.salary = salary;
    

    public String toString() 
        return "Teachername = " + name + ", salary = " + salary + "";
    

package com.gch.d7_reflect_test;

import java.io.FileReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Properties;

/**
   目标:反射可以跟配置文件结合的方式,动态的创建对象,并调用方法
 */
public class MyReflectDemo 
    public static void main(String[] args) throws Exception 
        // 1.读取配置文件中的信息
        Properties prop = new Properties();
        // 创建字节输入流用于读取文件
        FileReader fr = new FileReader("day13-reflect-demo\\\\prop.properties");
        // 加载属性文件的键值对信息到属性集合prop中去
        prop.load(fr);
        // 释放资源
        fr.close();
        System.out.println(prop); // classname="com.gch.d7_reflect_test.Teacher", method=teach

        // 2.获取全类名和方法名
        String className = (String) prop.get("classname");
        String methodName = (String) prop.get("method");

        System.out.println(className); // "com.gch.d7_reflect_test.Teacher"
        System.out.println(methodName); // teach

        // 3.利用反射去创建对象并运行方法
        // 获取字节码文件的对象
        System.out.println(className);
        Class clazz = Class.forName(className);
        System.out.println(className);

        // 获取构造方法
        Constructor con = clazz.getDeclaredConstructor();
        con.setAccessible(true);
        Object o = con.newInstance();
        System.out.println(o);

        // 获取成员方法并运行
        Method method = clazz.getDeclaredMethod(methodName);
        method.setAccessible(true); // 暴力反射,表示权限被打开
        // 运行方法
        method.invoke(o);
    

 泛型擦除,反射可以绕过编译阶段为集合添加数据:

package com.gch.d8_reflect_genericity;

import java.lang.reflect.Method;
import java.util.ArrayList;

/**
   目标:反射的作用-绕过编译阶段为集合添加数据
   反射实现泛型擦除后,加入其他类型的元素
 */
public class ReflectDemo 
    public static void main(String[] args) throws Exception
        ArrayList<String> lists1 = new ArrayList<>();
        ArrayList<Integer> lists2 = new ArrayList<>();

        System.out.println(lists1.getClass()); // class java.util.ArrayList
        System.out.println(lists2.getClass()); // class java.util.ArrayList

        // 结论:同一个类编译后只产生一份class文件
        System.out.println(lists1.getClass() == lists2.getClass()); // true , 本质ArrayList.class
        System.out.println("==================================================================");

        ArrayList<Integer> lists3 = new ArrayList<>();
        lists3.add(23);
        lists3.add(22);
//        lists3.add("黑马"); 直接报错

        // 获取字节码文件对象
        Class clazz = lists3.getClass(); // ArrayList.class ===> public void add(E  e)
        // 获取add方法
        Method method = clazz.getDeclaredMethod("add",Object.class);
        // 运行获取到的add方法
        boolean rs = (boolean) method.invoke(lists3,"黑马");
        System.out.println(rs); // true

        System.out.println(lists3); // [23, 22, 黑马]
        System.out.println();
        System.out.println("==================突破泛型的限制还可以这样做============================");
        // 把lists3赋值给同类型的变量lists4
        ArrayList lists4 = lists3;
        // 给lists4集合添加元素
        lists4.add("白马");
        lists4.add(true);
        // 输出lists3集合
        System.out.println(lists3); // [23, 22, 黑马, 白马, true]
    

 

 

Java反射机制详解

java 反射 定义 功能 示例

概要:

Java反射机制详解

| |目录

  • 1反射机制是什么
  • 2反射机制能做什么
  • 3反射机制的相关API
  • ·通过一个对象获得完整的包名和类名
  • ·实例化Class类对象
  • ·获取一个对象的父类与实现的接口
  • ·获取某个类中的全部构造函数 - 详见下例
  • ·通过反射机制实例化一个类的对象
  • ·获取某个类的全部属性
  • ·获取某个类的全部方法
  • ·通过反射机制调用某个类的方法
  • ·通过反射机制操作某个类的属性
  • ·反射机制的动态代理
  • 4反射机制的应用实例
  • ·在泛型为Integer的ArrayList中存放一个String类型的对象。
  • ·通过反射取得并修改数组的信息
  • ·通过反射机制修改数组的大小
  • ·将反射机制应用于工厂模式

1反射机制是什么

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

2反射机制能做什么

反射机制主要提供了以下功能: 

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

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

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

  • 在运行时调用任意一个对象的方法;

  • 生成动态代理。

3反射机制的相关API

通过一个对象获得完整的包名和类名

Java | 复制

1

2

3

4

5

6

7

8

package net.xsoftlab.baike;

public class TestReflect {

    public static void main(String[] args) throws Exception {

        TestReflect testReflect = new TestReflect();

        System.out.println(testReflect.getClass().getName());

        // 结果 net.xsoftlab.baike.TestReflect

    }

}

实例化Class类对象

Java | 复制

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

package net.xsoftlab.baike;

public class TestReflect {

    public static void main(String[] args) throws Exception {

        Class<?> class1 = null;

        Class<?> class2 = null;

        Class<?> class3 = null;

        // 一般采用这种形式

        class1 = Class.forName("net.xsoftlab.baike.TestReflect");

        class2 = new TestReflect().getClass();

        class3 = TestReflect.class;

        System.out.println("类名称   " + class1.getName());

        System.out.println("类名称   " + class2.getName());

        System.out.println("类名称   " + class3.getName());

    }

}

获取一个对象的父类与实现的接口

Java | 复制

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

package net.xsoftlab.baike;

import java.io.Serializable;

public class TestReflect implements Serializable {

    private static final long serialVersionUID = -2862585049955236662L;

    public static void main(String[] args) throws Exception {

        Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect");

        // 取得父类

        Class<?> parentClass = clazz.getSuperclass();

        System.out.println("clazz的父类为:" + parentClass.getName());

        // clazz的父类为: java.lang.Object

        // 获取所有的接口

        Class<?> intes[] = clazz.getInterfaces();

        System.out.println("clazz实现的接口有:");

        for (int i = 0; i < intes.length; i++) {

            System.out.println((i + 1) + ":" + intes[i].getName());

        }

        // clazz实现的接口有:

        // 1:java.io.Serializable

    }

}

获取某个类中的全部构造函数 - 详见下例

通过反射机制实例化一个类的对象

Java | 复制

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

package net.xsoftlab.baike;

import java.lang.reflect.Constructor;

public class TestReflect {

    public static void main(String[] args) throws Exception {

        Class<?> class1 = null;

        class1 = Class.forName("net.xsoftlab.baike.User");

        // 第一种方法,实例化默认构造方法,调用set赋值

        User user = (User) class1.newInstance();

        user.setAge(20);

        user.setName("Rollen");

        System.out.println(user);

        // 结果 User [age=20, name=Rollen]

        // 第二种方法 取得全部的构造函数 使用构造函数赋值

        Constructor<?> cons[] = class1.getConstructors();

        // 查看每个构造方法需要的参数

        for (int i = 0; i < cons.length; i++) {

            Class<?> clazzs[] = cons[i].getParameterTypes();

            System.out.print("cons[" + i + "] (");

            for (int j = 0; j < clazzs.length; j++) {

                if (j == clazzs.length - 1)

                    System.out.print(clazzs[j].getName());

                else

                    System.out.print(clazzs[j].getName() + ",");

            }

            System.out.println(")");

        }

        // 结果

        // cons[0] (java.lang.String)

        // cons[1] (int,java.lang.String)

        // cons[2] ()

        user = (User) cons[0].newInstance("Rollen");

        System.out.println(user);

        // 结果 User [age=0, name=Rollen]

        user = (User) cons[1].newInstance(20"Rollen");

        System.out.println(user);

        // 结果 User [age=20, name=Rollen]

    }

}

class User {

    private int age;

    private String name;

    public User() {

        super();

    }

    public User(String name) {

        super();

        this.name = name;

    }

    public User(int age, String name) {

        super();

        this.age = age;

        this.name = name;

    }

    public int getAge() {

        return age;

    }

    public void setAge(int age) {

        this.age = age;

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

    @Override

    public String toString() {

        return "User [age=" + age + ", name=" + name + "]";

    }

}

获取某个类的全部属性

Java | 复制

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

package net.xsoftlab.baike;

import java.io.Serializable;

import java.lang.reflect.Field;

import java.lang.reflect.Modifier;

public class TestReflect implements Serializable {

    private static final long serialVersionUID = -2862585049955236662L;

    public static void main(String[] args) throws Exception {

        Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect");

        System.out.println("===============本类属性===============");

        // 取得本类的全部属性

        Field[] field = clazz.getDeclaredFields();

        for (int i = 0; i < field.length; i++) {

            // 权限修饰符

            int mo = field[i].getModifiers();

            String priv = Modifier.toString(mo);

            // 属性类型

            Class<?> type = field[i].getType();

            System.out.println(priv + " " + type.getName() + " " + field[i].getName() + ";");

        }

         

        System.out.println("==========实现的接口或者父类的属性==========");

        // 取得实现的接口或者父类的属性

        Field[] filed1 = clazz.getFields();

        for (int j = 0; j < filed1.length; j++) {

            // 权限修饰符

            int mo = filed1[j].getModifiers();

            String priv = Modifier.toString(mo);

            // 属性类型

            Class<?> type = filed1[j].getType();

            System.out.println(priv + " " + type.getName() + " " + filed1[j].getName() + ";");

        }

    }

}

获取某个类的全部方法

Java | 复制

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

package net.xsoftlab.baike;

import java.io.Serializable;

import java.lang.reflect.Method;

import java.lang.reflect.Modifier;

public class TestReflect implements Serializable {

    private static final long serialVersionUID = -2862585049955236662L;

    public static void main(String[] args) throws Exception {

        Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect");

        Method method[] = clazz.getMethods();

        for (int i = 0; i < method.length; ++i) {

            Class<?> returnType = method[i].getReturnType();

            Class<?> para[] = method[i].getParameterTypes();

            int temp = method[i].getModifiers();

            System.out.print(Modifier.toString(temp) + " ");

            System.out.print(returnType.getName() + "  ");

            System.out.print(method[i].getName() + " ");

            System.out.print("(");

            for (int j = 0; j < para.length; ++j) {

                System.out.print(para[j].getName() + " " "arg" + j);

                if (j < para.length - 1) {

                    System.out.print(",");

                }

            }

            Class<?> exce[] = method[i].getExceptionTypes();

            if (exce.length > 0) {

                System.out.print(") throws ");

                for (int k = 0; k < exce.length; ++k) {

                    System.out.print(exce[k].getName() + " ");

                    if (k < exce.length - 1) {

                        System.out.print(",");

                    }

                }

            else {

                System.out.print(")");

            }

            System.out.println();

        }

    }

}

通过反射机制调用某个类的方法

Java | 复制

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

package net.xsoftlab.baike;

import java.lang.reflect.Method;

public class TestReflect {

    public static void main(String[] args) throws Exception {

        Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect");

        // 调用TestReflect类中的reflect1方法

        Method method = clazz.getMethod("reflect1");

        method.invoke(clazz.newInstance());

        // Java 反射机制 - 调用某个类的方法1.

        // 调用TestReflect的reflect2方法

        method = clazz.getMethod("reflect2"int.class, String.class);

        method.invoke(clazz.newInstance(), 20"张三");

        // Java 反射机制 - 调用某个类的方法2.

        // age -> 20. name -> 张三

    }

    public void reflect1() {

        System.out.println("Java 反射机制 - 调用某个类的方法1.");

    }

    public void reflect2(int age, String name) {

        System.out.println("Java 反射机制 - 调用某个类的方法2.");

        System.out.println("age -> " + age + ". name -> " + name);

    }

}

通过反射机制操作某个类的属性

Java | 复制

1

2

3

4

5

6

7

8

9

10

11

12

13

14

package net.xsoftlab.baike;

import java.lang.reflect.Field;

public class TestReflect {

    private String proprety = null;

    public static void main(String[] args) throws Exception {

        Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect");

        Object obj = clazz.newInstance();

        // 可以直接对 private 的属性赋值

        Field field = clazz.getDeclaredField("proprety");

        field.setAccessible(true);

        field.set(obj, "Java反射机制");

        System.out.println(field.get(obj));

    }

}

反射机制的动态代理

Java | 复制

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

// 获取类加载器的方法

TestReflect testReflect = new TestReflect();

        System.out.println("类加载器  " + testReflect.getClass().getClassLoader().getClass().getName());

package net.xsoftlab.baike;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

//定义项目接口

interface Subject {

    public String say(String name, int age);

}

// 定义真实项目

class RealSubject implements Subject {

    public String say(String name, int age) {

        return name + "  " + age;

    }

}

class MyInvocationHandler implements InvocationHandler {

    private Object obj = null;

    public Object bind(Object obj) {

        this.obj = obj;

        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);

    }

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

        Object temp = method.invoke(this.obj, args);

        return temp;

    }

}

/**

 * 在java中有三种类类加载器。

 

 * 1)Bootstrap ClassLoader 此加载器采用c++编写,一般开发中很少见。

 

 * 2)Extension ClassLoader 用来进行扩展类的加载,一般对应的是jrelibext目录中的类

 

 * 3)AppClassLoader 加载classpath指定的类,是最常用的加载器。同时也是java中默认的加载器。

 

 * 如果想要完成动态代理,首先需要定义一个InvocationHandler接口的子类,已完成代理的具体操作。

 

 * @author xsoftlab.net

 

 */

public class TestReflect {

    public static void main(String[] args) throws Exception {

        MyInvocationHandler demo = new MyInvocationHandler();

        Subject sub = (Subject) demo.bind(new RealSubject());

        String info = sub.say("Rollen"20);

        System.out.println(info);

    }

}

4反射机制的应用实例

在泛型为Integer的ArrayList中存放一个String类型的对象。

Java | 复制

1

2

3

4

5

6

7

8

9

10

11

package net.xsoftlab.baike;

import java.lang.reflect.Method;

import java.util.ArrayList;

public class TestReflect {

    public static void main(String[] args) throws Exception {

        ArrayList<Integer> list = new ArrayList<Integer>();

        Method method = list.getClass().getMethod("add", Object.class);

        method.invoke(list, "Java反射机制实例。");

        System.out.println(list.get(0));

    }

}

通过反射取得并修改数组的信息

Java | 复制

1

2

3

4

5

6

7

8

9

10

11

12

13

package net.xsoftlab.baike;

import java.lang.reflect.Array;

public class TestReflect {

    public static void main(String[] args) throws Exception {

        int[] temp = { 12345 };

        Class<?> demo = temp.getClass().getComponentType();

        System.out.println("数组类型: " + demo.getName());

        System.out.println("数组长度  " + Array.getLength(temp));

        System.out.println("数组的第一个元素: " + Array.get(temp, 0));

        Array.set(temp, 0100);

        System.out.println("修改之后数组第一个元素为: " + Array.get(temp, 0));

    }

}

通过反射机制修改数组的大小

Java | 复制

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

package net.xsoftlab.baike;

import java.lang.reflect.Array;

public class TestReflect {

    public static void main(String[] args) throws Exception {

        int[] temp = { 123456789 };

        int[] newTemp = (int[]) arrayInc(temp, 15);

        print(newTemp);

        String[] atr = { "a""b""c" };

        String[] str1 = (String[]) arrayInc(atr, 8);

        print(str1);

    }

    // 修改数组大小

    public static Object arrayInc(Object obj, int len) {

        Class<?> arr = obj.getClass().getComponentType();

        Object newArr = Array.newInstance(arr, len);

        int co = Array.getLength(obj);

        System.arraycopy(obj, 0, newArr, 0, co);

        return newArr;

    }

    // 打印

    public static void print(Object obj) {

        Class<?> c = obj.getClass();

        if (!c.isArray()) {

            return;

        }

        System.out.println("数组长度为: " + Array.getLength(obj));

        for (int i = 0; i < Array.getLength(obj); i++) {

            System.out.print(Array.get(obj, i) + " ");

        }

        System.out.println();

    }

}

将反射机制应用于工厂模式

Java | 复制

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

package net.xsoftlab.baike;

interface fruit {

    public abstract void eat();

}

class Apple implements fruit {

    public void eat() {

        System.out.println("Apple");

    }

}

class Orange implements fruit {

    public void eat() {

        System.out.println("Orange");

    }

}

class Factory {

    public static fruit getInstance(String ClassName) {

        fruit f = null;

        try {

            f = (fruit) Class.forName(ClassName).newInstance();

        catch (Exception e) {

            e.printStackTrace();

        }

        return f;

    }

}

/**

 * 对于普通的工厂模式当我们在添加一个子类的时候,就需要对应的修改工厂类。 当我们添加很多的子类的时候,会很麻烦。

 * Java 工厂模式可以参考

 * http://baike.xsoftlab.net/view/java-factory-pattern

 

 * 现在我们利用反射机制实现工厂模式,可以在不修改工厂类的情况下添加任意多个子类。

 

 * 但是有一点仍然很麻烦,就是需要知道完整的包名和类名,这里可以使用properties配置文件来完成。

 

 * java 读取 properties 配置文件 的方法可以参考

 * http://baike.xsoftlab.net/view/java-read-the-properties-configuration-file

 

 * @author xsoftlab.net

 */

public class TestReflect {

    public static void main(String[] args) throws Exception {

        fruit f = Factory.getInstance("net.xsoftlab.baike.Apple");

        if (f != null) {

            f.eat();

        }

    }

}

以上是关于Java反射机制的主要内容,如果未能解决你的问题,请参考以下文章

Java反射机制深入详解

Java-反射机制(超详解)

Java---反射机制

Java后端总结

java反射机制

java的反射机制