java反射机制

java反射能做什么?

好多文章和教程在讲解java反射的时候往往都是上来就说java反射机制是什么,
这次我们先来看看java发射机制能做什么,或者说为什么要设计出java的反射机制。

我们先来看一看这个场景:

程序员小A在做一个项目,写了一个类MyObject,其中有个方法myMethod,在项目中某个地方要调用MyObject中的myMethod
那么他就要在这个地方先通过new方法创建MyObject的实例myObject,再调用myMethod
突然有一天,需求变了,需要在此处调用MyObject1myMethod1方法。

这时候程序员小A需要在此处删除或者注释掉原来的代码,创建MyObject1的实例并调用myMethod1方法,然后Git代码,重新部署。

这时候程序员老B的写法就和小A不一样了。他先会写个配置文件,内容如下:

properties.class = MyObject
properties.method = MyMethod

当需求变更时,只需要在配置文件中更改类的名字和方法的名字。

这时候就一个疑问了,那老B在代码中是怎样实例化创建对象的,这时候就要用到java的反射机制,我们不需要在代码中使用new方法创建对象,只需要知道类的名字就可以创建对象。

java反射机制是什么

JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意方法和属性;
这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
                                                                      ——百度百科

上面这句话最重要的两个字是动态,我们可以很灵活的获取某个类的相关信息,而且还可以创建对象并调用方法。

举个栗子

我们先来创建一个类Student

public class Student {
    private String name1;
    private static final String name2 = "name2";
    protected String name3;
    public String name4;
    public int age;

    public Student() {}

    public Student(String name1, String name3, String name4, int age) {
        this.name1 = name1;
        this.name3 = name3;
        this.name4 = name4;
        this.age = age;
    }

    private Student(String name1, String name3, String name4) {
        this.name1 = name1;
        this.name3 = name3;
        this.name4 = name4;
    }

    public void printName1() {
        System.out.println(this.name1);
    }

    private void printName3() {
        System.out.println(this.name3);
    }

    public int getAge() {
        return this.age;
    }

    public void printSlogan(String slogan) {
        System.out.println(slogan);
    }

    private void printSex(String sex) {
        System.out.println(sex);
    }
}

我们目前在代码中是怎样使用这个Student类的呢?Student student = new Student();

Class类

我们在使用某个类时,jvm都会创建一个关于这个类的Class类的对象,每个类只有一个Class对象,就算创建多个Student的对象,Student类的Class对象也只有一个。

是不是有点晕。首先Class是一个类,它的名字叫Class,这只是个类名。这个类有什么用呢?
这个类用于记录加载到JVM中实体类的相关信息,JVM中的每个实体类都只有一个Class类的对象。

我们可以用以下三种方法获取某个类的Class对象:

