反射技术
其实就是动态加载一个指定的类,并获取该类中的所有的内容。
而且将字节码文件封装成对象,并将字节码文件中的内容都封装成对象,它允许程序在运行时(注意不是编译的时候)来进行自我检查并且对内部的成员进行操作。
简单说:反射技术可以对一个类进行解剖。
反射的好处:大大的增强了程序的扩展性。
反射的基本步骤:
获得Class对象,就是获取到指定的名称的字节码文件对象。
实例化对象,获得类的属性、方法或构造函数。
访问属性、调用方法、调用构造函数创建对象。
类类型class Class
用于描述程序中的各个类属于同一类事物的Java类,它封装了类的很多信息。
查看JDK中的源码:
public final
class Class implements java.io.Serializable,
java.lang.reflect.GenericDeclaration,
java.lang.reflect.Type,
java.lang.reflect.AnnotatedElement {
private static final int ANNOTATION= 0x00002000;
private static final int ENUM = 0x00004000;
private static final int SYNTHETIC = 0x00001000;
private static native void registerNatives();
static {
registerNatives();
}
/*
* Constructor. Only the Java Virtual Machine creates Class
* objects.
*/
private Class() {}
发现:Class类有构造器,并且它的构造方法是private的(可能是为了禁止开发者去自己创建Class类的实例)。
看到注释我们知道,这个类是JVM来创建的。如果我们拿到一个类的类型信息,就可以利用反射获取其各种成员以及方法了。
那么我们怎么拿到一个类的信息呢?【获取Class对象可以有3种方式】
如果没有对象实例的时候,主要有两种办法可以获取类类型:
Class cls1 = Show.class;//每一个数据类型(基本数据类型和引用数据类型)都有一个静态的属性class。弊端:必须要先明确该类。
Class cls2 = Class.forName("Show");//【推荐这种方法】这种方式的扩展性最强,只要将类名的字符串传入即可。如果类是在某个包中,要带上包名。
public static void main(String[] args) throws Exception {
Class cls1 = Show.class;
Object obj1 = cls1.newInstance();
System.out.println(obj1);
Class> cls2 = Class.forName("Show");
Object obj2 = cls2.newInstance();
System.out.println(obj2);
}
这样就创建了一个对象,缺点是:
第一,我们只能利用默认构造函数,因为Class的newInstance是不接受参数的;
第二,如果类的构造函数是private的,比如Class,我们仍旧不能实例化其对象。
如果有对象实例的话,除了上面的两种方法来获取类的信息外,还有第三种方法:对象.getClass()。弊端:必须要创建该类对象,才可以调用getClass方法。
反射就是把Java类中的各种成分映射成相应的Java类:
例如,一个Java类用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示【就像汽车是一个类,汽车中的发动机,变速箱也是一个个的类】。表示Java类的Class类中提供了一系列的方法来获取其中的变量(Field),方法(Method),构造方法(Contructor),修饰符(Modifiers),包(Package)等信息。
反射的用法
1.需要获得java类的各个组成部分,首先需要获得类的Class对象,获得Class对象的三种方式:
Class.forName(classname) 用于做类加载
obj.getClass() 用于获得对象的类型
类名.class 用于获得指定的类型,传参用
2.反射类的成员方法:
public Method getMethod(String name,Class>... parameterTypes)//返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
public Method[] getMethods()//返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口的公共 member 方法。
3.反射类的构造函数:
public Constructor>[] getConstructors() 返回类中所有的public构造器集合,默认构造器的下标为0
public Constructor getConstructor(Class>... parameterTypes) 返回指定public构造器,参数为构造器参数类型集合
public Constructor>[] getDeclaredConstructors() 返回类中所有的构造器,包括私有
public Constructor getDeclaredConstructor(Class>... parameterTypes) 返回任意指定的构造器
发现带上Declared的都是获得所有的构造方法,包括私有,这下我们就可以调用原本不允许调用的私有构造器了^_^
eg:Class cls1 = Show.class;
// 获取所有的构造方法集合
Constructor[] con1 = cls1.getDeclaredConstructors();
con1[1].setAccessible(true);// 设置可访问的权限
Object obj1 = con1[1].newInstance(new Object[] { "adanac" });
System.out.println(obj1);
// 指定参数列表获取特定的方法
Constructor con = cls1.getDeclaredConstructor(new Class[] { String.class });
con.setAccessible(true);
Object obj2 = con.newInstance(new Object[] { "lfz" });
System.out.println(obj2);
4.获取类的成员变量:
public Field getDeclaredField(String name) 获取任意指定名字的成员
public Field[] getDeclaredFields() 获取所有的成员变量
public Field getField(String name) 获取任意public成员变量
public Field[] getFields() 获取所有的public成员变量
Class cls1 = Show.class;
// 获取所有的构造方法集合
Constructor[] con1 = cls1.getDeclaredConstructors();
con1[1].setAccessible(true);// 设置可访问的权限
Object obj1 = con1[1].newInstance(new Object[] { "adanac" });
Field nameField = cls1.getDeclaredField("name");
nameField.setAccessible(true);
System.out.println(nameField.get(obj1));// 打印结果:adanac
现在私有变量也可以访问到了哈~~
获取了字节码文件对象后,最终都需要创建指定类的对象,创建对象的两种方式(其实就是对象在进行实例化时的初始化方式):
调用空参数的构造函数:使用了Class类中的newInstance()方法。
调用带参数的构造函数:先要获取指定参数列表的构造函数对象,然后通过该构造函数的对象的newInstance(实际参数) 进行对象的初始化。
注意:第二种方式必须要先明确具体的构造函数的参数类型,不便于扩展。所以一般情况下,被反射的类,内部通常都会提供一个公有的空参数的构造函数。
Object obj = clazz.newInstance();//该实例化对象的方法调用就是指定类中的空参数构造函数,给创建对象进行初始化。
当指定类中没有空参数构造函数时,该如何创建该类对象呢?
Class> clazz = Class.forName("heimablog.Person");
// 既然类中没有空参数的构造函数,那么只有获取指定参数的构造函数,用该函数来进行实例化。
// 获取一个带参数的构造器。
Constructor> constructor = clazz.getConstructor(String.class, int.class);
// 想要对对象进行初始化,使用构造器的方法newInstance();
Object obj = constructor.newInstance("zhagnsan", 30);
// 获取所有构造器。
Constructor[] constructors = clazz.getConstructors();// 只包含公共的
constructors = clazz.getDeclaredConstructors();// 包含私有的
for (Constructor> con : constructors) {
System.out.println(con);
}
【案例】
通过class取得一个类的全部框架:
package heimablog;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
class hello {
public static void main(String[] args) {
Class> demo = null;
try {
demo = Class.forName("heimablog.Person");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("===============本类属性========================");
// 取得本类的全部属性
Field[] field = demo.getDeclaredFields();
for (int i = 0; i < field.length; i++) {
// 权限修饰符
int mo = field[i].getModifiers();//返回此类或接口以整数编码的 Java 语言修饰符。
String priv = Modifier.toString(mo);
// 属性类型
Class> type = field[i].getType();
System.out.println(priv + " " + type.getName() + " "
+ field[i].getName() + ";");
}
System.out.println("===============实现的接口或者父类的属性========================");
// 取得实现的接口或者父类的属性
Field[] filed1 = demo.getFields();
for (int j = 0; j < filed1.length; j++) {
// 权限修饰符
int mo = filed1[j].getModifiers();
String priv = Modifier.toString(mo);
// 属性类型
Class> type = filed1[j].getType();
System.out.println(priv + " " + type.getName() + " "
+ filed1[j].getName() + ";");
}
}
}
通过反射操作属性:
package heimablog;
import java.lang.reflect.Field;
class hello {
public static void main(String[] args) throws Exception {
Class> cls1 = Class.forName("heimablog.Person");
Object object = cls1.newInstance();
Field field = cls1.getDeclaredField("name");
field.setAccessible(true);
field.set(object, "男");
System.out.println(field.get(object));//打印结果:男
}
}
通过反射取得并修改数组的信息:
package heimablog;
import java.lang.reflect.Array;
class hello {
public static void main(String[] args) throws Exception {
int[] temp={1,2,3,4,5};
Class>demo=temp.getClass().getComponentType();
System.out.println("数组类型: "+demo.getName());
System.out.println("数组长度 "+Array.getLength(temp));
System.out.println("数组的第一个元素: "+Array.get(temp, 0));
Array.set(temp, 0, 100);
System.out.println("修改之后数组第一个元素为: "+Array.get(temp, 0));
}
}
/**
* 数组类型: int
*数组长度 5
*数组的第一个元素: 1
*修改之后数组第一个元素为: 100
*/
通过反射修改数组大小:
package heimablog;
import java.lang.reflect.Array;
class hello{
public static void main(String[] args) {
int[] temp={1,2,3,4,5,6,7,8,9};
int[] newTemp=(int[])arrayInc(temp,12);
print(newTemp);
System.out.println("^^^^^");
String[] atr={"a","b","c"};
String[] str1=(String[])arrayInc(atr,5);
print(str1);
}
/**
* 修改数组大小
* */
public static Object arrayInc(Object obj,int len){
Class>arr=obj.getClass().getComponentType();
Object newArr=Array.newInstance(arr, len);
int co=Array.getLength(obj);
System.arraycopy(obj, 0, newArr, 0, co);
return newArr;
}
/**
* 打印
* */
public static void print(Object obj){
Class>c=obj.getClass();
if(!c.isArray()){
return;
}
System.out.println("数组长度为: "+Array.getLength(obj));
for (int i = 0; i < Array.getLength(obj); i++) {
System.out.print(Array.get(obj, i)+" ");
}
}
}
/**
*数组长度为: 10
*1 2 3 4 5 6 7 8 9 0 ^^^^^
*数组长度为: 5
*a b c null null
*/
获得类加载器:
package heimablog;
class test{
}
class hello{
public static void main(String[] args) {
test t=new test();
System.out.println("类加载器 "+t.getClass().getClassLoader().getClass().getName());
}
}
//类加载器 sun.misc.Launcher$AppClassLoader
反射小例子:
package heimablog;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.sql.Timestamp;
import java.util.Calendar;
public class ClassTest {
public static void main(String[] args) {
Calendar birthday = Calendar.getInstance();
birthday.set(1966, 9, 11, 0, 0, 0);
Student s1 = new Student("007", "Jack Brawer", true, new Timestamp(
birthday.getTimeInMillis()), 1.80);
ClassInfo classInfo = new ClassInfo();
System.out.println(classInfo.getClassInfo(s1));
}
}
// 该类可以获取任何类的元数据信息
class ClassInfo {
public String getClassInfo(Object obj) {
StringBuffer result = new StringBuffer();
// 得到参数类变量的Class引用变量
Class cls = obj.getClass();
// 得到参数类变量的属性信息
Field[] fields = cls.getDeclaredFields();
// 得到参数类变量的完整类名(含有包的名称)
String fullName = cls.getName();
// 得到去除包名称的类名
String className = fullName.substring(fullName.lastIndexOf('.') + 1);
// 如果有包的定义,可以得到包的名称
int packagePosition = fullName.lastIndexOf('.');
String packageName = null;
if (packagePosition < 0)
packageName = "";
else
packageName = fullName.substring(0, fullName.lastIndexOf('.'));
// 输出包名和类名
result.append("包的名称为:" + packageName + "\r\n");
result.append("类的名称为:" + className + "\r\n");
// 输出类中所有的属性信息
for (Field field : fields) {
// 允许访问私有成员
field.setAccessible(true);
try {
// 输出私有属性信息
if (field.getModifiers() == Modifier.PRIVATE)
result.append("私有属性" + field.getName() + ":值为"
+ field.get(obj) + "\n");
// 输出受保护属性信息
if (field.getModifiers() == Modifier.PROTECTED)
result.append("受保护属性" + field.getName() + ":值为"
+ field.get(obj) + "\n");
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
return result.toString();
}
}
// 学生类
class Student {
// 学号
private String number = null;
// 姓名
private String name = null;
// 性别
private boolean sex = false;
// 生日
private Timestamp birthday = null;
// 身高
private double height = 0;
// 默认构造函数
public Student() {
}
// 该构造函数可以对学生的属性进行初始化
public Student(String number, String name, boolean sex, Timestamp birthday,
double height) {
setNumber(number);
setName(name);
setSex(sex);
setBirthday(birthday);
setHeight(height);
}
// 学号的读取函数
public String getNumber() {
return number;
}
// 学号的设置函数
public void setNumber(String number) {
this.number = number;
}
// 姓名的读取函数
public String getName() {
return name;
}
// 姓名的设置函数
public void setName(String name) {
this.name = name;
}
// 性别的读取函数
public boolean isSex() {
return sex;
}
// 性别的设置函数
public void setSex(boolean sex) {
this.sex = sex;
}
// 生日的读取函数
public Timestamp getBirthday() {
return birthday;
}
// 生日的设置函数
public void setBirthday(Timestamp birthday) {
this.birthday = birthday;
}
// 身高的读取函数
public double getHeight() {
return height;
}
// 身高的设置函数
public void setHeight(double height) {
this.height = height;
}
// 格式化字符信息输出
public String toString() {
return "学号:" + number + "/n姓名:" + name + "/n是否为男生:" + sex + "/n生日:"
+ birthday + "/n身高:" + height;
}
}
/*
包的名称为:heimablog
类的名称为:Student
私有属性number:值为007
私有属性name:值为Jack Brawer
私有属性sex:值为true
私有属性birthday:值为1966-10-11 00:00:00.267
私有属性height:值为1.8
*/
在java中有三种类类加载器:
Bootstrap ClassLoader 此加载器采用c++编写,一般开发中很少见。
Extension ClassLoader 用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类
AppClassLoader 加载classpath指定的类,是最常用的加载器。同时也是java中默认的加载器。