反射技术:其实就是动态加载一个指定的类,并获取该类中的所有的内容。而且将字节码文件封装成对象,
并将字节码文件中的内容都封装成对象,对于任意一个对象,都能够调用它的任意一个方法和属性。
简单说:反射技术可以对一个类进行解剖。
反射的好处:大大的增强了程序的扩展性。
反射的基本步骤:
1、获得Class对象,就是获取到指定的名称的字节码文件对象。
2、实例化对象,获得类的属性、方法或构造函数。
3、访问属性、调用方法、调用构造函数创建对象。
获取这个Class对象,有三种方式:
1):通过每个对象都具备的方法getClass来获取。
弊端:必须要创建该类对象,才可以调用getClass方法。
Person p = new Person();
Class clazz1 = p.getClass();// 获得对象具体的类型
2):每一个数据类型(基本数据类型和引用数据类型)都有一个静态的属性class。
弊端:必须要先明确该类。
Class clazz2 = Person.class;
前两种方式不利于程序的扩展,因为都需要在程序使用具体的类来完成。
3):使用的Class类中的方法,静态的forName方法。
String classname = "cn.itcast.reflect.Person";// 来自配置文件
Class clazz = Class.forName(classname);// 此对象代表Person.class
反射的用法:
1)需要获得java类的各个组成部分,首先需要获得类的Class对象,获得Class对象的三种方式:
obj.getClass() 用于获得对象的类型
类名.class
2)反射类的成员方法:
Class clazz = Person.class;
Method method = clazz.getMethod(methodName, new Class[]{paramClazz1, paramClazz2}); method.invoke();
3)反射类的构造函数:
Constructor con = clazz.getConstructor(new Class[]{paramClazz1, paramClazz2,...}) con.newInstance(params...)
4)反射类的属性:
Field field = clazz.getField(fieldName);
field.setAccessible(true); //对私有字段的访问取消权限检查。暴力访问
获取了字节码文件对象后,最终都需要创建指定类的对象:
创建对象的两种方式(其实就是对象在进行实例化时的初始化方式):
(1),调用空参数的构造函数:使用了Class类中的newInstance()方法。
(2),调用带参数的构造函数:先要获取指定参数列表的构造函数对象,然后通过该构造函数的对象的newInstance(实际参数) 进行对象的初始化。
综上所述,第二种方式,必须要先明确具体的构造函数的参数类型,不便于扩展。所以一般情况下,被反射的类,内部通常都会提供一个公有的空参数的构造函数。
// 如何生成获取到字节码文件对象的实例对象。
Class clazz = Class.forName("cn.itcast.bean.Person");//类加载
// 直接获得指定的类型
clazz = Person.class;
// 根据对象获得类型
Object obj = new Person("zhangsan", 19);
clazz = obj.getClass();
Object obj = clazz.newInstance();
//该实例化对象的方法调用就是指定类中的空参数构造函数,给创建对象进行初始化。当指定类中没
//有空参数构造函数时,该如何创建该类对象呢?请看method_2();
public static void method_2() throws Exception {
Class clazz = Class.forName("cn.itcast.bean.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);
}
}
反射指定类中的方法:
//获取类中所有的方法。
public static void method_1() throws Exception {
Class clazz = Class.forName("cn.itcast.bean.Person");
Method[] methods = clazz.getMethods();//获取的是该类中的公有方法和父类中的公有方法。
methods = clazz.getDeclaredMethods();//获取本类中的方法,包含私有方法。
for(Method method : methods) {
System.out.println(method);
}
}
//获取指定方法;
public static void method_2() throws Exception {
Class clazz = Class.forName("cn.itcast.bean.Person");
//获取指定名称的方法。
Method method = clazz.getMethod("show", int.class,String.class);
//想要运行指定方法,当然是方法对象最清楚,为了让方法运行,调用方法对象的invoke方法即可,但
//是方法运行必须要明确所属的对象和具体的实际参数。
Object obj = clazz.newInstance();
method.invoke(obj, 39,"hehehe");//执行一个方法
}
//想要运行私有方法。
public static void method_3() throws Exception {
Class clazz = Class.forName("cn.itcast.bean.Person");
//想要获取私有方法。必须用getDeclearMethod();
Method method = clazz.getDeclaredMethod("method", null);
// 私有方法不能直接访问,因为权限不够。非要访问,可以通过暴力的方式。
method.setAccessible(true);//一般很少用,因为私有就是隐藏起来,所以尽量不要访问。
}
//反射静态方法。
public static void method_4() throws Exception {
Class clazz = Class.forName("cn.itcast.bean.Person");
Method method = clazz.getMethod("function",null);
method.invoke(null,null);
}
public static void main(String[] args) throws Exception {
// 获取TestClass的Class对象
Class testClass = Class.forName(TestClass.class.getName());
// (1)使用Class对象的newInstance方法创建一个实例,这种方法用默认构造方法创建对象
TestClass objectA = (TestClass) testClass.newInstance();
System.out.println("Class的newInstance() 方法创建默认TestClass实例: "
+ objectA.toString());
// (2)使用构造方法创建实例。这就可以使用带参数的构造方法创建实例了
Constructor[] cons = testClass.getDeclaredConstructors();
System.out.println("testClass有 " + cons.length + " 个构造方法");
Constructor con = null;
for (int i = 0; i < cons.length; i++) {
con = cons[i];
// 默认构造函数
if (con.getParameterTypes().length == 0) {
// 调用Constructor的newInstance方法创建实例
objectA = (TestClass) con.newInstance(null);
System.out
.println("Constructor 的 newInstance() 方法创建默认TestClass实例: "
+ objectA.toString());
} else {
// 带参数的构造函数
objectA = (TestClass) con.newInstance(new Object[] {
new Integer(55), new Integer(88) });
System.out
.println("Constructor 的 newInstance() 方法创建带参数的TestClass实例: "
+ objectA.toString());
}
}
// 获取所有方法
Method[] methods = testClass.getMethods();
// 获取某个特定的无参数的方法
Method saddMethod1 = testClass.getMethod("sadd", null);
Method addMethod1 = testClass.getMethod("add", null);
// 获取某个特定的有参数的方法
Method saddMethod2 = testClass.getMethod("sadd", new Class[] {
int.class, int.class });
Method addMethod2 = testClass.getMethod("add", new Class[] { int.class,
int.class });
// 调用不带参数的静态方法
int result = ((Integer) saddMethod1.invoke(null, null)).intValue();
System.out.println("调用不带参数的静态方法sadd: " + result);
// 调用带参数的静态方法
result = ((Integer) saddMethod2.invoke(null, new Object[] {
new Integer(30), new Integer(70) })).intValue();
System.out.println("调用带参数30, 70的静态方法sadd: " + result);
objectA = (TestClass) testClass.newInstance();
// 调用不带参数的实例方法
result = ((Integer) addMethod1.invoke(objectA, null)).intValue();
System.out.println("调用不带参数的实例方法add: " + result);
// 调用带参数的实例方法
result = ((Integer) addMethod2.invoke(objectA, new Object[] {
new Integer(130), new Integer(170) })).intValue();
System.out.println("调用带参数130, 170的实例方法add: " + result);
//访问私有方法
Method sub = testClass.getDeclaredMethod("sub", null);
sub.setAccessible(true);
System.out.println(sub.invoke(objectA, null));
}
}
TestClass类的代码
//测试类
class TestClass {
// 两个静态属性
static final int sa = 100;
static final int sb = 50;
// 两个实例属性
int a;
int b;
// 默认构造方法
public TestClass() {
this.a = 5;
this.b = 10;
}
// 带参数的构造方法
public TestClass(int a, int b) {
this.a = a;
this.b = b;
}
// 静态方法,实现add功能
public static int sadd() {
return sa + sb;
}
public static int sadd(int a, int b) {
return a + b;
}
// 实例方法,实现add功能
public int add() {
return this.a + this.b;
}
public int add(int a, int b) {
return a + b;
}
public String toString() {
return "a = " + this.a + "; b = " + this.b;
}
// 私有方法
private String sub() {
return "=======================";
}
}