JAVA反射机制总结

JAVA反射机制总结

反射的概述

什么是反射?

  • 反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改他状态或行为的一种能力。
  • JAVA反射机制是在运行状态中,对于任何一个类,都能知道这个类的所有属性和方法;对于任何一个对象,都能够调用他的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能成为Java语言的反射机制。
  • 简单的来说反射机制指的的是程序在运行是能够获取自身的信息。在java中,只要给定类的名字,那么就可以通过反射机制来获得类的所有信息。包括其访问修饰符、父类、实现的接口、属性和方法的所有信息,并可在运行时创建对象、修改属性(包括私有的)、调用方法(包括私有的)。

为什么用反射机制?

  • 初次接触反射的概念,不经会想,直接创建对象不就可以了么,这里涉及到动态和静态的概念。
  • 静态编译:在编译时确定类型,绑定对象,即通过。
Student stu = new Student("zhangsan",30);
  • 动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多台的应用,用以降低类之间的耦合性。
Class.forName("com.Lct.Person.Student").newInstance();
  • 一句话,反射机制的优点是可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发中。
  • 他的缺点是对性能的影响。使用反射基本上是一种解释操作,这类操作总是慢于直接执行的相同操作,所以什么时候使用反射,就要靠业务的需求、大小,以及经验的积累来决定。

反射机制的关键

Java的反射机制的实现要借助于class类和java.lang.reflect包下4个类: Constructor, Field, Method,Array

Class类 - 代表类的Class对象
Constructor类-代表类的构造方法
Field类-代表类的属性
Method类-代表类的方法
Array类 - 提供动态创建数组,以及访问数组元素的静态方法
通过前四个对象我们可以粗略的看到一个类的各个组成部分
Array类会在最后有所简述。

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

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

获取Class对象

获取Class对象的多种方式

代码示例(获取Class对象)

package com.Lct.classtype;

public class ClassDemo {

    public static void main(String[] args) {

        //使用实例对象.getClass()
        /*Employee employee = new Employee("zhangsan", 18);
        Class<?> class1 = employee.getClass();
        System.out.println(class1.getName());
        System.out.println(class1.getSuperclass().getName());*/

        //使用类名.Class
        /*Class<?> class1 = Employee.class;
        System.out.println(class1.getName());
        System.out.println(class1.getSuperclass().getName());*/

        //使用Class.forName("")
        /*try {
            Class<?> class1 = Class.forName("com.Lct.classtype.Employee");
            System.out.println(class1.getName());
            System.out.println(class1.getSuperclass().getName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }*/

        //基本数据类型的Class对象
/*      Class <?> class1 = int.class;
        System.out.println(class1.getName());
        //基本数据类型没有父类,下面代码会报NullPointerException异常
        //System.out.println(class1.getSuperclass().getName());
*/       

        //包装类获取基本数据类型的Class对象
/*      Class <?> class1 = Integer.TYPE;
        System.out.println(class1.getName());
        //基本数据类型没有父类,下面代码同样会报NullPointerException异常
        //System.out.println(class1.getSuperclass().getName());
*/  

        //获取包装类的Class对象
        Class <?> class1 = Integer.class;
        System.out.println(class1.getName());
        System.out.println(class1.getSuperclass().getName());
    }
}

class Employee {

    private String name;
    private int age;

    public Employee(String name, int age) {
        super();
        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;
    }

}

获得构造器的方法

Constructor getConstructor(Class[] params) — 根据构造函数的参数,返回一个具体的具有public属性的构造函数
Constructor getConstructors() — 返回所有具有public属性的构造函数数组
Constructor getDeclaredConstructor(Class[] params) — 根据构造函数的参数,返回一个具体的构造函数(不分public和非public属性)
Constructor getDeclaredConstructors() — 返回该类中所有的构造函数数组(不分public和非public属性)

获得属性的方法

