一、枚举

概念

枚举类型是Java 5中新增特性的一部分,它是一种特殊的数据类型,之所以特殊是因为它既是一种类(class)类型却又比类类型多了些特殊的约束,但是这些约束的存在也造就了枚举类型的简洁性、安全性以及便捷性。
枚举在各个语言当中都有着广泛的应用,通常用来表示诸如颜色、方式、类别、状态等等数目有限、形式离散、表达又极为明确的量。Java从JDK5开始,引入了对枚举的支持。

为什么使用枚举

在枚举出现之前,如果想要表示一组特定的离散值,往往使用一些常量。

示例1

  1. package com.alpaak.part11.demo1;
  2. /**
  3. * 实体对象
  4. */
  5. public class Entity1 {
  6. public static final int VIDEO = 1;//视频
  7. public static final int AUDIO = 2;//音频
  8. public static final int TEXT = 3;//文字
  9. public static final int IMAGE = 4;//图片
  10. private int id;
  11. private int type;
  12. public int getId() {
  13. return id;
  14. }
  15. public void setId(int id) {
  16. this.id = id;
  17. }
  18. public int getType() {
  19. return type;
  20. }
  21. public void setType(int type) {
  22. this.type = type;
  23. }
  24. @Override
  25. public String toString() {
  26. return "Entity{" +
  27. "id=" + id +
  28. ", type=" + type +
  29. '}';
  30. }
  31. }

例如,针对上述的Entity类,如果要对Entity对象的type属性进行赋值,一般会采用如下方法:

package com.alpaak.part11.demo1;

/**
 * 调用测试
 */
public class Demo1 {
    public static void main(String[] args) {
        Entity1 e = new Entity1();
        e.setId(10);
        //e.setType(2);
        //而这样的话,问题又来了。这样做,客户端必须对这些常量去建立理解,才能了解如何去使用这个东西。
        //说白了,在调用的时候,如果用户不到Entity类中去看看,还真不知道这个参数应该怎么传、怎么调
        e.setType(Entity1.AUDIO);
        System.out.println(e);
    }
}

这样做的缺点有:
(1)代码可读性差、易用性低。由于setType()方法的参数是int型的,在阅读代码的时候往往会让读者感到一头雾水,根本不明白这个2到底是什么意思,代表的是什么类型。
(2)类型不安全。在用户去调用的时候,必须保证类型完全一致,同时取值范围也要正确。像是setType(-1)这样的调用是合法的,但它并不合理,今后会为程序带来种种问题。
(3)耦合性高,扩展性差。假如,因为某些原因,需要修改Entity类中常量的值,那么,所有用到这些常量的代码也就都需要修改——当然,要仔细地修改,万一漏了一个,那可不是开玩笑的。同时,这样做也不利于扩展。

使用枚举

枚举(在Jave中简称为enum)是一个特定类型的类。所有枚举都是Java中的新类java.lang.Enum的隐式子类。此类不能手工进行子类定义。一个简单的枚举可以是这样:

示例2

public enum TypeEnum {
    VIDEO,AUDIO,TEXT,IMAGE;
}

上面的Entity类就可以改成这样:

public class Entity2 {

    private int id;
    private TypeEnum type;

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }

    public TypeEnum getType() {
        return type;
    }
    public void setType(TypeEnum type) {
        this.type = type;
    }
}

在为Entity对象赋值的时候,就可以这样:

package com.alpaak.part11.demo2;

/**
 * 调用测试
 */
public class Demo2 {
    public static void main(String[] args) {
        Entity2 e = new Entity2();
        e.setId(10);
        e.setType(TypeEnum.IMAGE);
        System.out.println(e);
    }
}

在调用setType()时,可选值只有四个,否则会出现编译错误,因此可以看出,枚举是类型安全的,不会出现取值范围错误的问题。同时,客户端不需要建立对枚举中常量值的了解,使用起来很方便,并且可以容易地对枚举进行修改,而无需修改客户端。如果常量从枚举中被删除了,那么客户端将会失败并且将会收到一个错误消息。
在Java中一个枚举就是一个类,它也可以有属性和方法,并且实现接口。只是所有的枚举都继承自java.lang.Enum类,因此enum不可以再继承其他的类。

枚举中声明属性和方法

public enum TypeEnum {
    VIDEO(1), AUDIO(2), TEXT(3), IMAGE(4);

    int value;

