20210320Java枚举类与注解

Java枚举类与注解

编辑时间:2021/03/20

读完本节:大概花费40分钟,共4339词

1.枚举类的使用
  1. 枚举类:类的对象只有有限个,确定个;当需要定义一组常量时常使用枚举类实现

  2. JDK1.5之前需要自定义枚举类;JDK1.5之后支持使用enum关键字用于定义枚举类,enum定义的枚举类默认继承于java.lang.Enum类

  3. 若枚举类只有一个对象,则可以作为一种单例模式的实现方式

  4. 枚举类的属性:

    1. 枚举类对象的属性不允许被改动,所以应该使用private final修饰
    2. 枚举类的使用:private final 修饰的属性应该在构造器中为其赋值
    3. 若枚举类显式的定义了带参数的构造器,则在列出枚举类值时也必须对应的传入参数
  5. 自定义枚举类的使用步骤:

    1. 声明对象的属性,使用private final修饰
    2. 私有化类的构造器,并给对象属性赋值
    3. 提供当前枚举类的多个枚举类常量,使用public static final修饰
    4. 其他,获取枚举类对象的属性或方法(例如toString()方法)
  6. enum关键字定义的枚举类的使用步骤:

    1. 提供当前枚举类的枚举类常量,多个对象之间使用“,”隔开;末尾对象使用“;”结束

    2. 声明对象的属性,使用private final修饰

    3. 私有化类的构造器,并给对象属性赋值

    4. 其他,获取枚举类的属性或方法,toString()方法一般在enum定义的枚举类中不重写,使用java.lang.Enum中定义的toString()方法即可获取对象名

    5. 格式

      enum 枚举类名{

      ​ 大写的枚举类常量名1(对象属性1,对象属性2…),

      ​ 大写的枚举类常量名2(对象属性1,对象属性2…),

      ​ 大写的枚举类常量名3(对象属性1,对象属性2…);

      ​ private final 类型 属性;

      ​ private 枚举类名(形参1,形参2…){

      ​ this.枚举类常量属性1 = 形参1;

      ​ this.枚举类常量属性2 = 形参2;

      ​ …

      ​ }

      ​ public 类型 getXxx1(){

      ​ return Xxx1;

      ​ }

      ​ public 类型 getXxx2(){

      ​ return Xxx2;

      ​ }

      ​ …

      }

  7. 枚举类Enum的主要方法

    1. valueOf():传递枚举类型的Class对象和枚举类常量名称给静态方法valueOf(),会得到与参数匹配的枚举常量,如果没找到参数匹配的枚举常量,则抛出异常:IllegalArgumentException
    2. toString():的套当前枚举常量的名称,可以通过重写这个方法来使得得到的结果更易读
    3. equals():在枚举类型中可以直接使用“”来比较两个枚举常量是否相等。Enum提供的这个equals()方法,也是直接使用“”实现的。它的存在是为了在Set、List和Map中使用,在枚举类中的equals是不可变的
    4. hashCode():Enum实现了hashCode()来和equals()保持一致。它也是不可变的
    5. getDeciaringClass():的到枚举类常量所属类型的Class对象。可以用它来判断两个枚举类常量是否属于同一个枚举类型
    6. name():得到当前枚举类常量的名称。建议使用toString()
    7. ordinal():得到当前枚举类常量的次序
    8. compareTo():枚举类型实现了Comparable接口,这样可以比较两个枚举常量的大小,按照声明顺序排列
    9. clone():枚举类型不能被clone。为了防止子类实现clone方法,Enum实现了一个仅抛出Clone Not Supported Exception异常的不变clone()
  8. 使用enum关键字定义的枚举类实现接口的情况

    1. 情况一:实现接口,在enum类中实现抽象方法
    2. 情况二:让枚举类的枚举常量分别实现接口中的抽象方法,这样可以做到不同的枚举常量实现位于接口中的不同的抽象方法
  9. 使用枚举类改善项目20210313开发团队调度软件中的员工状态

    将Status.java更改为枚举类

    package team.service;
    
    /**
     * @Author: xuehai.XUE
     * @MailBox: xuehai.xue@qq.com
     * @Date: 2021/3/12 16:04
     * @Description: 表示员工状态
     */
    public enum Status {
        /** 员工状态 */
        FREE,
        BUSY,
        VOCATION;
        
    //    private final String S;
    //    private Status(String S){
    //        this.S = S;
    //    }
    //
    //    /**
    //     * 空闲
    //     */
    //    public static final Status FREE = new Status("FREE");
    //    /**
    //     * 已加入开发团队
    //     */
    //    public static final Status BUSY = new Status("BUSY");
    //    /**
    //     * 正在休假
    //     */
    //    public static final Status VOCATION = new Status("VOCATION");
    //
    //    public String getS() {
    //        return S;
    //    }
    //
    //    @Override
    //    public String toString() {
    //        return S;
    //    }
    }
    

    修改TeamService中的addMember方法中的自定义枚举类为enum定义的枚举类

    package team.service;
    
    
    import team.domain.Architect;
    import team.domain.Designer;
    import team.domain.Employee;
    import team.domain.Programmer;
    
    /**
     * @Author: xuehai.XUE
     * @MailBox: xuehai.xue@qq.com
     * @Date: 2021/3/12 19:33
     * @Description:
     */
    public class TeamService {
        /**
         * count: 给memberId赋值使用
         */
        private static int counter = 1;
        /**
         * MAX_MEMBER: 限制开发团队的人数
         */
        private final int MAX_MEMBER = 5;
        /**
         * team: 保存开发团队的成员
         */
        private Programmer[] team = new Programmer[MAX_MEMBER];
        /**
         * total: 记录开发团队中实际的人数
         */
        private int total = 0;
    
        public TeamService() {
            super();
        }
    
        /**
         * 获取开发团队中的所有成员
         * @return 返回包含所有成员的数组
         */
        public Programmer[] getTeam(){
            //恰好保证this.team中非空的值赋给当前创建的数组
            Programmer[] team = new Programmer[total];
            for(int i = 0;i < team.length;i++){
                team[i] = this.team[i];
            }
            return team;
        }
    
        /**
         * 添加团队成员
         * @param e 用户想要添加的成员
         * @throws TeamException
         */
        public void addMember(Employee e) throws TeamException {
            //以下是添加不成功的可能:
            //成员已满,无法添加
            if(total >= MAX_MEMBER){
                throw new TeamException("成员已满,无法添加");
            }
    
            //该成员不是开发人员,无法添加
            if(!(e instanceof Programmer)){
                throw new TeamException("该成员不是开发人员,无法添加");
            }
    
            //该员工已经在被开发团队中
            if(isExist(e)){
                throw new TeamException("该员工已经在开发团队中");
            }
    
            //该员工已经是某开发团队的成员
            //该员工正在休假,无法添加
            Programmer p = (Programmer)e;
            //一定不会出现ClassCastException,
            //因为上述的if条件已经判断了能到这的e都是Programmer
            //自定义枚举类写法
    //        if("BUSY".equals(p.getStatus().getS())){
    //            //将"BUSY"写在前面不容易导致空指针异常,
    //            //Object的equals方法容易抛空指针异常,应使用常量或确定有值的对象来调用equals
    //            throw new TeamException("该员工已经是某开发团队的成员");
    //        }else if("VOCATION".equals(p.getStatus().getS())){
    //            throw new TeamException("该员工正在休假,无法添加");
    //        }
            //使用enum关键字定义枚举类
            switch (p.getStatus()){
                /* 当员工处于BUSY状态时,抛出异常 */
                case BUSY:
                    throw new TeamException("该员工已经是某开发团队的成员");
                /* 当员工处于BUSY状态时,抛出异常 */
                case VOCATION:
                    throw new TeamException("该员工正在休假,无法添加");
                default:
            }
    
            //团队中最多只能有一名架构师
            //团队中最多只能有两名设计师
            //团队中最多只能有三名程序员
            //获取team中已有成员中架构师、设计师、程序员的人数
            int numOfArch = 0, numOfDes = 0, numOfPro = 0;
            for(int i = 0;i < total;i++){
                if(team[i] instanceof Architect){
                    numOfArch++;
                }else if(team[i] instanceof Designer){
                    numOfDes++;
                }else if(team[i] instanceof Programmer){
                    numOfPro++;
                }
            }
    
            //判断当前对象属于哪类员工,如果该类员工人数已满则抛出异常
            if(p instanceof Architect){
                if(numOfArch >= 1){
                    throw new TeamException("团队中最多只能有一名架构师");
                }
            }if(p instanceof Designer){
                if(numOfDes >= 2){
                    throw new TeamException("团队中最多只能有两名设计师");
                }
            }if(p instanceof Programmer){
                if(numOfPro >= 3){
                    throw new TeamException("团队中最多只能有三名程序员");
                }
            }
    
            //走完上述程序没有抛出异常,则将p加入到现有的team
            team[total++] = p;
            //p的属性赋值
            p.setStatus(Status.BUSY);
            p.setMemberId(counter++);
        }
    
        /**
         * 该方法用于判断团队中是否存在e员工
         * @param e 指定的员工
         * @return 如果e员工存在则返回true否则返回false
         */
        private boolean isExist(Employee e) {
            for(int i = 0;i < total;i++){
                if(team[i].getId() == e.getId()){
                    return true;
                }
            }
            return false;
        }
    
        /**
         * 删除用户想要删除的团队成员
         * @param memberId 用户指定的用户成员id
         */
        public void removeMember(int memberId) throws TeamException {
            int i = 0;
            for(;i < total;i++){
                if(team[i].getMemberId() == memberId){
                    //更改用户状态
                    team[i].setStatus(Status.FREE);
                    break;
                }
            }
    
            //通过i判断for循环是正常遍历结束还是遇到break结束
            //由此可以设置未找到memberId的异常情况
            if(i == total){
                throw new TeamException("找不到指定TID的员工,删除失败");
            }
    
            //此时i保存的刚好是待删用户的数组索引
            for(int j = i + 1;j < total;j++){
                team[j - 1] = team[j];
            }
            //或
    //        for(int j = i;j < total - 1;j++){
    //            team[j] = team[j + 1];
    //        }
    
            //将末尾元素设为空
            team[--total] = null;
            //或写法二:
    //        team[total - 1] = null;
    //        total--;
    
        }
    
    }
    
