目录

总结 Java 主要内容,对语法进行快速浏览。

标识符规则

必须以字母、下划线(_)、美元符($)开头,后面可以更任意数目的字母、数字、下划线和美元符。此处字母并不限于26个英文字母,因 Java 支持 Unicode 字符集,还可以使用 Unicode 所能表示的多种语言的字符(取决于所使用的 Java 版本支持的 Unicode 版本),如中文、日文等。

标识符区分大小写。

数据类型

Java 是强类型的。强类型包含两方面的含义:

  • 所有变量必须先声明后使用
  • 指定类型的变量只能接受类型与之匹配的值

变量声明语法:type varName[ = 初始值];

Java 支持的类型:

  • 基本类型(Primitive Type)
    • boolean 类型,包括 true、false
    • 数值类型
      • 整数类型
        • byte(1 byte)、short(2 bytes)、int(4 bytes,Default)、long(8 bytes)
        • 字符类型,char(2 bytes)
      • 浮点类型,包括 float(4 bytes)、double(8 bytes,Default)
  • 引用类型(Reference Type)
    • 接口
    • 数组
    • null

运算符

基本运算符

  • 算数运算符
    • 包括 +、-、*、/、%、++、–
    • Math 类
  • 赋值运算符
    • =
    • +=、-=、*=、/=、%=、&=、|=、^=、<<=、>>=、>>>=
  • 比较运算符
    • >、>=、<、<=、==、!=、
  • 逻辑运算符
    • &&,与,前后两个操作数都为 true 则返回 true,否则返回 false
    • &,不短路与,作用与 && 相同,但不会短路
    • ||,或,两个操作数一个为 true 就为 true,否则返回 false
    • |,不短路或,作用与 || 相同,但不会短路
    • !,非,true => false,false => true
    • ^,异或,当两个操作数不同时返回 true,相同时返回 false
  • 位运算符
    • &、|、~、^、<<、>>、>>>(无符号右移运算符)
  • 类型相关运算符
    • instanceof
    • (type)

三目运算符

?:

(expression) ? if-true-statement : if-false-statement;

运算符的结合性和优先级

所有数学运算都认为是从左向右运算,Java 中大部分运算符也是从左向右结合的,只有单目运算符、赋值运算符和三目运算符例外,是从右向左结合的。

运算符有不同的优先级,所谓优先级就是在表达式运算中的运算顺序。下表列出了包括分隔符在内的所有运算符的优先级顺序,上一行的运算符总是优先于下一行的。

运算符优先级

运算符说明 Java 运算符
分隔符 .、[]、()、{}、,、;
单目运算符 ++、–、~、!
强制类型转换运算符 (type)
乘法/除法/求余 *、/、%
加法/减法 +、-
移位运算符 <<、>>、>>>
关系运算符 <、<=、>=、>、instanceof
等价运算符 ==、!=
按位与 &
按位异或 ^
按位或 |
条件与 &&
条件或 ||
三目运算符 ?:
赋值 =、+=、-=、*=、/=、&=、|=、^=、%=、<<=、>>=、>>>=

流程控制

顺序结构

顺序结构就是程序从上到下逐行地执行,中间没有任何判断和跳转。即如果没有流程控制,Java 方法里的语句是一个顺序执行流,从上向下依次执行每条语句。

分支结构

if 条件语句

使用布尔表达式或布尔值作为分支条件来进行分支控制,有如下三种形式:

if ( logic expression )
{
    statement...
}
if ( logic expression )
{
    statement...
}
else
{
    statement...
}
if ( logic expression )
{
    statement...
}
else if ( logic expression )
{
    statement...
}
...// 可以有零个或多个 else if 语句
else// 最后的 else 语句也可以省略
{
    statement...
}

switch 分支语句

switch 语句由一个控制表达式或多个 case 标签组成,其后面的控制表达式的数据类型只能是 byte、short、char、int 四种数据类型,枚举类型和java.lang.Sting 类型(从 Java 7 才允许),不能是 boolean 类型。

