学习笔记6:JavaSE & API(反射 & 路径 & 注解机制)
1、反射定义
- 反射是java的动态机制,可以允许我们在程序“运行期间”再确定实例化,调用某个方法,操作某个属性。
- 反射机制大大的提高了代码的灵活度,但是会有更高的系统开销和较慢的运行效率,因此反射机制不能被过度的使用。
2、反射原理
(1)类对象:JVM在加载一个类的class文件时,就会同时创建一个Class的实例,使用该实例记录加载的类的一切信息(类名,有哪些属性,哪些方法,哪些构造器等)。并且每个被JVM加载的类都有且只有一个Class的实例与之对应。
(2)操作步骤:
- 反射的第一步就是获取要操作的类的类对象,以便程序在运行期间得知要操作的类的一切信息然后对其进行响应的操作,包括其属性、方法等等所有信息。
- 通过反射机制实例化对象,操作反射出的实例对象,实现相应的业务逻辑。
3、反射常用的方法
方法 | 说明 | |
---|---|---|
1 | 类名.class |
例如:Class cls = int.class; 注意,基本类型获取类对象只有这一种方式 |
2 | Class.forName(类名) | 例如:Class cls = Class.forName("java.lang.String"); 注意,这里传入的类名必须是类的完全限定名,即:包名.类名 |
3 | 通过类加载器形式完成 |
|
方法(cls表示类对象) | 说明 | |
---|---|---|
1 | cls.newInstance() |
实例化对象。通过公开的无参构造器直接实例化对象 |
2 |
Constructor c = cls.getConstructor(String.class,int.class); Object obj = c.newInstance(参数); |
获取构造器,参数是类对象;也可以是无参的。返回Constructor。 实例化对象。通过有参构造器实例化对象 |
方法 (cls表示类对象) | 说明 | |
---|---|---|
1 | cls.getName() |
完全限定名 |
2 | cls.getSimpleName() | 对象的类名,不包含包名 |
3 |
cls.getPackage()
|
获取包对象,Package |
4 |
cls.getPackage().getName
|
类的包名 |
5 | cls.getMethods() |
获取所有公开方法,包含从超类继承下来的方法, 注意,返回值Method [ ] , 包名:java.lang.reflect.Method |
6 |
cls.getMethod(方法名);
cls.getMethod(方法名,String.class);
|
获取无参方法,返回Method类对象method;
获取有参方法,参数是类对象;
|
7 |
cls.getDeclaredMethods()
|
获取所有本类自定义方法,包括私有方法 |
8 |
cls.getDeclaredMethod(方法名);
cls.getDeclaredMethod(方法名,参数类型);
|
获取自定义方法,包括私有方法。参数类型传类对象,如 String.class |
9 |
cls.isAnnotationPresent(注解名.class)
|
类是否被某个注解标注了 |
方法(method表示方法对象) | 说明 | |
---|---|---|
1 |
method.invoke(obj); method.invoke(obj,参数); |
调用无参方法,invoke 需要传入 Object obj = cls.newInstance() 调用有参方法。 |
2 |
method.setAccessible(true) |
强制打开方法的访问权限,可以调用私有方法。 |
3 |
method.getparameterCount() |
获取参数个数 |
4 |
method.geModifiers() |
获取方法修饰符。补充,静态常量:Modifier.PUBLIC 等 |
5 | method.isAnnotationPresent(注解名.class) | 方法是否被某个注解标注了 |
6 | method.getAnnotation(注解名.class); |
获取注解对象,返回自定义的注解对象 通过注解对象可以获取注解传递的参数,例如,int value = obj.vale() ,obj为注解对象 |
5、注解
- 注解在开发中常被我们利用到反射机制中,辅助反射机制做更多灵活的操作。
- 注解在如今JAVA流行的框架中被大量的应用,简化了以前繁琐的配置工作。
- 注解也可以传递参数。
* 注解可以在: * 类上,属性上,方法上,构造器上,以及参数上使用 * 可以通过java内置的注解@Target来说明当前注解可以被应用的位置,对应的值被定义在ElementType上 * 例如: * @Target(ElementType.TYPE) 注解只能被用于类上 * @Target({ElementType.TYPE,ElementType.METHOD}) 注解只能被用于类上或方法上 * 当可以用于多个位置时,需要定义成数组的方式包含所有ElementType的值,即"{}"包含 * * * @Retention注解,用于标注当前注解的保留级别,有三个选项 * RetentionPolicy.SOURCE 注解仅保留在源代码中 * RetentionPolicy.CLASS 注解保留在字节码中,但是反射机制不能调用 * RetentionPolicy.RUNTIME 注解保留在字节码文件中,并且可以被反射机制所使用 * 当不指定@Retention时,默认的保留级别为CLASS,因此我们通常都需要明确指出保留级别为RUNTIME */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface AutoRunClass { }
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface AutoRunMethod { /* 1、定义参数的格式为: 格式:类型 参数名() [default 默认值] 注:default可选,用于为当前参数定义默认值。如果不指定,则使用注解时必须为此参数赋值。 2、使用注解传参时格式: @注解名(参数名1=参数值1[,参数名2=参数值2,....]) 3、如果注解@AutoRunMethod只有一个参数,且参数名为num时,那么使用时格式如下: @AutoRunMethod(num=1) =============重点============= 如果注解中只有一个参数,参数名建议选取value,这样的好处是,使用时可以不指定参数名,如: @AutoRunMethod(1) 4、如果指定了默认值,则可以不指定参数,例如: @AutoRunMethod() 此时注解中参数的使用default的默认值 */ //为注解定义一个int型的参数 // int num() default 1;//一个参数时,参数名不建议选取value以外的名字。 int value() default 1; }
(4)从注解中取值
public static void main(String[] args) throws Exception { Class cls = Class.forName("reflect.Person"); Method[] methods = cls.getDeclaredMethods(); for(Method method : methods){ //判断该方法是否被注解@AutoRunMethod标注了 if(method.isAnnotationPresent(AutoRunMethod.class)){ //通过方法对象获取该注解 AutoRunMethod arm = method.getAnnotation(AutoRunMethod.class); int value = arm.value(); System.out.println( "方法"+method.getName()+ "上的注解AutoRunMethod指定的参数值为:"+value ); } } }