1.元注解:负责解释其他注解
@Target :用于描述注解的使用范围,即被描述的注解的使用位置
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
//测试元注解
@MyAnnotation
public class Test02 {
@MyAnnotation
public void test(){
}
}
//定义一个注解 @interface是用来定义注解的
// 此时定义的Target在方法和类上有效,如上所示,未报错
@Target({ElementType.METHOD,ElementType.TYPE})
@interface MyAnnotation{
}
@Retention:在什么级别保存该注释信息,用于描述注解的生命周期。source<class<runtime
@Retention(value = RetentionPolicy.RUNTIME) //表示我们的注解在什么地方还有效
@interface MyAnnotation{
}
@Document:说明该注解被包含在javadoc中
@Inherited:说明子类可以继承父类中的该注解
2.自定义注解
-
@interface用来定义注解,自动继承 java.lang.annotation.*;接口;
注解的参数的格式:参数类型+参数名+();
- 其中的每一个方法实际上声明了一个配置参数;
-
方法的名称就是参数的名称;
-
返回值类型就是参数的类型;且返回值类型只能是基本类型class,string,enum;
-
可以通过default来申明参数的默认值;
-
如果只有一个参数,一般参数名为value;
-
注解元素必须要有值,定义注解元素时,经常使用空字符串或者0作为默认。
3.java反射机制reflection
3.1什么时反射
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7k6i38zZ-1629016587633)(C:\Users\56504\AppData\Roaming\Typora\typora-user-images\image-20210811164743148.png)]
package com.yang;
//什么叫反射
public class Test04 {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> user = Class.forName("com.yang.User"); //不确定实体类类型,所以用问好代替
Class<?> user1 = Class.forName("com.yang.User");
/*打印出来的信息都一样,证明反射出来的时同一个类
一个类在内存中只有一个class对象
一个类被加载后,类的整个结构都会被封装在class对象中
*/
System.out.println(user);
System.out.println(user1);
System.out.println(user.hashCode());
System.out.println(user1.hashCode());
}
//通过反射获取类的class对象
}
class User{
}
3.2class类
-
Class对象只能由系统建立对象
-
Class本身也是一个类,是reflection的根源,任何想动态加载运行的类都要先获得对应的class对象
-
一个加载的类在JVM中只能有一个Class实例
-
一个class对象对应的是一个加载到JVM中的一个.class文件
package com.yang; public class Test05 { public static void main(String[] args) throws ClassNotFoundException { //正常方式获得 Person person = new Student(); System.out.println("这个人是:"+person.name); //hashCode比较是不是同一个类 //方式一:通过对象获得 Class c1 = person.getClass(); System.out.println(c1.hashCode()); //方式二:forname获得 Class c2 = Class.forName("com.yang.Student"); System.out.println(c2.hashCode()); //方式三:类名.class获得 Class c3 = Student.class; System.out.println(c3.hashCode()); //方式四:只适用于基本类型。基本内置类型的包装类都有个Type属性 Class type = Integer.TYPE; System.out.println(type.hashCode()); //获得父类 Class superclass = c2.getSuperclass(); System.out.println(superclass); } } 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="老师"; } }
方式一:通过对象获得
方式二:forname获得
方式三:类名.class获得
3.3哪些类有可以有class对象
package com.yang;
//所有类型的class
public class Test06 {
public static void main(String[] args) {
Class c1 = Object.class; //Object类
Class c2 = Comparable.class; //接口
Class c3 = String[].class; //一维数组
Class c4 = int[][].class; //二维数组
Class c10 = double[][].class; //二维数组
Class c5 = Override.class; //注解
Class c6 = Integer.class; //基本类型
Class c7 = void.class; //无返回值void
Class c8 = Enum.class; //枚举类型
Class c9 = Class.class; //Class类
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);//打印结果:几个【就代表几维数组,后面的首字母代表类型[[I
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
System.out.println(c9);
System.out.println(c10);
//只要元素类型和维度一样,就是同一个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.Integer
void
class java.lang.Enum
class java.lang.Class
class [[Ljava.lang.Comparable;
4.类的加载与class loader
1.加载
将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行的数据结构,然后生成一个代表这个类的java.lang.Class对象。
2.链接
将Java类的二进制代码合并到jvm的运行状态之中
1.验证:确保加载的类信息符合JVM规范
2.准备:正式为类变量,static分配内存并设置类变量初始值,都在方法区中进行
3.解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程
3.初始化
-
执行类构造器《clinit()》方法的过程。
-
初始化一个类时,如果父类没有初始化,先初始化父类。
-
虚拟机会保证一个类的clinit()方法在多线程的环境中被正确的加载和同步。
package com.yang; public class Test07 { public static void main(String[] args) { A a = new A(); System.out.println(A.m); } } class A{ static { System.out.println("A类静态代码块初始化"); m = 100; } static int m = 300; static { System.out.println("A类静态代码块初始化2"); m = 400; } public A(){ System.out.println("A类的无参构造初始化"); } } 结果: A类静态代码块初始化 A类静态代码块初始化2 A类的无参构造初始化 400
5.什么时候发生类的初始化
##### 5.1类的主动引用
一定会发生类的初始化
- 当虚拟机启动时,先初始化main方法所在的类
- new一个对象时
- 调用类的静态成员和静态方法(final常量除外)
- 使用java.lang.reflect包的方法对类进行反射调用
- 初始化一个类时,如果父类没有初始化,先初始化父类。
5.2类的被动引用
不会发生类的初始化
1. 访问一个静态域时,只有真正申明这个域的类才会被初始化。如子类引用弗雷德静态变量时,不会导致子类的初始化
2. 通过数组定义类引用,不会发生类的初始化
3. 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)
package com.yang;public class Test08 { static { System.out.println("main类被加载"); } public static void main(String[] args) throws ClassNotFoundException { //1.new一个对象时主动引用 // Son son = new Son(); //2.反射主动引用 Class.forName("com.yang.Son"); //不会产生类的引用的方法 System.out.println(Son.a);//静态代码 Son [] srray = new Son[5];//数组 System.out.println(Son.F);//常量 }}class Father{ static int a = 2; static { System.out.println("父类被加载"); }}class Son extends Father{ static { System.out.println("子类被加载"); m = 300; } static int m = 100; static final int F=4;}
6.类加载器
- 系统加载器==用户加载器
- 父类加载器==扩展类加载器
- 根加载器,C/C++写的,获取不到
package com.yang;public class Test09 { 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.yang.Test06").getClassLoader(); System.out.println(classLoader); //用户加载器加载的 //测试JDK内置类是哪个加载器加载的 classLoader = Class.forName("java.lang.Object").getClassLoader(); System.out.println(classLoader);//根加载器加载的 //获取系统类加载器的加载路径,即初始化时系统启动的jar包 System.out.println(System.getProperty("java.class.path")); //双亲委派机制,检测安全性,自己命名的包类名跟系统现有的一样就不会生效。 }}jdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dcjdk.internal.loader.ClassLoaders$PlatformClassLoader@58ceff1nulljdk.internal.loader.ClassLoaders$AppClassLoader@2437c6dcnullC:\Users\56504\IdeaProjects\annotation\target\classes