侧边栏壁纸
  • 累计撰写 98 篇文章
  • 累计创建 20 个标签
  • 累计收到 3 条评论

Java 反射机制

林贤钦
2020-04-21 / 0 评论 / 9 点赞 / 657 阅读 / 0 字
温馨提示:
本文最后更新于 2020-05-12,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

Java 反射机制

基本概念

​ 在java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法?

答案是肯定的

这种动态获取类的信息以及动态调用对象的方法的功能来自于Java语言的反射(Reflection)机制

反射也称作框架设计的灵魂,简单来说,反射将类的各个部分封装为其他对象。

使用反射,可以在程序运行过程中,操作这些对象,也可以解耦,提高程序的可扩展性

那么,既然反射是如何操作的?

待我一步一步慢慢道来,首先,先不管三七二十一,在idea敲上这些代码

public class DumpMethods {
    public static void main(String[] args) throws Exception{
        //获得字符串所标识的class对象
        //在此处传入字符串指定类名,所以参数获取可以是一个运行期的行为,可以用args[0]
        Class<?> clazz=Class.forName("java.lang.String");

        //返回class对象所对应的类或接口,所声明的所有方法和数组(包括私有方法)
        Method[] methods = clazz.getDeclaredMethods();

        //遍历输出所有方法声明
        for (Method method : methods) {
            System.out.println(method);
        }
    }
}

然后运行一下main方法,然后你就能看到巴拉巴拉一大堆String类下的方法了

然后我们思考一下,我们如何获取String 类下的方法的这里也没有用传统的new一个对象,然后对象.方法

在上面的代码中,有三步

  • 第一步
    通过Class.forName("全类名")获取Class对象,

  • 第二步
    通过类.getDeclaredMethods()获取该类声明的所有方法和数组,连私有方法都能拿得到,简直隐私都没了

  • 第三步
    通过一个遍历,将每个方法的声明都输出

结果如下(只写前5个,太多了,就不一一列举)

public boolean java.lang.String.equals(java.lang.Object)
public java.lang.String java.lang.String.toString()
public int java.lang.String.hashCode()
public int java.lang.String.compareTo(java.lang.String)
public int java.lang.String.compareTo(java.lang.Object)


再举个例子,我们模拟传统new一个对象,和用反射获取的对象和方法,来调用方法

public class InvokeTester {
    public int add(int param1,int param2){
        return param1+param2;
    }
    public String echo(String message){
        return "hello,"+message;
    }

    public static void main(String[] args) throws Exception{
        //以前常规执行手段
        InvokeTester tester = new InvokeTester();
        System.out.println(tester.add(1,2));
        System.out.println(tester.echo("linxianqin"));
        System.out.println("tester---->"+tester);
        System.out.println("---------------------------------------------");
        
        //通过反射机制
        //第一步,获取Class对象
        // 前面用Class.forName()方法获取
        // 这里用第二种方法,类名.class
        Class<?> clazz = InvokeTester.class;

        //生成新的对象,用newInstance()方法
        Object invokeTester = clazz.newInstance();
        System.out.println(invokeTester instanceof InvokeTester);// 输出true

        //通过反射调用方法
        //首先需要获得与该方法对应的Method对象
        Method addMethod  = clazz.getMethod("add", int.class, int.class);
        System.out.println("addMethod--------->"+addMethod);
        // 第一个参数是方法名,第二个参数是这个方法所需要的参数的Class对象的数组
//        Method addMethod = clazz.getMethod("add", new Class[] { int.class,
//                int.class });
        //调用目标方法
        Object result  = addMethod.invoke(invokeTester, new Object[]{1, 2});
        System.out.println(result);

        //调用第二个方法
        Method echoMethod = clazz.getMethod("echo", String.class);
        Object invoke = echoMethod .invoke(invokeTester, new Object[]{"linxianqin"});
        System.out.println(invoke);
    }
}

通过上面的例子,我们也大概知道了什么是反射,如果还不懂,下面举更详细的例子

其实反射机制就是可以将一个类,类的成员,方法,继承与哪个类,实现了哪个接口,当作一个个对象来操作,在反射面前,没有所谓的私有,当然,也有特殊情况,被设置权限了。

下面举一些例子,来详细说一下反射。

首先,先整一个Person,然后我们来玩它。

package com.linxianqin.reflection;

/**
 * 功能描述:被反射玩的对象
 *
 * @author 林贤钦
 * @version 1.00
 * @Date 2020/4/21
 */
