Java学习之路JAVA基础知识Java学习笔记

JAVA基础系列(十一)反射

2017-06-04  本文已影响53人  Acamy丶

通过class文件对象,去使用该文件中的成员变量Field,构造方法Constructor,成员方法Method。

1.获取class文件对象

获取class文件对象的方式:
1:Object类的getClass()方法
2:数据类型的静态属性class
3:Class.forName(String className);

** 注意:**在开发是我们一般用第三种,因为第三种是一个字符串,而不是一个具体的类名。这样我们就可以把这样的字符串配置到配置文件中。

** Demo:辅助类Person**

package com.hust.jianshu.reflectDemos.acamy;

public class Person {
    private String name;
    int age;
    public String address;

    public Person() {
    }

    private Person(String name) {
        this.name = name;
    }

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

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

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

    public void method(String s) {
        System.out.println("method " + s);
    }

    public String getString(String s, int i) {
        return s + "---" + i;
    }

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

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + ", address=" + address
                + "]";
    }

}

** Demo:获取class文件对象的几种方式**

public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        // 方式1
        Person p = new Person();
        Class c = p.getClass();

        Person p2 = new Person();
        Class c2 = p2.getClass();

        System.out.println(p == p2);// false
        System.out.println(c == c2);// true

        // 方式2
        Class c3 = Person.class;
        // int.class;
        // String.class;
        System.out.println(c == c3);//true

        // 方式3
        // ClassNotFoundException
        Class c4 = Class.forName("com.hust.jianshu.reflectDemos.acamy.Person");
        System.out.println(c == c4);//true
    }
}

2.获取构造方法

通过反射既可以获取公有构造方法,也可以暴力访问私有构造方法。同时可以单个获取,也可以批量获取。

** Demo:通过反射获取构造方法并使用**

public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        // 获取字节码文件对象
        Class c = Class.forName("com.hust.jianshu.reflectDemos.acamy.Person");

        // 获取多个构造方法
        // public Constructor[] getConstructors():所有公共构造方法
        // public Constructor[] getDeclaredConstructors():所有构造方法
        Constructor[] cons = c.getDeclaredConstructors();
        for (Constructor con : cons) {
            System.out.println(con);
        }

        // 获取单个构造方法
        // 获取无参构造方法对象
        // public Constructor<T> getConstructor(Class<?>... parameterTypes)
        // 参数表示的是:你要获取的构造方法的构造参数个数及数据类型的class字节码文件对象
        Constructor con = c.getConstructor();// 返回的是构造方法对象
        // public T newInstance(Object... initargs)
        // 使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
        Object obj = con.newInstance();
        System.out.println(obj);

        // 获取带参构造方法对象
        // public Constructor<T> getConstructor(Class<?>... parameterTypes)
        Constructor con1 = c.getConstructor(String.class, int.class,
                String.class);
        // 通过带参构造方法对象创建对象
        // public T newInstance(Object... initargs)
        Object obj1 = con1.newInstance("林青霞", 27, "北京");
        System.out.println(obj1);

        // 获取私有构造方法对象
        // NoSuchMethodException:每个这个方法异常
        // 原因是一开始我们使用的方法只能获取公共的,下面这种方式就可以了。
        Constructor con2 = c.getDeclaredConstructor(String.class);
        // 用该私有构造方法创建对象
        // IllegalAccessException:非法的访问异常。
        // 暴力访问
        con2.setAccessible(true);// 值为true则指示反射的对象在使用时应该取消Java语言访问检查。
        Object obj2 = con2.newInstance("风清扬");
        System.out.println(obj2);
    }
}

3.获取成员变量

>通过反射可以获取所有类型的成员变量,并且在创建对象后可以对获取的成员变量设置值