Method getMethod(String name, Class[] params) — 根据方法名和参数,返回一个具体的具有public属性的方法
Method[] getMethods() — 返回所有具有public属性的方法数组
Method getDeclaredMethod(String name, Class[] params) — 根据方法名和参数,返回一个具体的方法(不分public和非public属性)
Method[] getDeclaredMethods() — 返回该类中的所有的方法数组(不分public和非public属性)

获得方法信息的方法

Method getMethod(String name, Class[] params) — 根据方法名和参数,返回一个具体的具有public属性的方法
Method[] getMethods() — 返回所有具有public属性的方法数组
Method getDeclaredMethod(String name, Class[] params) — 根据方法名和参数,返回一个具体的方法(不分public和非public属性)
Method[] getDeclaredMethods() — 返回该类中的所有的方法数组(不分public和非public属性)

关于getMethods()和getDeclaredMethods()的区别

Java的API文档中的注解
Method[] getDeclaredMethods()
返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
Method[] getMethods()
返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。

抽象的文字描述总是没有代码表现的直白,示例代码中有这两个方法的调用,运行后的结果如下:

getDeclaredMethods方法获取的所有方法 getMethods方法获取的所有方法
修饰符–方法名–返回值类型 修饰符–方法名–返回值类型
public – toString – class java.lang.String public – toString – class java.lang.String
public – getName – class java.lang.String public – getName – class java.lang.String
public – write – void public – write – void
public – setName – void public – setName – void
privatework – void 无法获得work方法
public – setAge – void public – setAge – void
public – getAge – int public – getAge – int
public – sleep – void (父类公共方法)
public final – wait – void (超类公共方法)
public final native – wait – void (超类公共方法)
public final – wait – void (超类公共方法)
public – equals – boolean (超类公共方法)
public native – hashCode – int (超类公共方法)
public final native – getClass – class java.lang.Class (超类公共方法)
public final native – notify – void (超类公共方法)
public final native – notifyAll – void (超类公共方法)

配合表格再去看API中的注解就事半功倍了。

Note:

  • 子类继承父类,既可以重写父类的方法,一旦重写,通过getDeclaredMethods获得就是子类中重写的方法,不要误以为获得的是父类中的方法。
  • getDeclaredMethod()和getMethod()是通过方法名和参数列表获取指定的方法,所能获取的方法的范围各不相同,上述讨论的情况一致。

代码示例

package com.Lct.reflection;

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

/**
 *  测试类
 */
public class ReflectionAPIDemo {

    public static void main(String[] args) throws Exception{
        //获取Employee类所关联的Class对象
        Class<?> classType =  Class.forName("com.Lct.reflection.Employee");

        //方法1
        //通过反射机制调用无参构造方法来实例化对象
        Employee employee = (Employee) classType.newInstance();
        System.out.println(employee);
        System.out.println("----------------------------------");

        //方法2
        //通过反射机制获取构造方法对象,再通过该对象来实例化Employee类的实例
        Constructor<?> ct = classType.getConstructor(new Class[]{});
        //直接强制类型转换也是可以的
        Object object = ct.newInstance(new Object[]{});
        if (object instanceof Employee) {
            Employee em1 = (Employee) object;
            System.out.println(em1);
        }
        System.out.println("----------------------------------");

        //通过反射机制获取带参数的构造方法对象,再通过反对向实例化Employee类的实例
        Constructor<?> ct2 = classType.getConstructor(new Class[]{String.class,int.class});
        Employee em2 = (Employee) ct2.newInstance(new Object[]{"张三",18});
        System.out.println(em2);

        //<<关于getMethods()和getDeclaredMethods()的区别>>
        //通过反射机制获取Employee类中的所有方法,包括私有的
        Method[] methods = classType.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(Modifier.toString(method.getModifiers())+"--"+method.getName()+"--"+method.getReturnType());
        }
        System.out.println("----------------------------------");
        //通过反射机制获取Employee类和超类中的所有共有方法
        Method[] m2 = classType.getMethods();
        for (Method m : m2) {
            System.out.println(Modifier.toString(m.getModifiers())+"--"+m.getName()+"--"+m.getReturnType());
        }
        System.out.println("----------------------------------");

