5.7.反射
能够分析反射能力的程序称为反射(reflective)。反射机制可以用来:
-
在运行时分析类的能力
-
运行时查看对象,例如,编一个toString方法供所有类是使用
-
实现通用的数组操作代码
-
利用Method对象,这个对象很像c++中的函数指针
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
静态编译和动态编译
静态编译:在编译时确定类型,绑定对象
动态编译:运行时确定类型,绑定对象
反射机制优缺点
优点: 运行期类型的判断,动态加载类,提高代码灵活度。
缺点: 性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的java代码要慢很多。
5.7.1 Class类
在程序运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类。虚拟机利用运行时类型信息选择相应的方法执行。
然而,可以通过专门的Java类访问这些信息。保存这些信息的类被称为Class,这个名字很容易让人混淆。Object类中的getClass()方法将返回一个Class类型的实例。
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Scanner;
/**
* 测试:
java.lang.Double
*/
public class ReflectionTest {
public static void main (String[] args) {
//从命令行或用户输入中读类名
String name;
if (args.length > 0) name = args[0];
else
{
Scanner in = new Scanner ( System.in );
System.out.println ("Enter class name(e.g.java.util.Date):");
name = in.next ();
}
try {
//打印类名和父类名(if != Object)
Class c1 = Class.forName ( name );
//返回Class对象
Class superc1 = c1.getSuperclass ();
String modifiers = Modifier.toString ( c1.getModifiers () );
if (modifiers.length () > 0)
System.out.println (modifiers + " ");
System.out.println ("class "+name);
if (superc1 != null && superc1 != Object.class)
System.out.println ("extends"+ superc1.getName ());
System.out.println ("\n{\n");
printConstructors(c1);
System.out.println ();
printMethods(c1);
System.out.println ();
printFields(c1);
System.out.println ("}");
} catch (ClassNotFoundException e) {
e.printStackTrace ();
}
System.exit ( 0 );
}
/**
* 打印所有域名类
* @param c1
*/
private static void printFields (Class c1) {
Field[] fields = c1.getDeclaredFields ();
for (Field f : fields){
Class type = f.getType ();
String name = f.getName ();
System.out.println (" ");
String modifiers = Modifier.toString ( f.getModifiers () );
if (modifiers.length () > 0)
System.out.println (modifiers + " ");
System.out.println (type.getName ()+ " " + name+ ";");
}
}
/**
* 打印一个类的所有方法
* @param c1
*/
private static void printMethods (Class c1) {
Method[] methods = c1.getDeclaredMethods ();
for (Method m : methods){
Class retType = m.getReturnType ();
String name = m.getName ();
System.out.println (" ");
//打印modifiers,返回类型和方法名
String modifiers = Modifier.toString ( m.getModifiers () );
if (modifiers.length () > 0)
System.out.println (modifiers + " ");
System.out.println (retType.getName ()+ " " + name+ "(");
Class[] paramTypes = m.getParameterTypes ();
for (int j=0; j < paramTypes.length; j++){
if (j>0)
System.out.println (", ");
System.out.println (paramTypes[j].getName ());
}
System.out.println (");");
}
}
/**
* 打印类构造器
* @param c1
*/
private static void printConstructors (Class c1) {
Constructor[] constructors = c1.getConstructors ();
for (Constructor c : constructors){
String name = c.getName ();
System.out.println (" ");
String modifiers = Modifier.toString ( c.getModifiers () );
if (modifiers.length () > 0){
System.out.println (modifiers+" ");
}
System.out.println (name + "(");
//打印Parameter 类型
Class[] paramTypes = c.getParameterTypes ();
for (int j=0; j < paramTypes.length; j++){
if (j>0)
System.out.println (", ");
System.out.println (paramTypes[j].getName ());
}
System.out.println (");");
}
}
}
测试用例代入的结果:
java.lang.Double
public final
class java.lang.Double
extendsjava.lang.Number
{
public
java.lang.Double(
double
);
public
java.lang.Double(
java.lang.String
);
public
boolean equals(
java.lang.Object
);
public static
java.lang.String toString(
double
);
public
java.lang.String toString(
);
public
int hashCode(
);
public static
int hashCode(
double
);
public static
double min(
double
,
double
);
public static
double max(
double
,
double
);
public static native
long doubleToRawLongBits(
double
);
public static
long doubleToLongBits(
double
);
public static native
double longBitsToDouble(
long
);
public volatile
int compareTo(
java.lang.Object
);
public
int compareTo(
java.lang.Double
);
public
byte byteValue(
);
public
short shortValue(
);
public
int intValue(
);
public
long longValue(
);
public
float floatValue(
);
public
double doubleValue(
);
public static
java.lang.Double valueOf(
java.lang.String
);
public static
java.lang.Double valueOf(
double
);
public static
java.lang.String toHexString(
double
);
public static
int compare(
double
,
double
);
public static
boolean isNaN(
double
);
public
boolean isNaN(
);
public static
boolean isFinite(
double
);
public static
boolean isInfinite(
double
);
public
boolean isInfinite(
);
public static
double sum(
double
,
double
);
public static
double parseDouble(
java.lang.String
);
public static final
double POSITIVE_INFINITY;
public static final
double NEGATIVE_INFINITY;
public static final
double NaN;
public static final
double MAX_VALUE;
public static final
double MIN_NORMAL;
public static final
double MIN_VALUE;
public static final
int MAX_EXPONENT;
public static final
int MIN_EXPONENT;
public static final
int SIZE;
public static final
int BYTES;
public static final
java.lang.Class TYPE;
private final
double value;
private static final
long serialVersionUID;
}
5.7.2 利用反射分析类的能力
反射机制最重要的内容------------检查类的结构
在java.lang.reflect包中有三个类Field、Method和Constructor分别用于描述类的域、方法和构造器。这三个类都有个叫做getName的方法,用来返回描述域所属类型的Class对象。Method和Constructor类能够报告参数类型的方法,Method类还有个可以报告返回类型的方法。这三个类还有个交getModifiers的方法,它将返回一个整形数值,用不同的位开关描述public和static这样修饰符使用状况。另外,可使用Modifier类的静态方法分析getModifiers返回的整形数值。可调用Modifier.toString方法将修饰符打印出来。
Class类中的getFields、getMethods和getConstructors方法分别返回类提供的public域、方法和构造器数组,其中包括超类的公有成员。Class类的getDeclareFields、getDeclareMethods和getDeclaredConstructor是方法分别返回类中声明的全部域、方法和构造器,其中包括私有和受保护成员,但不包括超类的成员。
5.7.3运行时使用反射机制分析对象
查看数据域的实际内容
反射机制默认行为受限于Java的访问控制。然而,如果一个Java程序没有受到安全管理器的控制,就可以覆盖访问控制。为了达到这个目的,需要调用Field、Method或Constructor对象的setAccessible方法。
5.7.4反射案例
package reflection;
import java.lang.reflect.*;
import java.util.Date;
//import java.util.Date;
public class MainClass {
public static void main (String[] args) throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, InstantiationException {
String classname = "java.util.Date";
//加载类java.util.Date并进行实例化
/**
* forName方法的工作
* 1.根据类名到类路径中查找有没有这个类的.class文件
* 2.如果找到类路径中找到这个字节码文件,则会经过检查加载到虚拟机中
* 3.在扫描检查的过程中,虚拟机还会得到这个字节码中定义的内容
* (属性,方法等信息),则forName方法会实例化一个Class对象,把检查到的
* 字节码信息存入Class对象
* 4.通过Class对象,可以通过一种比较友好的方式,得到字节码中的信息
*/
Class c = Class.forName ( classname );
System.out.println (c.getName ());
// 字段集合
Field[] fs = c.getFields ();//只能拿到公有字段(属性)
for (Field f: fs){
System.out.println (f.getName ()+":"+f.getType ());
}
Field[] fs1 = c.getDeclaredFields ();//拿到公,私有字段(属性)
for (Field f: fs1){
System.out.println (f.getName ()+":"+f.getType ());
}
Constructor[] cs = c.getConstructors ();
for (Constructor cc : cs ){
System.out.println (cc.getParameterCount ()+":"+cc.getName ());
}
//硬编码 前期绑定
// Date date = new Date ( );
//后期绑定(通过反射得到的对象进行实例化)
Object o = cs[2].newInstance ( );
System.out.println (o);
Method[] methods = c.getDeclaredMethods ();
for (Method m : methods){
System.out.println (m.getModifiers ()+":"
+m.getName ()+":"
+m.getReturnType().getName ()
+m.getParameters ());
if ("setYear".equals ( m.getName () )){
m.invoke ( o,2000 );//改变年份的值
System.out.println ("定义时间:"+ o);
}
Parameter[] ps = m.getParameters ();
for (Parameter p : ps){
System.out.println ("***"+p.getName ()+":"+p.getType ().getName ());
}
}
}
}
运行结果:
java.util.Date
gcal:class sun.util.calendar.BaseCalendar
jcal:class sun.util.calendar.BaseCalendar
fastTime:long
cdate:class sun.util.calendar.BaseCalendar$Date
defaultCenturyStart:int
serialVersionUID:long
wtb:class [Ljava.lang.String;
ttb:class [I
6:java.util.Date
1:java.util.Date
0:java.util.Date
1:java.util.Date
3:java.util.Date
5:java.util.Date
Sun Aug 25 16:20:42 CST 2019
1:after:boolean[Ljava.lang.reflect.Parameter;@45ee12a7
***arg0:java.util.Date
1:before:boolean[Ljava.lang.reflect.Parameter;@330bedb4
***arg0:java.util.Date
1:equals:boolean[Ljava.lang.reflect.Parameter;@2503dbd3
***arg0:java.lang.Object
1:toString:java.lang.String[Ljava.lang.reflect.Parameter;@4b67cf4d
1:hashCode:int[Ljava.lang.reflect.Parameter;@7ea987ac
1:clone:java.lang.Object[Ljava.lang.reflect.Parameter;@12a3a380
1:compareTo:int[Ljava.lang.reflect.Parameter;@29453f44
***arg0:java.util.Date
4161:compareTo:int[Ljava.lang.reflect.Parameter;@5cad8086
***arg0:java.lang.Object
2:readObject:void[Ljava.lang.reflect.Parameter;@6e0be858
***arg0:java.io.ObjectInputStream
2:writeObject:void[Ljava.lang.reflect.Parameter;@61bbe9ba
***arg0:java.io.ObjectOutputStream
18:normalize:sun.util.calendar.BaseCalendar$Date[Ljava.lang.reflect.Parameter;@610455d6
***arg0:sun.util.calendar.BaseCalendar$Date
18:normalize:sun.util.calendar.BaseCalendar$Date[Ljava.lang.reflect.Parameter;@511d50c0
9:parse:long[Ljava.lang.reflect.Parameter;@60e53b93
***arg0:java.lang.String
9:from:java.util.Date[Ljava.lang.reflect.Parameter;@5e2de80c
***arg0:java.time.Instant
1:getTime:long[Ljava.lang.reflect.Parameter;@1d44bcfa
1:setTime:void[Ljava.lang.reflect.Parameter;@266474c2
***arg0:long
1:getDate:int[Ljava.lang.reflect.Parameter;@6f94fa3e
1:setYear:void[Ljava.lang.reflect.Parameter;@5e481248
定义时间:Sat Aug 25 16:20:42 CST 3900
***arg0:int
9:UTC:long[Ljava.lang.reflect.Parameter;@66d3c617
***arg0:int
***arg1:int
***arg2:int
***arg3:int
***arg4:int
***arg5:int
26:convertToAbbr:java.lang.StringBuilder[Ljava.lang.reflect.Parameter;@63947c6b
***arg0:java.lang.StringBuilder
***arg1:java.lang.String
18:getCalendarDate:sun.util.calendar.BaseCalendar$Date[Ljava.lang.reflect.Parameter;@2b193f2d
26:getCalendarSystem:sun.util.calendar.BaseCalendar[Ljava.lang.reflect.Parameter;@355da254
***arg0:sun.util.calendar.BaseCalendar$Date
26:getCalendarSystem:sun.util.calendar.BaseCalendar[Ljava.lang.reflect.Parameter;@4dc63996
***arg0:int
26:getCalendarSystem:sun.util.calendar.BaseCalendar[Ljava.lang.reflect.Parameter;@d716361
***arg0:long
1:getDay:int[Ljava.lang.reflect.Parameter;@6ff3c5b5
1:getHours:int[Ljava.lang.reflect.Parameter;@3764951d
58:getJulianCalendar:sun.util.calendar.BaseCalendar[Ljava.lang.reflect.Parameter;@4b1210ee
24:getMillisOf:long[Ljava.lang.reflect.Parameter;@4d7e1886
***arg0:java.util.Date
1:getMinutes:int[Ljava.lang.reflect.Parameter;@3cd1a2f1
1:getMonth:int[Ljava.lang.reflect.Parameter;@2f0e140b
1:getSeconds:int[Ljava.lang.reflect.Parameter;@7440e464
18:getTimeImpl:long[Ljava.lang.reflect.Parameter;@49476842
1:getTimezoneOffset:int[Ljava.lang.reflect.Parameter;@78308db1
1:getYear:int[Ljava.lang.reflect.Parameter;@27c170f0
1:setDate:void[Ljava.lang.reflect.Parameter;@5451c3a8
***arg0:int
1:setHours:void[Ljava.lang.reflect.Parameter;@2626b418
***arg0:int
1:setMinutes:void[Ljava.lang.reflect.Parameter;@5a07e868
***arg0:int
1:setMonth:void[Ljava.lang.reflect.Parameter;@76ed5528
***arg0:int
1:setSeconds:void[Ljava.lang.reflect.Parameter;@2c7b84de
***arg0:int
1:toGMTString:java.lang.String[Ljava.lang.reflect.Parameter;@3fee733d
1:toInstant:java.time.Instant[Ljava.lang.reflect.Parameter;@5acf9800
1:toLocaleString:java.lang.String[Ljava.lang.reflect.Parameter;@4617c264
反射机制的应用场景有哪些?
反射是框架设计的灵魂。
在我们平时的项目开发过程中,基本上很少会直接使用到反射机制,但这不能说明反射机制没有用,实际上有很多设计、开发都与反射机制有关,例如模块化的开发,通过反射去调用对应的字节码;动态代理设计模式也采用了反射机制,还有我们日常使用的 Spring/Hibernate 等框架也大量使用到了反射机制。
举例:①我们在使用JDBC连接数据库时使用Class.forName()通过反射加载数据库的驱动程序;②Spring框架也用到很多反射机制,最经典的就是xml的配置模式。Spring 通过 XML 配置模式装载 Bean 的过程:1) 将程序内所有 XML 或 Properties 配置文件加载入内存中; 2)Java类里面解析xml或properties里面的内容,得到对应实体类的字节码字符串以及相关的属性信息; 3)使用反射机制,根据这个字符串获得某个类的Class实例; 4)动态配置实例的属性
Java获取反射的三种方法
1.通过new对象实现反射机制 2.通过路径实现反射机制 3.通过类名实现反射机制
public class Get {
//获取反射机制三种方式
public static void main(String[] args) throws ClassNotFoundException {
//方式一(通过建立对象)
Student stu = new Student();
Class classobj1 = stu.getClass();
System.out.println(classobj1.getName());
//方式二(所在通过路径-相对路径)
Class classobj2 = Class.forName("fanshe.Student");
System.out.println(classobj2.getName());
//方式三(通过类名)
Class classobj3 = Student.class;
System.out.println(classobj3.getName());
}
}
本文深入探讨Java反射机制,包括其工作原理、应用场景及如何利用反射分析类和对象。通过示例代码展示了反射在动态加载类、获取类信息、实例化对象等方面的应用。
1361

被折叠的 条评论
为什么被折叠?



