感觉好高大上的概念
学习来源慕课网:反射——Java高级开发必须懂的
万物皆对象先定义一个类
1 2 3 4 5 6 class Foo { public void print () { System.out.println("Foo" ); } }
除了基础类型和类的静态成员外,Java一切皆对象,包括使用class关键词定义的类,他是Class
类的对象,可以通过以下三种方法获取它,分别为通过类的.class
成员,通过类的实例对象的.getClass()
方法以及通过Class
对象的.forName()
方法获得,这三者结果等价
1 2 3 4 5 6 7 8 9 10 11 12 Foo foo = new Foo(); Class c1 = Foo.class; Class c2 = foo.getClass(); Class c3 = Class.forName("com.example.reflect.Foo" ); System.out.println(c1 == c2); System.out.println(c2 == c3);
在运行时动态加载类从源码到运行,java程序经历了编译和运行两个阶段,而常使用的new
来初始化对象是在编译阶段,而反射则可以让类在运行时初始化为对象,这样如果项目设计的比较好的话添加新的类就不要重新编译整个项目,而是只用编译新添加的文件就可以了
使用Class类的.newInstance()
方法在运行时动态初始化一个对象
举个例子,先定义一个接口
1 2 3 public interface Person { void say () ; }
实现一个类
1 2 3 4 5 6 public class Superman implements Person { @Override public void say () { System.out.println("I'm Superman!" ); } }
主函数根据输入的参数动态加载类
1 2 3 4 5 6 7 8 9 10 11 public class DynamicLoad { public static void main (String[] args) { try { Class c = Class.forName(args[0 ]); Person p = (Person)c.newInstance(); p.say(); } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) { e.printStackTrace(); } } }
编译这三个文件
1 javac com/example/reflect/dynamic/*.java
运行
1 java com.example.reflect.dynamic.DynamicLoad com.example.reflect.dynamic.Superman
输出结果如下
此时再添加一个类,也实现了接口的方法
1 2 3 4 5 6 public class Batman implements Person { @Override public void say () { System.out.println("I'm Batman!" ); } }
此时只需要编译这个文件即可
1 javac com/example/reflect/dynamic/Batman.java
运行
1 java com.example.reflect.dynamic.DynamicLoad com.example.reflect.dynamic.Batman
结果
这样可以使程序的灵活度更高。
获取类的方法名类的所有方法都是java.lang.reflect.Method
类的对象,可以使用Class类的.getMethods()
和.getDeclaredMethods()
方法获得,其中,前者可以获取该类和它继承的所有类的public
类型的方法,而后者可以获取该类全部的方法,公开私有保护方法。
而Method类中提供了查询函数信息的各种方法,常用的如下
getName() 获取方法名称
getReturnType() 获取返回值类型,也是一个Class类的对象
getParameterTypes() 获取方法参数,是一个数组,每个成员也是Class类的对象
下面的程序输出传入对象的所有方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public static void getClassMethodInfo (Object obj) { Class c = obj.getClass(); Method[] methods2 = c.getDeclaredMethods(); for (Method method : methods2) { Class returnType = method.getReturnType(); System.out.print(returnType.getSimpleName() + " " ); System.out.print(method.getName() + " ( " ); Class[] paramTypes = method.getParameterTypes(); for (int j = 0 ; j < paramTypes.length; j++) { System.out.print(paramTypes[j].getSimpleName()); if (j != paramTypes.length - 1 ) { System.out.print(", " ); } } System.out.println(" )" ); } }
如果传入的是String类型的,则输出如下,因为太多了,这里就选择靠前的
1 2 3 4 5 6 7 8 9 10 11 12 13 void checkPackageAccess ( ClassLoader, boolean ) Class forName ( String ) Class forName ( String, boolean, ClassLoader ) Class forName0 ( String, boolean, ClassLoader, Class ) String toString ( ) ProtectionDomain getProtectionDomain ( ) boolean isAssignableFrom ( Class ) boolean isInstance ( Object ) int getModifiers ( ) boolean isInterface ( ) boolean isArray ( ) boolean isPrimitive ( ) ...
获取该类成员的信息类的所有成员信息都是 java.lang.reflect.Field
类的对象,通过Class的.getFields()
和.getDeclaredFields()
,返回的是一个数组,和之前的一样,前者是获取包含父类父父类..的public类型成员,而后者只包含该类的全部成员,一下程序打印一个类的所有成员
1 2 3 4 5 6 7 8 9 10 11 12 public static void getClassMemberInfo (Class c) { Field[] fields = c.getDeclaredFields(); for (Field field : fields) { Class fieldType = field.getType(); String name = field.getName(); System.out.println(fieldType.getSimpleName() + " " + name); } }
如果是Integer类型的话输出如下
1 2 3 4 5 6 7 8 9 10 11 int MIN_VALUE int MAX_VALUE Class TYPE char[] digits char[] DigitTens char[] DigitOnes int[] sizeTable int value int SIZE int BYTES long serialVersionUID
获取该类构造函数的信息类的所有构造函数都是 java.lang.reflect.Constructor
类的对象,通过Class类的 .getDeclaredConstructors()
方法获得,一下程序打印出所有的构造方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public static void getClassConstructorInfo (Class c) { Constructor[] constructors = c.getDeclaredConstructors(); for (Constructor constructor : constructors) { String name = constructor.getName(); System.out.print(name + " ( " ); Class[] types = constructor.getParameterTypes(); for (int i = 0 ; i < types.length; i++) { System.out.print(types[i].getSimpleName()); if (i != types.length - 1 ) { System.out.print(", " ); } } System.out.println(" )" ); } }
如果是String类型的话输出如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 java.lang.String ( byte[], int, int ) java.lang.String ( byte[], Charset ) java.lang.String ( byte[], String ) java.lang.String ( byte[], int, int, Charset ) java.lang.String ( byte[], int, int, String ) java.lang.String ( char[], boolean ) java.lang.String ( StringBuilder ) java.lang.String ( StringBuffer ) java.lang.String ( byte[] ) java.lang.String ( int[], int, int ) java.lang.String ( ) java.lang.String ( char[] ) java.lang.String ( String ) java.lang.String ( char[], int, int ) java.lang.String ( byte[], int ) java.lang.String ( byte[], int, int, int )
用非常规方式操作类的实例先创建一个类吧
1 2 3 4 5 6 7 8 9 10 11 12 13 package com.example.jdbc.reflecttest;public class Runner { private String name; public Runner () { } public Runner (String name) { this .name = name; } public void run (int length) { System.out.println("I'm " + this .name + ", I've run " + length + "km!" ); } }
首先要对类进行实例化,这里不使用关键词 new
。 通过 Class.forName
方法获得该类,然后有两种选择,第一种是直接 newInstance
,不过这种方法无法调用有参构造方法,所以使用的类中必须要有无参构造方法,否则会报错;还有一种是获得它的构造函数,再通过它来初始化,方法分别如下
1 2 3 4 5 6 7 8 9 10 11 12 13 Class runnerClass = Class.forName("com.example.jdbc.reflecttest.Runner" ); Runner runner1 = (Runner) runnerClass.newInstance(); Field runner1Name = runnerClass.getDeclaredField("name" ); runner1Name.setAccessible(true ); runner1Name.set(runner1, "Runner1" ); Constructor runner2Constructor = runnerClass.getConstructor(String.class); Runner runner2 = (Runner) runner2Constructor.newInstance("Runner2" );
其次,要使用它的成员方法,也不直接调用,而是采用如下的手段
1 2 runnerClass.getMethod("run" , int .class).invoke(runner1, 12 ); runnerClass.getDeclaredMethod("run" , int .class).invoke(runner2, 21 );
两个方法的不同之处就是后者可以使用私有方法而前者不行
泛型的意义泛型的约束只在编译阶段有效,而在运行阶段时泛型就没有用了,测试程序如下
1 2 3 4 5 6 7 8 9 10 public static void main (String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { ArrayList<String> arr = new ArrayList<>(); ArrayList.class.getMethod("add" , Object.class).invoke(arr, 12 ); System.out.println(arr.size()); System.out.println(arr); }
这里先声明了一个类型为String的泛型数组,但是通过反射的技巧调用其add
方法向其中添加一个整形数,编译不会报错,因为反射仅限于运行阶段