什么是反射?
在我们学习 Java 之初,我们怎么去写代码?
1 Student student = new Student();
首先我们要创建类的实例化对象,在去调用类的方法、获取值等操作,如果没有这个对象的话,那么我们什么也做不了,例如:
1 package 反射; 2 3 public class Reflection01 { 4 public static void main(String[] args) { 5 Student student = new Student(); 6 student.say(); 7 } 8 } 9 10 class Student { 11 public void say() { 12 System.out.println("执行say()方法"); 13 } 14 } 15 16 /** 17 输出: 18 执行say()方法 19 */
1 package 反射; 2 3 public class Reflection02 { 4 public static void main(String[] args) { 5 6 } 7 } 8 9 interface Animal { 10 void eat(); 11 } 12 13 class Cat implements Animal { 14 public void eat() { 15 System.out.println("猫吃鱼"); 16 } 17 } 18 19 class Dog implements Animal { 20 public void eat() { 21 System.out.println("狗吃骨头"); 22 } 23 }
1 Cat cat = new Cat(); 2 cat.eat(); // 猫吃鱼 3 Dog dog = new Dog(); 4 dog.eat(); // 狗吃骨头
假如我们在编译期间没有办法确定我们是用 Cat 类 还是用 Dog 类,那么该怎么办呢?
这时候就需要用到了反射。
Java反射机制 是在运行状态中,对任意一个类 ,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制。
反射 被视为动态语言的关键,反射机制允许程序在执行期间借助于 Reflection API 取得任何类的年内部信息,并能直接操作对象的内部属性和方法。
由此可以做如下总结,Java 反射就是在运行状态中:
-
获取任意类的名称、package 信息、所有属性、方法、注解、类型、类加载器等
-
获取任意对象的属性,并能改变对象的属性
-
调用任意对象的方法
-
判断任意一个对象所属的类
-
实例化任意一个类对象
通过反射 ,我们可以动态实现装配 ,降低代码耦合,动态代理等。
但是,反射过度使用也会消耗系统的性能。
Java 运行分为两种状态:
编译时,通过
Javac命令,生成一个或多个.class字节码 文件,(每个 .class 字节码文件对应一个类);运行时,通过
Java命令,将一个或多个.class字节码文件加载到内存中。(由 JVM 提供的类加载器完成)。
反射的原理
研究反射的原理,首先要知道类加载的过程
类加载过程

那么反射是什么呢?反射是反而为之,在编译期我们并没有确定加载哪个类,而是在运行期来确定。
那么反射为什么能够在运行期执行这样一个反射的操作呢?
接下来,我们从 JVM 类加载角度来分析一下。
JVM 类加载流程和内存结构
观察上图,首先是看图片左侧,是 Java 的一个源文件,这个源文件在编译的时候会变成一个 .class 文件,如:cafe babe 0000 0031 0022 0a00... 这其实就是该类的所有信息,在我们有所有信息的时候,classLoader(类加载器)才会把这个 .class 文件加载到 JVM 中,在加载的过程中就涉及到了加载、验证、准备、解析、初始化等一系列操作,加载完成之后就是 JVM 的内存管理了。
完成上述操作:
-
首先是需要一个 Java 源代码,
-
第二个是
.class文件,这个文件是需要后续别加载进去的, -
第三个是
classloader(类加载器 ),用于加载.class文件
那么显然我们就会知道,反射其实用的就是 .class 文件,那么我们如何去使用这个文件呢?
class文件包含了哪些内容?

生成对象的步骤

