Java反射机制

本文详细介绍了Java反射机制,包括其原理、作用、提供的功能、优缺点和常见应用场景。通过Class、Constructor、Field和Method四个关键对象,开发者可以在运行时动态获取类信息和调用方法。文章还讨论了反射的灵活性、性能影响以及如何通过反射进行类的实例化和方法调用,并提供了实例代码演示。
摘要由CSDN通过智能技术生成

在这里插入图片描述

1. 什么是反射机制

答案:
所谓的反射机制就是 java 语言在运行时拥有一项自观的能力。通过这种能力可以彻底的了解自身的情况为下一步的动作做准备。 Java 的反射机制的实现要借助于 4 个类:Class,Constructor,Field,Method; 其中 Class - 类对 象,Constructor-类的构造器对象,Field-类的属性对象,Method-类的方法对象。通过这四个对象我们可以粗略的看到一个 类的各个组成部分。

1.1 反射原理

Java反射机制(Java Reflection)是Java语言中一种动态(运行时)访问、检测和修改它本身的能力,主要作用是动态(运行时)获取类的完整结构信息和调用对象的方法。

2. Java反射的作用

答案:
在 Java 运行时环境中,对于任意一个类,可以知道这个类有哪些属性和方法。对于任意一个对象,可以调用它的任意一个方法。这种动态获取类的信息 以及动态调用对象的方法的功能来自于 Java 语言的反射(Reflection)机制。

3. Java反射机制提供功能

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

静态编译和动态编译
静态编译:在编译时确定类型,绑定对象
动态编译:运行时确定类型,绑定对象

4. Java反射的优缺点

优点:
1.增加程序的灵活性,可以在运行的过程中动态对类进行修改和操作
2.提高代码的复用率,比如动态代理、spring管理bean,就是用到反射来实现的
3.可以在运行时轻松获取任意一个类的方法、属性,并且还能通过反射进行动态调用

缺点:
1.反射会涉及到动态类型的解析,所以jvm无法对这些代码进行优化,导致性能要比非反射调用更低
2.使用反射以后,代码的可读性会下降
3.反射可以绕过一些限制访问的属性或者方法,可能会导致破坏代码本身的抽象性

5. 应用场景

在这里插入图片描述

  • 逆向代码 ,例如反编译
  • 与注解相结合的框架,例如 Retrofit
  • 单纯的反射机制应用框架,例如 EventBus
  • 动态生成类框架 例如Gson

6. Class的几种获取方式

(1)获取类的java.lang.Class实例对象,常见的三种方式分别为:

  • 通过MyClass.class获取,这里的MyClass指具体类
  • 通过Class.forName(“类的全局定名”)获取,全局定名为包名+类名
  • 通过new MyClass().getClass()获取,这里的MyClass指具体类
//第一种方式 通过Class类的静态方法——forName()来实现
class1 = Class.forName("com.tsp.reflection.Person");

//第二种方式 通过类的class属性
class1 = Person.class;

//第三种方式 通过对象getClass方法
Person person = new Person();
Class<?> class1 = person.getClass();

(2)通过MyClass.class获取,JVM会使用ClassLoader类加载器将类加载到内存中,但并不会做任何类的初始化工作,返回java.lang.Class对象

(3)通过Class.forName(“类的全局定名”)获取,同样,类会被JVM加载到内存中,并且会进行类的静态初始化工作,返回java.lang.Class对象

(4)通过new MyClass().getClass()获取,这种方式使用了new进行实例化操作,因此静态初始化和非静态初始化工作都会进行,getClass方法属于顶级Object类中的方法,任何子类对象都可以调用,哪个子类调用,就返回那个子类的java.lang.Class对象

这3种方式,最终在JVM堆区对应类的java.lang.Class对象都属于同一个,也就是内存地址相同,进行==双等号比较结果为true,原因是JVM类加载过程中使用的是同一个ClassLoader类加载器加载某个类,不论加载多少次,生成到堆区的java.lang.Class对象始终只有一个,除非自定义类加载器,破坏JVM的双亲委派机制,使得同一个类被不同类加载器加载,JVM才会把它当做两个不同的java.lang.Class对象

7. 实例代码

7.1 常用方法测试

package reflect;

import java.lang.reflect.Method;
import java.util.Scanner;

