JAVA反射

    技术2022-07-11  88

    JAVA反射

    class类

    获取一个类的实例有三种方法 方法一:直接通过一个class的静态变量class获取 Class cls = String.class 方法二:如果我们有一个实例变量,可以通过该实例变量提供的getClass()方法获取 String s = "Hello"; Class cls = s.getClass(); 方法三:如果知道一个class的完整类名,可以通过静态方法Class.forName()获取 Class cls = Class.forName("java.lang.String"); 通过实例获取类的信息 System.out.println("Class name: " + cls.getName()); System.out.println("Simple name: " + cls.getSimpleName()); if (cls.getPackage() != null) { System.out.println("Package name: " + cls.getPackage().getName()); } System.out.println("is interface: " + cls.isInterface()); System.out.println("is enum: " + cls.isEnum()); System.out.println("is array: " + cls.isArray()); System.out.println("is primitive: " + cls.isPrimitive()); // 获取String的Class实例: Class cls = String.class; // 创建一个String实例: String s = (String) cls.newInstance(); 上述代码相当于new String()。 通过Class.newInstance()可以创建类实例, 它的局限是:只能调用public的无参数构造方法。 带参数的构造方法,或者非public的构造方法都无法通过Class.newInstance()被调用 动态加载 JVM在执行Java程序的时候,并不是一次性把所有用到的class全部加载到内存,而是第一次需要用到class时才加载 动态加载class的特性对于Java程序非常重要。利用JVM动态加载class的特性, 我们才能在运行期根据条件加载不同的实现类。例如,Commons Logging总是优先使用Log4j, 只有当Log4j不存在时,才使用JDK的logging。利用JVM动态加载特性,大致的实现代码如下: // Commons Logging优先使用Log4j: LogFactory factory = null; if (isClassPresent("org.apache.logging.log4j.Logger")) { factory = createLog4j(); } else { factory = createJdkLog(); } boolean isClassPresent(String name) { try { Class.forName(name); return true; } catch (Exception e) { return false; } } 这就是为什么我们只需要把Log4j的jar包放到classpath中,Commons Logging就会自动使用Log4j的原因。

    访问字段

    getField(name):根据字段名获取某个public的field(包括父类) getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类) Field[] getFields():获取所有public的field(包括父类) Field[] getDeclaredFields():获取当前类的所有field(不包括父类) getName():返回字段名称 getType():返回字段类型 getModifiers():返回字段的修饰符 Field f = String.class.getDeclaredField("value"); f.getName(); // "value" f.getType(); // class [B 表示byte[]类型 int m = f.getModifiers(); Modifier.isFinal(m); // true Modifier.isPublic(m); // false Modifier.isProtected(m); // false Modifier.isPrivate(m); // true Modifier.isStatic(m); // false 获取字段的值 Object p = new Person("Xiao Ming"); Class c = p.getClass(); Field f = c.getDeclaredField("name"); // 注意name要是private是要写这句 f.setAccessible(true); Object value = f.get(p); System.out.println(value); // "Xiao Ming" 通过Field实例可以读取或设置某个对象的字段,如果存在访问限制,要首先调用setAccessible(true)来访问非public字段。 通过反射读写字段是一种非常规方法,它会破坏对象的封装。

    调用方法

    Method getMethod(name, Class...):获取某个public的Method(包括父类) Method getDeclaredMethod(name, Class...):获取当前类的某个Method(不包括父类) Method[] getMethods():获取所有public的Method(包括父类) Method[] getDeclaredMethods():获取当前类的所有Method(不包括父类) getName():返回方法名称,例如:"getScore"; getReturnType():返回方法返回值类型,也是一个Class实例,例如:String.class; getParameterTypes():返回方法的参数类型,是一个Class数组,例如:{String.class, int.class}; getModifiers():返回方法的修饰符,它是一个int,不同的bit表示不同的含义。 调用方法 String s1 = "hello world"; Method substring = String.class.getMethod("substring", int.class); Object invoke = substring.invoke(s1, 6); System.out.println(invoke); 调用静态方法 如果获取到的Method表示一个静态方法,调用静态方法时,由于无需指定实例对象, 所以invoke方法传入的第一个参数永远为null。我们以Integer.parseInt(String)为例: // 获取Integer.parseInt(String)方法,参数为String: Method m = Integer.class.getMethod("parseInt", String.class); // 调用该静态方法并获取结果: Integer n = (Integer) m.invoke(null, "12345"); // 打印调用结果: System.out.println(n); 调用非public方法 Person p = new Person(); Method m = p.getClass().getDeclaredMethod("setName", String.class); m.setAccessible(true); //跟访问非public的字段是一样的 m.invoke(p, "Bob"); System.out.println(p.name); 多态 针对某个类型的方法调用,其真正执行的方法取决于运行时期实际类型的方法 Method m = Person.class.getMethod("hello"); m.invoke(new Student()); 实际上相当于: Person p = new Student(); p.hello(); 调用构造函数 getConstructor(Class...):获取某个public的Constructor; getDeclaredConstructor(Class...):获取某个Constructor; getConstructors():获取所有public的Constructor; getDeclaredConstructors():获取所有Constructor。 注意Constructor总是当前类定义的构造方法,和父类无关,因此不存在多态的问题。 调用非public的Constructor时, 必须首先通过setAccessible(true)设置允许访问。 旧:Person p = Person.class.newInstance(); 新:Constructor cons1 = Person.class.getConstructor(); Person p = cons1.newInstance(); 获取继承关系 Class getSuperclass():获取父类类型; Class i = Integer.class; Class n = i.getSuperclass(); System.out.println(n); Class o = n.getSuperclass(); System.out.println(o); System.out.println(o.getSuperclass()); Class[] getInterfaces():获取当前类实现的所有接口。 Class s = Integer.class; Class[] is = s.getInterfaces(); for (Class i : is) { System.out.println(i); } 通过Class对象的isAssignableFrom()方法可以判断一个向上转型是否可以实现 // Integer i = ? Integer.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Integer // Number n = ? Number.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Number // Object o = ? Object.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Object // Integer i = ? Integer.class.isAssignableFrom(Number.class); // false,因为Number不能赋值给Integer

    动态代理

    什么是动态代理:Java标准库提供了动态代理功能,允许在运行期动态创建一个接口的实例 通常情况我们写的就是静态代理的代码: public interface Hello { void morning(String name); } 编写实现类: public class HelloWorld implements Hello { public void morning(String name) { System.out.println("Good morning, " + name); } } 这是提前就编译好的。动态代理就是运行时候才生成一个实现接口的实例 动态代理步骤: 1. 定义一个InvocationHandler实例,它负责实现接口的方法调用 2. 通过Proxy.newProxyInstance()创建interface实例,它需要3个参数: 使用的ClassLoader,通常就是接口类的ClassLoader 需要实现的接口数组,至少需要传入一个接口进去 用来处理接口方法调用的InvocationHandler实例 将返回的Object强制转型为接口 InvocationHandler dynamic_proxy = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ("hello".equals(method.getName())) { System.out.println("dynamic proxy"); } return null; } }; Person o = (Person) Proxy.newProxyInstance( Person.class.getClassLoader(), new Class[] {Person.class}, dynamic_proxy); o.hello();
    Processed: 0.017, SQL: 12