JAVA代码学习(终)

2023年将会持续于B站、优快云等各大平台更新,可加入粉丝群与博主交流:838681355,为了老板大G共同努力。
【商务合作请私信或进群联系群主】

九、注释与注解

9.1 JAR文件

程序打包,可以提供一个单独的文件,JAVA归档(JAR)文件就说为此目的而设计的,一个JAR文件即可以包含类文件,也可以包含诸如图像的声音等其它类型文件,可以将jar文件比喻成zip压缩格式。
9.1.1 创建JAR文件
jar工具制作JAR文件(默认JDK安装中,工具位于jdk/bin目录下)
命令: jar cvf jarFileName file1 file2 ...
    
-c (create)创建新档案
-t 列出档案目录
-x 从档案中提取指定的 (或所有) 文件
-u (update)更新现有档案
-v 在标准输出中生成详细输出
-f 指定档案文件名
-m 包含指定清单文件中的清单信息
-n 创建新档案后执行 Pack200 规范化
-e 为捆绑到可执行 jar 文件的独立应用程序
指定应用程序入口点
-0 仅存储; 不使用任何 ZIP 压缩
-P 保留文件名中的前导 '/' (绝对路径)".." (父目录) 组件
-M 不创建条目的清单文件
-i 为指定的 jar 文件生成索引信息
-C 更改为指定的目录并包含以下文件
9.1.2 清单文件
每个JAR还包含一个清单文件(manifest),用于描述归档文件的特殊特性。
清单文件名为MANIFEST.MF,它是位于JAR文件的一个特殊的META-INF子目录中。

复杂的清单文件可能包含更多条目,浙西而清单条目被分为很多节,第一节被称为主节(main section),它作用域整个JAR文件。随后的条目用来指定命名实体的属性,如单个文件、包或者URL,它们都必须以一个Name条目开始。节与节之间用空行分开。

想要编辑清单文件,需要将希望添加到清单文件中的行放到文本文件中,运行:jar cfm jarFileName manifestFileNmae...
例如创建一个包含清单文件的JAR文件,应该运行:jar cfm MyArchive.jar manifest.mf com/mycompany/mypkg/*.class
想要更新一个包含清单文件的JAR,则需要将增加的部分放置到一个文本文件中并执行:jar ufm MyArchive.jar manifest-additions.mf
9.1.3 执行JAR文件
1. 可以使用jar命令中的e选项指定程序入口点,即通常需要在调用java程序启动器时指定的类:
jar cvfe MyProgram.jar com.mycompany.mypkg.MainAppClass files to add

2. 或者可以在清单文件中指定程序的主类,包括以下形式语句:
Main-Class: com.mycompany.mypkg.MainAppClass

3. 或者简单通过命令启动程序: java -jar MyProgram.jar
9.1.4 文档注释
JDK包含一个工具名为javadoc,它可以由源文件生成一个HTML文档。
9.1.4.1 注释的插入
javadoc实用工具从下面几项中抽取信息:
* 模块;
*;
* 公共类与接口;
* 公共的和受保护的字段;
* 公共的和受保护的构造器即方法;
9.1.4.2 类注释
类注释必须放在import语句之后,类定义之前。
例如:
/**
blibli.cllmsyK
*/
public class Card
{
    ...
}
3.5.4.3 方法注释
每一个方法注释必须放在所描述的方法之前。除了通用标记之外,还可以使用下面的标记:
* @param variable description:这个标记将给当前方法的"parameters"(参数)部分添加一个条目。这个描述可以占据多行,并且可以使用HTML标记。一个方法的所有@param标记必须放在一起。
* @return description:这个标记将给当前方法添加"returns"(返回)部分。这个描述可以跨多行,并且可以使用HTML标记
* @throws class description:这个标记将添加一个注释,表示这个方法由可能抛出异常。
9.1.4.4 字段注释
只需要对公共字段(通常指的时静态常量)建立文档。例如:
/**
* the "hearts" card suit
*/
public static final int HEARTS = 1;
9.1.4.5 通用注释
标记@since text会建立一个"since"(始于)条目。text(文本)可以是引入这个特性版本的任何描述。例如:@since 1.7.1
* @author name:这个标记会产生一个"author"(作者)条目。
* @version text:这个标记将产生一个"version"(版本)条目。
9.1.4.6 包注释
可以直接将类、方法和变量的注释放置在java源文件中,只要用/** ... */ 文档注释界定就可以了。
9.1.4.7 注释抽取
假设HTML文件将放在名为docDirectory的目录下,执行以下步骤:
1. 切换到包含想要生成文档的源文件的目录。如果由嵌套的包要生成文档,例如com.horstmann.corejava,就必须切换到包含子目录com的目录。
2. 
如果是一个包,应该运行命令:javadoc -d docDirectory nameOfPackage
或者,如果要为多个包生成文档:javadoc -d docDirectory nameOfPackage1 nameOfPackage2...
如果文件在无名的包中,就应该运行:javadoc -d docDirectory *.java