public class Main {
    public static void main(String[] args) {
        //第一种方式获取Class对象
        Student student = new Student();//这一new 产生一个Student对象,一个Class对象。
        Class stuClass1 = student.getClass();//获取Class对象
        System.out.println(stuClass1.getName());

        //第二种方式获取Class对象
        Class stuClass2 = Student.class;
        System.out.println(stuClass1 == stuClass2);//判断第一种方式获取的Class对象和第二种方式获取的是否是同一个

        //第三种方式获取Class对象
        try {
            Class stuClass3 = Class.forName("Student");//注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
            System.out.println(stuClass3 == stuClass2);//判断三种方式是否获取的是同一个Class对象
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

输出结果:

Student
true
true

以上三种方法中,最有用的还是方法三。在方法一和二中,我们都已经在代码中引入Student类了,还需要Class对象获取它的相关信息吗?
方法三中,我们只要知道实体类的名字就可以获取它的所有信息,甚至可以实例化创建对象。

已经介绍了java反射机制中的Class类了,接下来介绍三个重要的类:Constructor类、Method类和Field类。

Constructor类

我们已经知道怎样获取一个类的Class对象了,那么就可以通过这个对象获取这个类的构造函数等信息,这就需要Constructor类了。

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

public class Main {
    public static void main(String[] args) throws Exception {
        System.out.println("获取Class对象");
        Class stuClass = Class.forName("Student");
        System.out.println("获取所有公有构造函数");
        Constructor[] constructors = stuClass.getConstructors();
        for (Constructor constructor : constructors)
            System.out.println(constructor);
        System.out.println("获取所有构造函数");
        constructors = stuClass.getDeclaredConstructors();
        for (Constructor constructor : constructors)
            System.out.println(constructor);
        System.out.println("调用公有无参构造函数");
        Constructor constructor = stuClass.getConstructor();
        Student student = (Student) constructor.newInstance();
        System.out.println("调用私有构造函数");
        constructor = stuClass.getDeclaredConstructor(String.class, String.class, String.class);
        constructor.setAccessible(true);  //设置权限可以访问私有构造函数
        student = (Student) constructor.newInstance("name1", "name3", "name4");
    }
}

通过Class对象我们可以访问任意一个构造函数。

Method类

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

public class Main {
    public static void main(String[] args) throws Exception {
        System.out.println("获取Class对象");
        Class stuClass = Class.forName("Student");
        System.out.println("通过反射创建Student对象");
        Student student = (Student) stuClass.getConstructor(String.class, String.class, String.class, int.class).newInstance("name1", "name3", "name4", 18);
        System.out.println("获取方法");
        Method[] methods = stuClass.getMethods();
        for (Method method : methods)
            System.out.println(method);
        System.out.println("获取所有方法");
        methods = stuClass.getDeclaredMethods();
        for (Method method : methods)
            System.out.println(method);
        System.out.println("调用公有方法");
        Method method = stuClass.getMethod("printName1");
        method.invoke(student);
        System.out.println("调用有返回值的方法");
        method = stuClass.getMethod("getAge");
        System.out.println(method.invoke(student));
        System.out.println("调用私有方法");
        method = stuClass.getDeclaredMethod("printName3");
        method.setAccessible(true);
        method.invoke(student);
        System.out.println("调用有参方法");
        method = stuClass.getMethod("printSlogan",  String.class);
        method.invoke(student, "调用了有参方法");
    }
}

除了可以获取对象的方法外,也可以获取静态方法,当然也包括main方法

Field类

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

public class Main {
    public static void main(String[] args) throws Exception {
        System.out.println("获取Class对象");
        Class stuClass = Class.forName("Student");
        System.out.println("通过反射创建Student对象");
        Student student = (Student) stuClass.getConstructor(String.class, String.class, String.class, int.class).newInstance("name1", "name3", "name4", 18);
        System.out.println("获取公有变量");
        Field[] fields = stuClass.getFields();
        for (Field field : fields)
            System.out.println(field);
        System.out.println("获取所有变量");
        fields = stuClass.getDeclaredFields();
        for (Field field : fields)
            System.out.println(field);
        System.out.println("设置获取公有变量");
        Field field = stuClass.getField("name4");
        field.set(student, "name4-4");
        System.out.println(field.get(student));
        System.out.println("设置获取私有变量");
        field = stuClass.getDeclaredField("name1");
        field.setAccessible(true);
        field.set(student, "name1-1");
        System.out.println(field.get(student));
    }
}

总结

通过栗子我们可以看到java的反射机制有以下特点:

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

java反射机制的作用不仅仅局限于文章开头讲到的场景,主要适用于各种框架的开发,比如Spring等。

java反射机制给框架设计带来灵活性,但它还是存在缺点:

  • 首先会遇到性能问题,利用反射获取对象比直接写在代码中要慢很多,不过少量的反射其实也不会有太大影响
  • 内部私有方法或者变量可以通过反射访问,造成了内部数据的暴露,会有安全问题
  • 反射的滥用会导致程序内部逻辑模糊
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值