反射
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象,的方法的功能称为java语言的反射机制。(如在运行状态中(正在运行的服务器,不用关了服务器再修改),修改配置文件,切换不同的数据库)
不用类名也可以获得class对象,this.getClass()方法,不能再static方法中使用。
在java虚拟机规范中,必须开始初始化的情况之一就是调用Class.forName()。而加载、验证、准备必须在初始化之前开始。
调用Class.forName(“xxx.Test1”)后,开始初始化。准备阶段会给static类变量赋值0,初始化阶段会赋值5,这里输出5。
Class.forName()会调用forName0方法,使用默认的类加载器,而forName0是native方法。
native关键字说明其修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(如C和C++)实现的文件中。
- Class.forName()必须开始初始化
- 可以在Class.forName()传入类加载器classLoader,findClass和loadClass方法,重写loadClass可以破坏双亲委派模型。
- 调用Class.forName后,完成类的加载过程。即该类被加载进了内存。每个类都有一个Class对象,存放在方法区中。
- Class clazz = Class.forName(“aaa”);得到Class类型的对象后,可以生成实例,并调用方法。
反射用途
当我们在使用IDE(如Eclipse,IDEA)时,当我们输入一个对象或类并想调用它的属性或方法时,一按点号,编译器就会自动列出它的属性或方法,这里就会用到反射。
很多框架(比如Spring)都是配置化的(比如通过XML文件配置JavaBean,Action之类的),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射——运行时动态加载需要加载的对象。
反射能够使用私有的方法属性
访问私有字段,需要调用Declared方法
method.setAccessible(true);//设置访问权限,如果不设置会抛异常
field.setAccessible(true);
Declared方法不可以获得继承的方法(公有继承也不行)。当然也包括它所实现接口的方法。不加Declared可以获得公有的继承方法和实现接口的方法(包括Object中的方法)
通过反射能够获得方法Method的形参类型,返回值类型,方法名称,修饰符,注解。Field可以获得修饰符,注解,字段的类型,字段的值
通过getClass()方法来获取类的定义信息,通过定义信息再调用getFields()方法来获取类的所有公共属性(只有公有,可以获得父类属性),或者调用getDeclaredFields()方法来获取类的所有属性,包括公共,保护,私有,默认的方法,但是这里有一点要注意的是这个方法只能获取当前类里面显示定义的属性,不能获取到父类或者父类的父类及更高层次的属性的,可以使用Class.getSuperclass()得到父类,再获取其字段
没有多继承
之所以没有多继承,是因为多继承容易引起混乱。比如一个相同的方法两个父类都有,子类到底是继承哪个叻?
java是通过实现多个接口来弥补不能多继承的缺陷的。接口中的方法都是抽象的,子类可以选择实现。
Java的注解
JDK1.5引入注解(java.lang.annotation包)用一个词就可以描述注解,那就是元数据,即一种描述数据的数据。所以,可以说注解就是源代码的元数据。注解Annotations仅仅是元数据,和业务逻辑无关。业务逻辑可以通过反射,获得注解中的数据
元注解(描述注解的注解)
@Documented一个简单的Annotations标记注解,表示是否将注解信息添加在java文档中。
@Retention– 定义该注解的生命周期。
RetentionPolicy.SOURCE – 在编译阶段丢弃。这些注解在编译结束之后就不再有任何意义,所以它们不会写入字节码。@Override, @SuppressWarnings都属于这类注解。
RetentionPolicy.CLASS – 在类加载的时候丢弃。在字节码文件的处理中有用。注解默认使用这种方式。
RetentionPolicy.RUNTIME– 始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。
@Target – 表示该注解用于什么地方。如果不明确指出,该注解可以放在任何地方。以下是一些可用的参数。
ElementType.CONSTRUCTOR(构造方法声明),FIELD(字段声明),LOCAL VARIABLE(局部变量声明),METHOD(方法声明),PACKAGE(包声明),PARAMETER(参数声明),TYPE(类接口),ANNOTATION_TYPE (另一个注释,元注解中用到)
@Inherited – 定义该注释和子类的关系
Annotations只支持基本类型、String、枚举、class类型。注释中所有的属性被定义成类似方法的样子,并允许提供默认值。
元注解target的源码
自定义注解实例
定义一个注解与定义一个类相似,可以在eclipse生成(右键包,new,annotation)
这里的@Retention必须为runtime,否则反射时不能用。
method或field都有getAnnotation方法,可以获得method或field上的注解,这里控制台会输出mao。
注解仅仅是元数据(描述数据的数据,只含有数据),和业务逻辑无关。业务逻辑可以通过反射,获得注解中的数据
Spring中:
@Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入,而@Resource默认按 byName自动注入罢了。@Resource有两个属性是比较重要的,分是name和type,Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。