提供网站建设课程报告,镇江网站制作价格如何计算,电脑系统优化工具,免费行情app注解和反射
一、注解
什么是注解#xff1f;Annotation Override #xff1a;表示一个方法声明打算重写超类的另一个方法声明Deprecated#xff1a;表示不鼓励程序员使用这样的元素#xff0c;#xff08;此注释可以用于修饰方法#xff0c;属性#xff0c;类#xf…注解和反射
一、注解
什么是注解Annotation Override 表示一个方法声明打算重写超类的另一个方法声明Deprecated表示不鼓励程序员使用这样的元素此注释可以用于修饰方法属性类SupperssWarnings()用来抑制编译时的警告信息all, unchecked, value{unchecked,deprecation})]
元注解
元注解的作用就是负责注解其他注解Java定义了4个标准的meta-annotation类型它们被用来对其他annotation类型作说明。 Target用于描述注解的使用范围即被描述的注解可以用在什么地方 表示我们的注解可以用在哪些地方(字段、方法等) Retention表示需要什么级别保存该注释信息用于描述注解的生命周期source源码 class runtime运行 表示我们的注解在什么地方还有效 Document说明该注解将被包含在javadoc中 表示是否将我们的注解生成在javadoc中 Inherited说明子类可以继承父类中的该注解 子类可以继承父类的注解
二、反射机制
动态语言 在运行时可以根据某些条件改变自身结构。如C、C#、JavaScript、PHP、Python
静态语言 在运行时结构不可改变的语言就是静态语言如Java、C、C
Java不是动态语言但是Java可以称之为“准动态语言”。即Java有一定的动态性我们可以利用反射机制获得类似动态语言的特性。
Reflection反射是Java被视为动态语言的关键反射机制允许程序在执行期间于Reflection API取得任何类的内部信息并能直接操作任意对象的内部属性及方法。
Class c Class.forName(java.lang.String)
加载完类之后在堆内存的方法区中就产生了一个Class类型的对象一个类只有一个Class对象这个对象就包含了完整的类的结构信息构造器、方法、字段、包括私有字段等。我们可以通过这个对象看到类的结构。这个对象就像一面镜子透过这个镜子看到类的结构所以我们形象的称之为反射 正常方式引入需要的“包类”名称 --- 通过new实例化---取得实例化对象 反射方式实例化对象---getClass()---取得完整的“包类”名称
Java反射机制提供的功能 在运行时判断任意一个对象所属的类、判断任意一个类所具有的成员变量和方法。 在运行时构造任意一个类的对象 在运行时获取泛型信息 在运行时调用任意一个对象的成员变量和方法 在运行时处理注解 生成动态代理 .......
Java反射优缺点 优点可以实现动态创建对象和编译体现出很大的灵活性。 缺点对性能有影响。使用反射基本上是一种解释操作我们可以告诉JVM我们希望做什么并且它满足我们的要求。这类操作总是慢于直接执行相同的操作。 java.lang.Class; 代表一个类java.lang.reflect.Method; 代表类的方法java.lang.reflect.Field; 代表类的成员变量java.lang.reflect.Constructor; 代表类的构造器
Class类
在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对象
Class类的常用方法 Class类中部分源码 public class ClassT {/*** 根据类的全限定名称获取 Class 对象** param className 类的全限定名称* return Class 对象* throws ClassNotFoundException 如果找不到指定名称的类*/public static Class? forName(String className)throws ClassNotFoundException {return Class.forNameImpl(className, false);}// 实现细节private static Class? forNameImpl(String name, boolean initialize)throws ClassNotFoundException {ClassLoader cl null;try {if (cl null) {cl Thread.currentThread().getContextClassLoader();}return cl.loadClass(name);} catch (ClassNotFoundException e) {// 尝试使用当前类加载器cl ClassLoader.getSystemClassLoader();return cl.loadClass(name);}}// 构造器是私有的不允许外部直接创建 Class 对象private Class() {}// 返回类的全限定名称public native String getName();// 返回类的规范名称public String getCanonicalName() {return null; // 实际实现可能返回具体的规范名称}// 返回类的简单名称public String getSimpleName() {int lastDot name.lastIndexOf(.);return (lastDot 0) ? name : name.substring(lastDot 1);}// 创建并返回此 Class 对象所表示的类的一个新实例Deprecated(since 9)public T newInstance() throws InstantiationException, IllegalAccessException {return getDeclaredConstructor().newInstance();}// 获取此 Class 对象所表示的类声明的所有方法public Method[] getDeclaredMethods() {Method[] result declaredMethods;if (result null) {// 实际实现会从类信息中获取方法// 这里简化处理result new Method[0];}return result;}// 获取此 Class 对象所表示的类声明的所有字段public Field[] getDeclaredFields() {Field[] result declaredFields;if (result null) {// 实际实现会从类信息中获取字段// 这里简化处理result new Field[0];}return result;}// 测试指定对象是否分配了此 Class 对象所表示的类或接口public boolean isInstance(Object obj) {return this.equals(obj.getClass());}// 获取此 Class 对象所表示的类的直接超类public native Class? getSuperclass();}
获取Class类的实例 若已知具体的类通过类的class属性获取该方法最为安全可靠程序性能最高。 Class c1 Person.class; 已知某个类的实例调用该实例的getClass()方法获取Class对象 Class c2 person.getClass(); 已知一个类的全类名且该类在类路径下可以通过Class类的静态方法forName()获取可能会抛出ClassNotFoundException Class c3 Class.forName(Deom1.Student); 基本内置数据类型的包装类都有一个Type属性 Class c4 Integer.Type; 还可以利用ClassLoader
哪些类型可以有Class对象 class外部类成员成员内部类静态内部类局部内部类匿名内部类。 Interface接口 []数组 enum枚举 annotation注解interface primitive type基本数据类型 void
package com.briup.chap13;import java.lang.annotation.ElementType;/*** author 35329*/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;// voidClass c9 Class.class;// ClassSystem.out.println(c1 c1);System.out.println(c2 c2);System.out.println(c3 c3);System.out.println(c4 c4);System.out.println(c5 c5);System.out.println(c6 c6);System.out.println(c7 c7);System.out.println(c8 c8);System.out.println(c9 c9);// 只要元素类型与维度一样就是同一个Classint[] a new int[10];int[] b new int[100];System.out.println(a.getClass().hashCode() a.getClass().hashCode());System.out.println(b.getClass().hashCode() b.getClass().hashCode());// 运行结果://c1 class java.lang.Object//c2 interface java.lang.Comparable//c3 class [Ljava.lang.String;//c4 class [[I//c5 interface java.lang.Override//c6 class java.lang.annotation.ElementType//c7 class java.lang.Integer//c8 void//c9 class java.lang.Class//a.getClass().hashCode() 21685669//b.getClass().hashCode() 21685669}} 类加载内存分析 类的加载过程 加载将class文件字节码内容加载到内存中并将这些静态数据转化成方法区的运行时数据结构然后生成一个代表这个类的java.lang.Class对象。 链接将Java类的二进制代码合并到JVM的运行状态之中的过程。 验证确保加载的类信息符合JVM规范没有安全方面的问题。 准备正式为类变量static分配内存并设置类变量默认初始值的阶段这些内存都将在方法去中进行分配。 解析虚拟机常量池内的符号引用常量名替换为直接引用地址的过程。 初始化 执行类构造器clinit()方法的过程类构造器clinit()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。类构造器是构造类信息的不是构造该类对象的构造器。 当初始化一个类的时候如果发现其父类还没有进行初始化则需要先触发其父类的初始化。 虚拟机会保证一个类的clinit()方法在多线程环境中被正确加锁和同步。 package com.briup.chap13;/*** author 35329*/public class Test05 {public static void main(String[] args) {A a new A();System.out.println(A.m);/*** 1、加载到内存会产生一个类对应Class对象* 2、链接链接结束后 m 0;* 3、初始化* clinit(){* System.out.println(A类静态代码块初始化);* m 300;* m 100;* }* m 100;*/}}class A{static {System.out.println(A类静态代码块初始化);m 300;}static int m 100;/** m 300* m 100* 后面的会覆盖前面的* */public A(){System.out.println(A类的无参构造初始化);}}// 运行结果//A类静态代码块初始化//A类的无参构造初始化//100
什么时候会发生类初始化 类的主动引用一定会发生类的初始化重点 当虚拟机启动先初始化main方法所在的类 new一个类的对象 调用类的静态成员除了final常量和静态方法 使用java.lang.reflect包的方法对类进行反射调用 当初始化一个类如果其父类没有被初始化则先会初始化它的父类 类的被动引用不会发生类的初始化 当访问一个静态域时只有真正声明这个域的类才会被初始化。如当通过子类引用父类的静态变量不会导致子类初始化。 通过数组定义类引用不会触发此类的初始化 引用常量不会触发此类的初始化常量在链接阶段就存入调用类的常量池中了
package com.briup.chap13;/*** author 35329*/// 测试类什么时候会初始化public class Test06 {static {System.out.println(mian类被加载);}public static void main(String[] args) throws ClassNotFoundException {// 1、主动被加载// Son son new Son();// 反射也会产生主动引用// Class.forName(com.briup.chap13.Son);// 不会产生类的引用的方法// System.out.println(Son.b Son.b);// Son[] arr new Son[5];System.out.println(Son.M Son.M);}}class Father{static int b 2;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对象。 类加载器作用是用来把类class装载进内存的。JVM规范定义了如下类型的类的加载器。 引导类加载器用C编写是JVM自带的类加载器负责Java平台核心库用来装载核心类库。该加载器无法直接获取。 扩展类加载器负责jre/lib/ext目录下的jar包或 -D java.ext.dirs指定目录下的jar包装入工作库 系统类加载器负责java -classpath 或 -D java.class.path所指的目录下的类与jar包装入工作是最常用的加载器。
package com.briup.chap13;import com.briup.chap07.test.bean.Cat;/*** author 35329*/public class Test07 {public static void main(String[] args) throws ClassNotFoundException {// 获取系统类的加载器ClassLoader systemClassLoader ClassLoader.getSystemClassLoader();System.out.println(systemClassLoader systemClassLoader);// 获取系统类加载器的父类加载器 -- 扩展类加载器ClassLoader parent systemClassLoader.getParent();System.out.println(parent parent);// 获取扩展类加载器 -- 根加载器C/cClassLoader parent1 parent.getParent();System.out.println(parent1 parent1);// 运行结果//systemClassLoader sun.misc.Launcher$AppClassLoader18b4aac2//parent sun.misc.Launcher$ExtClassLoader14ae5a5//parent1 null// 测试当前类是那个加载器加载的ClassLoader classLoader Class.forName(com.briup.chap13.Test07).getClassLoader();System.out.println(classLoader classLoader);// 测试JDK内置的类是谁加载的System.out.println(Class.forName(\java.lang.Object\).getClassLoader() Class.forName(java.lang.Object).getClassLoader());// 运行结果://classLoader sun.misc.Launcher$AppClassLoader18b4aac2//Class.forName(java.lang.Object).getClassLoader() null// 如何获得系统类加载器可以加载的路径System.out.println(System.getProperty(java.class.path));// 运行结果/** C:\Program Files\Java\jdk1.8.0_144\jre\lib\charsets.jar;* C:\Program Files\Java\jdk1.8.0_144\jre\lib\deploy.jar;* C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\access-bridge-64.jar;* C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\cldrdata.jar;* C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\dnsns.jar;* C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jaccess.jar;* C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jfxrt.jar;* C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\localedata.jar;* C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\nashorn.jar;* C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunec.jar;* C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunjce_provider.jar;* C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunmscapi.jar;* C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunpkcs11.jar;* C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\zipfs.jar;* C:\Program Files\Java\jdk1.8.0_144\jre\lib\javaws.jar;* C:\Program Files\Java\jdk1.8.0_144\jre\lib\jce.jar;* C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfr.jar;* C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfxswt.jar;* C:\Program Files\Java\jdk1.8.0_144\jre\lib\jsse.jar;* C:\Program Files\Java\jdk1.8.0_144\jre\lib\management-agent.jar;* C:\Program Files\Java\jdk1.8.0_144\jre\lib\plugin.jar;* C:\Program Files\Java\jdk1.8.0_144\jre\lib\resources.jar;* C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar;* D:\JDProject\corejava\out\production\corejava;* C:\Users\35329\.m2\repository\junit\junit\4.12\junit-4.12.jar;* C:\Users\35329\.m2\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;* D:\idea\ideaIU-2020\lib\idea_rt.jar* */}}
获取运行时类的完整结构
通过反射获取运行时类的完整结构
Field、Method、Constructor、Superclass、Interface、Annotation
package com.briup.chap13;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;/*** 通过反射获得类的信息* author 35329*/public class Test08 {public static void main(String[] args) throws Exception {Class c1 Class.forName(com.briup.chap13.user);// 获得类的名字System.out.println(c1.getName() c1.getName()); // 获得包名 类名System.out.println(c1.getSimpleName() c1.getSimpleName()); // 获得类名System.out.println(-----------------------------------);// 获得类的属性Field[] fields c1.getFields(); // 只能找到public属性fields c1.getDeclaredFields(); // 可以找到全部属性for (Field field : fields) {System.out.println(field);}// 获得指定属性的值Field name c1.getDeclaredField(name);System.out.println(name name);System.out.println(-----------------------------------------);// 获得类的方法Method[] methods c1.getMethods(); // 获得本类及其父类的全部public方法for (Method method : methods) {System.out.println(正常的 method);}Method[] declaredMethods c1.getDeclaredMethods(); // 获得本类的所有方法(包括私有的)for (Method declaredMethod : declaredMethods) {System.out.println(declaredMethod);}// 获得指定方法// 重载Method getName c1.getMethod(getName, null);Method setName c1.getMethod(setName, String.class);System.out.println(getName getName);System.out.println(setName setName);System.out.println(--------------------------------);// 获得类的构造器Constructor[] constructors c1.getConstructors(); // 获得public修饰的构造方法for (Constructor constructor : constructors) {System.out.println(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);}}
小结 在实际操作中取得类的信息的操作代码不会经常开发。 一定要熟悉java.lang.reflect包的作用反射机制。 如何取得属性、方法、构造器的名称修饰符等。
有了Class对象能做什么
创建一个类的对象调用Class对象的newInstance()方法 类必须有一个无参数的构造器 类的构造器的访问权限需要足够
思考难道没有无参的构造器就不能创建对象了吗只要在操作的时候明确的调用类中的构造器并将参数传递进去之后才可以实例化操作。
步骤如下 通过Class类的getDeclaredConstructor(Class ... parameterTypes)取得本类的指定形参类型的构造器。 向构造器的形参中传递一个对象数组进去里面包含了构造器中所需的各个参数。 通过Constructor实例化对象。
通过指定的方法
通过反射调用类中的方法通过Method类完成 通过Class类的getMethod(String name, Class ... parameter Types)方法取得一个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语言访问检查。 提高反射的效率如果代码中必须用反射而该句代码需要频繁的被调用那么请设置为true 使得原本无法访问的私有成员也可以访问 参数值为false则指示反射的对象应该实施Java语言访问检查。
package com.briup.chap13;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.Map;/*** 分析性能问题* author 35329*/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(普通方法执行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 NoSuchMethodException, IllegalAccessException, InvocationTargetException {test01();test02();test03();}}
反射操作泛型(了解即可) Java采用泛型擦除的机制来引入泛型Java中的泛型仅仅是给编译器javac使用的确保数据的安全性和免去强制类型转换问题但是一旦编译完成所有和泛型有关的类型全部擦除。 为了通过反射操作这些类型Java新增了ParameterizedType、GenericArrayType、TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。 ParameterizedType表示一种参数化类型比如CollectionString GenericArrayType表示一种元素类型的参数化类型或者类型变量的数组类型 TypeVariable是各种类型变量的公共接口 WildcardType代表一种通配符类型表达式
反射操作注解
练习什么是ORM Object relationship Mapping --对象关系映射 类和表结构的映射关系 类和表结构对应 属性和字段对应 对象和记录对应
package com.briup.chap13;import java.lang.annotation.*;import java.lang.reflect.Field;/*** 练习反射操作注解* author 35329*/public class Test12 {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {Class? c1 Class.forName(com.briup.chap13.Student2);// 通过反射获取注解Annotation[] annotations c1.getAnnotations();for (Annotation annotation : annotations) {System.out.println(annotation);}// 获得注解的Value值TableBriup tableBriup c1.getAnnotation(TableBriup.class);String value tableBriup.value();System.out.println(value);// 获得类指定的注解Field f c1.getDeclaredField(name);FieldBriup annotation f.getAnnotation(FieldBriup.class);System.out.println(annotation.columnName());System.out.println(annotation.type());System.out.println(annotation.length());}}TableBriup(db_student)class Student2{FieldBriup(columnName db_id, type int, length 10)private int id;FieldBriup(columnName db_age, type int, length 10)private int age;FieldBriup(columnName db_name, type varchar, length 3)private String name;public Student2() {}public Student2(int id, int age, String name) {this.id id;this.age age;this.name name;}Overridepublic String toString() {return Test12{ id id , age age , 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;}}// 类名注解Target(ElementType.TYPE)Retention(RetentionPolicy.RUNTIME)interface TableBriup{String value();}// 属性的注解Target(ElementType.FIELD)Retention(RetentionPolicy.RUNTIME)interface FieldBriup{String columnName();String type();int length();}