Shadow, with her veil drawn, follows Light in secret meekness,with her silent steps of love.
阴影戴上她的面幕,秘密地,温顺地,用她的沉默的爱的脚步,跟在“光”后边。
注解 Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。
Java 语言中的类、方法、变量、参数和包等都可以被标注。
Annotation作用
不是程序本身,可以对程序作出解释
可以被其他程序(如编译器等)读取
Annotation格式
注解是以”@注释名”在代码中存在,还可以添加一些参数值
Annotation在哪里使用?
可以附加在package,class,method,field等上面相当于给他们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些元数据的访问
内置的注解 Java 定义了一套注解,共有 7 个,3 个在 java.lang 中,剩下 4 个在 java.lang.annotation 中。
作用在代码的注解是
@Override
- 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
@Deprecated
- 标记过时方法。如果使用该方法,会报编译警告。表示不鼓励程序员
@SuppressWarnings
- 指示编译器去忽略注解中声明的警告。
作用在其他注解的注解(或者说 元注解)是
@Retention
- 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。(SOURCE < CLASS < RUNTIME)
a) 若 Annotation 的类型为 SOURCE,则意味着:Annotation 仅存在于编译器处理期间,编译器处理完之后,该 Annotation 就没用了。 例如,” @Override” 标志就是一个 Annotation。当它修饰一个方法的时候,就意味着该方法覆盖父类的方法;并且在编译期间会进行语法检查!编译器处理完后,”@Override” 就没有任何作用了。
b) 若 Annotation 的类型为 CLASS,则意味着:编译器将 Annotation 存储于类对应的 .class 文件中,它是 Annotation 的默认行为。
c) 若 Annotation 的类型为 RUNTIME,则意味着:编译器将 Annotation 存储于 class 文件中,并且可由JVM读入。
@Documented
- 标记这些注解是否包含在用户文档中。
@Target
- 标记这个注解应该是哪种 Java 成员。(被描述的注解可以用在什么地方)
@Inherited
- 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)
使用元注解自定义一个简单注解
1 2 3 4 5 @Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation1 { }
@interface
使用 @interface 定义注解时,意味着它实现了 java.lang.annotation.Annotation 接口,即该注解就是一个Annotation。
定义 Annotation 时,@interface 是必须的。
注意:它和我们通常的 implemented 实现接口的方法不同。Annotation 接口的实现细节都由编译器完成。通过 @interface 定义注解后,该注解不能继承其他的注解或接口。
@Documented*
类和方法的 Annotation 在缺省情况下是不出现在 javadoc 中的。如果使用 @Documented 修饰该 Annotation,则表示它可以出现在 javadoc 中。
定义 Annotation 时,@Documented 可有可无;若没有定义,则 Annotation 不会出现在 javadoc 中。
@Target(ElementType.TYPE)
前面我们说过,ElementType 是 Annotation 的类型属性。而 @Target 的作用,就是来指定 Annotation 的类型属性。
@Target(ElementType.TYPE) 的意思就是指定该 Annotation 的类型是 ElementType.TYPE。这就意味着,MyAnnotation1 是来修饰”类、接口(包括注释类型)或枚举声明”的注解。
定义 Annotation 时,@Target 可有可无。若有 @Target,则该 Annotation 只能用于它所指定的地方;若没有 @Target,则该 Annotation 可以用于任何地方。
@Retention(RetentionPolicy.RUNTIME)
前面我们说过,RetentionPolicy 是 Annotation 的策略属性,而 @Retention 的作用,就是指定 Annotation 的策略属性。
@Retention(RetentionPolicy.RUNTIME) 的意思就是指定该 Annotation 的策略是 RetentionPolicy.RUNTIME。这就意味着,编译器会将该 Annotation 信息保留在 .class 文件中,并且能被虚拟机读取。
定义 Annotation 时,@Retention 可有可无。若没有 @Retention,则默认是 RetentionPolicy.CLASS。
从 Java 7 开始,额外添加了 3 个注解
@SafeVarargs
- Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
@FunctionalInterface
- Java 8 开始支持,标识一个匿名函数或函数式接口。
@Repeatable
- Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
Annotation 架构
从中,我们可以看出:
1 个 Annotation 和 1 个 RetentionPolicy 关联。
每1个Annotation对象,都会有唯一的RetentionPolicy属性。
1 个 Annotation 和 1~n 个 ElementType 关联。
对于每 1 个 Annotation 对象,可以有若干个 ElementType 属性。
Annotation 有许多实现类,包括:Deprecated, Documented, Inherited, Override 等等。
Annotation 的每一个实现类,都 “和 1 个 RetentionPolicy 关联” 并且 “ 和 1~n 个 ElementType 关联”。
1 2 3 4 5 6 7 @Deprecated -- @Deprecated 所标注内容,不再被建议使用。@Override -- @Override 只能标注方法,表示该方法覆盖父类中的方法。@Documented -- @Documented 所标注内容,可以出现在javadoc中。@Inherited -- @Inherited 只能被用来标注“Annotation类型”,它所标注的Annotation具有继承性。@Retention -- @Retention 只能被用来标注“Annotation类型”,而且它被用来指定Annotation的RetentionPolicy属性。@Target -- @Target 只能被用来标注“Annotation类型”,而且它被用来指定Annotation的ElementType属性。@SuppressWarnings -- @SuppressWarnings 所标注内容产生的警告,编译器会对这些警告保持静默。
自定义注解 使用@interface自定义注解时,自动继承 java.lang.annotation.Annotation接口
分析:
@interface用来声明一个注解,格式public @interface 注解名{定义内容}
其中每一个方法实际上是声明了一个参数配置;
方法的名称就是参数的名称;
返回值类型就是参数的类型(返回值只能是基本类型,Class,String,enum);
可以通过default来声明参数的默认值;
如果只有一个参数成员,一般参数名为value;
注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class TestAnnotation { @MyAnnotation(name = "bobo") public void test () {} @MyAnnotation0("bobo") public void test0 () {} }@Documented @Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation{ String name () ; int age () default 0 ; int id () default ; }@interface MyAnnotation0{ String value () ; }
Annotation 的作用
编译检查
在反射中使用 Annotation
根据 Annotation 生成帮助文档
能够帮忙查看查看代码
反射机制 Class c = Class.forName("java.lang.String")
反射指的是我们可以在运行期间加载、探知、使用编译期间完全未知的类。是一个动态的机制,允许我们通过字符串来指挥程序实例化,操作属性、调用方法。使得代码提高了灵活性,但是同时也带来了更多的资源开销。
加载完类之后,在堆内存中,就产生了一个 Class 类型的对象(一个 类只有一个 Class 对象),这个对象就包含了完整的类的结构信息。 我们可以通过这个对象看到类的结构。
正常方式:引入需要的“包类”名称
—>通过new实例化
—>取的实例化对象
反射方式:实例化对象
—>getCLass()方法
—>得到完整的“包类”名称
Java中为什么需要反射?反射要解决什么问题? Java中编译类型有两种:
静态编译 :在编译时确定类型,绑定对象即通过。
动态编译 :运行时确定类型,绑定对象。动态编译最大限度地发挥了Java的灵活性,体现了多态的应用,可以减低类之间的耦合性。
反射是Java被视为动态(或准动态)语言的关键,反射机制允许程序在执行期借助Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。包括其modifiers(诸如public、static等)、superclass(例如Object)、实现之interfaces(例如Cloneable),也包括fields和methods的所有信息,并可于运行时改变fields内容或唤起methods。
Reflection可以在运行时加载、探知、使用编译期间完全未知的classes。即Java程序可以加载一个运行时才得知名称的class,获取其完整构造,并生成其对象实体、或对其fields设值、或唤起其methods。
反射(reflection)允许静态语言在运行时(runtime)检查、修改程序的结构与行为。 在静态语言中,使用一个变量时,必须知道它的类型。在Java中,变量的类型信息在编译时都保存到了class文件中,这样在运行时才能保证准确无误。
实现Java反射机制的类都位于java.lang.reflect包中:
Class类:代表一个类
Field类:代表类的成员变量(类的属性)
Method类:代表类的方法
Constructor类:代表类的构造方法
Array类:提供了动态创建数组,以及访问数组的元素的静态方法
优点 :可以实现动态创建对象和编译,体现出很大的灵活性
缺点 :对性能有影响,使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且他满足我们的要求,这类操作总是慢于直接执行相同的操作。
获取Class类的对象 举例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 package com.bobo.reflection;public class Test01 extends Object { public static void main (String[] args) throws ClassNotFoundException { Class<?> c1 = Class.forName("com.bobo.reflection.User" ); System.out.println(c1); Class<?> c2 = Class.forName("com.bobo.reflection.User" ); Class<?> c3 = Class.forName("com.bobo.reflection.User" ); Class<?> c4 = Class.forName("com.bobo.reflection.User" ); System.out.println(c2.hashCode()); System.out.println(c3.hashCode()); System.out.println(c4.hashCode()); } }class User { private String name; private int id; private int age; public User () { } public User (String name, int id, int age) { this .name = name; this .id = id; this .age = age; } public String getName () { return name; } public void setName (String name) { this .name = name; } public int getId () { return id; } public void setId (int id) { this .id = id; } public int getAge () { return age; } public void setAge (int age) { this .age = age; } @Override public String toString () { return "User{" + "name='" + name + '\'' + ", id=" + id + ", age=" + age + '}' ; } }
若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高
Class c2 = Class.forName("com.bobo.reflection.Student");
一直某个类的实例,调用该实例的getClass()方法获取Class对象
Class c1 = person.getClass();
一直一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException异常
Class<Student> c3 = Student.class;
内置基本数据类型可以直接用类名.Type
Class<Integer> c4 = Integer.TYPE;
还可以利用ClassLoader
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 package com.bobo.reflection;public class Test02 { public static void main (String[] args) throws ClassNotFoundException { Person person = new Student(); System.out.println("这个人是:" +person.name); Class c1 = person.getClass(); System.out.println(c1.hashCode()); Class c2 = Class.forName("com.bobo.reflection.Student" ); System.out.println(c2.hashCode()); Class<Student> c3 = Student.class; System.out.println(c3.hashCode()); Class<Integer> c4 = Integer.TYPE; System.out.println(c4); Class c5 = c1.getSuperclass(); System.out.println(c5); } }class Person { public String name; public Person () { } public Person (String name) { this .name = name; } @Override public String toString () { return "Person{" + "name='" + name + '\'' + '}' ; } }class Student extends Person { public Student () { this .name="学生" ; } }class Teacher extends Person { public Teacher () { this .name="学生" ; } }
所有类型的class对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 package com.bobo.reflection;import java.lang.annotation.ElementType;public class Test03 { public static void main (String[] args) { Class c1 = Object.class; Class c2 = Comparable.class; Class c3 = String[].class; Class c4 = int [][].class; Class c5 = Override.class; Class c6 = ElementType.class; Class c7 = Integer.class; Class c8 = void .class; Class c9 = Class.class; System.out.println(c1); System.out.println(c2); System.out.println(c3); System.out.println(c4); System.out.println(c5); System.out.println(c6); System.out.println(c7); System.out.println(c8); System.out.println(c9); int [] a = new int [10 ]; int [] b = new int [100 ]; System.out.println(a.getClass().hashCode()); System.out.println(b.getClass().hashCode()); } }
类加载内存分析
JVM把class文件加载到内存,并对数据进行校验、准备、解析、初始化,最终形成JVM可以直接使用的Java类型的过程。
加载
将class字节码文件加载到内存中,并将这些数据转换成方法区中的运行时数据(静态变量、静态代码块、常量池等),在堆中生成一个Class类对象代表这个类(反射原理),作为方法区类数据的访问入口。
链接
将Java类的二进制代码合并到JVM的运行状态之中(JRE)。
验证 确保加载的类信息符合JVM规范,没有安全方面的问题。
准备 正式为类变量(static变量)分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配。注意此时的设置初始值为默认值,具体赋值在初始化阶段完成
解析 虚拟机常量池内的符号引用替换为直接引用(地址引用)的过程。
初始化
初始化阶段是执行类构造器()方法的过程。类构造器()方法是由编译器自动收集类中的所有类变量的赋值 动作和静态语句块(static块) 中的语句合并产生的。
当初始化一个类的时候,如果发现其父类还没有进行过初始化、则需要先初始化其父类。
虚拟机会保证一个类的()方法在多线程环境中被正确加锁和同步。
类的初始化
父类
1 2 3 4 5 6 class Father { static int b = 2 ; static { System.out.println("父类被加载" ); } }
子类
1 2 3 4 5 6 7 8 class Son extends Father { static { System.out.println("子类被加载" ); m = 300 ; } static int m = 100 ; static final int M = 1 ; }
主动引用(一定会初始化)
new一个类的对象;
当虚拟启动时,先初始化main方法所在的类;
调用类的静态成员(除了final常量)和静态方法;
使用java.lang.reflect包的方法对类进行反射调用;
当初始化一个类,如果其父类没有被初始化,则先会初始化他的父类
实现类
new一个类的对象主动引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package com.bobo.reflection;public class Test { static { System.out.println("main类被加载" ); } public static void main (String[] args) { Son son = new Son(); } }
实现类
反射产生主动引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package com.bobo.reflection;public class Test { static { System.out.println("main类被加载" ); } public static void main (String[] args) throws ClassNotFoundException { Class.forName("com.bobo.reflection.Son" ); } }
被动引用
当访问一个静态域时,只有真正声明这个域的类才会被初始化。例如:通过子类引用父类的静态变量,不会导致子类初始化。
通过数组定义类引用,不会触发此类的初始化。
引用常量不会触发此类的初始化(常量在编译阶段就存入调用类的常量池中了)。
实现类
子类引用父类的静态变量,不会导致子类的初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.bobo.reflection;public class Test { static { System.out.println("main类被加载" ); } public static void main (String[] args) { System.out.println(Son.b); } }
实现类
只是为一片内存赋名,不会初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.bobo.reflection;public class Test { static { System.out.println("main类被加载" ); } public static void main (String[] args) { Son[] array = new Son[5 ]; } }
实现类
只是为一片内存赋名,不会初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.bobo.reflection;public class Test { static { System.out.println("main类被加载" ); } public static void main (String[] args) { System.out.println(Son.M); } }
类加载器的原理 类缓存
标准的Java SE类加载器可以按要求查找类,一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过,JVM垃圾收集器可以回收这些Class对象。
类加载的作用
将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
类加载器分类
引导类加载器(bootstrap class loader)
它用来加载 Java 的核心库(JAVA_HOME/jre/lib/rt.jar,sun.boot.class.path路径下的内容),是用原生代码(C语言)来实现的,并不继承自 java.lang.ClassLoader。
加载扩展类和应用程序类加载器。并指定他们的父类加载器。
扩展类加载器(extensions class loader)
用来加载 Java 的扩展库(JAVA_HOME/jre/ext/*.jar,或java.ext.dirs路径下的内容) 。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java类。
由sun.misc.Launcher$ExtClassLoader实现。
应用程序类加载器(application class loader)
它根据 Java 应用的类路径(classpath,java.class.path 路径下的内容)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。
由sun.misc.Launcher$AppClassLoader实现。
自定义类加载器
开发人员可以通过继承 java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package com.bobo.reflection;public class Test06 { public static void main (String[] args) throws ClassNotFoundException { ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); System.out.println(systemClassLoader); ClassLoader parent = systemClassLoader.getParent(); System.out.println(parent); ClassLoader grandparent = parent.getParent(); System.out.println(grandparent); ClassLoader classLoader = Class.forName("com.bobo.reflection.Test06" ).getClassLoader(); System.out.println(classLoader); classLoader = Class.forName("java.lang.Object" ).getClassLoader(); System.out.println(classLoader); System.out.println(System.getProperty("java.class.path" )); } }
使用JDK11编译结果如上
使用JDK8编译结果如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 sun.misc.Launcher$AppClassLoader@18b4aac2 sun.misc.Launcher$ExtClassLoader@1b6d3586null sun.misc.Launcher$AppClassLoader@18b4aac2null /Library/Java/JavaVirtualMachines/jdk1.8 .0_131 .jdk/Contents/Home/jre/lib/charsets.jar; /Library/Java/JavaVirtualMachines/jdk1.8 .0_131 .jdk/Contents/Home/jre/lib/deploy.jar; /Library/Java/JavaVirtualMachines/jdk1.8 .0_131 .jdk/Contents/Home/jre/lib/ext/cldrdata.jar; /Library/Java/JavaVirtualMachines/jdk1.8 .0_131 .jdk/Contents/Home/jre/lib/ext/dnsns.jar; /Library/Java/JavaVirtualMachines/jdk1.8 .0_131 .jdk/Contents/Home/jre/lib/ext/jaccess.jar; /Library/Java/JavaVirtualMachines/jdk1.8 .0_131 .jdk/Contents/Home/jre/lib/ext/jfxrt.jar; /Library/Java/JavaVirtualMachines/jdk1.8 .0_131 .jdk/Contents/Home/jre/lib/ext/localedata.jar; /Library/Java/JavaVirtualMachines/jdk1.8 .0_131 .jdk/Contents/Home/jre/lib/ext/nashorn.jar; /Library/Java/JavaVirtualMachines/jdk1.8 .0_131 .jdk/Contents/Home/jre/lib/ext/sunec.jar; /Library/Java/JavaVirtualMachines/jdk1.8 .0_131 .jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar; /Library/Java/JavaVirtualMachines/jdk1.8 .0_131 .jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar; /Library/Java/JavaVirtualMachines/jdk1.8 .0_131 .jdk/Contents/Home/jre/lib/ext/zipfs.ja; /Library/Java/JavaVirtualMachines/jdk1.8 .0_131 .jdk/Contents/Home/jre/lib/javaws.jar; /Library/Java/JavaVirtualMachines/jdk1.8 .0_131 .jdk/Contents/Home/jre/lib/jce.jar; /Library/Java/JavaVirtualMachines/jdk1.8 .0_131 .jdk/Contents/Home/jre/lib/jfr.jar; /Library/Java/JavaVirtualMachines/jdk1.8 .0_131 .jdk/Contents/Home/jre/lib/jfxswt.jar; /Library/Java/JavaVirtualMachines/jdk1.8 .0_131 .jdk/Contents/Home/jre/lib/jsse.jar; /Library/Java/JavaVirtualMachines/jdk1.8 .0_131 .jdk/Contents/Home/jre/lib/management-agent.jar; /Library/Java/JavaVirtualMachines/jdk1.8 .0_131 .jdk/Contents/Home/jre/lib/plugin.jar; /Library/Java/JavaVirtualMachines/jdk1.8 .0_131 .jdk/Contents/Home/jre/lib/resources.jar; /Library/Java/JavaVirtualMachines/jdk1.8 .0_131 .jdk/Contents/Home/jre/lib/rt.jar; /Library/Java/JavaVirtualMachines/jdk1.8 .0_131 .jdk/Contents/Home/lib/ant-javafx.jar; /Library/Java/JavaVirtualMachines/jdk1.8 .0_131 .jdk/Contents/Home/lib/dt.jar; /Library/Java/JavaVirtualMachines/jdk1.8 .0_131 .jdk/Contents/Home/lib/javafx-mx.jar; /Library/Java/JavaVirtualMachines/jdk1.8 .0_131 .jdk/Contents/Home/lib/jconsole.jar; /Library/Java/JavaVirtualMachines/jdk1.8 .0_131 .jdk/Contents/Home/lib/packager.jar; /Library/Java/JavaVirtualMachines/jdk1.8 .0_131 .jdk/Contents/Home/lib/sa-jdi.jar; /Library/Java/JavaVirtualMachines/jdk1.8 .0_131 .jdk/Contents/Home/lib/tools.jar; /Users/renbo/Desktop/研究生/学习笔记/编程/JAVA/狂神/relfection/target/classes
双亲委派机制
如果一个类加载器收到了类加载请求,它并不会自己先加载,而是把这个请求委托给父类的加载器去执行
如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的引导类加载器;
如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成加载任务,子加载器才会尝试自己去加载,这就是双亲委派机制
父类加载器一层一层往下分配任务,如果子类加载器能加载,则加载此类,如果将加载任务分配至系统类加载器也无法加载此类,则抛出异常
优势
避免类的重复加载
保护程序安全,防止核心API被随意篡改
自定义类:java.lang.String (没用)
自定义类:java.lang.ShkStart(报错:阻止创建 java.lang开头的类)
获得类的运行时结构 Java中反射获取类的成员时需要调用getFields()、getDeclaredFields();getMethods()、getDeclaredMethods();getConstructors()、getDeclaredConstructoers()等方法获取Field、Method、Constructor对象,这几对方法的主要区别如下:
getFields()与getDeclaredFields()
getFields()返回类中所有public的字段,包括从父类或接口继承的public字段; getDeclaredFields()返回本类中声明的所有字段,包括public、protected、private字段,不包括从父类或接口继承的字段
getMethods()与getDeclaredMethods() getMethods()返回类中所有public的方法,包括从父类或接口继承的public方法 getDeclaredMethods()返回本类中声明的所有方法,包括public、protected、private方法,不包括从父类或接口继承的方法
getConstructors()与getDeclaredConstructors() getConstructors()返回类中所有public的构造方法; getDeclaredConstructors()返回类中声明的所有构造方法,包括public、protected、private构造方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 package com.bobo.reflection;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;public class Test07 { public static void main (String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException { Class c1 = Class.forName("com.bobo.reflection.User" ); User user = new User(); c1 = user.getClass(); System.out.println(c1.getName()); System.out.println(c1.getSimpleName()); System.out.println("====================================" ); Field[] fields = c1.getFields(); fields = c1.getDeclaredFields(); for (Field field : fields) { System.out.println(field); } Field name = c1.getDeclaredField("name" ); System.out.println(name); System.out.println("====================================" ); Method[] methods = c1.getMethods(); Method[] declaredMethods = c1.getDeclaredMethods(); for (Method method : methods) { System.out.println("正常的:" +method); } for (Method declaredMethod : declaredMethods) { System.out.println("declaredMethod:" +declaredMethod); } System.out.println("====================================" ); Method getName = c1.getMethod("getName" , null ); Method setName = c1.getMethod("setName" , String.class); System.out.println(getName); System.out.println(setName); System.out.println("====================================" ); Constructor[] constructors = c1.getConstructors(); Constructor[] declaredConstructors = c1.getDeclaredConstructors(); for (Constructor constructor : constructors) { System.out.println(constructor); } for (Constructor declaredConstructor : declaredConstructors) { System.out.println("#" +declaredConstructor); } System.out.println("====================================" ); Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int .class, int .class); System.out.println("指定+" +declaredConstructor); } }
invoke()方法:用于调用指定的方法
1 Object invoke (Object obj,Object... args)
第一个Object对应原方法的返回值,若原方法无返回值则返回null
括号里的第一个Object代表调用这个方法的对象
若原方法形参列表为空,Object[] args为null
如果原方法声明为private,需要在调用invoke()之前调用setAccessible()方法
setAccessible()方法:用于启动和禁止访问安全检查的开关
1 void setAccessible (boolean flag)
参数为false表示反射的对象应该实施访问检查,参数为true则关闭
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package com.bobo.reflection;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;public class Test08 { public static void main (String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException { Class c1 = Class.forName("com.bobo.reflection.User" ); Constructor constructor = (Constructor) c1.getDeclaredConstructor(String.class, int .class, int .class); User user2 = (User) constructor.newInstance("bobo" , 001 , 18 ); System.out.println(user2); User user3 = (User) c1.newInstance(); Method setName = c1.getDeclaredMethod("setName" , String.class); setName.invoke(user3,"bobo" ); System.out.println(user3.getName()); System.out.println("================================" ); User user4 = (User) c1.newInstance(); Field name = c1.getDeclaredField("name" ); name.setAccessible(true ); name.set(user4,"bobo2" ); System.out.println(user4.getName()); } }
性能对比分析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 package com.bobo.reflection;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;public class Test09 { public static void test01 () { User user = new User(); long startTime = System.currentTimeMillis(); for (int i = 0 ; i < 1000000000 ; i++) { user.getName(); } long endTime = System.currentTimeMillis(); System.out.println("普通方式执行10亿次:" +(endTime-startTime)+"ms" ); } public static void test02 () throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { User user = new User(); Class c1 = user.getClass(); Method getName = c1.getDeclaredMethod("getName" , null ); long startTime = System.currentTimeMillis(); for (int i = 0 ; i < 1000000000 ; i++) { getName.invoke(user, null ); } long endTime = System.currentTimeMillis(); System.out.println("反射方式执行10亿次:" +(endTime-startTime)+"ms" ); } public static void test03 () throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { User user = new User(); Class c1 = user.getClass(); Method getName = c1.getDeclaredMethod("getName" , null ); getName.setAccessible(true ); long startTime = System.currentTimeMillis(); for (int i = 0 ; i < 1000000000 ; i++) { getName.invoke(user, null ); } long endTime = System.currentTimeMillis(); System.out.println("关闭检测,反射方式执行10亿次:" +(endTime-startTime)+"ms" ); } public static void main (String[] args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException { test01(); test02(); test03(); } }
1 2 3 普通方式执行10 亿次:5ms 反射方式执行10 亿次:2470ms 关闭检测,反射方式执行10 亿次:1169ms
反射操作泛型
Java采用泛型擦除机制来引入泛型。Java中的泛型仅仅是给编译器Javac使用的,确保数据的安全性和免去强制类型转换的麻烦。但是编译一旦完成,所有和泛型有关的类型全部被擦除。
为了通过反射操作这些类型以迎合实际开发的需要,Java新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。
ParameterizedType
:表示一种参数化的类型,比如Collection<String>
GenericArrayType
:表示一种元素类型是参数化类型或者类型变量的数组类型
TypeVariable
:是各种类型变量的公共父接口
WildcardType
:代表一种通配符类型表达式,比如?
、? extends Number
、? super Integer
。(wildcard是一个单词:就是”通配符“)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package com.bobo.reflection;import java.lang.reflect.Method;import java.lang.reflect.Parameter;import java.lang.reflect.ParameterizedType;import java.lang.reflect.Type;import java.util.List;import java.util.Map;public class Test { public void test01 (Map<String,User> map, List<User> list) { System.out.println("test01" ); } public Map<String ,User> test02 () { System.out.println("test02" ); return null ; } public static void main (String[] args) throws NoSuchMethodException { Method method = Test.class.getMethod("test01" , Map.class, List.class); Type[] genericParameterTypes = method.getGenericParameterTypes(); for (Type genericParameterType : genericParameterTypes) { System.out.println("genericParameterType:" +genericParameterType); if (genericParameterType instanceof ParameterizedType){ Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println(actualTypeArgument); } } } method = Test.class.getMethod("test02" , null ); Type genericReturnType = method.getGenericReturnType(); if (genericReturnType instanceof ParameterizedType){ Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println(actualTypeArgument); } } } }
1 2 3 4 5 6 7 8 9 返回结果: genericParameterType:java.util.Map<java.lang.String,com.bobo.reflection.User>class java .lang .String class com .bobo .reflection .User genericParameterType :java .util .List <com .bobo .reflection .User >class com .bobo .reflection .User class java .lang .String class com .bobo .reflection .User
反射操作注解 获取注解的方法:
Class.getAnnotations() 获取所有的注解,包括自己声明的以及继承的
Class.getAnnotation(Class< A > annotationClass) 获取指定的注解,该注解可以是自己声明的,也可以是继承的
Class.getDeclaredAnnotations() 获取自己声明的注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 package com.bobo.reflection;import java.lang.annotation.*;import java.lang.reflect.Field;public class Test11 { public static void main (String[] args) throws ClassNotFoundException, NoSuchFieldException { Class c1 = Class.forName("com.bobo.reflection.Students" ); Annotation[] annotations = c1.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } Tablebobo tablebobo = (Tablebobo) c1.getAnnotation(Tablebobo.class); String value = tablebobo.value(); System.out.println(value); Field name = c1.getDeclaredField("name" ); Filebobo annotation = name.getAnnotation(Filebobo.class); System.out.println(annotation.columnName()); System.out.println(annotation.type()); System.out.println(annotation.length()); } }@Tablebobo("db_students") class Students { @Filebobo(columnName = "db_id",type = "int",length = 10) private int id; @Filebobo(columnName = "db_age",type = "int",length = 10) private int age; @Filebobo(columnName = "db_name",type = "varcher",length = 3) private String name; public Students () { } public Students (int id, int age, String name) { this .id = id; this .age = age; this .name = name; } public int getId () { return id; } public void setId (int id) { this .id = id; } public int getAge () { return age; } public void setAge (int age) { this .age = age; } public String getName () { return name; } public void setName (String name) { this .name = name; } @Override public String toString () { return "Students{" + "id=" + id + ", age=" + age + ", name='" + name + '\'' + '}' ; } }@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface Tablebobo{ String value () ; }@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @interface Filebobo{ String columnName () ; String type () ; int length () ; }
1 2 3 4 5 6 运行结果:@com .bobo.reflection.Tablebobo("db_students" ) db_students db_name varcher3