** Demo:通过反射获取成员变量并使用**

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        // 获取字节码文件对象
        Class c = Class.forName("com.hust.jianshu.reflectDemos.acamy.Person");

        // 获取多个的成员变量
        // Field[] fields = c.getFields();所有公共成员变量
        Field[] fields = c.getDeclaredFields();// 所有成员变量
        for (Field field : fields) {
            System.out.println(field);
        }

        // 通过无参构造方法创建对象
        Constructor con = c.getConstructor();
        Object obj = con.newInstance();
        System.out.println(obj);

        // 获取单个的成员变量
        // 获取address并对其赋值
        Field addressField = c.getField("address");
        // public void set(Object obj,Object value)
        // 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
        addressField.set(obj, "北京"); // 给obj对象的addressField字段设置值为"北京"
        System.out.println(obj);

        // 获取name并对其赋值
        // NoSuchFieldException
        Field nameField = c.getDeclaredField("name");
        // IllegalAccessException
        nameField.setAccessible(true);
        nameField.set(obj, "林青霞");
        System.out.println(obj);

        // 获取age并对其赋值
        Field ageField = c.getDeclaredField("age");
        ageField.setAccessible(true);
        ageField.set(obj, 27);
        System.out.println(obj);
    }
}

4.获取成员方法

通过反射可以获取对象的所有类型成员变量,并调用

** Demo:通过反射获取成员方法并使用**

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

public class ReflectDemo {
    public static void main(String[] args) throws Exception {
        // 获取字节码文件对象
        Class c = Class.forName("com.hust.jianshu.reflectDemos.acamy.Person");

        // 获取所有的方法
        // Method[] methods = c.getMethods(); // 获取自己的包括父亲的公共方法
        Method[] methods = c.getDeclaredMethods(); // 获取自己的所有的方法
        for (Method method : methods) {
            System.out.println(method);
        }

        Constructor con = c.getConstructor();
        Object obj = con.newInstance();     

        // 获取单个方法并使用
        // public void show()
        // public Method getMethod(String name,Class<?>... parameterTypes)
        // 第一个参数表示的方法名,第二个参数表示的是方法的参数的class类型
        Method m1 = c.getMethod("show");
        // public Object invoke(Object obj,Object... args)
        // 返回值是Object接收,第一个参数表示对象是谁,第二参数表示调用该方法的实际参数
        m1.invoke(obj); // 调用obj对象的m1方法

        System.out.println("----------");
        // public void method(String s)
        Method m2 = c.getMethod("method", String.class);
        m2.invoke(obj, "hello");
        System.out.println("----------");

        // public String getString(String s, int i)
        Method m3 = c.getMethod("getString", String.class, int.class);
        Object objString = m3.invoke(obj, "hello", 100);
        System.out.println(objString);
        // String s = (String)m3.invoke(obj, "hello",100);
        // System.out.println(s);
        System.out.println("----------");

        // private void function()
        Method m4 = c.getDeclaredMethod("function");
        m4.setAccessible(true);
        m4.invoke(obj);
    }
}

5.反射应用举例

5.1 通过反射越过泛型检查

集合里面的泛型是给编绎器看的,在编绎成字节码后已经去掉了该类型,变成时论的Object类型,可以通过反编绎工具验证一下。正是由于集合里面的泛型有这种机制,我们才可以利用反射得到字节码文件,越过泛型检查

** Demo:**在ArrayList<Integer>集合中添加一个字符串数据

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
public class ArrayListDemo {
    public static void main(String[] args) throws NoSuchMethodException,
            SecurityException, IllegalAccessException,
            IllegalArgumentException, InvocationTargetException {
        // 创建集合对象
        ArrayList<Integer> array = new ArrayList<Integer>();

        // array.add("hello");
        array.add(10);

        Class c = array.getClass(); // 集合ArrayList的class文件对象
        Method m = c.getMethod("add", Object.class);

        m.invoke(array, "hello"); // 调用array的add方法,传入的值是hello
        m.invoke(array, "world");
        m.invoke(array, "java");

        System.out.println(array);
    }
}

