内容
- 获取源头Class
- 构造器
- 实例化对象(重点)
- 接口与父类
- 修饰符
- 属性
- 方法
- 数组
- 类加载器
- 反射相关操作(重点)
“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”,如Python,
Ruby是动态语言;显然C++,Java,C#不是动态语言,但是JAVA有着一个非常突出
的动态相关机制:Reflection。
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方
法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以
及动态调用对象的方法的功能称为java语言的反射机制。如
/** * 入门级示例:通过对象获取 包名.类名 * @author Administrator */ public class Simple { public static void main(String[] args) { Simple s=new Simple(); System.out.println(s.getClass().getName()); } } |
Java反射机制,可以实现以下功能:
①在运行时判断任意一个对象所属的类;
②在运行时构造任意一个类的对象;
③在运行时判断任意一个类所具有的成员变量和方法;
④在运行时调用任意一个对象的方法;
⑤生成动态代理。
相关的api为
一、 获取源头Class(重点)
打开权限:
add.setAccessible(true);
所有类的对象其实都是Class的实例。这个Class实例可以理解为类的模子,就是包含了类的结构信息,类似于图纸。我们日常生活中,需要创造一个产品,如想山寨一个iphone手机,怎么办?
有三种方式可以实现:
⑴买个iphone手机,拆的七零八落的,开始山寨;
⑵到iphone工厂参观,拿到iphone磨具,开始山寨;
⑶跑到美国盗取iphone的图纸,开始山寨,最后一种最暴力,最爽。
序列化:实现serializable接口,
反序列化
克隆:实现cloneable接口,重写clone()方法,修改权限为public
New 反射
同理,获取类的class对象,也有三种方式:
①Class.forName(”包名.类名”)//一般尽量采用该形式
②类.class
③对象.getClass()
示例如下:
public class Source { public static void main(String[] args) { //第一种方式:对象.class Source s=new Source(); Class<?>c1=s.getClass(); //第二种方式:类.class Class<?>c2=Source.class; //第三种方式(推荐方式):Class.forName() Class<?>c3=null; try { c3=Class.forName("com.shsxt.ref.simple.Source"); } catch (ClassNotFoundException e) { e.printStackTrace(); } System.out.println(c1.getName()); System.out.println(c2.getName()); System.out.println(c3.getName()); } } |
有了class对象,我们就有了一切,这就是反射的源头,接下来就是“庖丁解牛”。
二、 构造器
根据Class对象,我们可以获得构造器,为实例化对象做准备。调用以下api即可
public class GetConstructor { public static void main(String[] args) { try { Class<?>clz=Class.forName("com.shsxt.ref.simple.User"); //1、获取所有的public 权限的构造器 Constructor<?>[]con=clz.getConstructors(); //注意查看构造器的顺序 for(Constructor<?> c:con){ System.out.println(c); } //2、获取所有的构造器 con=clz.getDeclaredConstructors(); System.out.println("--------------"); for(Constructor<?> c:con){ System.out.println(c); } System.out.println("----------------"); //3、获取指定的构造器(放入具体的类型) Constructor<?> c=clz.getConstructor(String.class); System.out.println(c); //非public权限 System.out.println("----------------"); c=clz.getDeclaredConstructor(String.class,String.class); System.out.println(c); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } |
三、 实例化对象(重点)
之前我们讲解过创建对象的方式,有new 、克隆、反序列化,再加一种,根据Class对象,使用newInstance() 或者构造器实例化对象。调用以下api即可
//获取源头 Class<?> clz = Class.forName("com.shsxt.ref.simple.User"); //第一种:通过newInstance()创建对象 User user=(User)clz.newInstance(); user.setUname("sxt"); user.setUpwd("good"); //第二种:通过getDeclaredConstructors()创建对象,取得全部构造函数(注意顺序) Constructor<?>[] cons=clz.getDeclaredConstructors(); for(Constructor<?>c:cons){ System.out.println(c); } //注意观察上面的输出结果,再实例化,否则参数容易出错 User u1=(User)cons[0].newInstance("shsxt","good"); User u2=(User)cons[1].newInstance("sxt"); User u3=(User)cons[2].newInstance(); System.out.println(u1.getUname()+u1.getUpwd()); |
注意:newInstance()是调用空构造,如果空构造不存在,会出现异常。由此可知,使用其他构造器创建对象比较麻烦,使用空构造非常简单。确保空构造存在
四、 接口与父类
通过api获取接口与父类
//获取源头 Class<?> clz =Class.forName("com.shsxt.ref.simple.User"); //获取所有接口 Class<?>[] inters=clz.getInterfaces(); for(Class<?> in:inters){ System.out.println(in.getName()); } //获取父类 Class<?> cls=clz.getSuperclass(); System.out.println("继承的父类为:"+cls.getName()); |
五、 修饰符
获取修饰符,使用Modifier即可
Class<?>clz=Class.forName("com.shsxt.ref.simple.User"); //获得修饰符 int n=clz.getModifiers(); //使用Modifier转换为相应的字符串 System.out.println(Modifier.toString(n)); |
PUBLIC: 1 (二进制 0000 0001) PRIVATE: 2 (二进制 0000 0010) PROTECTED: 4 (二进制 0000 0100) STATIC: 8 (二进制 0000 1000) FINAL: 16 (二进制 0001 0000) SYNCHRONIZED: 32 (二进制 0010 0000) VOLATILE: 64 (二进制 0100 0000) TRANSIENT: 128 (二进制 1000 0000) NATIVE: 256 (二进制 0001 0000 0000) INTERFACE: 512 (二进制 0010 0000 0000) ABSTRACT: 1024 (二进制 0100 0000 0000) STRICT: 2048 (二进制 1000 0000 0000) |
六、 属性
获取所有属性(包括父类或接口) ,使用Field 即可操作
Class<?> clz = Class.forName("com.shsxt.ref.simple.User"); //获取属性 System.out.println("===============本类属性=========="); // 取得本类的全部属性 Field[] field = clz.getDeclaredFields(); for (int i = 0; i < field.length; i++) { // 1、权限修饰符 int mo = field[i].getModifiers(); String vis = Modifier.toString(mo); // 2、属性类型 Class<?> type = field[i].getType(); //3、名字 String name = field[i].getName(); System.out.println(vis + " " + type.getName() + " "+ name + ";"); } System.out.println("=========公开的属性包括接口或者父类属性======"); field = clz.getFields(); for (int i = 0; i < field.length; i++) { System.out.println(field [i]); } |
七、 方法
获取所有方法(包括父类或接口),使用Method即可。
public static void test() throws Exception { Class<?> clz = Class.forName("com.shsxt.ref.simple.User "); //获取属性 System.out.println("===============本类方法==============="); // 取得全部公共方法 Method[] methods =clz.getMethods(); for(Method m:methods){ //1、权限 int mod=m.getModifiers(); System.out.print(Modifier.toString(mod)+" "); //2、返回类型 Class<?> returnType=m.getReturnType(); System.out.print(returnType.getName()+" "); //3、名字 String name =m.getName(); System.out.print(name +"("); //4、参数 Class<?>[] para=m.getParameterTypes(); for(int i=0;i<para.length;i++){ Class<?> p =para[i]; System.out.print(p.getName() +" arg"+i); if(i!=para.length-1){ System.out.print(","); } } //异常 Class<?>[] exce=m.getExceptionTypes(); if(exce.length>0){ System.out.print(") throws "); for(int k=0;k<exce.length;++k){ System.out.print(exce[k].getName()+" "); if(k<exce.length-1){ System.out.print(","); } } }else{ System.out.print(")"); } System.out.println(); } } |
八、 数组
操作数组需要借助Array类
//1、创建数组 Object obj =Array.newInstance(int.class, 5); //2、获取大小 if(obj.getClass().isArray()){ //3、判断是否为数组 System.out.println(Array.getLength(obj)); //4、设置值 Array.set(obj,0, 100); //5、获取值 System.out.println(Array.get(obj,0)); } |
九、 类加载器
在java中有三种类类加载器:
⑴Bootstrap ClassLoader 此加载器采用c++编写,一般开发中很少见。
⑵Extension ClassLoader 用来进行扩展类的加载,一般对应的是jrelibext目录中的类
⑶AppClassLoader 加载 classpath指定的类,是最常用的加载器。同时也是java中默认的加载器。 了解即可。
public static void main(String[] args) throws Exception { System.out.println("类加载器 "+ClassLoader.class.getClassLoader().getClass().getName()); } |
了解一下类的生命周期:
在一个类编译完成之后,下一步就需要开始使用类,如果要使用一个类,肯定离不开JVM。在程序执行中JVM通过装载,链接,初始化这3个步骤完成。
类的装载是通过类加载器完成的,加载器将.class文件的二进制文件装入JVM的方法区,并且在堆区创建描述这个类的java.lang.Class对象。用来封装数据。 但是同一个类只会被类装载器装载一次。
链接就是把二进制数据组装为可以运行的状态。链接分为校验,准备,解析这3个阶段
1、校验一般用来确认此二进制文件是否适合当前的JVM(版本),
2、准备就是为静态成员分配内存空间。并设置默认值
3、解析指的是转换常量池中的代码作为直接引用的过程,直到所有的符号引用都可以被运行程序使用(建立完整的对应关系)
完成之后,类型也就完成了初始化,初始化之后类的对象就可以正常使用了,直到一个对象不再使用之后,将被垃圾回收。释放空间。当没有任何引用指向Class对象时就会被卸载,结束类的生命周期
十、 反射相关操作(重点)
1、操作属性
//1、获取Class对象 Class<?> clz=Class.forName("com.shsxt.ref.simple.User"); //2、获取对象 User u=(User)clz.newInstance(); //3、设置属性 Field field=clz.getDeclaredField("uname"); field.setAccessible(true);//打开权限 field.set(u, "0523"); //4、获取此属性 System.out.println(field.get(u)); |
2、调用方法
调用方法,都是直接对象.方法([实参]);反射之后,动态调用方法需要使用 invoke即可。
1)、方法调用
//1、获取Class对象 Class<?> clz=Class.forName("com.shsxt.ref.simple.User"); //2、获取对象 User u=(User)clz.newInstance(); //3、获取方法 Method m =clz.getMethod("coding", String.class,String.class); //4、成员方法的调用 m.invoke(u, "反射","多个参数"); //若是静态方法,传递null即可 因为静态方法属性类,不属于对象 m=clz.getMethod("testStatic",int.class); m.invoke(null, 100);//与对象无关 |
2)、操作setter与getter访问器
/** * @param obj 操作的对象 * @param att 操作的属性 * @param value 设置的值 * */ public static void setter(Object obj, String att,Object value) { try { //setUname setUpwd -->第一个字母大写 att=att.substring(0,1).toUpperCase()+att.substring(1); Method method = obj.getClass().getMethod("set" + att, value.getClass()); method.invoke(obj, value); } catch (Exception e) { e.printStackTrace(); } } public static Object getter(Object obj, String att) { try { //getUname getUpwd -->第一个字母大写 att=att.substring(0,1).toUpperCase()+att.substring(1); Method method = obj.getClass().getMethod("get" + att); return method.invoke(obj); } catch (Exception e) { e.printStackTrace(); } return null; } main方法 //1、获取Class对象 Class<?> clz=Class.forName("com.shsxt.ref.simple.User"); //2、获取对象 User u=(User)clz.newInstance(); //3、设置值 setter(u,"uname","shsxt"); //获取值 String str=(String) getter(u,"uname"); System.out.println(str); |