【重学Java五】反射

举报
莫逸风 发表于 2022/07/26 15:11:16 2022/07/26
【摘要】 在Java8之前,如果一个接口想要添加新的方法,那么要修改所有实现了该接口的类,让它们都实现新增的方法。HashSet和HashMap等集合类使用了hashCode()方法来计算对象应该存储的位置,因此要将对象添加到这些集合类中,需要让对应的类实现hashCode()方法。clone()是Object的protected方法,它不是public,一个类不显式去重写clone(),其它类就不能直接去

反射

每个类都有一个 Class 对象,包含了与类有关的信息。当编译一个新类时,会产生一个同名的 .class 文件,该文件内容保存着 Class 对象。

1. Class类

获取一个类的Class对象的三种方式

// 每个类都有一个隐含的静态变量
Class c1 = Apple.class;
// 通过实例化对象的getClass()方法
Apple apple = new Apple();
Class c2 = apple.getClass();
// 通过类的全限定名
Class c3 = Class.forName("com.myf.pojo.Apple");

判断是否是某个类的实例

我们可以借助反射中 Class 对象的 isInstance() 方法来判断是否为某个类的实例,它是一个 native 方法:

public native boolean isInstance(Object obj);

通过反射生成对象

使用Class对象的newInstance()方法来创建Class对象对应类的实例。

下面示例使用的是有参构造,默认调用无参构造orangeClass.getDeclaredConstructor().newInstance()。Class.newInstance()方法在java9之后被Deprecated不推荐了。

Orange orange = new Orange("莫逸风");
Class<? extends Orange> orangeClass = orange.getClass();
Orange orange1 = orangeClass.getDeclaredConstructor(String.class).newInstance("莫逸风");

示例实体:

class Orange {
    String name;

    public Orange(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
2. java.lang.reflect

Class 和 java.lang.reflect 一起对反射提供了支持,java.lang.reflect 类库主要包含了以下三个类:

  • Field :可以使用 get() 和 set() 方法读取和修改 Field 对象关联的字段;
  • Method :可以使用 invoke() 方法调用与 Method 对象关联的方法;
  • Constructor :可以用 Constructor 的 newInstance() 创建新的对象。
3. Class API:
  • Field[] getFields():返回一个包含Field对象的数组,该数组包含这个类或其超类的公有域。
  • Field[] getDeclaredFields():返回这个类的全部域。如果类型没有成员变量则返回长度为0的数组。
  • Method[] getMethods():返回包含Method对象的数组,该数组包含这个类或其超类的公有方法。
  • Method[] getDeclaredMethods():返回这个类或接口的全部方法,但不包括由超类继承的方法(公有方法也没有)。
  • Constructor<?>[] getConstructors():类的所有公有有构造器
  • Constructor<?>[] getDeclaredConstructors():类的所有构造器
3. Field:
  • Object get(Object obj):获取变量的值,入参为实例对象。

    Orange orange = orangeClass.getDeclaredConstructor(String.class).newInstance("莫逸风");
    Field[] fields = orangeClass.getDeclaredFields();
    Object o = fields[0].get(orange);
    System.out.println(o);	//莫逸风
    
  • void set(Object obj, Object value):设置变量的值。

    fields[0].set(orange,"莫逸雪");
    Object o = fields[0].get(orange);
    System.out.println(o);	//莫逸雪
    
    
  • Class<?> getType():返回属性的类型。

    Class<?> aClass = fields[0].getType();
    System.out.println(aClass==String.class);  //true
    
  • void setAccessible(boolean flag):设置属性的访问权限。

    如果我们将上述示例属性值改为private,则在get,set反射操作变量时会报IllegalAccessException异常。在操作之前设置setAccessible(true)可以解决这个问题。

    fields[0].setAccessible(true);
    
  • String getName():返回变量名称。

    String name = fields[0].getName();
    System.out.println(name);		//name
    
4. Method:
  • void setAccessible(boolean flag):设置方法的访问权限。

  • String getName():返回方法名称。

  • <T extends Annotation> T getAnnotation(Class<T> annotationClass):如果存在,返回对应的注解

    没找到合适测试的注解,自定义一个

    @Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Tag {
        String value();
    }
    
    // Orange的getName方法添加此注解
    @Tag("设置名称")
    public String getName() {
      return name;
    }
    
    Method[] methods = orangeClass.getMethods();
    Tag annotation = methods[0].getAnnotation(Tag.class);
    System.out.println(annotation.value()); //设置名称
    
  • Annotation[] getAnnotations():内部实现是调用getDeclaredAnnotations

  • Annotation[] getDeclaredAnnotations():返回方法上的注解,忽略继承的注解,如果没有直接存在的注解返回长度为0的注解。

  • Object invoke(Object obj, Object... args):执行方法。

    Orange orange = orangeClass.getDeclaredConstructor(String.class).newInstance("莫逸风");
    methods[1].invoke(orange1,"莫逸雪");
    Object invoke = methods[0].invoke(orange1);
    System.out.println(invoke);  //莫逸雪
    
5. Constructor:
  • T newInstance(Object ... initargs):使用构造方法创建对象。

  • Class<T> getDeclaringClass():获取所属类的Class对象。

    Class<? extends Orange> orangeClass = orange.getClass();
    Class<?> declaringClass = constructors[0].getDeclaringClass();
    System.out.println(declaringClass==orangeClass); // true
    
  • void setAccessible(boolean flag):设置构造器的访问权限。

  • int getModifiers():返回一个用于描述构造器、方法或域的修饰符的整形数值。使用

6. Modifier:
// Orange中添加第二个变量
public static String test;

int modifiers = fields[1].getModifiers();
System.out.println(modifiers);													// 9
System.out.println(Modifier.toString(modifiers));				// public static
System.out.println(Modifier.isAbstract(modifiers));			// false
System.out.println(Modifier.isFinal(modifiers));				// false
System.out.println(Modifier.isInterface(modifiers));		// false
System.out.println(Modifier.isNative(modifiers));				// false
System.out.println(Modifier.isPrivate(modifiers));			// false
System.out.println(Modifier.isProtected(modifiers));		// false
System.out.println(Modifier.isPublic(modifiers));				// true
System.out.println(Modifier.isStatic(modifiers));				// true
System.out.println(Modifier.isStrict(modifiers));				// false
System.out.println(Modifier.isSynchronized(modifiers));	// false
System.out.println(Modifier.isVolatile(modifiers));			// false
  • static String toString(int mod):返回对应的修饰符
  • static boolean isAbstract(int modifiers)
  • static boolean isFinal(int modifiers)
  • static boolean isInterface(int modifiers)
  • static boolean isNative(int modifiers)
  • static boolean isPrivate(int modifiers)
  • static boolean isProtected(int modifiers)
  • static boolean isPublic(int modifiers)
  • static boolean isStatic(int modifiers)
  • static boolean isStrict(int modifiers)
  • static boolean isSynchronized(int modifiers)
  • static boolean isVolatile(int modifiers)
7. 结语

由于反射会额外消耗一定的系统资源,因此如果不需要动态地创建一个对象,那么就不需要用反射。

另外,反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。