Java高级——Java反射机制


一. 什么是反射?


  • 反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。

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

  • 简单的来说,反射机制指的是程序在运行时能够获取 自身的信息。在Java中只要给定类的名字,那么就可以根据反射来获取类的所有信息。包括访问修饰符、父类、实现的接口、属性和方法的所有信息,并可在运行时创建对象、修改属性(包括私有的)、调用方法(包括私有的)


二. 为什么要用反射机制?直接创建对象不就可以了吗? 这就涉及到了动态与静态的概念。

  • 静态编译:在编译时确定类型,绑定对象,即通过。
    如: Student student = new Student();

  • 动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了Java的灵活性,体现了多态的应用,用以降低类之间的耦合性。
    如:Class.forName("com.test.book.Student").newInstance();

  • 优点:反射机制的优点就是可以实现动态创建对象和编译,体现出很大的灵活性。

  • 缺点:它的缺点是对性能有影响,使用反射基本上是一种解释操作,这种操作总是慢于直接执行的相同操作。


三.Java反射机制主要提供了一下功能:

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的的成员变量和方法
  • 在运行时调用任意一个对象的方法

四.Class对象:Class对象是Reflection的起源,想要操纵类中的属性和方法,都必须从获取Class对象开始。

  • 类是程序的一部分,每个类都有一个Class对象。换言之,每当编写并编译了一个新类,就回产生一个与之对应的Class对象。
  • Class类没有公共构造方法。Class对象是在加载类时由Java虚拟机以及通过调用类加载器中的方法自动构造的,因此不能显示的构造一个Class对象。

获取Class对象的方法:

//创建一个类
class Person {

    private String name;

    public Person() {

    }

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

    public String getName() {
        return name;
    }
}
  • object.getClass();获取指定实例的Class,例:
 public static void main(String[] args) {
      //通过 对象名.class 获取Class对象
      Person person = new Person();
      Class personclass = person.getClass();
       System.out.println(personclass.getName());
  }
  • class.getSuperclass();获取当前类的继承的父类的Class,如:
 public static void main(String[] args) {
      //通过 对象名.class 获取Class对象
      Person person = new Person();
      Class personclass = person.getClass();
      System.out.println(personclass.getName());
      //获取personclass父类的Class
      Class P_class = personclass.getSuperclass();
      System.out.println(P_class.getName());
  }
  • .class语法:
 public static void main(String[] args) {
      //通过 .class获取Class对象
       Class personclass = Person.class;
       System.out.println(personclass.getName());
  }
  • Class.forName(类名); 用Class的静态方法,传入类的全称(包名+类名)即可。(此方法用的比较多)
  public static void main(String[] args) {
       //通过 Class.forName()获取Class对象
        Class personclass = null;
       try {
           personclass = Class.forName("com.test.book.Person");
           System.out.println(personclass.getName());
       } catch (ClassNotFoundException e) {
           e.printStackTrace();
       }
   }
  • Primitive.TYPE 基本类型的包装类获取Class的方式。
  public static void main(String[] args) {
       //通过 Primitive.TYPE获取基本类型的包装类的Class对象
        Class intclass = Integer.TYPE;
        Class doubleclass = Double.TYPE;
        
        System.out.println(intclass.getName());
        System.out.println(doubleclass.getName());
   }

五.java.lang.reflect库和Java反射常用API

Class类与java.lang.reflect类库一起对反射的概念进行支持。
java.lang.reflect包下:

  • Fidld类:代表成员变量(成员变量也成为类的属性)
  • Method类:代表类的方法
  • Constructor类:代表类的构造方法
  • Array类:提供了动态创建数组,以及访问数组元素的静态方法

通过反射机制实例化对象:

  • 实例化无参构造函数的对象,两种方式:

    Class.newInstance();

    Class.getConstructor(new Class[]{}).newInstance(new Object[]{});

  • 实例化带参构造函数的对象

    Class.getConstructor(Class... parameterTypes).newInstance(Object... initargs);

通过反射获取并调用方法:

  • 获取当前类及超类的public Method
    Method[] arrMethods = classType.getMethods();
  • 获取当前类申明的所有Method(包括私有的)
    Method[] arrMethods = classType.getDeclaredMethod();
  • 获取当前类及超类指定的public Method
    Method arrMethod = classType.getMethod(String name,Class... parameterTypes);
  • 获取当前类及超类申明的指定的Method(包括私有的)
    Method arrMethod = classType.getDeclaredMethod(String name,Class... parameterTypes);
  • 通过反射动态运行指定Method
    Object obj = method.invoke(Object obj,Object... args)

通过反射获取并调用属性:

  • 获取当前类及超类的public Field
    Field[] arrFields = classType.getFields();

  • 获取当前类申明的所有Field(包括私有的)
    Field[] arrFields = classType.getDeclaredFields();

  • 获取当前类及超类指定的public Field
    Field field = classType.getField(String name);

  • 获取当前类及超类申明的指定的Field(包括私有的)
    Field field = classType.getDeclaredField(String name);

  • 通过反射动态设定Field的值
    field.set(Object obj,Object value)

  • 通过反射动态获取Field的值
    field.get(Object obj)

    示例:
    我们先新建一个类,我们等下用Java的反射机制来操作这个类:

