Java中的反射机制(reflection)

Java中的反射机制(reflection)

1.概述
反射就是在程序运行过程中,根据内存中的类,查看类信息,创建类实例,访问字段,调用方法的技术。反射是java语言的一个特性,它允程序在运行时(注意不是编译的时候)来进行自我检查并且对内部的成员进行操作。例如它允许一个java的类获取他所有的成员变量和方法并且显示出来。许多框架例如:log4j,Servlet、SSM的底层都是用到了反射机制,所以说反射是真的很重要。
反射的简单原理
1)运行程序,启动java虚拟机JVM,JVM的类加载器会把类的字节码文件加载进内存。
2)在java中把Person,Test,String,Scanner,System,Object等所有的类抽象为Class类,即每一个类就是一个Class对象。
3)把所有的字段抽象为Field类,每一个字段就是一个Field对象;把所有方法抽象为Method类,每个方法就是一个Method对象。
4)把所有的构造方法抽象为Constructor类,每个构造方法就是一个Constructor对象,把所有修饰符抽象为Modifier类,每个修饰符就是一个Modifier对象。
5)应用程序类加载器把类的字节码加载到内存后,系统就会创建一个对应的Class对象,可以简单的把内存中的类理解为一个Class对象。
6)反射就是程序运行后,根据Class对象进行编程,即简单的理解为根据内存中的类(字节码)进行编程,编程就是创建对象,访问字段,调用方法。
Java中编译类型有两种:

  • 静态编译:在编译时确定类型,绑定对象即通过。
  • 动态编译:运行时确定类型,绑定对象。动态编译最大限度地发挥了Java的灵活性,体现了多态的应用,可以降低类之间的耦合性。

Java反射是Java被视为动态(或准动态)语言的一个关键性质。 这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public、static等)、superclass(例如Object)、实现interfaces(例如Cloneable),也包括fields和methods的所有信息,并可于运行时改变fields内容或唤起methods。

Reflection可以在运行时加载、探知、使用编译期间完全未知的classes。即Java程序可以加载一个运行时才得知名称的class,获取其完整构造,并生成器对象实体、或对其fields设置、或唤起其methods。

反射(reflection)允许静态语言在运行时(runtime)检查、修改程序的结构与行为。
在静态语言中,使用一个变量时,必须知道它的类型。在Java中,变量的类型信息在编译时都保存到了class文件中,这样在运行时才能保证准确无误;换句话说,程序在运行时的行为都是固定的。如果想在运行时改变,就需要反射这东西了。

2.反射的作用
1)动态创建对象。
2)动态操作属性。
3)动态调用方法。
4)解耦合,提高程序的可扩展性。

3.反射的使用——通过反射获取类的五种方式
1)第一种方式
Class.forName(“全类名”);
将字节码文件加载进内存,返回Class对象。多用于配置文件,将类名定义在配置文件中。读取配置文件,加载类。
即,通过类.Class获取

Class class1 = Class.forName("reflect中Class对象的创建.Person");
System.out.println("1:"+class1);

2)第二种方式
类名.Class
通过类名的属性class获取。多用于参数传递。

Class class2=Person.class;
System.out.println("2:"+class2);

3)第三种方式
对象.getClass()
getClass()方法在Object类中定义着。多用于对象获取字节码的方式。

Person person=new Person();
Class class3 = person.getClass();
System.out.println("3:"+class3);

4)第四种方式
Class class= Integer.TYPE 对于内置类型的包装类,可以通过 TYPE 反射

Class class4= Integer.TYPE;
System.out.println("4:"+class4);

5)第五种方式
Class class5 = class1.getSuperclass()
获取父类

Class class5 = class1.getSuperclass();
System.out.println("5:"+class5);

运行截图:
在这里插入图片描述
案例:

需求:声明一个Person类和Student用于完成基于五种方式来获取Class对象
资源:全类名:包名+类名,即全类名为reflect中Class对象的创建.Person
类的名字
对象的名字
目录结构如下:
在这里插入图片描述
Person类

package reflectClass对象的创建;
public class Person {
    private String name;
    private int age;
    public String a;
    public String b;
    public String c;
    public String d;

    public Person() {
    }

