Java注解与反射
注解
annotation,从jdk5开始引入的技术,给程序做出解释,可以被其他程序(编译器等)读取
注解可以加一些参数,还可以加在package、class、method、field上面,相当于给他们添加了额外的辅助信息,
我们可以通过反射机制编程实现对这些元数据的访问。
内置注解
常见三个内置注解:@Override重写、@Deprecated不鼓励使用、@SuppressWarnings抑制编译时的警告信息
其中@SuppressWarnings与前两个不同,需要添加一个参数才能正确使用,这些参数都是已经定义好了的我们选择性的使用就好。
例如@SupressWarnings(“all”)
@SupressWarnings(“unchecked”)
@SupressWarnings(value={“unchecked”,“deprecation”})
元注解
负责注解其他注解,java定义了4个标准的meta-annotation类型,他们被用来提供对其他annotation类型作说明。
这些类型和他们所支持的类在java.lang.annotation包中可以找到
@Target、@Retention、@Documented、@Inherited
- @Target:描述注解的使用范围
- @Rentention 表示需要在什么级别保存该注释信息,用于描述注解的生命周期(source<class<runtime),框架基本都用runtime
- @Document 该注解将被包含在javadoc中
- @Inherited 说明子类继承父类中的该注解
package com.cihiyuanzhang.annotation;
import java.lang.annotation.*;
//测试元注解(给注解的注解)
@MyAnnotation
public class Test02 {
}
//定义一个注解
//Target表示我们的注解可以用在那些地方
//Retention表示注解在哪里还有效
//@Document表示是否要将注解生成在javadoc中
//@inherited表示子类是否可以继承父类注解
@Target(value = {ElementType.METHOD,ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
@Inherited
@interface MyAnnotation{
}
自定义注解
使用个@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口
- @interface用来声明一个注解,格式:public @ interface 注解名{定义内容}
- 其中的每一个方法实际上是声明了一个配置参数
- 方法的名称就是参数的名称
- 返回值类型就是参数的类型(返回值只能是基本类型class,string,enum)
- 可以通过default来声明参数的默认值
- 如果只有一个参数成员,一般参数名为value
- 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值
package com.cihiyuanzhang.annotation;
import java.lang.annotation.*;
//测试元注解(给注解的注解)
public class Test02 {
//注解可以显示赋值,如果没有默认值,必须给注解赋值
@MyAnnotation(name = "你好")
public void test(){
}
}
//定义一个注解
//Target表示我们的注解可以用在那些地方
//Retention表示注解在哪里还有效
//@Document表示是否要将注解生成在javadoc中
//@inherited表示子类是否可以继承父类注解
@Target(value = {ElementType.METHOD,ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
@Inherited
@interface MyAnnotation{
String name() default "";
int id() default -1; //默认值为-1,代表不存在
}
反射
(翻译)
Java反射机制可以让我们在编译期(Compile Time)之外的运行期(Runtime)检查类,接口,变量以及方法的信息。反射还可以让我们在运行期实例化对象,调用方法,通过调用get/set方法获取变量的值。
Java反射机制功能强大而且非常实用。举个例子,你可以用反射机制把Java对象映射到数据库表,就像Butterfly Persistence所做的那样,或者把脚本中的一段语句在运行期映射到相应的对象调用方法上,就像 Butterfly Container在解析它的配置脚本时所做的那样。
阐述Java反射机制的基本原理包括如何去使用数组,注解,泛型以及动态代理还有类的动态加载以及类的重载的实现。同时也会向你展示如何实现一些比较有特性的功能,比如从一个类中读取所有的get/set方法,或者访问一个类的私有变量以及私有方法。在这个系列的指南中同时也会说明一些非反射相关的但是令人困惑的问题,比如哪些泛型信息在运行时是有效的,一些人声称所有的泛型信息在运行期都会消失,其实这是不对的。
(自述)
反射是java被视为动态语言的关键,反射机制允许程序在执行期间借助于ReflecctionAPI取得任何类的内部信息,并能直接操作任意对象的内部属性及方法
Class C = Class.forName(“java.lang.String”)
加载完类之后,在堆内存的方法区就产生了一个class类型的对象(一个类只有一个class对象),这个对象包含了完整的类的结构信息,我们可以通过这个对象看到类的结构,这个对象就像一面镜子,透过这个镜子看到的类的结构,所以我们称之为反射。
反射机制提供的功能与优缺点:
在运行时:
判断任意一个对象所属的类
构造任意一个类的对象
判断任意一个类具有的成员变量和方法
获取泛型信息
调用任意一个对象的成员变量和方法
处理注解
生成动态代理(AOP等广泛运用,框架大量运用)
反射优点:实现动态创建对象和编译,体现出很大的灵活性
反射缺点:对性能有影响,使用反射基本上是一种解释操作,我们可以告诉JVM,希望作什么,它满足我们要求,这类操作总是慢于直接执行相同的操作(用new)
相关API:
java.lang.Class:代表一个类
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor:代表类的构造器
//一个类在内存中只有一个class对象
//一个类被加载侯,类的整个结构都会封装在class对象中
获取user类的小例子
package com.cihiyuanzhang.reflection;
//什么叫反射
public class Test02 {
public static void main(String[] args) throws ClassNotFoundException {
//通过反射获取类的class对象
Class c1=Class.forName("com.cihiyuanzhang.reflection.User");
System.out.println(c1);
}
}
//一个类在内存中只有一个class对象
//一个类被加载侯,类的整个结构都会封装在class对象中
//实体类
//实体类一般用pojo或者entity去表示。
class User{
private String name;
private int id;
private int age;
public User() {
}
public String getName() {
return name;
}
public void setName(String name) {
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;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", id=" + id +
", age=" + age +
'}';
}
}
Class类
对象照镜子侯可以得到的信息:某个类的属性,方法和构造器,某个类到底实现了那些接口,对于每个类而言,jre都为其暴力路一个不变的class类型的对象,一个class对象包含了特定某个结构(calss/interface/enum/annotation/primitive type/void[])的有关信息
- class本身也是一个类
- class对象只能由系统建立对象
- 一个加载的类在JVM中只会有一个class示例
- 一个class对象对应的是一个加载到jvm中的一个.class文件
- 每个类的实例都会记得自己是由哪个class实例所生成
常用方法
- static 类<?> forName(String className) 返回与给定字符串名称的类或接口相关联的 类对象。
- T newInstance() 创建由此 类对象表示的类的新实例。
- String getName() 返回由 类对象表示的实体(类,接口,数组类,原始类型或空白)的名称,作为 String
- 类<? super T> getSuperclass() 返回 类表示此所表示的实体(类,接口,基本类型或void)的超类 类 。
- 类<?>[] getInterfaces() 确定由该对象表示的类或接口实现的接口。
- ClassLoader getClassLoader() 返回类的类加载器。
- Constructor getConstructor(类<?>… parameterTypes)
返回一个 Constructor对象,该对象反映 Constructor对象表示的类的指定的公共 类函数。 - 方法 getMethod(String name, 类<?>… parameterTypes)
返回一个 方法对象,它反映此表示的类或接口的指定公共成员方法 类对象。 - Field[] getDeclaredFields() 返回的数组 Field对象反映此表示的类或接口声明的所有字段 类对象。
获取Class类的实例
- 若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高
Class clazz = Person.class - 已知某个类的示例,调用该实例的getClass()方法获取class对象
Class clazz = person.getcalss(); - 已知一个类的全类名,且该类在类路径下,可以通过Class类的静态方法froName()获取,可能抛出ClassNotFoundException异常
Class clazz = Class.forName(“Demmo01.Student”); - 内置基本数据类型可以直接用类名.Type获取
- 还可以利用ClassLoader
哪些类型有class对象?
class:外部类,成员(内部类,静态内部类),局部内部类,匿名内部类
interface:接口
数组
enum枚举
annotation注解
primitive type基本数据类型
void也有
只要元素类型与维度一样,就是同一个class
例如数组:a[100]与a[10],他们底层是同一个
内存分析
java的内存分为堆、栈、方法区。
堆存放new的对象和数组、可以被所有的线程共享,不会存放别的对象引用。
栈存放基本变量类型(会包含这个基本类型的具体数值),存放引用对象的变量(会存放这个引用在堆里边的具体地址)
方法区存放可以被所有的线程共享,包含了所有的class和static,可以视作**特殊的堆**
类的加载和ClassLoader
总结:一些静态常量等,是在链接时候就已经初始化了的。
new出来的对象和通过反射调用的对象才会进行初始化。
类的初始化:
类加载器的作用
简单总结
- 类加载机制:如果我们定义一个包名,类目与原有类完全一致的类,则是无法进行加载的,当然,可以使用自定义类加载器进行加载,特定的出来。当我们在已经启动了的程序中,修改一个类,然后,想得到该类的对象,我们需要重新对该类进行加载,使用类加载器,如果不适用则无法进行新的new,因为对应的.class还不在内存中。
- 类加载过程
加载–>链接–>初始化
加载:将来class文件加载到内存中
链接:1校验:格式校验
2准备:为类的静态变量准备内存
3解析:将直接引用转化为内存引用。
初始化:初始化参数
双亲委派机制
当程序要加载一个类的时候,不是本类直接加载,而是传递给父类加载器,看看是否已经加载了该类,如果父类加载器已经加载了,则不需要再次进行加载,否则自己加载。
为什么要用双亲委派机制?
1:防止重复加载同一个.class文件,通过向上一层问一下,加载过了就不用再加载,保证了数据安全
2:保证核心.class文件不能被篡改,通过委托的方式,不回去篡改核心.class,即使篡改也不会去加载,即使加载也不会是同一个Class对象,保证了.class的执行安全
获取指定构造器、方法、类属性等DEMO
package com.cihiyuanzhang.reflection;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test03 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c1 = Class.forName("com.cihiyuanzhang.reflection.User");
//获得类的名字
System.out.println(c1.getName()); //获得包名+类名
System.out.println(c1.getSimpleName());//获得类名
System.out.println("=========================");
//获得类的属性
Field[] fields = c1.getFields();//智能找到public属性
fields=c1.getDeclaredFields(); //能找到全部属性
for (Field field : fields) {
System.out.println(field);
}
System.out.println("=========================");
//获得指定属性的值
Field name = c1.getDeclaredField("name");
System.out.println(name);
System.out.println("=========================");
//获得类的方法
Method[] methods = c1.getMethods();//获得本类及其父类的全部public方法
for (Method method : methods) {
System.out.println(method);
}
methods=c1.getDeclaredMethods(); //获得本类的所有方法
for (Method method : methods) {
System.out.println(method);
}
}
}
class Person{
private int age;
private String name;
private int id;
public Person(int age, String name, int id) {
this.age = age;
this.name = name;
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;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
", id=" + id +
'}';
}
}
class对象的作用(能做什么?)
示例:
package com.cihiyuanzhang.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test04 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
//获得Class对象
Class c1 = Class.forName("com.cihiyuanzhang.reflection.User");
//构造一个对象
User user = (User)c1.newInstance(); //本质是调用了类的无参构造器
System.out.println(user);
//通过构造器创建对象
Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
User user2 = (User)constructor.newInstance("nihao", 001, 11);
System.out.println(user2);
//通过反射调用普通方法
User user3 = (User)c1.newInstance();
//通过反射获取一个方法
Method setName = c1.getDeclaredMethod("setName", String.class);
//invoke: 激活的意思
//(对象,“方法的值”)
setName.invoke(user3,"nihao");
System.out.println(user3.getName());
//通过反射操作属性
User user4 = (User)c1.newInstance();
Field name = c1.getDeclaredField("name");
//关掉权限检测,否则会因为私有属性不可访问
name.setAccessible(true); //设置可访问,默认flase不可操作
name.set(user4,"wobuhao");
System.out.println(user4.getName());
}
}
反射操作泛型
package com.cihiyuanzhang.reflection;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
//通过反射获取泛型
public class Test05 {
public void test01(Map<String,User> map, List<User> list){
System.out.println("test1");
}
public Map<String,User> test02(){
System.out.println("test2");
return null;
}
//通过
public static void main(String[] args) throws NoSuchMethodException {
Method method = Test05.class.getMethod("test01", Map.class, List.class);
//通过方法获得参数类型信息
Type[] genericParameterTypes = method.getGenericParameterTypes();
for (Type genericParameterType : genericParameterTypes) {
System.out.println("##"+genericParameterType);
//以上只能拿到Map和List
//要想拿到String、User类型的话,要继续对其进行遍历
if (genericParameterType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
}
}
反射操作注解
小例子:
package com.cihiyuanzhang.reflection;
import java.lang.annotation.*;
import java.lang.reflect.Field;
//练习反射操作注解
public class Test06 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c1 = Class.forName("com.cihiyuanzhang.reflection.Student2");
//通过反射获得注解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//获得注解的value值
Tables tables = (Tables)c1.getAnnotation(Tables.class);
String value = tables.value();
System.out.println(value);
//获得类指定的注解
Field f = c1.getDeclaredField("id");
Tables annotation = f.getAnnotation(Tables.class);
System.out.println(annotation.columnName());
System.out.println(annotation.type());
System.out.println(annotation.length());
}
}
@Tables("db_student")
class Student2{
@Fieldgg(columnName = "db_id",type = "int",length = 10)
private int id;
@Fieldgg(columnName = "db_age",type = "int",length = 10)
private int age;
@Fieldgg(columnName = "db_name",type = "varchar",length = 3)
private String name;
public Student2() {
}
public Student2(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;
}
@Override
public String toString() {
return "Student2{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}
//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
//创建一个注解
@interface Tables{
String value();
}
//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Fieldgg{
String columnName();
String type();
int length();
}