Annotation是从JDK5.0开始引入的新技术
Annotation的作用:
不是程序本身,可以对程序作出解释(这一点和注释(comment)没什么区别)可以被其他程序(比如:编译器等)读取Annotation的格式:
注解是以@注释名在代码中存在的,还可以添加一些参数值,例如@SuppressWarnings(value:“unchecked”)
Annotation在哪里使用?
可以附加在package,class,method,field等上面,相当于给他们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些元数据的访问。元注解的作用就是辅助注解其他注解,Java定义了4个标准的meta-annotation类型,他们被用来提供对其他annotation类型作说明。
这些类型和他们所支持的类在java.lang.annotation包中可以找到(@Target,@Retention,@Documented,@Inherited)
@Target : 用于描述注解的适用范围(即:被描述的注解可以用在什么地方)
@Retention:表示需要在什么级别保存该注释信息,用于描述注解的生命周期
(SOURCE<CLASS<RUNTIME)
@Document:说明该注解将被包含在javadoc中
@Inherited:说明子类可以继承父类中的该注解
package com.sc.annotation; import java.lang.annotation.*; //测试元注解 @MyAnnotation public class Test02 { } //定义一个注解 //Target 表示我们的注解可以用在哪些地方 @Target(value = {ElementType.METHOD,ElementType.TYPE}) //Retention 表示我们的注解在什么地方还有效。 // runtime>class>sources @Retention(value = RetentionPolicy.RUNTIME) //@Documented 表示是否将我们的注解生成在JAVAdoc中 @Documented //Inherited 子类可以继承父类的注解 @Inherited @interface MyAnnotation{}使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口
@interface用来声明一个注解,格式:public @ interface注解名{定义内容}其中的每一个方法实际上是声明了一个配置参数方法的名称就是参数的名称返回值类型就是参数的类型(返回值只能是基本类型(Class,String,enum))可以通过default来声明参数的默认值如果只有一个参数成员,一般参数名为value注解元素必须必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值 package com.sc.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; //自定义注解 public class Test03 { //注解可以显示赋值,如果没有默认值,我们必须给注解赋值 @MyAnnotation2(name = "suc",age =18, schools = {"中国科学技术大学"}) public void test(){} @MyAnnotation3("") //如果注解只有一个值,可以用value命名,然后传递值时可以省略value public void test2(){ } } //注解一 @Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation2{ //注解的参数:参数类型+参数名(); String name() default ""; int age() ; int id() default -1;//如果默认值为-1,代表不存在。 String[] schools() default {"MIT","USTC"}; } //注解二 @Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation3{ String value(); }静态语言(强类型语言)
静态语言是在编译时变量的数据类型即可确定的语言,多数静态类型语言要求在使用变量之前必须声明数据类型。 例如:C++、Java、Delphi、C#等。
动态语言(弱类型语言)
动态语言是在运行时确定数据类型的语言。变量使用之前不需要类型声明,通常变量的类型是被赋值的那个值的类型。 例如PHP、ASP、Ruby、Python、Perl、ABAP、SQL、JavaScript、Unix Shell等等。
Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
Class c = Class.forName(“java.lang.String”)
加载完类以后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射
正常方式:引入需要的“包类”名称—>通过new实例化—>取得实例化对象
反射方式:实例化对象–>getClass()方法—>得到完整的"包类"名称
在运行时判断任意一个对象所属的类;
在运行时构造任意一个类的对象;
在运行时判断任意一个类所具有的成员变量和方法;
在运行时获取泛型信息
在运行时调用任意一个对象的成员变量和方法
在运行时处理注解
生成动态代理
优点:可以实现动态创建对象和编译,体现出很大的灵活性
缺点:对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类总是慢于直接执行相同操作。
在Object类中定义了以下方法,此方法将被所有子方法继承
public final Class getClass()
以上方法返回值是一个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称
对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定某个结构(class/interface/enum/annotation/primitive/type/void/[])的有关信息。
Class本身也是一个类Class对象只能由系统建立对象一个加载的类在JVM中只有一个Class实例一个Class对象对应的是一个加载到JVM的一个.class文件每个类的实例都会记得自己是由哪个Class实例所生成通过Class可以完整地得到一个类的所有被加载的结构Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象【代码演示】
package com.sc.reflection; //测试class类的创建方式有哪些 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()); //方式二:forName获得 Class c2 = Class.forName("com.sc.reflection.Student"); System.out.println(c2.hashCode()); //方式三:通过类名.class获得 Class c3 = Student.class; System.out.println(c3.hashCode()); //方式四:基本内置类型的包装类都有一个Type属性 Class 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 = "教师"; } } /* 输出: 这个人是:学生 1956725890 1956725890 1956725890 int class com.sc.reflection.Person */【代码演示】
package com.sc.reflection; import java.lang.annotation.ElementType; //所有类型的Class 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); //只要元素类型与维度一样,就是同一个Class int[] a =new int [10]; int[] b =new int [100]; System.out.println(a.getClass().hashCode()); System.out.println(b.getClass().hashCode()); } } /* 输出: class java.lang.Object interface java.lang.Comparable class [Ljava.lang.String; class [[I interface java.lang.Override class java.lang.annotation.ElementType class java.lang.Integer void class java.lang.Class 1956725890 1956725890 */堆:
1.存放new的对象和数组
2.可以被所有线程共享。不会存放别的对象引用
栈:
1.存放基本变量类型(会包含这个基本类型的具体数值)
2.引用对象的变量(会存放这个引用在堆里面的具体地址)
方法区:
1.可以被所有线程共享
2.包含了所有class和static变量
【代码演示】
package com.sc.reflection; public class Test04 { public static void main(String[] args) { A a = new A(); System.out.println(A.m); /* 1.加载到内存,会产生一个类对应class对象 2.链接,链接结束后 m=0 3.初始化 <clint>(){ System.out.println("A静态代码块初始化"); m=30; m=100; } */ } } class A{ static { System.out.println("A静态代码块初始化"); m=30; } static int m =100; public A(){ System.out.println("A类的无参构造初始化"); } } /* 输出: A静态代码块初始化 A类的无参构造初始化 100 */类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。
JVM规范定义了如下类型的类加载器
引导类加载器(根加载器):用C++编写,是JVM自带的类加载器,负责java平台核心库,用来装载核心库。该加载器无法直接获取
扩展类加载器:负责jre/lib/ext目录下的jar包或-D java.ext.dirs 指定目录下的jar包装入工作库
系统类加载器:负责java -classpath或-D java.class.path所指的目录下的类与jar包装入工作,是最常用的加载器。
【代码演示】
package com.sc.reflection; public class Test07 { public static void main(String[] args) throws ClassNotFoundException { //获取系统类的加载器 ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); System.out.println(systemClassLoader); //获取系统类加载器的父亲加载器-->扩展类加载器 ClassLoader parent = systemClassLoader.getParent(); System.out.println(parent); //获取扩展类加载器的父亲加载器-->根加载器(C/C++) ClassLoader parent1 = parent.getParent(); System.out.println(parent1); //测试当前类是哪个加载器加载的 ClassLoader classLoader = Class.forName("com.sc.reflection.Test07").getClassLoader(); System.out.println(classLoader); //测试JDK内置的类是谁加载的 classLoader = Class.forName("java.lang.Object").getClassLoader(); System.out.println(classLoader); //如何获得系统类加载可以加载的路径 System.out.println(System.getProperty("java.class.path")); } } /* 输出: sun.misc.Launcher$AppClassLoader@18b4aac2 sun.misc.Launcher$ExtClassLoader@74a14482 null sun.misc.Launcher$AppClassLoader@18b4aac2 null E:\Environment\Java\jdk1.8\jre\lib\charsets.jar; E:\Environment\Java\jdk1.8\jre\lib\deploy.jar; E:\Environment\Java\jdk1.8\jre\lib\ext\access-bridge-64.jar; E:\Environment\Java\jdk1.8\jre\lib\ext\cldrdata.jar; E:\Environment\Java\jdk1.8\jre\lib\ext\dnsns.jar; E:\Environment\Java\jdk1.8\jre\lib\ext\jaccess.jar; E:\Environment\Java\jdk1.8\jre\lib\ext\jfxrt.jar; E:\Environment\Java\jdk1.8\jre\lib\ext\localedata.jar; E:\Environment\Java\jdk1.8\jre\lib\ext\nashorn.jar; E:\Environment\Java\jdk1.8\jre\lib\ext\sunec.jar; E:\Environment\Java\jdk1.8\jre\lib\ext\sunjce_provider.jar; E:\Environment\Java\jdk1.8\jre\lib\ext\sunmscapi.jar; E:\Environment\Java\jdk1.8\jre\lib\ext\sunpkcs11.jar; E:\Environment\Java\jdk1.8\jre\lib\ext\zipfs.jar; E:\Environment\Java\jdk1.8\jre\lib\javaws.jar; E:\Environment\Java\jdk1.8\jre\lib\jce.jar; E:\Environment\Java\jdk1.8\jre\lib\jfr.jar; E:\Environment\Java\jdk1.8\jre\lib\jfxswt.jar; E:\Environment\Java\jdk1.8\jre\lib\jsse.jar; E:\Environment\Java\jdk1.8\jre\lib\management-agent.jar; E:\Environment\Java\jdk1.8\jre\lib\plugin.jar; E:\Environment\Java\jdk1.8\jre\lib\resources.jar; E:\Environment\Java\jdk1.8\jre\lib\rt.jar; F:\Project\JAVA\Javalesson\out\production\Thread; F:\Project\JAVA\Javalesson\Thread\src\com\lib\commons-io-2.6.jar; E:\Program Files\IntelliJ IDEA 2019.3.5\lib\idea_rt.jar */【代码演示】
package com.sc.reflection; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; //获得类的信息 public class Test08 { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException { Class c1 = Class.forName("com.sc.reflection.User"); //获得类的名字 System.out.println(c1.getName());//包名+类名 System.out.println(c1.getSimpleName());//获得类名 //获得类的属性 System.out.println("================="); Field[] fields = c1.getFields();//只能找到public属性 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();//获得本类及其父类的全部public方法 for(Method method:methods){ System.out.println("正常的:"+method); } methods = c1.getDeclaredMethods();//获得本类的所有方法 for(Method method:methods){ System.out.println("getDeclaredMethods:"+method); } //获得指定方法 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(); for (Constructor constructor : constructors) { System.out.println("public:"+constructor); } constructors = c1.getDeclaredConstructors(); for (Constructor constructor : constructors) { System.out.println("所有:"+constructor); } //获得指定构造器 Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class); System.out.println("指定:"+ declaredConstructor); } /* 输出: com.sc.reflection.User User ================= private java.lang.String com.sc.reflection.User.name ==================== 正常的:public java.lang.String com.sc.reflection.User.getName() 正常的:public int com.sc.reflection.User.getId() 正常的:public void com.sc.reflection.User.setName(java.lang.String) 正常的:public void com.sc.reflection.User.setId(int) 正常的:public int com.sc.reflection.User.getAge() 正常的:public void com.sc.reflection.User.setAge(int) 正常的:public final void java.lang.Object.wait() throws java.lang.InterruptedException 正常的:public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException 正常的:public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException 正常的:public boolean java.lang.Object.equals(java.lang.Object) 正常的:public java.lang.String java.lang.Object.toString() 正常的:public native int java.lang.Object.hashCode() 正常的:public final native java.lang.Class java.lang.Object.getClass() 正常的:public final native void java.lang.Object.notify() 正常的:public final native void java.lang.Object.notifyAll() getDeclaredMethods:public java.lang.String com.sc.reflection.User.getName() getDeclaredMethods:public int com.sc.reflection.User.getId() getDeclaredMethods:public void com.sc.reflection.User.setName(java.lang.String) getDeclaredMethods:public void com.sc.reflection.User.setId(int) getDeclaredMethods:public int com.sc.reflection.User.getAge() getDeclaredMethods:public void com.sc.reflection.User.setAge(int) public java.lang.String com.sc.reflection.User.getName() public void com.sc.reflection.User.setName(java.lang.String) ============== public:public com.sc.reflection.User() public:public com.sc.reflection.User(java.lang.String,int,int) 所有:public com.sc.reflection.User() 所有:public com.sc.reflection.User(java.lang.String,int,int) 指定:public com.sc.reflection.User(java.lang.String,int,int) */ }有了Class对象,能做什么
创建类的对象:调用Class对象的newInstance()方法
类必须有一个无参构造器类的构造器的访问权限需要足够没有无参构造器,在操作时明确调用类中的构造器,并将参数传递进去之后,才可以实例化操作
通过Class类的getDeclaredConstructor(Class … parameterTypes)取得本类的指定形参类型的构造器向构造器的形参中传递一个对象数组进去,里面包含了构造器所需的各个参数通过Constructor实例化对象通过反射,调用类中的方法,通过Method类完成
通过Class类的getMethod(String name,Class…parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的的类型参数之后使用Object invoke(Object obj,Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息Object invoke(Object obj,Object … args)
Object对应原方法的返回值,若原方法无返回值,此时返回null若原方法为静态方法,此时形参Object obj可为null若原方法形参列表为空,则Object[] args为null若原方法声明为private,则需要在调用此invoke()方法之前,显式调用方法对象的setAccessible(true)方法,则可以访问private的方法。setAccessible
Method和Field、Constructor对象都有setAccessible()方法
setAccessible作用是启动和禁用访问安全检查的开关,参数值为true则指示反射的对象在使用时应该取消Java语言的访问检查,参数为false则指示反射的对象应该实施Java语言访问检查
【代码演示】
package com.sc.reflection; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; //动态创建对象,通过反射 public class Test09 { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { //获得Class对象 Class c1 = Class.forName("com.sc.reflection.User"); //构造一个对象 // User user = (User) c1.newInstance();//本质是调用了无参构造 //System.out.println(user); //通过构造器创建对象 Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class); User user2 = (User)constructor.newInstance("suc", 001, 24); System.out.println(user2); //通过反射调用普通方法 User user3 = (User) c1.newInstance(); //通过反射获取一个方法 Method setName = c1.getDeclaredMethod("setName", String.class); //invoke:激活的意思 //(对象,"方法的值") setName.invoke(user3,"suc"); System.out.println(user3.getName()); //通过反射操作属性 System.out.println("===================="); User user4 =(User) c1.newInstance(); Field name = c1.getDeclaredField("name"); //不能直接操作私有属性,我们需要关闭程序的安全监测,属性或者方法的setAccessible(true) name.setAccessible(true); name.set(user4,"suc2"); System.out.println(user4.getName()); } } /* 输出: com.sc.reflection.User@74a14482 suc ==================== suc2 */【代码演示-性能检测】
package com.sc.reflection; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; //分析性能问题 public class Test10 { //普通方法调用 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("普通方法执行十亿次:"+(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("反射方法执行十亿次:"+(endTime-startTime)+"ms"); } //反射方式调用,关闭检测 public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { User user = new User(); Class c2 = user.getClass(); Method getName = c2.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("反射方法(关闭检测)执行十亿次:"+(endTime-startTime)+"ms"); } public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { test01(); test02(); test03(); } } /* 输出: 普通方法执行十亿次:2ms 反射方法执行十亿次:1845ms 反射方法(关闭检测)执行十亿次:1258ms */Java 中的泛型仅仅是给编译器 javac 使用的,确保数据的安全性和免去强制类型转换的麻烦,但是一旦编译完成,所有与泛型有关的类型全部擦除。使用泛型直接读取泛型,是读取不到的,因为反射是操作加载以后的类的。为了通过反射操作这些类型以迎合实际开发的需要,Java新增了几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型
类型描述ParameterizedType表示一种参数化的类型 , 比如 Collection,可以获取 String信息GenericArrayType泛型数组类型TypeVariable各种类型变量的公共父接口WildcardType代表一种通配符类型表达式,比如? extends Number,? super Integer (Wildcard 是一个单词,就是通配符) package com.sc.reflection; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; import java.util.Map; public class Test11 { 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 = Test11.class.getMethod("test01", Map.class, List.class); Type[] genericParameterTypes = method.getGenericParameterTypes(); for (Type genericParameterType : genericParameterTypes) { System.out.println("#"+genericParameterType); if (genericParameterType instanceof ParameterizedType){ Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { System.out.println(actualTypeArgument); } } } System.out.println("======================"); method = Test11.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); } } } } /* 输出: #java.util.Map<java.lang.String, com.sc.reflection.User> class java.lang.String class com.sc.reflection.User #java.util.List<com.sc.reflection.User> class com.sc.reflection.User ====================== class java.lang.String class com.sc.reflection.User */