9.2 注解

注解理解为代码里的特殊标记,这些标记可以在编译、类加载和运行时被读取,并执行相应的处理。
    
注解可以元数据这个词来描述,即一种描述数据的数据。所以可以说注解就是源代码的元数据。例如以下代码:
    @Override
    public String toString() {
        return "C语言中文网Java教程";
    }
重写了 Object 类的 toString() 方法并使用了 @Override 注解。如果不使用 @Override 注解标记代码,程序也能够正常执行。那么这么写有什么好处吗?事实上,使用 @Override 注解就相当于告诉编译器这个方法是一个重写方法,如果父类中不存在该方法,编译器便会报错,提示该方法没有重写父类中的方法。这样可以防止不小心拼写错误造成麻烦。
    
注解常见的作用有以下几种:
    1. 生成帮助文档。这是最常见的,也是 Java 最早提供的注解。常用的有 @see@param@return 等;
    2. 跟踪代码依赖性,实现替代配置文件功能。比较常见的是 Spring 2.5 开始的基于注解配置。作用就是减少配置。现在的框架基本都使用了这种配置来减少配置文件的数量;
    3. 在编译时进行格式检查。如把 @Override 注解放在方法前,如果这个方法并不是重写了父类方法,则编译时就能检查出。

基本注解包括:@Override@Deprecated@SuppressWarnings@SafeVarargs@FunctionalInterface
9.2.1 @Override注解
Java@Override 注解是用来指定方法重写的,只能修饰方法并且只能用于方法重写,不能修饰其它的元素。它可以强制一个子类必须重写父类方法或者实现接口的方法。
    
使用 @Override 注解示例代码如下:
    public class Person {
        private String name = "";
        private int age;
        ...
        @Override
        public String t0String() { //toString()
            return "Person [name=" + name + ", age=" + age + "]";
        }
    }

如果 toString() 不小心写成了 t0String(),那么程序会发生编译错误。会有如下的代码提示:
    类型为 Person 的方法t0String()必须覆盖或实现超类型方法
所以 @Override 的作用是告诉编译器检查这个方法,保证父类要包含一个被该方法重写的方法,否则就会编译出错。这样可以帮助程序员避免一些低级错误。
9.2.2 @Deprecated注解
Java@Deprecated 可以用来注解类、接口、成员方法和成员变量等,用于表示某个元素(类、方法等)已过时。当其他程序使用已过时的元素时,编译器将会给出警告。
    
使用 @Deprecated 注解示例代码如下:
    @Deprecated
    public class Person {
        @Deprecated
        protected String name;
        private int age;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        @Deprecated
        public void setNameAndAge(String name, int age) {
            this.name = name;
            this.age = age;
        }
        @Override
        public String toString() {
            return "Person [name=" + name + ", age=" + age + "]";
        }
    }
9.2.3 @SuppressWarningns:抑制编译器警告
Java 中的 @SuppressWarnings 注解指示被该注解修饰的程序元素(以及该程序元素中的所有子元素)取消显示指定的编译器警告,且会一直作用于该程序元素的所有子元素。例如,使用 @SuppressWarnings 修饰某个类取消显示某个编译器警告,同时又修饰该类里的某个方法取消显示另一个编译器警告,那么该方法将会同时取消显示这两个编译器警告。
   
