一文让你读懂“反射机制”!
Posted Putarmor
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一文让你读懂“反射机制”!相关的知识,希望对你有一定的参考价值。
反射机制
1.定义
反射(reflection)机制:在程序运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象都能够调用它的任意方法和属性,既然能拿到这些信息,我们就可以顺便修改部分类型信息。我们将这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制
。
2.用途
在日常开发过程中,经常会遇到某个类的成员变量、成员方法是私有属性,也就是说被private关键字进行修饰,此时针对这种情况,就可以用Java语言中的反射机制来获取需要的私有方法和私有属性。
反射最重要的用途就是开发一些通用框架,比如在spring中,我们将所有的Bean交给spring容器进行管理,无论是xml配置Bean还是注解配置,当我们从容器中获取Bean来依赖注入时,容器会读取配置,而配置中结合的就是类的信息,spring根据这些信息,需要创建哪些Bean,spring就动态地创建这些类。
3.基本信息
Java程序中很多对象在运行时会有两种形态,分别是运行时类型
和编译时类型
,比如一个向上转型的示例:Person p = new Student(),编译时期p的类型为Person,运行时期p的类型为Student,程序需要在运行时发现对象和类的真实信息,通过反射程序就能判断对象属于哪些类。
4.反射相关的类
反射中重要的类有四个:
类名 | 用途 |
---|---|
Class类 | 代表类地实体,在运行地Java程序中表示类和接口 |
Field | 代表类地成员变量和类的属性 |
Method类 | 代表类地方法 |
Constructor | 代表类的构造方法 |
反射机制起源:
Java文件被编译后成生成了.class字节码文件,.class文件需要在JVM上去执行,JVM把**.class**文件解析为一个对象,这个对象就是java.lang.Class对象。
反射的根本:从Class对象出发
Person p1 = new Person();
Person p2 = new Person();
我们看到的是Person类具有多个实例,但是Class对象始终只有一个,因为Class对象是和.class文件对应的;当程序运行时,每个.java文件最终变成Class类对象的一个实例。
获得类相关的方法:
方法 | 用途 |
---|---|
getClassLoader() | 获得类的加载器 |
getDeclaredClasses() | 返回一个数组,数组中包含该类中所有类和接口类的对象(包含私有) |
forName(String className) | 根据类名返回类的对象 |
newInstance() | 创建类的实例 |
getName() | 获得类的完整路径名字 |
获得类中属性相关的方法:
方法 | 用途 |
---|---|
getField(String name) | 获得某个公有的属性对象 |
getFields() | 获得所有共有的属性对象 |
getDeclaredField(String name) | 获得某个属性对象 |
getDeclaredFields() | 获得所有属性对象 |
获得类中构造器相关的方法:
方法 | 用途 |
---|---|
getConstructor(Class…<?> parameterTypes) | 获得该类中与参数类型匹配的公有构造方法 |
getConstructors() | 获得该类的所有公有构造方法 |
getDeclaredConstructor(Class…<?> parameterTypes) | 获得该类中与参数类型匹配的所有构造方法 |
getDeclaredConstructors() | 获得该类所有构造方法 |
获得类中方法相关的方法:
方法 | 用途 |
---|---|
getMethod(String name, Class…<?> parameterTypes) | 获得该类某个公有的方法 |
getMethods() | 获得该类所有的公有方法 |
getDeclaredMethod(String name, Class…<?> parameterTypes) | 获得该类某个方法 |
getDeclaredMethods() | 获得该类所有方法 |
反射示例
获得Class对象的三种方式
在反射之前,需要做的第一步就是先拿到当前需要反射的类的Class对象,然后通过Class对象的核心方法,达到反射目的。也就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,那么我们就可以容易地修改一些信息!!!
第一种方式:使用Class.forName(“类的全路径名”);这是静态方法,使用该方法的前提是已经事先知道类的全路径名;
第二种方式:使用.class方法,仅仅适合在编译之前就已经明确要操作的类;
第三种方式:使用类对象的getClass()方法。
代码演示三种创建Class对象的方式:
class Student{
//私有属性name
private String name = "bit";
//公有属性age
public int age = 18;
//不带参数的构造方法
public Student(){
System.out.println("Student()");
}
private Student(String name,int age) {
this.name = name;
this.age = age;
System.out.println("Student(String,name)");
}
private void eat(){
System.out.println("i am eat");
}
public void sleep(){
System.out.println("i am pig");
}
private void function(String str) {
System.out.println(str);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\\'' +
", age=" + age +
'}';
}
}
public class TestDemo {
public static void main(String[] args) throws ClassNotFoundException {
//1、用的最多的方式,但是可能抛出ClassNotFoundException异常
Class<?> c1 = Class.forName("Student");
//2、直接通过类名.class得到,该方法最安全可靠,程序性能更高,
//说明每个类都有一个隐含的静态成员变量class
Class<?> c2 = Student.class;
//3、通过类对象调用getClass方法获得
Student s = new Student();
Class<?> c3 = s.getClass();
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
}
}
代码执行结果:
可以看出c1、c2、c3都是同一个实例对象,再一次印证了一个类在JVM中只有一个Class实例对象。
反射使用
通过反射创建对象
// 创建对象
public static void reflectNewInstance() {
try {
//1、获得Class对象
Class<?> cl = Class.forName("Student");
//2、创建类的实例
Student student = (Student) cl.newInstance(); //newInstance返回的是T类型
System.out.println(student);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
调用该方法执行的结果如下:
调用newInstance()时候调用的是Student的默认的无参构造方法,对于类的实例student来说,它的属性name值和age值是默认的成员属性值。
反射私有构造方法
Class<?> cl = Class.forName("Student");
//2、获取的是不带有参数的构造方法
Constructor<?> constructor = cl.getConstructor();
Student student = (Student) constructor.newInstance();
System.out.println(student);
通过Class对象调用getConstructor()可以创建Class构造器,再使用构造器也可以创建类的实例对象,默认调用的是无参构造方法。
public static void reflectPrivateConstructor() {
try {
Class<?> cl = Class.forName("Student");
//获得该类所有的构造方法,包含私有和公有
//传参时,参数类型和原本带有参数的构造方法参数类型相匹配
Constructor<?> constructor =
cl.getDeclaredConstructor(String.class, int.class);
//反射私有东西时,要设置下面这句命令,否则将出现异常
constructor.setAccessible(true);
Student student = (Student) constructor.newInstance("trq", 24);
System.out.println(student);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
程序执行结果:
当没有设置constructor.setAccessible(true)时,将引发下面的异常,可以看出这个时候我们就无法获得私有的构造方法。
反射私有属性
public static void reflectPrivateField() {
try {
Class<?> cl = Class.forName("Student");
Field field = cl.getDeclaredField("name");
Field field1 = cl.getDeclaredField("age");
field.setAccessible(true);
//拿到类的对象才能设置属性,因为成员属性是属于对象的
Student student = (Student) cl.newInstance();
System.out.println(student);
field.set(student,"trq");
field1.set(student,24);
System.out.println(student);
System.out.println(field.get(student)); //拿到对象的属性
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
程序执行结果如下:
可以看出,私有成员属性name被反射,对其值进行了修改。
反射私有方法
public static void reflectPrivateMethod() {
try {
//1、
Class<?> cl = Class.forName("Student");
//参数:要反射的方法的名称 与方法中参数匹配的类型
Method method = cl.getDeclaredMethod("function",String.class);
//下面的方法通过数组接收
//Method[] methods = cl.getDeclaredMethods();
method.setAccessible(true);
Student student = (Student) cl.newInstance();
method.invoke(student,"我是trq!"); //
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
程序执行结果如下:
通过反射拿到了私有方法function(),并对该方法进行了修改
5.反射优点、缺点
反射优点:
1.对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;
2.增加程序的灵活性和扩展性,降低耦合性,提高自适应能力;
3.反射已经应用在很多流行框架如:spring Hibernate
反射缺点:
1.使用反射存在效率问题,导致程序效率降低;
当去修改一个字段的值时,反射需要创建两个对象如下图所示;而不使用反射时我们直接可以通过set方法去设置成员属性的值,相比之下反射效率有所降低。
2.反射技术绕过了源代码的技术,因此会带来一定的维护问题,反射代码比相应的直接代码更加复杂。
希望你我他对反射有了更清晰的认识…
以上是关于一文让你读懂“反射机制”!的主要内容,如果未能解决你的问题,请参考以下文章