基本概念
一.反射概念
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
二.字节码
字节码(Byte-code)是一种包含执行程序、由一序列 op 代码/数据对组成的二进制文件。字节码是一种中间码,它比机器码更抽象。它经常被看作是包含一个执行程序的二进制文件,更像一个对象模型。字节码被这样叫是因为通常每个 opcode 是一字节长,但是指令码的长度是变化的。每个指令有从 0 到 255(或十六进制的: 00 到FF)的一字节操作码,被参数例如寄存器或内存地址跟随。
三.Class类
Class
类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class
对象。基本的 Java 类型(boolean
、byte
、char
、short
、int
、long
、float
和double
)和关键字 void
也表示为 Class
对象。
Class类是反射的基石。
四.Constructor
类
Constructor
提供关于类的单个构造方法的信息以及对它的访问权限。
五.Method
Method
提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。
六.Field
Field
提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。
实例及动态应用
一.如何获取字节码对应的实例对象
Class的赋值不能用new来赋值,因为这个值是存在于内存中的字节码.
Class的赋值方式有3种,见如下代码
<span style="font-family:SimSun;font-size:18px;">package test;
public class T1 {
public static void main(String[] args) throws Exception {
String str="java";
Class cls = String.class;
Class cls1 = str.getClass();
Class cls2 = Class.forName("java.lang.String");
System.out.println(cls);
System.out.println(cls1);
System.out.println(cls2);
}
}
//最后输出的结果都为:java.lang.String</span>
二.九个预定义Class实例对象
基本的 Java 类型(
boolean
、byte
、char
、short
、int
、long
、float
和double
)和关键字 void已经被预定义为卷
。
<pre name="code" class="java"><span style="font-family:SimSun;font-size:18px;">package test;
public class T1 {
public static void main(String[] args) throws Exception {
<span> </span>String str="java";</span>
<span style="font-family:SimSun;font-size:18px;">
<span> </span>Class cls = String.class;
<span> </span>Class cls1 = str.getClass();
<span> </span>Class cls2 = Class.forName("java.lang.String");</span>
<span style="font-family:SimSun;font-size:18px;">
<span> </span>System.out.println(cls);
<span> </span>System.out.println(cls.isPrimitive());
<span> </span>System.out.println(cls1);
<span> </span>System.out.println(cls2);
}
//输出结果为:</span><span style="font-family: SimSun; font-size: 18px;">java.lang.String</span><span style="font-family: SimSun; font-size: 18px;">,true,</span><span style="font-family: SimSun; font-size: 18px;">java.lang.String,</span><span style="font-family: SimSun; font-size: 18px;">java.lang.String</span>
补充一些可能用到的方法:boolean isPrimitive()
判定指定的 Class
对象是否表示一个基本类型。
boolean
isAnnotation()
如果此 Class 对象表示一个注释类型则返回 true。
boolean
isArray()
判定此 Class
对象是否表示一个数组类。
booleanisInterface()
判定指定的 Class
对象是否表示一个接口类型。。
三.构造方法的反射应用
<span style="font-family:SimSun;font-size:18px;">package test;
import java.lang.reflect.Constructor;
public class T1 {
@SuppressWarnings("unchecked")
public static void main(String[] args) throws Exception {
Class cls = String.class;
//通过getConstructors()获取所有的构造参数
Constructor constructors[] = cls.getConstructors();
//输出结果
for(Constructor constructor : constructors){
System.out.println(constructor);
}
//通过参数类型和个数来获得构造函数
Constructor constructor0 = cls.getConstructor(StringBuffer.class);
//输出结果
System.out.println(constructor0);
//一般方式和通过constructor来创建对象
String str1 = new String("abc");
String str = (String)constructor0.newInstance(new StringBuffer("abc"));
//输出下标为2的字符
System.out.println(str.charAt(2));
}
}
输出结果:public java.lang.String(byte[],int,int)
public java.lang.String(byte[],java.nio.charset.Charset)
public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException
public java.lang.String(byte[],int,int,java.nio.charset.Charset)
public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException
public java.lang.String(java.lang.StringBuilder)
public java.lang.String(java.lang.StringBuffer)
public java.lang.String(byte[])
public java.lang.String(int[],int,int)
public java.lang.String()
public java.lang.String(char[])
public java.lang.String(java.lang.String)
public java.lang.String(char[],int,int)
public java.lang.String(byte[],int)
public java.lang.String(byte[],int,int,int)
public java.lang.String(java.lang.StringBuffer)
c</span>
注意点:getConstructor()的参数可以是一个也可以是多个;
通过newInstance()创建实例对象时要用到强制类型转换,因为虚拟机在执行构造函数时才会知道函数的返回类型;
调用获得方法是要用到上面相同类型的实例对象;
四:成员变量的反射
<span style="font-family:SimSun;font-size:18px;">package test;
import java.lang.reflect.Field;
class Person{
private int x;
public int y;
public String str;
public String str1;
public String str2;
public static int z;
Person()
{
x = 3;
y = 4;
z = 5;
str = "abc";
str2 = "abcf";
str1 = "abce";
}
}
public class T1 {
public static void main(String[] args) throws Exception {
Person pe = new Person();
Class cls = pe.getClass();
//私有成员不可访问
// Field fieldx = cls.getField("x");
// System.out.println(fieldx);
//可以通过以下下方式访问
Field fieldx = cls.getDeclaredField("x");
fieldx.setAccessible(true);
System.out.println(fieldx.get(pe));
//获取y的值并输出
Field fieldy = cls.getField("y");
System.out.println(fieldy.get(pe));
//获取z的值并输出
Field fieldz = cls.getField("z");
System.out.println(fieldz.get(pe));
//获取str的值并输出
Field fieldstr = cls.getField("str");
System.out.println(fieldstr.get(pe));
changeStringValue(pe);
//输出省略
}
private static void changeStringValue(Object obj) throws Exception{
Field[] fields = obj.getClass().getFields();
for(Field field : fields){
if(field.getType() == String.class)
{
String oldValue = (String)field.get(obj);
String newValue = oldValue.replace('c', 'd');
field.set(obj, newValue);
}
}
}
}
</span>
注意点:私有成员的访问要更改权限才可以操作
五.成员方法的反射
package test;
import java.lang.reflect.Method;
public class CopyOfT1 {
public static void main(String[] args) throws Exception {
String str = new String("abbcd");
Method methodCharAt=String.class.getMethod("charAt", int.class);
System.out.println(methodCharAt.invoke(str, 3));
}
}
注意:JDK1.4和JDK1.5invoke方法的区别
JDK 1.4 public Objiect invoke(Objiect obj,Objiect... args[]);
JDK 1.5 public Objiect invoke(Objiect obj,Objiect[]args);