2.注解(Annotaion)
  1. 概述:

    1. 从JDK1.5开始,Java增加了对元数据(MataData)的支持,也就是Annotation(注解)
    2. Annotation实际上是代码中的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用Annotation,可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或者进行部署
    3. Annotation可以像修饰符一样被使用,可以用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明,这些信息被保存在Annotation的“name = value”对中
    4. 在JavaSE中,注解的使用目的比较简单,例如标记过时的功能、忽略警告等。在JavaEE、Andriod中注解占用了更重要的角色,如用来配置应用程序的任何切面,代替JavaEE旧版中所遗留的繁冗代码和XML配置等。
    5. 现在的开发模式都是基于注解大的,JPA是基于注解的,Spring2.5以上都是基于注解的,Hibernate3.X以后也是基于注解的,Struts2有一部分也是基于注解的,一定程度上可以说:框架 = 注解 + 反射 + 设计模式。
  2. 常见的Annotation示例:

    1. 使用Annotation时,要在前面增加“@”符号,并把该Annotation当成一个修饰符使用。用于修饰它支持的程序元素。

    2. 生成文档相关注释:@author、@version、@see(参考转向)、@since、@param、@return、@exception。其中@param、@return、@exception三个标记只适用于方法,@exception对方法可能抛出的异常进行说明,如果方法没有用throws显示抛出异常就不能写;@param的格式要求:@param 形参名 形参类型 形参说明;@return的格式要求:@return 返回值类型 返回值说明;@exception的格式要求:@exception 异常类型 异常说明;@param和@exception可以并列多个

    3. 在编译时进行格式检查:@Override:限定重写父类的方法,该注解只能用于方法、@Deprecated:用于表示所修饰的元素(类、方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择、@SuppressWarning:抑制编译器警告

    4. 跟踪代码依赖性,实现替代配置文件的功能:

      1. Servlet3.0提供了注解(annotation),使得不在需要在web.xml文件中进行Servlet的部署

        @webServlet("/login")
        public class LoginServlet extends HttpServlet{
            private static final long serialVersionUID = 1L;
            protected void doGet(HttpServletRequset request, HttpServletResponse response) throws ServletException, IOException{}
            protected void doPost(HttpServletRequset request, HttpServletResponse response) throws ServletException, IOException{
                doGet(request, response){
                    
                }
            }
        }
        

        使用了@webServlet注解之后就不需要进行下面的xml配置

        <servlet>
            <servlet-name>LoginServlet</servlet-name>
            <servlet-class>com.servlet.LoginServlet</servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>LoginServlet</servlet-name>
            <url-pattern>/login</url-pattern>
        </servlet-mapping>
        
      2. Spring框架中关于“事务”的管理

        @Transational(propagation=Propagation.REQUIRES_NEW,isolation=Isolation.READ_COMMITED,readOnly=false,timeout=3)
        public void buyBook(String username, String isbn){
            //1.查询书的单价
            int price = bookShopDao.findBookPriceByIsbn(isbn);
            //2.更新库存
            bookShopDao.updateBookStock(isbn);
            //3.更新用户的余额
            bookShopDao.updateUserAccount(username, price);
        }
        
        <!-- 配置事务属性 -->
        <tx:advice transaction-manager="dataSourseTransactionManager"id="texAdvice">
        	<tx:attributes>
            	<!-- 配置每个方法使用的事务属性 -->
                <tx:method name="buyBook" propagation="REQUISE_NEW" isolation="READ_COMMITED" read-only="false" timeout="3" />
            </tx:attributes>
        </tx:advice>
        
      3. JUnit单元测试中也有大量的注解的使用

        1. @Test:标记在非静态的方法上。只有标记@Test的方法才能被作为一个测试方法单独测试。一个类中可以有多个@Test标记的方法。运行时如果执行运行其中以一个@Test标记的帆帆发,那么选择这个方法名然后直接运行,否则整个类标记了@Test的方法都会被执行。@Test(timeout = 1000):设置超时时间,如果测试时间超过了用户定义的timeout,则测试失败;@Test(excepted):声明会发生的异常,比如@Test(excepted = Exception.class)
        2. @BeforeClass:标记在静态方法上。因为这个方法只执行一次,在类的初始化时执行
        3. @AfterClass:标记在静态方法上。因为这个方法只执行一次,在所有方法执行完成之后执行
        4. @Before:标记在非静态方法上。在@Test方法前面执行,而且时在每一个@Test方法前面执行
        5. @After:标记在非静态方法上。在@Test方法后面执行,而且时在每一个@Test方法后面执行
        6. @Ignore:标记在本次不参与测试的方法上。这个注解的含义就是“某些方法尚未完成,暂不参与本次测试”
        7. @BeforeClass、@AfterClass、@Before、@After、@Ignore都是配合@Test使用的,单独使用没有意义
  3. 如何自定义注解:

    1. 定义新的Annotation类型使用@interface关键字

    2. 自定义注解自动继承了java.lang.annotation.Annotation接口

    3. Annotation的成员变量在Annotation定义中以无参数方法的形式来声明。其方法名和返回值定义了该成员的名字和类型。称为配置参数。类型只能是八种基本数据类型:String、Class、enum、Annotation、以上类型的数组

    4. 可以在定义Annotation的成员变量时为其指定初始值,指定成员变量的初始值可以使用default关键字

    5. 如果只有一个参数成员,建议使用参数名为value

    6. 如果定义的注解含有配置阐述,那么使用时必须指定参数值,除非它有默认值。格式时“参数名= 参数值”,如果只有一个参数成员,且名称为value,则可以省略“value = ”

    7. 没有成员定义的Annotation称为标记,包含成员变量的Annotation称为“元数据Annotation”

    8. 自定义注解必须配上注解的信息处理流程才有意义

    9. 如何自定义注解:①注解声明为@interface;②内部定义成员,通常使用value表示;③可以指定成员的默认值,使用default定义;④如果自定义注解没有成员,表明是一个标识作用,如果注解有成员,在使用注解的时候需要指明其成员的值

    10. JDK提供的4种元注解@Retention;@Target;@Document;@Inherited

      1. @Retention:只能用于修饰一个Annotation定义,用于指定该Annotation的生命周期。@Retention包含一个RetentionPolicy类型的成员变量,使用@Retention时必须为该value成员变量指定值:

        1. RetentionPolicy.SOURSE:在源文件中有效(即源文件保留),编译器直接丢弃这种策略的注解。
        2. RetentionPolicy.CLASS:在class文件中有效(即class保留),运行Java程序时,JVM不会保留注解。这是默认的@Retention的value值
        3. RetentionPolicy.RUNTIME:在运行时有效(即运行时保留),当运行Java程序时,JVM会保留注解,程序可以通过反射获取该注解
      2. @Target:用于修饰Annotation定义,用于指定被修饰的Annotation能用于修饰哪些程序元素。@Target也包含一个名为value的成员变量。

        value取值(ElementType)描述
        CONSTRUCTOR用于描述构造器
        PACKAGE用于描述包
        FIELD用于描述域
        PARAMETER用于描述参数
        LOCAL_VARIABLE用于描述局部变量
        TYPE用于描述类、接口(包括注解类型)或enum声明
        METHOD用于描述方法
      3. @Documented:用于指定该元注解Annotation修饰的Annotation类将被Javadoc工具提取成文档。默认情况下,Javadoc时不包括注解的。定义为Document的注解必须设置Retention的值为RUNTIME

      4. @Inherited:被它修饰的Annotation将具有继承性。如果某个类使用了@Inherited修饰的Annotation,则其子类自动具有该注解

  4. JDK1.8后注解的新特性:可重复注解、注解类型

    1. 可重复注解:①在自定义注解上方声明@Repeatable,成员值为注解类;②可重复注解的元注解配置应该和注解相同

    2. 类型注解:在JDK1.8之后,关于元注解@Target的参数类型ElementType枚举值多了两个:TYPE_PARAMETER,TYPR_USE.

      在JDK1.8前,注解只能是在声明的地方使用,JDK1.8以后注解可以应用在任何地方:

      1. ElementType.TYPE_PARAMETER表示该注解能写在类型变量的声明语句种(如:泛型声明)

      2. ElementType.TYPE_USE表示该注解能写在使用类型的任何语句中

        public class TestTypeDefine<@TypeDefine() U>{
            private U u;
            public <@TypeDefine() T> void test(T t){
                
            }
        }
        @Target({ElementTYpe.TYPE_PARAMETER})
        @interface TypeDefine{
            
        }
        

image-20210303180846928

STAY ANGER!!!
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值