# 反射

# 简介

Java 反射说的是在运行状态中,对于任何一个类,我们都能够知道这个类有哪些方法和属性。对于任何一个对象,我们都能够对它的方法和属性进行调用。我们把这种动态获取对象信息和调用对象方法的功能称之为反射机制。

反射机制大大的提高了代码的灵活度,但是会有更高的系统开销和较慢的运行效率.

因此反射机制不能被过度使用.

# 为什么需要反射

好好的我们new User(); 不是很好,为什么要去通过反射创建对象呢? 那我要问你个问题了,你为什么要去餐馆吃饭呢? 例如:我们要吃个牛排大餐,如果我们自己创建,就什么都得管理。 好处是,每一步做什么我都很清晰,坏处是什么都得自己实现,那不是累死了。牛接生你管,吃什么你管,屠宰你管,运输你管,冷藏你管,烹饪你管,上桌你管。就拿做菜来说,你能有特级厨师做的好? 那怎么办呢?有句话说的好,专业的事情交给专业的人做,饲养交给农场主,屠宰交给刽子手,烹饪交给特级厨师。那我们干嘛呢? 我们翘起二郎腿直接拿过来吃就好了。 再者,饭店把东西做好,不能扔到地上,我们去捡着吃吧,那不是都成原始人了。那怎么办呢?很简单,把做好的东西放在一个容器中吧,如把牛排放在盘子里。 在开发的世界里,spring就是专业的组织,它来帮我们创建对象,管理对象。我们不在new对象,而直接从spring提供的容器中beans获取即可。Beans底层其实就是一个Map<String,Object>,最终通过getBean(“user”)来获取。而这其中最核心的实现就是利用反射技术。 总结一句,类不是你创建的,是你同事或者直接是第三方公司,此时你要或得这个类的底层功能调用,就需要反射技术实现。有点抽象,别着急,我们做个案例,你就立马清晰。

# 什么是反射

反射是 Java 编程语言的一个特性,它提供了在运行时检查和动态调用类、方法、属性的能力。

Reflection(反射) 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,也有称作“自省”。反射非常强大,它甚至能直接操作程序的私有属性。我们前面学习都有一个概念,private的只能类内部访问,外部是不行的,但这个规定被反射赤裸裸的打破了。 反射就像一面镜子,它可以在运行时获取一个类的所有信息,可以获取到任何定义的信息(包括成员变量,成员方法,构造器等),并且可以操纵类的字段、方法、构造器等部分。

# 反射能做什么

反射可以在运行时检查类,接口,方法和变量。还可以实例化对象、调用方法、获取和设置变量值。比如当我们不知道一个类中是否拥有某个方法时,我们就可以使用反射来检查是否拥有这个方法。

# 类对象-Class的实例

JVM在加载一个类的class文件时,就会同时创建一个Class的实例,使用该实例记录加载的 类的一切信息(类名,有哪些属性,哪些方法,哪些构造器等)。并且每个被JVM加载的类都有 且只有一个Class的实例与之对应。

反射的第一步就是获取要操作的类的类对象,以便程序在运行期间得知要操作的类的一切信息然后对其进行响应的操作。

# 反射常用类

# 常用类

  • Class:Class 类表示正在运行的 Java 程序中的类和接口。
  • Field:提供有关类或接口的单个域的信息和动态访问。如数据类型,访问修饰符,域的名称和值。
  • Method:提供有关类或接口上的单个方法的信息和访问权限。如访问修饰符,返回类型,名称,参数类型和方法的异常类型。
  • Constructor:提供有关类的单个构造函数的信息和访问权限。例如构造函数的访问修饰符,名称和参数类型。
  • Modifier:提供了有关访问修饰符的信息。

# Class

Class 类没有公共构造方法,可以通过以下方法获取 Class 实例。

  • Object 提供的 getClass() 方法。

  • 类名.Class

    Class cls = String.class;
    Class cls = int.class;
    //注意:基本类型获取类对象只有这一种方式
    
    1
    2
    3
  • Class.forName(String className) 方法,className 为类的全限定名。

    Class cls = Class.forName("java.lang.String");
    //这里传入的类名必须是类的完全限定名,即:包名.类名
    
    
    1
    2
    3
  • 类加载器ClassLoader形式

# Class 类常用方法:

方法 描述
Field getField(String name) 获取指定的域对象
Field[] getFields() 返回所有的公有域对象数组
Method getDeclaredMethod(String name, Class<?>... parameterTypes) 返回指定的方法对象,(含私有方法,不含从超类继承的方法)
Method[] getMethods() 返回所有的公有方法对象数组,(包含从超类继承的方法)
Method[] getDeclaredMethods() 返回所有方法对象数组
String getName() 获取全限定名
Package getPackage() 获取当前类对象所表示的类的包,返回的Package实例表示该包信息
String getSimpleName() 获取类名(不包含包名)

更多方法请查阅官方文档 (opens new window)

获得包名、类名

class.getPackage().getName()//包名
class.getSimpleName()//类名
clazz.getName()//完整类名
!!成员变量定义信息
getFields()//获得所有公开的成员变量,包括继承的变量
getDeclaredFields()//获得本类定义的成员变量,包括私有,不包括继承的变量
getField(变量名)
getDeclaredField(变量名)

1
2
3
4
5
6
7
8
9

!!构造方法定义信息

getConstructor(参数类型列表)//获得公开的构造方法
getConstructors()//获得所有公开的构造方法
getDeclaredConstructors()//获得所有构造方法,包括私有
getDeclaredConstructor(int.class, String.class)

1
2
3
4
5

方法定义信息

getMethods()//获得所有可见的方法,包括继承的方法
getMethod(方法名,参数类型列表)
getDeclaredMethods()//获得本类定义的方法,包括私有,不包括继承的方法
getDeclaredMethod(方法名, int.class, String.class)

1
2
3
4
5

反射新建实例

c.newInstance();//执行无参构造
c.newInstance(6, "abc");//执行有参构造
c.getConstructor(int.class, String.class); //执行含参构造,获取构造方法

1
2
3
4

反射调用成员变量

c.getDeclaredField(变量名); //获取变量
c.setAccessible(true); //使私有成员允许访问
f.set(实例,); //为指定实例的变量赋值,静态变量,第一参数给 null
f.get(实例); //访问指定实例的变量的值,静态变量,第一参数给 null

1
2
3
4
5

反射调用成员方法

Method m = c.getDeclaredMethod(方法名, 参数类型列表);
m.setAccessible(true) ;//使私有方法允许被调用
m.invoke(实例, 参数数据) ;//让指定的实例来执行该方法
1
2
3
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class ReflectDemo {
    public int a;
    private int b;

    public static void main(String[] args) {
        Class<ReflectDemo> reflectDemoClass = ReflectDemo.class;
        //输出所有的域名称
        for (Field declaredField : reflectDemoClass.getDeclaredFields()) {
            //可以通过Modifier将具体的权限信息输出,否则只会显示代表权限的数值
            System.out.println("域:" + declaredField.getName() + " 修饰符:" + Modifier.toString(declaredField.getModifiers()));
        }
        //输出所有的公有域名称
        for (Field field : reflectDemoClass.getFields()) {
            System.out.println("公有域:" + field.getName() + " 修饰符:" + Modifier.toString(field.getModifiers()));
        }
        //输出类的所有方法名
        for (Method declaredMethod : reflectDemoClass.getDeclaredMethods()) {
            System.out.println("方法:" + declaredMethod.getName() + " 修饰符:" + Modifier.toString(declaredMethod.getModifiers()));
        }
    }

    public void fun1() {
    }

    private void fun2() {
    }

}
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

反射虽然提供了强大的能力,但是确会带来很多问题,如性能降低,权限漏洞以及权限问题。所以能不用反射尽量不用。

# 反射的应用

# 创建类

class Student{
	String name="jack";
	int age=20;
	
	public Student() {
		System.out.println("无参构造");
	}
	public Student(String name) {
		this.name=name;
		System.out.println("含参构造"+name);
	}
	
