Constructor 的使用
Constructor类存在于反射包(java.lang.reflect)中,反映的是Class 对象所表示的类的构造方法。获取Constructor对象是通过Class类中的方法获取的,Class类与Constructor相关的主要方法如下
package reflect;import java.io.Serializable;
import java.lang.reflect.Constructor;public class ReflectDemo implements Serializable{public static void main(String[] args) throws Exception {Class> clazz = null;//获取Class对象的引用clazz = Class.forName("reflect.User");//实例化默认构造方法,User必须无参构造函数,否则将抛异常User user = (User) clazz.newInstance();user.setAge(20);user.setName("Rollen");System.out.println(user);System.out.println("--------------------------------------------");//获取带String参数的public构造函数Constructor cs1 =clazz.getConstructor(String.class);//创建UserUser user1= (User) cs1.newInstance("xiaolong");user1.setAge(22);System.out.println("user1:"+user1.toString());System.out.println("--------------------------------------------");//取得指定带int和String参数构造函数,该方法是私有构造privateConstructor cs2=clazz.getDeclaredConstructor(int.class,String.class);//由于是private必须设置可访问cs2.setAccessible(true);//创建user对象User user2= (User) cs2.newInstance(25,"lidakang");System.out.println("user2:"+user2.toString());System.out.println("--------------------------------------------");//获取所有构造包含privateConstructor> cons[] = clazz.getDeclaredConstructors();// 查看每个构造方法需要的参数for (int i = 0; i < cons.length; i++) {//获取构造函数参数类型Class> clazzs[] = cons[i].getParameterTypes();System.out.println("构造函数["+i+"]:"+cons[i].toString() );System.out.print("参数类型["+i+"]:(");for (int j = 0; j < clazzs.length; j++) {if (j == clazzs.length - 1)System.out.print(clazzs[j].getName());elseSystem.out.print(clazzs[j].getName() + ",");}System.out.println(")");}}
}@Data
class User {private int age;private String name;public User() {super();}public User(String name) {super();this.name = name;}/*** 私有构造* @param age* @param name*/private User(int age, String name) {super();this.age = age;this.name = name;}}
Constructor cs3=clazz.getDeclaredConstructor(int.class,String.class);System.out.println("-----getDeclaringClass-----");
Class uclazz=cs3.getDeclaringClass();
//Constructor对象表示的构造方法的类
System.out.println("构造方法的类:"+uclazz.getName());System.out.println("-----getGenericParameterTypes-----");
//对象表示此 Constructor 对象所表示的方法的形参类型
Type[] tps=cs3.getGenericParameterTypes();
for (Type tp:tps) {System.out.println("参数名称tp:"+tp);
}
System.out.println("-----getParameterTypes-----");
//获取构造函数参数类型
Class> clazzs[] = cs3.getParameterTypes();
for (Class claz:clazzs) {System.out.println("参数名称:"+claz.getName());
}
System.out.println("-----getName-----");
//以字符串形式返回此构造方法的名称
System.out.println("getName:"+cs3.getName());System.out.println("-----getoGenericString-----");
//返回描述此 Constructor 的字符串,其中包括类型参数。
System.out.println("getoGenericString():"+cs3.toGenericString());
/**输出结果:-----getDeclaringClass-----构造方法的类:reflect.User-----getGenericParameterTypes-----参数名称tp:int参数名称tp:class java.lang.String-----getParameterTypes-----参数名称:int参数名称:java.lang.String-----getName-----getName:reflect.User-----getoGenericString-----getoGenericString():private reflect.User(int,java.lang.String)*/
Field类及其用法
Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。同样的道理,我们可以通过Class类的提供的方法来获取代表字段信息的Field对象,Class类与Field对象相关方法如下:
public class ReflectField {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {Class> clazz = Class.forName("reflect.Student");//获取指定字段名称的Field类,注意字段修饰符必须为public而且存在该字段,// 否则抛NoSuchFieldExceptionField field = clazz.getField("age");System.out.println("field:"+field);//获取所有修饰符为public的字段,包含父类字段,注意修饰符为public才会获取Field fields[] = clazz.getFields();for (Field f:fields) {System.out.println("f:"+f.getDeclaringClass());}System.out.println("================getDeclaredFields====================");//获取当前类所字段(包含private字段),注意不包含父类的字段Field fields2[] = clazz.getDeclaredFields();for (Field f:fields2) {System.out.println("f2:"+f.getDeclaringClass());}//获取指定字段名称的Field类,可以是任意修饰符的自动,注意不包含父类的字段Field field2 = clazz.getDeclaredField("desc");System.out.println("field2:"+field2);}/**输出结果: field:public int reflect.Person.agef:public java.lang.String reflect.Student.descf:public int reflect.Person.agef:public java.lang.String reflect.Person.name================getDeclaredFields====================f2:public java.lang.String reflect.Student.descf2:private int reflect.Student.scorefield2:public java.lang.String reflect.Student.desc*/
}class Person{public int age;public String name;//省略set和get方法
}class Student extends Person{public String desc;private int score;//省略set和get方法
}
上述方法需要注意的是,如果我们不期望获取其父类的字段,则需使用Class类的getDeclaredField/getDeclaredFields方法来获取字段即可,倘若需要连带获取到父类的字段,那么请使用Class类的getField/getFields,但是也只能获取到public修饰的的字段,无法获取父类的私有字段。下面将通过Field类本身的方法对指定类属性赋值,代码演示如下:
//获取Class对象引用
Class> clazz = Class.forName("reflect.Student");Student st= (Student) clazz.newInstance();
//获取父类public字段并赋值
Field ageField = clazz.getField("age");
ageField.set(st,18);
Field nameField = clazz.getField("name");
nameField.set(st,"Lily");//只获取当前类的字段,不获取父类的字段
Field descField = clazz.getDeclaredField("desc");
descField.set(st,"I am student");
Field scoreField = clazz.getDeclaredField("score");
//设置可访问,score是private的
scoreField.setAccessible(true);
scoreField.set(st,88);
System.out.println(st.toString());//输出结果:Student{age=18, name='Lily ,desc='I am student', score=88} //获取字段值
System.out.println(scoreField.get(st));
// 88
Method类及其用法
Method 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息,所反映的方法可能是类方法或实例方法(包括抽象方法)。下面是Class类获取Method对象相关的方法:
import java.lang.reflect.Method;public class ReflectMethod {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {Class clazz = Class.forName("reflect.Circle");//根据参数获取public的Method,包含继承自父类的方法Method method = clazz.getMethod("draw",int.class,String.class);System.out.println("method:"+method);//获取所有public的方法:Method[] methods =clazz.getMethods();for (Method m:methods){System.out.println("m::"+m);}System.out.println("=========================================");//获取当前类的方法包含private,该方法无法获取继承自父类的methodMethod method1 = clazz.getDeclaredMethod("drawCircle");System.out.println("method1::"+method1);//获取当前类的所有方法包含private,该方法无法获取继承自父类的methodMethod[] methods1=clazz.getDeclaredMethods();for (Method m:methods1){System.out.println("m1::"+m);}}/**输出结果:method:public void reflect.Shape.draw(int,java.lang.String)m::public int reflect.Circle.getAllCount()m::public void reflect.Shape.draw()m::public void reflect.Shape.draw(int,java.lang.String)m::public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedExceptionm::public final native void java.lang.Object.wait(long) throws java.lang.InterruptedExceptionm::public final void java.lang.Object.wait() throws java.lang.InterruptedExceptionm::public boolean java.lang.Object.equals(java.lang.Object)m::public java.lang.String java.lang.Object.toString()m::public native int java.lang.Object.hashCode()m::public final native java.lang.Class java.lang.Object.getClass()m::public final native void java.lang.Object.notify()m::public final native void java.lang.Object.notifyAll()=========================================method1::private void reflect.Circle.drawCircle()m1::public int reflect.Circle.getAllCount()m1::private void reflect.Circle.drawCircle()*/
}class Shape {public void draw(){System.out.println("draw");}public void draw(int count , String name){System.out.println("draw "+ name +",count="+count);}}
class Circle extends Shape{private void drawCircle(){System.out.println("drawCircle");}public int getAllCount(){return 100;}
}
在通过getMethods方法获取Method对象时,会把父类的方法也获取到,如上的输出结果,把Object类的方法都打印出来了。而getDeclaredMethod/getDeclaredMethods方法都只能获取当前类的方法。我们在使用时根据情况选择即可。下面将演示通过Method对象调用指定类的方法:
Class clazz = Class.forName("reflect.Circle");
//创建对象
Circle circle = (Circle) clazz.newInstance();//获取指定参数的方法对象Method
Method method = clazz.getMethod("draw",int.class,String.class);//通过Method对象的invoke(Object obj,Object... args)方法调用
method.invoke(circle,15,"圈圈");//对私有无参方法的操作
Method method1 = clazz.getDeclaredMethod("drawCircle");
//修改私有方法的访问标识
method1.setAccessible(true);
method1.invoke(circle);//对有返回值得方法操作
Method method2 =clazz.getDeclaredMethod("getAllCount");
Integer count = (Integer) method2.invoke(circle);
System.out.println("count:"+count);/**输出结果:draw 圈圈,count=15drawCirclecount:100
*/
反射包中的Array类
在Java的java.lang.reflect包中存在着一个可以动态操作数组的类,Array,它提供了动态创建和访问 Java 数组的方法。Array 允许在执行 get 或 set 操作进行取值和赋值。在Class类中与数组关联的方法是:
package reflect;import java.lang.reflect.Array;public class ReflectArray {public static void main(String[] args) throws ClassNotFoundException {int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };//获取数组类型的Class 即int.classClass> clazz = array.getClass().getComponentType();//创建一个具有指定的组件类型和长度的新数组。//第一个参数:数组的类型,第二个参数:数组的长度Object newArr = Array.newInstance(clazz, 15);//获取原数组的长度int co = Array.getLength(array);//赋值原数组到新数组System.arraycopy(array, 0, newArr, 0, co);for (int i:(int[]) newArr) {System.out.print(i+",");}//创建了一个长度为10 的字符串数组,//接着把索引位置为6 的元素设为"hello world!",然后再读取索引位置为6 的元素的值Class clazz2 = Class.forName("java.lang.String");//创建一个长度为10的字符串数组,在Java中数组也可以作为Object对象Object array2 = Array.newInstance(clazz2, 10);//把字符串数组对象的索引位置为6的元素设置为"hello"Array.set(array2, 6, "hello world!");//获得字符串数组对象的索引位置为5的元素的值String str = (String)Array.get(array2, 6);System.out.println();System.out.println(str);//hello}/**输出结果:1,2,3,4,5,6,7,8,9,0,0,0,0,0,0,hello world!*/
}
通过上述代码演示,确实可以利用Array类和反射相结合动态创建数组,也可以在运行时动态获取和设置数组中元素的值,其实除了上的set/get外Array还专门为8种基本数据类型提供特有的方法,如setInt/getInt、setBoolean/getBoolean,其他依次类推,需要使用是可以查看API文档即可。除了上述动态修改数组长度或者动态创建数组或动态获取值或设置值外,可以利用泛型动态创建泛型数组如下:
/*** 接收一个泛型数组,然后创建一个长度与接收的数组长度一样的泛型数组,* 并把接收的数组的元素复制到新创建的数组中,* 最后找出新数组中的最小元素,并打印出来* @param a* @param */public > void min(T[] a) {//通过反射创建相同类型的数组T[] b = (T[]) Array.newInstance(a.getClass().getComponentType(), a.length);for (int i = 0; i < a.length; i++) {b[i] = a[i];}T min = null;boolean flag = true;for (int i = 0; i < b.length; i++) {if (flag) {min = b[i];flag = false;}if (b[i].compareTo(min) < 0) {min = b[i];}}System.out.println(min);}
ok~,到这反射中几个重要并且常用的类我们都基本介绍完了,但更重要是,我们应该认识到反射机制并没有什么神奇之处。当通过反射与一个未知类型的对象打交道时,JVM只会简单地检查这个对象,判断该对象属于那种类型,同时也应该知道,在使用反射机制创建对象前,必须确保已加载了这个类的Class对象,当然这点完全不必由我们操作,毕竟只能JVM加载,但必须确保该类的”.class”文件已存在并且JVM能够正确找到。关于Class类的方法在前面我们只是分析了主要的一些方法,其实Class类的API方法挺多的,建议查看一下API文档,浏览一遍,有个印象也是不错的选择,这里仅列出前面没有介绍过又可能用到的API:
/** * 修饰符、父类、实现的接口、注解相关 *///获取修饰符,返回值可通过Modifier类进行解读
public native int getModifiers();
//获取父类,如果为Object,父类为null
public native Class super T> getSuperclass();
//对于类,为自己声明实现的所有接口,对于接口,为直接扩展的接口,不包括通过父类间接继承来的
public native Class>[] getInterfaces();
//自己声明的注解
public Annotation[] getDeclaredAnnotations();
//所有的注解,包括继承得到的
public Annotation[] getAnnotations();
//获取或检查指定类型的注解,包括继承得到的
public A getAnnotation(Class annotationClass);
public boolean isAnnotationPresent(Class extends Annotation> annotationClass);/** * 内部类相关*/
//获取所有的public的内部类和接口,包括从父类继承得到的
public Class>[] getClasses();
//获取自己声明的所有的内部类和接口
public Class>[] getDeclaredClasses();
//如果当前Class为内部类,获取声明该类的最外部的Class对象
public Class> getDeclaringClass();
//如果当前Class为内部类,获取直接包含该类的类
public Class> getEnclosingClass();
//如果当前Class为本地类或匿名内部类,返回包含它的方法
public Method getEnclosingMethod();/** * Class对象类型判断相关*/
//是否是数组
public native boolean isArray();
//是否是基本类型
public native boolean isPrimitive();
//是否是接口
public native boolean isInterface();
//是否是枚举
public boolean isEnum();
//是否是注解
public boolean isAnnotation();
//是否是匿名内部类
public boolean isAnonymousClass();
//是否是成员类
public boolean isMemberClass();
//是否是本地类
public boolean isLocalClass();