注解和反射
什么是注解(Annotation)
-
Annotation 是 从 JDK5.0 开始引进的新技术 ,(2019 年 就 12了)
-
Annotation的作用 :
1. 不是程序本身,可以对程序作出解释. (这一点和注释(comment)没什么区别)
2. 可以被其他程序(比如 编译器)读取 -
Annotation 的格式
- 注解是以"@ 注释名" 在代码中存在的 ,还可以添加一些参数值, 例如 - 如 :@SupperessWarning(value="unchecked")
-
Annotation 在哪里使用 ?
- 可以附加在 package class method field 等上面,相当于给他们添加了额外的辅助信息我们
我们可以通过反射机制编程实现对这些数据的访问
内置注解
public class annotation {
//@Override 重写的注解
@Override
public String toString() {
return super.toString();
}
//@Deprecated 不推荐程序员使用,但是可以使用,或者存在更好的方式
@Deprecated
public static void test(){
System.out.println("Deprecated");
}
//@SuppressWarnings() 镇压警告的注解,有参数
@SuppressWarnings("all")
public void test02(){
List list = new ArrayList();
}
public static void main(String[] args) {
test();
}
元注解
- 为其他注解 注解
//自定义注解
// @Target 作用域 表示我们的注解可以用在哪些地方 (方法,类)
@Target(value = {ElementType.METHOD,ElementType.TYPE})
//@Retention(保持) 表示我们的注解在什么地方有效
//什么时间有效 runtime(运行时还有效) > class (在编译后有效) > sources(只在源码中有效)
@Retention(value = RetentionPolicy.RUNTIME)
//@Documented 表示是否将我们的注解生成在JAVADoc文档中
@Documented
//@Inherited 子类可以继承父类的注解
@Inherited
@interface MyAnnotation{
}
自定义注解
@interface MyAnnotation2
public class Tset02 {
public static void main(String[] args) {
}
@MyAnnotation2(age = 18)//记得传参数,
public void test1(){
}
@MyAnnotation3("李白") // 这个就不用,可以直接传
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 {"北京大学","清华大学"};
}
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation3{
String value();
}
反射如镜子
得到class 类的几种方法
public class Test03 {
public static void main(String[] args) throws ClassNotFoundException {
Person person = new student();
System.out.println("这个人是:"+person.name);
//方式一 :通过对象获得 ,, 知道实例
Class<? extends Person> c1 = person.getClass();
System.out.println(c1.hashCode());
//方式二 :forname 获得 ,, 知道路径
Class<?> c2 = Class.forName("com.kaung.student");
System.out.println(c2.hashCode());
//方式三: 通过类名 .class 获得 属性获取
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{
String name;
public Person(){
}
public Person(String name) {
this.name = name;
}
}
class student extends Person{
public student() {
this.name = "学生";
}
}
class teacher extends Person{
public teacher() {
this.name = "老师";
}
}
所有类型的Class对象
public class Test04 {
public static void main(String[] args) {
Class<Object> c1 = Object.class; //类
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<Integer> c7 = Integer.class;//基本数据类型
Class<Void> c8 = void.class;//void
Class<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);
int[] a = new int[10];
int[] b = new int[100];
System.out.println(a.getClass().hashCode());
System.out.println(b.getClass().hashCode());
}
}
类的加载内存分析
- static 静态代码块 最先加载 在变量 被 new 之前就已经执行
-
加载到内存,会产生一个类对应的Class对象
-
链接 ,链接结束后 m = 0
-
初始化
(){
System.out.println(“A类静态代码块初始化”
m =300;
m =100;
}
public class Test05 {
public static void main(String[] args) {
A a = new A();
System.out.println(A.m);
}
}
class A{
public A() {
System.out.println("A类无参构造初始化");
}
static {
System.out.println("A类静态代码块初始化");
m = 300;
}
static int m=100;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TexoXoE2-1637922340728)(C:\Users\74771\AppData\Roaming\Typora\typora-user-images\1637824314966.png)]
- 所以
- static 不用加载就可以调用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JjQmwdgU-1637922340742)(C:\Users\74771\AppData\Roaming\Typora\typora-user-images\1637824689890.png)]
分析类初始化
什么时候发生类初始化
-
类的主动引用( 一定会发生类的初始化)
1. 当虚拟机启动,先初始化main 方法所在的类
2. new 一个类的对象
3. 调用类的静态成员 (除了 final 常量 ) 和静态方法
4. 使用 java .lang .reflect 包的方法对类进行反射调用
5. 当初始化一个类,如果其父类没有被初始化, 则先初始化其父类 -
类的被动引用(不会发生类的初始化)
1. 当访问一个静态域时 , 只有真正声明这个域的类才会被初始化. 如 ,当通过子类引用父类的静态变量 , 不会导致子类的初始化 2. 通过数组定义类引用 , 不会触发此类的初始化 3. 引用常量不会触发此类的初始化 ( 常量在连接阶段就存入调用类的常量池中了)
public class Test06 {
public static void main(String[] args) throws ClassNotFoundException {
//1. 主动引用
Son son = new Son();
//2 反射也会产生主动引用
Class.forName("com.kaung.Son");
//不会产生类的引用的方法
System.out.println(son.b);
System.out.println(son.M);
Son[] sons = new Son[5];
}
}
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)装载进内存的 . JVM规范定义了如下类型的类的加载器
类加载的作用:将class 文件字节码内容加载到内存中,并将这些静态数据转化成方法区的运行时数据结构, 然后再堆中生成一个代表这个类的java .long.class 对象 ,作为方法区中类数据的访问入口.
类缓存:
标准的javaSE类加载器可以按要求查找类, 但一旦某个类被加载到类加载器中, 它将维持加载 (缓存) 一段时间 .不过JVM 垃圾回收机制 可以回收这些 Class 对象
- 引导累加载器 : 用c++ 编写的, 是JVM自带的类加载器 ,负责Java平台核心库,用来装载核心类库.该加载器无法直接获取
- 扩展类加载器 :负责jre /lib /ext 目录下的jar 包或 - D java.ext.dirs指定目录下的jar包装入工作库
- 系统类加载器 :负责java - classpath 或者 -D java.class.path 所指的目录下的类与jar 包装入工作,是最常用的加载器
- 类加载器 就是 类装载器 !!!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QvjHKY5Q-1637922340746)(C:\Users\74771\AppData\Roaming\Typora\typora-user-images\1637828832731.png)]
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.kaung.Test07").getClassLoader();
System.out.println(classLoader);
ClassLoader classLoader1 = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader1);
}
}
获得类的对象
public class Test08 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class<?> c1 = Class.forName("com.kaung.student");
//获得类的名字
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);
// }
//能找到全部属性
Field[] fields = c1.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
//获得指定属性的值
Field s = c1.getDeclaredField("name");
System.out.println(s);
//获得类的方法
System.out.println("===========");
//获得本类及其父类的全部public 方法
Method[] methods = c1.getMethods();
for (Method method : methods) {
System.out.println(method);
}
//获得本类的全部方法
methods = c1.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method);
}
//获得指定方法
//可以有重载,把null 改为 int string 类型就可
Method student = c1.getMethod("student", null);
System.out.println(student);
//获得指定构造器
System.out.println("=============");
Constructor<?>[] constructors = c1.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
Constructor<?>[] declaredConstructors = c1.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
//获得指定的构造器
//按照构造方法传参数
// c1.getDeclaredConstructor(String.class,int.class);
}
}
动态创建对象执行方法
- 通过构造器创建对象
- 通过反射调用普通方法
- 通过反射操作属性
- invoke : 激活的意思
//动态的创建对象 , 通过反射
public class Test09 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
//获得Class 对象
Class<?> c1 = Class.forName("com.kaung.student");
//构造一个对象
//student stu = (student) c1.newInstance();
//System.out.println(stu);
//通过构造器创建对象
//Constructor<?> constructor = c1.getDeclaredConstructor(String.class, int.class, String.class);
//student stu2 = (student) constructor.newInstance("李白", 18, "少年");
//System.out.println(stu2);
//通过反射调用普通方法
// student stu3 = (student) c1.newInstance();
//通过发射获取一个方法
Method method = c1.getMethod("show", int.class);
//invoke : 激活的意思
//(对象 , "方法的值")
// method.invoke(stu3,10);
// System.out.println(stu3.age);
//通过反射操作属性
student stu4 = (student) c1.newInstance();
Field name = c1.getDeclaredField("name");
//不能直接操作私有属性,要关闭程序的安全监测
name.setAccessible(true);
name.set(stu4,"dufu");
System.out.println(stu4.name);
}
}
分析性能
//分析性能问题
public class Test10 {
//普通方式调用
public static void test01(){
student student = new student();
long starttime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
student.show();
}
long endtime = System.currentTimeMillis();
System.out.println("执行时间:"+(endtime-starttime));
}
//反射方式调用
public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
student student = new student();
Class<? extends com.kaung.student> c1 = student.getClass();
Method show = c1.getMethod("show", null);
long starttime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
show.invoke(student,null);
}
long endtime = System.currentTimeMillis();
System.out.println("执行时间:"+(endtime-starttime));
}
//反射方式调用,但关闭监测
public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
student student = new student();
Class<? extends com.kaung.student> c1 = student.getClass();
Method show = c1.getMethod("show", null);
show.setAccessible(true);
long starttime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
show.invoke(student,null);
}
long endtime = System.currentTimeMillis();
System.out.println("执行时间:"+(endtime-starttime));
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
test01();
test02();
test03();
}
}
关于泛型
public class Test11 {
public void test01(Map<String,student> map, List<student>list){
System.out.println("test01");
}
public Map<String,student> 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) {
if(genericParameterType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
}
}
获得注解的信息
public class Test12 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class<?> c1 = Class.forName("com.kaung.Student2");
//通过反射获得注解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//获得注解的value值
Tablekuang annotation = c1.getAnnotation(Tablekuang.class);
String value = annotation.value();
System.out.println(value);
//获得类的指定注解
Field f = c1.getDeclaredField("name");
Fieldkuang annotation1 = f.getAnnotation(Fieldkuang.class);
System.out.println(annotation1.columnName());
System.out.println(annotation1.type());
System.out.println(annotation1.length());
}
}
@Tablekuang("db _student")
class Student2{
@Fieldkuang(columnName = "db_id",type = "int",length = 10)
private int age;
@Fieldkuang(columnName = "db_name",type = "varchar",length = 3)
private String name;
public Student2(){}
public Student2(int age, String name) {
this.age = age;
this.name = name;
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Tablekuang{
String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Fieldkuang{
String columnName();
String type();
int length();