Java的注解与反射-框架底层学习的基础:
1.注解(Annotation):
1.1功能
- 不是程序本身,可以对程序做出解释(这一点和注释comment没有区别);
- 可以被其他的程序(比如编译器)读取;
- 检查与约束
1.2分类
1.2.1内置注解:
(最常用的三种方法,在Java帮助文档可以查阅)
- @Override:重写方法
- @Deprecated:表示不鼓励程序员使用
- SuppressWarnings:镇压警告,镇压在编译过程中出现的警告,放在类上就镇压类的警告,放在方法上镇压方法的警告
1.2.2元注解(meta-annotation):
可以注解其他的注解,理解为注解的注解。
- @Target:用于描述注解的适用范围:
- @Retention:在什么级别保存该注释,描述注解的生命周期(source<class<runtime(默认))
- @Documented:说明该注解将被包含在javadoc中
- @Inherited:说明子类可以继承父类中的该注解
例:定义元注解
//测试元注解
public class Test03{
}
//定义注解的生命周期在源码级别,即在源码上还有效,一般默认为runtime
@Retention(RetentionPolicy.SOURCE)
//定义一个元注解,作用域为方法上
@Target(value = ElementType.METHOD)
//表示是否将我们的注解生成在Javadoc中
@Documented
//子类可以继承父类的注解
@Inherited
//定义一个注解
@interface MyAnnotation{
}
1.3自定义注解
1.3.1格式:
- (public)@interface 注解名 {定义内容}
- 其中的每个方法实际上是声明了一个配置参数.
- 方法的名称就是参数的名称.
- 返回值类型就是参数的类型(返回值只能是基本类型,Class,String,enum)
- 可以通过default来声明参数的默认值
- 如果只有一个参数成员,一般参数名为value
- 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值.
例:
public class Tset{
//如果没有默认值,则必须给注解赋值
@MyAnnotation(name = "爱玛",schools = {"你好","哈哈哈"})
@MyAnnotation("爱玛")
public void test(){}
}
@Target({ElementType.TYPE,ElementType.METHOD})//作用在类和方法上
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation02{
//注解的参数:参数类型+参数名();
String name() default "";
int id() default -1;//如果默认值为-1,则代表找不到
String[] schools();
}
@interface MyAnnotation3{
String value();//使用value参数的时候没有默认值时,不需要显示的写出参数名
}
2.反射(重点)
2.1重要性
1.反射是Java被视为动态语言的关键
2.加载一个类时会生成一个class对象,通过获取这个对象来获取class的各种信息(一个class在加载的时候也只有一个class实例)
2.2.优缺点
优点:可以动态的创建对象和编译,体现出很大的灵活性;
缺点:对性能有影响,这是解释操作,慢于直接执行的操作
2.3获取方法
- 若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高。
Class c2 = Person.class;
- 已知某个类的实例,调用该实例的getClass()方法获取Class对象
Class c3 = person.getClass();
- 已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException
Class c4 = Class.forName("包名+类名");
- 内置基本数据类型可以直接用类名.Type
Class c5 = Integer.TYPE
2.4哪些类型可以有Class对象
- class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类。
- interface:接口
- []:数组
- enum:枚举
- annotation:注解@interface
- primitive type:基本数据类型
- void
只要类型一样并且维度一样,不论大小,他们的hashcode就一样(一维数组和二维数组不一样,但是int[10]和int[100]获得的Class对象的hashcode一样)
2.5获取运行时类的完整结构
例:通过类的对象的各个方法获取类的属性:
//c1为类的对象
c1.getName();//获得类的名字
c1.getSimpleName()//获得类的简单名字
//获得类的属性
c1.getFiles();//获得所有属性,这是一个数组,但是这个只能找到piblic属性
c1.getDeclaredFields();//找到全部的属性
//获得类的方法
c1.getMethods();
c1.getDeclaredMethods();
2.6动态创建对象执行方法
创建类的对象:调用Class对象的newlnstance()方法
1)类必须有一个无参数的构造器
2)类的构造器的访问权限需要足够
Class c1 = Class.forName("包名+类名");
Obeject x = c1.newInstance();//可以进行类型的强转
☆当没有类的无参构造器时的做法
步骤如下:
1)通过Class类的getDeclaredConstructor(Class.…parameterTypes)取得本类的指定形参类型的构造器
2)向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。
3)通过Constructor实例化对象
Object object = c1.getDeclaredConstructor(参数.class)
Obeject c2 = object.newInstance(参数)
通过反射调用方法:
Class c1 = Class.forName("包名+类名");
Object c2 = c1.newInstance();
Method c3 = c2.getDeclaredMethod(参数);
c3.invoke(对象,方法值);
还可以通过反射调用属性:
同上面一样,利用getDeclaredField()方法获取属性,然后用get,set方法进行属性的传参。
- 但是注意,这种方法不能操作私有属性,会报错,必须在set之前使用:对象.setAccessible(true)语句关闭权限检测
2.7setAccessible
setAccessible作用是启动和禁用访问安全检查的开关。
-
参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。作用:
- 提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true。(因为反射的效率低于正常操作的效率)
- 使得原本无法访问的私有成员也可以访问。
-
参数值为false则指示反射的对象应该实施Java语言访问检查