Java注解和反射学习笔记

1.什么是注解

  • Annotation是从JDK5.0开始引入的新技术
  • 作用:可以被其他程序(比如编译器等)读取
  • 格式:@注释名,还可以添加一些参数值

内置注解:

@Override 重写

@Deprecated 不推荐使用,但是可以使用,或者存在更好的方式

@SuppressWarning 镇压警告 @SuppressWarning(“all”)镇压所有警告

元注解:

作用就是负责注解其他注解

@Target:表示注解的使用范围,可以用在哪些地方

1
2
3
4
5
6
7
8
9
10
11
12
//测试元注解
@MyAnnotation
public class Test01 {
@MyAnnotation
public void test(){

}
}

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

@Retention(保留的意思):表示注解的生命周期,在什么地方还有效, runtime>class>source

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

@Documentted:表示是否将我们注解生成在javadoc中

1
2
//Documented表示是否将我们的注解生成在javadoc中
@Documented

@Inherited:表示子类可继续父类的注解

1
2
//Inherited表示子类可继承父类的注解
@Inherited

自定义注解:

使用@interface自定义注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//自定义注解
public class Test02 {
//注解可以显示赋值,如果没有默认值,就必须为其赋值
@MyAnnotation1()
public void test1(){}
@MyAnnotation3("侬好")//当自定义注解里面只有一个参数,并且是value时才可以直接写
public void test2(){}
}

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation1{
//注解的参数:参数类型+参数名();
String name() default "";
int age() default 18;
int id() default -1;//如果默认值为-1代表不存在
String[] shcool() default {"清华大学"};
}
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation3{
String value();
}

2.什么是反射

动态语言:运行时可以改变其结构的语言,比如JavaScript,php,python

静态语言:运行时结构不可改变,比如java,c,c++

概念:

  • Reflection(反射)是java语言被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法

  • 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构,这就是反射。

优点:

实现动态创建对象和编译,灵活性

缺点:

对性能有影响,使用反射基本上是一种解释操作,我们告诉JVM,我们希望做什么并且它满足我们的需求,比直接执行相同的操作慢。

反射相关的主要API:

java.lang.Class:代表一个类

java.lang.reflect.Method:代表类的方法

java.lang.reflect.Field:代表类的成员变量

java.lang.reflect.Constructor:代表类的构造器

Class类:

Class类是Reflection的根源,针对任何你想动态加载、运行的类,要先获得相应的Class对象。

获取Class类的实例:

  1. 通过对象获取(getClass)
  2. 通过类名获取
  3. 通过类名.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
43
44
45
46
47
48
49
package test;
//测试Class类的创建方式有哪些
public class Test04 {
public static void main(String[] args) throws ClassNotFoundException {
Person person=new Student();
System.out.println("这个人是"+person.name);
//方式一,通过对象获得
Class c1=person.getClass();
System.out.println(c1.hashCode());
//方式二,通过forname获得
Class c2=Class.forName("test.Student");
System.out.println(c2.hashCode());
//方式三,通过类名.class
Class c3=Student.class;

//获得父类类型
Class c4= c1.getSuperclass();
System.out.println(c4);
}
}
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="老师";
}
}

哪些类型可以有Class对象?

class:外部类,成员,局部内部类,匿名内部类

interface:接口

[]:数组

enum:枚举

annotation:注解

基本数据类型

void

java内存分析

java内存:

  • 堆内存:
    • 存放new的对象和数组
    • 可以被所有的线程共享,不会存放别的对象引用
  • 栈内存
    • 存放基本变量类型(会包括这个基本类型的具体数值)
    • 引用对象的变量(会存放这个引用在堆里面的具体地址)
  • 方法区
    • 可以被所有的线程共享
    • 包含了所有的class和static变量

类的加载与ClassLoader的理解

加载:

链接:

初始化:

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

类的主动引用:一定会发生类的初始化

  • 虚拟机启动,先初始化main方法所在的类
  • new一个类的对象
  • 调用类的静态成员和静态方法
  • 使用java.lang.reflect包的方法对类进行反射调用
  • 当初始化一个类时,其父类没有被初始化,则会先初始化它的父类

类的被动引用:不会发生类的初始化

  • 当访问静态域时,只有真正声明这个域的类才会被初始化,例如:当子类引用父类的静态变量,不会导致子类初始化
  • 通过数组定义类引用,不会触发此类的初始化
  • 引用常量不会触发此类的初始化(常量和静态变量在连接阶段就存入调用类的常量池中了)

