Java反射机制详解

获取抖音中x-secsdk-csrf-token的方法 阅读详情

前言

    本文是我整理的Java反射的一些知识,其中大部分内容是翻译http://tutorials.jenkov.com/java-reflection/index.html的。

1. Java反射简介

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。

Java反射机制是Java语言被视为“准动态”语言的关键性质。Java反射机制的核心就是允许在运行时通过Java Reflection APIs来取得已知名字的class类的内部信息(包括其modifiers(诸如public, static等等)、superclass(例如Object)、实现interfaces(例如Serializable),也包括fields和methods的所有信息),动态地生成此类,并调用其方法或修改其域(甚至是本身声明为private的域或方法)。

Java反射机制主要提供了以下功能:在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。

2. Class对象

Class对象是Java反射的基础,它包含了与类相关的信息,事实上,Class对象就是用来创建类的所有对象的。Class对象是java.lang.Class<T>这个类生成的对象,其中类型参数T表示由此 Class 对象建模的类的类型。例如,String.class的类型是 Class<String>;如果将被建模的类未知,则使用Class<?>。以下是Java API的描述:

Class类的实例表示正在运行的 Java应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为Class对象的一个类,所有具有相同元素类型和维数的数组都共享该Class对象。基本的Java类型(boolean、byte、char、short、int、long、float和double)和关键字void也表示为Class对象。

Class没有公共构造方法。Class对象是在加载类时由Java虚拟机以及通过调用类加载器中的defineClass方法自动构造的。

实际上,每个类都有一个Class对象。换言之,每当编写并且编译了一个新类,就会产生一个Class对象(更恰当的说,是被保存在一个同名的.class文件中)。如果我们想生成这个类的对象,运行这个程序的Java虚拟机(JVM)将使用类加载器子系统,类加载器首先检查这个类的Class对象是否已经加载。如果尚未加载,默认的类加载器就会根据类名查找.class文件,并将其载入,一旦某个类的Class对象被载入内存,它就被用来创建这个类的所有对象。

获取Class对象有三种方式:

(1)    通过实例变量的getClass()方法。例如:

Class c1 = new String("abc").getClass();

(2)    通过Class类的静态方法——forName()来实现,例如:

Class class =Class.forName(className);

注意:当使用Class.forName()方法时,你必须提供完全限定类名。即类名要包括所有包

名。例如,如果MyObject是位于包com.jenkov.myapp下,那么类的完全限定名称是com.jenkov.myapp.MyObject。如果在运行时类路径上找不到类,Class.forName()方法会抛出一个ClassNotFoundException。

(3)  使用类字面常量或TYPE字段,例如:

Class myObjectClass= MyObject.class;(类字面常量不仅可以应用于普通的类,也可以应用

于接口、数组以及基本数据类型),这种方式不仅更简单,而且更安全,因为它在编译时就会受到检查,并且根除了对forName方法的调用,所以也更高效,建议使用“.class”的形式。

Class c = Integer.TYPE;(TYPE是基本数据类型的包装类型的一个标准字段,它是一

个引用,指向对应的基本数据类型的Class对象),附表如下,两边等价:

boolean.class

Boolean.TYPE

char.class

Character.TYPE

byte.class

Byte.TYPE

short.class

Short.TYPE

int.class

Integer.TYPE

long.class

Long.TYPE

float.class

Float.TYPE

double.class

Double.TYPE

void.class

Void.TYPE

 

3. 类

使用Java反射,你可以在运行时检查Java类。检查类是使用反射时经常做的第一件事情。从类中可以获取以下信息:

(1) 类名

(2) 类修饰符 (public, private, synchronized等)

(3) 包信息

(4) 父类

(5) 实现的接口

(6) 构造函数

(7) 方法

(8) 字段

(9) 注解

3.1类名

    从Class对象中可以获取两个不同的类名。完全限定类名(包括包名)可以使用getName()或getCanonicalName()方法获取,例如:

Class aClass = MyObject.class;
String className = aClass.getName();
String className1 = aClass.getCanonicalName();

如果想要获取不含包名的类名可以使用getSimpleName() 方法,如下:

Class  aClass = MyObject.class;
     String simpleClassName = aClass.getSimpleName();

3.2修饰符

使用Class对象可以获取一个类的修饰符.类的修饰符即关键字"public","private", "static"等. 如下:

    Class aClass = MyObject.class;
    int modifiers = aClass.getModifiers();

修饰符被包装进一个int内,每一个修饰符都是一个标志位(置位或清零)。可以使用java.lang.reflect.Modifier类中的以下方法来检验修饰符:

    Modifier.isAbstract(int modifiers)
    Modifier.isFinal(int modifiers)
    Modifier.isInterface(int modifiers)
    Modifier.isNative(int modifiers)
    Modifier.isPrivate(int modifiers)
    Modifier.isProtected(int modifiers)
    Modifier.isPublic(int modifiers)
    Modifier.isStatic(int modifiers)
    Modifier.isStrict(int modifiers)
    Modifier.isSynchronized(int modifiers)
    Modifier.isTransient(int modifiers)
    Modifier.isVolatile(int modifiers)