    TypeEnum(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }
}

如果要为每个枚举值指定属性,则在枚举中必须声明一个参数为属性对应类型的构造方法(不能是public)。否则编译器将给出The constructor TypeEnum(int, String) is undefined的错误。在此例中,属性为int型,因此构造方法应当为int型。除此之外,还可以为枚举指定多个属性

枚举构造器为什么不能是public?
如果其含有public构造器,那么在类的外部就可以通过这个构造器来新建实例,显然这时实例的数量和值就不固定了,这与定义枚举类的初衷相矛盾,为了避免这种形象,就对枚举类的构造器默认使用private修饰。如果为枚举类的构造器显式指定其它访问控制符,则会编译出错。

多属性的应用

示例3

package com.alpaak.part11.demo3;

import java.util.HashMap;

public enum TypeEnum {
    VIDEO(1, "视频"), AUDIO(2, "音频"), TEXT(3, "文本"), IMAGE(4, "图像");

    int value;
    String name;

    TypeEnum(int value, String name) {
        this.value = value;
        this.name = name;
    }

    public int getValue() {
        return value;
    }

    public String getName() {
        return name;
    }

    //将数值1,2,3一起封装到HashMap中
    private static final HashMap<Integer, TypeEnum> valueMap = new HashMap<Integer, TypeEnum>();
    //静态代码块
    static{
        for (TypeEnum item : TypeEnum.values()) {
            valueMap.put(item.getValue(), item);
        }
    }

    /**
     * 获得对象
     *
     * @param code
     * @return
     */
    public static TypeEnum getObject(Integer code) {
        return valueMap.get(code);
    }
}
package com.alpaak.part11.demo3;

public class Entity3 {

    private int id;
    private TypeEnum3 type;

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }

    public TypeEnum3 getType() {
        return type;
    }
    public void setType(TypeEnum3 type) {
        this.type = type;
    }

    @Override
    public String toString() {
        return "Entity3{" +
                "id=" + id +
                ", type=" + type +
                '}';
    }
}
package com.alpaak.part11.demo3;

/**
 * 调用测试
 */
public class Demo3 {
    public static void main(String[] args) {
        Entity3 e = new Entity3();
        e.setId(10);
//        e.setType(TypeEnum.IMAGE);

        TypeEnum3 type = TypeEnum3.getObject(4);
        e.setType(type);

        System.out.println(e);
    }
}

二、注解

概念

概念:说明程序的。给计算机看的

注释:用文字描述程序的。给程序员看的

定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
使用注解:@注解名称

JDK中预定义的一些注解

  • @Override :检测被该注解标注的方法是否是继承自父类(接口)的
  • @Deprecated:该注解标注的内容,表示已过时
  • @SuppressWarnings:压制警告
  • 一般传递参数all @SuppressWarnings(“all”)

    示例4:

    ```java package com.alpaak.part11.demo4;

/**

  • 注解示例 */

@SuppressWarnings(“all”) public class AnnotationDemo1 {

@Override
public String toString() {
    return super.toString();
}

@Deprecated
public void test(){
    System.out.println("this is deprecated...");
}

public void test2(){
    System.out.println("this is old....");
}


public static void main(String[] args) {
    AnnotationDemo1 annotationDemo1 = new AnnotationDemo1();
    annotationDemo1.test();
}

}

<a name="OzANR"></a>
## 自定义注解
语法格式:
```java
元注解
public @interface 注解名称{
属性列表;
}
  • 本质:注解本质上就是一个接口,该接口默认继承Annotation接口
  • public interface MyAnno extends java.lang.annotation.Annotation {}
  • 属性:接口中的抽象方法
  1. 属性的返回值类型有下列取值
    • 基本数据类型
    • String
    • 枚举
    • 注解
    • 以上类型的数组
  2. 定义了属性,在使用时需要给属性赋值
    1. 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。
    2. 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可。
    3. 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略

      示例5:

      ```java package com.alpaak.part11.demo5;

public enum Sex { Male,Female; }

```java
package com.alpaak.part11.demo5;

public @interface MyAnno {
//    int show1();
//
//    boolean show2();

    /*
    属性的返回值类型有下列取值
    * 基本数据类型
    * String
    * 枚举
    * 注解
    * 以上类型的数组
     */
//    int age();
//    String name();
//    String url() default "/*";
//    Sex sex() default Sex.Female;
//    MyAnno2 anno2();
    String[] arr();
    String value();//value属性非常特殊,当只有一个属性的时候,可以省略
    String url() default "/*";
    Sex sex() default Sex.Female;
}