如上图所示:
1 Person person = new Person();
运行期加载呢?
-
首先我们也是加载
.class文件
Class personClazz = Class.forName("com.muse.Person");
- 其次我们要创建构造函数
Constructor constructor = personClazz.getConstructor();
- 最后,通过构造函数 Person() 创建对象
Person person = (Person)contructor.newInstance();
1 package 反射; 2 3 public class Person { 4 public String name = "laoma"; 5 6 protected Integer age = 18; 7 8 private Byte sex = (byte) 1; 9 10 Boolean isMarriage = false; 11 12 public Person() { 13 } 14 15 public Person(String name, Integer age, Byte sex, Boolean isMarriage) { 16 this.name = name; 17 this.age = age; 18 this.sex = sex; 19 this.isMarriage = isMarriage; 20 } 21 22 public String getName() { 23 return name; 24 } 25 26 public void setName(String name) { 27 this.name = name; 28 } 29 30 public Integer getAge() { 31 return age; 32 } 33 34 public void setAge(Integer age) { 35 this.age = age; 36 } 37 38 public Byte getSex() { 39 return sex; 40 } 41 42 public void setSex(Byte sex) { 43 this.sex = sex; 44 } 45 46 public Boolean getMarriage() { 47 return isMarriage; 48 } 49 50 public void setMarriage(Boolean marriage) { 51 isMarriage = marriage; 52 } 53 54 }
获取 .class
1 package 反射; 2 3 public class Reflection03 { 4 public static void main(String[] args) throws Throwable { 5 // 方式一 6 // 类.class 7 Class personClazz = Person.class; 8 System.out.println(personClazz); 9 10 // 方式二 11 // 实例.getClass() 12 Person person = new Person(); 13 Class personClazz1 = person.getClass(); 14 System.out.println(personClazz1); 15 16 // 方式三 17 // Class.forName("类的全路径") 18 Class personClazz2 = Class.forName("反射.Person"); 19 System.out.println(personClazz2); 20 } 21 }
反射实例
1 package 反射; 2 3 import java.lang.reflect.Constructor; 4 import java.lang.reflect.Field; 5 6 public class Reflection04 { 7 public static void main(String[] args) throws Throwable { 8 // 获取 Person.class 的字节码 9 Class personClazz = Class.forName("反射.Person"); 10 // 获取构造方法 Person() 11 // 获取无参的构造函数 12 Constructor Controctor1 = personClazz.getConstructor(); 13 // 获取有参的构造函数 14 Constructor Controctor2 = personClazz.getConstructor(String.class, Integer.class, Byte.class, Boolean.class); 15 System.out.println(Controctor1); 16 System.out.println(Controctor2); 17 // 通过构造函数 Person() 创建对象 18 // 调用无参构造方法创建对象 19 Person person1 = (Person)Controctor1.newInstance(); 20 person1.setName("你好"); 21 System.out.println("person=" + person1); 22 // 调用有参构造方法创建对象 23 Person person2 = (Person)Controctor2.newInstance("loama", 22,(byte)1, true); 24 System.out.println("person=" + person2); 25 26 // 获取类中的构造方法 27 Constructor[] declarCoonstructors = personClazz.getDeclaredConstructors(); 28 System.out.println("获取person的所有构造方法" + declarCoonstructors); 29 30 // 获取类中的某个对象 31 Field nameField = personClazz.getField("name"); 32 // 操作Field,获得属性值 33 String name = String.valueOf(nameField.get(person1)); 34 System.out.println("获取类中的name属性" + name); 35 36 // 获取类中的私有属性 37 /** 38 * 通过反射获得类的public属性值 39 * <p> 40 * getField 只能获取public的,包括从父类继承来的字段。 41 * getDeclaredField 可以获取本类所有的字段,包括private的,但是不能获取继承来的字段。 (注: 这里只能获取到private的字段,但并不能访问该private字段的值, 42 * 除非加上setAccessible(true)) 43 */ 44 Field nameFieldPrivate = personClazz.getDeclaredField("sex"); 45 nameFieldPrivate.setAccessible(true); // 设置为 true 就可以访问私有 46 Byte sex = (Byte) nameFieldPrivate.get(person); 47 System.out.println(sex); 48 } 49 }
| 用途 | |
|---|---|
| Class类 | 代表类的实体,在运行的Java应用程序中表示类和接口 |
| Field类 | 代表类的成员变量(成员变量也称为类的属性) |
| Method类 | 代表类的方法 |
| Constructor类 | 代表类的构造方法 |
Class类
Class 代表类的实体,在运行的Java应用程序中表示类和接口。在这个类中提供了很多有用的方法,这里对他们简单的分类介绍。
-
获得类相关的方法
| 方法 | 用途 |
|---|---|
asSubclass(Class<U> clazz) |
把传递的类的对象转换成代表其子类的对象 |
Cast |
把对象转换成代表类或是接口的对象 |
getClassLoader() |
获得类的加载器 |
getClasses() |
返回一个数组,数组中包含该类中所有公共类和接口类的对象 |
getDeclaredClasses() |
返回一个数组,数组中包含该类中所有类和接口类的对象 |
forName(String className) |
根据类名返回类的对象 |
getName() |
获得类的完整路径名字 |
newInstance() |
创建类的实例 |
getPackage() |
获得类的包 |
getSimpleName() |
获得类的名字 |
getSuperclass() |
获得当前类继承的父类的名字 |
getInterfaces() |
获得当前类实现的类或是接口 |
-
获得类中属性相关的方法
| 方法 | 用途 |
|---|---|
getField(String name) |
获得某个公有的属性对象 |
getFields() |
获得所有公有的属性对象 |
getDeclaredField(String name) |
获得某个属性对象 |
getDeclaredFields() |
获得所有属性对象 |
-
获得类中注解相关的方法
| 方法 | 用途 |
|---|---|
getAnnotation(Class<A> annotationClass) |
返回该类中与参数类型匹配的公有注解对象 |
getAnnotations() |
返回该类所有的公有注解对象 |
getDeclaredAnnotation(Class<A> annotationClass) |
返回该类中与参数类型匹配的所有注解对象 |
getDeclaredAnnotations() |
返回该类所有的注解对象 |
-
获得类中构造器相关的方法
| 方法 | 用途 |
|---|---|
getConstructor(Class...<?> parameterTypes) |
获得该类中与参数类型匹配的公有构造方法 |
getConstructors() |
获得该类的所有公有构造方法 |
getDeclaredConstructor(Class...<?> parameterTypes) |
获得该类中与参数类型匹配的构造方法 |
getDeclaredConstructors() |
获得该类所有构造方法 |
-
获得类中方法相关的方法
| 方法 | 用途 |
|---|---|
getMethod(String name, Class...<?> parameterTypes) |
获得该类某个公有的方法 |
getMethods() |
获得该类所有公有的方法 |
getDeclaredMethod(String name, Class...<?> parameterTypes) |
获得该类某个方法 |
getDeclaredMethods() |
获得该类所有方法 |
-
类中其他重要的方法
| 方法 | 用途 |
|---|---|
isAnnotation() |
如果是注解类型则返回true |
isAnnotationPresent(Class<? extends Annotation> annotationClass) |
如果是指定类型注解类型则返回true |
isAnonymousClass() |
如果是匿名类则返回true |
isArray() |
如果是一个数组类则返回true |
isEnum() |
如果是枚举类则返回true |
isInstance(Object obj) |
如果obj是该类的实例则返回true |
isInterface() |
如果是接口类则返回true |
isLocalClass() |
如果是局部类则返回true |
isMemberClass() |
如果是内部类则返回true |
Field类
Field 代表类的成员变量(成员变量也称为类的属性)。
| 方法 | 用途 |
|---|---|
equals(Object obj) |
属性与 obj 相等则返回true |
get(Object obj) |
获得obj 中对应的属性值 |
set(Object obj, Object value) |
设置 obj 中对应属性值 |
Method类
Method 代表类的方法。
| 方法 | 用途 |
|---|---|
invoke(Object obj, Object... args) |
传递object对象及参数调用该对象对应的方法 |
Constructor类
Constructor 代表类的构造方法。
| 方法 | 用途 |
|---|---|
newInstance(Object... initargs) |
根据传递的参数创建类的对象 |
利用反射机制实现 BeanUtils 工具
可以将一个对象的属性相同的值赋值给另一个对象
1 // 目标对象 Person1 2 /* 3 * Copyright (C) 2020 Baidu, Inc. All Rights Reserved. 4 */ 5 package 反射; 6 7 /** 8 * @author laoma 9 */ 10 public class Person1 { 11 12 public String name; 13 14 protected Integer age; 15 16 private Byte sex; 17 18 Boolean isMarriage1; 19 20 public Person1() { 21 } 22 23 public Person1(String name, Integer age, Byte sex, Boolean isMarriage) { 24 this.name = name; 25 this.age = age; 26 this.sex = sex; 27 this.isMarriage1 = isMarriage; 28 } 29 }
1 package 反射; 2 3 import java.lang.reflect.Field; 4 5 // 利用反射实现 BeanUtils 工具 6 public class BeanUtilsDemo { 7 /** 8 * @param originObj 原始对象 9 * @param targetObj 目标对象 10 * */ 11 public static void convertor(Object originObj, Object targetObj) throws Throwable { 12 // 利用反射 13 // 1. 首先获取类 14 Class originClazz = originObj.getClass(); 15 Class targetClazz = targetObj.getClass(); 16 17 // 2. 获取属性值 18 // 使用 getDeclaredFields 所有的属性值 19 Field[] originFields = originClazz.getDeclaredFields(); 20 Field[] targetFields = targetClazz.getDeclaredFields(); 21 22 // 3. 赋值 23 // 遍历赋值 24 // 通过属性名字来判断,如果属性名相同,那么进行赋值 25 for (Field originItem : originFields) { 26 for (Field targetItem : targetFields) { 27 // equals() 比较字符串中所包含的内容是否相同。 28 if (originItem.getName().equals(targetItem.getName())) { 29 // 这里把两个对象的 setAccessible 都打开,这样就不用担心类型问题 30 originItem.setAccessible(true); 31 targetItem.setAccessible(true); 32 // 使用 set() 设置属性值 33 targetItem.set(targetObj, originItem.get(originObj)); 34 } 35 } 36 } 37 } 38 39 public static void main(String[] args) throws Throwable { 40 // 创建一个对象 41 Person person = new Person("laomahh", 10, (byte) 1, false); 42 43 // 创建一个目标对象 44 Person1 person1 = new Person1(); 45 46 // 调用方法 47 BeanUtilsDemo.convertor(person, person1); 48 49 System.out.println("person == " + person); 50 System.out.println("person1 == " + person1); 51 System.out.println("person1.isMarriage1 == " + person1.isMarriage1); 52 } 53 }
“==” 比较变量的本事,即 两个对象在内存中的首地址
(Java 中,对象的首地址是它在内存中存放的起始地址,它后面的地址是用来存放它所包含的各个属性的地址,所以内存中会用多个内存块来存放对象的各个参数,而通过这个首地址就可以找到该对象,进而可以找到该对象的各个属性。)
“equals()” 方法比较字符串中所包含的内容是否相同
例如:
1 String s1,s2,s3 = "abc", s4 ="abc" ; 2 s1 = new String("abc"); 3 s2 = new String("abc"); 4 5 s1==s2 // 是 false 两个变量的内存地址不一样,也就是说它们指向的对象不 一样, 6 7 s1.equals(s2) // 是 true 两个变量的所包含的内容是abc,故相等。对于非字符串变量来说,"=="和"equals"方法的作用是相同的都是用来比较其,对象在堆内存的首地址,即用来比较两个引用变量是否指向同一个对象。

浙公网安备 33010602011771号