3.3包信息

使用Class对象可以获取包信息,如下:

Class aClass = MyObject.class;
Package package = aClass.getPackage();
String packageName = package.getname();

从Package对象中你可以访问诸如名字等包信息。您还可以访问类路径上这个包位于JAR文件中Manifest这个文件中指定的信息。例如,你可以在Manifest文件中指定包的版本号。可以在java.lang.Package中了解更多包类信息。

3.4父类

通过Class对象可以获取类的父类,如下:

Class  aClass = MyObject.class;
Class superclass = aClass.getSuperclass();

父类的Class对象和其它Class对象一样是一个Class对象,可以继续使用反射.

3.5实现的接口

通过给定的类可以获取这个类所实现的接口列表,如下:

Class aClass = MyObject.class;
Class[] interfaces = aClass.getInterfaces();

一个类可以实现多个接口。因此返回一个Class数组。在Java反射机制中,接口也由Class对象表示。

注意:只有给定类声明实现的接口才会返回。例如,如果类A的父类B实现了一个接口C,但类A并没有声明它也实现了C,那么C不会被返回到数组中。即使类A实际上实现了接口C,因为它的父类B实现了C。

为了得到一个给定的类实现接口的完整列表,需要递归访问类和其超类。

3.6构造函数

使用Class对象可以获取类的构造函数,如下:

     Class aClass = MyObject.class;
Constructor[] constructors = aClass.getConstructors();

关于构造函数更详细信息参见 构造函数这节。

3.7方法

使用Class对象可以获取类的方法,如下:

     Class aClass = MyObject.class;
Method[] methods = aClass.getMethods();

关于方法更详细信息参见方法这节.

3.8字段

使用Class对象可以获取类的字段(成员变量),如下:

      Class aClass = MyObject.class;
Field[] fields = aClass.getFields();

关于字段更详细信息参见 字段这节.

3.9注解

使用Class对象可以获取类的注解,如下:

      Class aClass = MyObject.class;
Annotation[] annotations = aClass.getAnnotations();

关于注解更详细信息参见注解这节.

4. 构造函数

使用Java反射可以在运行时检查类的构造函数并实例化对象。这是通过Java类java.lang.reflect.Constructor来实现的。以下是Java Construcor对象的更多细节:

(1) 获取Constructor对象

(2) 构造函数参数

(3) 使用Constructor对象实例化对象

4.1获取Constructor对象

 Constructor类是从Class对象获取的,举例:

Class aClass = MyObject.class;
Constructor[] constructors = aClass.getConstructors();

Constructor数组为每一个在类中声明的public构造函数保存一个Constructor实例。

如果知道要访问的构造函数确切的参数类型,可以不获取构造函数数组。本示例将返回给定类中接受一个字符串作为参数的公共构造函数。

Class aClass = MyObject.class;//MyObject有一个参数为字符串的公共构造函数
Constructor constructor = aClass.getConstructor(new Class[]{String.class});

如果没有匹配给定的构造函数参数,在这个例子当中是String.class,会抛出 NoSuchMethodException 异常.

4.2构造函数参数

可以知道给定的构造函数接受什么参数,如下:

Class aClass = MyObject.class;//MyObject有一个参数为字符串的公共构造函数
Constructor constructor = aClass.getConstructor(new Class[]{String.class});
Class[] parameterTypes = constructor.getParameterTypes();

4.3使用Constructor对象实例化对象

可以像这样实例化对象:

//获取使用字符串作为参数的constructor
Constructor constructor = MyObject.class.getConstructor(String.class);
MyObject myObject = (MyObject)constructor.newInstance("constructor-arg1");

Constructor.newInstance() 方法使用可变长度的参数,但是在调用构造函数时必须为每一个参数提供一个准确的参量.在这个例子中构造函数接受一个字符串作为参数,所以必须要提供一个字符串。

5. 字段

使用Java反射你可以在运行时检查类的字段(成员变量)并 get / set它们.这是通过Java类java.lang.reflect.Field来完成的.以下是JavaField对象更多细节:

(1) 获取Field 对象

(2) 字段名称

(3) 字段类型

(4) 获取和设置字段值

5.1获取Field 对象

     Field类是从Class对象获取的.举例:

Class aClass = MyObject.class;
Field[] methods = aClass.getFields();

Field数组为类中声明的每一个public字段保存一个Field 实例。

如果知道要访问的字段名称,可以这样获取它:

Class aClass = MyObject.class
Field field = aClass.getField("someField");

根据下面 MyObject 声明的someField字段,以上的例子将返回Field实例:

public class MyObject{
       public String someField = null;
}

如果不存在getField()方法中所给参数名字对应的字段,会抛出NoSuchFieldException异常.

5.2字段名称

一旦获得一个 Field实例,可以使用Field.getName()方法获取它的字段名字,如下:

Field field = ... //获取 field 对象
String fieldName = field.getName();