元注解:用于描述注解的注解

  • @Target:描述注解能够作用的位置
    • ElementType取值:
    • ElementType.TYPE 指定注解只能修饰类或者接口 —重点
    • ElementType.FIELD 指定注解只能修饰成员属性 —重点
    • ElementType.METHOD 指定注解只能修饰方法 —重点
    • ElementType.PARAMETER 指定注解只能修饰方法的参数
    • ElementType.CONSTRUCTOR 指定注解只能修饰构造方法
    • ElementType.LOCAL_VARIABLE 指定注解只能修饰局部变量
    • ElementType.TYPE_USE 指定注解能修饰所有的 —JDK1.8后拥有的
  • @Retention:描述注解被保留的阶段
    • RetentionPolicy.SOURCE 注解信息保留在源文件中
    • RetentionPolicy.CLASS 保留在class文件中
    • RetentionPolicy.RUNTIME 注解信息在运行时保留,搭配反射使用 —重点

      示例6:

      ```java package com.alpaak.part11.demo6;

import java.lang.annotation.*;

@Target({ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})//标识注解作用的位置

@Retention(RetentionPolicy.RUNTIME)//描述注解被保留的阶段 //当前被描述的注解,会保留到class字节码文件中,并被JVM读取到 public @interface MyAnno2 {

}

```java
package com.alpaak.part11.demo6;

@MyAnno2
public class Worker {


    private String name;

    @MyAnno2
    public void show(){
        System.out.println("show.....");
    }
}

三、内部类

什么是内部类

可以将一个类的定义放在里另一个类的内部,这就是内部类。广义上我们将内部类分为四种:成员内部类、静态内部类、局部(方法)内部类、匿名内部类。
语法:

/**
*    我是一个外部类(外部是相对内部而言)
*/
public class Outer{
    /**
    *    我是一个内部类
    */
    class Inner{
    //...
    }
}

使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。

如何使用内部类

成员内部类

1、外部类、内部类

示例7:

package com.alpaak.part11.demo7;

/**
 * 外部类、成员内部类的定义
 */
public class Outer {

    private int outerVariable = 1;
    private int commonVariable = 2;
    private static int outerStaticVariable = 3;
    //省略getter/setter

    /**
     * 成员方法
     */
    public void outerMethod() {
        System.out.println("我是外部类的outerMethod方法");
    }

    /**
     * 静态方法
     */
    public static void outerStaticMethod() {
        System.out.println("我是外部类的outerStaticMethod静态方法");
    }

    /**
     * 内部类
     */
    public class Inner {

        private int commonVariable = 20;

        /**
         * 构造方法
         */
        public Inner() {
        }

        /**
         * 成员方法,访问外部类信息(属性、方法)
         */
        public void innerShow() {
            //当和外部类冲突时,直接引用属性名,是内部类的成员属性
            System.out.println("内部的commonVariable:" + commonVariable);
            //内部类访问外部属性
            System.out.println("outerVariable:" + outerVariable);
            //当和外部类属性名重叠时,可通过外部类名.this.属性名
            System.out.println("外部的commonVariable:" + Outer.this.commonVariable);
            System.out.println("outerStaticVariable:" + outerStaticVariable);
            //访问外部类的方法
            outerMethod();
            outerStaticMethod();
        }
    }

    /**
     *    外部类访问内部类信息
     */
    public void outerShow() {
        Inner inner = new Inner();
        inner.innerShow();
    }

//  可在Outer中定义get方法,获得Inner对象,那么使用时,只需outer.getInnerInstance()即可。
    public Inner getInnerInstance(){
        return new Inner();
    }
}

2、其他类使用成员内部类

package com.alpaak.part11.demo7;

/*
*    其他类使用成员内部类
 */
public class Other {

    public static void main(String[] args) {
        //外部类对象
        Outer outer = new Outer();

//        Outer.Inner inner = outer.getInnerInstance();
        //创造内部类对象
        Outer.Inner inner = outer.new Inner();
        inner.innerShow();

/*
//        可在Outer中定义get方法,获得Inner对象,那么使用时,只需outer.getInnerInstance()即可。
        public Inner getInnerInstance(Inner类的构造方法参数){
         return new Inner(参数);
        }*/

    }
}

