反射

基本概念

什么是反射

在程序运行过程中分析类的一种能力;

程序运行先在.java源文件中编写源代码,然后通过编译将其编译成.class的字节码文件,此时就可以通过对象 . 的形式来调用具体的方法。把这个动作反过来就可以理解成反射。比如:现在调用某一个对象的某一个方法,此时要先获取到后缀名为.class的字节码文件,然后通过字节码文件来反问类中的构造方法、成员方法、成员变量

反射能做什么

①分析类  通过类加载器实现

类加载器ClassLoader :负责将类的字节码文件加载到内存中,并生成对应的Class对象

Class对象 :指的是java.lang.Class类的对象,也叫字节码文件对象,每个Class对象对应一个字节码文件

类的加载时机 :①创建类的实例  eg:Student stu=new Student(); 类加载器会将学生类的字节码文件加载到内存    中,如果再次编写这行代码来创建学生类的对象,那么类加载器不会再加载该类的字节码文件,因为一个类的字节码文件只会加载一次。

                             ②访问类的静态成员:eg:调用Calendar.getInstance();方法,类加载器会先将Calendar类的字节码文件加载到内存中。

                             ③初始化类的子类 eg:class User extends Person{}     User user=new User();

先加载Person类的字节码文件,然后加载User类的字节码文件

                             ④通过反射方式创建类的Class对象:Class class="Class".forName("类的正名");正名指的是包名+类名 eg:cn.itcast.demo1.Student

获取Class对象的三种方式 :①Object类中的getClass()方法,即所有类中都有这个方法

                                                    Class class=对象名.getClass();

                                                 ②类的静态属性  Class class=类名.class;

                                                 ③通过Class类的静态方法:  Class class="Class".forName("类的正名");

加载并初始化一个类--->查看类的所有属性和方法

一个源文件.java对应一个字节码文件.class

package com.wang.reflect;

public class classDemo01 {
   public static void main(String[] args) throws ClassNotFoundException {
       //需求:获取Class对象
       //1、getClass()方法创建字节码文件
       Student stu=new Student();
       Class class1=stu.getClass();

       //2、类名.class的方式
       Class class2=Student.class;

       //3、Class class="Class".forName("类的正名");
       Class class3=Class.forName("com.wang.reflect.Student");
       //这三种方式获取到的字节码文件是同一个
       System.out.println(class1.equals(class2));
       System.out.println(class3.equals(class2));
  }
}
class Student{

}

②查看并使用对象   查看一个对象的所有属性和方法,使用对象的任意属性和方法

A.反射方式获取构造方法并使用

Constructor<T> 对象 :构造器对象,属于java.base模块,java.lang.reflect包下的类

通过Class对象获取构造器对象:a、getConstructor(Class<?>...parameterTypes):返回一个Constructor对象,只

                                                             能获取公共的构造函数。Class<?>...:可变参数,代表Class类型的数组

                                                             ?:通配符,代表不确定的任意类型

                                                    b、getDeclaredConstructor(Class<?>...parameterTypes):返回一个Constructor

                                                          对象,可以获取私有构造函数

                                                   c、getConstructors()  获取该类中所有的(不含私有)构造函数的数组

Constructor的常用方法:a、String getName();  返回构造函数名

                                            b、T newInstance(Object... initargs):使用此构造函数和指定参数创建并初始化对象

package com.wang.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class constructorDemo01 {
   public static void main(String[] args) throws Exception {
       //通过反射方式创建Student1类型的对象
       //1、获取Student1类的字节码文件
       Class clazz=Student1.class;
       // 2、根据字节码文件,获取指定的构造器对象
       /*获取公共的无参构造
       Constructor con1=clazz.getConstructor();
       System.out.println(con1);

        */
          //获取公共的有参构造
       Constructor con2=clazz.getConstructor(String.class);
       System.out.println(con2);

       /*获取所有的非私有构造方法
       Constructor[] cons=clazz.getConstructors();
       for (Constructor con:cons) {
           System.out.println(con);
       }

        */
       //获取私有的构造方法
       Constructor con3=clazz.getDeclaredConstructor(int.class);
       System.out.println(con3);
       //获取构造器的名字,看看它是那个类的构造器
       String name=con3.getName();
       System.out.println(name);
       //3、根据构造器对象和参数,创建对象
       Student1 stu=(Student1) con2.newInstance("王迎婧");
       System.out.println(stu);
       //打印结果


  }
}
class Student1{
   public Student1(){}
   public Student1(String name){
       System.out.println("您输入的name的值是:"+name);
  }
   private Student1(int age){
       System.out.println("您输入的age的值是:"+age);
  }
}

B、反射方式获取成员方法并使用

Method对象 :方法对象,属于java.base模块,java.lang.reflect包

通过Class对象获取方法:a、getMethod(String name,Class<?>...parameterTypes):返回一个Method对象,仅

                                                  公共成员方法。name:要获取的方法名 parameterTypes:方法的参数列表

                                           b、getDeclaredMethod(String,class<?>...):返回一个Method对象,可获取私有方法

                                           c、getMethods():返回此类所有(不含私有)方法的数组