如果你确认程序中的警告没有问题,可以不用理会。通常情况下,如果程序中使用没有泛型限制的集合将会引起编译器警告,为了避免这种编译器警告,可以使用 @SuppressWarnings 注解消除这些警告。

注解的使用有以下三种:
   1. 抑制单类型的警告:@SuppressWarnings("unchecked")
   2. 抑制多类型的警告:@SuppressWarnings("unchecked","rawtypes")
   3. 抑制所有类型的警告:@SuppressWarnings("unchecked")
关键字用途
all抑制所有警告
boxing抑制装箱、拆箱操作时候的警告
cast抑制映射相关的警告
dep-ann抑制启用注释的警告
deprecation抑制过期方法警告
fallthrough抑制在 switch 中缺失 breaks 的警告
finally抑制 finally 模块没有返回的警告
hiding抑制相对于隐藏变量的局部变量的警告
incomplete-switch忽略不完整的 switch 语句
nls忽略非 nls 格式的字符
null忽略对 null 的操作
rawtypes使用 generics 时忽略没有指定相应的类型
restriction抑制禁止使用劝阻或禁止引用的警告
serial忽略在 serializable 类中没有声明 serialVersionUID 变量
static-access抑制不正确的静态访问方式警告
synthetic-access抑制子类没有按最优方法访问内部类的警告
unchecked抑制没有进行类型检查操作的警告
unqualified-field-access抑制没有权限访问的域的警告
unused抑制没被使用过的代码的警告
示例:
使用 @SuppressWarnings 注解示例代码如下:
    public class HelloWorld {
        @SuppressWarnings({ "deprecation" })
        public static void main(String[] args) {
            Person p = new Person();
            p.setNameAndAge("C语言中文网", 20);
            p.name = "Java教程";
        }
    }
9.8.4 @SafeVarargs注解
可用 @SafeVarargs 注解抑制编译器警告,代码如下:
    public class HelloWorld {
        public static void main(String[] args) {
            // 传递可变参数,参数是泛型集合
            display(10, 20, 30);
            // 传递可变参数,参数是非泛型集合
            display("10", 20, 30); // 没有@SafeVarargs会有编译警告
        }
        @SafeVarargs
        public static <T> void display(T... array) {
            for (T arg : array) {
                System.out.println(arg.getClass().getName() + ":" + arg);
            }
        }
    }
上述代码在可变参数 display 前添加了 @SafeVarargs 注解,当然也可以使用 @SuppressWarnings("unchecked") 注解,但是两者相比较来说 @SafeVarargs 注解更适合。

注意:@SafeVarargs注解不适用于非 static 或非 final 声明的方法,对于未声明为 staticfinal 的方法,如果要抑制 unchecked 警告,可以使用 @SuppressWarnings 注解。
9.8.5 @FunctionalInterface注解
如果接口中只有一个抽象方法(可以包含多个默认方法或多个 static 方法),那么该接口就是函数式接口。@FunctionalInterface 就是用来指定某个接口必须是函数式接口,所以 @FunInterface 只能修饰接口,不能修饰其它程序元素。
    
函数式接口就是为 Java 8Lambda 表达式准备的,Java 8 允许使用 Lambda 表达式创建函数式接口的实例,因此 Java 8 专门增加了 @FunctionalInterface。
    
使用 @FunctionalInterface 修饰了函数式接口:
    @FunctionalInterface
    public interface FunInterface {
        static void print() {
            System.out.println("C语言中文网");
        }
        default void show() {
            System.out.println("我正在学习C语言中文网Java教程");
        }
        void test(); // 只定义一个抽象方法
    }

编译上面程序,可能丝毫看不出程序中的 @FunctionalInterface 有何作用,因为 @FunctionalInterface 注解的作用只是告诉编译器检查这个接口,保证该接口只能包含一个抽象方法,否则就会编译出错。

@FunctionalInterface 注解主要是帮助程序员避免一些低级错误,例如,在上面的 FunInterface 接口中再增加一个抽象方法 abc(),编译程序时将出现如下错误提示:
    “@FunctionInterface”批注无效;FunInterface不是functional接口
