undefined

一、注解

注解是以 @ 开头

内置注解

1
2
3
@override:重写方法
@Deprecated:注释为不推荐使用,但可以使用,或者存在更好的方式
@SuppressWarnings("all"):镇压警告(使警告不报出来)

元注解

作用是负责注解其他注解

1
2
3
4
@Targe: 描述注解的使用范围
@Retention:表示需要在什么级别保存该注解信息,用于描述注解的生命周期
@Documented:说明该注解将被包含在javadoc中
@Inherited:说明子类可以继承父类中的该注解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import java.lang.annotation.*;

// 测试元注解
@MyAnnotation
public class Test01 extends Object {
@MyAnnotation
public void test(){

}
}

// 定义一个注解
// Target 表示我们的注解可以用在什么地方
@Target(value = {ElementType.METHOD, ElementType.TYPE})

// Retention 表示我们的注解在什么地方有效
// runtime->class->source
@Retention(value = RetentionPolicy.RUNTIME)

// Documented 表示是否将我们的注解生成在JavaDoc中
// Documented

// Inherited 子类可以继承父类的注解
@Inherited
@interface MyAnnotation{

}

自定义注册

使用 @interface 自定义注解是,自动继承了 java.lang.annotation.Annotation 接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

public class Test02 {
// 注解可以显示赋值,如果没有默认值,我们就必须给注解赋值
@MyAnnotation2(age = 18)
public void test() {}
}

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{
// 注解的参数:参数类型+参数名
String name() default "";
int age();
int id() default -1; // 如果默认值为-1,代表不存在
String[] schools() default {"hello", "world"};
}

二、反射

反射使 java 成为准动态语言。反射性能很差

1
2
3
4
5
6
7
8
9
10
11
12
13
import java.lang.reflect.Method;

public class Test01 {
public static void main(String[] args) throws Exception {
Class c1 = Class.forName("com.tencent.learn.Reflection.User");
System.out.println(c1);

Method[] fields = c1.getMethods();
for (Method field : fields) {
System.out.println(field.getName());
}
}
}

得到 class 类的几种方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class Test02 {
public static void main(String[] args) throws Exception {
Person person = new Student();
System.out.println("这个人是: " + person.name);

// 1. 通过对象获得
Class c1 = person.getClass();
System.out.println(c1.hashCode());

// 2. forname 获得
Class c2 = Class.forName("com.tencent.learn.Reflection.Student");
System.out.println(c2.hashCode());

// 3. 通过类名.class 获取
Class c3 = Student.class;
System.out.println(c3.hashCode());

// 4. 基本内置类型的包装类都有一个 Type 属性
Class c4 = Integer.TYPE;
System.out.println(c4);

// 获得父类类型
Class c5 = c1.getSuperclass();
System.out.println(c5);
}
}

class Person {
public String name;
}

class Student extends Person {
public Student() {
this.name = "学生";
}
}

class Teacher extends Person {
public Teacher() {
this.name = "老师";
}
}

所有类型的class对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.lang.annotation.ElementType;

public class Test03 {
public static void main(String[] args) {
Class c1 = Object.class; // 类
Class c2 = Comparable.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
}
}

类的加载与ClassLoader的理解

  • 加载:将class 文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的 java.lang.Class 对象
  • 链接:将 Java 类的二进制代码合并到 JVM 的运行状态之中的过程
    1. 验证:确保加载类信息符合 JVM 规范,没有安全方面的问题
    2. 准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配
    3. 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程
  • 初始化
    1. 执行类构造器 <clinit>() 方法的过程。类构造器 <clinit>() 方式是由编译器自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)
    2. 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化
    3. 虚拟机会保证一个类的 <clinit>() 方法在多线程环境中被正确加锁和同步

什么时候会发生类初始化?

  • 类的主动引用(一定会发生类的初始化)
    • 当虚拟机启动,先初始化 main 方法所在的类
    • new 一个类的对象
    • 调用类的静态成员(除了 final 常量)和静态方法
    • 使用 java.lang.reflect 包的方法对类进行反射调用
    • 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类
  • 类的被动引用(不会发生类的初始化)
    • 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化
    • 通过数组定义类引用,不会出发此类的初始化
    • 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)