Java---->强烈安利0_0详解注解和反射机制
时刻保持学习之心,方能成就不世功业
注解和反射
1. 注解
1.1 注解概念
注解:Java注解(Annotation)又称Java标注,是JDK5.0之后引入的一种注释机制,它可以标注在类,方法,参数、变量、包等都可以被标注。Java的标注可以通过反射来获取内容;
2.注解类型
2.1 内置注解
三个注解均在Java.Lang中,且SuppressWarnings(“all or uncher…”)
@Override—>检查该方法是否重写方法,如果其父类和实现的接口没有该方法,则会报错;
@Deprecated---->标记过时的方法,如果该方法过时,会发出警告,且该方法能用,但不建议使用;
@SuppressWarnings—>忽略代码中的声明警告;
2.2 元注解
元注解也是注解的注解,主要负责注解其他注解包括四个,在Java.lang.Annoation类型下
@Target---->用来描述注解的使用范围,属于一个接口类型
@Target(value = {ElementType.METHOD,ElementType.PARAMETER,ElementType.TYPE})
// 适用范围,可以是方法,类,包@interface test{}
@Retention ---->描述注解在三个位置中某个还有效,一般在runtime中,
runtime>class>source
@Retention(RetentionPolicy.CLASS)// 注解保存的地方(source,class,runtime) 一般在runtime中生效@interface test{}
@Documented Java注解是否生成在Javadoc中
@Inherited 子类可以继承父类中的注解
2.3 自定义注解
我们可以通过@interface 注解名{ 定义内容 }来自定义注解;注解也可设置返回值但只能是基本类型(Class,eum,Sting)
注解的元素必须有值,我们一般在定义时通常使用0,来设置默认的注解值
@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)// 注解生效的地方(source,class,runtime) 一般在runtime中效@interface test{// 注解声明元素的默认格式:类型 属性名 ();// 可通过default 还设置声明参数的默认值,当有默认值时,注解参数可不填String name() default "xixi";//当注解中的声明元素只有一个时,可通过Value来命名String value();//只有一个时// 也可定义数组String[] school();}
3. 反射
3.1 概念
反射机制:(Reflection)是一种将Java静态语言变为准动态语言的关键,反射机制允许程序在运行阶段通过Reflection API取得任何类的内部信息,并且能操作任意对象的属性和方法;
原理:加载完类之后,会在堆内存方法区产生一个class类型对象(一个类只有一个class对象),这个对象包含完整类的信息结构,我们可以通过对象看到类的结构,这个对象就像一个镜子,通过其看到类的结构,因此为反射
3.2 执行方式
反射机制:实例化对象------>getClass()方法------>得到完整的包类名称
正常机制:引入需要的“包类”名称------>new 实例化------->取得实例化对象
3.3 反射的优点和缺点
优点:
可以实现动态编译和创建对象,有很大的灵活性
缺点:
影响性能,反射是一种解释操作,需要通过JVM虚拟机,要满足什么样的要求,这种操作总是慢于正常的操作;
3.3 获取Class类的方式
// 定义父类person
class Person{String name;int age;private int id;public Person() {}public Person(String name, int age, int id) {this.name = name;this.age = age;this.id = id;}
}
// 子类student继承person类
class student extends Person{public student(){this.name="学生";}
}
// 子类老师类继承person类
class teach extends Person{public teach(){this.name="老师";}
}
//实例化对象
Person person =new student();
方式一:通过实例化对象getClass获取Class
Class c1 = person.getClass();
System.out.println(c1.hashCode());
方式二:通过Class.forname()获取
Class c2 = Class.forName("com.muzi.test.Test.student"); //需要万完整的包名
System.out.println(c2.hashCode());
方式三:通过类名.Class获取Class
Class<student> c3 = student.class; // 已知类名的情况下
System.out.println(c3.hashCode());
方式四:通过基本类型的包装类内置TYPE属性
Class<Integer> c4 = Integer.TYPE;
System.out.println(c4);
方式五: 通过获取的Class类,在去获取父类的Class
Class c5 = c3.getSuperclass();
System.out.println(c5);
执行结果图:
3.4 各类型的Class
我们有很多类型都可以获取Class,Object,class,void,int[],String [] 等;
通过代码测试各类获取Class:
public static void main(String[] args) {Class<Object> c1 = Object.class; //object 类获取Class<Comparable> c2 = Comparable.class; //接口Class<String[]> c3 = String[].class; // 一维数组Class<int[][]> c4 = int[][].class; //二维数组获取Class<Override> c5 = Override.class; //注解获取Class<ElementType> c6 = ElementType.class; // 枚举Class<Void> c7 = void.class; // void 获取Class<Class> c8 = Class.class; //ClassClass<Integer> c9 = Integer.class; // 基本数据类型// 输出其class类 每个的hashCode()值不一样System.out.println(c1.hashCode());System.out.println(c2.hashCode());System.out.println(c3.hashCode());System.out.println(c4.hashCode());System.out.println(c5.hashCode());System.out.println(c6.hashCode());System.out.println(c7.hashCode());System.out.println(c8.hashCode());System.out.println(c9.hashCode());}
3.5 类的加载过程
步骤:
// jvm在加载类中,会先加载方法区内的静态文件 在堆中生成class文件
public class Test_03 {public static void main(String[] args) {A a = new A();System.out.println(a.m);}
}
class A {static {System.out.println("A的静态代码块的加载");int m = 300;}static int m = 10;public A() {System.out.println("A的初始化构造方法");}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fiNewK5q-1604460874885)(C:\Users\暴走小萝莉\AppData\Roaming\Typora\typora-user-images\image-20201101111443850.png)]
通过结果分析:程序 执行的时候先加载static方法,在加载构造方法,通过执行类构造器<clinit>方法,
3.6 类的初始化
主动引用与被动引用:
主动引用
java类的初始化阶段,虚拟机规范严格规定了5种情况必须立即对类进行初始化。
- 遇到new、getstatic、putstatic或invokestatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。
- 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
- 当初始化一个类时,如果发现其父类没有进行过初始化,则需要先触发其父类的初始化。
- 当虚拟机启动时,用户需要制定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个类。
- 当使用JDK1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_geStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。
被动引用
除了上述5种场景,其他所有类的方式都不会触发初始化,称为被动引用。
代码实现:
// 测试主动引用和被动引用
public class Test_04 {static {System.out.println("main方法初始化");}public static void main(String[] args) {//使用子类 ,父类也会被初始化,且先执行父类的方法 --->主动引用//Son son = new Son();// 被动引用// 当子类引用父类的静态变量,子类的静态代码块不会被初始化System.out.println(Son.n);// 定义一个数组,被动引用时,不会触发出初始化//Son[] array=new Son[5];// 调用Son的常量 ,已经存在于常量池中,不会触发初始化;System.out.println(Son.M);}
}//父类
class Father {static int n = 5;static {System.out.println("父类初始化");}public Father() {System.out.println("父类构造方法");}
}// 子类
class Son extends Father {static {System.out.println("子类初始化");}final static int M = 2;}
3.7 类加载的作用
1.Java文件运行的过程:
2.类加载的作用:
概念------->类加载:通过将字节码 .class文件内容加载到内存中,并将这些静态的数据转化为方法区运行时的数据结构,后在堆中生成一个Java.lang.class对象,作为方法区类数据的访问入口;
类缓存:标准JavaSE类加载器可以按照要求查找类,如果某个类一旦被加载到类加载器中,那么它将维持加载一段时间,jvm的垃圾回收机制可以回收这些class对象;
作用:将类加载到内存中;—>jvm规范了四种类加载器:
引导类加载器:---->Bootstrap ClassLoader
由c++编写,是jvm自带的类加载器,负责java平台核心库,用来加载Java核心类库,不能直接获取;rt.jar
扩展类加载器----->Extension ClassLoader
负责jre/lib/ext目录下的jar包,或者-D Java.ext.dirs指定目录下的jar包装入工作库
系统类加载器—>System ClassLoader
负责java -classpath所指目录下的类与jar包的装入工作,是最常见的类加载器
自定义类加载器—> ClassLoader
新建一个类继承自java.lang.ClassLoader,重写它的findClass方法。 将class字节码数组转换为Class类的实例;调用loadClass方法即可.
3. 代码实现:
public static void main(String[] args) {// 获取系统类加载ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();System.out.println(systemClassLoader);// 获取系统加载器的父类 ------->扩展加载器ClassLoader parent = systemClassLoader.getParent();System.out.println(parent);// 获取扩展加载器的父类----->引导类加载器(根加载器)是jvm虚拟机自带的,不能直接获取ClassLoader parent1 = parent.getParent();System.out.println(parent1);}// 测试当前类,是什么加载器加载的ClassLoader classLoader = Class.forName("com.muzi.test.Test.Test_05").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"));/*** C:\Program Files\Java\jdk1.8.0_181\jre\lib\charsets.jar;* C:\Program Files\Java\jdk1.8.0_181\jre\lib\deploy.jar;* C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;* C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;* C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\dnsns.jar;*/
3.8 获取运行类的完整结构
- 通过反射获取类的完整结构:
反射的方法---->Field、Method、Constructor、Superclass、Interface、Annotation
代码实现:
// 通过反射获取类Class c1 = Class.forName("com.muzi.test.Test.Person");System.out.println(c1);//获取类的名字System.out.println(c1.getName()); // 包名+类名;System.out.println(c1.getSimpleName());// 获取类名// 获取类的属性Field[] fields = c1.getDeclaredFields(); //可以找到类下的所有属性包名+属性名for (Field field : fields) {System.out.println(field);}Field[] fields1 = c1.getFields();// 只能获取 公共类的属性;for (Field field : fields1) {System.out.println(field);}Field field = c1.getField("name");//获取指定公共属性System.out.println(field);Field id = c1.getDeclaredField("id"); //获取类中的任意属性System.out.println("field2" + id);// 获取类的方法Method[] methods = c1.getMethods();// 只能获取公共类的方法和父类的方法for (Method method : methods) {System.out.println("method======" + method);}Method[] declaredMethods = c1.getDeclaredMethods();// 获取本类的私有和公共类的方法for (Method declaredMethod : declaredMethods) {System.out.println("declaredMethods====" + declaredMethod);}Method method = c1.getEnclosingMethod();//一个class表示在方法中的一个本地或匿名class, 那么通过java.lang.Class.getEnclosingMethod()方法将返回的底层类的立即封闭方法。 反之则为NULL。System.out.println(method);Method method1 = c1.getMethod("getName");//获取类的指定公共的方法System.out.println(method1);Method getId = c1.getDeclaredMethod("getId", null); //获取私有类的方法System.out.println("getId____"+getId);
3.9 动态创建对象执行方法
1.通过Class获取对象属性和结构的作用:
我们通过反射机制获取运行类的属性和结构,无需自身实例化,通过反射机制来例化 newInstance()
- ===代码实现:=
//获取类对象Class c1 = Class.forName("com.muzi.test.Test.TsetPerson");// 实例化对象 newInstance() 必须要由无参构造器//Person person = (Person) c1.newInstance();// System.out.println(person);//通过构造器创建对象 ----->有参构造 使用newInstance 这是在无参构造的方法下://Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);// Person2 instance = (Person2) constructor.newInstance("木子", 5, 18);//System.out.println(instance);// 通过方式调用普通方法//先实例化类TsetPerson p2 = (TsetPerson) c1.newInstance();//通过反射获取方法Method setName = c1.getDeclaredMethod("setName", String.class);// invoke(类型名,设置值) 激活 类型名必须声明setName.invoke(p2, "阿木木"); //给方法传入值System.out.println(p2.getName());// 通过实例化对象获取// 通过反射操作属性----->在无参构造的方法情况下TsetPerson person3 = (TsetPerson) c1.newInstance();Field name = c1.getDeclaredField("name");// 通过set Get方法给其赋值name.set(person3, "阿木木2");// 无法输出,因为权限的限定 解除权限name.setAccessible(true);//设置解除对name的权限限定System.out.println(person3.getName());
如果频繁对私有属性的调用,建议开启setAccessible(true),提高反射的效率;
3.10 通过反射获取泛型
泛型:Java采用泛型擦除机制引入泛型,Java中的泛型仅是给Javac使用的,确保数据的安全和免去强制类型转化的问题,且一旦编译完成,就会擦除所有和泛型有关的类型;
为通过反射机制获取泛型,Java新增了很多类型他们不能与class归在一类但是又与class齐名
通过代码来实现:
public static void test01(Map<String, TsetPerson> map, List<TsetPerson> list) {System.out.println("test01");}public Map<String, TsetPerson> test02() {System.out.println("test02");return null;}public static void main(String[] args) throws NoSuchMethodException {//通过类名获取class对象Method test01 = Test_09.class.getMethod("test01", Map.class, List.class);// 通过获取的方法来获取泛型Type[] genericParameterTypes = test01.getGenericParameterTypes();for (Type genericParameterType : genericParameterTypes) {System.out.println(genericParameterType);//将泛型打印出来// 获取泛型的参数信息,利用条件判断//genericParameterType ---->泛型参数类型//ParameterizedType 参数类型if (genericParameterType instanceof ParameterizedType) {//getActualTypeArguments----->获取实际参数类型Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();for (Type actualTypeArgument : actualTypeArguments) {System.out.println(actualTypeArgument);}}}//通过返回值参数泛型获取Method test02 = Test_09.class.getMethod("test02", null);Type genericReturnType = test02.getGenericReturnType();if (genericReturnType instanceof ParameterizedType) {//genericReturnType----->返回值参数泛型Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();for (Type actualTypeArgument : actualTypeArguments) {System.out.println("$$$$$$$" + actualTypeArgument);}}}
3.11 获取注解信息
ORM----->对象关系映射----->Object Relationship Mapping
反射操作注解:getAnnotations 与 getAnnotation
关于类和表的注解关系:
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {//通过反射获取注解信息Class c1 = Class.forName("com.muzi.test.Test.Animal");Annotation[] annotation = c1.getAnnotations();for (Annotation annotation1 : annotation) {System.out.println(annotation1);}//通过getAnnotation反射获取注解内部的值annotation1.value();Table annotation1 = (Table) c1.getAnnotation(Table.class);String value = annotation1.value();System.out.println(value);//获取属性的注解Field name = c1.getDeclaredField("name");Annotation[] annotation2 = name.getAnnotations();for (Annotation annotation3 : annotation2) {System.out.println("$$$$$" + annotation3);}//获取属性内部 的值 通过属性去获取注解的值与方法不同field annotation3 = name.getAnnotation(field.class);System.out.println(annotation3.column());System.out.println(annotation3.type());System.out.println(annotation3.type());}}@Table("db_animal")
class Animal {@field(column = "db_id", type = "int", length = 10)private int id;@field(column = "db_name", type = "String", length = 20)private String name;@field(column = "db_age", type = "int", length = 3)private int age;public Animal(int id, String name, int age) {this.id = id;this.name = name;this.age = age;}@Overridepublic String toString() {return "Animal{" +"id=" + id +", name='" + name + '\'' +", age=" + age +'}';}public Animal() {}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}// 创建注解类--->类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table {String value();
}// 创建属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface field {String column();//列String type();// 类型int length();//长度
}
Java---->强烈安利0_0详解注解和反射机制相关推荐
- python反射机制_详解python之反射机制
一.前言 deff1():print('f1')deff2():print('f2')deff3():print('f3')deff4():print('f4') a= 1 test.py impor ...
- Java单元测试之JUnit4详解
2019独角兽企业重金招聘Python工程师标准>>> Java单元测试之JUnit4详解 与JUnit3不同,JUnit4通过注解的方式来识别测试方法.目前支持的主要注解有: @B ...
- Java编程配置思路详解
Java编程配置思路详解 SpringBoot虽然提供了很多优秀的starter帮助我们快速开发,可实际生产环境的特殊性,我们依然需要对默认整合配置做自定义操作,提高程序的可控性,虽然你配的不一定比官 ...
- Java基础学习总结(24)——Java单元测试之JUnit4详解
Java单元测试之JUnit4详解 与JUnit3不同,JUnit4通过注解的方式来识别测试方法.目前支持的主要注解有: @BeforeClass 全局只会执行一次,而且是第一个运行 @Before ...
- java 8 新功能详解_Java 8和Java 14之间的新功能
java 8 新功能详解 从版本9开始,Java每6个月就有一次新功能,因此很难跟踪这些新更改. 互联网上的大多数信息都描述了最近2个Java版本之间的变化. 但是,如果您的情况与我相似,则说明您使用 ...
- Java基准测试工具JMH详解
Java基准测试工具JMH详解 1.JMH概述 1.1 JMH简介 1.2 JMH与JMeter区别 1.3 JMH注解说明 2.JMH验证 2.1 创建项目 2.2 引入依赖 2.3 启动异常解决 ...
- Java线程池ThreadPool详解
Java线程池ThreadPool详解 1. 线程池概述 1.1 线程池简介 1.2 线程池特点 1.3 线程池解决问题 2. 线程池原理分析 2.1 线程池总体设计 2.6 线程池流转状态 2.2 ...
- java -jar 和 -cp详解
java -jar 和 -cp详解 命令行执行程序 假如我们有一个程序,把它打包成Test.jar,如何运行才能成功输出Hello World package com.test; public cla ...
- java访问修饰符详解——学java,零基础不怕,不只要理论,更要实践+项目,a href=http://www.bjweixin.com太原维信科技提供 /a...
java访问修饰符详解--学java,零基础不怕,不只要理论,更要实践+项目 <a href=http://www.bjweixin.com>太原维信科技提供 </a> pub ...
最新文章
- python从入门到精通怎么样-Python 从入门到精通:一个月就够了
- Connection to node -1 (Desktop/192.168.0.102:9091) could not be established.
- 原生JavaScript第一篇
- Linux系统服务之dhcp
- 猜数字(HDU-2178)
- Android 系统(65)---Android修改分区格式为F2FS
- 为什么说堡垒机是企业IT运维的“安全终结者”?
- elk平台分析nginx日志的基本搭建
- 调整Redmine的用户显示格式
- CFA一级考试题型是什么?好不好考?
- 京东和淘宝近十年搜索热度对比,发现顶尖者的PK规律
- 键盘输入一个高精度的正整数N,去掉其中任意S个数字后剩下的数字按原左右次序将组成一个新的正整数。编程对给定的N和S,寻找一种方案使得剩下的数字组成的新数最小。(C++)(贪心法)
- CentOS7和CentOS8 FreeSWITCH 1.10.7 简单图形化界面18--内网的讯时FXO网关SIP对接到内网的FreeSWITCH
- MacOS强制卸载第三方输入法(搜狗输入法、百度输入法)
- 计算机编程教育资源,风变编程以科技实现教育普惠,俱进教育公平
- 《单片机原理及应用》复习提纲
- 快捷生成HTML代码的实现
- 求近似数最值_求近似数的方法
- 关于如何在Termux上安装kali(最好用旧手机)
- 「JOISC 2018 Day 3」比太郎的聚会