/**
 * JAVA反射机制
 * 反射是JAVA的动态机制,可以在程序【运行期间】再确定如:对象实例化,方法调用
 * 属性操作等。
 * 反射机制可以大大的提高代码的灵活度和可扩展性,但是随之带来的是较慢的运行
 * 效率和更多的系统开销。因此不能过度依赖反射机制。
 */
public class ReflectDemo1 {
    public static void main(String[] args) throws ClassNotFoundException {
        /*
            Class类
            Class类的实例被称为"类对象"。
            JVM在加载一个类的字节码文件到内部时就会实例化一个Class实例
            与加载的类对应,用这个Class实例记录加载的类的相关信息。并且
            在JVM内部每个被加载的类【都有且只有一个】类对象与之对应。

            获取一个类的类对象方式有:
            1:类名.class
            Class cls = String.class;
            Class cls = int.class;

            2.Class.forName(String className)
              参数:类的完全限定名(包名.类名)
            Class cls = Class.forName("java.lang.String")

            3:使用ClassLoader加载

            4:对象.getClass()
         */
        //获取String的类对象
//        Class cls = String.class;
//        Class cls = Person.class;

//        Class cls = Class.forName("java.lang.String");
//        Class cls = Class.forName("reflect.Person");
        /*
            java.lang.String
            java.util.HashMap
            java.util.ArrayList
            java.io.FileInputStream
            reflect.Person
         */
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入一个类名:");
        String className = scanner.nextLine();
        Class cls = Class.forName(className);

        String name = cls.getName();//获取类的完全限定名
        System.out.println(name);
        name = cls.getSimpleName();//仅获取类名
        System.out.println(name);
        /*
            Method类
            Method类的每一个实例称为"方法对象"。
            该类的每个实例表示某个类中的一个方法。通过方法对象我们可以得知
            其表示的方法的相关信息,如:方法名,返回值类型,参数个数,参数类型
            等等。
         */
        /*
            Class中的方法:
            Method[] getMethods()
            获取当前类对象所表示的类的所有公开方法
         */
        Method[] methods = cls.getMethods();
        System.out.println(name+"一共有"+methods.length+"个公开方法");
        for(Method method : methods){
            System.out.println(method.getName());
        }
    }
}
package reflect;

import java.util.Scanner;

/**
 * 使用类对象实例化
 */
public class ReflectDemo2 {
    public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        Person p = new Person();
        System.out.println(p);

        /*
            反射机制进行实例化的步骤:
            1:加载要实例化对象的类的类对象
            2:直接通过类对象的方法newInstance()实例化
         */
//        Class cls = Class.forName("reflect.Person");
        /*
            java.util.ArrayList     java.util.HashMap
            reflect.Person          java.util.Date
         */
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入一个类名:");
        String className= scanner.nextLine();
        Class cls = Class.forName(className);
        //newInstance()方法会调用类对象所表示的类的【公开的无参构造器】实例化
        Object obj = cls.newInstance();//new Person();
        System.out.println(obj);
    }
}

7.2 使用自定义类测试

package reflect.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 在注解上我们可以使用java预定义的注解来规范某些操作,常用的有:
 * @Target 用于指定当前注解可以被应用的位置。
 *         不指定该注解时,我们定义的注解可以被应用于任何可以使用注解的位置,
 *         比如:类上,方法上,构造器上,属性上,参数上等
 *         可以通过为Target注解传入参数来指定可以使用的特定位置,这些位置
 *         都被ElementType规定。
 *         @Target(ElementType.TYPE)
 *         只能在类上使用该注解
 *
 *         @Target({ElementType.TYPE,ElementType.FIELD})
 *         可以在类上或属性上使用该注解(多个位置时要以数组形式表达)
 *
 * @Retention 用于指定当前注解的保留级别,有三个可选值
 *         RetentionPolicy.SOURCE   注解仅保留在源代码中
 *         RetentionPolicy.CLASS    注解保留在字节码中,但是不可被反射机制访问(默认保留级别)
 *         RetentionPolicy.RUNTIME  注解保留在字节码中,可被反射机制访问
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface AutoRunClass {
}
package reflect.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AutoRunMethod {
    /*
        注解中可以定义参数
        格式为:
        类型 参数名() [default 默认值]
        默认值是可选的,不是必须指定的。如果指定了默认值,那么使用注解时
        可以不传入该参数,此时参数使用这个默认值。
        如果参数没有指定默认值,那么使用注解时必须为这个参数赋值。

        如果注解中只有一个参数时,参数名建议选取"value"。这样外界在使用
        该注解时,传递参数则不需要指定参数名,可以直接写作:
        @AutoRunMethod(5)    此时value的值就是5

        如果该参数名不叫value,比如叫:
        int num() default 1;
        则使用注解时为该参数传递值时必须写作:
        @AutoRunMethod(num=5)

     */
    int value() default 1;

    /*
        一个注解中可以定义多个参数,当使用该注解时传参格式为:name=value
        @AutoRunMethod(num=1,name="张三")
        或
        @AutoRunMethod(name="张三",num=1)    (传参顺序不所谓)

     */
//    int num() default 1;
//    String name();
    /*
        并且两个以上参数(含两个)时,其中某一个参数名指定为value,传参时也
        必须指定该参数名
        例如:
        @AutoRunMethod(value=1,name="张三")
        或
        @AutoRunMethod(name="张三",value=1)
     */
    //    int value() default 1;
    //    String name();

}
package reflect;

