创建运行时类的对象
创建类的对象:调用Class对象的newInstance()方法
要求:
1)类必须有一个无参数的构造器。
2)类的构造器的访问权限需要足够。
难道没有无参的构造器就不能创建对象了吗?
不是!
只要在操作的时候明确的调用类中的构造器,并将参数传递进去之后,才可以实例化操作。
步骤如下:
1)通过Class类的getDeclaredConstructor(Class … parameterTypes)取得本类的指定 形参类型的构造器
2)向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。
3)通过Constructor实例化对象。
获取运行时类的完整结构
1、取得类中的属性
- public Field[] getFields()
返回此Class对象所表示的类或接口的public的Field。 - public Field[] getDeclaredFields()
返回此Class对象所表示的类或接口的全部Field。
Field方法中:
- public int getModifiers() 以整数形式返回此Field的修饰符
- public Class<?> getType() 得到Field的属性类型
- public String getName() 返回Field的名称。
eg:
Class<Person> clazz = Person.class;
Field[] f1 = clazz.getFields();
for(Field f : f1) {
System.out.println(f);
}
System.out.println();
Field[] f2 = clazz.getDeclaredFields();
for(Field f : f2) {
System.out.println(f);
}
*********************************
for(Field f : f2) {
//权限修饰符
int medifier = f.getModifiers();
System.out.println(Modifier.toString(medifier));
//数据类型
Class type = f.getType();
System.out.println(type.getName());
//变量名
String fName = f.getName();
System.out.println(fName);
}
2、获取类中方法
-
public Method[] getDeclaredMethods()
返回此Class对象所表示的类或接口的全部方法 -
public Method[] getMethods()
返回此Class对象所表示的类或接口的public的方法 (不包括父类中声明的属性)
Method类中:
- public Class<?> getReturnType():取得全部的返回值
- public Class<?>[] getParameterTypes():取得全部的参数
- public int getModifiers():取得修饰符 public
- Class<?>[] getExceptionTypes():取得异常信息
Class clazz = Person.class;
Method[] method = clazz.getDeclaredMethods();
for(Method m : method) {
//1.获取方法声明中的注解
Annotation[] anos = m.getAnnotations();
for(Annotation a : anos) {
System.out.println(a);
}
//2.方法的权限修饰符
System.out.println(Modifier.toString(m.getModifiers()));
//3.方法的返回值类型
System.out.println(m.getReturnType());
//4.方法的方法名
System.out.println(m.getName() + "\t");
//5.方法的参数
Class[] p = m.getParameterTypes();
if(!(p==null && p.length==0)) {
for(Class c : p) {
System.out.println(c.getName());
}
}
//6.获取异常信息
Class[] e = m.getExceptionTypes();
if(!(e==null && e.length==0)) {
for(int i=0;i<e.length;i++) {
if(i == e.length-1) {
System.out.println(e[i].getName());
}
System.out.println(e[i].getName() + ",");
}
}
}
3、获取类中的构造器
- public Constructor[] getConstructors()
返回此 Class 对象所表示的类的所有public构造方法。 - public Constructor[] getDeclaredConstructors()
返回此 Class 对象表示的类声明的所有构造方法。
Constructor类中:
- 取得修饰符: public int getModifiers();
- 取得方法名称: public String getName();
- 取得参数的类型:public Class<?>[] getParameterTypes();
//调用了无参的构造器
Class clazz = Person.class;
Constructor cons = clazz.getDeclaredConstructor();
cons.setAccessible(true);
Person p = (Person) cons.newInstance();
System.out.println(p);
//调用了有参的构造器
Class clazz = Person.class;
Constructor cons = clazz.getDeclaredConstructor(String.class); //指明形参列表
cons.setAccessible(true);
Person p = (Person) cons.newInstance("abcdefg");
System.out.println(p);
4、泛型相关
- 获取父类泛型类型:Type getGenericSuperclass()
- 泛型类型:ParameterizedType
- 获取实际的泛型类型参数数组:getActualTypeArguments()
Class clazz = Person.class;
Type superclass = clazz.getGenericSuperclass();
ParameterizedType paramType = (ParameterizedType) superclass;
//获取泛型类型
Type[] p = paramType.getActualTypeArguments();
System.out.println(p[0]);
5、实现的全部接口
- public Class<?>[] getInterfaces()
确定此对象所表示的类或接口实现的接口。
6、所继承的父类
- public Class<? Super T> getSuperclass()
返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的Class
7、类所在的包
- Package getPackage()
8、Annotation相关
- get Annotation(Class annotationClass)
- getDeclaredAnnotations()
调用指定属性
在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的set()和get()方法就可以完成设置和取得属性内容的操作。
- public Field getField(String name)
返回此Class对象表示的类或接口的指定的public的Field。 - public Field getDeclaredField(String name)
返回此Class对象表示的类或接口的指定的Field。
在Field中:
- public Object get(Object obj)
取得指定对象obj上此Field的属性内容 - public void set(Object obj,Object value)
设置指定对象obj上此Field的属性内容
Class clazz = Person.class;
Person p1 = (Person) clazz.newInstance();
Field field = clazz.getDeclaredField("name");
//保证当前属性是可访问的
field.setAccessible(true);
field.set(p1, "abc");
String ans = (String) field.get(p1);
System.out.println(ans);
注:
关于setAccessible方法的使用
Method和Field、Constructor对象都有setAccessible()方法。
setAccessible启动和禁用访问安全检查的开关。
参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。
提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true。
使得原本无法访问的私有成员也可以访问
参数值为false则指示反射的对象应该实施Java语言访问检查。
调用指定的方法
Object invoke(Object obj, Object … args)
说明:
1.Object 对应原方法的返回值,若原方法无返回值,此时返回null
2.若原方法若为静态方法,此时形参Object obj可为null
3.若原方法形参列表为空,则Object[] args为null
4.若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法。
通过反射,调用类中的方法,通过Method类完成。
步骤:
1.通过Class类的getMethod(String name,Class…parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。
2.之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。
Class clazz = Person.class;
Person p1 = (Person) clazz.newInstance();
Method m = clazz.getDeclaredMethod("func");
m.setAccessible(true);
m.invoke(p1); //m.invoke(p1,null);
m = clazz.getDeclaredMethod("printf",String.class);
m.setAccessible(true);
m.invoke(p1, "abc");
m = clazz.getDeclaredMethod("func2",int.class);
m.setAccessible(true);
m.invoke(null, 123); //m.invoke(p1,123);
Method m =clazz.getDeclaredMethod("func3", String.class,int.class);
m.setAccessible(true);
m.invoke(p1, "abc",10);
关于反射的一些问题:
说了这么多,那么通过直接new的方式或反射的方式都可以调用公共的结构,开发中用哪个?
这里建议是用new的方法的。
那么如何看待反射机制和面向对象中的封装性?
首先,封装,是将具体的实现细节隐藏,而把功能作为整体提供给类的外部使用,也就是说,公有方法能够完成类所具有的功能。当别人使用这个类时,如果通过反射直接调用私有方法,可能根本实现不了类的功能,甚至可能会出错,因此通过反射调用私有方法可以说是没有任何用处的,开发人员没有必要故意去破坏封装好的类。从这点上看,封装性并没有被破坏。
若有不当之处,欢迎指正!!!