Java基础知识(19)- Java 反射机制 (一) | 反射机制简介、通过反射访问构造方法、通过反射访问成员变量
Java 反射机制 (Reflection)是 Java 语言的一个重要特性,在服务器程序和中间件程序中广泛运用。在服务器端,往往需要根据客户的请求,动态调用某一个对象的特定方法。
在 ORM 中间件的实现中,运用 Java 反射机制可以读取任意一个 JavaBean 的所有属性,或者给这些属性赋值。
1. 反射机制简介
1) 编译期和运行期
在了解 Java 反射机制前,先了解以下两个概念。
编译期:
是指把源码交给编译器编译成计算机可以执行的文件的过程。在 Java 中就是把 Java 代码编成 class 文件的过程。编译期只是做了一些翻译功能,并没有把代码放在内存中运行起来,而只是把代码当成文本进行操作,比如检查错误。
运行期:
是指把编译后的文件(.class)交给计算机执行,直到程序运行结束。所谓运行期就把在磁盘中的代码放到内存中执行起来。
2) 反射机制
反射机制是在运行期,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制。
简单来说,反射机制指的是程序在运行时能够获取自身的信息。一个类有多个组成部分,例如:成员变量、方法、构造方法等,反射就是加载类,并解剖出类的各个组成部分。
正常方式:
引入需要的包类名称 -> 通过 new 实例化 -> 取得实例化对象
反射方式:
实例化对象 -> getClass 方法 -> 得到完整的包类名称
3) 反射机制的特点
反射机制主要提供以下功能:
(1) 在运行时判断任意一个对象所属的类。
(2) 在运行时构造任意一个类的对象。
(3) 在运行时判断任意一个类所具有的成员变量和方法。
(4) 在运行时调用任意一个对象的方法。
(5) 生成动态代理。
反射机制优点:
(1) 能够运行时动态获取类的实例,大大提高系统的灵活性和扩展性。
(2) 与 Java 动态编译相结合,可以实现无比强大的功能。
(3) 对于 Java 这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。
反射机制缺点:
(1) 反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射;
(2) 反射调用方法时可以忽略权限检查,获取这个类的私有方法和属性,因此可能会破坏类的封装性而导致安全问题。
Java 反射机制在一般的 Java 应用开发中很少使用,即便是 Java EE 阶段也很少使用。
4) 反射机制的类
Java 反射机制的类位于 java.lang.reflect 包中,java.lang.Class 类是 Java 反射机制 API 中的核心类。
(1) java.lang.Class 类
java.lang.Class 类是实现反射的关键所在,Class 类的一个实例表示 Java 的一种数据类型,包括类、接口、枚举、注解(Annotation)、数组、基本数据类型和 void。
Class 类没有公有的构造方法,Class 实例是由 JVM 在类加载时自动创建的。
在程序代码中获得 Class 实例可以通过如下代码实现:
// 1. 通过类型class静态变量
Class clz1 = String.class;
String str = "Hello";
// 2. 通过对象的getClass()方法
Class clz2 = str.getClass();
每一种类型包括类和接口等,都有一个 class 静态变量可以获得 Class 实例。另外,每一个对象都有 getClass() 方法可以获得 Class 实例,该方法是由 Object 类提供的实例方法。
反射可访问的常用信息:
| 类型 | 访问方法 | 返回值类型 | 说明 |
| 包路径 | getPackage() | Package 对象 | 获取该类的存放路径 |
| 类名称 | getName() | String 对象 | 获取该类的名称 |
| 继承类 | getSuperclass() | Class 对象 | 获取该类继承的类 |
| 实现接口 | getlnterfaces() | Class 型数组 | 获取该类实现的所有接口 |
| 构造方法 |
getConstructors() getDeclaredContruectors() |
Constructor 型数组 Constructor 对象 |
获取所有权限为 public 的构造方法 获取当前对象的所有构造方法 |
| 方法 |
getMethods() getDeclaredMethods() |
Method 型数组 Method 对象 |
获取所有权限为 public 的方法 获取当前对象的所有方法 |
| 成员变量 |
getFields() getDeclareFileds() |
Field 型数组 Field 对象 |
获取所有权限为 public 的成员变量 获取当前对象的所有成员变量 |
| 内部类 |
getClasses() getDeclaredClasses() |
Class 型数组 Class 型数组 |
获取所有权限为 public 的内部类 获取所有内部类 |
| 内部类的声明类 | getDeclaringClass() | Class 对象 | 如果该类为内部类,则返回它的成员类,否则返回 null |
如上表所示,在调用 getFields() 和 getMethods() 方法时将会依次获取权限为 public 的字段和变量,然后将包含从超类中继承到的成员变量和方法。而通过 getDeclareFields() 和 getDeclareMethod() 只是获取在本类中定义的成员变量和方法。
(2) java.lang.reflect 包
包提供了反射中用到类,主要的类说明如下:
a) Constructor 类:提供类的构造方法信息。
b) Field 类:提供类或接口中成员变量信息。
c) Method 类:提供类或接口成员方法信息。
d) Array 类:提供了动态创建和访问 Java 数组的方法。
e) Modifier 类:提供类和成员访问修饰符信息。
2. 通过反射访问构造方法
为了能够动态获取对象构造方法的信息,首先需要通过下列方法之一创建一个 Constructor 类型的对象或者数组。
getConstructors()
getConstructor(Class<?>…parameterTypes)
getDeclaredConstructors()
getDeclaredConstructor(Class<?>...parameterTypes)
如果是访问指定的构造方法,需要根据该构造方法的入口参数的类型来访问。例如,访问一个入口参数类型依次为 int 和 String 类型的构造方法,下面的两种方式均可以实现。
objectClass.getDeclaredConstructor(int.class,String.class);
objectClass.getDeclaredConstructor(new Class[]{int.class,String.class});
创建的每个 Constructor 对象表示一个构造方法,然后利用 Constructor 对象的方法操作构造方法。
表1 Constructor类的常用方法
| 方法名称 | 说明 |
| isVarArgs() | 查看该构造方法是否允许带可变数量的参数,如果允许,返回 true,否则返回false |
| getParameterTypes() | 按照声明顺序以 Class 数组的形式获取该构造方法各个参数的类型 |
| getExceptionTypes() | 以 Class 数组的形式获取该构造方法可能抛出的异常类型 |
| newInstance(Object … initargs) | 通过该构造方法利用指定参数创建一个该类型的对象,如果未设置参数则表示采用默认无参的构造方法 |
| setAccessiable(boolean flag) | 如果该构造方法的权限为 private,默认为不允许通过反射利用 netlnstance() 方法创建对象。如果先执行该方法,并将入口参数设置为 true,则允许创建对象 |
| getModifiers() | 获得可以解析出该构造方法所采用修饰符的整数 |
通过 java.lang.reflect.Modifier 类可以解析出 getMocMers() 方法的返回值所表示的修饰符信息。在该类中提供了一系列用来解析的静态方法,既可以查看是否被指定的修饰符修饰,还可以字符串的形式获得所有修饰符。
表2 Modifier类的常用方法
| 静态方法名称 | 说明 |
| isStatic(int mod) | 如果使用 static 修饰符修饰则返回 true,否则返回 false |
| isPublic(int mod) | 如果使用 public 修饰符修饰则返回 true,否则返回 false |
| isProtected(int mod) | 如果使用 protected 修饰符修饰则返回 true,否则返回 false |
| isPrivate(int mod) | 如果使用 private 修饰符修饰则返回 true,否则返回 false |
| isFinal(int mod) | 如果使用 final 修饰符修饰则返回 true,否则返回 false |
| toString(int mod) | 以字符串形式返回所有修饰符 |
实例:
1 import java.lang.reflect.Constructor; 2 3 public class App { 4 public static void main( String[] args ) { 5 6 Class book = Book.class; 7 8 // 通过反射访问构造方法 9 Constructor[] declaredCon = book.getDeclaredConstructors(); 10 for (int i = 0; i < declaredCon.length; i++) { 11 12 Constructor con = declaredCon[i]; 13 14 System.out.println("构造方法名称 -> " + con.getName()); 15 16 System.out.print("参数类型 -> ( "); 17 Class[] parameterTypes = con.getParameterTypes(); 18 for (int j = 0; j < parameterTypes.length; j++) { 19 System.out.print(parameterTypes[j]); 20 if (j < parameterTypes.length-1) 21 System.out.print(", "); 22 } 23 System.out.print(" )\n"); 24 25 System.out.println("是否带可变数量参数 -> " + con.isVarArgs()); 26 27 /* 28 * 构造方法的访问权限为 private,会拋出异常; 29 * 或调用 setAccessible(true) 设置允许访问 private 成员 30 */ 31 //con.setAccessible(true); 32 33 try { 34 Book book2 = null; 35 36 if (parameterTypes.length == 0) { 37 // 通过执行默认构造方法 38 book2 = (Book) con.newInstance(); 39 } else if (parameterTypes.length == 2) { 40 // 通过执行带两个参数的构造方法 41 book2 = (Book) con.newInstance("Java Reflection", 101); 42 } else { 43 // 通过执行可变数量参数的构造方法 44 Object[] parameters = new Object[] { new String[] { "Java Reflection 2", "102" } }; 45 book2 = (Book) con.newInstance(parameters); 46 } 47 book2.print(); 48 49 } catch (Exception e) { 50 //e.printStackTrace(); 51 System.out.println("通过该构造方法实例化失败:error -> " + e.getMessage()); 52 } 53 54 System.out.println("----------------------------------------------"); 55 } 56 57 } 58 } 59 60 class Book { 61 String name; 62 int price; 63 64 private Book() { 65 } 66 67 protected Book(String name, int price) { 68 this.name = name; 69 this.price = price; 70 } 71 72 // 带可变参数的构造方法 73 public Book(String... args) throws NumberFormatException { 74 if (0 < args.length) 75 name = String.valueOf(args[0]); 76 if (1 < args.length) 77 price = Integer.valueOf(args[1]); 78 } 79 80 public void print() { 81 System.out.println("name: " + name + ", price: " + price); 82 } 83 }
输出:
构造方法名称 -> com.example.Book
参数类型 -> ( class [Ljava.lang.String; )
是否带可变数量参数 -> true
name: Java Reflection 2, price: 102
----------------------------------------------
构造方法名称 -> com.example.Book
参数类型 -> ( class java.lang.String, int )
是否带可变数量参数 -> false
name: Java Reflection, price: 101
----------------------------------------------
构造方法名称 -> com.example.Book
参数类型 -> ( )
是否带可变数量参数 -> false
通过该构造方法实例化失败:error -> Class com.example.ReflectionApp can not access a member of class com.example.Book with modifiers "private"
3. 通过反射访问成员变量
通过下列任意一个方法访问成员变量时将返回 Field 类型的对象或数组。
getFields()
getField(String name)
getDeclaredFields()
getDeclaredField(String name)
上述方法返回的 Field 对象代表一个成员变量。例如,要访问一个名称为 price 的成员变量,示例代码如下:
object.getDeciaredField("price");
Field 类的常用方法如表所示
| 方法名称 | 说明 |
| getName() | 获得该成员变量的名称 |
| getType() | 获取表示该成员变量的 Class 对象 |
| get(Object obj) | 获得指定对象 obj 中成员变量的值,返回值为 Object 类型 |
| set(Object obj, Object value) | 将指定对象 obj 中成员变量的值设置为 value |
| getlnt(0bject obj) | 获得指定对象 obj 中成员类型为 int 的成员变量的值 |
| setlnt(0bject obj, int i) | 将指定对象 obj 中成员变量的值设置为 i |
| setFloat(Object obj, float f) | 将指定对象 obj 中成员变量的值设置为 f |
| getBoolean(Object obj) | 获得指定对象 obj 中成员类型为 boolean 的成员变量的值 |
| setBoolean(Object obj, boolean b) | 将指定对象 obj 中成员变量的值设置为 b |
| getFloat(Object obj) | 获得指定对象 obj 中成员类型为 float 的成员变量的值 |
| setAccessible(boolean flag) | 此方法可以设置是否忽略权限直接访问 private 等私有权限的成员变量 |
| getModifiers() | 获得可以解析出该方法所采用修饰符的整数 |
实例:
1 import java.lang.reflect.Field; 2 3 public class App { 4 public static void main( String[] args ) { 5 6 // 通过反射访问成员变量 7 //BookVariable bookVariable = new BookVariable(); 8 //Class bookVariableClass = bookVariable.getClass(); 9 10 // 这里使用 Class 的 newInstance() 代替 new; 11 // Class 的 newInstance() 只能调用无参的构造函数。 12 Class bookVariableClass = null; 13 BookVariable bookVariable = null; 14 try { 15 bookVariableClass = Class.forName("com.example.BookVariable"); 16 bookVariable = (BookVariable) bookVariableClass.newInstance(); 17 } catch (Exception e) { 18 //e.printStackTrace(); 19 System.out.println("Create BookVariable instance failed: " + e.getMessage()); 20 return; 21 } 22 23 Field[] declaredFields = bookVariableClass.getDeclaredFields(); 24 25 // 遍历所有的成员 26 for (int i = 0;i < declaredFields.length;i++) { 27 28 Field field = declaredFields[i]; 29 30 System.out.println("变量名称 -> " + field.getName()); 31 Class fieldType = field.getType(); 32 System.out.println("变量类型 -> " + fieldType); 33 34 // 变量的访问权限为 private,会拋出异常; 35 // 或调用 setAccessible(true) 设置允许访问 private 成员 36 field.setAccessible(true); 37 38 try { 39 System.out.println("变量默认值 -> " + field.get(bookVariable)); 40 41 if(fieldType.equals(int.class)) { 42 field.setInt(bookVariable, 99); 43 System.out.println("setInt(99) = " + field.get(bookVariable)); 44 } else if(fieldType.equals(float.class)) { 45 field.setFloat(bookVariable, 21.02f); 46 System.out.println("setFloat(21.02f) = " + field.get(bookVariable)); 47 } else if(fieldType.equals(boolean.class)) { 48 field.setBoolean(bookVariable, true); 49 System.out.println("setBoolean(true) = " + field.get(bookVariable)); 50 } else { 51 field.set(bookVariable, "Java Variable"); 52 System.out.println("set('Java Variable') = " + field.get(bookVariable)); 53 } 54 } catch (Exception e) { 55 //e.printStackTrace(); 56 System.out.println("访问成员变量失败:error -> " + e.getMessage()); 57 } 58 59 System.out.println("----------------------------------------------"); 60 } 61 bookVariable.print(); 62 } 63 } 64 65 class BookVariable { 66 String name; 67 public int number; 68 private float price; 69 protected boolean isOpen; 70 71 public void print() { 72 System.out.println("name: " + name + ", number: " + number + ", price: " + price + ", isOpen: " + isOpen); 73 } 74 }
输出:
变量名称 -> name
变量类型 -> class java.lang.String
变量默认值 -> null
set('Java Variable') = Java Variable
----------------------------------------------
变量名称 -> number
变量类型 -> int
变量默认值 -> 0
setInt(99) = 99
----------------------------------------------
变量名称 -> price
变量类型 -> float
变量默认值 -> 0.0
setFloat(21.02f) = 21.02
----------------------------------------------
变量名称 -> isOpen
变量类型 -> boolean
变量默认值 -> false
setBoolean(true) = true
----------------------------------------------
name: Java Variable, number: 99, price: 21.02, isOpen: true
浙公网安备 33010602011771号