switch 语句往往需要在 case 标签后紧跟一个代码块,case 标签作为这个代码块的标识。其语法格式如下:

switch ( expression )
{
    case conditiaon1:
    {
        statement(s)
        break;
    }
    case conditiaon2:
    {
        statement(s)
        break;
    }
    ...
    case conditiaonN:
    {
        statement(s)
        break;
    }
    default:
    {
        statement(s)
    }
}

先对 expression 求值,依次匹配 condition1、…、conditiaonN等值,遇到匹配的值即执行对应的执行体;如果所有 case 标签后的值都不与 expression 表达式的值相等,则执行 default 标签后的代码块。

循环结构

循环语句可能包含如下 4 个部分:

  • 初始化语句(init_statement)
    一条或多条语句,这些语句用于完成一些初始化工作,初始化语句在循环开始之前执行。
  • 循环条件(test_expression)
    这是一个 boolean 表达式,这个表达式能决定是否执行循环体。
  • 循环体(body_statement)
    这个部分是循环的主体,如果循环条件允许,这个代码块将被重复执行。
  • 迭代语句(iteration_statement)
    这个部分在一次循环体执行结束后,对循环条件求值之前执行,通常用于控制循环条件中的变量,使得循环在合适的时候结束。

while 循环语句

语法格式如下:

[init_statement]
while (test_expression)
{
    statement;
    [iteration_statement]
}

do while 循环语句

与 while 循环的区别在于:do while 先执行循环体,然后才判断循环条件;而 while 刚好相反。

[init_statement]
do
{
    statement;
    [iteration_statement]
} while (test_expression);

for 循环

for ([init_statement]; [test_expression]; [iteration_statement])
{
    statement
}

控制循环结构

continue 和 break 来控制循环结构,return 可以结束整个方法,也就结束了一次循环。

  • break 在某种条件下强制结束循环
  • break LABEL; 可直接结束其外层循环到外部的标签(形如 LABEL: )
outer:
for (;;)
{
    for (;;)
    {
        break outer;
    }
}
  • continue 忽略本次循环剩下的语句
  • continue LABEL; 用于直接跳过标签所标识循环的当次循环的剩下语句,重新开始下一次循环。
  • return 结束方法

数组类型

  • 定义数组:
type[] arrayName; // 推荐使用
type arrayName[];
  • 数组的初始化
    • 静态初始化:由程序员显示制定每个数组元素的初始值,系统决定数组长度

语法格式如下:

  arrayName = new type[]{element1, element2, element3 ...}
  // 简化语法,定义数组和初始化要同时进行
  type[] arrayName = {element1, element2, element3 ...}
  • 动态初始化:程序员指定长度,系统为数组元素分配初始值
  arrayName = new type[length];
动态初始化的默认值规则:

- 整数类型(byte、short、int、long),0
- 符点类型(float、double),0.0
- 字符类型(char),'\u0000'
- 布尔类型(boolean),false
- 引用类型(类、接口、数组),null
  • 使用数组
arrayName[index]; // index 从 0 开始
  • 获得数组长度 arrayName.length

foreach 循环

遍历数组和集合更加简洁,其语法格式如下:

foreach (type variableName : array | collection)
{
    // variableName 自动迭代访问每个元素
}

类和对象

类(class)是某一批对象的抽象;对象(object,也称为实例,instance)是一个具体存在的实体。

  • 定义类的语法
[修饰符] class 类名
{
    零个到多个构造器定义...
    零个到多个成员变量...
    零个到多个方法...
}

修饰符可以是public、final、abstract,或者完全省略这三个修饰符。

  • 定义成员变量的语法
[修饰符] 类型 成员变量名 [= 默认值];

修饰符可以省略,也可以是 public、protected、private、static、final,其中 public、protected、private 三个最多只能出现一个,可以与 static、final 组合起来。

  • 定义方法的语法格式
