JAVA基础系列(十一)反射

通过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();
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,646评论 18 139
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,608评论 18 399
  • 高新技术的重要性 这里的高新技术指的是Java基础中的知识,比如:反射、注解、代理、线程池、依赖注入等等。市面上的...
    庞哈哈哈12138阅读 1,974评论 1 19
  • 弟弟蹭蹭蹭下楼来,我在门口散步,问他干嘛去。 他晃晃手中的钓鱼竿,“你猜。” “哪里还有鱼可钓?”门前两条河十几年...
    素素_sky阅读 224评论 0 0
  • 好久不看香港电影,虽然香港的电影和TVB剧圈住了整个少年不知事的岁月…… 而今,香港电影业已渐趋衰败难有佳作,TV...
    TKJ阅读 1,214评论 0 10