5.3字段类型

你可以使用Field.getType()方法确定一个字段的类型(String,int等):

Field field = aClass.getField("someField");
Object fieldType = field.getType();

5.4获取和设置字段值

一旦获得一个Field引用,可以使用Field.get() 和 Field.set()方法获取和设置它的值,如下:

Class aClass = MyObject.class
Field field = aClass.getField("someField");
MyObject objectInstance = new MyObject();
Object value = field.get(objectInstance);
field.set(objetInstance, value);

传递给get和set方法的对象实例参数应该是拥有该字段的类的一个实例。在上述的例子中使用了MyObject的一个实例,因为someField是MyObject类的一个实例成员。静态字段(public static)给get和set方法传递null作为参数,而不是以上传递的objectInstance参数。

6. 方法

使用Java反射你可以在运行时检查类的方法并调用它们.这是通过Java类java.lang.reflect.Method来实现的.本章将会更详细的介绍 Java Method对象.下面是本章所涵盖的主题:

(1) 获取 Method对象

(2) 方法的参数和返回值类型

(3) 使用Method对象调用方法

6.1获取Method对象

Method类是从 Class 对象中获取的.举例:

Class aClass = MyObject.class;
Method[] methods = aClass.getMethods();

 Method数组将为类中声明的每一个public的方法保存一个Method实例.

如果你知道要访问方法的确切的参数类型,可以不必获取方法数组。本示例返回给定类中接受一个字符串作为参数的公共方法”doSomething”。

Class  aClass = MyObject.class;
Method method = aClass.getMethod("doSomething", new Class[]{String.class});

如果没有方法匹配所给的方法名和参数,在这个例子中是String.class,将抛出NoSuchMethodException异常.

如果你想访问的方法没有参数,传递 null作为参数类型数组,如下:

Class  aClass = MyObject.class;
Method method = aClass.getMethod("doSomething", null);

6.2方法的参数和返回值类型

使用Method对象可以获取方法的参数,如下:

Method method = ... //获取 method – 如上
Class[] parameterTypes = method.getParameterTypes();

亦可以获取方法的返回值类型,如下:

Method method = ... // obtain method - see above
Class returnType = method.getReturnType();

6.3使用Method对象调用方法

可以像这样调用方法:

//get method that takes a String as argument
Method method = MyObject.class.getMethod("doSomething", String.class);
Object returnValue = method.invoke(null, "parameter-value1");

空参数是你想要调用该方法的对象。如果该方法是静态的,使用null,而不是一个对象实例。在这个例子中,如果doSomething(String.class)不是静态的,你需要提供有效的MyObject实例而不是null作为参数;
    Method.invoke(Object target, Object ...parameters)方法接受可变长度的参数,但是你在调用时必须为每一个参数提供一个准确的参量。在这个例子中,方法以字符串作为参数的,所以必须提供一个字符串。

7. get和set方法

    使用Java反射可以在运行时检查类的方法并调用它们。这可以用来检测一个给定的类有哪些get和set方法。可以通过扫描一个类的所有方法并检查每个方法是否是get或set方法。

下面是一段用来找到类的get和set方法的代码:

public staticvoid printGettersSetters(Class aClass){

        Method[]methods = aClass.getMethods();

        for(Methodmethod : methods){

           if(isGetter(method))System.out.println("getter: " + method);

           if(isSetter(method))System.out.println("setter: " + method);

        }

}