    //public reflect中Class对象的创建.Person(java.lang.String,int)
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getA() {
        return a;
    }

    public void setA(String a) {
        this.a = a;
    }

    public String getB() {
        return b;
    }

    public void setB(String b) {
        this.b = b;
    }

    public String getC() {
        return c;
    }

    public void setC(String c) {
        this.c = c;
    }

    public String getD() {
        return d;
    }

    private void setD(String d) {
        this.d = d;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", a='" + a + '\'' +
                ", b='" + b + '\'' +
                ", c='" + c + '\'' +
                ", d='" + d + '\'' +
                '}';
    }

    //重载方法的名称和参数
    public void eat(){
        System.out.println("eat......");
    }
    public void eat(String food){
        System.out.println("eat....."+food);
    }
}

学生类

package reflectClass对象的创建;
public class Student {
    public void sleep(){
        System.out.println("sleep......");
    }
}

反射测试类

package reflectClass对象的创建;
public class ReflectDemo {

    public static void main(String[] args) throws ClassNotFoundException {
        //1. Class.forName("全类名"):将字节码文件加载进内存,返回Class对象
        Class class1 = Class.forName("reflect中Class对象的创建.Person");
        System.out.println("1:"+class1);

        //2.类名.class:通过类名的属性class获取
        Class class2=Person.class;
        System.out.println("2:"+class2);

        //3.对象.getClass(),getClass()方法在Object类中定义着。
        Person person=new Person();
        Class class3 = person.getClass();
        System.out.println("3:"+class3);

        //4.Class class= Integer.TYPE
        Class class4= Integer.TYPE;
        System.out.println("4:"+class4);

        //获取父类
        Class class5 = class1.getSuperclass();
        System.out.println("5:"+class5);
        /**
         *  同一个字节码文件(*.class)在一次程序运行过程中,
         *  只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。
         *
         *  地址判断的方式验证一下   ==
         */
        System.out.println(class1==class2);
        System.out.println(class1==class3);

        //获取Student类的Class对象
        Class class6 = Student.class;
        System.out.println(class1==class6);
    }
}

运行结果如下:
在这里插入图片描述
结论:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。

4.反射机制常用的类文件

Java.lang.Class;       
Java.lang.reflect.Constructor;
Java.lang.reflect.Field;
Java.lang.reflect.Method;
Java.lang.reflect.Modifier;

在JDK中,主要有以下类来实现Java反射机制,这些类都位于Java.lang.reflect包下

Class类:代表一个类
Constructor类:代表类的构造方法
Field类:代表类的成员变量(属性)
Method类:代表类的成员方法

5.反射的入口Class类
除了基本的数据类型外,其他类型都是Class类

Class类是Java 反射机制的起源和入口

  • 用于获取与类相关的各种信息
  • 提供了获取类信息的相关方法
  • Class类继承自Object类

Class类是所有类的共同的图纸

  • 每个类有自己的对象,好比图纸和实物的关系
  • 每个类也可看做是一个对象,有共同的图纸Class,存放类的结构信息,比如类的名字、属性、方法、构造方法、父类和接口,能够通过相应方法取出相应信息
    Class类的对象称为类对象

6.Class对象功能
目录结构:
在这里插入图片描述
6.1获取成员变量

  • Field[] getFields():获取所有public修饰的成员变量
  • Field getField(String name):获取指定名称的public修饰的成员变量
  • Field[] getDeclaredFields():获取所有的成员变量,不考虑修饰符
  • Field getDeclaredField(String name):获取指定名称的成员变量,不考虑修饰符