3、小结:【成员内部类当成Outer的成员信息存在 】

  • 可以是任何的访问修饰符。
  • 外部类如何访问内部类信息,必须new之后通过(.)操作符访问。
  • 内部类可以直接使用外部类的任何信息,如果属性或者方法发生冲突,调用外部类.this.属性或者方法。
  • 其它类如何访问内部类:
                   Outer outer=new Outer();
                   //创造内部类对象
                   Outer.Inner inner=outer.new Inner();
                   inner.inner_show();
    

    静态内部类

    1、外部类、内部类

    示例8:

    ```java package com.alpaak.part11.demo8;

/**

  • 外部类、内部类定义 */ public class Outer {

    private int outerVariable = 1;

    /**

    • 外部类定义的属性(重名) */ private int commonVariable = 2;

      private static int outerStaticVariable = 3;

      static { System.out.println(“2 Outer的静态块被执行了……”); }

      public Outer() { System.out.println(“外部构造器被执行了”); }

      /**

    • 成员方法 */ public void outerMothod() { System.out.println(“我是外部类的outerMethod方法”); }

      /*

      • 静态方法 */ public static void outerStaticMethod() { System.out.println(“3 我是外部类的outerStaticMethod静态方法”); }
/**
 * 静态内部类
 */
public static class Inner {
    /**
     * 成员信息
     */
    private int innerVariable = 10;
    //和外部的变量命名冲突
    private int commonVariable = 20;

    static {
        System.out.println("1 Outer.Inner的静态块执行了……");
    }

    public Inner() {
        System.out.println("5 内部构造器执行了");
    }

    private static int innerStaticVariable = 30;

    /**
     * 成员方法
     */
    public void innerShow() {
        System.out.println("6 innerVariable:" + innerVariable);
        System.out.println("7 内部的commonVariable:" + commonVariable);
        System.out.println("8 outerStaticVariable:"+outerStaticVariable);

// outerStaticMethod(); }

    /**
     * 静态方法
     */
    public static void innerStaticShow() {
        //被调用时会先加载Outer类
        outerStaticMethod();
        System.out.println("4 outerStaticVariable"+outerStaticVariable);
    }
}

/**
 * 外部类的内部如何和内部类打交道
 */
public static void callInner() {
    System.out.println(Inner.innerStaticVariable);
    Inner.innerStaticShow();
}

}

**2、其他类使用成员内部类**
```java
package com.alpaak.part11.demo8;

public class Other {

    public static void main(String[] args) {
        //访问静态内部类的静态方法,Inner类被加载,此时外部类未被加载,独立存在,不依赖于外围类。
        Outer.Inner.innerStaticShow();
        //访问静态内部类的成员方法
        Outer.Inner oi = new Outer.Inner();
        oi.innerShow();
    }
}

3、小结【和成员内部类对比理解(区别异同)】

  • 作为静态成员属性存在,可以被任意的权限修饰符修饰
  • 静态内部类的方法只能访问外部类的static关联的信息。
  • 利用 外部类.内部类 引用=new 外部类.内部类(); 然后利用引用.成员信息(属性、方法)调用。

    //访问静态内部类的成员方法
          Outer.Inner oi = new Outer.Inner();
          oi.innerShow();
    
  • 访问内部类的静态信息,直接外部类.内部类.静态信息就可以了。

    //访问静态内部类的静态方法,Inner类被加载,此时外部类未被加载,独立存在,不依赖于外围类。
          Outer.Inner.innerStaticShow();
    

    匿名内部类

    1、定义接口

    ```java /**

  • 接口中方法默认为public */ public interface IAnimal{ void speak(); } ```

    2、匿名内部类使用

    示例9

    ```java package com.alpaak.part11.demo9;

/**

  • 匿名类 */ public class Demo9 {

    public static IAnimal getInnerInstance(String speak){ return new IAnimal(){

       @Override
       public void speak(){
           System.out.println(speak);
       }};
       //注意上一行的分号必须有
    

    }

    public static void main(String[] args){ //调用的speak()是重写后的speak方法。 Demo9.getInnerInstance(“小狗汪汪汪!”).speak(); } }

```

3、小结【匿名内部类常常被用来重写某个或某些方法】

  1. 匿名内部类是没有访问修饰符的。
  2. 使用匿名内部类时,这个new之后的类首先是要存在的,其次我们要重写new后的类的某个或某些方法。