java基础-深入理解反射

299 阅读4分钟

什么是反射

定义:在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语音的反射机制。

java反射是java被视于动态语音的一个关键特性,它能在运行时加载、探知、使用编译期间完全未知的classes,使java程序可以加载一个运行时才得知名称的class,获取完整构造,并生成其对象实例,或对其fields赋值,或唤起其方法。

反射的应用场合

在java程序中许多对象在运行中都会出现两种状态:编译时状态和运行时状态,编译时的类型由声明对象时实用的类型来决定,运行时的类型由实际赋值给对象的类型决定,例如:Animala=newDog();(编译时类型为Animal,运行时状态为Dog)为了解决这个问题,程序需要在运行时发现对象和类的真实信息。解决该问题有以下两种做法:

  • 假设在编译和运行时都完全指定类型的具体信息,在这种情况下,可以先使用 instanceof 运算符进行判断,再利用 强制类型转换将其转换成运行时类型的变量即可。

  • 第二种做法是编译时根本无法预知该对象和类可能属于哪些类,程序只依靠运行时信息来发现该对象和类的真实信息,这就必须使用反射。

如何使用反射

  • 1、java反射api
类名 用途
Class类 反射的核心类,可以获取类的属性,方法等信息
Field类 Java.lang.reflec 包中的类,表示类的成员变量,可以用来获取和设置类之中的属性值。
Method类 Java.lang.reflec 包中的类,表示类的方法,它可以用来获取类中的方法信息或者执行方法。
Constructor类 Java.lang.reflec 包中的类,表示类的构造方法。
  • 2、获取class对象

每个类被加载之后,系统就会为该类生成一个对应的 Class 对象,通过该 Class 对象就可以访问到 JVM 中的这个类。在 Java 程序中获得 Class 对象通常有如下四种方式:

package com.reflect;

public class Test {
    public static void main(String[] args) throws Exception {
    
        Class clazzA = Class.forName("com.reflect.Test");
        Class clazzB = Test.class;
        Class clazzC = Thread.currentThread().getContextClassLoader().loadClass("com.reflect.Test");
        Class clazzD = new Test().getClass();
        System.out.println(clazzA.getName());
        System.out.println(clazzB.getName());
        System.out.println(clazzC.getName());
        System.out.println(clazzD.getName());

    }
}
  • 3、从class中获取信息
package com.reflect;
public class Dog {
    private String name;
    public Dog(){

    }
    public Dog(String name){
        this.name=name;
    }
    public void speak(){
        System.out.println(name+":汪汪汪汪汪汪");
    }
    private void trian(String content){
        System.out.println(name+"训练:"+content);
    }
    private String getMessge(int a,String b){
       return b+a;
    }
}
package com.reflect;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectTest {
    public static void main(String[] args) throws Exception{
        Class clazz=Class.forName("com.reflect.Dog");
        //获取构造方法并创建对象
        Constructor constructor=clazz.getDeclaredConstructor(String.class);
        Dog xiaoliu=(Dog)constructor.newInstance("小六");
        //使用newInstane方法创建对象
        Dog dog=(Dog)clazz.newInstance();
        Field name=clazz.getDeclaredField("name");
        name.setAccessible(true);
        name.set(dog,"小七");
        Method speakMethod=clazz.getMethod("speak");
        speakMethod.invoke(dog);
        Method trianMethod=clazz.getDeclaredMethod("trian",String.class);
        trianMethod.setAccessible(true);
        trianMethod.invoke(dog,"捕猎");
        Method getMessgeMethod=clazz.getDeclaredMethod("getMessge",int.class,String.class);
        getMessgeMethod.setAccessible(true);
        System.out.println(getMessgeMethod.invoke(dog,1,"捕猎").toString());
    }
}

运行结果

小七:汪汪汪汪汪汪
小七训练:捕猎
捕猎1

反射的优缺点

优点

  • 反射提高了程序的灵活性和扩展性,降低耦合性,提高自适应能力。

  • 它允许程序创建和控制任何类的对象,无需提前硬编码目标类。

缺点

  • 反射效率低,反射包括了一些动态类型,JVM无法对这些代码进行优化,我们应该避免在经常被执行的代码或对性能要求很高的程序中使用反射。

  • 由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用:代码有功能上的错误,降低可移植性,反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。

  • 反射破坏了代码的封装性。

哪里用到反射

  • JDBC中,利用反射动态加载了数据库驱动程序。

  • Web服务器中利用反射调用了Sevlet的服务方法。

  • Spring(注入属性,调用方法等)。