import reflect.annotations.AutoRunClass;
import reflect.annotations.AutoRunMethod;

/**
 * 使用当前类测试反射操作
 */
@AutoRunClass
public class Person {
    private String name = "张三";
    private int age = 20;
    public Person(){}

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @AutoRunMethod
    public void sayHello(){
        System.out.println(name+":hello!");
    }
    @AutoRunMethod(5)
    public void sayHi(){
        System.out.println(name+":hi!");
    }
    public void say(String info){
        System.out.println(name+":"+info);
    }
    public void say(String info,int count){
        for(int i=0;i<count;i++) {
            System.out.println(name + ":" + info);
        }
    }
    @AutoRunMethod(8)
    public void watchTV(){
        System.out.println(name+"看电视");
    }
    public void playGame(){
        System.out.println(name+"打游戏");
    }
    @AutoRunMethod(3)
    public void justDoIt(){
        System.out.println(name+"只管干!");
    }
    public void eat(){
        System.out.println(name+"干饭!");
    }
    public void sleep(){
        System.out.println(name+"睡觉");
    }
    private void hehe(){
        System.out.println("我是Person的私有方法!");
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    
}
package reflect;

import java.lang.reflect.Constructor;

/**
 *  使用有参构造器实例化对象
 */
public class ReflectDemo3 {
    public static void main(String[] args) throws Exception {
        Person p = new Person("李四",22);
        System.out.println(p);

        //加载类对象
        Class cls = Class.forName("reflect.Person");
        /*
            Constructor 构造器对象
            该类的每一个实例用于表示一个类中的某个构造器
         */
        //通过类对象获取无参构造器
//        Constructor constructor = cls.getConstructor();//Person()
        //Person(String,int)
        Constructor con = cls.getConstructor(String.class,int.class);
        //new Person("王五",33)
        Object obj = con.newInstance("王五",33);
        System.out.println(obj);

    }
}
package reflect;

import java.lang.reflect.Method;
import java.util.Scanner;

/**
 * 使用反射机制调用方法
 */
public class ReflectDemo4 {
    public static void main(String[] args) throws Exception {
        Person p = new Person();
        p.eat();

        //实例化
//        Class cls = Class.forName("reflect.Person");
//        Object o = cls.newInstance();//Person p = new Person();
//        //调用方法
//        Method method = cls.getMethod("eat");//获取名为"eat"的无参方法
//        method.invoke(o);//执行eat方法  p.eat()

        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入类名:");
        String className = scanner.nextLine();
        System.out.println("请输入方法名:");
        String methodName = scanner.nextLine();

        Class cls = Class.forName(className);
        Object o = cls.newInstance();
        Method method = cls.getMethod(methodName);
        method.invoke(o);
        
    }
}
package reflect;

import java.lang.reflect.Method;

/**
 * 调用有参方法
 */
public class ReflectDemo5 {
    public static void main(String[] args) throws Exception {
        Person p = new Person();
        p.say("你好!");

        //实例化
        Class cls = Class.forName("reflect.Person");
        Object o = cls.newInstance();
        //调用方法
        Method m = cls.getMethod("say1",String.class);//say(String)
        //invoke方法第二个参数开始为调用方法时传递的实际参数
        m.invoke(o,"大家好!");

        Method m2 = cls.getMethod("say",String.class,int.class);
        m2.invoke(o,"嘿嘿",5);

    }
}
package reflect;

import java.lang.reflect.Method;

/**
 * 反射机制访问私有成员(暴力反射)
 */
public class ReflectDemo6 {
    public static void main(String[] args) throws Exception {
//        Person p = new Person();
//        p.hehe();//编译不通过,私有方法不可以被类的外部调用

        Class cls = Class.forName("reflect.Person");
        Object o = cls.newInstance();
        /*
            Class的方法:
            Method getMethod(String name,Class... cls)
            Method[] getMethods()
            上述两个方法仅能获取该类的公开方法(包括从超类继承的方法)

            getDeclaredMethod()
            getDeclaredMethods()
            上述两个方法可以获取本类定义的方法(含有私有的,不含有继承的)

         */
//        Method m = cls.getMethod("hehe");

//        Method[] methods = cls.getDeclaredMethods();
//        for(Method method:methods){
//            System.out.println(method.getName());
//        }
        Method m = cls.getDeclaredMethod("hehe");
        m.setAccessible(true);//强行打开访问权限
        m.invoke(o);
        m.setAccessible(false);//对于私有成员,访问后关闭(必须)
    }
}
package reflect;
//单例模式
public class Singleton {
    private static Singleton instance = new Singleton();
    private Singleton(){}
    public static Singleton getInstance(){
        return instance;
    }

}
package reflect;

import java.lang.reflect.Constructor;

/**
 * 反射机制可破坏单例模式
 */
public class ReflectDemo7 {
    public static void main(String[] args) throws Exception {
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();
        System.out.println(s1);
        System.out.println(s2);

        Class cls = Class.forName("reflect.Singleton");
        //获取私有的无参构造器
        Constructor c = cls.getDeclaredConstructor();
        c.setAccessible(true);
        Object o = c.newInstance();
        System.out.println(o);
    }
}
package reflect;

import reflect.annotations.AutoRunClass;

/**
 * 反射机制访问注解
 */
public class ReflectDemo8 {
    public static void main(String[] args) throws Exception {
        /*
            常用的反射对象:
            Class,Method,Constructor,Field,Annotation
            都提供了一个方法:
            boolean isAnnotationPresent(Class cls)
            判断当前反射对象表示的内容是否被cls所表示的注解标注了
         */
        Class cls = Class.forName("reflect.Person");
        //判断当前cls所表示的类Person是否被注解AutoRunClass标注了
        if(cls.isAnnotationPresent(AutoRunClass.class)){
            System.out.println("被标注了!");
        }else{
            System.out.println("没有被标注!");
        }

    }
}
package reflect;

import reflect.annotations.AutoRunMethod;

import java.lang.reflect.Method;

public class ReflectDemo9 {
    public static void main(String[] args) throws Exception {
        Class cls = Class.forName("reflect.Person");
//        Method method = cls.getMethod("sayHi");
        Method method = cls.getMethod("eat");
        if(method.isAnnotationPresent(AutoRunMethod.class)){
            System.out.println("被标注了!");
        }else{
            System.out.println("没有被标注!");
        }
    }
}
package reflect;

import reflect.annotations.AutoRunClass;
import reflect.annotations.AutoRunMethod;

import java.lang.reflect.Method;

/**
 * 访问注解参数
 */
public class ReflectDemo10 {
    public static void main(String[] args) throws Exception {
        Class cls = Class.forName("reflect.Person");
        if(cls.isAnnotationPresent(AutoRunClass.class)){
            Method method = cls.getDeclaredMethod("watchTV");
            if(method.isAnnotationPresent(AutoRunMethod.class)){
                /*
                    所有反射对象都支持方法:
                    Annotation getAnnotation(Class cls)
                    获取cls类对象表示的注解
                 */
                //返回当前method对象表示的方法"watchTV"上的注解@AutoRunMethod
                AutoRunMethod arm = method.getAnnotation(AutoRunMethod.class);
                int value = arm.value();
                System.out.println("参数值:"+value);
            }
        }

    }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值