java的反射机制:
在运行状态中,对于任意一个类,都能知道这个类的所有属性和方法,
对于任意一个对象,都能调用它的任意一个方法和属性,
这种动态获取信息,以及动态调用对象的方法的功能称为java语言的反射机制
1.Class类的使用
在面向对象的世界中,万事万物都是对象
普通数据类型不是面向对象 比如int long
但是它们的包装类型 integer Long是对象
静态的成员 不是对象 是属于类的
什么是类
具有相同特性(数据元素)和行为(功能)的对象的抽象就是类。因此,对象的抽象是类,类的具体化就是对象,也可以说类的实例是对象,类实际上就是一种数据类型。
类是对象的模版
对象是类的实例
但是 类也是对象 类是java.lang.Class类的实例对象
比如:
Class Foo{}
Foo的对象 是 Foo f1=new Foo();
但是 Foo类 也是一个对象 它是Class类的对象
只有java的虚拟机 可以访问Class.java的构造方法 从而构造出任何Class
任何一个类都是Class的实例对象 这个实例对象有三种表示方式
第一种:
Class c1=Foo.class
//任何一个类都有一个隐含的静态成员变量
第二种:
Class c2=f1.getClass()
//已经该类的对象 通过getClass方法
// 官网的解释 c1 c2 此时表示的是Foo类的 类类型 (class type)
类也是对象 是Class类的实例对象
这个对象 我们称为该类的类类型
第三种
Class c3=Class.forName("Foo");
这里 c1==c2==c3 true
我们完全可以通过该类的类类型创建该类的对象实例
Class c1=Foo.class
Foo foo=(Foo)c1.newInstance(); //需要做强制类型转换
动态加载类
Class.forName("类的全称")
不仅表示了类的Class type 还代表了动态加载类
编译时刻加载类是静态加载
运行时刻加载类是动态加载
比如说
这样直接去进行编译 会报错,因为Word和Excel类均不存在
new 创建对象 是静态加载必须在编译期的时候就将所有类都加载进去
如果是动态加载类
那么在编译期是不会报错的
new 创建对象 是静态加载类 再编译时刻就
需要加载所有的可能使用到的类
比如 Foo foo=new Foo();
如果 Foo类不存在的话 是不能通过编译的
但是 动态加载类是在运行时刻进行加载
需要用到的时候才会去加载
比如
//运行时刻加载
Class c= Class.forName(args[0]);
//通过类类型 创建该类对象
Object oa=(Object)c.newInstance();
是可以在编译的时候通过,在运行的时候
输入参数args[0]去加载对象
这里如果是要加载Foo 那么Foo 实现 Object接口就行
先定义 interface Object
然后 foo implement Object
这样即可实现动态加载任何实现了Object接口的类 而不需要重新编译该程序
这样也可以实现热部署
因此功能性的类 尽可能的使用动态加载类 而不是静态加载类
这样方便重新升级的时候不用重新编译所有的代码
获取方法信息
Class c1=int.class //int的类类型
Class c2=String.class //String 的类类型
Class c3=void.class
拿到类类型之后
c1.getName(); //结果是 int
c2.getName() //结果是java.lang.String
c2.getsimpleName //结果是 String
//getName 基本数据类型直接拿到名字
// 类拿到是带包名的名字
Class类的基本API
获取方法的信息
//要获取类的信息 首先要获取类的类类型
Class c=obj.getClass();
//获取类的名称
c.getName();
//获取类的方法
//Method类 方法对象 所有的方法 都是这个类的对象
//一个成员方法 就是一个Method对象
//getMethods()方法 获取的事的所有public的函数 包括父类继承来的
//getDeclareMethods() 获取的是所有该类自己声明的方法 不论访问权限 父类继承来的没有
Method[] ms=c.getMethods(); //获取这个类里所有的public方法 存到一个method的数组中
ms[i].getReturnType();//获取方法返回值类型的Class type
//比如说返回值是String 我们拿到的是String.class
//所以返回值类型是 ms[i].getReturnType().getName()
ms[i].getName();//获取方法名字
//获取参数类型 得到的是参数列表的类型的Class type
Class[] paramTypes=ms[i].getParameterTypes();
paramTypes[i].getName();//拿到参数名字
获取成员变量的信息
成员变量也是对象
java.lang.reflect.Field 的对象
FieId类封装了关于成员变量的操作
getFieIds()方法获取的是所有的public的成员变量的信息
getDeclaredFields()获取的是该类自己声明的成员变量的信息
Field[] fs=c.getDeclareFields();
//得到成员变量的类类型
Class fieldType =fs[i].getType();
//得到成员变量的类型名字
String typeName=fieldType.getName();
//得到成员变量的名字
String fieldName=fs[i].getName();
获取构造函数信息
Class c=obj.getClass();
//构造函数也是对象
//是 java.lang.Constructor中封装了构造函数的信息
//getConstructors获取所有的public的构造函数
//getDeclaredConstructors 获取所有的构造方法 包括私有
Constructor[] cs=c.getDeclareConstructors();
cs[i].getName() //获取构造函数的名字
//获取构造函数的参数的类类型
Class[] paramTypes = cs[i].getParameterTypes();
paramTypes[i].getName()// 获取类型名字
2.方法的反射
1.如何获取某个方法
方法的名称和方法的参数列表才能唯一决定某个方法
方法的反射操作
method的对象有个invoker方法 ,这个方法通过参数列表确定某个方法
method.invoke(对象,参数列表)
public static void main(String[] args) {
//要获取print(int ,int) 方法
//要获取一个方法,就是要获取类的信息,获取类的信息,首先要获取类的类类型
A a1 = new A();
Class c = a1.getClass();
///获取方法:名称和参数列表来决定
//getMethod 获取的是public方法
//getDelcareMethod自己声明的方法
try {
Method m = c.getMethod("print", int.class, int.class);
//此时获取到了method的方法的对象
//方法的反射操作:是用Method对象来进行方法的调用操作
//方法如果没有返回值则返回null,如果有返回值 则返回具体的返回值
Object o = m.invoke(a1, 1, 2);
//等同于
a1.print(1, 2);
//通过反射调用方法的私有对象
//先获取声明的所有方法
Method m2 = c.getDeclaredMethod("print",int.class);
System.out.println("开始获取方法");
System.out.println(m2.getName());
System.out.println("参数列表为");
for (Class> aClass : m2.getParameterTypes()) {
System.out.println(aClass.getName());
}
System.out.println("调用私有方法");
// m2.invoke(a1, 1);
//会报异常
//java.lang.IllegalAccessException: Class classDemo.demo3.MethodDemo1 can not access a member of class
// classDemo.demo3.A with modifiers "private"
//但是手动将该方法改为公有的即可
m2.setAccessible(Boolean.TRUE);// 将私有访问限制改为public
m2.invoke(a1, 1);
} catch (Exception ex) {
ex.printStackTrace();
}
}
输出如下
泛型:
java集合种的泛型 是防止错误输入的,只有在编译阶段有效
绕过编译 ,就无效了
比如说 通过反射来操作,绕过编译
反射是在运行期起作用的
public class MethodDemo4 {
public static void main(String[] args) {
ArrayList list=new ArrayList();
ArrayList
list1.add("hello");
// list1.add(2); //直接放是错误的 会报错 因为编译时限制了类型是String
Class c1=list.getClass();
Class c2=list1.getClass();
System.out.println(c1==c2);
//c1==c2 编译之后集合的泛型是去泛型化的
try {
Method m=c2.getMethod("add",Object.class);
m.invoke(list1,2); //绕过泛型的限制 将2这个整型放进去了 ,说明在运行时候 去没有限制的
System.out.println(list1.size());
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
对象除了可以通过new创建,也可以反射创建,class.instant 或者反序列化创建