public class Person {
    private int age;
    private String name;
    public Person(){

    }
    public Person(int age, String name){
        this.age = age;
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

1、通过Java反射机制得到类的包名和类名

Person person = new Person();
System.out.println("包名:"+person.getClass().getPackage().getName());
System.out.println("完整类名:"+person.getClass().getName());

控制台输出:

包名:com.linxianqin.reflection
完整类名:com.linxianqin.reflection.Person

2、验证所有的类都是Class类的实例对象

//定义两个类型都未知的Class , 设置初值为null, 看看如何给它们赋值成Person类
Class<?> clazz1=null;
Class<?> clazz2=null;
//写法一: 可能抛出 ClassNotFoundException [多用这个写法]
clazz1= Class.forName("com.linxianqin.reflection.Person");
System.out.println("包名:"+clazz1.getClass().getPackage().getName());
System.out.println("完整类名:"+clazz1.getClass().getName());
//写法二:
clazz2=Person.class;
System.out.println("包名:"+clazz2.getClass().getPackage().getName());
System.out.println("完整类名:"+clazz2.getClass().getName());

控制台输入:

包名:java.lang
完整类名:java.lang.Class
包名:java.lang
完整类名:java.lang.Class

3、通过Java反射机制,用Class 创建类对象[这也就是反射存在的意义所在]

Class<?> clazz1= null;
clazz1=Class.forName("com.linxianqin.reflection.Person");
//由于这里不能带参数,所以你要实例化的这个类Person,一定要有无参构造函数哈
//如果带参数,有另外的方法来实例化这个Person
Person person = (Person) clazz1.newInstance();
person.setAge(18);
person.setName("林贤钦");
System.out.println(person.getName()+"永远"+person.getAge()+"岁");

控制台输出:

林贤钦永远18岁

4、通过Java反射机制得到一个类的构造函数,并实现创建带参实例对象

Class<?> clazz1= null;
Person person1=null;
Person person2=null;
clazz1=Class.forName("com.linxianqin.reflection.Person");
//得到一系列构造函数集合
Constructor<?>[] constructors = clazz1.getConstructors();
person1 =(Person) constructors[0].newInstance();//这里的constructors[0] 就是我们person类中的第一个无参构造方法
person1.setAge(18);
person1.setName("林贤钦");
//这里的constructors[1] 就是我们person类中的第二个构造方法带参数
person2=(Person) constructors[1].newInstance(20,"linxianqin");
System.out.println(person1.getName() + " : " + person1.getAge()
        + "  ,   " + person2.getName() + " : " + person2.getAge()
);

控制台输出:

林贤钦 : 18 , linxianqin : 20

5、通过Java反射机制操作成员变量, set 和 get

Class<?> clazz1 = null;
clazz1 = Class.forName("com.linxianqin.reflection.Person");
Object obj = clazz1.newInstance();
Field personNameField  = clazz1.getDeclaredField("name");
personNameField.setAccessible(true);
personNameField.set(obj,"林贤钦");
System.out.println("Demo5: 修改属性之后得到属性变量的值:" + personNameField.get(obj));

控制台输出:

Demo5: 修改属性之后得到属性变量的值:林贤钦

6、通过Java反射机制得到类的一些属性: 继承的接口,父类,函数信息,成员信息,类型等

6.1 创建ActionInterface接口

public interface ActionInterface {
    public void walk(int m);
}

6.2、创建SuperMan类集成Person和实现ActionInterface接口

package com.linxianqin.reflection;
public class SuperMan extends Person implements ActionInterface {
    private boolean BlueBriefs;

    public void fly() {
        System.out.println("超人会飞耶~~");
    }

    public boolean isBlueBriefs() {
        return BlueBriefs;
    }

    public void setBlueBriefs(boolean blueBriefs) {
        BlueBriefs = blueBriefs;
    }

    @Override
    public void walk(int m) {
        // TODO Auto-generated method stub
        System.out.println("超人会走耶~~走了" + m + "米就走不动了!");
    }
}

6.3 接着玩

Class<?> clazz1= null;
clazz1=  Class.forName("com.linxianqin.reflection.SuperMan");
//取得父类的名称
Class<?> superClass=clazz1.getSuperclass();
System.out.println("SuperMan的父类名称:"+superClass.getName());

System.out.println("===============================================");
Field[] fields = clazz1.getDeclaredFields();//获取所有的成员变量
//然后我们可以打印一波
for (int i = 0; i < fields.length; i++) {
    System.out.println("SuperMan类中的成员"+fields[i]);
}
//取得类方法
Method[] methods = clazz1.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
    System.out.println("Demo6,取得SuperMan类的方法:");
    System.out.println("函数名:" + methods[i].getName());
    System.out.println("函数返回类型:" + methods[i].getReturnType());
    System.out.println("函数访问修饰符:" + Modifier.toString(methods[i].getModifiers()));
    System.out.println("函数代码写法: " + methods[i]);
}

System.out.println("===============================================");
//取得类实现的接口,因为接口类也属于Class ,所以接口中的方法也是一样的
Class<?> interfaces[]=clazz1.getInterfaces();
for (int i = 0; i <interfaces.length ; i++) {
    System.out.println("实现的接口类名:"+interfaces[i].getName());
}

控制台输出:

SuperMan的父类名称:com.linxianqin.reflection.Person
===============================================
SuperMan类中的成员private boolean com.linxianqin.reflection.SuperMan.BlueBriefs
取得SuperMan类的方法:
函数名:fly
函数返回类型:void
函数访问修饰符:public
函数代码写法: public void com.linxianqin.reflection.SuperMan.fly()
取得SuperMan类的方法:
函数名:isBlueBriefs
函数返回类型:boolean
函数访问修饰符:public
函数代码写法: public boolean com.linxianqin.reflection.SuperMan.isBlueBriefs()
取得SuperMan类的方法:
函数名:walk
函数返回类型:void
函数访问修饰符:public
函数代码写法: public void com.linxianqin.reflection.SuperMan.walk(int)
取得SuperMan类的方法:
函数名:setBlueBriefs
函数返回类型:void
函数访问修饰符:public
函数代码写法: public void com.linxianqin.reflection.SuperMan.setBlueBriefs(boolean)
===============================================
实现的接口类名:com.linxianqin.reflection.ActionInterface

7、通过Java反射机制调用类方法

Class<?> class1 = null;
class1 = Class.forName("com.linxianqin.reflection.SuperMan");

System.out.println("Demo7: \n调用无参方法fly():");
Method method = class1.getMethod("fly");
method.invoke(class1.newInstance());

System.out.println("调用有参方法walk(int m):");
method = class1.getMethod("walk",int.class);
method.invoke(class1.newInstance(),100);

控制台输出:

Demo7:
调用无参方法fly():
超人会飞耶~~
调用有参方法walk(int m):
超人会走耶~~走了100米就走不动了!

8、通过Java反射机制得到类加载器信息

Class<?> class1 = null;
class1 = Class.forName("com.linxianqin.reflection.SuperMan");
String nameString = class1.getClassLoader().getClass().getName();
System.out.println("Demo8: 类加载器类名: " + nameString);

控制台输出:

Demo8: 类加载器类名: sun.misc.Launcher$AppClassLoader

反射机制的相关类

这些是我从网上找来的,其实直接看jdk源码中的Class类就可以了,jdk源码写的也很详细

与Java反射相关的类如下:

类名用途
Class类代表类的实体,在运行的Java应用程序中表示类和接口
Field类代表类的成员变量(成员变量也称为类的属性)
Method类代表类的方法
Constructor类代表类的构造方法

Class类

Class代表类的实体,在运行的Java应用程序中表示类和接口。在这个类中提供了很多有用的方法,这里对他们简单的分类介绍。

