Java基础之注解与反射

什么是注解?


  • Annotation是从JDK5.0开始引入的新技术
  • Annotation的作用:
    • 不是程序本身,可以对程序作出解释
    • 不可被其他程序(比如:编译器)读取
  • Annotation在哪里使用?
    • 可以附加在package,class,method,field等上面,相当于给他们添加了额外的辅助信息,可以通过反射机制编程实现对这些元数据的访问!

元注解


  • 元注解的作用是负责注解其他注解,Java定义了4个标准的meta-annotation类型,它们被用来提供对其他annotation类型说明。
  • 这些类型和它们所支持的类在java.lang.annotation包中可以找到(@Target , @Retention , @Document , @Inherited
    • @Target :用于描述注解的使用范围(即:被描述的注解可以用在什么地方,如:类,方法,构造器上等)
    • @Retention:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(SOURCE(源码)<CLASS(类)<RUNTIME

    @Retention自己写的注解一般是使用RUNTIME,即当运行时使用该注解。

    • @Document :说明该注解将被包含在Javadoc中,即是否将注解生成在Javadoc
    • @Inherited:说明子类可以继承父类中的该注解

自定义注解


  • 使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口
  • 分析:
    • @interface来声明一个注解,格式:public @interface 注解名 { 定义的内容}
    • 其中每一个方法实际上是声明了一个配置参数
    • 方法的名称就是参数的名称
    • 返回值类型就是参数的类型(返回值只能是基本类型,Class,String,enum)
    • 可以通过default来声明参数的默认值
    • 如果只有一个参数成员,一般参数名为value,使用该注解写参数时value可以省略
  • 举例:
public class Test{
  @MyAnnotation(name="cwx")
  publice void test(){}
}

@Target(value={ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{
  String name();//注意,这里的name()不是方法,就是参数!
  int age() default 18;//由于这里有默认值,所以注解可以不用写这个参数,如果没有就必须要写参数!
}

动态语言 VS 静态语言


  • 动态语言

是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗的讲就是代码可以根据某些条件改变自身结构。

  • 主要的动态语言:Object-C、C#、JavaScript、PHP、Python等。
  • 静态语言:

与动态语言相对应的,运行时结构不可变的语言,如Java、C、C++。

  • ★虽然Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性,可以利用反射机制获得类似动态语言的特性。

Java Reflection (反射)

  • Reflection是Java被视为动态语言的关键,反射机制允许程序在执行时借助于Reflection API取得任何类内部的信息,并能直接操作对象的内部属性及方法. 但是由于反射相对于JVM是解释型操作,性能会比正常的访问慢!

Class c =Class.forName("java.lang.string")

  • 加载完类之后,在堆内存中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们称之为:反射。
  • 举例:
    正常方式:引入需要“包类”名称--->通过new实例化--->取得实例化对象
    反射方式:实例化对象--->getClass()方法--->得到完整的“包类”名称。

Java内存分析:

  • java内存 :
    • 堆:存放new的对象和数组,可以被所有的线程共享,不会存放别的对象引用
    • 栈:存放基本变量类型(会包含这个基本类型的具体数值),存放引用对象的变量(会存放这个引用在堆里面的具体地址)
    • 方法区:可以被所有的线程共享,包含了所有的class和static变量

类的加载与ClassLoader的理解:

  • 加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象.
  • 链接:将Java类的二进制代码合并到JVM的运行状态之中的过程。
    • 验证:确保加载的类信息符合JVM规范,没有安全方面的问题
    • 准备:正式为变量(static)分配内存并设置变量默认初始值的阶段,这些内存都将在方法区中进行分配
    • 解析:虚拟机常量池内的符号引用(常量名)替换为引用(地址)的过程
  • 初始化
    1. 执行类构造器<clinit>()方法的过程。类构造器<clinit>()方法是有编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)。
    2. 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类初始化。
    3. 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步。

什么时候回发生类初始化?

  • 类的主动引用(一定会发生类的初始化):
    1、 当虚拟机启动,先初始化main方法所在的类
    2、new一个类的对象
    3、调用类的静态成员(除了final常量)和静态方法
    4、使用java.lang.reflect包的方法对类进行反射调用
    5、当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类
  • 类的别动引用(不会发生类的初始化):
    1、当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量或静态方法,不会导致子类初始化
    2、通过数组定义类引用,不会触发此类的初始化,例如:

Test[ ] test = new Test[5];//Test是一个类,这里是通过数组的方式引用

3、引用常量不会触发此类的初始化(常量在链接阶段就存入了调用类的常量池中了)。

  • 类加载器的作用:
    • 类加载器作用是用来把类(class)装载进内存的

有了Class对象,能做什么?

  • 获得Class对象:通过反射,通过已经实例化的对象.getClass()等。
  • 创建类的对象:调用Class对象的newInstance()方法,需要强转类型.
    1)类必须有一个无参数的构造器。
    2)类的构造器的访问权限需要足够。

难道没有无参的构造器就不能创建对象了吗?只要在操作的时候明确调用类中的构造器,并将参数传递进去之后,才可以实例化操作,例如:

主类:
public class Test{
  public static void main[ String[] (args) throw ClassNotFoundException, IllegalAccessException{
    Class c1 = Class.forName("com.cn.cwx.User");  
    //构造一个对象
    User user = (User) c1.newInstance();//本质是调用了类的无参构造器
    //通过构造器创建对象
    Constructor constructor = c1.getDeclaredConstructor(String.class,int.class,int.class);
    User user = (User)constructor.newInstance("cwx",001,18);
    //通过反射调用普通方法
    User user3 = (User)c1.newInstance();
    //通过反射获取一个方法,(方法名,方法参数的类类型)若方法没有参数,则写null
    Method setName = c1.getDeclaredMethod("setName",String.class);/*
    为什么要加String.class?
    因为程序需要通过该方法的参数类型去寻找方法,
    在方法中有可能会出现重载,如果不加String.class会让程序不知道找哪个方法*/

    //invoke:激活的意思
    //invoke(对象,方法的参数)若方法没有参数,则写null
    setName.invoke(user3,"cwx");
      //通过反射操作属性
      User user4 = (User)c1.Instance();
      //获得属性的名称获得属性
      Filed name = c1.getDeclaredFiled("name");
      //不能直接操作私有属性或__私有方法__,必须关闭程序的安全检测
      name.setAccessible(true);//利用`setAccessible(true)`保证安全检测关闭
      name.set("cwx2");
      //通过`user4.getName()`获得操作后的属性
}
}
User类:
public class User{
  private String name;
  private int id;
  private int age;
   有参构造;
   无参构造;
   get和set方法;

}

Tips: 如果程序里面用了很多的反射,效率很低,可以利用setAccessible(true)方法提高效率!

了解什么是ORM?

  • Object Relationship Mapping--->对象关系映射
    • 类(类名)和表结构(表名)对应
    • 属性和字段对应
    • 对象和记录对应
  • 举例:利用注解和反射完成类和表结构的映射关系
public class Test1{
  public static void main[ String[] (args) throw ClassNotFoundException, IllegalAccessException{
    
  Class c1 = Class.forName("com.cwx.student");
  //通过反射获得注解
  Annotation[] annotation = c1.getAnnotations();
  //获得注解的value的值
  Table table = (Table)c1.getAnnotation(Table.class);
    String value = table.value();

}
@Table("db_student")
class Student{
  @FieldTest(columnName="db_id",type="int",length=10)
  private int id;
  @FieldTest(columnName="db_age",type="int",length=20)
  private int age;
    get和set方法;
    有参和无参构造;
}
//自定义类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table{
  String value();//数据库名称
}
//自定义属性的注解
@Target(ElementType.Field)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldTest{
  String columnName();//列名
  String type();
  int length();
}

Tips: 这个例子中可以看出,其实注解是和数据库打交道的,由于可以通过反射方法获得注解的值,也就是数据库中的内容,这其实就现了类和表结构的映射关系,很多框架里面的注解都类似于这种。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352

推荐阅读更多精彩内容