Java之反射(二):运行时类

创建运行时类的对象

创建类的对象:调用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的方法的。

那么如何看待反射机制和面向对象中的封装性?
      首先,封装,是将具体的实现细节隐藏,而把功能作为整体提供给类的外部使用,也就是说,公有方法能够完成类所具有的功能。当别人使用这个类时,如果通过反射直接调用私有方法,可能根本实现不了类的功能,甚至可能会出错,因此通过反射调用私有方法可以说是没有任何用处的,开发人员没有必要故意去破坏封装好的类。从这点上看,封装性并没有被破坏。


若有不当之处,欢迎指正!!!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值