内置注解
public class Anno1 { |
元注解
作用于其他注解的注解
在 java.lang.annotation 包中可以找到.( @ Target , @Retention,@Documented , @Inherited )
- @Target :用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
- @Retention :表示需要在什么级别保存该注释信息,用于描述注解的生命周期(SOURCE < CLASS < RUNTIME)
- @Document:说明该注解将被包含在 javadoc 中
- @Inherited: 说明子类可以继承父类中的该注解
import java.lang.annotation.*; |
自定义注解
- 使用@interface 自定义注解时,自动继承了 java.lang .annotation.Annotation 接口
- @interface 用来声明一个注解,格式: public @ interface 注解名{定义内容}
- 其中的每一个方法实际上是声明了一个配置参数.
- 方法的名称就是参数的名称.
- 返回值类型就是参数的类型(返回值只能是基本类型,Class , String , enum ).
- 可以通过 default 来声明参数的默认值
- 如果只有一个参数成员, 一般参数名为 value
- 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0 作为默认值.
import java.lang.annotation.*; |
现在,注解定义和使用结束了,下面结合反射来读取注解数据.
反射
- 可以在运行时实现动态创建对象和编译,有很大的灵活性,但会影响一定性能
Class 类
Class 类时反射机制的根源,常用方法如下:
获取 Class 类的对象
public class Demo1 {
public static void main(String[] args) throws ClassNotFoundException {
//方式一:通过对象获取
Class c1 = new Student().getClass();
System.out.println(c1);
//方式二:通过forname获取
Class c2 = Class.forName("twenty_one.reflection.Person");
System.out.println(c2);
//通过类名.class获得(最可靠,高效)
Class c3 = Student.class;
System.out.println(c3);
//方式四:基本内置类型的包装类都有一个TYPE属性
Class c4 = Integer.TYPE;
System.out.println(c4);
}
}
class Person {
public String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
public String toString() {
return "Person{" + "name='" + name + '\'' + '}';
}
}
class Student extends Person {
public Student() {
this.name = "Student";
}
}
class Teacher extends Person {
public Teacher() {
this.name = "Teacher";
}
}Class 对象类型
import java.lang.annotation.ElementType;
public class Demo2 {
public static void main(String[] args) {
Class c1 = Object.class; //类
Class c2 = Runnable.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);//class java.lang.Object
System.out.println(c2);//interface java.lang.Comparable
System.out.println(c3);//class [Ljava.lang.String;
System.out.println(c4);//class [[I
System.out.println(c5);//interface java.lang.Override
System.out.println(c6);//class java.lang.annotation.ElementType
System.out.println(c7);//class java.lang.Integer
System.out.println(c8);//void
System.out.println(c9);//class java.lang.Class
}
}
类的加载过程
加载: 首先类加载器读取.class 字节码文件到方法区内存,转换数据结构为运行时,并生成 java.lang.Class 对象.
- 在反射中获取 Class 对象并不是创建,而是从内存中获取.
链接:将 Java 类的二进制代码合并到 JVM 的运行状态之中的过程。
- 验证:确保加载的类信息符合 JVM 规范,没有安全方面的问题
- 准备:正式为类变量(static) 分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。
- 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
初始化:
- 执行类构造器< clinit> ()方法的过程。
类构造器< clinit> ()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。
(类构造 器是构造类信息的,不是构造该类对象的构造器)。 - 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
- 虚拟机会保证一 个类的 ()方法在多线程环境中被正确加锁和同步。
- 执行类构造器< clinit> ()方法的过程。
例子
public class Demo3 {
public static void main(String[] args) {
A a = new A();
System.out.println(A.m);//300
/*
1.加载到内存,产生一个类对应Class对象
2.链接,链接结束后m=◎
3.初始化
<clinit>(){
m=100;
System.out.println( "A类静态代码块初始化");
m = 300;
}
*/
}
}
class A {
// * 1.1
static int m = 100;
// * 1.2
static {
System.out.println("A类静态代码块初始化");
m = 300;
}
// * 2
public A() {
System.out.println("A类的构造初始化");
}
}
实例化与 static 加载
尝试下实例化与否对 static 资源调用的影响
实例化+调用
public class Demo3 {
public static void main(String[] args) {
A a = new A();
System.out.println(A.m);//300
}
}A类静态代码块初始化
A类的构造初始化
300实例化+不调用
public class Demo3 {
public static void main(String[] args) {
A a = new A();
// System.out.println(A.m);//300
}
}A类静态代码块初始化
A类的构造初始化不实例化+调用
public class Demo3 {
public static void main(String[] args) {
// A a = new A();
System.out.println(A.m);//300
}
}A类静态代码块初始化
300可以发现在实例化对象之后调用 static 资源,并不会再次执行静态代码块
- 如果没实例化的情况下调取 static 资源,类会加载,但不会执行构造器
什么时候会发生类初始化?
public class Demo4 { |
类加载器
作用
分类
操作类加载器
public class Demo5 {
public static void main(String[] args) throws ClassNotFoundException {
//获取系统的类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);//jdk.internal.loader.ClassLoaders$AppClassLoader@4459eb14
//获取系统类加载器的父类加载器-->扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);//jdk.internal.loader.ClassLoaders$PlatformClassLoader@7960847b
//获取扩展类加载器的父类加载器-->根加载器(C/C++)
ClassLoader grantparent = parent.getParent();
System.out.println(grantparent);//null
//测试当前类是哪个加载器加载的
ClassLoader classLoader = Demo5.class.getClassLoader();
System.out.println(classLoader);//jdk.internal.loader.ClassLoaders$AppClassLoader@4459eb14
//测试JDK内置的类是谁加载的
ClassLoader classLoader1 = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader1);//null
//如何获得系统类加载器可以加载的路径
System.out.println(System.getProperty("java.class.path"));//D:\Game\Weidows\Java\target\classes;D:\Game\Scoop\persist\maven\mvn-repository\commons-io\commons-io\2.8.0\commons-io-2.8.0.jar
}
}
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 ⭐️齐下无贰⭐️!
评论