[修饰符] 方法返回值类型 方法名(形参列表)
{
    // 由零条到多条可执行性语句组成的方法体
}

修饰符可以省略,也可以是 public、protected、private、static、final、abstract,其中 public、protected、private 三个最多只能出现一个;abstract 和 final 最多只能出现其中之一,他们可以和 static 组合起来修饰方法。

方法返回值类型:如果声明了返回值类型则必须有一个有效的 return 语句,且返回类型与声明相匹配;如果没有返回值,必须使用 void 来声明。

形参列表:定义方法可接受的参数,由零组到多组「参数类型 形参名」组合而成,多组参数之间以英文逗号(,)隔开。

  • 构造器

构造器是一个特殊方法,其语法格式如下:

[修饰符] 构造器名(形参列表)
{
    // 由零条到多条可执行性语句组成的构造器执行体
}

修饰符可以省略,也可是 public、protected、private 其中之一。构造器名必须和类名相同,其既不能定义返回值类型,也不能用 void 声明。

  • 对象的产生和使用

创建对象的根本途径是构造器,通过 new 关键字来调用某个类的构造器即可创建其实例。

ClassName objectVar;
objectVar = new ClassName();

// 简写形式为定义的同时并赋值
ClassName objectVar = new ClassName();

类或实例访问方法或成员变量的语法是:类.类变量|方法,或者实例.实例变量|方法。

static 修饰的方法和成员变量可以通过类或实例来调用;没有使用 static 修饰的普通方法和成员变量,只可通过实例来调用。

  • 方法

方法不能独立存在,必须属于类或对象。如果方法使用了 static 修饰,则其属于所在的类,否则属于所在类的实例。

执行方法时必须使用类或对象来作为调用者,即所有方法都必须使用「类.方法」或「对象.方法」的形式来调用。同一个类的一个方法调用另外一个方法时,如果被调方法是普通方法,则默认使用 this 作为调用者;如果被调用方法是静态(static)方法,则默认使用类作为调用者。

  • 方法的参数

Java 里方法的参数传递方式只有一种:值传递,即将实际参数值的副本传入方法内,而参数本身不会受到任何影响。

JDK 1.5 之后,Java 允许定义形参个数可变的参数,从而允许为方法指定数量不确定的形参。在定义方法时,在最后一个形参的类型后面增加三点(…),则表明该形参可以接受多个参数值,其被当成数组传入。如下:

public void test(int a, String... books)
{
    for (String tmp : books)
    {
    }
}

test(5, "123", "456");
  • 方法重载

Java 允许同一个类里定义多个同名方法,只要形参列表不同就可以。如果同一个类中包含两个或以上的方法名相同,但形参列表不同,则成为方法重载。

方法重载的要求就是两同一不同:同一个类中方法名相同,参数列表不同。至于方法的其他部分,如方法返回值类型、修饰符等,与方法重载没有任何关系。

包含可变参数的重载方法

public void test(String msg)
{
}
// 因前面有一个字符串参数的 test() 方法
// 此处的长度可变形参里不包含一个字符串参数的形式
public void test(String... books)
{
}

test() 和 test(“aa”, “bb”) 调用 test(String… books) 方法,而 test(“aa”) 调用 test(String msg) 方法。如果要使用一个字符串参数调用可变参数的方法,则可采用传入字符串数组的形式,如:

text(new String[]{"aa"});

大部分时候不推荐重载形参长度可变的方法。

  • 变量
    • 成员变量
      • 实例变量(不以 static 修饰)
      • 类变量(以 static 修饰)
    • 局部变量
      • 形参(方法签名中定义的变量)
      • 方法局部变量(在方法内定义)
      • 代码块局部变量(在代码块内定义)

隐藏与封装

  • 访问控制级别
private
(当前类访问权限)
default
(包访问权限)
protected
(子类访问权限)
public
(公共访问权限)
同一个类中
同一个包中
子类中
全局范围内