        //getMethod方法调用父类的方法
        //getDeclaredMethod方法无法调用父类的public方法
        Method method2 = classType.getMethod("eat", new Class[]{});
        method2.invoke(em2, new Object[]{});
        System.out.println("----------------------------------");

        //通过反射机制获取指定的方法,包括私有的,并可以调用它
        Method method = classType.getDeclaredMethod("work", new Class[]{int.class});
        System.out.println(method);
        //使私有的可以访问
        method.setAccessible(true);
        //方法的调用
        method.invoke(em2, new Object[]{3});
        System.out.println("----------------------------------");

        //通过反射机制获取所有属性,包括私有的
        Field[] fields = classType.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        System.out.println("----------------------------------");

        //通过反射机制获取指定的属性,包括私有的,并可以改变它
        Field field = classType.getDeclaredField("name");
        //使私有的可以访问
        field.setAccessible(true);
        String object2 = (String) field.get(em2);
        System.out.println(object2);
        field.set(em2, "李四");
        System.out.println(em2);
        System.out.println("----------------------------------");
    }
}

/**
 *  父类
 */
class Person{

    public void sleep () {
        System.out.println("sleeping....");
    }

    @SuppressWarnings("unused")
    private void eat () {
        System.out.println("eating....");
    }
}

/**
 *  接口
 */
interface Study {
    public void write();
}

/**
 *  子类
 */
class Employee extends Person implements Study{

    private String name;
    private int age;
    private Object object;

    public Employee() {
        super();
        System.out.println("无参构造方法");
    }

    public Employee(String name, int age) {
        super();
        System.out.println("带参数的构造方法");
        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: "+this.name+" age: "+this.age+"]";
    }

    @SuppressWarnings("unused")
    private void work (int num) {
        System.out.println(num+" Employee -- working...");
    }

    @Override
    public void write() {
        System.out.println(" Employee -- writing...");
    }

}

Array

目前很少这么用,个人感觉也是没必要,但是本着存在即合理的原则还是写下来,留个印象。

代码示例

package com.Lct.reflection;

import java.lang.reflect.Array;

public class reflectionArrayDemo {

    public static void main(String[] args) throws Exception{
        Class<?> class1 = Class.forName("java.lang.String");
        //创建一维数组
        Object array = Array.newInstance(class1, 9);
        Array.set(array, 0, "zhangdan");
        System.out.println(Array.get(array, 0));

        //创建二维数组
        Object array2 = Array.newInstance(class1, new int[]{2,3}/*创建2行3列的二维数组*/);
        Object arrayObj = Array.get(array2, 1);
        Array.set(arrayObj, 1, "lisi");
        System.out.println(Array.get(arrayObj, 1));
    }

}

反射总结

  • 只要用到反射,现货的Class对象
  • 没有方法可以获取当前类的超类的private的方法和属性,你必须通过getSupperclass()方法找到超类之后再去尝试获得
  • 通常情况即使是当前类,private属性或方法是不能访问的,需要设置压制权限setAccessible(true)来取得private的访问权。但是需要注意,这已经破坏了java面向对象的封装性规则,所以要谨慎使用。
  • 关于Java反射机制,这里入门而已,还有很多地方值得深入,未完待续~~~

巨人的肩膀

Android反射机制实现与原理
作者写的巨细靡遗,其中LoadMethod类和升级版的LoadMethodEx类非常值得学习。

Android系统原理与源码分析(1):利用Java反射技术阻止通过按钮关闭对话框
这篇博客是Java反射机制在android中的使用实例,知道可以这样用就好,但不推荐,虽然反射很强大,但是也需谨慎使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章