java反射机制
一、什么是反射(Reflect)?
Java反射说的是在运行状态
中,对于任何一个类,我们都能够知道这个类有哪些方法和属性。
对于任何一个对象,我们都能够对它的方法和属性进行调用。我们把这种动态获取对象信息和调用对象方法的功能称之为反射机制
。
- 想要使用反射机制,就必须要先获取到该类的字节码文件对象(.class),通过字节码文件对象,就能够通过该类中的方法获取到我们想要的所有信息(方法,属性,类名,父类名,实现的所有接口等等),每一个类对应着一个字节码文件也就对应着一个Class类型的对象,也就是字节码文件对象。
-
java中一切操作都是在类中,万事万物都是对象,类也是对象,是Class的对象(类是对象,类是
java.lang.Class
类的实例对象) -
假设一个class Demo{}
Demo的实例对象是Demo d1 = new Demo();
而Demo类也是Class的实例对象,3种表达方式:
1、Class c1 = Demo.class;
2、Class c2 = Demo.getClass();
c1 c2 表示Demo的类类型(Class type)
c1 c2 都代表了Demo类的类类型,一个类只可能是Class类的一个实例对象
c1 == c2 是 true
3、 Class c3 = null;
c3 = Class.forName("包名.类名");
//类的全路径名获取
- 获取到类后可以进行如下操作:可以通过类的类类型创建该类的对象实例
Demo d2 = (Demo)c1.newInstance();
//前提是Demo要有无参构造函数
Class.forName(“包名.类名”);
表示类的类类型,还代表了动态加载类
编译时刻
加载类是静态加载类,运行时刻
加载类是动态加载类
new创建对象是静态加载类,在编译时刻就需要加载所有可能的使用的类
动态加载类在实际的开发中使用更为灵活,因为开发者不能每次都获取到用户想要使用的功能,而动态加载类能根据使用者自己的需求动态的提供给使用者不同的类。
通过放射机制获取方法信息:
getMethods()
//获取所有的public的函数,包括父类继承而来
getDeclaredMethods()
获取所有该类自己声明的方法,不问访问权限是一个Method[]数组
[i].getReturnType()
//得到方法的返回值类型的类类型Class
m[i].getParameterTypes()
//得到参数列表的类型的类类型 Class[]
public static void printClassMessage(Object obj) {
Class class1 = obj.getClass();
System.out.println("类的名称:"+class1.getName());
Method[] ms = class1.getMethods();
//class1.getDeclaredMethods();
for(int i=0;i<ms.length;i++) {
Class returnType = ms[i].getReturnType();
System.out.print(returnType.getName()+" ");//方法返回类型
System.out.print(ms[i].getName()+"("); //方法名
Class[] parameters = ms[i].getParameterTypes();
for(Class c:parameters) {
System.out.print(c.getName()+",");
}
System.out.println(")");
}
}
获取成员变量信息:
Reflect.Field封装了关于成员变量的操作
getFields()//获取的是所有的public成员变量的信息
getDeclaredFields()//获取该类自己声明成员变量信息
public static void printClassFields(Object obj) {
System.out.println("--------------打印类的成员变量-----------");
Class class1 = obj.getClass();
System.out.println("类的名称:"+class1.getName());
Field[] fields = class1.getDeclaredFields();
for(Field fs : fields) {
Class fieldType = fs.getType();
String TypeName = fieldType.getName();
String fieldName = fs.getName();
System.out.println(TypeName+" "+fieldName);
}
}
获取构造函数信息:
public static void printClassConstruct(Object obj) {
Class class1 = obj.getClass();
//Constructor[] cs = class1.getConstructors();
Constructor[] constructors = class1.getDeclaredConstructors();
for(Constructor cs:constructors) {
System.out.print(cs.getName()+"(");
Class[] typePara = cs.getParameterTypes();
for(Class ty:typePara) {
System.out.print(ty.getName()+",");
}
System.out.println(")");
}
}
方法的反射:
操作:method.invoke(对象,参数列表)
获取方法 名称和参数列表来决定
getMethod是获取public的方法
getDeclaredMethod获取自己申明的方法
public class MethodDemo1 {
public static void main(String[] args) {
// TODO 自动生成的方法存根
A a = new A();
B b = new B();
Class aClass = a.getClass();
Class bClass = b.getClass();
try {
Method ma = aClass.getMethod("print", int.class,int.class);
//方法的反射操作
ma.invoke(a, 10,20);
Method mb = bClass.getDeclaredMethod("print", String.class,String.class);
mb.invoke(b, "Hello","World");
Method ma1 = aClass.getMethod("print");
ma1.invoke(a);
} catch (Exception e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
class A{
public void print() {
System.out.print("hello world");
}
public void print(int a,int b) {
System.out.print(a+b+"\n");
}
}
class B{
public void print(String a,String b) {
System.out.println(a+"---"+b);
}
}
集合泛型的本质
泛型防止错误输入,只在编译阶段有效。
反射的操作都是编译后的操作,
编译之后集合的泛型是去泛型化的,
通过方法的发射来操作,能绕过编译阶段。
public static void main(String[] args){
List<Integer> list1 = new ArrayList<>();
list1.add(1);//合法
//list1.add("Hello");//编译时不合法
try{
Class c = list1.getClass();
Method m = c.getMethod("add",Object.class);
m.invoke(list1,"Hello");//绕过编译的操作
System.out.print("长度为:"+list1.size());//结果为 2 ,成功绕过编译
}catch(Exception e){
e.printStackTrace();
}
}