反射指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类、方法和字段,这种动态获取程序信息以及动态调用对象的功能称为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条)