一.引入 

     上文http://zy19982004.iteye.com/blog/1979208中,提到了注解类JyzTargetPackage可以定义为@Target(ElementType.PACKAGE),可是在被注解类里我无论怎么加,编译器都报错,于是引入了package-info.java这个文件。

 

二.创建package-info.java

  1. "I found that when you create a new package in eclispe there is a check box to check if you want a package-info.java."勾上就行了。
  2. 如果不幸的是你已经创建了这个包并在里面定义了很多类,而eclispe又是不能直接创建一个package-info.java文件的。只能在包对应文件夹里,手动创建一个package-info.java,写上包名,最后刷新eclispe即可。

 

三.package-info.java的作用

 

  1. "Package annotations must be in file package-info.java",package-info.java为我们提供了包注解的地方。JyzTargetPackage(http://zy19982004.iteye.com/blog/1979208)苦苦寻找终于找到地方了。
  2. 提供包级别的类(或接口),这些类(或接口)只有本包里才能访问,即使是子包也不能访问。
  3. 提供包的整体注释说明。

 package-info.java

    1. /**  
    2.  * <b>package-info不是平常类,其作用有三个:</b><br>  
    3.  * 1、为标注在包上Annotation提供便利;<br>  
    4.  * 2、声明包的私有类和常量;<br>  
    5.  * 3、提供包的整体注释说明。<br>  
    6.  * 
    7.  * @author JoyoungZhang@gmail.com 
    8. */   
    9. @JyzTargetPackage(version="1.0")  
    10. package com.jyz.study.jdk.annotation;  
    11.   
    12. class PackageInfo{  
    13. public void common(){  
    14. "sa");  
    15.     }  
    16. }  
    17.   
    18. class PackageInfoGeneric<T extends Throwable>{  
    19. private T obj;  
    20. public void set(T obj){  
    21. this.obj = obj;  
    22.     }  
    23. public void common(){  
    24. "sa");  
    25.     }  
    26. }  
    27.   
    28. interface packageInfoInteger{  
    29. public void test();  
    30. }  
    31.   
    32. class PackageConstants{  
    33. public static final String ERROE_CODE = "100001";     
    34. }

     

     

    TestPackageInfo.java

    1. package com.jyz.study.jdk.annotation;  
    2.   
    3. import java.io.IOException;  
    4.   
    5. /**
    6.  * 测试package-info.java文件的作用
    7.  * 1、为标注在包上Annotation提供便利;<br>  
    8.  * 2、声明包的私有类和常量;<br>  
    9.  * @author JoyoungZhang@gmail.com
    10.  *
    11.  */  
    12. public class TestPackageInfo {  
    13.   
    14. public static void main(String[] args) {  
    15. //1  
    16. "com.jyz.study.jdk.annotation");  
    17. if(p != null && p.isAnnotationPresent(JyzTargetPackage.class)){  
    18. class);  
    19. if(nav != null){   
    20. "package version:" + nav.version());  
    21.             }  
    22.         }  
    23.           
    24. //2  
    25. new PackageInfo();  
    26.         packageInfo.common();  
    27.           
    28. //泛型也能很好的工作,在pakcage-info.java里定义的类和普通类没什么区别  
    29. new PackageInfoGeneric<Exception>();  
    30. new IOException("device io"));  
    31.         packageInfoGeneric.common();  
    32.           
    33.           
    34. new Sub();  
    35.         sub.test();  
    36.           
    37.         System.out.println(PackageConstants.ERROE_CODE);  
    38.     }  
    39. }  
    40.   
    41. class Sub implements packageInfoInteger{  
    42.       
    43. @Override  
    44. public void test() {  
    45. "sub");  
    46.     }  
    47.       
    48. }  
    49.   
    50.   
    51. console output:  
    52. package version:1.0  
    53. sa  
    54. java.io.IOException: device iosa  
    55. sub  
    56. 100001

     

      需要注意两点

    1. package-info.java里不能声明public class(或 interface)
    2. 刚开始p.isAnnotationPresent(JyzTargetPackage.class)返回false,后来找到原因JyzTargetPackage没有加上@Retention(RetentionPolicy.RUNTIME)。

    翻看以前的笔记,看到一个特殊的java文件:pacakge-info.java,虽然有记录,但是不全,就尝试着追踪一下该问题, 分享一下流水账式的结果。

    首先,它不能随便被创建。在Eclipse中, package-info文件不能随便被创建,会报“Type name is notvalid”错误,类名无效,Java变量定义规范是:字母、数字、下划线,还有那个不怎么常用的$符号(顺带说下,Java是支持中文名称的变量,习惯挑战的同学可以尝试下,分享一下这方面的经验),这个中划线可不再之列,那怎么创建这个文件呢?

    很简单,用记事本创建一个,然后拷贝进去再改一下就成了,更直接的办法就是从别的项目中拷贝过来一个,这更方便。

      其次,服务的对象很特殊。一个类是一类或一组事物的描述,比如Dog这个类,就是描述旺财的,那package-info这个类是描述啥的呢?它总要有一个被描述或被陈述的对象,它是描述和记录本包信息。

      最后,类不能带有public、private访问权限。package-info.java再怎么特殊,也是一个类文件,也会被编译成package-info.class,但是在package-info.java中只能声明默认访问权限的类,也就是友好类。

    其实还有几个特殊的地方,比如不可以继承,没有接口,没有类间关系(关联、组合、聚合等等)等。

    这个文件的特殊性说完了,那再说说它有什么作用,它有三个作用:

    1、为标注在包上Annotation提供便利;

    2、声明友好类和包常量;

    3、提供包的整体注释说明。

        我们来建立一个项目演示这三个作用,建立一个package-info的Java Project,在com.company包三个类:package-info.java 是我们重点关注的,PkgAnnotation.java是一个标注在包上的注解定义,Client.java模拟业务操作类。其结构如下图:

     

     

    为标注在包上Annotation提供便利

         首先定义一个包类型的注解,它只能放置的一个包上:

     

      1. /**
      2.  * 定义只能标注在package上的注解
      3. */  
      4. @Target(ElementType.PACKAGE)  
      5. @Retention(RetentionPolicy.RUNTIME)  
      6. public @interface PkgAnnotation {  
      7. }

       

       

           再定义一个package-info类,这个是一个特殊的类,先看代码:

       

      1. @PkgAnnotation  
      2. package com.company;

            很简单,就这么个文件,里面啥都没有,就这两句话,没有class类,没有常变量声明。接着写一个模拟交易类,代码如下:

        1. public class Client {     
        2. public static void main(String[] args) {  
        3. //可以通过I/O操作或配置项获得包名  
        4. "com.company";       
        5.         Package pkg = Package.getPackage(pkgName);  
        6. //获得包上的注解  
        7.         Annotation[] annotations = pkg.getAnnotations();  
        8. //遍历注解数组  
        9. for(Annotation an:annotations){  
        10. if(an instanceof PkgAnnotation){  
        11. "Hi,I'm the PkgAnnotation ,which is be placed on package!");  
        12. /*
        13.                  * 注解操作
        14.                  * MyAnnotation myAnn = (PkgAnnotation)an;
        15.                  * 还可以操作该注解包下的所有类,比如初始化,检查等等
        16.                  * 类似Struts的@Namespace,可以放到包名上,标明一个包的namespace路径
        17.                  */           
        18.             }  
        19.         }  
        20.     }  
        21. }

              运行结果如下所示:

         

         

        Hi,I'm the PkgAnnotation ,which is be placed on package!

         

         

        声明友好类和包常量

             这个比较简单,而且很实用,比如一个包中有很多的内部访问的类或常量,就可以统一的放到package-info类中,这样就方便,而且集中管理,减少friendly类到处游走的情况,看例子:

          1. @PkgAnnotation  
          2. package com.company;  
          3. //这里是包类,声明一个包使用的公共类,强调的是包访问权限  
          4. class PkgClass{  
          5. public void test(){  
          6.     }  
          7. }  
          8. //包常量,只运行包内访问,适用于分“包”开发  
          9. class PkgConst{  
          10. static final String PACAKGE_CONST="ABC";  
          11. }

           

           

           

            提供包的整体注释说明

               如果是分“包”开发,也就是说一个包实现一个业务逻辑或功能点、或模块、或组件,则需要对一个包有很好的说明,说明这个包是干啥的,有啥作用,版本变迁,特别说明等等,如下:

          1. /**
          2.  * <b>package-info不是平常类,其作用有三个:</b><br>
          3.  * 1、为标注在包上Annotation提供便利;<br>
          4.  * 2、声明包的私有类和常量;<br>
          5.  * 3、提供包的整体注释说明。<br> 
          6. */  
          7. package com.company;

           

           

           

               通过javadoc生成的API文档如下:

           

               这与包下放置package.htm没啥区别,只是package-info可以更好的在代码中维护文档的完整性,并且可以实现代码与文档同步更新,package.htm也可以做到,不争论,建议是Java 1.5以上版本都使用package-info.java来注释。

           

              与package-info相关的问题

               在项目开发中,可以放置在包上的常用注解有:Struts的@namespace、Hibernate的@FilterDef和@TypeDef等等。在包下,随便一个类中的包名前加这些注解,Eclipse会提示“Package annotations must be in file package-info.java”,在该包下建立package-info.java文件,把注解移到这里即可。

              使用Checkstyle插件做代码检查时,会报一个警告“Missing package-info.java file.”也是这个package-info文件惹的祸,在各个包下创建一个即可。