5.2 反射工具类

** Demo: 反射工具类**

import java.lang.reflect.Field;

public class ToolDemo {
    public static void main(String[] args) throws NoSuchFieldException,
            SecurityException, IllegalArgumentException, IllegalAccessException {
        Person p = new Person();
        Tool t = new Tool();
        t.setProperty(p, "name", "林青霞");
        t.setProperty(p, "age", 27);
        System.out.println(p);
        System.out.println("-----------");

        Dog d = new Dog();

        t.setProperty(d, "sex", '男');
        t.setProperty(d, "price", 12.34f);

        System.out.println(d);
    }
}

class Tool {
    public void setProperty(Object obj, String propertyName, Object value)
            throws NoSuchFieldException, SecurityException,
            IllegalArgumentException, IllegalAccessException {
        // 根据对象获取字节码文件对象
        Class c = obj.getClass();
        // 获取该对象的propertyName成员变量
        Field field = c.getDeclaredField(propertyName);
        // 取消访问检查
        field.setAccessible(true);
        // 给对象的成员变量赋值为指定的值
        field.set(obj, value);
    }
}

class Dog {
    char sex;
    float price;

    @Override
    public String toString() {
        return sex + "---" + price;
    }
}

class Person {
    private String name;
    public int age;

    @Override
    public String toString() {
        return name + "---" + age;
    }

6.动态代理

代理:本来应该自己做的事情,却请了别人来做,被请的人就是代理对象。
举例:春季回家买票让人代买
动态代理:在程序运行过程中产生的这个对象,动态代理其实就是通过反射来生成一个代理

** Demo:反射实现动态代理类**

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Test {
    public static void main(String[] args) {
        UserDao ud = new UserDaoImpl();
        System.out.println(ud.getClass().getName());// ...UserDaoImpl
        ud.add();
        ud.delete();
        ud.update();
        ud.find();
        System.out.println("-----------");
        // 我们要创建一个动态代理对象
        // Proxy类中有一个方法可以创建动态代理对象

        // 我准备对ud对象做一个代理对象
        MyInvocationHandler handler = new MyInvocationHandler(ud);

        // public static Object newProxyInstance(ClassLoader loader,Class<?>[]
        // interfaces,InvocationHandler h)
        // ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
        // Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
        // InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上

        UserDao proxy = (UserDao) Proxy.newProxyInstance(ud.getClass()
                .getClassLoader(), ud.getClass().getInterfaces(), handler);

        // 创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,
        // 也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,
        // 以$开头,proxy为中,最后一个数字表示对象的标号。
        System.out.println(proxy.getClass().getName());// ...$Proxy0

        proxy.add();
        proxy.delete();
        proxy.update();
        proxy.find();
    }
}

// 每一个动态代理类都必须要实现InvocationHandler这个接口,
// 并且每个代理类的实例都关联到了一个handler,
// 当我们通过代理对象调用一个方法的时候,
// 这个方法的调用就会被转发为由InvocationHandler这个接口的invoke 方法来进行调用。
class MyInvocationHandler implements InvocationHandler {
    private Object target; // 目标对象

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    // proxy:代表动态代理对象
    // method:代表正在执行的方法
    // args:代表调用目标方法时传入的实参
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("权限校验");
        Object result = method.invoke(target, args);
        System.out.println("日志记录");
        return result; // 返回的是代理对象
    }
}

class UserDaoImpl implements UserDao {

    @Override
    public void add() {
        System.out.println("添加功能");
    }

    @Override
    public void delete() {
        System.out.println("删除功能");
    }

    @Override
    public void update() {
        System.out.println("修改功能");
    }

    @Override
    public void find() {
        System.out.println("查找功能");
    }

}

interface UserDao {
    public abstract void add();

    public abstract void delete();

    public abstract void update();

    public abstract void find();
}
上一篇下一篇

猜你喜欢

热点阅读