JavaSE:反射

    科技2024-11-07  13

    JavaSE:反射

    概述关于java.lang.Class类的理解 通过反射创建运行时类的对象对比Class类的实例化使用ClassLoader加载配置文件使用反射获取运行时类的完整结构属性方法获取带泛型的父类以及其泛型获取运行时类的接口、所在包、注解 动态代理动态代理代码示例:

    概述

    Reflection(反射):是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

    加载完类之后,在对内存的方法区中就产生了一个class类型的对象(一个类只有一个class对象),这个对象就包含了完整的类的结构信息。外面可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。

    动态语言:

    是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构。

    主要动态语言:Object-C、C#、JavaScript、PHP、Python、Erlang

    静态语言:

    与动态语言相对的,运行时结构不可变的语言就是静态语言。如:Java、C、C++。

    Java不是动态语言,但Java可以称为“准动态语言”。即Java由一定的动态性,外面可以利用反射机制、字节码操作获得类似动态语言的特性。

    反射机制提供的功能:

    在运行时判断任意一个对象所属的类在运行时构造任意一个类的对象在运行时判断任意一个类所具有的成员变量和方法在运行时获取泛型信息在运行时调用任意一个对象的成员变量和方法在运行时处理注解生成动态代理

    关于java.lang.Class类的理解

    类的加载过程: 程序在经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)接着使用java.exe命令对某个字节码文件进行解释运行,像用于将某个字节码文件加载到内存中,此过程就称为类的加载。加载到内存中的类,外面就i称为运行时类,就作为Class的一个实例。换句话说,Class的实例对应着一个运行时类。加载到内存中的运行时类,会缓存一定的时间。在此时间内,我们可以通过不同的方式来获取此运行时类。

    类的加载过程:

    通过反射创建运行时类的对象

    对比

    @Test public void test1() throws Exception{ //通过new 的方式创建对象,使用对象调用public 修饰的属性和方法 Person person = new Person("张三", 21); System.out.println(person); person.age = 18; System.out.println(person); person.show(); System.out.println("******************* 反射 *******************"); //使用反射 //1.获取Class类的实例对象 Class<Person> p = Person.class; //2.通过反射获取Person类的构造器 Constructor<Person> constructor = p.getConstructor(String.class, int.class); //3.创建Person类对象 Person person1 = constructor.newInstance("李四", 22); System.out.println(person1); //4.获取Person类属性 Field age = p.getDeclaredField("age"); age.set(person1,18); System.out.println(person1); //5.获取Person类的方法 Method show = p.getDeclaredMethod("show"); show.invoke(person1); //通过反射获取Person类中的私有构造器、私有属性、私有方法 System.out.println("******************* 反射获取私有结构 *******************"); Class<Person> p2 = Person.class; Constructor<Person> constructor1 = p2.getDeclaredConstructor(String.class); constructor1.setAccessible(true); Person person2 = constructor1.newInstance("王五"); System.out.println(person2); Field name = p2.getDeclaredField("name"); name.setAccessible(true); name.set(person2,"赵六"); System.out.println(person2); Method getNation = p2.getDeclaredMethod("getNation", String.class); getNation.setAccessible(true); String str = (String) getNation.invoke(person2, "中国"); System.out.println(str); }

    运行结果

    Class类的实例化

    @Test public void test2() throws Exception{ //Class类的实例化 //方式一 Class clazz = Person.class; //方式二: Person p = new Person("Tom", 28); Class clazz2 = p.getClass(); //方式三:使用Class类的 静态方法 Class clazz3 = Class.forName("Person"); System.out.println( clazz == clazz2); System.out.println(clazz == clazz3); //方式四:通过类加载器加载 ClassLoader loader = p.getClass().getClassLoader(); Class clazz4 = loader.loadClass("Person"); System.out.println(clazz == clazz4); }

    使用ClassLoader加载配置文件

    @Test public void test3() throws Exception{ //使用ClassLoader加载配置文件 Properties pro = new Properties(); //方式一:此时默认路径在当前Module下 FileInputStream fis = new FileInputStream("jdbc.properties"); pro.load(fis); //方式二:此时当前默认路径在src下 // ClassLoader loader = ReflectionTest.class.getClassLoader(); // InputStream is = loader.getResourceAsStream("jdbc.properties"); // pro.load(is); String username = pro.getProperty("username"); String password = pro.getProperty("password"); System.out.println("用户名为:" + username + ",密码为:" + password); }

    使用反射获取运行时类的完整结构

    属性

    //获取运行时类的属性结构及其内部结构 @Test public void test(){ Class clazz = Person.class; //获取所有类及其父类中声明为public的属性 Field[] fields = clazz.getFields(); for(Field f:fields){ System.out.println(f); } System.out.println("*********************************************************"); //获取类中的所有属性 Field[] field = clazz.getDeclaredFields(); for(Field f:field){ // System.out.println(f); //获取属性的权限修饰符 int modifier = f.getModifiers(); System.out.print(Modifier.toString(modifier) + "\t"); Class<?> type = f.getType(); System.out.print(type.getName() + "\t"); String name = f.getName(); System.out.print(name); System.out.println(); } }

    方法

    @Test public void test2(){ //获取运行时类的方法的内部结构 Class clazz = Person.class; //获取所有类及其父类中声明为public的方法 // Method[] methods = clazz.getMethods(); // for (Method method:methods){ // System.out.println(method); // } //获取当前类的说有方法 Method[] methods = clazz.getDeclaredMethods(); for (Method method:methods){ //获取权限修饰符 int modifiers = method.getModifiers(); System.out.print(Modifier.toString(modifiers) + "\t"); //获取返回值类型 Class<?> type = method.getReturnType(); System.out.print(type.getName() + "\t"); //获取方法名 String name = method.getName(); System.out.print(name+ "\t"); //获取参数列表 System.out.print("("); Class<?>[] parameterTypes = method.getParameterTypes(); if (parameterTypes.length > 0){ for (int i = 0; i < parameterTypes.length; i++) { if (i == parameterTypes.length - 1){ System.out.print(parameterTypes[i].getName() + "\targs_" + i); break; } System.out.print(parameterTypes[i].getName() + "\targs_" + i + ","); } } System.out.print(")"); //获取异常 Class<?>[] exceptionTypes = method.getExceptionTypes(); if (exceptionTypes.length > 0){ System.out.print("\tthrows\t"); for (int i = 0; i < exceptionTypes.length; i++) { if (i == exceptionTypes.length - 1){ System.out.print(exceptionTypes[i].getName()); break; } System.out.print(exceptionTypes[i].getName() + ","); } } System.out.println("{}"); } }

    构造器同上。

    获取带泛型的父类以及其泛型

    @Test public void test3(){ Class clazz = Person.class; //获取当前类的带泛型的父类 Type type = clazz.getGenericSuperclass(); //强转为ParameterizedType类 ParameterizedType paramType = (ParameterizedType) type; //获取当前类的带泛型的父类的泛型 Type[] args = paramType.getActualTypeArguments(); for(Type arg:args){ System.out.println(arg.getTypeName()); } }

    获取运行时类的接口、所在包、注解

    @Test public void test4(){ //获取运行时类的接口、所在包、注解 Class clazz = Person.class; Class[] interfaces = clazz.getInterfaces(); for (Class interfacess:interfaces){ System.out.println(interfacess); } Package aPackage = clazz.getPackage(); System.out.println(aPackage); Annotation[] annotations = clazz.getAnnotations(); for (Annotation anno:annotations){ System.out.println(anno); } }

    动态代理

    代理设计模式的原理:

    使用一个代理将对象包装起来,然后用此代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。

    静态代理:特征是代理类和目标类对象的类都是在编译期间确定下来的,不利于程序的扩展。同时,每个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。最好可以通过一个代理类完成全部的代理功能。

    动态代理是指客户通过代理类来调用其他对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。

    动态代理相比静态代理的优点:

    ​ 抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更灵活和统一的处理众多的方法。

    动态代理代码示例:

    //动态代理测试 public class ProxyTest { public static void main(String[] args) { ANTAClothFactory a = new ANTAClothFactory(); clothFactory instance = (clothFactory) ProxyFactory.getInstance(a); instance.productCloth(); String name = instance.getName("ANTA"); System.out.println(name); } } interface clothFactory{ void productCloth(); String getName(String name); } class ANTAClothFactory implements clothFactory{ @Override public void productCloth() { System.out.println("ANTA生产了一批衣服"); } @Override public String getName(String name) { return name; } } //根据加载到内存中的被代理类,动态创建一个代理类以及其对象 //当通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法。 class ProxyFactory{ //调用此方法,返回一个代理类对象 public static Object getInstance(Object obj){//obj-->被代理类 MyInvocationHandler handler = new MyInvocationHandler(); handler.bind(obj); ClassLoader loader = obj.getClass().getClassLoader(); return Proxy.newProxyInstance(loader, obj.getClass().getInterfaces(),handler); } } class MyInvocationHandler implements InvocationHandler{ private Object obj; public void bind(Object obj){//绑定被代理类 this.obj = obj; } //当我们通过代理类的对象,调用方法A时,就会自动调用如下方法,invoke() //将被代理对象要执行的方法A的功能就声明在invoke方法中 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //method即为代理类对象调用方法,此方法也就作为了被代理类对象要调用的方法 //obj被代理类对象 System.out.println("动态代理"); return method.invoke(obj,args); } }

    运行结果

    Processed: 0.022, SQL: 8