package Class对象功能的演示;
import reflectClass对象的创建.Person;
import java.lang.reflect.Field;
public class ReflectDemo1 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        //通过类名.class获取
        Class personClass= Person.class;
        System.out.println(personClass);
        
         /**
         * 一、获取公有成员变量
         * * Field[] getFields() :            获取所有public修饰的成员变量
         * * Field getField(String name)       获取指定名称的 public修饰的成员变量
         * *
         */
        Field[] fields = personClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }

        System.out.println("==========");
        Field b = personClass.getField("b");
        System.out.println(b);
        System.out.println("==========");
        /**
         * 获取到的变量b的作用:
         *         赋值
         *         取值
         */
        Person person=new Person();
        //Object value = b.get(person);
        //System.out.println("value:"+value);   //null

        b.set(person,"云尚");                   //通过反射动态赋值
        Object value = b.get(person);           //取值
        System.out.println("value:"+value);     //云尚

        /**
         * 二、获取的成员变量不考虑修饰符
         * *Field[] getDeclaredFields()          获取所有的成员变量,不考虑修饰符
         * *Field getDeclaredField(String name)  获取指定名称的成员变量,不考虑修饰符
         */
        System.out.println("==========");
        Field[] declaredFields = personClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }

        System.out.println("==========");
        Field age = personClass.getDeclaredField("age");
        System.out.println(age);

        System.out.println("==========");
        //------暴力反射
        age.setAccessible(true);                   //需要操作私有,请求放行
        Object value1 = age.get(person);
        System.out.println("age:"+value1);
        //异常:Class Class对象功能的演示.ReflectDemo1 can not access a member of class reflect中Class对象的创建.Person with modifiers "private"
        //需要加age.setAccessible(true)方法

        age.set(person,18);
        Object value2 = age.get(person);
        System.out.println(value2);
    }
}

运行结果如下:
在这里插入图片描述
6.2获取构造方法

  • Constructor<?>[] getConstructors()
  • Constructor getConstructor(类<?>… parameterTypes)
  • Constructor getDeclaredConstructor(类<?>… parameterTypes)
  • Constructor<?>[] getDeclaredConstructors()
package Class对象功能的演示;
import reflectClass对象的创建.Person;
import java.lang.reflect.Constructor;
public class ReflectDemo2 {
    public static void main(String[] args) throws Exception{
        //通过类名.class获取
        Class personClass= Person.class;
        System.out.println(personClass);

        System.out.println("=============");
        //获取公有的构造方法
        Constructor constructor = personClass.getDeclaredConstructor(String.class, int.class);
        System.out.println(constructor);

        Person person = (Person) constructor.newInstance("云尚", 18);
        System.out.println(person);
    }
}

在这里插入图片描述
6.3获取成员方法

  • Method[] getMethods()
  • Method getMethod(String name, 类<?>… parameterTypes)
  • Method[] getDeclaredMethods()
  • Method getDeclaredMethod(String name, 类<?>… parameterTypes)
package Class对象功能的演示;
import reflectClass对象的创建.Person;
import java.lang.reflect.Method;
public class ReflectDemo3 {
    public static void main(String[] args) throws Exception{
        //通过类名.class获取
        Class personClass= Person.class;
        System.out.println(personClass);
        System.out.println("=============");

        //一、获取公有无参成员方法
        Method eat_method=personClass.getMethod("eat");
        System.out.println(eat_method);   //输出结果:public void reflect中Class对象的创建.Person.eat()
        //执行方法
        Person person=new Person();
        eat_method.invoke(person);
        System.out.println("=============");

        //二、获取公有有参成员方法
        Method eat1_method = personClass.getMethod("eat", String.class);
        System.out.println(eat1_method);
        eat1_method.invoke(person,"米饭");

        System.out.println("所有成员方法===没重写的都继承自Object类==========");
        //获取所有的成员方法-------默认继承Object的所有方法
        Method[] methods = personClass.getMethods();
        for (Method method : methods) {
            System.out.println(method+"——"+method.getName());  //方法的名称
        }

        System.out.println("私有成员方法=============");
        //获取私有的成员方法
        Method[] declaredMethods = personClass.getDeclaredMethods();
        //获取私有的方法
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod+"——"+declaredMethod.getName());
        }

        System.out.println("=============");
        String className = personClass.getName();
        System.out.println("personClass:"+personClass);
        System.out.println("className:"+className);
    }
}

运行结果如下:
在这里插入图片描述
在这里插入图片描述
总结:使用反射可以赋予jvm动态编译的能力,否则类的元数据信息只能用静态编译的方式实现,例如热加载,Tomcat的classloader等等都没法支持。
7.反射的优缺点
7.1反射优点:
可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发中它的灵活性就表现的十分明显。比如,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如这样的话,这个软件肯定是没有多少人用的。采用静态的话,需要把整个程序重新编译一次才可以实现功能的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功能。
7.2反射缺点:
对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值