外部类只有两种访问控制级别:public 和默认,不能使用 private 和 protected 修饰,因为外部类没有处于任何类的内容,也就没有其所在类的内部、所在类的子类两个范围。

  • package、import 和 import static
    • Java 允许将一组功能相关的类放在同一个 package 下,其必须是第一个非注释行
  package packageName;
  • import 导入指定包层次下某个类或全部类,应出现在 package 之后、类定义之前
  // 导入单个类
  import package.subpackage...ClassName;
  // 导入指定包下全部类。 星号(*)只代表类,不代表包。
  import package.subpackage...*;
  
  // JDK 1.5 以后新增静态导入语法,用于导入指定类的某个静态成员变量、方法
  // 或全部的静态类成员变量、方法
  import static package.subpackage...ClassName.fieldName|methodName;
  import static package.subpackage...ClassName.*;
  • Java 源文件的大体结构
// 0 个或 1 个,必须放在文件开始
package 语句
// 0 个或多个,必须放在所有类定义之前
import | import static 语句
// 0 个或 1 个 public 类、接口或枚举定义
public classDefinition | interfaceDefinition | enumDefinition
// 0 个或多个普通类、接口或枚举定义
classDefinition | interfaceDefinition | enumDefinition
  • 构造器重载

如果系统中包含多个构造器,其中一个构造器的执行体里完全包含另一个构造器的执行体,如:

// 标记为 A
public void test()
{
    statementA;
    statementB;
    statementC;
}
// 完全包含构造器 test(),标记为 B
public void test(int a)
{
    statementA;
    statementB;
    statementC;
    statementD;
    func(a);
}

对于这种完全包含的情况,如果是两个方法之间存在这种关系,则可在方法 B 中调用方法 A。但构造器不能直接被调用,构造器必须使用 new 关键字来调用。但一旦使用 new 来调用构造器,将导致系统重新创建一个对象。

为在构造器 B 中调用构造器 A 中的初始化代码,又不会重新创建一个 Java 对象,可以使用 this 关键字来调用相应的构造器。

// 标记为 A
public void test()
{
    statementA;
    statementB;
    statementC;
}
// 完全包含构造器 test(),标记为 B
public void test(int a)
{
    // 通过 this 调用另一个重载的构造器的初始化代码
    this();
    statementD;
    func(a);
}

使用 this 调用另一个重载的构造器只能在构造器中使用,而且必须作为构造器执行体的第一条语句。使用 this 调用重载的构造器时,this 根据括号里的实参来调用形参列表与之对应的构造器。

  • 类的继承

Java 的继承通过 extends 关键字来实现,实现继承的类成为子类,被继承的类成为父类,有的也称其为基类、超类。

修饰符 class SubClass extends SuperClass
{
    // 类定义部分
}
  • 方法重载

子类包含与父类同名方法的现象称为方法重写(Override),也称为方法覆盖。方法充血要遵循「两同两小一大」规则,「两同」即方法名相同、形参列表相同;「两小」指的是子类方法返回值类型应比父类方法返回值类型更小或相等,子类方法声明抛出的异常类应比父类方法声明抛出的异常类更小或相等;「一大」指的是子类方法的访问权限应比父类方法的访问权限更大或相等。覆盖方法和被覆盖方法要么都是类方法,要么都是实例方法。

子类覆盖父类方法后,子类的对象将无法访问父类中被覆盖的方法,但在子类方法中可以调用父类中被覆盖的方法,可使用 super(被覆盖的是实例方法)或父类类名(被覆盖的是类方法)作为调用者来调用父类中被覆盖的方法。

如果父类方法具有 private 访问权限,则该方法对其子类是隐藏的,子类无法访问或重写该方法。如子类中定义一个与父类 private 方法具有相同的方法名、相同的形参列表、相同的返回值类型的方法,依然不是重写,只是在子类中重新定义了一个新方法。

