JAVA类加载机制与反射,动态代理Proxy串联分析与应用
2017-09-29 15:29
811 查看
JAVA类加载机制与反射,通过反射绕过泛型的编译验证
1.类加载机制
当程序使用一个还未被加载到内存的类时,JVM会通过加载、连接、初始化三个步骤将该类进行初始化。类的加载指的是将类的class文件读入内存,并为之创建一个java.lang.Class对象。(1).CLASS类
Class类也是类的一种,只是名字和class关键字高度相似。Java是大小写敏感的语言。Class类的对象内容是你创建的类的类型信息,比如你创建一个shapes类,那么,Java会生成一个内容是shapes的Class类的对象
Class类的对象不能像普通类一样,以 new shapes() 的方式创建,它的对象只能由JVM创建,因为这个类没有public构造函数
Class类的作用是运行时提供或获得某个对象的类型信息,和C++中的typeid()函数类似。这些信息也可用于反射。
获得一个Class类对象
第一种办法,Class类的forName函数注意,这里forName(arg)里面传入的是类的全称public class shapes{} Class obj= Class.forName("shapes");
第二种办法,使用对象的getClass()函数
public class shapes{} shapes s1=new shapes(); Class obj=s1.getClass(); Class obj1=s1.getSuperclass();//这个函数作用是获取shapes类的父类的类型
第三种办法,使用类字面常量注意,使用这种办法生成Class类对象时,不会使JVM自动加载该类(如String类)。而其他办法会使得JVM初始化该类。
Class obj=String.class; Class obj1=int.class;
(2)静态加载类
静态加载类导致的弊端是,假如一个类里面需要加载多个类,其中某一个类有问题,或者不存在,则在程序编译的过程中,会导致编译报错;尤其是在一个主干上加载一些功能型模块,我们一般希望某个功能出问题不要影响其他功能的实现,这就需要引入动态加载类的概念public class StaticLoadingClass { public static void main(String[] args) { //new 创建对象 是静态加载类,在编译时刻就需要加载所有可能使用到的类 String[] args1=new String[]{"QQ","WEIXIN"}; if("QQ".equals(args1[0])){ QQ qq =new QQ(); //<------假设只定义了QQ这个类,这句话会正确编译 qq.begin(); } if("WEIXIN".equals(args1[1])){ WEIXIN weixin=new WEIXIN(); //<-----假设没有定义WEIXIN这个类,那么这句话编译时报错 weixin.begin(); } } }
(3)动态加载类
思考:下面的class.forMame可以动态的给,假设场景是,用户在前台点击多个功能按钮中的一个,会传不同的参数到这里,假如是QQ,这个QQ功能模块是能正常使用的,假如某个用户点击WEIXIN这个模块,这时程序才会报错,这样就隔离了QQ和微信的功能模块之间的关系DynamicLoadingClass.java
public class DynamicLoadingClass { public static void main(String[] args) { try { //动态加载类,在运行时刻加载 Class c=Class.forName("javareflect.loadingClass.QQ"); //通过类类型,创建该类对象 Tencent tx=(Tencent)c.newInstance(); tx.begin(); Class b=Class.forName("javareflect.loadingClass.WEIXIN"); Tencent tx1=(Tencent)b.newInstance(); tx1.begin(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
Tencent.java
interface Tencent { public void begin(); }QQ.java
public class QQ implements Tencent{ public void begin(){ System.out.println("QQ......begin"); } }
2.通过反射查看类的信息
下面例子是通过反射获取方法,成员变量,构造函数的信息package javareflect; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; public class ClassUtil { /* * 打印类的信息,包活类的成员函数 * * @param obj 该对象所属类的信息 */ public static void printClassMessage(Object obj) { // 要获取类的信息 首先获取类的类类型 Class<? extends Object> c = obj.getClass();// 传递的是哪个子类的对象,c就是该类的类类型 // 获取类的名称 System.out.println("类的名称是:" + c.getName()); /* * Method类,方法对象 一个成员方法就是一个Method对象 public Method[] * getMethods()返回某个类的所有公用(public)方法包括其继承类的公用方法, 当然也包括它所实现接口的方法。 public * Method[] getDeclaredMethods()对象表示的类或接口声明的所有方法, * 包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。当然也包括它所实现接口的方法。 */ Method[] ms = c.getMethods(); for (int i = 0; i < ms.length; i++) { // 得到方法的返回值类型的类类型 Class<?> returnType = ms[i].getReturnType(); System.out.print(returnType.getSimpleName() + " "); // 得到方法名称 System.out.println(ms[i].getName() + "("); // 获取参数类型--->得到参数列表的类型的类类型 Class[] paramType = ms[i].getParameterTypes(); for (Class<?> paramTypeList : paramType) { System.out.print(paramTypeList.getName() + ","); } System.out.println(")"); } } private static void getFieldMessage(Object obj) { Class c = obj.getClass(); /* * 成员变量也是对象 java.lang.reflect.Field Field类封装了关于成员变量的操作 * getFields()方法获取的是所有的public的成员变量的信息 * getDeclaredFields()方法获取的是该类自己声明的成员变量的信息 */ // Field[] fs = c.getFields(); Field[] fs = c.getDeclaredFields(); for (Field field : fs) { // 得到成员变量的类型的类类型 Class fieldType = field.getType(); String typeName = fieldType.getName(); // 得到成员变量的名称 String fieldName = field.getName(); System.out.println(typeName + " " + fieldName); } } private static void getConstructMessage(Object obj){ Class c=obj.getClass(); /* * 构造函数也是对象 * java.lang.Constructor封装了构造函数信息 * getConstructors()获取所有的public的构造函数 * getDeclaredConstructors()得到所有构造函数 */ //Constructor[] cs = c.getConstructors(); Constructor[] cs = c.getDeclaredConstructors(); for(Constructor construor:cs){ System.out.println(construor.getName()+"("); //获取构造函数的参数列表,得到的是参数列表的类类型 Class[] paramTypes=construor.getParameterTypes(); for(Class class1:paramTypes){ System.out.print(class1.getName()+","); } System.out.println(")"); } } public static void main(String[] args) { String str = "1"; printClassMessage(str); System.out.println("+++++++++++++++++++++++++++++++++"); getFieldMessage(str); System.out.println("+++++++++++++++++++++++++++++++++"); getConstructMessage(str); } }
3.方法的反射
(1)如何获取某个方法:方法的名称和方法的参数列表才能唯一决定某个方法
(2)方法反射的操作
method.invoke(对象,参数列表)
这里有一个需要注意的是:c.getDeclaredMethod(name, parameterTypes) 表示的类或接口声明的所有方法,假如要获取一个私有的private方法,需要在反射出这个方法后,给这个方法开启私有访问权限
Method m2 = c.getDeclaredMethod("printNullParameterTypes");
m2.setAccessible(true);// 开启私有访问权限
m2.invoke(a);
package javareflect; import java.lang.reflect.Method; /* * 1.如何获取某个方法:方法名称和方法的参数列表才能唯一决定某个方法 * 2.方法的反射操作: method.invoke(对象,参数列表) */ public class MethodInvoke { public static void main(String[] args) { // 要获取print(int,int)方法 1.要获取一个方法就是获取类的信息,获取类的信息首先要获取类的类类型 A a = new A(); Class c = a.getClass(); /* * 2.获取方法 名称和参数列表决定 getMethod(name, parameterTypes)获取的是public方法 * c.getDeclaredMethod(name, parameterTypes) 表示的类或接口声明的所有方法 */ try { // Method m=c.getMethod("print", new Class[]{int.class,int.class}); Method m = c.getMethod("print", int.class, int.class); // 方法反射操作 // a.print(10,20); // m.invoke(a, new Object[]{10,20}); Object obj = m.invoke(a, 10, 20); /* * Method m1=c.getMethod("printNullParameterTypes"); Object * obj1=m1.invoke(a); */ Method m2 = c.getDeclaredMethod("printNullParameterTypes"); m2.setAccessible(true);// 开启私有访问权限 m2.invoke(a); } catch (Exception e) { e.printStackTrace(); } } } class A { private void printNullParameterTypes() { System.out.println("null参数"); } public void print(int a, int b) { System.out.println(a + b); } }
4.通过Class,Method来认识泛型的本质
java中集合的泛型,是防止错误输入的,只在编译阶段有效,绕过编译就无效了,我们可以通过方法的反射绕过编译package javareflect; import java.lang.reflect.Method; import java.util.ArrayList; /* * 通过Class,Method认识泛型本质 */ public class GenericityReflect { public static void main(String[] args) { ArrayList list=new ArrayList(); ArrayList<String> list1=new ArrayList<String>(); list1.add("hello"); //list1.add(10); 编译错误 Class c1 =list.getClass(); Class c2=list1.getClass(); System.out.println(c1==c2); //反射操作都是编译之后的操作 /* * c1==c2结果返回true 表示编译之后集合的泛型是去泛型化的 * java中集合的泛型是为了防止错误输入,只在编译阶段有效,绕过编译阶段就失效了 * 我们可以通过方法的反射操作,绕过编译,验证上一句话 */ try { Method m=c2.getMethod("add", Object.class); Object obj=m.invoke(list1,10); System.out.println(list1.size()); System.out.println(list1); // for(String str:list1){ // System.out.println(str); // } 不能这么编译,因为有int型也传入进去了 } catch (Exception e) { e.printStackTrace(); } } }
相关文章推荐
- proxy动态代理机制分析
- Java代理和动态代理机制分析和应用
- Proxy动态代理应用、源码分析
- 黑马程序员——反射——类的加载,反射的应用,简单动态代理
- 代理模式Proxy(动态代理)在程序运行时,运用反射机制动态创建而成
- Java代理和动态代理机制分析和应用
- 框架学习前基础加强 泛型高级,注解,反射(泛型&注解)应用案例,IOC,Servlet3.0,动态代理,类加载器
- Java代理和动态代理机制分析和应用
- 类加载机制与反射5——使用反射生成JDK动态代理
- Java代理和动态代理机制分析和应用
- java类加载,反射,动态代理入门理解
- Android应用setContentView与LayoutInflater加载解析机制源码分析
- Java 动态代理机制分析及扩展
- java反射中的动态代理机制(有实例)
- 15. JAVA 反射机制 Part 2(动态代理、类的生命周期、工厂设计模式) ----- 学习笔记
- Android应用setContentView与LayoutInflater加载解析机制源码分析
- 反射,类加载器,动态代理
- Java 动态代理机制分析及扩展
- Java 动态代理机制分析及扩展,第 1 部分(转)
- 使用反射生成JDK动态代理---使用Proxy和InvocationHandler创建动态代理