注解和反射
注解 java.Annotation
java.lang 包下
@override
重写注解
@Deprecated
已过时
@Suppresswarnings
镇压警告
@
@加参数名 还可以加一些参数值
对程序做出解释
可以被其它程序读取
package com.yang.annotation; //什么是注解 //镇压警告 @SuppressWarnings("all") public class Test01 extends Object{ //重写的注解 @Override public String toString() { return "Test01{}"; } //已过时,不推荐使用,但是可以使用 @Deprecated public static void text(){ System.out.println("已过时"); } //镇压警告 @SuppressWarnings("all") public static void main(String[] args) { text(); } }
元注解 meta-annotation 负责注解其他注解
public @interface 自定义注解名{
类型 属性名 () default 值;
//注解类型
public Anno2 anno() default @Anno2;
//int 数组
public int[] arr() defualt {1,2,3,4}
}
属性类型
基本数据类型 String Class 注解 枚举
以上类型的所有数组
import com.yang.Season; @Deprecated @SuppressWarnings("all") @interface Anno{ public int a() default 1; public String str() default "武则天"; public Season season() default Season.SPRING; public Class classzz() default Anno.class; public Anno2 anno() default @Anno2; public int[] arr() default {1,2,3}; }
注解属性没有默认值,我们要给定属性值
元注解 描述注解的注解
@Target (限制注解使用在哪个地方)进入类中去看很容理解 FIELD,TYPE ,METHOD
@Retention 注解的保留时间
@Documented 将注解生成到JAVAdao中 (该注解会出现在帮助文档中)
@Inherited 表示自定义的注解可以被子类继承
package com.yang.annotation; import java.lang.annotation.*; //测试元注解 @MyAnnotation public class Test02 { @MyAnnotation public void test(){ } } //表示我们额注解可以用在那些地方 词义目标 @Target(value = {ElementType.METHOD,ElementType.TYPE}) //Retention表示注解到什么时候有效 source源码级别 CLASS JAVAC编译后 RUNTIME 运行时 //runtine>class>source @Retention(value = RetentionPolicy.RUNTIME ) //表示是否将我们的注解生成到 JAVAdao中 @Documented //表示子类可以继承父类的注解 @Inherited @interface MyAnnotation{ }
反射 Java Reflection
Java可以通过Reflection(反射)是Java可以被看作是动态语言的关键,反射机制允许程序在执行期间借助Reflection API取得任何类的内部信息(类名 类的接口 类的字段 类的属性等等),,并能操作任意对象的内部属性和方法;
Class c=Class.forName("java.lang.String")
加载完类之后,就在堆内存的方法区中产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了完整类的结构信息。因此我们可以通过对象看到类的结构。这个对象相当于一面镜子,透过这个镜子看到类的结构。
正常方式 import 包类 名称👉 new实例化对象👉取得实例化对象
反射方式 实例化对象 👉 getClass()方法 👉得到完整的“包类”名称
静态语言VS动态语言
动态语言
在运行时可以改其结构的语言:通俗将运行时可以根据某些条件改变自身的结构;例如:新的函数,对象,甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。
主要的动态语言:Object-C, C#,JavaScript,PHP,Python等
静态语言
运行时结构不可变的语言就是静态语言。如Java,C,C++。Java不是动态语言,但Java可以成为“准动态语言”。我们可以利用反射机制获得类似动态语言的特征。
优点
可以实现动态创建对象和编译,体现出很大的灵活性
缺点
对行能有影响。使用反射机制基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于直接执行相同的操作。
常用API
java.lang.class 代表类
java.lang.reflect.Method 代表类的方法
java.lang.reflect.Field 代表类的成员变量‘
java.lang.reflect.Constructor 代表类的构造器
.........
Class类
对象反射后可以得到的信息:某个类的属性,方法和构造器,某个类到底实现了哪些接口。对于每个类而言,JRE都会为其保留一个不变的Class类型的对象。
一个Class对象包含了特定某个结构(class/interface/enum/annotation/primitive type/void[])的有关信息
Class 本身也是一个类
Class对象只能由系统创建对象
一个加载类在JVM中指挥有一个Class实例
一个Class对象对应的是一个加载JVM中的一个Class文件
通过Class可以完整的得到一个中所有被加载的结构
Class是Relection的根源,针对任何你想动态加载,运行的类,唯有先获得相应的Class对象
获取Class类的实例
1 以知具体的类,通过类的cass属性获取,该方法最为安全可靠,程序性能最高
Class claxx=Student.class;
2 以知某个类的实例,调用该实例的getClass()方法获取Class对象;
Class claxx=student.getClass()
3 以知一个类的全类名,且该类在类路径下,通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException
Class claxx=Class.forName("com.yang.Student")
4 内置基本数据类型可以直接用类名.Type
5 还可以利用ClassLoader
package com.yang.reflection; //测试class类的创建方式有哪些 public class Test03 { 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.yang.reflection.Student"); System.out.println(c2.hashCode()); //通过类名.calss获得 Class<Student> 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(String name) { this.name = name; } public Person() { } @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类型
package com.yang.reflection; import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer; import java.lang.annotation.ElementType; public class Test04 { 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; //void Class c9=Class.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(一个类只有一个class对象) int[] a=new int[10]; int[] b=new int[100]; System.out.println(a.getClass().hashCode()); System.out.println(b.getClass().hashCode()); } }
类的加载过程
程序要使用某个类,该类没有被加载大内存中。需要进行三个步骤
1 有类的加载器。将类加载到内存中,并为之创建一个java.lang.Class对象。
2将类的二进制数据合并到jre中
3类的初始化 JVM负责类的初始化
jdk 包含jre和java开发工具包,可以编译运行Java程序
jre java的运行环境,针对使用Java的客户,能够运行java字节码(.class)。但不能编译java源码
jvmJava虚拟机,能够解释字节码文件.class,但是不能正确执行
jvm java虚拟机时java跨平台的关键,java程序首先会被编译为字节码.class,JVM的核心任务就是解释.class,将其映射到真正的Cup指令集或被系统调用
jre Java的运行环境,只有Jvm是不能运行Java的字节码的(.class),因为解释字节码需要lib库
jdk java的核心,包含Java的运行环境jre和一些Java的工具和一些基础类库。
总体来说就是,我们利用JDK(调用JAVA API)开发了属于我们自己的JAVA程序后,通过JDK中的编译程序(javac)将我们的文本java文件编译成JAVA字节码,在JRE上运行这些JAVA字节码,JVM解析这些字节码,映射到CPU指令集或OS的系统调用。
类的加载
加载
将Class字节码加载到内存中,并将静态的数据转换为方法区运行时动态的数据结构,然后在堆内存中生成java.lang.Class对象,作为方法区中类数据的访问接口
链接
jre中的lib解释字节码文件,合并到JVM(bin)的运行状态之中。
步骤1 验证:验证加载类信息(解释的字节码文件)是否符合JVM的规范。没有安全问题。
步骤2 准备: 正式为变量(Static) 分配内存(内存在方法去中进行分配(特殊的堆内存)), 将静态区变量设为默认值。
步骤3 解析 虚拟机常量池中的符号引用(常量名),解析为直接引用(地址)
初始化
类构造器<clinit>()方法 类构造器是构造类信息的,不是构造该类对象的
<clinit>()类构造器,是由编译时期自动收集类中所有类变量的赋值动作,和静态代码块语句合并产生的
初始化一个类,若父类没有初始化,先初始化父类
JVM会保证一个类的
package com.yang.reflection; import jdk.internal.dynalink.beans.StaticClass; public class Test05 { public static void main(String[] args) { A a=new A(); System.out.println(A.m); /* 1 加载到内存产生一个class对象 2 链接 链接结束后 m=0 3c初始化 <clinit>(){ //合并代码块 System.out.println("A静态代码初始化"); m=300; System.out.println("A的无参构造初始化"); } */ } } class A{ static { System.out.println("A静态代码初始化"); m=300; } static int m=100; public A(){ System.out.println("A的无参构造初始化"); } }
m最后等于100
<clinit>()类构造器在多线程中的被正确的加锁或同步。
类的主动引用
1当JVM启动时先初始化Main方法所在的类
2 new 一个类新的对象时
3 调用类的静态成员(除了final常量)和静态方法
4 使用java.lang.reflect包的方法对类进行反射调用
5 当初话一个类时,先初始化他的父类
类的被动引用(不会发生类的初始化)
子类调用父类的静态成员变量或者方法不会产生子类的初始化
通过数组定义类的引用,不会触发此类的初始化
引用常量不会触发此类的初始化(常量在链接阶段就调入常量池中了)
package com.yang.reflection; //测试类什么时候会初始化和 import jdk.internal.dynalink.beans.StaticClass; public class Test06 { static { System.out.println("Main类被加载"); } public static void main(String[] args) throws ClassNotFoundException { //主动引用 //Son son=new Son(); //反射也会产生主动引用 //Class.forName("com.yang.reflection.Son"); //不会产生类的阴用的方法 //子类调用父类的静态变量或方法不会产生子类的初始化 System.out.println(Son.b); //通过数组定义类的引用并不会触发类的初始化 Son[] arr=new Son[10]; //调用类中的常量不会触发父类和子类的初始化 } } class Father{ static int b=100; static{ System.out.println("父类被加载"); } } class Son extends Father{ static { System.out.println("子类被加载"); m=300; } static int m=100; static final int M=1; }
类加载器的作用
加载
将Class字节码加载到内存中,并将静态的数据转换为方法区运行时动态的数据结构,然后在堆内存中生成java.lang.Class对象,作为方法区中类数据的访问接口
类缓存
标准的JavaSe类加载器可以按照要求查找类,但一旦某个类被加载到类加载器中,它将维持加载一段时间,不过JVM垃圾回收机制可以回收这些Class类对象。
类加载器
引导类加载器 C++编写的,时JVM自带的类加载器,用来装载核心类库,该类加载器无法直接获取 rt.jar
扩展类加载器 负责jre/lib/ext目录下的jar包或者D java。ext。dirs指定目录的下jar包装入工作库
系统类加载器 负责java-classpath或者 -D java.class.path 所指的目录下的类与jar包的装入工作,时常用的类加载器
获取运行时类的完整结构
Field Method Constructor Superclass Interface Annotation
package com.yang.reflection; import java.lang.Class; 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.yang.reflection.User"); //获得类的名字 System.out.println(c1.getName());//获得包名+类名 System.out.println(c1.getSimpleName());//获得类名 //获得类的属性 System.out.println("==================================="); Field[] fields=c1.getFields();//只能找到public属性 for (Field field : fields) { System.out.println(field); } fields=c1.getDeclaredFields();//找到全部的属性 for (Field field : fields) { System.out.println(field); } System.out.println("===================================="); //获得指定属性的值 Field field=c1.getDeclaredField("name"); System.out.println(field); System.out.println("===================================="); //获得类的方法 Method[] methods=c1.getMethods(); //获取本类及其父类的public所有方法 for (Method method : methods) { System.out.println("正常的"+method); } Method[] methods1=c1.getDeclaredMethods(); //获取本类的所有方法 for (int i = 0; i < methods1.length; i++) { System.out.println("getDeclaredMethods"+methods1[i]); } System.out.println("===================================="); //获得指定的方法 //重载 所以需要参数 Method method= c1.getMethod("getName",null); Method method1=c1.getMethod("setName", String.class) ; System.out.println("无参获取指定的方法"+method); System.out.println("有参获取指定的方法"+method1); System.out.println("===================================="); //获得指定的构造器 Constructor[] constructor=c1.getConstructors(); for (Constructor constructor1 : constructor) { System.out.println(constructor1); } Constructor[] constructors=c1.getDeclaredConstructors(); for (Constructor constructor1 : constructors) { System.out.println(constructor1); } //获得指定的构造器 Constructor constructor1=c1.getDeclaredConstructor(String.class,int.class,int.class); System.out.println("指定构造器"+constructor1); } }
动态创建对象的执行方法
有了class对象能做什么
public static <user3> void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { //获得class对象 Class c=Class.forName("com.yang.reflection.User"); //构造一个对象 User user=(User) c.newInstance();//本质上调用了无参构造器,没有无参构造报错。 System.out.println(user); //通过构造器创建对象 Constructor constructors=c.getDeclaredConstructor(String.class,int.class,int.class); User user1=(User) constructors.newInstance("王重阳",001,18); System.out.println("构造器创建的对象"+user1); //通过反射调用方法 User user2= (User) c.newInstance(); //通过反射获取一个方法 Method method=c.getMethod("setName", String.class); //invoke 激活 method.invoke(user2,"王重阳"); System.out.println(user2.getName()); //通过反射操作属性 User user3=(User)c.newInstance(); Field field=c.getDeclaredField("name"); //不能操作私有成员变量,需要求该权限检测的开关 field.setAccessible(true);//关掉权限检测 field.set(user3,"王老六"); System.out.println(user3.getName()); }
关闭权限检测能减少利用反射调用方法的运行时间
setAccessible(true)
反射操作泛型
java采用泛型擦除机制来引入泛型,java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制转换类型转换问题,但是一旦编译完成,所有和泛型相关的类型全部擦除。
为了通过反射操作这些类型,Java新增了ParameterizedType,GenericArrayType,TypeVariable,和wildcardType几种类型来代不能被归一到Class类型但是又和原始类型齐名的类型