什么是注解
- 注解:Annotation
- Annotation 是从JDK5.0 开始引入的新技术
- 注解的作用
- 不是程序本身,可以堆程序做出解释
- 可以被其他程序所读取
- 注解的格式
- 注解是以’@注释名’在代码中存在的,还可以添加一些参数值,例如:@Value(value = “hello”)
- 注解在那里使用
- 可以附加在package、class、method、field上面,相当于给他们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些元数据的访问
可以理解为,注解就是打标签,然后我们对标签进行解析,那么就对标签赋予了相应的能力
常见的内置注解
@Override:定义在java.lang.Override 中,此注解只用来修饰方法,表示一个方法声明打算重写超类中的另一个方法声明
- @Deprecated:定义在java.lang.Deprecated中,此注释可以用于修饰方法、属性、类,表示已经过期,不建议程序员使用这样的元素,通常是因为它比较危险,并且有更好的选择
@SuppressWarnings() :定义在java.lang.@SuppressWarnings 中,用来有抑制编译中的警告信息
元注解
元注解的作用就是负责注解其他注解,Java定义了4个标准的 meta-annotation 类型,他们被用来提供对其他 annotation 类型做说明
- 这些类型和它们所支持的类,java.lang.annotation 包中可以找到(@Target @Retention @Document @Inherited)
- @Target 用于描述注解的使用范围,也就是注解可以使用在什么地方
- @Retention 表示需要在什么级别保存该注解信息,用于描述注解的声明周期
- SOURCE < CLASS < RUNTIME
- @Document 说明该注解将被包含在 javaDoc中
- @Inherited 说明子类可以 继承 父类的该注解 ```java @Target(value = { ElementType.METHOD, ElementType.TYPE }) // runtime > class > source @Retention(value = RetentionPolicy.RUNTIME) // 表示是否将我们的注解生成在JavaDoc中 @Documented // 表示子类可以继承父类的注解 @Inherited public @interface MyAnno {
}
<a name="axaxR"></a># 自定义注解- 使用 @interface 自定义注解的时候,自动继承了 java.lang.annotation.Annotataion 接口- **分析**- @interface用来声明一个注解 格式 public @interface MyAnno {}- 其中的每一个方法实际上就是声明了一个配置参数- 方法的名称就是参数的名称- 返回值的类型就是参数的类型(返回值类型只能是基本类型,Class、String、enum)- 可以使用default来声明参数的默认值- 如果只有一个参数成员,一般参数名为 value- 注解元素必须要有值,我们定义注解元素的时候, 经常使用空字符串,0作为默认值```java@Target({ ElementType.TYPE, ElementType.METHOD })@Retention(RetentionPolicy.RUNTIME)public @interface MyAnno2 {// 注解的参数: 参数类型+参数名();String name() default "";int age() default 0;// 如果默认为 -1 ,就是不存在int id() default -1;String[] schools() default {};}
public class Test {// 注解可以显示赋值,也可以使用默认值@MyAnno2(name = "ic", age = 12)public static void main(String[] args) {}}
如果注解只有一个内容,那么就只要 写 value,就可以直接放值,如果是多共个,value 就必须使用
注解使用
单独的注解的定义是没有意义的,因为没有赋予功能,所以就要将下面的反射,反射使得注解具备了功能
反射机制
反射:Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期间借助于Reflection API 取得任何类的内部信息,并能直接操作任意对象的内部属性和方法
Class c = Class.forName("java.lang.String");
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息,我们可以通过这个对象,获得类的所有的属性方法
- 正常方式:引入需要的”包类”名称 -> 通过 new 实例化 -> 取得实例化对象
- 反射方式:实例化对象 -> getClass()方法 -> 得到完整的”包类”名称
- 优点:可以实现动态创建对象和编译,体现很大的灵活性
缺点:对性能有影响,使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且他能满足要求,这类操作是慢于直接执行等同效果的操作
动态语言和静态语言
动态语言:是一类可以在运行时改变其结构的语言,Object-c、C#、JavaScript、PHP、Python
- 静态语言:与动态语言对应的,运行时不可修改的语言,如Java、C、C++;Java不是动态语言,但是Java可以称为”准静态语言”,也就是说Java有一定的动态性
反射的API
java.lang.Class 代表一个类java.lang.reflect.Method 代表类的方法java.lang.reflect.Field 代表类的成员变量java.lang.reflect.Constructor 代表类的构造器
理解Class类并获取Class实例
```java public class Test01 { public static void main(String[] args) throws Exception {
} }// 通过反射获取Class对象Class<?> clazz = Class.forName("com.ldl.baselearn.anno.reflect.User");Class<?> clazz2 = Class.forName("com.ldl.baselearn.anno.reflect.User");Class<?> clazz3 = Class.forName("com.ldl.baselearn.anno.reflect.User");// 一个类在内存中只有一个Class对象// 一个类被加载之后,类的整个结构都会被封装在Class对象中System.out.println(clazz == clazz2);System.out.println(clazz2 == clazz3);User user = (User) clazz.newInstance();user.setUsername("hello");System.out.println(user);
@Data class User { private String username; private int id; private int age; }
<a name="N7h43"></a>
# 类的加载ClassLoader
- 类的加载->类的连接->类的初始化
```java
public class Test03 {
static {
System.out.println("main类被初始化");
}
public static void main(String[] args) throws Exception {
// 会主动引用的案例
// 创建对象
// Son son = new Son();
// 通过反射
// Class<?> clazz = Class.forName("cn.icanci.reflection.Son");
// 不回产生类的引用的方法,此时子类没有被初始化
// System.out.println(Son.b);
// 初始化数组也不回
// Son[] sons = new Son[5];
// 常量不回引起子类和父类的初始化
System.out.println(Son.M);
}
}
class Father {
static int b = 2;
static {
System.out.println("父类被加载");
}
}
class Son extends Father {
static {
System.out.println("子类被加载");
m = 200;
}
static int m = 100;
static final int M = 1;
}
public class Test05 {
public static void main(String[] args) throws Exception {
// 获取系统的类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
// 获取系统的类加载器 -> 拓展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);
// 获取拓展类的加载器 父类加载器 -> 根加载器(C/C++) 根加载器 无法打印
ClassLoader root = parent.getParent();
System.out.println(root);
// 测试当前类是哪个加载器加载的
ClassLoader classLoaderTest05 = Class.forName("ccom.ldl.baselearn.anno.reflect.Test05").getClassLoader();
System.out.println(classLoaderTest05);
// 测试JDK内部类是哪个加载器加载的 根加载器 无法打印
ClassLoader classLoaderObject = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoaderObject);
// 如何获取系统类加载器可以加载的路径
System.out.println(System.getProperty("java.class.path"));
}
}
上述案例输出如下
sun.misc.Launcher$AppClassLoader@4e0e2f2a sun.misc.Launcher$ExtClassLoader@2a139a55 null sun.misc.Launcher$AppClassLoader@4e0e2f2a null /Users/icanci/ideaProjects/NewLearn/base-learn/target/classes:/Users/develop/repertoties/maven_repertoty/it/unimi/dsi/fastutil/8.2.2/fastutil-8.2.2.jar:/Users/develop/repertoties/maven_repertoty/net/sf/trove4j/trove4j/3.0.3/trove4j-3.0.3.jar:/Users/develop/repertoties/maven_repertoty/com/googlecode/lambdaj/lambdaj/2.3.3/lambdaj-2.3.3.jar:/Users/develop/repertoties/maven_repertoty/org/hamcrest/hamcrest-all/1.1/hamcrest-all-1.1.jar:/Users/develop/repertoties/maven_repertoty/org/objenesis/objenesis/1.2/objenesis-1.2.jar:/Users/develop/repertoties/maven_repertoty/cglib/cglib-nodep/2.2/cglib-nodep-2.2.jar:/Users/develop/repertoties/maven_repertoty/org/projectlombok/lombok/1.18.22/lombok-1.18.22.jar:/Users/develop/repertoties/maven_repertoty/org/mvel/mvel2/2.2.8.Final/mvel2-2.2.8.Final.jar:/Users/develop/repertoties/maven_repertoty/com/alibaba/fastjson/1.2.79/fastjson-1.2.79.jar:/Users/develop/repertoties/maven_repertoty/org/apache/lucene/lucene-core/4.0.0/lucene-core-4.0.0.jarClass类
Class类,Class类也是一个实实在在的类,存在于JDK的java.lang包中。Class类的实例表示java应用运行时的类(class ans enum)或接口(interface and annotation)(每个java类运行时都在JVM里表现为一个class对象,可通过类名.class、类型.getClass()、Class.forName(“类名”)等方法获取class对象)。数组同样也被映射为为class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。基本类型boolean,byte,char,short,int,long,float,double和关键字void同样表现为 class 对象 ```java public final class Class
implements java.io.Serializable, GenericDeclaration, Type, AnnotatedElement { private static final int ANNOTATION= 0x00002000; private static final int ENUM = 0x00004000; private static final int SYNTHETIC = 0x00001000; private static native void registerNatives(); static {
registerNatives();}
/*
- Private constructor. Only the Java Virtual Machine creates Class objects. //私有构造器,只有JVM才能调用创建Class对象
- This constructor is not used and prevents the default constructor being
- generated. */ private Class(ClassLoader loader) { // Initialize final field for classLoader. The initialization value of non-null // prevents future JIT optimizations from assuming this final field is null. classLoader = loader; }
- **总结**
- Class类也是类的一种,和 class 关键字是不一样的
- 手动编写的类被编译之后会产生一个Class对象,其表示的是创建的类的类型信息,而且这个Class对象保存在同名.class文件中(字节码文件)
- 每个通过关键字标识的类,在内存中有且只有一个与之对应的Class对象来描述其类型信息,无论创建多少个实例对象,其依据的都是一个Class对象
- Class类只存私有构造函数,所以Class对象只能是JVM创建和加载
- Class类的对象的作用是运行时提供整个或者获取某个对象的类型信息
<a name="G51Q0"></a>
# 类加载机制-简介
<a name="mUdwE"></a>
## 类加载过程

<a name="NHTut"></a>
## 类的加载

- 关于类加载会在JVM的部分详细讲解
<a name="n8tEV"></a>
# Class类对象的获取
- 在类加载的时候,JVM会创建一个class对象
- class对象是反射中最常用的,获取class对象的方式主要有三种
- 根据类名:类名.class
- 根据对象:对象.getClass()
- 根据全限定类名:Class.forname("全限定类名")
```java
public class ClassTest {
public static void main(String[] args) throws Exception {
String str = "123";
// 类名.class
Class<String> sc1 = String.class;
// 对象.getClass()
Class<? extends String> sc2 = str.getClass();
// Class.forname("全限定类名")
Class<?> sc3 = Class.forName("java.lang.String");
System.out.println(sc1 == sc2);
System.out.println(sc1 == sc3);
}
}
Class类的方法 | 方法名 | 说明 | | —- | —- | | forName() | (1)获取Class对象的一个引用,但引用的类还没有加载(该类的第一个对象没有生成)就加载了这个类。
(2)为了产生Class引用,forName()立即就进行了初始化。 | | Object-getClass() | 获取Class对象的一个引用,返回表示该对象的实际类型的Class引用。 | | getName() | 取全限定的类名(包括包名),即类的完整名字。 | | getSimpleName() | 获取类名(不包括包名) | | getCanonicalName() | 获取全限定的类名(包括包名) | | isInterface() | 判断Class对象是否是表示一个接口 | | getInterfaces() | 返回Class对象数组,表示Class对象所引用的类所实现的所有接口。 | | getSupercalss() | 返回Class对象,表示Class对象所引用的类所继承的直接基类。应用该方法可在运行时发现一个对象完整的继承结构。 | | newInstance() | 返回一个Oject对象,是实现“虚拟构造器”的一种途径。使用该方法创建的类,必须带有无参的构造器。 | | getFields() | 获得某个类的所有的公共(public)的字段,包括继承自父类的所有公共字段。 类似的还有getMethods和getConstructors。 | | getDeclaredFields | 获得某个类的自己声明的字段,即包括public、private和proteced,默认但是不包括父类声明的任何字段。类似的还有getDeclaredMethods和getDeclaredConstructors。 |举例 ```java public class ClassTest2 { public static void main(String[] args) throws IllegalAccessException, InstantiationException {
Class<Dog> dog = Dog.class; //类名打印 System.out.println(dog.getName()); // com.ldl.baselearn.anno.reflect.Dog System.out.println(dog.getSimpleName()); // Dog System.out.println(dog.getCanonicalName());// com.ldl.baselearn.anno.reflect.Dog //接口 System.out.println(dog.isInterface()); //false for (Class iI : dog.getInterfaces()) { System.out.println(iI); } /* interface com.ldl.baselearn.anno.reflect.I1 interface com.ldl.baselearn.anno.reflect.I2 */ //父类 System.out.println(dog.getSuperclass());// class com.ldl.baselearn.anno.reflect.Animal //创建对象 Dog d = dog.newInstance(); //字段 for (Field f : dog.getFields()) { System.out.println(f.getName()); } /* mDogPublic sDogPublic mAnimalPublic sAnimalPublic mCellPublic //父类的父类的公共字段也打印出来了 */ System.out.println("---------"); for (Field f : dog.getDeclaredFields()) { System.out.println(f.getName()); } /* 只有自己类声明的字段 mDogPrivate mDogPublic mDogProtected mDogDefault sDogPrivate sDogProtected sDogDefault sDogPublic */} }
interface I1 { }
interface I2 { }
class Cell { public int mCellPublic; }
class Animal extends Cell { private int mAnimalPrivate; protected int mAnimalProtected; int mAnimalDefault; public int mAnimalPublic; private static int sAnimalPrivate; protected static int sAnimalProtected; static int sAnimalDefault; public static int sAnimalPublic; }
class Dog extends Animal implements I1, I2 { private int mDogPrivate; public int mDogPublic; protected int mDogProtected; private int mDogDefault; private static int sDogPrivate; protected static int sDogProtected; static int sDogDefault; public static int sDogPublic; }
<a name="IWZAz"></a>
## getName、getCanonicalName与getSimpleName的区别
- getSimpleName:只获取类名
- getName:类的全限定名,jvm中Class的表示,可以用于动态加载Class对象,例如Class.forName
- getCanonicalName:返回更容易理解的表示,主要用于输出(toString)或log打印,大多数情况下和getName一样,但是在内部类、数组等类型的表示形式就不同了
```java
public class ClassTest3 {
private class inner {
}
public static void main(String[] args) throws ClassNotFoundException {
//普通类
System.out.println(ClassTest3.class.getSimpleName()); // ClassTest3
System.out.println(ClassTest3.class.getName()); // com.ldl.baselearn.anno.reflect.ClassTest3
System.out.println(ClassTest3.class.getCanonicalName()); // com.ldl.baselearn.anno.reflect.ClassTest3
//内部类
System.out.println(inner.class.getSimpleName()); // inner
System.out.println(inner.class.getName()); // com.ldl.baselearn.anno.reflect.ClassTest3$inner
System.out.println(inner.class.getCanonicalName()); // com.ldl.baselearn.anno.reflect.ClassTest3.inner
//数组
System.out.println(args.getClass().getSimpleName()); // String[]
System.out.println(args.getClass().getName()); // [Ljava.lang.String;
System.out.println(args.getClass().getCanonicalName()); // java.lang.String[]
// 我们不能用getCanonicalName去加载类对象,必须用getName
// Class.forName(inner.class.getCanonicalName()); 报错
Class.forName(inner.class.getName());
}
}
Constructor类及其用法
- Constructor类存在于反射包(java.lang.reflect)中,反映的是Class 对象所表示的类的构造方法
获取Constructor对象是通过Class类中的方法获取的,Class类与Constructor相关的主要方法如下 | 方法返回值 | 方法名称 | 方法说明 | | —- | —- | —- | | static Class<?> | forName(String className) | 返回与带有给定字符串名的类或接口相关联的 Class 对象。 | | Constructor | getConstructor(Class<?>… parameterTypes) | 返回指定参数类型、具有public访问权限的构造函数对象 | | Constructor<?>[] | getConstructors() | 返回所有具有public访问权限的构造函数的Constructor对象数组 | | Constructor | getDeclaredConstructor(Class<?>… parameterTypes) | 返回指定参数类型、所有声明的(包括private)构造函数对象 | | Constructor<?>[] | getDeclaredConstructor() | 返回所有声明的(包括private)构造函数对象 | | T | newInstance() | 调用无参构造器创建此 Class 对象所表示的类的一个新实例。 |
下面看一个简单例子来了解Constructor对象的使用: ```java public class ConstructionTest {
public static void main(String[] args) throws Exception {
Class<?> clazz = null; // 获取Class对象的引用 clazz = Class.forName("com.ldl.baselearn.anno.reflect.ConstructionTestUser"); // 第一种方法,实例化默认构造方法,User必须无参构造函数,否则将抛异常 ConstructionTestUser user = (ConstructionTestUser) clazz.newInstance(); user.setAge(20); user.setName("icanci"); System.out.println(user); System.out.println("--------------------------------------------"); // 获取带String参数的public构造函数 Constructor cs1 = clazz.getConstructor(String.class); // 创建User ConstructionTestUser user1 = (ConstructionTestUser) cs1.newInstance("hiway"); user1.setAge(22); System.out.println("user1:" + user1); System.out.println("--------------------------------------------"); // 取得指定带int和String参数构造函数,该方法是私有构造private Constructor cs2 = clazz.getDeclaredConstructor(int.class, String.class); // 由于是private必须设置可访问 cs2.setAccessible(true); // 创建user对象 ConstructionTestUser user2 = (ConstructionTestUser) cs2.newInstance(25, "hiway2"); System.out.println("user2:" + user2); System.out.println("--------------------------------------------"); // 获取所有构造包含private Constructor<?> 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()); else System.out.print(clazzs[j].getName() + ","); } System.out.println(")"); }} }
@Data class ConstructionTestUser { private int age; private String name;
public ConstructionTestUser() {
super();
}
public ConstructionTestUser(String name) {
super();
this.name = name;
}
private ConstructionTestUser(int age, String name) {
super();
this.age = age;
this.name = name;
}
}
- 输出结果如下
```java
ConstructionTestUser(age=20, name=icanci)
--------------------------------------------
user1:ConstructionTestUser(age=22, name=hiway)
--------------------------------------------
user2:ConstructionTestUser(age=25, name=hiway2)
--------------------------------------------
构造函数[0]:private com.ldl.baselearn.anno.reflect.ConstructionTestUser(int,java.lang.String)
参数类型[0]:(int,java.lang.String)
构造函数[1]:public com.ldl.baselearn.anno.reflect.ConstructionTestUser(java.lang.String)
参数类型[1]:(java.lang.String)
构造函数[2]:public com.ldl.baselearn.anno.reflect.ConstructionTestUser()
参数类型[2]:()
关于Constructor类本身一些常用方法如下(仅部分,其他可查API) | 方法返回值 | 方法名称 | 方法说明 | | —- | —- | —- | | Class | getDeclaringClass() | 返回 Class 对象,该对象表示声明由此 Constructor 对象表示的构造方法的类,其实就是返回真实类型(不包含参数) | | Type[] | getGenericParameterTypes() | 按照声明顺序返回一组 Type 对象,返回的就是 Constructor对象构造函数的形参类型。 | | String | getName() | 以字符串形式返回此构造方法的名称。 | | Class<?>[] | getParameterTypes() | 按照声明顺序返回一组 Class 对象,即返回Constructor 对象所表示构造方法的形参类型 | | T | newInstance(Object… initargs) | 使用此 Constructor对象表示的构造函数来创建新实例 | | String | toGenericString() | 返回描述此 Constructor 的字符串,其中包括类型参数。 |
代码演示如下
public class ConstructionTest { public static void main(String[] args) throws Exception { Class<?> clazz = null; // 获取Class对象的引用 clazz = Class.forName("com.ldl.baselearn.anno.reflect.ConstructionTestUser"); 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()); } }输出结果如下 ```java ——-getDeclaringClass——- 构造方法的类:com.ldl.baselearn.anno.reflect.ConstructionTestUser ——-getGenericParameterTypes——- 参数名称tp:int 参数名称tp:class java.lang.String ——-getParameterTypes——- 参数名称:int 参数名称:java.lang.String ——-getName——- getName:com.ldl.baselearn.anno.reflect.ConstructionTestUser ——-getoGenericString——- getoGenericString():private com.ldl.baselearn.anno.reflect.ConstructionTestUser(int,java.lang.String)
<a name="PhFGR"></a>
# Field类及其用法
- Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段
- 同样的道理,我们可以通过Class类的提供的方法来获取代表字段信息的Field对象,Class类与Field对象相关方法如下
| 方法返回值 | 方法名称 | 方法说明 |
| --- | --- | --- |
| Field | getDeclaredField(String name) | 获取指定name名称的(包含private修饰的)字段,不包括继承的字段 |
| Field[] | getDeclaredField() | 获取Class对象所表示的类或接口的所有(包含private修饰的)字段,不包括继承的字段 |
| Field | getField(String name) | 获取指定name名称、具有public修饰的字段,包含继承字段 |
| Field[] | getField() | 获取修饰符为public的字段,包含继承字段 |
- 下面的代码演示了上述方法的使用过程
```java
public class ReflectField {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("com.ldl.baselearn.anno.reflect.Student");
// 获取指定字段名称的Field类,注意字段修饰符必须为public而且存在该字段,
// 否则抛NoSuchFieldException
Field field = clazz.getField("age");
System.out.println("field:" + field);
// 获取所有修饰符为public的字段,包含父类字段,注意修饰符为public才会获取
Field fields[] = clazz.getFields();
for (Field f : fields) {
System.out.println("f:" + f.getName());
}
System.out.println("================getDeclaredFields====================");
// 获取当前类所字段(包含private字段),注意不包含父类的字段
Field fields2[] = clazz.getDeclaredFields();
for (Field f : fields2) {
System.out.println("f2:" + f.getName());
}
// 获取指定字段名称的Field类,可以是任意修饰符的自动,注意不包含父类的字段
Field field2 = clazz.getDeclaredField("desc");
System.out.println("field2:" + field2);
}
}
@Data
class Person {
public int age;
public String name;
}
@Data
class Student extends Person {
public String desc;
private int score;
}
输出结果如下
field:public int com.ldl.baselearn.anno.reflect.Person.age f:desc f:age f:name ================getDeclaredFields==================== f2:desc f2:score field2:public java.lang.String com.ldl.baselearn.anno.reflect.Student.desc上述方法需要注意的是,如果我们不期望获取其父类的字段,则需使用Class类的getDeclaredField/getDeclaredFields方法来获取字段即可,倘若需要连带获取到父类的字段,那么请使用Class类的getField/getFields,但是也只能获取到public修饰的的字段,无法获取父类的私有字段。下面将通过Field类本身的方法对指定类属性赋值,代码演示如下
public class ReflectField { public static void main(String[] args) throws Exception { //获取Class对象引用 Class<?> clazz = Class.forName("com.ldl.baselearn.anno.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(desc=I am student, score=88) //获取字段值 System.out.println(scoreField.get(st)); // 88 } }其中的set(Object obj, Object value)方法是Field类本身的方法,用于设置字段的值,而get(Object obj)则是获取字段的值,当然关于Field类还有其他常用的方法如下: | 方法返回值 | 方法名称 | 方法说明 | | —- | —- | —- | | void | set(Object obj, Object value) | 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。 | | Object | get(Object obj) | 返回指定对象上此 Field 表示的字段的值 | | Class<?> | getType() | 返回一个 Class 对象,它标识了此Field 对象所表示字段的声明类型。 | | boolean | isEnumConstant() | 如果此字段表示枚举类型的元素则返回 true;否则返回 false | | String | toGenericString() | 返回一个描述此 Field(包括其一般类型)的字符串 | | String | getName() | 返回此 Field 对象表示的字段的名称 | | Class<?> | getDeclaringClass() | 返回表示类或接口的 Class 对象,该类或接口声明由此 Field 对象表示的字段 | | void | setAccessible(boolean flag) | 将此对象的 accessible 标志设置为指示的布尔值,即设置其可访问性 |
上述方法可能是较为常用的,事实上在设置值的方法上,Field类还提供了专门针对基本数据类型的方法,如setInt()/getInt()、setBoolean()/getBoolean、setChar()/getChar()等等方法,这里就不全部列出了,需要时查API文档即可。需要特别注意的是被final关键字修饰的Field字段是安全的,在运行时可以接收任何修改,但最终其实际值是不会发生改变的。
Method类及其用法
Method 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息,所反映的方法可能是类方法或实例方法(包括抽象方法) | 方法返回值 | 方法名称 | 方法说明 | | —- | —- | —- | | Method | getDeclaredMethod(String name, Class<?>… parameterTypes) | 返回一个指定参数的Method对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。 | | Method[] | getDeclaredMethod() | 返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。 | | Method | getMethod(String name, Class<?>… parameterTypes) | 返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。 | | Method[] | getMethods() | 返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。 |
同样通过案例演示上述方法: ```java public class ReflectMethod { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
Class clazz = Class.forName("com.ldl.baselearn.anno.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,该方法无法获取继承自父类的method Method method1 = clazz.getDeclaredMethod("drawCircle"); System.out.println("method1::" + method1); // 获取当前类的所有方法包含private,该方法无法获取继承自父类的method Method[] methods1 = clazz.getDeclaredMethods(); for (Method m : methods1) { System.out.println("m1::" + m); }} }
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;
}
}
- 执行结果
```java
method:public void com.ldl.baselearn.anno.reflect.Shape.draw(int,java.lang.String)
m::public int com.ldl.baselearn.anno.reflect.Circle.getAllCount()
m::public void com.ldl.baselearn.anno.reflect.Shape.draw()
m::public void com.ldl.baselearn.anno.reflect.Shape.draw(int,java.lang.String)
m::public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
m::public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
m::public final void java.lang.Object.wait() throws java.lang.InterruptedException
m::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 com.ldl.baselearn.anno.reflect.Circle.drawCircle()
m1::private void com.ldl.baselearn.anno.reflect.Circle.drawCircle()
m1::public int com.ldl.baselearn.anno.reflect.Circle.getAllCount()
在通过getMethods方法获取Method对象时,会把父类的方法也获取到,如上的输出结果,把Object类的方法都打印出来了。而getDeclaredMethod/getDeclaredMethods方法都只能获取当前类的方法。我们在使用时根据情况选择即可。下面将演示通过Method对象调用指定类的方法
public class ReflectMethod { public static void main(String[] args) throws Exception { Class clazz = Class.forName("com.ldl.baselearn.anno.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=15 drawCircle count:100在上述代码中调用方法,使用了Method类的invoke(Object obj,Object… args)第一个参数代表调用的对象,第二个参数传递的调用方法的参数。这样就完成了类方法的动态调用 | 方法返回值 | 方法名称 | 方法说明 | | —- | —- | —- | | Object | invoke(Object obj, Object… args) | 对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。 | | Class<?> | getReturnType() | 返回一个 Class 对象,该对象描述了此 Method 对象所表示的方法的正式返回类型,即方法的返回类型 | | Type | getGenericReturnType() | 返回表示由此 Method 对象所表示方法的正式返回类型的 Type 对象,也是方法的返回类型。 | | Class<?>[] | getParameterTypes() | 按照声明顺序返回 Class 对象的数组,这些对象描述了此 Method 对象所表示的方法的形参类型。即返回方法的参数类型组成的数组 | | Type[] | getGenericParameterTypes() | 按照声明顺序返回 Type 对象的数组,这些对象描述了此 Method 对象所表示的方法的形参类型的,也是返回方法的参数类型 | | String | getName() | 以 String 形式返回此 Method 对象表示的方法名称,即返回方法的名称 | | boolean | isVarArgs() | 判断方法是否带可变参数,如果将此方法声明为带有可变数量的参数,则返回 true;否则,返回 false。 | | String | toGenericString() | 返回描述此 Method 的字符串,包括类型参数。 |
getReturnType方法/getGenericReturnType方法都是获取Method对象表示的方法的返回类型,只不过前者返回的Class类型后者返回的Type(前面已分析过),Type就是一个接口而已,在Java8中新增一个默认的方法实现,返回的就参数类型信息 ```java public interface Type { //1.8新增 default String getTypeName() {
return toString();} }
- 而getParameterTypes/getGenericParameterTypes也是同样的道理,都是获取Method对象所表示的方法的参数类型,其他方法与前面的Field和Constructor是类似的
<a name="XyIIA"></a>
# 反射机制执行的流程
- 先看个例子
```java
public class HelloReflect {
public static void main(String[] args) {
try {
// 1. 使用外部配置的实现,进行动态加载类
HelloReflect test = (HelloReflect) Class.forName("com.ldl.baselearn.anno.reflect.HelloReflect").newInstance();
test.sayHello("call directly");
// 2. 根据配置的函数名,进行方法调用(不需要通用的接口抽象)
Object t2 = new HelloReflect();
Method method = t2.getClass().getDeclaredMethod("sayHello", String.class);
method.invoke(test, "method invoke");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public void sayHello(String word) {
System.out.println("hello," + word);
}
}
- 来看执行流程
反射获取类实例
- 首先调用了java.lang.Class的静态方法,获取类信息 ```java @CallerSensitive public static Class<?> forName(String className) throws ClassNotFoundException { // 先通过反射,获取调用进来的类信息,从而获取当前的 classLoader Class<?> caller = Reflection.getCallerClass(); // 调用native方法进行获取class信息 return forName0(className, true, ClassLoader.getClassLoader(caller), caller); }
- forName()反射获取类信息,并没有将实现留给了java,而是交给了jvm去加载。
- 主要是先获取 ClassLoader, 然后调用 native 方法,获取信息,加载类则是回调 java.lang.ClassLoader
- 最后,jvm又会回调 ClassLoader 进类加载
```java
// load class
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
// sun.misc.Launcher
public Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException {
int var3 = var1.lastIndexOf(46);
if(var3 != -1) {
SecurityManager var4 = System.getSecurityManager();
if(var4 != null) {
var4.checkPackageAccess(var1.substring(0, var3));
}
}
if(this.ucp.knownToNotExist(var1)) {
Class var5 = this.findLoadedClass(var1);
if(var5 != null) {
if(var2) {
this.resolveClass(var5);
}
return var5;
} else {
throw new ClassNotFoundException(var1);
}
} else {
return super.loadClass(var1, var2);
}
}
// java.lang.ClassLoader
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// 先获取锁
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
// 如果已经加载了的话,就不用再加载了
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
// 双亲委托加载
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
// 父类没有加载到时,再自己加载
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
protected Object getClassLoadingLock(String className) {
Object lock = this;
if (parallelLockMap != null) {
// 使用 ConcurrentHashMap来保存锁
Object newLock = new Object();
lock = parallelLockMap.putIfAbsent(className, newLock);
if (lock == null) {
lock = newLock;
}
}
return lock;
}
protected final Class<?> findLoadedClass(String name) {
if (!checkName(name))
return null;
return findLoadedClass0(name);
}
下面来看一下 newInstance() 的实现方式 ```java // 首先肯定是 Class.newInstance @CallerSensitive public T newInstance() throws InstantiationException, IllegalAccessException{ if (System.getSecurityManager() != null) {
// 权限检查 checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);}
// NOTE: the following code may not be strictly correct under // the current Java memory model.
// Constructor lookup // newInstance() 其实相当于调用类的无参构造函数,所以,首先要找到其无参构造器 if (cachedConstructor == null) {
if (this == Class.class) { // 不允许调用 Class 的 newInstance() 方法 throw new IllegalAccessException( "Can not call newInstance() on the Class for java.lang.Class" ); } try { // 获取无参构造器 Class<?>[] empty = {}; final Constructor<T> c = getConstructor0(empty, Member.DECLARED); // Disable accessibility checks on the constructor // since we have to do the security check here anyway // (the stack depth is wrong for the Constructor's // security check to work) java.security.AccessController.doPrivileged( new java.security.PrivilegedAction<Void>() { public Void run() { c.setAccessible(true); return null; } }); cachedConstructor = c; } catch (NoSuchMethodException e) { throw (InstantiationException) new InstantiationException(getName()).initCause(e); }} Constructor
tmpConstructor = cachedConstructor; // Security check (same as in java.lang.reflect.Constructor) int modifiers = tmpConstructor.getModifiers(); if (!Reflection.quickCheckMemberAccess(this, modifiers)) { Class<?> caller = Reflection.getCallerClass(); if (newInstanceCallerCache != caller) { Reflection.ensureMemberAccess(caller, this, null, modifiers); newInstanceCallerCache = caller; }} // Run constructor try {
// 调用无参构造器 return tmpConstructor.newInstance((Object[])null);} catch (InvocationTargetException e) {
Unsafe.getUnsafe().throwException(e.getTargetException()); // Not reached return null;} }
- newInstance() 主要做了三件事:
- 权限检测,如果不通过直接抛出异常;
- 查找无参构造器,并将其缓存起来;
- 调用具体方法的无参构造方法,生成实例并返回;
- 下面是获取构造器的过程
```java
private Constructor<T> getConstructor0(Class<?>[] parameterTypes,
int which) throws NoSuchMethodException{
// 获取所有构造器
Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
for (Constructor<T> constructor : constructors) {
if (arrayContentsEq(parameterTypes,
constructor.getParameterTypes())) {
return getReflectionFactory().copyConstructor(constructor);
}
}
throw new NoSuchMethodException(getName() + ".<init>" + argumentTypesToString(parameterTypes));
}
- getConstructor0() 为获取匹配的构造方器;分三步
- 先获取所有的constructors, 然后通过进行参数类型比较
- 找到匹配后,通过 ReflectionFactory copy一份constructor返回
- 否则抛出 NoSuchMethodExceptio ```java
// 获取当前类所有的构造方法,通过jvm或者缓存
// Returns an array of “root” constructors. These Constructor
// objects must NOT be propagated to the outside world, but must
// instead be copied via ReflectionFactory.copyConstructor.
private Constructor
// Lazily create and cache ReflectionData
private ReflectionData
// 新创建缓存,保存反射信息
private ReflectionData
// 使用cas保证更新的线程安全性,所以反射是保证线程安全的
while (true) {
ReflectionData<T> rd = new ReflectionData<>(classRedefinedCount);
// try to CAS it...
if (Atomic.casReflectionData(this, oldReflectionData, new SoftReference<>(rd))) {
return rd;
}
// 先使用CAS更新,如果更新成功,则立即返回,否则测查当前已被其他线程更新的情况,如果和自己想要更新的状态一致,则也算是成功了
oldReflectionData = this.reflectionData;
classRedefinedCount = this.classRedefinedCount;
if (oldReflectionData != null &&
(rd = oldReflectionData.get()) != null &&
rd.redefinedCount == classRedefinedCount) {
return rd;
}
}
}
- 如上,privateGetDeclaredConstructors(), 获取所有的构造器主要步骤
- 先尝试从缓存中获取
- 如果缓存没有,则从jvm中重新获取,并存入缓存,缓存使用软引用进行保存,保证内存可用
- 另外,使用 relactionData() 进行缓存保存;ReflectionData 的数据结构如下
```java
// reflection data that might get invalidated when JVM TI RedefineClasses() is called
private static class ReflectionData<T> {
volatile Field[] declaredFields;
volatile Field[] publicFields;
volatile Method[] declaredMethods;
volatile Method[] publicMethods;
volatile Constructor<T>[] declaredConstructors;
volatile Constructor<T>[] publicConstructors;
// Intermediate results for getFields and getMethods
volatile Field[] declaredPublicFields;
volatile Method[] declaredPublicMethods;
volatile Class<?>[] interfaces;
// Value of classRedefinedCount when we created this ReflectionData instance
final int redefinedCount;
ReflectionData(int redefinedCount) {
this.redefinedCount = redefinedCount;
}
}
其中,还有一个点,就是如何比较构造是否是要查找构造器,其实就是比较类型完成相等就完了,有一个不相等则返回false ```java private static boolean arrayContentsEq(Object[] a1, Object[] a2) { if (a1 == null) {
return a2 == null || a2.length == 0;}
if (a2 == null) {
return a1.length == 0;}
if (a1.length != a2.length) {
return false;}
for (int i = 0; i < a1.length; i++) {
if (a1[i] != a2[i]) { return false; }}
return true; } // sun.reflect.ReflectionFactory /** Makes a copy of the passed constructor. The returned
constructor is a "child" of the passed one; see the comments in Constructor.java for details. */public
Constructor copyConstructor(Constructor arg) { return langReflectAccess().copyConstructor(arg); }
// java.lang.reflect.Constructor, copy 其实就是新new一个 Constructor 出来
Constructor
Constructor<T> res = new Constructor<>(clazz,
parameterTypes,
exceptionTypes, modifiers, slot,
signature,
annotations,
parameterAnnotations);
// root 指向当前 constructor
res.root = this;
// Might as well eagerly propagate this if already present
res.constructorAccessor = constructorAccessor;
return res;
}
- 通过上面,获取到 Constructor 了
- 接下来就只需调用其相应构造器的 newInstance(),即返回实例了
```java
// return tmpConstructor.newInstance((Object[])null);
// java.lang.reflect.Constructor
@CallerSensitive
public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException {
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, null, modifiers);
}
}
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
ConstructorAccessor ca = constructorAccessor; // read volatile
if (ca == null) {
ca = acquireConstructorAccessor();
}
@SuppressWarnings("unchecked")
T inst = (T) ca.newInstance(initargs);
return inst;
}
// sun.reflect.DelegatingConstructorAccessorImpl
public Object newInstance(Object[] args)
throws InstantiationException,
IllegalArgumentException,
InvocationTargetException {
return delegate.newInstance(args);
}
// sun.reflect.NativeConstructorAccessorImpl
public Object newInstance(Object[] args)
throws InstantiationException,IllegalArgumentException,InvocationTargetException {
// We can't inflate a constructor belonging to a vm-anonymous class
// because that kind of class can't be referred to by name, hence can't
// be found from the generated bytecode.
if (++numInvocations > ReflectionFactory.inflationThreshold()
&& !ReflectUtil.isVMAnonymousClass(c.getDeclaringClass())) {
ConstructorAccessorImpl acc = (ConstructorAccessorImpl)
new MethodAccessorGenerator().
generateConstructor(c.getDeclaringClass(),
c.getParameterTypes(),
c.getExceptionTypes(),
c.getModifiers());
parent.setDelegate(acc);
}
// 调用native方法,进行调用 constructor
return newInstance0(c, args);
}
返回构造器的实例后,可以根据外部进行进行类型转换,从而使用接口或方法进行调用实例功能了
反射获取方法
第一步,先获取Method ```java // java.lang.Class @CallerSensitive public Method getDeclaredMethod(String name, Class<?>… parameterTypes) throws NoSuchMethodException, SecurityException { // 检查 checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true); // 寻找方法 Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes); if (method == null) {
throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));} return method; }
- 忽略第一个检查权限,剩下就只有两个动作了
- 获取所有方法列表
- 根据方法名称和方法列表,选出符合要求的方法
- 如果没有找到相应方法,抛出异常,否则返回对应方法
- 所以,先看一下怎样获取类声明的所有方法
```java
// Returns an array of "root" methods. These Method objects must NOT
// be propagated to the outside world, but must instead be copied
// via ReflectionFactory.copyMethod.
private Method[] privateGetDeclaredMethods(boolean publicOnly) {
checkInitted();
Method[] res;
ReflectionData<T> rd = reflectionData();
if (rd != null) {
res = publicOnly ? rd.declaredPublicMethods : rd.declaredMethods;
if (res != null) return res;
}
// No cached value available; request value from VM
res = Reflection.filterMethods(this, getDeclaredMethods0(publicOnly));
if (rd != null) {
if (publicOnly) {
rd.declaredPublicMethods = res;
} else {
rd.declaredMethods = res;
}
}
return res;
}
- 很相似,和获取所有构造器的方法很相似,都是先从缓存中获取方法,如果没有,则从jvm中获取
- 不同的是,方法列表需要进行过滤 Reflection.filterMethods;当然后面看来,这个方法我们一般不会派上用场 ```java
// sun.misc.Reflection public static Method[] filterMethods(Class<?> containingClass, Method[] methods) { if (methodFilterMap == null) { // Bootstrapping return methods; } return (Method[])filter(methods, methodFilterMap.get(containingClass)); } // 可以过滤指定的方法,一般为空,如果要指定过滤,可以调用 registerMethodsToFilter(), 或者… private static Member[] filter(Member[] members, String[] filteredNames) { if ((filteredNames == null) || (members.length == 0)) { return members; } int numNewMembers = 0; for (Member member : members) { boolean shouldSkip = false; for (String filteredName : filteredNames) { if (member.getName() == filteredName) { shouldSkip = true; break; } } if (!shouldSkip) { ++numNewMembers; } } Member[] newMembers = (Member[])Array.newInstance(members[0].getClass(), numNewMembers); int destIdx = 0; for (Member member : members) { boolean shouldSkip = false; for (String filteredName : filteredNames) { if (member.getName() == filteredName) { shouldSkip = true; break; } } if (!shouldSkip) { newMembers[destIdx++] = member; } } return newMembers; }
- 第二步:根据方法名和参数类型过滤指定方法返回
```java
private static Method searchMethods(Method[] methods,
String name,
Class<?>[] parameterTypes){
Method res = null;
// 使用常量池,避免重复创建String
String internedName = name.intern();
for (int i = 0; i < methods.length; i++) {
Method m = methods[i];
if (m.getName() == internedName
&& arrayContentsEq(parameterTypes, m.getParameterTypes())
&& (res == null
|| res.getReturnType().isAssignableFrom(m.getReturnType())))
res = m;
}
return (res == null ? res : getReflectionFactory().copyMethod(res));
}
- 大概意思看得明白,就是匹配到方法名,然后参数类型匹配,才可以
- 但是可以看到,匹配到一个方法,并没有退出for循环,而是继续进行匹配
- 这里是匹配最精确的子类进行返回(最优匹配)
- 最后,还是通过 ReflectionFactory, copy 方法后返回
调用method.invoke()方法
```java @CallerSensitive public Object invoke(Object obj, Object… args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException{ if (!override) { if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
} } MethodAccessor ma = methodAccessor; // read volatile if (ma == null) { ma = acquireMethodAccessor(); } return ma.invoke(obj, args); }Class<?> caller = Reflection.getCallerClass(); checkAccess(caller, clazz, obj, modifiers);
- invoke时,是通过 MethodAccessor 进行调用的,而 MethodAccessor 是个接口,在第一次时调用 acquireMethodAccessor() 进行新创建
```java
// probably make the implementation more scalable.
private MethodAccessor acquireMethodAccessor() {
// First check to see if one has been created yet, and take it
// if so
MethodAccessor tmp = null;
if (root != null) tmp = root.getMethodAccessor();
if (tmp != null) {
// 存在缓存时,存入 methodAccessor,否则调用 ReflectionFactory 创建新的 MethodAccessor
methodAccessor = tmp;
} else {
// Otherwise fabricate one and propagate it up to the root
tmp = reflectionFactory.newMethodAccessor(this);
setMethodAccessor(tmp);
}
return tmp;
}
// sun.reflect.ReflectionFactory
public MethodAccessor newMethodAccessor(Method method) {
checkInitted();
if (noInflation && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {
return new MethodAccessorGenerator().
generateMethod(method.getDeclaringClass(),
method.getName(),
method.getParameterTypes(),
method.getReturnType(),
method.getExceptionTypes(),
method.getModifiers());
} else {
NativeMethodAccessorImpl acc =
new NativeMethodAccessorImpl(method);
DelegatingMethodAccessorImpl res =
new DelegatingMethodAccessorImpl(acc);
acc.setParent(res);
return res;
}
}
- 两个Accessor详情 ```java
// NativeMethodAccessorImpl / DelegatingMethodAccessorImpl class NativeMethodAccessorImpl extends MethodAccessorImpl { private final Method method; private DelegatingMethodAccessorImpl parent; private int numInvocations;
NativeMethodAccessorImpl(Method method) {
this.method = method;
}
public Object invoke(Object obj, Object[] args)
throws IllegalArgumentException, InvocationTargetException
{
// We can't inflate methods belonging to vm-anonymous classes because
// that kind of class can't be referred to by name, hence can't be
// found from the generated bytecode.
if (++numInvocations > ReflectionFactory.inflationThreshold()
&& !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {
MethodAccessorImpl acc = (MethodAccessorImpl)
new MethodAccessorGenerator().
generateMethod(method.getDeclaringClass(),
method.getName(),
method.getParameterTypes(),
method.getReturnType(),
method.getExceptionTypes(),
method.getModifiers());
parent.setDelegate(acc);
}
return invoke0(method, obj, args);
}
void setParent(DelegatingMethodAccessorImpl parent) {
this.parent = parent;
}
private static native Object invoke0(Method m, Object obj, Object[] args);
} class DelegatingMethodAccessorImpl extends MethodAccessorImpl { private MethodAccessorImpl delegate;
DelegatingMethodAccessorImpl(MethodAccessorImpl delegate) {
setDelegate(delegate);
}
public Object invoke(Object obj, Object[] args)
throws IllegalArgumentException, InvocationTargetException
{
return delegate.invoke(obj, args);
}
void setDelegate(MethodAccessorImpl delegate) {
this.delegate = delegate;
}
}
- 进行 ma.invoke(obj, args); 调用时,调用 DelegatingMethodAccessorImpl.invoke();
- 最后被委托到 NativeMethodAccessorImpl.invoke(), 即
```java
public Object invoke(Object obj, Object[] args)
throws IllegalArgumentException, InvocationTargetException {
// We can't inflate methods belonging to vm-anonymous classes because
// that kind of class can't be referred to by name, hence can't be
// found from the generated bytecode.
if (++numInvocations > ReflectionFactory.inflationThreshold()
&& !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {
MethodAccessorImpl acc = (MethodAccessorImpl)
new MethodAccessorGenerator().
generateMethod(method.getDeclaringClass(),
method.getName(),
method.getParameterTypes(),
method.getReturnType(),
method.getExceptionTypes(),
method.getModifiers());
parent.setDelegate(acc);
}
// invoke0 是个 native 方法,由jvm进行调用业务方法。从而完成反射调用功能。
return invoke0(method, obj, args);
}
- 其中, generateMethod() 是生成具体类的方法
```java
/* This routine is not thread-safe /
public MethodAccessor generateMethod(Class<?> declaringClass,
return (MethodAccessor) generate(declaringClass,String name, Class<?>[] parameterTypes, Class<?> returnType, Class<?>[] checkedExceptions, int modifiers){
}name, parameterTypes, returnType, checkedExceptions, modifiers, false, false, null);
- generate() 戳详情
```java
/** This routine is not thread-safe */
private MagicAccessorImpl generate(final Class<?> declaringClass,
String name,
Class<?>[] parameterTypes,
Class<?> returnType,
Class<?>[] checkedExceptions,
int modifiers,
boolean isConstructor,
boolean forSerialization,
Class<?> serializationTargetClass)
{
ByteVector vec = ByteVectorFactory.create();
asm = new ClassFileAssembler(vec);
this.declaringClass = declaringClass;
this.parameterTypes = parameterTypes;
this.returnType = returnType;
this.modifiers = modifiers;
this.isConstructor = isConstructor;
this.forSerialization = forSerialization;
asm.emitMagicAndVersion();
// Constant pool entries:
// ( * = Boxing information: optional)
// (+ = Shared entries provided by AccessorGenerator)
// (^ = Only present if generating SerializationConstructorAccessor)
// [UTF-8] [This class's name]
// [CONSTANT_Class_info] for above
// [UTF-8] "sun/reflect/{MethodAccessorImpl,ConstructorAccessorImpl,SerializationConstructorAccessorImpl}"
// [CONSTANT_Class_info] for above
// [UTF-8] [Target class's name]
// [CONSTANT_Class_info] for above
// ^ [UTF-8] [Serialization: Class's name in which to invoke constructor]
// ^ [CONSTANT_Class_info] for above
// [UTF-8] target method or constructor name
// [UTF-8] target method or constructor signature
// [CONSTANT_NameAndType_info] for above
// [CONSTANT_Methodref_info or CONSTANT_InterfaceMethodref_info] for target method
// [UTF-8] "invoke" or "newInstance"
// [UTF-8] invoke or newInstance descriptor
// [UTF-8] descriptor for type of non-primitive parameter 1
// [CONSTANT_Class_info] for type of non-primitive parameter 1
// ...
// [UTF-8] descriptor for type of non-primitive parameter n
// [CONSTANT_Class_info] for type of non-primitive parameter n
// + [UTF-8] "java/lang/Exception"
// + [CONSTANT_Class_info] for above
// + [UTF-8] "java/lang/ClassCastException"
// + [CONSTANT_Class_info] for above
// + [UTF-8] "java/lang/NullPointerException"
// + [CONSTANT_Class_info] for above
// + [UTF-8] "java/lang/IllegalArgumentException"
// + [CONSTANT_Class_info] for above
// + [UTF-8] "java/lang/InvocationTargetException"
// + [CONSTANT_Class_info] for above
// + [UTF-8] "<init>"
// + [UTF-8] "()V"
// + [CONSTANT_NameAndType_info] for above
// + [CONSTANT_Methodref_info] for NullPointerException's constructor
// + [CONSTANT_Methodref_info] for IllegalArgumentException's constructor
// + [UTF-8] "(Ljava/lang/String;)V"
// + [CONSTANT_NameAndType_info] for "<init>(Ljava/lang/String;)V"
// + [CONSTANT_Methodref_info] for IllegalArgumentException's constructor taking a String
// + [UTF-8] "(Ljava/lang/Throwable;)V"
// + [CONSTANT_NameAndType_info] for "<init>(Ljava/lang/Throwable;)V"
// + [CONSTANT_Methodref_info] for InvocationTargetException's constructor
// + [CONSTANT_Methodref_info] for "super()"
// + [UTF-8] "java/lang/Object"
// + [CONSTANT_Class_info] for above
// + [UTF-8] "toString"
// + [UTF-8] "()Ljava/lang/String;"
// + [CONSTANT_NameAndType_info] for "toString()Ljava/lang/String;"
// + [CONSTANT_Methodref_info] for Object's toString method
// + [UTF-8] "Code"
// + [UTF-8] "Exceptions"
// * [UTF-8] "java/lang/Boolean"
// * [CONSTANT_Class_info] for above
// * [UTF-8] "(Z)V"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
// * [UTF-8] "booleanValue"
// * [UTF-8] "()Z"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
// * [UTF-8] "java/lang/Byte"
// * [CONSTANT_Class_info] for above
// * [UTF-8] "(B)V"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
// * [UTF-8] "byteValue"
// * [UTF-8] "()B"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
// * [UTF-8] "java/lang/Character"
// * [CONSTANT_Class_info] for above
// * [UTF-8] "(C)V"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
// * [UTF-8] "charValue"
// * [UTF-8] "()C"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
// * [UTF-8] "java/lang/Double"
// * [CONSTANT_Class_info] for above
// * [UTF-8] "(D)V"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
// * [UTF-8] "doubleValue"
// * [UTF-8] "()D"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
// * [UTF-8] "java/lang/Float"
// * [CONSTANT_Class_info] for above
// * [UTF-8] "(F)V"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
// * [UTF-8] "floatValue"
// * [UTF-8] "()F"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
// * [UTF-8] "java/lang/Integer"
// * [CONSTANT_Class_info] for above
// * [UTF-8] "(I)V"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
// * [UTF-8] "intValue"
// * [UTF-8] "()I"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
// * [UTF-8] "java/lang/Long"
// * [CONSTANT_Class_info] for above
// * [UTF-8] "(J)V"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
// * [UTF-8] "longValue"
// * [UTF-8] "()J"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
// * [UTF-8] "java/lang/Short"
// * [CONSTANT_Class_info] for above
// * [UTF-8] "(S)V"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
// * [UTF-8] "shortValue"
// * [UTF-8] "()S"
// * [CONSTANT_NameAndType_info] for above
// * [CONSTANT_Methodref_info] for above
short numCPEntries = NUM_BASE_CPOOL_ENTRIES + NUM_COMMON_CPOOL_ENTRIES;
boolean usesPrimitives = usesPrimitiveTypes();
if (usesPrimitives) {
numCPEntries += NUM_BOXING_CPOOL_ENTRIES;
}
if (forSerialization) {
numCPEntries += NUM_SERIALIZATION_CPOOL_ENTRIES;
}
// Add in variable-length number of entries to be able to describe
// non-primitive parameter types and checked exceptions.
numCPEntries += (short) (2 * numNonPrimitiveParameterTypes());
asm.emitShort(add(numCPEntries, S1));
final String generatedName = generateName(isConstructor, forSerialization);
asm.emitConstantPoolUTF8(generatedName);
asm.emitConstantPoolClass(asm.cpi());
thisClass = asm.cpi();
if (isConstructor) {
if (forSerialization) {
asm.emitConstantPoolUTF8
("sun/reflect/SerializationConstructorAccessorImpl");
} else {
asm.emitConstantPoolUTF8("sun/reflect/ConstructorAccessorImpl");
}
} else {
asm.emitConstantPoolUTF8("sun/reflect/MethodAccessorImpl");
}
asm.emitConstantPoolClass(asm.cpi());
superClass = asm.cpi();
asm.emitConstantPoolUTF8(getClassName(declaringClass, false));
asm.emitConstantPoolClass(asm.cpi());
targetClass = asm.cpi();
short serializationTargetClassIdx = (short) 0;
if (forSerialization) {
asm.emitConstantPoolUTF8(getClassName(serializationTargetClass, false));
asm.emitConstantPoolClass(asm.cpi());
serializationTargetClassIdx = asm.cpi();
}
asm.emitConstantPoolUTF8(name);
asm.emitConstantPoolUTF8(buildInternalSignature());
asm.emitConstantPoolNameAndType(sub(asm.cpi(), S1), asm.cpi());
if (isInterface()) {
asm.emitConstantPoolInterfaceMethodref(targetClass, asm.cpi());
} else {
if (forSerialization) {
asm.emitConstantPoolMethodref(serializationTargetClassIdx, asm.cpi());
} else {
asm.emitConstantPoolMethodref(targetClass, asm.cpi());
}
}
targetMethodRef = asm.cpi();
if (isConstructor) {
asm.emitConstantPoolUTF8("newInstance");
} else {
asm.emitConstantPoolUTF8("invoke");
}
invokeIdx = asm.cpi();
if (isConstructor) {
asm.emitConstantPoolUTF8("([Ljava/lang/Object;)Ljava/lang/Object;");
} else {
asm.emitConstantPoolUTF8
("(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
}
invokeDescriptorIdx = asm.cpi();
// Output class information for non-primitive parameter types
nonPrimitiveParametersBaseIdx = add(asm.cpi(), S2);
for (int i = 0; i < parameterTypes.length; i++) {
Class<?> c = parameterTypes[i];
if (!isPrimitive(c)) {
asm.emitConstantPoolUTF8(getClassName(c, false));
asm.emitConstantPoolClass(asm.cpi());
}
}
// Entries common to FieldAccessor, MethodAccessor and ConstructorAccessor
emitCommonConstantPoolEntries();
// Boxing entries
if (usesPrimitives) {
emitBoxingContantPoolEntries();
}
if (asm.cpi() != numCPEntries) {
throw new InternalError("Adjust this code (cpi = " + asm.cpi() +
", numCPEntries = " + numCPEntries + ")");
}
// Access flags
asm.emitShort(ACC_PUBLIC);
// This class
asm.emitShort(thisClass);
// Superclass
asm.emitShort(superClass);
// Interfaces count and interfaces
asm.emitShort(S0);
// Fields count and fields
asm.emitShort(S0);
// Methods count and methods
asm.emitShort(NUM_METHODS);
emitConstructor();
emitInvoke();
// Additional attributes (none)
asm.emitShort(S0);
// Load class
vec.trim();
final byte[] bytes = vec.getData();
// Note: the class loader is the only thing that really matters
// here -- it's important to get the generated code into the
// same namespace as the target class. Since the generated code
// is privileged anyway, the protection domain probably doesn't
// matter.
return AccessController.doPrivileged(
new PrivilegedAction<MagicAccessorImpl>() {
public MagicAccessorImpl run() {
try {
return (MagicAccessorImpl)
ClassDefiner.defineClass
(generatedName,
bytes,
0,
bytes.length,
declaringClass.getClassLoader()).newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new InternalError(e);
}
}
});
}
- 主要看这一句:ClassDefiner.defineClass(xx, declaringClass.getClassLoader()).newInstance();
- 在ClassDefiner.defineClass方法实现中,每被调用一次都会生成一个DelegatingClassLoader类加载器对象 ,这里每次都生成新的类加载器,是为了性能考虑,在某些情况下可以卸载这些生成的类,因为类的卸载是只有在类加载器可以被回收的情况下才会被回收的,如果用了原来的类加载器,那可能导致这些新创建的类一直无法被卸载。
而反射生成的类,有时候可能用了就可以卸载了,所以使用其独立的类加载器,从而使得更容易控制反射类的生命周期
反射调用小结
反射类及反射方法的获取,都是通过从列表中搜寻查找匹配的方法,所以查找性能会随类的大小方法多少而变化;
- 每个类都会有一个与之对应的Class实例,从而每个类都可以获取method反射方法,并作用到其他实例身上;
- 反射也是考虑了线程安全的,放心使用;
- 反射使用软引用relectionData缓存class信息,避免每次重新从jvm获取带来的开销;
- 反射调用多次生成新代理Accessor, 而通过字节码生存的则考虑了卸载功能,所以会使用独立的类加载器;
- 当找到需要的方法,都会copy一份出来,而不是使用原来的实例,从而保证数据隔离;
-
反射操作泛型
Java采用泛型擦除的机制来引入泛型,Java中的泛型仅仅是给编译器javac使用的,避免数据的安全性和免去强制类型转换的问题,但是一旦,编译完成,所有和泛型有关的类型会全部擦除
- 为了通过反射操作这些类型,Java新增了ParameteriedType GenericArrayType TypeVarilable 和 WildcardType 几种类型标识不能被归一待Class类中但是和原始类型齐名的类型
- ParameteriedType:表示一种参数化类型 比如 :Collection
- GenericArrayType:表示一种元素类型是参数化类型或者剋下变量的数组类型
- TypeVarilable:是各种类型变量的公共父接口
WildcardType :代表一种通配符类型表达式
public class Test08 { public void test01(Map<String, User> map, List<User> list) { System.out.println("Test08.test01"); } public Map<String, User> test02() { System.out.println("Test08.test02"); return null; } public static void main(String[] args) throws Exception { Method test01 = Test08.class.getMethod("test01", Map.class, List.class); Type[] getGenericParameterTypes = test01.getGenericParameterTypes(); for (Type getGenericParameterType : getGenericParameterTypes) { System.out.println("* " + getGenericParameterType); if (getGenericParameterType instanceof ParameterizedType) { Type[] actualTypeArguments = ((ParameterizedType) getGenericParameterType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println(actualTypeArgument); } } } System.out.println("========================"); Method test02 = Test08.class.getMethod("test02"); Type genericReturnType = test02.getGenericReturnType(); System.out.println(genericReturnType); if (genericReturnType instanceof ParameterizedType) { Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println(actualTypeArgument); } } } }反射的练习:ORM
```java public class StudentMain { public static void main(String[] args) throws Exception {
// 1. 获取对象 Class<?> clazz = Class.forName("com.ldl.baselearn.anno.orm.Student"); System.out.println("------------------------------------"); // 2. 通过反射获取注解 Annotation[] annotations = clazz.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } System.out.println("------------------------------------"); Table table = clazz.getAnnotation(Table.class); String tableValue = table.value(); System.out.println("table:" + tableValue); System.out.println("------------------------------------"); Field nameField = clazz.getDeclaredField("name"); nameField.setAccessible(true); Column nameFieldAnnotation = nameField.getAnnotation(Column.class); System.out.println("columnName:" + nameFieldAnnotation.columnName() + ",type:" + nameFieldAnnotation.type() + ",length:" + nameFieldAnnotation.length()); System.out.println("------------------------------------"); Field[] declaredFields = clazz.getDeclaredFields(); for (Field declaredField : declaredFields) { declaredField.setAccessible(true); Column annotation = declaredField.getAnnotation(Column.class); System.out.println("columnName:" + annotation.columnName() + ",type:" + annotation.type() + ",length:" + annotation.length()); }} }
- 输出结果
```java
------------------------------------
@com.ldl.baselearn.anno.orm.Table(value=student)
------------------------------------
table:student
------------------------------------
columnName:name,type:varchar,length:13
------------------------------------
columnName:id,type:int,length:11
columnName:age,type:int,length:12
columnName:name,type:varchar,length:13