重载和重写: 方法重载和方法重写在英语中分别是 overload 和 override;重载主要发生在同一个类的多个同名方法之间,而重写发生在子类和父类的同名方法之间。

  • super 限定
// 调用父类实例方法
super.parentMethod();
// 调用父类构造器,只能在子类构造器内使用且必须是执行体的第一行
super();
  • 多态(Polymorphism)

Java 引用变量有两个类型:一个是编译时类型,一个是运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。如果编译时类型和运行时类型不一致,就可能出现所谓的多态。

class BaseClass
{
    public int book = 6;
    public void base(){}
    public void test(){}
}
public class SubClass extends BaseClass
{
    // 重新定义一个 book 实例变量隐藏父类的 book 实例变量
    public String book = "abcdef":
    public void test(){}
    public void sub(){}
    public static void main(String[] args)
    {
        // 下面编译时类型和运行时类型完全一样,因此不存在多态
        BaseClass bc = new BaseClass();
        System.out.println(bc.book);
        bc.base();
        bc.test();
        // 下面编译时类型和运行时类型完全一样,因此不存在多态
        SubClass sc = new SubClass();
        System.out.println(sc.book);
        sc.base();
        sc.test();
        // 下面编译时类型和运行时类型不一样,多态发生
        BaseClass ploymophicBc = new SubClass();
        System.out.println(ploymophicBc.book);
        ploymophicBc.base();
        ploymophicBc.test();
        // 因为 ploymophicBc 的编译时类型是 BaseClass
        // BaseClass 类没有提供 sub() 方法,所以下面代码编译时会出错
        // ploymophicBc.sub();
    }
}

ploymophicBc 编译时类型时 BaseClass,而运行时类型时 SubClass,当调用该引用变量的 test() 方法时,实际执行的是 SubClass 类中覆盖后的 test() 方法,这就可能出现多态了。

因为子类其实是一种特殊的父类,因此 Java 允许把一个子类对象直接赋给一个父类引用变量,无须任何类型转换,或者被称为向上转型(upcasting),向上转型由系统自动完成。

与方法不同的是,对象的实例变量则不具备多态性。

  • 引用变量的强制类型转换

编写 Java 程序时,引用变量只能调用它编译时类型的方法,而不能调用它运行时类型的方法,即使它实际所引用的对象确实包含该方法。如需要让这个引用变量调用它运行时类型的方法,则必须把它强制类型转换成运行时类型。

类型转换运算符用法时:(type)variable,其可以将一个基本类型转换为另一个类型,还可以将一个引用类型变量转换为其子类类型。

当强制类型转换时需注意:

  1. 基本类型之间的转换只能在数值类型之间进行,不能转换为布尔类型;
  2. 引用类型之间的转换只能在具有继承关系的两个类型间进行;如果把父类实例转换成子类型,则这个对象必须实际上是子类实例才行(即编译时为父类类型,而运行时是子类类型),否则运行时引发 ClassCastException 异常。

鉴于转换时可能出现的异常,进行转换之前应用 instanceof 运算符来判断是否可以成功转换,如 if (objPri instanceof String)

  • 初始化块

初始化块是 Java 类里可出现的第 4 种成员,一个类里可以有多个初始化块,相同类型的初始化块之间有顺序:前面定义的初始化块先执行,后面定义的初始化块后执行。初始化块的语法格式如下:

[修饰符] {
    // 初始化块的可执行性代码
    ...
}

初始化块的修饰符只能是 static,使用 static 修饰的初始化块称为静态初始化块。初始化块里的代码可以包含任何可执行性语句,包括定义局部变量、调用其他对象的方法,以及使用分支、循环语句等。

初始化块无法通过类、对象来调用,其只在创建 Java 对象时隐式执行,而且在执行构造器之前执行。

  • 静态初始化块

定义初始化块时使用了 static 修饰符称为静态初始化块,也称为类初始化块。静态初始化块在类初始化阶段执行静态初始化块,而不是像普通初始化块那样在创建对象时才执行。因此静态初始化块总是比普通初始化块先执行。