类加载器

概念:类加载器作用是用来把类(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
package test;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Test06 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class c1 = Class.forName("test.User");

//获取类名
System.out.println(c1.getName());//类名+包名
System.out.println(c1.getSimpleName());
//获得类的属性
Field[] fields = c1.getFields();//只能找到public属性

fields = c1.getDeclaredFields();//获得全部属性
for (Field field : fields) {
System.out.println(field);
}
//获得类的指定属性
Field age = c1.getDeclaredField("age");
System.out.println(age);

//获得类的方法
c1.getMethods();//获取本类及其父类的所有的方法
c1.getDeclaredMethods();//获得本类所有的方法

//获得指定的方法
//因为java重载,所以还要指定类型,不然识别不了是哪个方法
Method getName = c1.getMethod("getName", null);
Method setName = c1.getMethod("setName", String.class);

//获得构造器
c1.getConstructors();//获得public的构造器
c1.getDeclaredConstructors();

//获得指定的构造器
c1.getConstructor(String.class,int.class,int.class);
}
}

有了Class对象,能做什么?

  • 通过反射创建对象
    • newInstance //本质是调用了类的无参构造器
    • 通过构造器创建对象 //调用了有参构造方法
  • 通过反射调用普通方法 //getDeclaredMethod //invoke(对象,”方法的值”)
  • 通过反射操作属性 //getDeclaredField
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
package test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

//通过反射动态创建对象
public class Test07 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
//获得Class对象
Class c1 = Class.forName("test.User");

//构造一个对象
//本质是调用了类的无参构造器
User user = (User) c1.newInstance();

//通过构造器创建对象
// 调用了有参构造方法
Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
User user1 = (User) constructor.newInstance("大白", 18, 001);
System.out.println(user1);

//通过反射调用普通方法
//invoke(对象,"方法的值")
User user2 = (User) c1.newInstance();
Method setName= c1.getDeclaredMethod("setName", String.class);
setName.invoke(user2,"反射");
System.out.println(user2.getName());

//通过反射操作属性
User user3 = (User) c1.newInstance();
Field name = c1.getDeclaredField("name");
//不能直接操作私有属性,我们需要关闭程序的安全检测
name.setAccessible(true);
name.set(user3,"反射操作属性");
System.out.println(user3.getName());
}
}

性能检测:

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package test;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

//分析性能
public class Test08 {
public static void test01(){
//普通方式调用
User user=new User();
long start=System.currentTimeMillis();

for (int i = 0; i < 1000; i++) {
user.getName();
}

long end=System.currentTimeMillis();
System.out.println("普通方式执行时间:"+(end-start));
}

//反射方式调用
public static void test02() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//普通方式调用
Class c1= Class.forName("test.User");
User user= (User) c1.newInstance();
long start=System.currentTimeMillis();
Method getName= c1.getDeclaredMethod("getName");
for (int i = 0; i < 1000; i++) {
getName.invoke(user,null);
}

long end=System.currentTimeMillis();
System.out.println("反射执行时间:"+(end-start));
}

//反射调用,关闭检测
public static void test03() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//普通方式调用
Class c1= Class.forName("test.User");
User user= (User) c1.newInstance();
long start=System.currentTimeMillis();
Method getName= c1.getDeclaredMethod("getName");
getName.setAccessible(true);
for (int i = 0; i < 100000000; i++) {
getName.invoke(user,null);
}

long end=System.currentTimeMillis();
System.out.println("反射执行(关闭检测)时间:"+(end-start));
}

public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {
test01();
test02();
test03();
}

}

反射读取注解

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
package test;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//反射操作注解
public class Test10 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c1=Class.forName("test.Student1");
//通过反射获取注解
c1.getAnnotations();

//获取注解的value值
Table table= (Table) c1.getAnnotation(Table.class);
String value= table.value();
System.out.println(value);

//获得类指定的注解
java.lang.reflect.Field f= c1.getDeclaredField("name");
Field annotation = f.getAnnotation(Field.class);
annotation.columnName();
annotation.type();
annotation.length();

}
}


@Table("db_stydent")
class Student1{
@Field(columnName = "db_id",type = "int",length = 10)
private int id;
@Field(columnName = "db_age",type = "int",length = 10)
private int age;
@Field(columnName = "db_name",type = "varchar",length = 10)
private String name;

public Student1() {
}

public Student1(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table{
String value();
}

//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Field{
String columnName();
String type();
int length();
}