java学习笔记-反射(1)-Class

慢慢来比较快,虚心学技术

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

反射

反射的概念(是什么):

java中的反射------相当于对象的镜子,对象在照镜子时可以获取自身的信息,比如:属性,方法和构造器,实现接口等,而且这面镜子还可以直接调用执行java对象的方法

反射的用处(有什么用):

1.当接收到别人传过来的类时,可以进行解析

2.当只有类名时,可以获取到对象的完整信息,从而执行相应处理

多用于可配置和动态代理

java反射涉及的几个类:

java.lang. Class:反射的核心类,可以获取类的属性,方法等信息

java.lang.ClassLoader:类的加载器,用来把类(class)装载进 JVM

java.lang.reflect.Field :类的成员变量,用来获取和设置类的属性

java.lang.reflect.Method:类的方法,用来获取类中方法的信息或调用类的方法

java.lang.reflect.Modifier:类的修饰符工具,可以用来判断类中属性或者方法的修饰级别

为方便后续学习,事先创建一个基本类

public class BaseEntity {

    private String name;

    private Integer age;

    public BaseEntity(){
        super();
    }

    public BaseEntity(String name,Integer age){
        super();
        this.name = name;
        this.age = age;
    }

    private void addAge(String name,Integer age){
        if(this.name.equals(name)){
            this.age+=age;
        }else{
            System.out.println("名称不符,请核验");
        }
    }

}
基本类BaseEntity

Class类

class是一个用来描述类的类,封装了当前对象所对应的类信息,我们可以通过该类获取目标对象的属性,方法等信息,还可以根据class类创建目标对象实例

获取Class类

1.通过类名获取 : 类名.class

Class clazz = BaseEntity.class;

2. 通过类全类名获取:Class.forName(全类名)--这种方式一般在开发中用的比较多,多为配置项获取实例,如最简单的JDBC获取驱动方式

Class clazz =Class.forName("com.java.entity.BaseEntity");

3.通过对象实例获取

BaseEntity baseEntity = new BaseEntity("test",50);

Class clazz = baseEntity.getClass();

获取父类Class

Class clazz = Class.forName("com.java.entity.BaseEntity");

Class personClazz = sonClazz.getSuperclass();

创建目标对象实例

Object newObject = clazz.newInstance();

类的字段Field

通过class类获取目标对象的字段对象,获取到字段对象后可以对具体对象的属性值进行读取和设置操作

获取字段列表:

//获取基本公共字段(无法获取保护字段和父类字段)
Field[] fields = clazz.getFields();  //结果是null,因为BaseEntity没有public字段

 //获取所有字段(无法获取父类字段)
fields = clazz.getDeclaredFields();  //结果是name和age的对应字段对象

获取指定字段

一般使用getDeclaredField方法获取,两种方法找不到对应字段时都会抛NoSuchFieldException异常

方法结构如下:

Field getField(字段名);

Field getDeclaredField(字段名) ;

//getField()---只能获取到公共字段
Field field = clazz.getField("name");

//getDeclaredField()----可以获取到指定字段,不管是否保护字段
Field field = clazz.getDeclaredField("name");

设置对象对应字段的值field.set(Object obj,Object value)

方法结构如下:

void set(实例对象,字段值)

如果是保护字段,无论读取还是修改属性内容,都需要先setAccessible(true)方可操作

//如果是保护字段,无论读取还是修改属性内容,都需要先setAccessible(true)方可操作
field.setAccessible(true);

//设置属性值
field.set(baseEntity,"base");

获取对象对应字段的值field.get(Object obj)

//如果是保护字段,无论读取还是修改属性内容,都需要先setAccessible(true)方可操作
field.setAccessible(true);

//获取实例baseEntity对应属性的属性值(此处获取的是name的值)
field.get(baseEntity);        //-----------得到结果为base

类的方法Method

通过class类获取目标对象的方法对象,获取到方法对象后可以对具体对象的方法进行调用和方法

获取方法列表

两种方法获取都不能够获取到父类的方法,只能通过获取父类Class来简介获取父类方法

//获取公共方法列表,无法获取保护方法
Method[] methods = clazz.getMethods();

//获取全部方法列表
Method[] methods = clazz.getDeclaredMethods();

获取指定方法

两种方法的区别和获取指定对象的区别一致,方法内的参数结构如下:

Method getMethod("方法名",方法参数类型列表)

Method getDeclaredMethod("方法名",方法参数类型列表)

//获取BaseEntity的addAge方法对象
Method method = clazz.getMethod("addAge",String.class,Integer.class);

Method method = clazz.getDeclaredMethod("addAge",String.class,Integer.class);

执行指定方法 invoke(Object obj, Object... args)

反射执行指定方法是框架中用的比较普遍的用法,方法结构如下:

Object invoke(具体实例对象, 参数值列表)

如果是保护方法,执行方法前都需要先setAccessible(true)方可操作

method.setAccessible(true);

//执行调用baseEntity的addAge方法,传入参数name和age的值
method.invoke(baseEntity, "base", 12);