  • 获得类相关的方法
方法用途
asSubclass(Class clazz)把传递的类的对象转换成代表其子类的对象
Cast把对象转换成代表类或是接口的对象
getClassLoader()获得类的加载器
getClasses()返回一个数组,数组中包含该类中所有公共类和接口类的对象
getDeclaredClasses()返回一个数组,数组中包含该类中所有类和接口类的对象
forName(String className)根据类名返回类的对象
getName()获得类的完整路径名字
newInstance()创建类的实例
getPackage()获得类的包
getSimpleName()获得类的名字
getSuperclass()获得当前类继承的父类的名字
getInterfaces()获得当前类实现的类或是接口
  • 获得类中属性相关的方法
方法用途
getField(String name)获得某个公有的属性对象
getFields()获得所有公有的属性对象
getDeclaredField(String name)获得某个属性对象
getDeclaredFields()获得所有属性对象
  • 获得类中注解相关的方法
方法用途
getAnnotation(Class annotationClass)返回该类中与参数类型匹配的公有注解对象
getAnnotations()返回该类所有的公有注解对象
getDeclaredAnnotation(Class annotationClass)返回该类中与参数类型匹配的所有注解对象
getDeclaredAnnotations()返回该类所有的注解对象
  • 获得类中构造器相关的方法
方法用途
getConstructor(Class...<?> parameterTypes)获得该类中与参数类型匹配的公有构造方法
getConstructors()获得该类的所有公有构造方法
getDeclaredConstructor(Class...<?> parameterTypes)获得该类中与参数类型匹配的构造方法
getDeclaredConstructors()获得该类所有构造方法
  • 获得类中方法相关的方法
方法用途
getMethod(String name, Class...<?> parameterTypes)获得该类某个公有的方法
getMethods()获得该类所有公有的方法
getDeclaredMethod(String name, Class...<?> parameterTypes)获得该类某个方法
getDeclaredMethods()获得该类所有方法
  • 类中其他重要的方法
方法用途
isAnnotation()如果是注解类型则返回true
isAnnotationPresent(Class<? extends Annotation> annotationClass)如果是指定类型注解类型则返回true
isAnonymousClass()如果是匿名类则返回true
isArray()如果是一个数组类则返回true
isEnum()如果是枚举类则返回true
isInstance(Object obj)如果obj是该类的实例则返回true
isInterface()如果是接口类则返回true
isLocalClass()如果是局部类则返回true
isMemberClass()如果是内部类则返回true

Field类

Field代表类的成员变量(成员变量也称为类的属性)。

方法用途
equals(Object obj)属性与obj相等则返回true
get(Object obj)获得obj中对应的属性值
set(Object obj, Object value)设置obj中对应属性值

Method类

Method代表类的方法。

方法用途
invoke(Object obj, Object... args)传递object对象及参数调用该对象对应的方法

Constructor类

Constructor代表类的构造方法。

方法用途
newInstance(Object... initargs)根据传递的参数创建类的对象

参考文献

Java高级特性——反射

9

评论区