目录
- 1 什么是反射?主要提供了哪些功能?
- 2 如何通过反射获取类的信息?
- 3 数组的特殊处理。
- 4 反射的应用场景。
- 5 其他。
正文
1 什么是反射?主要提供了哪些功能?
1) Java的反射机制是指在程序运行期间,对于任意一个类,都可以知道这个类的属性和方法,并进行调用。
2) 提供以下功能:
① 在运行时可以判断任意一个对象所属的类。
② 在运行时可以构造任意一个类的对象。
③ 在运行时可以获取任意一个类的所有成员变量和方法。
④ 在运行时调用任意一个对象的方法。
⑤ 生成动态代理。
3) 优缺点
① 优点:增加灵活性。
② 缺点:使用不当会导致效率低、会破坏封装,且可以访问类的私有方法,不安全。
2 如何通过反射获取类的信息?
1) 首先获取类的Class对象:
① 每一个类、接口、数组、基本类型都有唯一的一个对应的Class类对象。通过Class类对象可以获取类的全部信息(包括成员变量、方法、父类、接口等)。
② 获得Class对象的三种方式。【参考TestLoadClass.java】
③ Class的常用方法:【参考TestClass.java】
package com.devway.j2se.reflect; public class TestLoadClass { public static void main(String[] args) { try { // 获取Class对象的三种方法 // 方法一,通过对象实例获取, A a = new A(); Class<?> class1 = a.getClass(); System.out.println("1:getClass " + class1); // 方法二,根据类名获取 Class<?> class2 = B.class; System.out.println("2:.class " + class2); // 方法三,根据类的全名获取 Class<?> class3 = Class.forName("com.devway.j2se.reflect.C"); System.out.println("3:Class.forName " + class3); } catch (ClassNotFoundException e) { System.err.println(e); } } } class A { static { System.out.println("init A static"); } } class B { static { System.out.println("init B static"); } } class C { static { System.out.println("init C static"); } }
package com.devway.j2se.reflect; public class TestClass { public static void main(String[] args) { try { Class<?> class1 = Class.forName("com.devway.j2se.reflect.Child"); System.out.println("1:" + class1);// true // Class类的常用方法 // 判断是否是接口 boolean isInterface = class1.isInterface();// false // 判断是否是数组 boolean isArray = class1.isArray();// false // 判断是否是8种基本类型 boolean isPrimitive = class1.isPrimitive();// false; System.out.println("2:" + isInterface + "," + isArray + "," + isPrimitive); // 获取父类的Class类对象 Class<?> superClass = class1.getSuperclass(); // 获取所有接口 Class<?>[] interfaces = class1.getInterfaces(); // 获取类型修饰符 int modifiers = class1.getModifiers(); System.out.println("3:" + superClass + "," + interfaces + "," + modifiers); // 获取数组的组件类型 int[] ints = { 1, 2, 3 }; Class<?> class4 = ints.getClass(); Class<?> class5 = class4.getComponentType();// int System.out.println("4:" + class4); System.out.println("4:" + class5); // 返回声明这个的Class对象,如果不是嵌套类型,返回null Class<?> class6 = class1.getDeclaringClass();// null System.out.println("5:" + class6); // 获取包 Package package1 = class1.getPackage(); System.out.println("6:" + package1); } catch (ClassNotFoundException e) { System.err.println(e); } } } package com.devway.j2se.reflect; public interface Interface1 { String iv1 = "a"; String iv2 = "b"; void f1(); } package com.devway.j2se.reflect; public class Parent { public String pv1; protected String pv2; public Parent() { } public String pf1(String s) { return s; } protected void pf2() { } void pf3() { } private void pf4() { } } package com.devway.j2se.reflect; public class Child extends Parent implements Interface1 { public String cv1; protected String cv2; private String cv3; public static String scv1; public Child() { } public Child(String s, int i) { } protected Child(int i) { } private Child(String s) { } public String cf1(String s, int i) throws NullPointerException { return s + i; } protected void cf2() { System.out.println("protected method cf4()"); } void cf3() { System.out.println("default method cf4()"); } private void cf4() { System.out.println("private method cf4()"); } @Override public void f1() { System.out.println("public method f1()"); } }
2) 对类的构造函数、方法、属性的访问和调用:
① Constructor类:用于构造函数。【参考TestConstrcutor.java】
② Method类:用于类的方法。【参考TestMethod.java】
③ Field类:用于类的属性。【参考TestField.java】
④ Modifer类:用于判断修饰符类型。【参考TestModifier.java】
⑤ Field、Method、Constructor类都继承了AccessibleObject类(该类用于获取和修该访问权限)【参考TestAccessible.java】,同时也实现了Member接口。
package com.devway.j2se.reflect; import java.lang.reflect.Constructor; public class TestConstrcutor { public static void main(String[] args) { try { Class<?> class1 = Class.forName("com.devway.j2se.reflect.Child"); // 获取所有的public构造函数,包括父类和接口的 Constructor<?>[] publicConstructors = class1.getConstructors(); for (Constructor<?> constructor : publicConstructors) { System.out.println("1:" + constructor); } // 获取当前类的所有构造函数,不包括父类和接口的 Constructor<?>[] declaredConstructors = class1.getDeclaredConstructors(); for (Constructor<?> constructor : declaredConstructors) { System.out.println("2:" + constructor); } // 根据名称和参数获取方法 Constructor<?> constructor1 = class1.getConstructor(String.class, int.class); System.out.println("3:" + constructor1); // 获取方法参数对应的Class对象 Class<?>[] paramTypesClass = constructor1.getParameterTypes(); for (Class<?> paramType : paramTypesClass) { System.out.println("4:" + paramType); } // 获取方法抛出的异常对应的Class对象 Class<?>[] exceptionTypesClass = constructor1.getExceptionTypes(); for (Class<?> exceptionType : exceptionTypesClass) { System.out.println("6:" + exceptionType); } // 创建实例并调用方法 Interface1 inf = (Interface1) class1.newInstance(); inf.f1(); } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException e) { System.err.println(e); } } }
package com.devway.j2se.reflect; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class TestMethod { public static void main(String[] args) { try { Class<?> class1 = Class.forName("com.devway.j2se.reflect.Child"); // 获取所有的public方法,包括父类和接口的 Method[] publicMethods = class1.getMethods(); for (Method method : publicMethods) { System.out.println("1:" + method); } // 获取当前类的所有方法,不包括父类和接口的 Method[] declaredMethods = class1.getDeclaredMethods(); for (Method method : declaredMethods) { System.out.println("2:" + method); } // 根据名称和参数获取方法 Method method1 = class1.getMethod("cf1", String.class, int.class); System.out.println("3:" + method1); // 获取方法返回值类型对应的Class对象 Class<?> returnTypeClass = method1.getReturnType(); System.out.println("4:" + returnTypeClass); // 获取方法参数对应的Class对象 Class<?>[] paramTypesClass = method1.getParameterTypes(); for (Class<?> paramType : paramTypesClass) { System.out.println("5:" + paramType); } // 获取方法抛出的异常对应的Class对象 Class<?>[] exceptionTypesClass = method1.getExceptionTypes(); for (Class<?> exceptionType : exceptionTypesClass) { System.out.println("6:" + exceptionType); } // 方法调用 Child child = (Child) class1.newInstance(); String value = (String) method1.invoke(child, "abc", 123); System.out.println("7:" + value); } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { System.err.println(e); } } }
package com.devway.j2se.reflect; import java.lang.reflect.Field; public class TestField { public static void main(String[] args) { try { Class<?> class1 = Class.forName("com.devway.j2se.reflect.Child"); // 获取所有的public域,包括父类和接口的 Field[] publicFields = class1.getFields(); for (Field field : publicFields) { System.out.println("1:" + field); } // 获取当前类的所有域,不包括父类和接口的 Field[] declaredFields = class1.getDeclaredFields(); for (Field field : declaredFields) { System.out.println("2:" + field); } // 根据名称获取域 Field field1 = class1.getField("cv1"); System.out.println("3:" + field1); // 获取域类型的Class对象 Class<?> field1Class = field1.getType(); System.out.println("4:" + field1Class); // 读取域的值 Child child = (Child) class1.newInstance(); child.cv1 = "abc"; String field1Value = (String) field1.get(child);// abc System.out.println("5:" + field1Value); // 修改域的值 field1.set(child, "def"); System.out.println("6:" + child.cv1); } catch (ClassNotFoundException | NoSuchFieldException | SecurityException | InstantiationException | IllegalAccessException e) { System.err.println(e); } } }
package com.devway.j2se.reflect; import java.lang.reflect.Modifier; public class TestModifier { public static void main(String[] args) { try { Class<?> class1 = Class.forName("com.devway.j2se.reflect.Child"); boolean isPrivate = Modifier.isPrivate(class1.getModifiers());// false; System.out.println("1:" + isPrivate); boolean isAbastract = Modifier.isAbstract(class1.getModifiers());// false System.out.println("2:" + isAbastract); } catch (ClassNotFoundException e) { System.err.println(e); } } }
package com.devway.j2se.reflect; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class TestAccessible { public static void main(String[] args) { try { Class<?> class1 = Class.forName("com.devway.j2se.reflect.Child"); Method method1 = class1.getDeclaredMethod("cf4"); Interface1 inf = (Interface1) class1.newInstance(); // ! method1.invoke(inf,null);//cf4是private方法,不能访问 System.out.println("1:" + method1.isAccessible());//false //取消访问限制 method1.setAccessible(true); method1.invoke(inf,null); System.out.println("2:" + method1.isAccessible());//true } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { System.err.println(e); } } }
3 数组的特殊处理。
1) 数组是没有方法和属性的对象,所以不能通过反射常用的的方法来获取或设置成员。
2) 可以通过反射创建数组,使用两种new Instance的方法来创建。使用Array.set和Array.get来设置或获取数组元素的值。【参考TestArray.java】
package com.devway.j2se.reflect; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Method; public class TestArray { public static void main(String[] args) { Integer[] arrays1 = { 1, 2, 3, 4 }; Class<?> class1 = arrays1.getClass(); // 数组是没有方法和域的对象,所以都返回null Method[] delcaredMethods = class1.getDeclaredMethods(); for (Method method : delcaredMethods) { System.out.println("1:" + method);// null } Field[] delcaredFields = class1.getDeclaredFields(); for (Field field : delcaredFields) { System.out.println("2:" + field);// null } // 创建数组方式1 Integer[] arrays2 = (Integer[]) Array.newInstance(Integer.class, 5); // 创建数组方式2,返回多为数组 int[] intval = { 2, 2 }; Integer[][] arrays3 = (Integer[][]) Array.newInstance(Integer.class, intval); // 设置数组元素的值 for (int i = 0; i < arrays2.length; i++) { Array.set(arrays2, i, i + 1); } // 获取数组元素的值 for (int i = 0; i < arrays2.length; i++) { System.out.println("3:" + Array.get(arrays2, i)); } } }
4 反射的应用场景。
1) 用于基础框架中,如Spring。
2) JDBC,数据库可能使用Oracle,也可能使用Mysql,不同的数据库有不同的驱动,在运行时,再通过Class.forName()运用反射动态加载。
3) 应用开发中,尽量避免使用反射。
5 其他。
1) class.getName方法返回结果
① 数组类型以[为前缀,后面跟着成员的类型编码,基础类型则简写如下。
(B:byte、C:char、D:double、F:float、I:int、J:long、S:short、Z:boolean)
② 嵌套类型使用$符号将类型名隔开:Outer$Inner
2) 注意这种写法是错误的:Class<Parent> c = Child.class; 因为Child.class不是Parent的子类,正确写法是:Class<? extends Parent> c= Child.class。
3) 向上转型和向下转型:
① 向上转型:子类转为父类,自动转换。
② 向下转型:父类转为子类,需要强制转换。