public class Employee{

    private String name;
    private int age;

    public Employee(){

    }

    public Employee(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

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

    private void Work(){
        System.out.println("wroking...");
    }
}

通过反射机制构建一个Employee实例对象:

  • 通过Class.newInstance()构建Employee实例对象((默认调用无参数构造方法)
  public static void main(String [] args) throws Exception{
  //获取Employee类所关联的Class对象
    Class employeeClass = Class.forName("book.ly.com.testjava.Employee");
    //通过Class.newInstance()构建Employee实例对象(默认调用无参数构造方法)
    Employee employee = (Employee) employeeClass.newInstance();
  }
  • 通过Class.getConstructor(new Class[]{}).newInstance(new Object[]{});构建Employee实例对象
  public static void main(String [] args) throws Exception{
  //获取Employee类所关联的Class对象
    Class employeeClass = Class.forName("book.ly.com.testjava.Employee");
  //调用指定的构造方法构建Employee实例对象(无参数构造方法)
    Constructor constructor  = employeeClass.getConstructor(new Class[]{});
    Employee employee = (Employee) constructor.newInstance(new Object[]{});
  }
  • 调用带参数构造方法构建Employee实例对象
  public static void main(String [] args){
  //获取Employee类所关联的Class对象
    Class employeeClass = Class.forName("book.ly.com.testjava.Employee");
    Constructor constructor  = employeeClass.getConstructor(new Class[]{String.class,int.class});
    Employee employee = (Employee) constructor.newInstance("张三",20);
  }

获取Class对象申明的方法并调用(包括私有的)

 public static void main(String [] args) throws Exception{
  //获取Employee类所关联的Class对象
    Class employeeClass = Class.forName("book.ly.com.testjava.Employee");
    Constructor constructor  = employeeClass.getConstructor(new Class[]{String.class,int.class});
    Employee employee = (Employee) constructor.newInstance("张三",20);
    
      //获取Class对象申明的所有方法(包括私有的)
     Method[] methods = employeeClass.getDeclaredMethods();
     for (Method method:methods){
     //打印方法的方法名 + 方法的修饰符(1-public,2-private...)
     System.out.println(method.getName() + "---" + method.getModifiers());
     }
     
    //获取Class对象指定的方法(包括私有的)
    Method method = employeeClass.getDeclaredMethod("toString",new Class[]{});
    //方法调用
    String str = (String)method.invoke(employee,new Object[]{});
    System.out.println(str);
    
    //调用私有方法
    Method method1 = employeeClass.getDeclaredMethod("Work");
    method1.setAccessible(true);//设置方法能被访问
    method1.invoke(employee);
}

获取Class对象申明的属性(包括私有的)

 public static void main(String [] args) throws Exception{
  //获取Employee类所关联的Class对象
    Class employeeClass = Class.forName("book.ly.com.testjava.Employee");
    Constructor constructor  = employeeClass.getConstructor(new Class[]{String.class,int.class});
    Employee employee = (Employee) constructor.newInstance("张三",20);
    
    //获取Class对象申明的所有属性(包括私有的)
    Field[] fields = employeeClass.getDeclaredFields();
    for (Field field:fields){
    //由于有私有的变量,我们设置为能被访问
    field.setAccessible(true);
    //打印获取到的属性的值
    System.out.println( field.get(employee));
    }
    
    //获取Class对象指定的属性(包括私有的)
    Field field = employeeClass.getDeclaredField("name");
    field.setAccessible(true);//设置为能被访问
    field.set(employee,"李四");//设置变量值
    System.out.println( field.get(employee));
  }

使用Java的反射机制创建数组:

 public static void main(String [] args) throws Exception{
        //创建一个一维数组(String)
        Class tClass = Class.forName("java.lang.String");
        Object array = Array.newInstance(tClass, 5);//tClass 为数据类型的Class对象 5为数组长度
        //给array数组下标为 3 的元素赋值为“abc”
        Array.set(array,3,"abc");
        //取出array数组下标为 3 的值 并打印
        System.out.println(Array.get(array,3));

        //创建一个二维数组
        int [] dimens = {3,3};//3行  3列
        //创建一个int 类型 3行3列 的数组
        Object array2 = Array.newInstance(int.class,dimens);
        //给第二行 第二列 的元素赋值
        Object arrayobj = Array.get(array2,1);//获取第二行(一个一维数组)
        //给第二列赋值 (给获取的第二行的一维数组的第二个元素赋值)
        Array.set(arrayobj,1,10);
        int [] [] arr = (int[][]) array2;//强转为二维数组对象
        System.out.println(arr[1][1]);//取出第二行第二列的值并打印
  }

总结:

  • 只要用到反射,就必须先获取到Class对象
  • 没有方法能够获得当前类的超类的private属性和方法,你必须先通过getSuperclass()找到超类以后再去尝试获取
  • 通常情况下即使是当前类,private属性和方法也是不能访问的,你需要设置压制权限setAccessibleible(true)来获得private的访问权。但说实话,这已经破坏了面向对象的规则,所以除非万不得已,请尽量少用。

你可能感兴趣的:(Java高级——Java反射机制)