JavaSE学习——类加载器和注解
类加载器
BootClassLoader:加载JDK核心模块
PlatformClassLoader:加载平台相关的模块(扩展类),BootClassLoader是父类
AppClassLoader:加载Main,PlatformClassLoader是父类
一般来说,比如一个Main文件要被AppClassLoader加载,会先向上传递给PlatformClassLoader,再向上传递给BootClassLoader加载,上层加载不了再下一层传递。这样做是为了安全考虑(防止自己写一个java.lang.String替换掉 JDK 核心 String)
注解
@Override是注解,有一定的含义。注解也是一个类,可以被标注在任何地方,部分可以保留到运行时。注解 = 给类、方法、变量贴个 “标签”。它本身不干活,只是标记;需要反射去读取这个标签,然后做相应逻辑。
预设注解
JDK预设了下面的注解,作用于代码:
@Override用于检查这个方法是否重写了父类,如果变成publicvoidequals() { }就会编译器报错
public class Main { public static void main(String[] args){ }@Overridepublic boolean equals(Object obj) { return super.equals(obj); } }@Deprecated用来标注某个方法是“过时的”,不推荐用。这个只是一个警告的作用,且会保留到运行时(.class文件里也会有)
@SuppressWarnings用于忽略一些不需要的警告,比如在底层实现一个arr数组的时候使用的是Object对象创建,在实际使用的时候是泛型T,这个时候比如get方法返回的是T泛型的内容需要强制类型转换,这个时候编译器会警告,只要使用@SuppressWarnings就不会警告了
public class Main<T> { Object[] arr; public static void main(String[] args){ } @SuppressWarnings("unchecked") public T get(int index){ return(T)arr[index]; } }@FunctionalInterface接口用于表明是一个函数式接口(匿名函数),比如下面的代码使用了它的一个匿名函数,查看Supplier会发现有注解:
Supplier<String> supplier = ()->"";
元注解
“贴在注解上的注解”,专门用来控制注解的行为。主要有4 个标准元注解:
@Target规定注解能贴在什么地方(FIELD表示可以在变量上打标签)
@Retention规定注解保留到哪个阶段,Retention里面的参数有RetentionPolicy.SOURCE,CLASS,RUNTIME这三个。
@Documented标记这些注解是否包含在用户文档里
@Inherited让子类继承父类上的注解
@Repeatable代表一个注解可以打多次标签
举两个例子:
以override为例,Retention里面的参数RetentionPolicy.SOURCE代表在源代码里面保存,.class文件里面就没有了
自定义一个注解
先来定义一个简单的注解,限制注解保留到运行时且可以在类和方法上打这个注解的标签
import static java.lang.annotation.ElementType.*; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target({ TYPE, METHOD }) public @interface test { }@test public class Main { @test public static void main(String[] args){ } }使用这个注解
比如在注解里面定义一个方法,需要在方法后面加括号,且传入参数使用,这个参数不能是变量,只能是一个枚举类或者一个直接的值:
public @interface test {String value();//注解的属性要加括号 }@test("lhw")public class Main { privatefinalstatic String str = "hxl"; @test(str) public static void main(String[] args){ } }
此外,如果不传参数使用,那个注解必须有默认值:
public @interface test { String value()default "a"; //注解的属性要加括号 }数组类型的参数和使用(一个元素的时候,可以直接填值):
public @interface test { String[] value()default {"1","2"}; //注解的属性要加括号 }@test({"lhw"}) public class Main { @test("hxl") public static void main(String[] args){ } }
如果注解里面有多个属性的时候,需要在使用的时候加上属性名称和值:
public @interface test { String[]value() default {"1","2"}; //注解的属性要加括号 intage(); }@test(value = {"lhw"},age = 1) public class Main { @test(value ="hxl",age =2) public static void main(String[] args){ } }因为注解也是一个类,所以也可以作为一个参数去使用:
public class Main { @test(value = "hxl",age = 2) public static void main(String[] args){ }private static void test(test t){ int a = t.age(); }}但是注解类的实例化对象只能通过反射去拿到,不允许直接实例化。
反射获取注解
使用getAnnotation方法去获取一个注解的实例对象,需要使用到注解对应类的反射对象。
public @interface test { String value(); //注解的属性要加括号 }@test(value = "hxl")public class Main { public static void main(String[] args){ Class<Main> mainClass =Main.class; test annotation = mainClass.getAnnotation(test.class); System.out.println(annotation.value()); } }
上面是关于类上的注解,还有关于方法的注解的获取:
public class Main { @test(value = "hxl") public static void main(String[] args) throws NoSuchMethodException { Class<Main> mainClass =Main.class;Method main = mainClass.getMethod("main", String[].class); test annotation = main.getAnnotation(test.class); System.out.println(annotation.value()); } }关于参数的注解:
public class Main { public static void main(@test(value = "hxl") String[] args) throws NoSuchMethodException { Class<Main> mainClass = Main.class; Method main = mainClass.getMethod("main", String[].class); Annotation[][] parameterAnnotations = main.getParameterAnnotations(); System.out.println(Arrays.deepToString(parameterAnnotations)); } }