//之后再获取baseEntity的age值看一下结果:
Field field = clazz.getDeclaredField("age");

field.setAccessible(true);
field.get(baseEntity,"age");//---------------得到结果:50+12= 62,方法执行正确

类的修饰符工具Modifier

如上所述,方法和属性字段都有保护和公共之分,那么如何判断一个方法或者属性是否公共呢?java反射提供了Modifier工具类,该工具类对类和成员访问修饰符进行解码,通过判断类和成员修饰符的整数编码,判定类和成员是否保护以及修饰符类型

Modifier类将修饰符作为整数编码代号,具体部分常量如下:

public static final int PUBLIC  = 0x00000001;
public static final int PRIVATE  = 0x00000002;
public static final int PROTECTED  = 0x00000004;
public static final int STATIC  = 0x00000008;
public static final int FINAL  = 0x00000010;
public static final int SYNCHRONIZED  = 0x00000020;

Modifier工具类提供如下静态方法判断类和成员的修饰符:

Modifier.isPublic(int mod)
Modifier.isPrivate(int mod)
Modifier.isProtected(int mod)
Modifier.isStatic(int mod)
Modifier.isFinal(int mod)
Modifier.isSynchronized(int mod)

所以,我们可以通过调用Modifier工具类的方法来判断我们的方法或者属性字段是否公共字段/方法,其中,可以使用field.getModifiers(),method.getModifiers()获取字段或方法修饰符的对应编码值

//判断field是否公共字段
if(!Modifier.isPublic(field.getModifiers())){
     field.setAccessible(true);
}

field.set(baseEntity,50);

综合使用-动态执行方法(通过传入全类名,通过传入具体对象)

①依旧使用BaseEntity作为基本类操作

②创建ClassUtil类,作为工具类

public class ClassUtil {

    /**
     * 执行方法
     *
     * @param className 目标class名称
     * @param methodName 目标方法名称
     * @param args 执行目标方法所需要的参数值
     * @return java.lang.Object 执行目标方法后的返回值
     *
     * @author *** 2019/2/15
     * @version 1.0
     **/
    public static Object invoke(String className,String methodName,Object ...args){
        Class clazz = null;
        Class[] parementerType = new Class[args.length];

        try {
            //通过传入的class路径反射class
            clazz = Class.forName(className);

            for(int i=0;i<args.length;i++){
                parementerType[i] = args[i].getClass();
            }

            //获取要执行的目标方法对象
            Method method = clazz.getDeclaredMethod(methodName,parementerType);

            System.out.println("执行方法:"+method.getName());

            //开启权限,执行方法
            method.setAccessible(true);
            return method.invoke(clazz.newInstance(),args);

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 执行方法
     *
     * @param obj 要执行方法的对象
     * @param methodName 要执行的目标方法
     * @param args 要执行目标方法需要的参数
     *
     * @return java.lang.Object 执行目标方法需要的参数值
     *
     * @author *** 2019/2/15
     * @version 1.0
     **/
    public static Object invoke(Object obj,String methodName,Object ...args){

        Class clazz = null;
        Class[] parementerType = new Class[args.length];

        try {
            //通过传入的class路径反射class
            clazz = obj.getClass();

            for (int i = 0; i < args.length; i++) {
                parementerType[i] = args[i].getClass();
            }

            //获取要执行的目标方法对象
            Method method = clazz.getDeclaredMethod(methodName, parementerType);

            System.out.println("执行方法:" + method.getName());

            //开启权限,执行方法
            method.setAccessible(true);
            return method.invoke(obj, args);

        }catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        return null;
    }

}

③综合测试

//传入全类名调用方法
ClassUtil.invoke("com.java.entity.BaseEntity","setAge",123);

//传入具体对象调用方法
BaseEntity baseEntity = new BaseEntity();
ClassUtil.invoke(baseEntity,"setAge",123);
System.out.println(baseEntity.getAge());

总结

1.java反射-对象在照镜子时可以获取自身的信息,比如:属性,方法和构造器,实现接口等,而且这面镜子还可以直接调用执行java对象的方法

2.java反射的主要类为Class,ClassLoader,Field,Method,Modifier

3.Class类是一个用来描述类的类,封装了当前对象所对应的类信息,我们可以通过该类获取目标对象的属性,方法等信息,还可以根据class类创建目标对象实例

4.Field类,通过class类获取目标对象的字段对象,获取到字段对象后可以对具体对象的属性值进行读取和设置操作。对于保护的字段的读取和更改,需要先调用setAccessible(true)方法。

5.Method类,通过class类获取目标对象的方法对象,获取到方法对象后可以对具体对象的方法进行调用和方法。对于保护的方法执行,需要先调用setAccessible(true)方法。

6.java反射提供了一个专门封装了类和类的成员修饰符的工具类Modifier,用于判断类与类的成员的修饰符。其底层使用的是修饰符整数编码,本人认为与日志级别的设计思想一致,值得学习

参考文档

【1】java编程思想

【2】https://www.cnblogs.com/tech-bird/p/3525336.html

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