9.8.6 Java元注解作用及使用
元注解是负责对其它注解进行说明的注解,自定义注解时可以使用元注解。Java 5 定义了 4 个注解,分别是 @Documented@Target@Retention@InheritedJava 8 又增加了 @Repeatable@Native 两个注解。这些注解都可以在 java.lang.annotation 包中找到。
9.8.6.1 @Documented
@Documented 是一个标记注解,没有成员变量。用 @Documented 注解修饰的注解类会被 JavaDoc 工具提取成文档。默认情况下,JavaDoc 是不包括注解的,但如果声明注解时指定了 @Documented,就会被 JavaDoc 之类的工具处理,所以注解类型信息就会被包括在生成的帮助文档中。
下面通过示例来了解它的用法,代码如下所示。
    @Documented
    @Target({ ElementType.TYPE, ElementType.METHOD })
    public @interface MyDocumented {
        public String value() default "这是@Documented注解";
    }

测试类:
    @MyDocumented
    public class DocumentedTest {
        /**
         * 测试document
         */
        @MyDocumented
        public String Test() {
            return "C语言中文网Java教程";
        }
    }

打开 Java 文件所在的目录,分别输入如下两条命令行:
    javac MyDocumented.java DocumentedTest.java
    javadoc -d doc MyDocumented.java DocumentedTest.java

运行成功后,打开生成的帮助文档,可以看到在类和方法上都保留了 MyDocument 的注解信息。
9.8.6.2 @Target
@Target 注解用来指定一个注解的使用范围,即被 @Target 修饰的注解可以用在什么地方。@Target 注解有一个成员变量(value)用来设置适用目标,value 是 java.lang.annotation.ElementType 枚举类型的数组,下表为 ElementType 常用的枚举常量。
名称说明
CONSTRUCTOR用于构造方法
FIELD用于成员变量(包括枚举常量)
LOCAL_VARIABLE用于局部变量
METHOD用于方法
PACKAGE用于包
PARAMETER用于类型参数(JDK 1.8新增)
TYPE用于类、接口(包括注解类型)或 enum 声明
示例:
自定义一个 MyTarget 注解,使用范围为方法,代码如下所示。
    @Target({ ElementType.METHOD })
    public @interface MyTarget {
    }
    class Test {
        @MyTarget
        String name;
    }

如上代码第 6 行会编译错误,错误信息为:
    The annotation @MyTarget is disallowed for this location
        
提示此位置不允许使用注解 @MyDocumented@MyTarget 不能修饰成员变量,只能修饰方法。
9.8.6.3 @Retention
@Retention 用于描述注解的生命周期,也就是该注解被保留的时间长短。@Retention 注解中的成员变量(value)用来设置保留策略,value 是 java.lang.annotation.RetentionPolicy 枚举类型,RetentionPolicy3 个枚举常量,如下所示。
    SOURCE:在源文件中有效(即源文件保留)
    CLASS:在 class 文件中有效(即 class 保留)
    RUNTIME:在运行时有效(即运行时保留)

生命周期大小排序为 SOURCE < CLASS < RUNTIME,前者能使用的地方后者一定也能使用。如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解;如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用 CLASS 注解;如果只是做一些检查性的操作,比如 @Override@SuppressWarnings,则可选用 SOURCE 注解。
9.8.6.4 @Inherited
@Inherited 是一个标记注解,用来指定该注解可以被继承。使用 @Inherited 注解的 Class 类,表示这个注解可以被用于该 Class 类的子类。就是说如果某个类使用了被 @Inherited 修饰的注解,则其子类将自动具有该注解。 
示例:
创建一个自定义注解,代码如下所示:
    @Target({ ElementType.TYPE })
    @Inherited
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyInherited {
    }

测试类代码如下:
    @MyInherited
    public class TestA {
        public static void main(String[] args) {
            System.out.println(TestA.class.getAnnotation(MyInherited.class));
            System.out.println(TestB.class.getAnnotation(MyInherited.class));
            System.out.println(TestC.class.getAnnotation(MyInherited.class));
        }
    }
    class TestB extends TestA {
    }
    class TestC extends TestB {
    }
