java编程之ReFlect(反射)
- Class类的反射
- 方法的反射
- 成员变量的反射
- 构造函数的反射
java类加载机制
Class类的反射
Class类
在面向对象的世界里,万事万物都是对象。
java语言中,静态的成员,普通的数据类型也是对象,类的实例就是类的对象。
那么类是谁的对象呢?
类是对象,类是java.lang.Class类的实力对象。
There is a class named Class
package com.imooc.ReFlectTest;
/**
*这段代码介绍了关于Class类的实例的三种创建方法
*/
public class ClassDemo1 {
public static void main(String[] args) {
//创建一个Dog类的实例对象
Dog dog = new Dog();
//Dog类也是实例对象,Dog类是Class类的实例对象
//Class类只有private权限的构造函数,只允许jvm调用!
//我Class类的实例对象,一共有3种表示方式
//第一种每个类都有一个隐含的static(静态)的成员class,可以直接调用
Class c1 = Dog.class;
//第二种,已知一个类的实例对象,可以调用getClass()方法
Class c2 = dog.getClass();
/**
* 官方上给的解释,此处的c1,c2是Dog类的(class type(类类型))
*/
//结果是true,说明c1,c2都代表了Dog类的类类型,也说明了一个类只有一个Class类的一个实例对象
System.out.println(c1 == c2);
//第三种方式,调用Class类的forName方法,会有异常
Class c3 = null;
try {
//方法中写入类的全称
c3 = Class.forName("com.imooc.ReFlectTest.Dog");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//此处依然是true,还是那句话一个类只有一个Class类的实例对象
System.out.println(c2 == c3);
/**
* 我们也可以通过类的类类型来创建该类的实例
* 即我们可以用c1 or c2 or c3 来创建一个Dog类的对象
*/
try {
//类类型创建的类的实例是Object类型的,此处因为没有用泛型,所以我们需要进行强制类型转换
//前提是该类要有无参数的构造方法
Dog dog1 = (Dog)c1.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
class Dog{}
Class.forName(“类的全称”)
- 不仅表示类的类类型,还表示了动态加载类
- 区分编译和运行
- 编译时加载的类是静态加载类,运行时加载的类是动态加载类
使用反射访问类的所有信息
代码中的注释已经详细解释了他们的作用
package com.imooc.ReFlectTest;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Classutil {
/**
* 打印类的信息,包括类的成员函数的信息
* obj 即类的对象,此处没用泛型,所以用多态来实现
*/
public static void printClassMassage(Object obj) {
//获取实际传递的obj的类类型
Class c = obj.getClass();//存在多态,实际调用时,调用的是传递的类的方法
//获取类的名字
System.out.println(c.getName());
/**
* Method方法,用来获取类的方法名
* 一个方法就是一个Method对象
* getMethods()方法获取的是所有的public的函数,包括从父类继承来的
* getDeclaredMethods()方法获取类自己声明的所有方法,不问访问权限
*/
Method[] methods = c.getMethods();//c.getDeclaredMethods();
for(int i=0; i<methods.length; i++) {
//输出方法的名字
System.out.print(methods[i].getName() + "(");
//通过方法,获取方法的参数列表
//getParameterType()方法,获取参数列表的类型的类类型
Class[] parameterType = methods[i].getParameterTypes();
//依次打印方法的参数列表
for (Class class1 : parameterType) {
System.out.print(class1.getName() + ",");
}
System.out.println(")");
}
}
public static void printFieldMessage(Object obj) {
Class c = obj.getClass();
/**
* 获取类的成员变量的信息
* 用java.lang.reflect.Field
* Field封装了关于类的成员变量的所有操作
* getFileds()方法获取所有权限为public的成员变量的信息
* getDeclaredFileds()方法获取的是该类自己声明的变量的信息
*/
Field[] fileds = c.getDeclaredFields();
for (Field field : fileds) {
//得到成员变量的类型的类类型
Class fieldType = field.getType();
//typeName,成员变量的名字,即变量名
String typeName = fieldType.getName();
//fieldName,变量的类类型的名字,即变量的类型名(例:int,String)
String fieldName = field.getName();
System.out.println("typeName = " + typeName + " " +"fieldName = "+ fieldName);
}
}
public static void printConMessage(Object obj) {
Class c = obj.getClass();
//获取所有的public的构造方法
//Constructor[] cs = c.getConstructors();
//获取当前类内的所有的构造方法Constructor
Constructor[] cs = c.getDeclaredConstructors();
for (Constructor constructor : cs) {
System.out.print(constructor.getName() + "(");
Class[] parameter = constructor.getParameterTypes();
for (Class class1 : parameter) {
System.out.print(class1.getName() + ",");
}
System.out.println(")");
}
}
}
通过上述代码的方法,我们可以访问任意一个类的名称以及它的所有方法的名称,参数列表以及返回值,包括构造函数!
当然除了上述的三种访问以外,reflect中还封装了很多很好的方法,我们可以在需要的时候,去查阅java.lang,Class的API
通过获取的类信息,可以做反射操作
我们通过获取的类的信息,可以方法调用对象
package com.imooc.ReFlectTest;
import java.lang.reflect.Method;
public class ClassDemo5 {
public static void main(String[] args) {
A a1 = new A();
//获取方法的信息的前提是获取类的信息,获取类的信息要先获取类的类类型
Class c = a1.getClass();
try {
/**
* 获取方法,由名称和参数列表来决定
* getMethod()获取public的方法
* getDeclaredMethod()获取自己声明的方法
*/
Method m = c.getMethod("print", int.class, int.class);
//方法的反射调用,利用m对象进行方法的调用invoke()方法,和a1.print()的效果完全相同
//invoke(obj, new Object[])
//方法无返回值,则返回null,否则返回方法的返回值
Object o = m.invoke(a1, new Object[]{10,20});
// Object o = m.invoke(a1, 10,20);
System.out.println("================================");
Method m1 = c.getMethod("printStr", String.class, String.class);
//用方法进行反射操作
// a1.printStr("Hello", "World");
m1.invoke(a1, "hello", "World");
System.out.println("=================================");
//方法没有参数
//方法1 ,传一个空的类类型数组
// Method m2 = c.getMethod("print", new Class[]{});
//方法2,不穿参数也可以
Method m2 = c.getMethod("print");
// m2.invoke(a1, new Object[]{});
m2.invoke(a1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
class A {
public void print(int a, int b) {
System.out.println(a + b);
}
public void printStr(String a, String b) {
System.out.println(a.toUpperCase() + "," + b.toUpperCase());
}
}
通过反射我们可以理解集合泛型的本质
泛型的目的是为了防止输入错误,但只在编译时有效。
package com.imooc.ReFlectTest;
import java.awt.event.FocusEvent;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class ClassDemo6 {
public static void main(String[] args) {
List l1 = new ArrayList();
List<String> l2 = new ArrayList<String>();
Class c1 = l1.getClass();
Class c2 = l2.getClass();
//结果为true
System.out.println(c1 == c2);
l2.add("hello");
//String类型的List不能添加int型的数,编译时无法通过
// l2.add(20);
/**
* 反射的操作都是编译之后的操作
* c1 == c2 返回值为true说明编译之后的集合是“去泛型化”的
* java集合中的泛型,是防止错误输入的,只在编译阶段有效
* 当我们绕过编译时,泛型就无效了
* 验证:通过方法的反射操作,绕过编译
*/
try {
Method m = c2.getMethod("add", Object.class);
m.invoke(l2, 20);
System.out.println(l2.size());
System.out.println(l2);
/*
* 不能使用foreach循环,会出异常
* for (String string : l2) {
* System.out.println(string);
* }
*/
} catch (Exception e) {
e.printStackTrace();
}
}
}