面试系列之Java反射

反射指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类、方法和字段,这种动态获取程序信息以及动态调用对象的功能称为Java的反射机制。

在Java中,除了int、double等基本类型外,Java的其他类型全部都是class,

class是由JVM在执行过程中动态加载的,每加载一种class,JVM就为其创建一个Class实例。通过Class实例获取class信息的方法称为反射;

只有JVM能创建Class实例,我们自己的java程序是无法创建Class实例的。

每个Java文件都会被编译成.class文件,这些class文件在程序运行时会被ClassLoader加载到JVM中,当一个类被加载以后,JVM就会在内存中自动产生一个Class对象。

由于JVM为每个加载的class创建了对应的Class实例,并在实例中保存了该class的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段等,因此如果获取了某个Class实例,我们就可以通过这个Class实例获取到该实例对应的class所有信息。

获取Class实例的三种方式

  • 通过一个class的静态变量获取
  • 通过静态变量提供的getClass()获取
  • 通过静态方法Class.forName()获取
        //方法一、直接通过一个class的静态变量获取
        Class cls = String.class;

        //方法二、通过实例变量提供的getClass()获取
        String s = "Hello";
        Class cs = s.getClass();

        //方法三、如果知道一个class的完整类名,可以通过静态方法Class.forName()获取
        Class ss = Class.forName("java.lang.String");

        System.out.println(s1 == s2); //true
        System.out.println(s1 == s3); //true

因为Class实例在JVM中是唯一的,所以上述方法获取的Class实例是同一个实例。

通过Class实例我们可以创建对应类型的实例,相当于new String(),不过只能调用public的无参构造方法。

        Class aa = String.class;
        String str = (String)aa.newInstance();

利用反射获取成员变量

  • Field getField(name):根据字段名获取某个public的field(包括父类)
  • Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)
  • Field[] getFields():获取所有public的field(包括父类)
  • Field[] getDeclaredFields():获取当前类的所有field(不包括父类)
        Class stu = Student.class;
        for(Field ff:stu.getDeclaredFields()) {
            System.out.println(ff);
        }
        //获取所有public的成员变量
        System.out.println(stu.getFields());
        //获取所有非public的成员变量
        System.out.println(stu.getDeclaredFields());
        //根据Name获取变量
        System.out.println(stu.getDeclaredField("paperNo"));


        执行结果:
        private java.lang.String com.garin.springboot.demo.Student.name
        private int com.garin.springboot.demo.Student.age
        private int com.garin.springboot.demo.Student.id
        private java.lang.String com.garin.springboot.demo.Student.paperNo
        private java.lang.String com.garin.springboot.demo.Student.mobile
        private java.lang.String com.garin.springboot.demo.Student.mail
        private java.util.Date com.garin.springboot.demo.Student.createTime
        private java.util.Date com.garin.springboot.demo.Student.updateTime
        [Ljava.lang.reflect.Field;@10f87f48
        [Ljava.lang.reflect.Field;@b4c966a
        private java.lang.String com.garin.springboot.demo.Student.paperNo
@Data
public class Student {

    private String name;
    private int age;
    private int id;
    private String paperNo;
    private String mobile;
    private String mail;
    private Date createTime;
    private Date updateTime;

   public Student(String name){
       this.name = name;
   }
}

如果利用set、get方法来获取和设置private、protected修饰的成员变量时,需要利用setAccessibl(true)来忽略访问修饰符的检查,否则程序将报错。

利用反射获取方法

  • Method getMethod(name, Class...):获取某个public的Method(包括父类)
  • Method getDeclaredMethod(name, Class...):获取当前类的某个Method(不包括父类)
  • Method[] getMethods():获取所有public的Method(包括父类)
  • Method[] getDeclaredMethods():获取当前类的所有Method(不包括父类)
        Class stu = Student.class;
        // 获取public方法getScore,参数为String:
        System.out.println(stu.getMethod("getName"));
        // 获取所有的public方法:
        System.out.println(stu.getDeclaredMethods());
        //循环打印每个方法名
        for (Method mm:stu.getDeclaredMethods()) {
            System.out.println("方法名:"+ mm);
        }
        // 获取所有的private方法:
        System.out.println(stu.getMethods());

获取到类的成员方法之后,如果要执行某一个方法,可以使用invoke()方法来执行。

获取构造方法:

  • getConstructor(Class...):获取某个public的Constructor;
  • getDeclaredConstructor(Class...):获取某个Constructor;
  • getConstructors():获取所有public的Constructor;
  • getDeclaredConstructors():获取所有Constructor。

调用非public的时候,需要设置setAccessible(true)可以访问非public方法

        Class st = Student.class;
        ClassLoader cd = st.getClassLoader();
        System.out.println("类加载器"+cd);
        //获取所有构造方法
        Constructor[] ct = st.getConstructors();
        //带参构造方法
        Constructor cs = st.getConstructor(String.class);
        Student ss = (Student)cs.newInstance("studentA");
        System.out.println(ss);

学了这么多反射的知识后,我们来看下反射的面试题:

1、获取字节码的方式有几种 ??

获取字节码的三种方法:

  • Class.forName() //Class ss = Class.forName("java.lang.String");
  • 类名.class // Class cls = String.class;
  • this.getCLass() // String s = "Hello"; Class cs = s.getClass();

2、Java反射API有几类??

  • Field类:提供有关类的属性信息
  • Constructor类:提供有关类的构造方法的信息
  • Method类:提供有关类的方法的信息,包括抽象方法
  • Class类:表示正在运行的Java应用程序的类的实例
  • Object类:Object类是所有Java类的父类,所有对象都默认实现了Object类的方法

3、实例化对象的方式有几种 ?

实例化对象的五种方式:

  • 通过new语句创建对象
  • 通过工厂方法返回对象,如String str = String.valueOf(123);
  • 运用反射手段, Class.newInstance()
  • 调用对象的clone()方法
  • 通过I/o流,调用java.io.ObjectInputStream对象的readObject()方法

4、反射机制的优缺点?

优点

  • 在程序运行过程中可以操作类对象,增加了程序的灵活性
  • 解耦,从而提高程序的可扩展性,提高代码的复用率,方便外部调用
  • 对于任何一个类,当知道它的类名后,就能够知道这个类的所有属性和方法。

缺点

性能问题:Java反射中包含了一些动态类型,JVM无法对这些动态代码进行优化,因此通过反射来操作的方式比正常操作效率低

安全问题:使用反射时要求必须在一个没有安全限制的环境中运行,如果程序安全限制,就不能使用反射

程序健康性:反射允许代码执行一些平常不被允许的操作,破坏了程序结构的抽象性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值