9.8.6.5 @Repeatable
@Repeatable 注解是 Java 8 新增加的,它允许在相同的程序元素中重复注解,在需要对同一种注解多次使用时,往往需要借助 @Repeatable 注解。Java 8 版本以前,同一个程序元素前最多只能有一个相同类型的注解,如果需要在同一个元素前使用多个相同类型的注解,则必须使用注解“容器”。 
示例:
Java 8 之前的做法:
    public @interface Roles {
        Role[] roles();
    }
    public @interface Roles {
        Role[] value();
    }
    public class RoleTest {
        @Roles(roles = {@Role(roleName = "role1"), @Role(roleName = "role2")})
        public String doString(){
            return "这是C语言中国网Java教程";
        }
    }

Java 8 之后增加了重复注解,使用方式如下:
    public @interface Roles {
        Role[] value();
    }
    @Repeatable(Roles.class)
    public @interface Role {
        String roleName();
    }
    public class RoleTest {
        @Role(roleName = "role1")
        @Role(roleName = "role2")
        public String doString(){
            return "这是C语言中文网Java教程";
        }
    }
不同的地方是,创建重复注解 Role 时加上了 @Repeatable 注解,指向存储注解 Roles,这样在使用时就可以直接重复使用 Role 注解。从上面例子看出,使用 @Repeatable 注解更符合常规思维,可读性强一点。

两种方法获得的效果相同。重复注解只是一种简化写法,这种简化写法是一种假象,多个重复注解其实会被作为“容器”注解的 value 成员的数组元素处理。
9.8.6.6 @Native
使用 @Native 注解修饰成员变量,则表示这个变量可以被本地代码引用,常常被代码生成工具使用。对于 @Native 注解不常使用,了解即可。
9.8.7 自定义注解
声明自定义注解使用 @interface 关键字(interface 关键字前加 @ 符号)实现。定义注解与定义接口非常像,如下代码可定义一个简单形式的注解类型。
    // 定义一个简单的注解类型
    public @interface Test {
    }
定义注解和定义类相似,注解前面的访问修饰符和类一样有两种,分别是公有访问权限(public)和默认访问权限(默认不写)。一个源程序文件中可以声明多个注解,但只能有一个是公有访问权限的注解。且源程序文件命名和公有访问权限的注解名一致。

不包含任何成员变量的注解称为标记注解,例如上面声明的 Test 注解以及基本注解中的 @Override 注解都属于标记注解。根据需要,注解中可以定义成员变量,成员变量以无形参的方法形式来声明,其方法名和返回值定义了该成员变量的名字和类型。代码如下所示:
    public @interface MyTag {
        // 定义带两个成员变量的注解
        // 注解中的成员变量以方法的形式来定义
        String name();
        int age();
    }

以上代码中声明了一个 MyTag 注解,定义了两个成员变量,分别是 name 和 age。成员变量也可以有访问权限修饰符,但是只能有公有权限和默认权限。

如果在注解里定义了成员变量,那么使用该注解时就应该为它的成员变量指定值,如下代码所示。
    public class Test {
        // 使用带成员变量的注解时,需要为成员变量赋值
        @MyTag(name="xx", age=6)
        public void info() {
            ...
        }
        ...
    }

注解中的成员变量也可以有默认值,可使用 default 关键字。如下代码定义了 @MyTag 注解,该注解里包含了 name 和 age 两个成员变量。
    public @interface MyTag {
        // 定义了两个成员变量的注解
        // 使用default为两个成员变量指定初始值
        String name() default "C语言中文网";
        int age() default 7;
    }

如果为注解的成员变量指定了默认值,那么使用该注解时就可以不为这些成员变量赋值,而是直接使用默认值。
    public class Test {
        // 使用带成员变量的注解
        // MyTag注释的成员变量有默认值,所以可以不为它的成员变量赋值
        @MyTag
        public void info() {
            ...
        }
        ...
    }

当然也可以在使用 MyTag 注解时为成员变量指定值,如果为 MyTag 的成员变量指定了值,则默认值不会起作用。
    
根据注解是否包含成员变量,可以分为如下两类:
    1. 标记注解:没有定义成员变量的注解类型被称为标记注解。这种注解仅利用自身的存在与否来提供信息,如前面介绍的 @Override@Test 等都是标记注解。
    2. 元数据注解:包含成员变量的注解,因为它们可以接受更多的元数据,所以也被称为元数据注解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cllmsy_K

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值