Method常用方法:a、String getName():返回方法名

                                 b、Object invoke(Object obj,Object... args) :在指定对象上调用此方法,参数为args

                                        obj是对象名

package com.wang.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Map;

public class methodDemo01 {
   public static void main(String[] args) throws Exception{
       //需求:反射方式获取Student2类中的成员方法并调用
       //1、获取Student2类的字节码文件
       Class clazz=Student2.class;
       //2、获取该类的构造器对象,然后创建Student2类的对象
       Constructor con =clazz.getConstructor();
       Student2 stu=(Student2)con.newInstance();
       System.out.println(stu);
       //3、获取该类的成员方法对象,然后调用此方法
       Method method=clazz.getMethod("show");
       System.out.println(method.getName());//打印方法名
       //调用方法
       method.invoke(stu);//方法返回时void,不需要变量做接收
       System.out.println("============================================");
       Method method1=clazz.getMethod("show2", int.class);
       System.out.println(method1.getName());
       method1.invoke(stu,3);
       System.out.println("===============================================");
       Method method2=clazz.getDeclaredMethod("show3", int.class, int.class);
       System.out.println(method2.getName());
       //因为是私有方法,所以要用暴力反射
       method2.setAccessible(true);
       int i= (int) method2.invoke(stu,2,3);//不能用int接收Object类型的数据
       System.out.println("输入的两个数之和为:"+i);
       
       //获取类中所有的非私有成员方法
       Method[] methods=clazz.getMethods();
       for (Method m:methods) {
           System.out.println(m);
      }
  }
}
class Student2{
   public Student2(){}
   public void show(){
       System.out.println("我是公有的空参方法");
  }
   public void show2(int a){
       System.out.println("我是公共的带参方法,您传入的a的值为:"+a);
  }
   private int  show3(int a,int b){
       System.out.println("我是带参的私有方法");
       return a+b;
  }

}

需求:通过反射获取类的setter方法,使用该方法为属性赋值

分析:setter方法的方法名由set和属性名(首字母大写)组成:setName

           setter方法有且只有一个参数,参数类型为属性的类型:setName(String name)

           setter方法为public修饰的方法,反射获取该方法使用getMethod(class<?>...);

           根据上述分析分别为属性name、age赋值并使用

package com.wang.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Method;

public class methodDemo02 {
   public static void main(String[] args) throws Exception{
       //需求:通过反射获取类的setter方法,使用该方法为属性赋值
       Class clazz=Student3.class;
       Constructor con=clazz.getConstructor();
       Student3 stu=(Student3)con.newInstance();
       //System.out.println(stu);
       Method method=clazz.getMethod("setName", String.class);
       Method method1=clazz.getMethod("setAge", int.class);

       method.invoke(stu,"王迎婧");
       method1.invoke(stu,23);
       System.out.println(stu);

  }
}
class Student3{
   private String name;
   private int age;
   public Student3(){}
   public Student3(String name,int age){
       this.name=name;
       this.age=age;
  }

   public void setName(String name) {
       this.name = name;
  }

   public String getName() {
       return name;
  }

   public void setAge(int age) {
       this.age = age;
  }

   public int getAge() {
       return age;
  }

   @Override
   public String toString() {
       return "Student3{" +
               "name='" + name + '\'' +
               ", age=" + age +
               '}';
  }
}

C、反射方式获取成员变量并使用

Field对象 : 域(属性、成员变量)对象,属于java.base模块,java.lang.reflect包

通过Class对象获取属性  getField(String name) 返回一个Field对象,仅公共属性。

                                          getDeclaredField(String name)   获取私有属性

                                          getDeclaredFields() 返回此类所有(含私有)属性的数组

Field的常用方法:void set(Object obj,Object value)   设置obj对象的指定属性为value

                               void setAccessible(boolean flag)    将此属性的可访问性设置为指定布尔值 即:是否开启暴力     反射

package com.wang.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

public class fieldDemo01 {
   public static void main(String[] args) throws Exception {
         //通过反射获取成员变量并使用
       //1、先获取Student类的字节码文件
       Class clazz=Student4.class;
       //2、通过字节码对象获取构造器对象,然后创建学生类对象
       Constructor con=clazz.getConstructor();
       Student4 stu=(Student4)con.newInstance();
       Student4 stu1=(Student4)clazz.getConstructor().newInstance();//第二种写法 链式编程
       System.out.println(stu);
       System.out.println(stu1);
       //3、设置学生对象的各个属性值
       Field field1=clazz.getField("name");
       field1.set(stu,"王迎婧");
       field1.set(stu1,"王迎婧");
       Field field=clazz.getDeclaredField("age");
       field.setAccessible(true);//开启暴力反射
       field.set(stu,23);
       field.set(stu1,22);
       System.out.println(stu);
       System.out.println(stu1);
  }
}
class Student4{
   public Student4(){}
   public String name;
   private int age;

   @Override
   public String toString() {
       return "Student4{" +
               "name='" + name + '\'' +
               ", age=" + age +
               '}';
  }
}

反射的使用场景

①构建通用的工具

②搭建具有高度灵活性和拓展性的系统框架

posted @ 2019-11-25 16:20  王迎婧  阅读(302)  评论(0编辑  收藏  举报