静态初始化块通常用于对类变量执行初始化处理,不能对实例变量进行初始化处理。其也不能访问非静态成员,包括不能访问实例变量和实例方法。

包装类

为了解决 8 种基本数据类型的变量不能当成 Object 类型变量使用,Java 提供了包装类(Wrapper Class),为 8 种基本数据类型分别定义了相应的引用类型,称为基本数据类型的包装类。

基本数据类型和包装类的对应关系

基本数据类型 包装类
byte Byte
short Short
int Integer
long Long
char Character
float Float
double Double
boolean Boolean
  • 自动装箱(Autoboxing)

把一个基本类型直接赋给对应的包装类变量,或者赋给 Object 变量(Object 是所有类的父类,子类对象可以直接赋给父类变量)

  • 自动拆箱(AutoUnboxing)

把包装类对象直接赋给一个对应的基本类型变量。

自动装箱和自动拆箱大大简化了基本类型变量和包装类对象之间的转换过程。

抽象类

抽象方法和抽象类

有抽象方法的类只能被定义为抽象类,抽象类里可以没有抽象方法。

规则如下:

  • 抽象类必须使用 abstract 修饰符来修饰,抽象方法也必须使用 abstract 修饰符来修饰,抽象方法不能有方法体。
  • 抽象类不能被实例化,无法使用 new 关键字来调用抽象类的构造器创建抽象类的实例。
  • 抽象类可以包含成员变量、方法(普通方法和抽象方法均可)、构造器、初始化块、内部类(接口、枚举)5 种成分。抽象类的构造器不能用于创建实例,主要用于被其子类调用。
  • 含有抽象方法的类(包括直接定义一个抽象方法;或继承了一个抽象父类,但没有完全实现父类包含的抽象方法;或实现一个接口,但没有完全实现接口包含的抽象方法三种情况)只能被定义成抽象类。

示例:

// 抽象类
public abstract class Test
{
    // 抽象方法
    public abstract String getType();
}
// 抽象类的实现由普通类 extends 继承实现
public class ImplementTest extends Test
{
    public String getType(){};
}

接口

接口的定义

基本语法如下:

[修饰符] interface 接口名 extends 父接口1, 父接口2...
{
零个到多个常量定义...
零个到多个抽象方法定义...
零个到多个内部类、接口、枚举定义...
零个到多个定义默认方法或类方法...
}

修饰符可以是 public 或默认采用包权限访问控制符。

接口定义是一种规范,不能包含构造器和初始化块定义。

  • 接口定义默认方法必须使用 default 修饰,且不能使用 static 修饰,其总是使用 public 修饰。
  • 接口中定义类方法,必须使用 static 修饰,且不能使用 default 修饰,其总是使用 public 修饰。
  • 接口里的成员变量默认时使用 public static final 修饰的。

使用接口

一个类可以实现一个或多个接口,使用 implements 关键字,其语法格式为:

[修饰符] class 类名 extends 父类 implements 接口1, 接口2...
{
    类体部分
}

内部类

内部类也称为嵌套类,主要作用如下:

  • 提供了更好的封装,不允许同一个包中的其他类访问该内部类
  • 内部类成员可以直接访问外部类的私有数据,但外部类不能访问内部类的实现细节
  • 匿名内部类适合用于创建仅需要一次使用的类

与外部类定义语法大致相同,区别如下:

  • 内部类定义在其他类(外部类)之内
  • 内部类比外部类可以多三个修饰符:private、protected、static
  • 非静态内部类不能拥有静态成员

定义内部类只要把一个类放在另一个类内部即可。此处的「类内部」包括类中的任何位置,甚至在方法内也可以定义(称之为局部内部类)。

定义语法:

public class Outer
{
    // 此处定义内部类
}

内部类大部分作为成员内部类定义,其是与成员变量、方法、构造器、初始化块相似的类成员,而局部内部类和匿名内部类则不是类成员。