public staticboolean isGetter(Method method){

        if(!method.getName().startsWith("get"))      return false;

        if(method.getParameterTypes().length!= 0)   return false; 

        if(void.class.equals(method.getReturnType())return false;

        returntrue;

}

public staticboolean isSetter(Method method){

        if(!method.getName().startsWith("set"))return false;

        if(method.getParameterTypes().length!= 1) return false;

        return true;

}

8. 私有字段和方法

可以通过Java反射访问类的私有字段和方法。

8.1访问私有字段

要想访问私有字段你需要调用Class.getDeclaredField(Stringname)或 Class.getDeclaredFields()方法. Class.getField(String name) 和 Class.getFields()方法仅返回public字段。下面是一个简单的示例,类有一个私有字段,代码通过反射来访问这个私有字段:

public class PrivateObject {
        private String privateString = null;
        public PrivateObject(String privateString) {
           this.privateString = privateString;
        }
}
PrivateObject privateObject = new PrivateObject("The Private Value");
Field privateStringField = PrivateObject.class. getDeclaredField("privateString");
privateStringField.setAccessible(true);
String fieldValue = (String) privateStringField.get(privateObject);
System.out.println("fieldValue = " + fieldValue);

代码示例输出"fieldValue = The Private Value",即代码示例最开始创建的PrivateObject实例的私有字段 privateString的值。

注意PrivateObject.class.getDeclaredField("privateString")的使用。这个方法仅仅返回特定类声明的字段,而不包括任何父类中声明的字段。

注意粗体代码.通过调用Field.setAcessible(true)方法关闭了特定Field实例的访问检查,现在通过反射可以访问它,即使它是私有的,保护的或包范围,甚至调用者不属于这些范围。但编译器不允许使用普通的代码该字段,因为仅适用于反射。

8.2访问私有方法

想要访问私有方法需要调用Class.getDeclaredMethod(String name,Class[] parameterTypes) 或Class.getDeclaredMethods() 方法. Class.getMethod(String name, Class[]parameterTypes) 和 Class.getMethods() 方法仅返回public方法。下面是一个简单的示例,类有一个私有方法,代码通过反射来访问这个私有方法:

public class PrivateObject {
        private String privateString = null;
        public PrivateObject(String privateString) {
           this.privateString = privateString;
        }
        private String getPrivateString(){
           return this.privateString;
        }
}
PrivateObject privateObject = new PrivateObject("The Private Value");
Method privateStringMethod = PrivateObject.class.getDeclaredMethod("getPrivateString", null);
privateStringMethod.setAccessible(true);
String returnValue = (String)privateStringMethod.invoke(privateObject, null);
System.out.println("returnValue = " + returnValue);

代码示例输出" returnValue = The Private Value ",即代码示例最开始创建的PrivateObject实例调用的私有方法 getPrivateString()的返回值。

注意PrivateObject.class.getDeclaredMethod("privateString")方法的使用。这个方法仅仅返回特定类声明的方法,而不包括任何父类中声明的方法。

注意粗体代码.通过调用Method.setAcessible(true)方法关闭了特定Method实例的访问检查。现在通过反射可以访问它,即使它是私有的,保护的或包范围,甚至调用者不属于这些范围。但编译器不允许使用普通的代码访问该方法,因为仅适用于反射。

9. Array

在Java反射中使用数组有时候有点棘手,特别是如果你想获得某种类型的数组的Class对象,如int[]等。本节将讨论如何通过Java反射创建数组和Class对象。以下是本章涵盖的主题:

(1) java.lang.reflect.Array

(2) 创建数组

(3) 访问数组

(4) 获取数组的Class对象

(5) 获取数组的组件类型

9.1 java.lang.reflect.Array

通过Java反射机制使用数组是由thejava.lang.reflect.Array类完成的。不要把这个类和Java集合框架中的java.util.Arrays类相混淆,java.util.Arrays类包含数组排序,将它们转换为集合等公共方法。

9.2 创建数组

通过Java反射机制创建数组是由java.lang.reflect.Array类来完成的,如下:

int[] intArray = (int[]) Array.newInstance(int.class, 3);

这行代码创建了一个int数组。Array.newInstance()方法的第一个参数告诉我们数组中的元素类型。第二个参数声明了数组需要为多少个元素分配空间。

9.3 访问数组

使用Java反射机制可以访问数组的元素。这是通过 Array.get(...) 和 Array.set(...) 方法实现的,如下:

int[] intArray = (int[]) Array.newInstance(int.class, 3);
Array.set(intArray, 0, 123);
Array.set(intArray, 1, 456);
Array.set(intArray, 2, 789);
System.out.println("intArray[0] = " + Array.get(intArray, 0));
System.out.println("intArray[1] = " + Array.get(intArray, 1));
System.out.println("intArray[2] = " + Array.get(intArray, 2));

这段代码输出:

intArray[0] = 123
intArray[1] = 456
intArray[2] = 789

9.4 获取数组的Class对象

获取数组的Class对象可以使用无反射的代码,如下:

Class stringArrayClass = String[].class;

使用 Class.forName()方法并不易懂。例如,你可以像下面这样获取int数组的Class对象:

Class intArray = Class.forName("[I");

字母 I在JVM中代表一个。左边的 [ 表示它是一个int数组的class。这对其它的基本类型也有效。

对于对象,你需要使用一个稍微不同的记号:

Class stringArrayClass = Class.forName("[Ljava.lang.String;");

注意到左边的” [L”和右边的”  ; ”之间的类名。它代表对象数组的给定类型。

另外一个需要注意的是你不能够使用Class.forName()方法获取基本类型的Class对象。下面两个例子都会抛出ClassNotFoundException异常:

Class intClass1 = Class.forName("I");
Class intClass2 = Class.forName("int");

可以像下面这样获取基本数据类型和对象的Class名:

public Class getClass(String className){
       if("int" .equals(className)) return int .class;
       if("long".equals(className)) return long.class;
       ...
       return Class.forName(className);
}

一旦你获得某个类型的Class对象,有一个简单的方法来获得该类型的数组的Class对象。解决方案是创建所需类型的空数组并从这个空数组获取Class对象,如下:

Class theClass = getClass(theClassName);
Class stringArrayClass = Array.newInstance(theClass, 0).getClass();

这提供了一个单一的、统一的方法来访问任何类型的数组的Class对象。

要确保 Class对象是一个数组,可以使用Class.isArray()方法来校验:

Class stringArrayClass = Array.newInstance(String.class, 0).getClass();
System.out.println("is array: " + stringArrayClass.isArray());

9.5 获取数组的组件类型

一旦获取到某个数组的Class对象可以使用 Class.getComponentType()方法来获取它的组件类型。组件类型就是数组中元素的类型。例如,int数组的组件类型是 int.class Class对象.String[]数组的组件类型是 java.lang.String Class对象。

下面是获取数组的组件类型的示例:

String[] strings = new String[3];
Class stringArrayClass = strings.getClass();
Class stringArrayComponentType = stringArrayClass.getComponentType();
System.out.println(stringArrayComponentType);

输出 "class java.lang.String"即为String数组的组件类型.

10.        注解

使用Java反射你可以在运行时访问Java类中的注解.下面是本章涵盖的主题:

(1)什么是Java注解?

(2)类注解

(3)方法注解

(4)参数注解

(5)字段注解

10.1 什么是Java注解?

注解是Java5的一个新功能。注解是一种可以在Java代码中插入的注释或元数据。这些注解可以在编译时由预编译工具进行处理,也可以在运行时通过Java反射机制来处理。下面是一个类注解的示例:

@MyAnnotation(name="someName",  value = "Hello World")
public class TheClass {
}

TheClass类的上面有  @MyAnnotation 注解.注解像接口那样定义。下面是MyAnnotation注解 的定义:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyAnnotation {
        public String name();
        public String value();
}

在interface前面的”@”标志它是一个注解。一旦你定义了那个注解你就可以在代码中使用它,就像上面的例子那样。

在注解中定义的两个指令@Retention(RetentionPolicy.RUNTIME)和 @Target(ElementType.TYPE)表明注解是如何使用的

@Retention(RetentionPolicy.RUNTIME)表明在运行时可以使用反射来访问这个注解。如果没有设置这个指令,在运行时这个注解将不会被保存,因此通过反射访问不到。

@Target(ElementType.TYPE) 表明注解仅能用于类、接口(包括注释类型)或枚举声明等类型上。你也可以指定 METHOD 或FIELD,或者@Target什么都不指定,这样它可以用在任何程序元素上。

10.2 类注解

可以在运行时获取类、方法或字段的注解。下面是获取类注解的示例:

Class aClass = TheClass.class;
Annotation[] annotations = aClass.getAnnotations();
for(Annotation annotation : annotations){
        if(annotation instanceof MyAnnotation){
            MyAnnotation myAnnotation = (MyAnnotation) annotation;
            System.out.println("name: " + myAnnotation.name());
            System.out.println("value: " + myAnnotation.value());
         }
}

也可以获取指定的类注解,如下:

Class aClass = TheClass.class;
Annotation annotation = aClass.getAnnotation(MyAnnotation.class);
 
if(annotation instanceof MyAnnotation){
         MyAnnotation myAnnotation = (MyAnnotation) annotation;
         System.out.println("name: " + myAnnotation.name());
         System.out.println("value: " + myAnnotation.value());
}

10.3 方法注解

下面是方法注解的示例:

public class TheClass {
         @MyAnnotation(name="someName",  value = "Hello World")
          public void doSomething(){}
}

获取方法的所有注解,如下:

Method method = ... //obtain method object
Annotation[] annotations = method.getDeclaredAnnotations();
for(Annotation annotation : annotations){
         if(annotation instanceof MyAnnotation){
             MyAnnotation myAnnotation = (MyAnnotation) annotation;
             System.out.println("name: " + myAnnotation.name());
             System.out.println("value: " + myAnnotation.value());
         }
}

获取方法的特定注解:

Method method = ... // obtain method object
Annotation annotation = method.getAnnotation(MyAnnotation.class);
if(annotation instanceof MyAnnotation){
       MyAnnotation myAnnotation = (MyAnnotation) annotation;
       System.out.println("name: " + myAnnotation.name());
        System.out.println("value: " + myAnnotation.value());
}

10.4 参数注解

也可以给方法声明的参数添加注解,如下:

public class TheClass {
         public static void doSomethingElse(
            @MyAnnotation(name="aName", value="aValue") String parameter){
         }
}

从Method对象获取参数注解:

Method method = ... //obtain method object
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
Class[] parameterTypes = method.getParameterTypes();
int i=0;
for(Annotation[] annotations : parameterAnnotations){
         Class parameterType = parameterTypes[i++];
         for(Annotation annotation : annotations){
            if(annotation instanceof MyAnnotation){
                MyAnnotation myAnnotation = (MyAnnotation) annotation;
                System.out.println("param: " + parameterType.getName());
                System.out.println("name : " + myAnnotation.name());
                System.out.println("value: " + myAnnotation.value());
            }
         }
}

注意 Method.getParameterAnnotations() 方法返回的是二维的Annotation数组,每个方法参数都有一个一维的Annotation数组。

10.5 字段注解

下面是字段注解示例:

public class TheClass {
         @MyAnnotation(name="someName",  value = "Hello World")
         public String myField = null;
}

获取字段的所有注解,如下:

Field field = ... //obtain field object
Annotation[] annotations = field.getDeclaredAnnotations();
for(Annotation annotation : annotations){
         if(annotation instanceof MyAnnotation){
            MyAnnotation myAnnotation = (MyAnnotation) annotation;
            System.out.println("name: " + myAnnotation.name());
            System.out.println("value: " + myAnnotation.value());
         }
}

获取指定字段的注解,如下:

Field field = ... // obtain method object
Annotation annotation = field.getAnnotation(MyAnnotation.class);
if(annotation instanceof MyAnnotation){
         MyAnnotation myAnnotation = (MyAnnotation) annotation;
         System.out.println("name: " + myAnnotation.name());
         System.out.println("value: " + myAnnotation.value());
}                                                                                      

11.     泛型

经常在文章和论坛里读到说所有Java泛型信息在编译的时候被擦除了所以在运行时访问不到任何泛型信息。这并不完全正确。在极少数的情况下,在运行时是可以访问泛型信息的。这些情况实际上涵盖一些我们需要的Java泛型信息。本章将解释这些情况。下面是本章涵盖的主题:

(1) 泛型反射的经验法则

(2) 泛型方法的返回值类型

(3) 泛型方法的参数类型

(4) 泛型字段类型

11.1 泛型反射的经验法则

使用Java的泛型通常分为两种不同的情况:

(1)声明一个可参数化的类/接口。

(2)使用参数化的类。

当写一个类或接口时,可以指定它应该是可参数化的。就像java.util.List接口那样,可以参数化java.util.List来创建一个String列表而不是创建Object列表。

java.util.List在运行时会检查参数化的类型,但是没有办法知道参数化的是什么类型。这是因为在相同的应用程序中类型可以被参数化为所有的类型。但是,当你检查方法或字段所声明使用的参数化类型,你可以在运行时看到可参数化的类型被参数化为什么类型。总之,你不能在运行时看到类型本身被参数化为什么类型,但你可以在使用和参数化它的字段和方法中看到它。

11.2 泛型方法的返回值类型

如果你获取到一个java.lang.reflect.Method 对象,可以获取它的返回值类型信息。下面是一个示例,一个类有一个有参数化返回值类型的方法:

public class MyClass {
        protected List<String> stringList = ...;
        public List<String> getStringList(){
           return this.stringList;
        }
}

在这个类中可以获取 getStringList()方法的泛型返回值类型。换句话说,可以探测到getStringList()返回的是List<String> 而不仅是一个 List。如下:

Method method = MyClass.class.getMethod("getStringList", null);
Type returnType = method.getGenericReturnType();
if(returnType instanceof ParameterizedType){
       ParameterizedType type = (ParameterizedType) returnType;
       Type[] typeArguments = type.getActualTypeArguments();
       for(Type typeArgument : typeArguments){
           Class typeArgClass = (Class) typeArgument;
           System.out.println("typeArgClass = " + typeArgClass);
       }
}

代码输出 "typeArgClass = class java.lang.String"。

Type[]数组 typeArguments包含一项 -一个代表类java.lang.String的 Class实例 。 Class实现了 Type接口。

11.3 泛型方法的参数类型

通过Java反射可以在运行时访问参数类型的泛型类型。下面的示例中,类有一个使用参数化的List作为参数的方法:

public class MyClass {
         protected List<String> stringList = ...;
 
          public void setStringList(List<String> list){
             this.stringList = list;
          }
}

可以访问方法参数的泛型参数类型,如下:

Method method = Myclass.class.getMethod("setStringList", List.class);
Type[] genericParameterTypes = method.getGenericParameterTypes();
for(Type genericParameterType : genericParameterTypes){
        if(genericParameterType instanceof ParameterizedType){
           ParameterizedType aType = (ParameterizedType) genericParameterType;
           Type[] parameterArgTypes = aType.getActualTypeArguments();
           for(Type parameterArgType : parameterArgTypes){
               Class parameterArgClass = (Class) parameterArgType;
               System.out.println("parameterArgClass = " + parameterArgClass);
           }
         }
}

代码输出"parameterArgType= class java.lang.String"。

 Type[]数组 parameterArgTypes包含一项 -一个代表类java.lang.String的 Class实例 。 Class实现了 Type接口。

11.4 泛型字段类型

可以访问public字段的泛型类型。字段即类的成员变量-静态的或实例变量。下面是一个例子,类有一个实例变量stringList.

public class MyClass {
         public List<String> stringList = ...;
}
Field field = MyClass.class.getField("stringList");
Type genericFieldType = field.getGenericType();  
if(genericFieldType instanceof ParameterizedType){
         ParameterizedType aType = (ParameterizedType) genericFieldType;
         Type[] fieldArgTypes = aType.getActualTypeArguments();
         for(Type fieldArgType : fieldArgTypes){
            Class fieldArgClass = (Class) fieldArgType;
            System.out.println("fieldArgClass = " + fieldArgClass);
          }
}

代码将输出"fieldArgClass = class java.lang.String"。

 Type数组fieldArgTypes包含一项 – 一个代表类java.lang.String的 Class实例 。 Class实现了 Type接口。

uniapp [安卓苹果App端] - 详细实现下载文件保存到用户手机本地,指定文件存储位置路径及文件夹名称下载并保存,Uniapp app端将文件下载保存并储存到指定目录(手机系统存储空间权限检测) uni-app,安卓Android,苹果ios,安卓app端,苹果App端,uniapp苹果安卓app,app端下载文件,移动端文件下载,并保存到手机本地存储空间,uniapp下载文件保存到手机本地,iOS下载文件怎么保存,系统读写存储空间权限,下载文件到本地详细流程及源代码,保存自定义目录,uniapp,实现下载文件并保存到本地,文件后缀,如何指定手机的存放目录路径url,点击下载文档到手机中并提示存储位置,如何在uniapp中实现文件下载功能,uniApp文件下载,怎么指定文件保存目录,下载报错,没反应 阅读详情

相关推荐

银河飞腾FT6678实战指南:OpenMP多核并行编程优化技巧

本文详细介绍了在银河飞腾FT6678多核处理器上使用OpenMP进行并行编程的实战技巧与优化方法。从基础语法到环境配置,再到性能优化的五大核心技巧,包括循环并行化、并行粒度控制、reduction子句使用等,帮助开发者充分发挥FT6678的8核计算能力。通过实际案例展示如何将图像处理算法从200ms优化到28ms,并深入探讨了任务调度、内存访问优化等进阶主题。

weixin_29234423的博客 285

Java的反射机制API使用及实现

java的反射机制学习 一般在实例化对象时,必须要知道类名才可以实例化它的对象,这样我们在应用方面就会受到限制,反射技术,让我们不知道这个类的类名就可以实例化它的对象。 java程序在运行时可以获得任何一个类的字节码信息,包括类的修饰符(public,static等),基类(超类,父类),实现的接口,字段和方法等信息. java程序在运行时可以根据字节码信息来创建该类的实例对象,改变对象的字段内...

newzhong 449

拉曼光谱仪的原理与应用——TFN 7G F660手持式拉曼光谱识别仪

拉曼光谱仪的原理与应用——TFN 7G F660手持式拉曼光谱识别仪

TFN_yzgd的博客 926

Java反射机制

反射,reflection,听其名就像照镜子一样,可以看见自己也可以看见别人的每一部分。在java语言中这是一个很重要的特性。下面是来自sun公司官网关于反射的介绍:    Reflection is a feature in the Java programming language. It allows an executing Java program to examine

853

2020.12.25丨CRISPResso2安装教程

一个月前接到一个项目,说是做关于CRISPR的编辑效率个性化分析。本来这个分析以前是没有做过的,不过客户发了参考文献。里面流程和命令行都非常清楚,再加上读研期间CRISPR的项目“中道崩殂”,让我对CRiSPR的技术有些执念,便下决心接下来试试。 整个测序分析使用的工具就是CRISPResso,,下面介绍最快捷的安装方式。 首先,CRISPResso只支持python2.X,不支持3.X你需要下载Anaconda或者miniconda,以Miniconda为例: 安装环境: Linux 64-bi

4631

福建公共资源交易平台 portal-sign与响应Data解密逆向实录

本文分析了福建公共资源交易电子公共服务平台的接口加密机制。通过抓包和代码分析,发现请求头中的portal-sign由MD5算法生成,包含固定秘钥和排序后的参数拼接。响应数据采用AES-256-CBC加密,需使用特定Key和IV解密。文章详细介绍了签名生成规则、参数处理流程及加密常量定位方法,为接口对接提供了技术参考。整个过程展示了如何通过逆向工程还原前端加密逻辑,最终实现脱离浏览器环境的纯协议调用。

自成背后的博客 1157

java基础-反射详解

java反射常用API,获取Class对象、newInstance方法、Field方法操作字段、invoke、getMethod方法 动态调用指定方法

张刚的博客 2624

锐明技术Crocus系统 DeviceState.do SQL注入漏洞复现

锐明技术Crocus系统 DeviceState.do 接口存在SQL注入漏洞,经过身份验证的远程攻击者除了可以利用SQL注入漏洞获取数据库中的信息(例如,管理员后台密码、站点的用户个人信息)之外,甚至在高权限的情况可向服务器中写入木马,进一步获取服务器系统权限。

0xSec 452

java--基础--20.3--反射--反射api的功能详解

java–基础–20.3–反射–反射api的功能详解 1、Java反射简介 1.1、定义 JAVA反射机制是在运行状态中 对于任意一个类,都能够知道这个类的所有属性和方法 对于任意一个对象,都能够调用它的任意一个方法 这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。 1.2、反射机制的核心 允许在运行时通过Java Reflection APIs来取得已知名字的class类的内部信息(包括其modifiers(诸如public, static等等)、

zhou920786312的博客 774

为YOLOv11引入自适应特征融合模块(ASFF)

本文探讨了在YOLOv11中引入ASFF(自适应空间特征融合)模块以解决多尺度目标检测性能不均衡的问题。作者分析了传统FPN固定融合策略的缺陷,详细介绍了ASFF模块的实现原理,包括动态权重生成网络设计和特征融合机制。通过将ASFF集成到YOLOv11颈部网络,实现了特征在双向流动时的自适应融合,在复杂场景下mAP提升1.2%而推理时间仅增加3-5ms。文章还分享了训练调参技巧和部署注意事项,指出ASFF特别适合交通监控等目标尺度变化大的场景,但强调需根据实际需求平衡模型复杂度与性能。最后提供了工程实践建议

as1225120794的博客 320

部分旧版统信国产系统下载资源

注:全部自来网络查找,建议到官网下载最新版。

aggs1990的专栏 5224

Java获取数组组件类型及Array API简单示例

Java获取数组组件类型及Array API简单示例

学无止境 1万+

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

Java之反射机制详解

Java反射是一种允许程序在运行时动态检查和操作类内部信息的机制,提供了极大的灵活性和扩展性。它通过`java.lang.reflect`包中的类实现,包括Class、Constructor、Field和Method等核心类。反射机制的应用场景广泛,如框架设计、插件系统、单元测试等。具体实现包括获取Class对象、获取类的信息以及操作类,如创建对象、调用方法和访问字段。此外,反射在处理注解方面也起着关键作用。然而,反射存在性能开销和安全隐患,因此需要谨慎使用,并采取相应的优化和安全措施。

一个老Java开发的自白 1576

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

java反射机制原理详解_反射机制使用详解

原文链接:反射机制使用详解反射机制使用详解反射定义反射API反射对于单例的破坏IOC容器反转控制原理反射的定义java反射机制是java开发中有一个非常重要的概念,也是java的重要特征之一。反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力,通过反射可以动态调用方法和属性,大部分框架也都是运用反射原理的。比如Spring。首先大家应该先了解...

weixin_39956022的博客 1219

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

java 反射机制详解_Java中反射机制详解

这篇文章主要介绍了Java反射机制及Method.invoke详解,本文讲解了JAVA反射机制、得到某个对象的属性、得到某个类的静态属性、执行某对象的方法、执行某个类的静态方法等内容,需要的朋友可以参考下 JAVA反射机制 JAVA反射机制是在运行状态中,对于任意一个类,反射:可以动态的获取指定类中的成员,以及建立类对象。好处:提高了程序的扩展性。张三,李四等这些生活中的对象,可以通过Person...

weixin_31629913的博客 177

Java 基础 - 反射机制详解

本文综合多篇文章后,总结了Java 反射的相关知识,希望可以提升你对Java中反射的认知效率。

qq_63353796的博客 1272

Java中反射机制详解

最近小熙在复习反射相关的知识,碰到一篇还不错的分享下     序言       在学习java基础时,由于学的不扎实,讲的实用性不强,就觉得没用,很多重要的知识就那样一笔带过了,像这个马上要讲的反射机制一样,当时学的时候就忽略了,到后来学习的知识中,很多东西动不动就用反射,所以回过头来把这个给重新补一下,自己欠下的债,迟早是要还的。                             ...

人生到处知何似,应似飞鸿踏雪泥。 251

java反射机制原理详解_java反射机制的详细讲解

一 , 什么是java反射机制?JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。1.意义首先,反射机制极大的提高了程序的灵活性和扩展性,降低模块的耦合性,提高自身的适应能力。其次,通过反射机制可以让程序创建和控制任何类的对象,无需提前硬编码目标...

weixin_39998462的博客 3612

JAVA的反射机制详解

首先,大家应该先明白两个概念:编译期和运行期。编译期就是编译器帮你把源代码翻译成机器能执行的文件,比如编译器把java代码编译成jvm能识别的字节码文件,而运行期指的是将可执行文件交给操作系统去执行。Java反射(Reflection)就是Java程序在运行时,可以加载、探知、使用编译期间完全未知的类。也就是说,Java可以加载一个运行时才得知类名的类,获得类的完整构造方法,并实例化出对象,给对象...

yechengchao的博客 192

java反射机制详解

资料来源:尚学堂(http://www.bjsxt.cn/) java反射机制详解 反射机制 指的是可以于运行时加载、探知、使用编译期间完全未知的类。 程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个已加载的类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性; Class类介绍 java.lang.Class类十分特殊,用来表示java中...

wangking407的博客 281

Java中反射机制详解(转)

转自:https://www.cnblogs.com/whgk/p/6122036.html     序言       在学习java基础时,由于学的不扎实,讲的实用性不强,就觉得没用,很多重要的知识就那样一笔带过了,像这个马上要讲的反射机制一样,当时学的时候就忽略了,到后来学习的知识中,很多东西动不动就用反射,所以回过头来把这个给重新补一下,自己欠下的债,迟早是要还的。          ...

qq_21479345的博客 168
上一篇: Java反射机制剖析
下一篇: Java BigDecimal详解
Footloose_
博客等级 码龄10年 254粉丝 11原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值