	public void show(int a) {
		System.out.println("show()..."+a);
	}
	
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 获取类对象

private static void method() throws Exception {
	Class clazz = Student.class;
	Class<?> clazz2 = Class.forName("seday15.Student");
	Class clazz3 = new Student().getClass();
	
	System.out.println(clazz.getName());
	System.out.println(clazz2.getName());
	System.out.println(clazz3.getName());
}

1
2
3
4
5
6
7
8
9
10

# 获取构造方法

private static void method3(Class clazz) {
	Constructor[] cs = clazz.getDeclaredConstructors();
	for (Constructor c : cs) {
		String name = clazz.getSimpleName();
		System.out.println(name);
		
		Class[] cs2 = c.getParameterTypes();//参数
		System.out.println(Arrays.toString(cs2));
		
	}
		
}
1
2
3
4
5
6
7
8
9
10
11
12

# 获取成员方法

private static void method4(Class clazz) {
	Method[] ms = clazz.getMethods();
	for (Method m : ms) {
		String name = m.getName();
		System.out.println(name);
		
		Class<?>[] cs = m.getParameterTypes();
		System.out.println(Arrays.toString(cs));
	}
}
1
2
3
4
5
6
7
8
9
10

# 获取成员变量

private static void method2(Class clazz) {
	Field[] fs = clazz.getFields();//获取public的属性
	for (Field f : fs) {
		String name = f.getName();
		String tname = f.getType().getSimpleName();
		System.out.println(name);
		System.out.println(tname);
	}
}
1
2
3
4
5
6
7
8
9

# 创建对象

package seday15;

import java.lang.reflect.Constructor;
import java.util.Scanner;

//反射新建两个对象
public class Test3 {
	public static void main(String[] args) throws Exception {
		String s =  new Scanner(System.in).nextLine();
		Class<?> clazz = Class.forName(s);
		
		Object o1 = clazz.newInstance();//用无参构造
		System.out.println(o1);

		Constructor<?> c = clazz.getConstructor(String.class);//用含参构造
		Object o2 = c.newInstance("jack");
		System.out.println(o2);
		
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 暴力反射

指可以将程序中的私有的属性或者方法通过反射技术,暴力的获取到资源。需要使用的常见方法如下: 在这里插入图片描述

# 创建Person类

class Person{
	
	private String name="jack";
	private int age = 30;
	
	private void show(int[] a) {
		System.out.println("show()..."+Arrays.toString(a));
	}
	private void test() {
		System.out.println("test()...");
	}
}
1
2
3
4
5
6
7
8
9
10
11
12

# 测试

1、 获取私有属性值并修改 2、 获取私有方法并执行

package seday16new;

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

public class Test3_ReflectPerson {
	public static void main(String[] args) throws Exception {
		Class<?> clazz = Class.forName("seday16new.Person");
//		method(clazz);//隐私属性
		method2(clazz);//执行方法
	}

	private static void method2(Class<?> clazz) throws Exception {
		Method m = clazz.getDeclaredMethod("show", int[].class);
		Object obj = clazz.newInstance();
		m.setAccessible(true);//方法隐私可见
		m.invoke(obj, new int[]{1,2,3});//执行
	}

	private static void method(Class clazz) throws Exception {
		Field f = clazz.getDeclaredField("name");
		System.out.println(f.getType().getName());
		f.setAccessible(true);//属性隐私可见
		Object obj = clazz.newInstance();
//		f.set(obj, "rose");//设置值
		System.out.println(f.get(obj));//获取值
	
		
		//---所有属性
		Field[] fs = clazz.getDeclaredFields();
		for (Field ff : fs) {
			System.out.println(ff);
			ff.setAccessible(true);//暴力反射
			System.out.println(ff.get(obj));
		}
		
	}
	
}
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

# 使用当前类测试反射机制

//Person.java

package reflect;

import reflect.annotations.AutoRunClass;
import reflect.annotations.AutoRunMethod;

/**
 * 使用当前类测试反射机制
 */
@AutoRunClass
public class Person {

    private String name = "张三";
    private int age = 22;

    public Person(){}//需要无参且公开的构造器

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @AutoRunMethod
    public void sayHello(){
        System.out.println(name+":"+"hello!");
    }
    @AutoRunMethod
    public void sayHi(){
        System.out.println(name+":"+"hi!");
    }

    @AutoRunMethod(5)
    public void sayGoodBye(){
        System.out.println(name+":"+"bye!");
    }

    public void dosome(String thing){
        System.out.println(name+":正在"+thing);
    }
    public void dosome(String thing,int sum){
        for(int i=0;i<sum;i++) {
            System.out.println(name + ":正在" + thing);
        }
    }
    private void secret(){//secret:秘密
        System.out.println(name+":这是我的私有方法!");
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

//Student.java
package reflect;

import reflect.annotations.AutoRunClass;
import reflect.annotations.AutoRunMethod;

@AutoRunClass
public class Student {
    public void study(){
        System.out.println("学生:good good study,day day up!");
    }

    @AutoRunMethod(7)
    public void playGame(){
        System.out.println("学生:打游戏!");
    }
}


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

# 利用反射机制实例化对象

package reflect;

import java.util.Scanner;

/**
 * 使用反射机制实例化对象
 */
public class ReflectDemo2 {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        Person p = new Person();
        System.out.println(p);

        //1.先获取要实例化对象的类所对应的类对象
//        Class cls = Class.forName("reflect.Person");
        /*
            java.util.ArrayList
            java.util.HashMap
            java.util.Date(日期)
         */
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入类名:");
        String className = scanner.nextLine();
        Class cls = Class.forName(className);
        //2.类对象提供了方法newInstance()可以调用无参且公开的构造器实例化
        Object o = cls.newInstance();
        System.out.println(o);
    }
}
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

# 使用指定的构造器实例化对象

package reflect;

import java.lang.reflect.Constructor;

/**
 * 使用指定的构造器实例化对象
 */
public class ReflectDemo3 {
    public static void main(String[] args) throws Exception {
        Person p = new Person("李四",55);
        System.out.println(p);
        //1加载类对象
        Class cls = Class.forName("reflect.Person");
        //2获取对应的构造器  Person(String name,int age)
//        cls.getConstructor();//不传参获取的为无参构造器
        Constructor c = cls.getConstructor(String.class,int.class);
        //3通过构造器实例化对象 new Person("王五",22);
        Object o = c.newInstance("王五",22);
        System.out.println(o);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 使用反射机制调用方法

package reflect;

import java.lang.reflect.Method;
import java.util.Scanner;

/**
 * 使用反射机制调用方法
 */
public class ReflectDemo4 {
    public static void main(String[] args) throws Exception {
        //实际开发时不能写throws
        Person p = new Person();
        p.sayHello();

        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入类名:");
        String className = scanner.nextLine();
        System.out.println("请输入方法名:");
        String methodName = scanner.nextLine();
        //1.实例化
//        Class cls = Class.forName("reflect.Person");
        Class cls = Class.forName(className);
        Object o = cls.newInstance();//相当于Person o = new Person();
        //2调用方法
        //2.1获取要调用的方法
        //仅传入方法名时,是获取该无参方法
//        Method method = cls.getMethod("sayHello");//表示的Person的成员方法sayHello()
        Method method = cls.getMethod(methodName);
        //2,2通过获取的方法对象来调用该方法
        method.invoke(o);//相当于o.sayHello(),因为o指向的是一个String对象,因此反射机制可以调用到它的方法

    }
}

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

# 调用有参方法

package reflect;

import java.lang.reflect.Method;

/**
 * 调用有参方法
 */
public class ReflectDemo5 {
    public static void main(String[] args) throws Exception {
        Class cls = Class.forName("reflect.Person");
        Object o = cls.newInstance();

        Method method = cls.getMethod("dosome",String.class);//dosome(String)
        method.invoke(o,"玩游戏");//p.dosome("玩游戏");

        Method method1 = cls.getMethod("dosome",String.class,int.class);
        method1.invoke(o,"看电视",5);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 强行开启访问权限

package reflect;

import java.lang.reflect.Method;

public class ReflectDemo6 {
    public static void main(String[] args) throws Exception {
//        Person p = new Person();
//        p.secret();//私有方法不能在类的外部被访问

        Class cls = Class.forName("reflect.Person");
        Object o = cls.newInstance();
        //getMethod()获取公开方法
//        Method method = cls.getMethod("secret");
        //获取私有方法
        Method method = cls.getDeclaredMethod("secret");
        //强行打开访问权限
        method.setAccessible(true);
        method.invoke(o);//o.secret();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

练习:自动调用Person本类中所有名字含有"s"的无参构造方法

package reflect;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * 自动调用Person本类中所有名字含有"s"的无参的公开方法
 * 提示:
 * Method提供的方法:
 * int getParameterCount()
 * 可以获取其表示的方法的参数个数
 */
public class Test1 {
    public static void main(String[] args) throws Exception {
        Class cls = Class.forName("reflect.Person");
        Object o = cls.newInstance();

        Method[] methods = cls.getDeclaredMethods();
        for(Method method : methods){
            if(method.getName().contains("s")&&
               method.getParameterCount()==0&&
                //判断当前方法是否为public的方法
                method.getModifiers() == Modifier.PUBLIC){
                System.out.println("自动执行方法:"+method.getName());
                method.invoke(o);
            }
        }
    }
}
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

练习:自动调用与当前类Test2在同一个包下所有类中方法名含有s的无参公开方法

package reflect;

import java.io.File;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URISyntaxException;

/**
 * 自动调用与当前类Test2在同一个包下所有类中 方法名含有s的无参公开方法。
 */
public class Test2 {
    public static void main(String[] args) throws Exception {\
        //1.Test2.class.getClassLoader.getResource(".").toURI()---定位Test2的class文件所在的最外层包的上一级------项目根目录
       
  
        File dir = new File(
            Test2.class.getResource(".").toURI() //2.定位Test2的class文件所在的目录(包)----"."----文件所在包
        );
        File[] subs = dir.listFiles(f->f.getName().endsWith(".class"));
        //遍历每一个class文件
        for(File sub : subs){
            String fileName = sub.getName();//获取文件名:例如Test2.class
            String className = fileName.substring(0,fileName.indexOf("."));
            Class cls = Class.forName(Test2.class.getPackage().getName()+"."+className);
            //实例化对象
            Object o = cls.newInstance();
            Method[] methods = cls.getDeclaredMethods();
            for(Method method : methods){
                if(method.getName().contains("s")&&
                   method.getParameterCount()==0&&
                   method.getModifiers()==Modifier.PUBLIC){
                    System.out.println("自动调用"+className+"的方法:"+method.getName());
                    method.invoke(o);
                }
            }

        }


    }
}
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

# 反射机制中使用注解

# 自动实例化与当前ReflectDemo7在同一个包下所有被@AutoRunClass标注的类

//AutoRunClass.java
package reflect.annotations;

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

/**
 * 该注解是用来标注那些可以被反射机制自动调用的类
 *
 * 定义注解时,我们通常会使用java内置的两个注解来加以修饰
 * 当前注解。其中:
 * @Retention 用来指定当前注解的保留级别。有三个可选值,
 * 对应:
 *  RetentionPolicy.SOURCE  表示当前注解仅保留在源码中
 *  RetentionPolicy.CLASS(默认值) 表示注解会保留在字节码中,但是反射机制不可用
 *  RetentionPolicy.RUNTIME 表示保留在字节码文件中,但是可以被反射机制使用
 *  通常我们定义的注解都会指定为RUNTIME级别,辅助反射机制的操作。
 *
 * @Target用于表示当前注解可以在什么位置上使用。可选项都定义在ElementType上
 * 常见的有:
 *       ElementType.TYPE 在类上使用
 *       ElementType.FIELD 在属性使用
 *       ElementType.METHOD 在方法上使用
 *       ...多个的话,用大括号括起来
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface AutoRunClass {

}





//ReflectDemo7.java
package reflect;

import reflect.annotations.AutoRunClass;

/**
 * 反射机制中使用注解
 */
public class ReflectDemo7 {
    public static void main(String[] args) throws Exception {
        //判断一个类是否有被@AutoRunClass标注
//        Class cls = Class.forName("reflect.Person");
//        Class cls = Class.forName("reflect.Student");
        Class cls = Class.forName("reflect.Test2");
        /*
            除了Class之外,像Method,Filed等其他反射对象也支持isAnnotationPresent
            方法,用来表示是否被指定注解标注。
            比如:
            Method的这个方法就是判断其表示的方法是否有被指定注解标注。
            Constructor的这个方法就是判断其表示的构造器是否被指定注解标注。
         */
        if(cls.isAnnotationPresent(AutoRunClass.class)){//isAnnotationPresent:判断当前类对象所表示的类是否被注解@AutoRunClass标注了,返回布尔值
            System.out.println(cls.getName()+":被@AutoRunClass标注了!");
        }else{
            System.out.println(cls.getName()+":没有被@AutoRunClass标注了!");
        }


    }
}

//Test3.java
package reflect;

import reflect.annotations.AutoRunClass;

import java.io.File;
import java.net.URISyntaxException;

public class Test3 {
    public static void main(String[] args) throws Exception {
        File dir = new File(
                Test3.class.getResource(".").toURI()
        );
        File[] subs = dir.listFiles(f->f.getName().endsWith(".class"));
        for(File sub : subs) {
            String fileName = sub.getName();
            String className = fileName.substring(0, fileName.indexOf("."));
            Class cls = Class.forName(
                    Test3.class.getPackage().getName() + "." + className);
            if(cls.isAnnotationPresent(AutoRunClass.class)){
                System.out.println(className+":被@AutoRunClass标注了!");
            }else{
                System.out.println(className+":没有被@AutoRunClass标注了!");
            }

        }
    }
}
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
89
90
91
92
93
94
95
96

# 自动实例化与当前ReflectDemo8在同一个包下所有被@AutoRunClass标注的类,自动调用那些被@AutoRunMethod标注的方法

//AutoRunClass.java

package reflect.annotations;

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

/**
 * 该注解是用来标注那些可以被反射机制自动调用的类
 *
 * 定义注解时,我们通常会使用java内置的两个注解来加以修饰
 * 当前注解。其中:
 * @Retention 用来指定当前注解的保留级别。有三个可选值,
 * 对应:
 *  RetentionPolicy.SOURCE  表示当前注解仅保留在源码中
 *  RetentionPolicy.CLASS(默认值) 表示注解会保留在字节码中,但是反射机制不可用
 *  RetentionPolicy.RUNTIME 表示保留在字节码文件中,但是可以被反射机制使用
 *  通常我们定义的注解都会指定为RUNTIME级别,辅助反射机制的操作。
 *
 * @Target用于表示当前注解可以在什么位置上使用。可选项都定义在ElementType上
 * 常见的有:
 *       ElementType.TYPE 在类上使用
 *       ElementType.FIELD 在属性使用
 *       ElementType.METHOD 在方法上使用
 *       ...多个的话,用大括号括起来
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface AutoRunClass {

}

//AutoRunMethod.java
package reflect.annotations;

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AutoRunMethod {
    /*
        为注解添加参数。
        语法:
        类型 参数名() [default 默认值]
        注:如果注解只有一个参数时,通常参数名使用value。
        原因:
        当我们定义参数后,比如下面的参数不用value,而使用:
        int num()
        那么外面在使用该注解并为参数赋值时,写法必须为:
        @AutoRunMethod(num=123)  即:@注解名(参数名=参数值)
        如果定义了多个参数时,使用格式:@注解名(参数名1=参数值1,参数名2=参数值2,...)
        注:参数的顺序可以不同,即:@注解名(参数名2=参数值2,参数名1=参数值1,...)也对

        如果只有一个参数,指定的是value,那么使用时可以忽略参数名,即:
        @AutoRunMethod(123)

     */
    int value() default 1;
}


//ReflectDemo8.java
package reflect;

import reflect.annotations.AutoRunClass;
import reflect.annotations.AutoRunMethod;

import java.io.File;
import java.lang.reflect.Method;
import java.net.URISyntaxException;

/**
 * 自动实例化与当前ReflectDemo8在同一个包下所有被@AutoRunClass标注的类
 */
public class ReflectDemo8 {
    public static void main(String[] args) throws Exception {
        File dir = new File(
                ReflectDemo8.class.getResource(".").toURI()
        );
        File[] subs = dir.listFiles(f -> f.getName().endsWith(".class"));
        //获取.class文件所在的目录中所有.class文件
        for (File sub : subs) {
            String fileName = sub.getName();
            String className = fileName.substring(0, fileName.indexOf("."));
            Class cls = Class.forName(
                    ReflectDemo8.class.getPackage().getName() + "." + className);//通过当前类的类对象获取所在的包名
            if (cls.isAnnotationPresent(AutoRunClass.class)) {
                System.out.println("实例化:" + className);
                Object o = cls.newInstance();
                System.out.println(o);
                //自动调用那些被@AutoRunMethod标注的方法
                Method[] methods = cls.getDeclaredMethods();
                for(Method method : methods){
                    if(method.isAnnotationPresent(AutoRunMethod.class)){
                        //通过方法对象获取在该方法上使用的注解@AutoRunMethod
                        AutoRunMethod arm = method.getAnnotation(AutoRunMethod.class);
                        //通过注解对象获取传给value参数的值
                        int num = arm.value();
                        System.out.println("自动调用方法:"+method.getName()+":"+num+"次");
                        for(int i=1;i<=num;i++) {
                            method.invoke(o);
                        }
                    }
                }

            }
        }
    }
}
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114

# 注解

# 概念

注解很厉害,它可以增强我们的java代码,同时利用反射技术可以扩充实现很多功能。它们被广泛应用于三大框架底层。传统我们通过xml文本文件声明方式,而现在最主流的开发都是基于注解方式,代码量少,框架可以根据注解去自动生成很多代码,从而减少代码量,程序更易读。例如最火爆的SpringBoot就完全基于注解技术实现。 注解设计非常精巧,初学时觉得很另类甚至多余,甚至垃圾。有了java代码干嘛还要有@注解呢?但熟练之后你会赞叹,它竟然可以超越java代码的功能,让java代码瞬间变得强大。大家慢慢体会吧。 常见的元注解:@Target、@Retention,jdk提供将来描述我们自定义的注解的注解。听起来好绕,别着急,做两个例子,立刻清晰。现在现有“元注解”这个概念。

# 分类

 JDK自带注解  元注解  自定义注解

# JDK注解

JDK注解的注解,就5个:

Override
Deprecated标记就表明这个方法已经过时了,但我就要用,别提示我过期
SuppressWarnings(“deprecation”) 忽略警告
SafeVarargs jdk1.7出现,堆污染,不常用
FunctionallInterface jdk1.8出现,配合函数式编程拉姆达表达式,不常用
1
2
3
4
5

# 元注解

描述注解的注解,就5个:

Target 注解用在哪里:类上、方法上、属性上
Retention 注解的生命周期:源文件中、class文件中、运行中
Inherited 允许子注解继承
Documented 生成javadoc时会包含注解,不常用
Repeatable注解为可重复类型注解,可以在同一个地方多次使用,不常用
1
2
3
4
5

# @Target ElementType.class

描述注解的使用范围:

ElementType.ANNOTATION_TYPE 		应用于注释类型
ElementType.CONSTRUCTOR 			应用于构造函数
ElementType.FIELD 					应用于字段或属性
ElementType.LOCAL_VARIABLE 		应用于局部变量
ElementType.METHOD 				应用于方法级
ElementType.PACKAGE 				应用于包声明
ElementType.PARAMETER 			应用于方法的参数
ElementType.TYPE 					应用于类的元素
1
2
3
4
5
6
7
8

# @Retention RetentionPolicy.class

定义了该注解被保留的时间长短,某些注解仅出现在源代码中,而被编译器丢弃; 而另一些却被编译在class文件中; 编译在class文件中的注解可能会被虚拟机忽略,而另一些在class被装载时将被读取。 为何要分有没有呢?没有时,反射就拿不到,从而就无法去识别处理。

SOURCE			在源文件中有效(即源文件保留)
CLASS			在class文件中有效(即class保留)
RUNTIME			在运行时有效(即运行时保留)
1
2
3

# 自定义注解

# 定义注解

//1,定义注解
//1.1,设置注解的使用范围@Target,啥都不写,哪儿都能用
//@Target({ElementType.METHOD})//作用于方法上
//@Target({ElementType.FIELD})//作用于属性上
@Target({ElementType.METHOD , ElementType.PACKAGE})//作用范围
@Retention(RetentionPolicy.SOURCE)//生命周期
@Target({ElementType.TYPE})//作用于类上
@interface Test{
	//3,定义属性
	int age() default 0;//使用时,必须给age属性赋值,如:age=X。除非设置好默认值。
//()不是参数,也不能写参数,只是特殊语法
	
	//4,特殊属性value
	String value() default "";//使用时,必须给value属性赋值,如:X  |  value=X。除非设置好默认值
}
注意:注解的语法写法和常规java的语法写法不同
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 使用注解

//2,使用注解
//@Test
//5,注解的组合属性
@Test(value="",age=0)
class HelloTest{
//	@Test(value="",age=0)
	String name;
}
1
2
3
4
5
6
7
8

# 解析注解

判断注解是否存在

package javapro.spring.annotation;

public class TestAnnotation {
	public static void main(String[] args) throws ClassNotFoundException {
		Class<?> clazz = Class.forName("javapro.spring.annotation.HelloController");
		Controller c = clazz.getAnnotation(Controller.class);
		
		if( c != null) {
			System.out.println(c);
			System.out.println(c.value());
		}
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13