成员内部类分为静态内部类和非静态内部类,其中静态内部类是使用 static 修饰的成员内部类。

枚举类

枚举类使用 enum 来定义,其一样有自己的成员变量、方法,可以实现一个或多个接口,也可以定义自己的构造器。其与普通类的区别是:

  • 枚举类可实现一个或多个接口,使用 enum 定义的枚举类默认继承了 java.lang.Enum 类,而不是默认继承 Object 类,因此其不能显示继承其他父类。
  • 使用 enum 定义、非抽象的枚举类默认会使用 final 修饰,因此不能派生子类。
  • 其构造器只能使用 private 访问控制符,如果省略了构造器的访问控制符,则默认使用 private 修饰;如果强制指定访问控制符,则只能指定 private。
  • 枚举类的所有实例必须在枚举类的第一行显式列出,否则其永远不能产生实例。列出这些实例时,系统会自动添加 public static final 修饰。
public enum SeasonEnum
{
    // 在第一行列出枚举实例
    SPRING, SUMMER, FALL, WINTER;
}

枚举类的成员变量、方法和构造器

public enum Gender
{
    MALE, FEMALE;
    // 定义一个 pubic 修饰的实例变量
    public String name;
}
// 使用 Gender 的实例变量
public class GenderDemo
{
    public static void main(String[] args)
    {
        // 通过 Enum 的 valueOf() 获取指定枚举类的枚举值
        Gender gd = Enum.valueOf(Gender.class, "MALE");
        // OR
        // Gender gd = Gender.valueOf("MALE");
        // 访问枚举值的 name 实例变量
        gd.name = "Male";
    }
}

枚举类的实例只能是枚举值,不是通过 new 来创建枚举类对象。

枚举类通常应该设计成不可变类,其成员变量值不应该允许改变。因此建议将枚举类成员变量都使用 private final 修饰,这样必须在构造器里为这些成员变量指定初始值(或在定义成员变量时指定默认值,或者在初始化块中指定初始值,但这两种情况并不常见)。应该为枚举类显式定义带参数的构造器。

一旦为枚举类显式定义了带参数的构造器,列出枚举值时就必须对应地传入参数。

public enum Gender
{
    // 枚举值必须调用对应的构造器来创建
    MALE("男"), FEMALE("女");
    private final String name;
    // 枚举类的构造器只能使用 private 修饰
    private Gender(String name)
    {
        this.name = name;
    }
    public String getName()
    {
        return this.name;
    }
}

实现接口的枚举类

枚举类可实现一个或多个接口,枚举类也需要实现该接口所包含的方法。

public interface GenderDesc
{
    void info();
}
public enum Gender implements GenderDesc
{
    MALE, FEMALE;

    public void info()
    {
        // 输出描述信息
    }
}

由枚举类来实现接口里的方法,则每个枚举值在调用该方法时都有相同的行为。如果需要呈现不同的行为方式,则可以让每个枚举值分别来实现该方法,每个枚举值提供不同的实现方式,如下:

public enum Gender implements GenderDesc
{
    MALE("男")
    // 花括号部分实际上是一个类体部分
    {
        public void info(){}
    },
    FEMALE("女")
    {
        public void info(){}
    };
}

包含抽象方法的枚举类

public enum MathCal
{
    PLUS
    {
        public double eval(double x, double y)
        {
            return x + y;
        }
    },
    MINUS
    {
        public double eval(double x, double y)
        {
            return x - y;
        }
    };
    // 为枚举类定义抽象方法
    // 该抽象方由不同的枚举值提供不同的实现
    public abstract double eval(double x, double y);
    
    public static void main(String[] args)
    {
        MathCal.PLUS.eval(3, 4);
    }
}

枚举类定义抽象方法时不能使用 abstract 关键字将枚举类定义成抽象类(系统自动添加 abstract 关键字),但因枚举类需显式创建枚举值,而不是作为父类,所以定义每个枚举值时必须为抽象方法提供实现。