本节学习目标:
使用枚举类型(Enumeration)可以取代以往定义常量的方式,同时枚举类型还赋予程序在编译时进行检查的功能。
早期设置常量时,通常将常量放置在接口中,这样在程序中可以直接使用,同时该常量不能被修改:
public interface Color {
String RED = "红色";
String GREEN = "绿色";
String BLUE = "蓝色";
}
JDK1.5之后Java提供了枚举类型,使用枚举类型可以更好的定义常量。枚举类型中的常量使用逗号分隔,并在最后一个常量后使用分号结尾。:
public enum Color {
RED, GREEN, BLUE;
}
Java使用enum
关键字来定义枚举类,需要在程序中使用常量时可以使用枚举类名.常量名
来获取。
枚举类同样可以以内部类形式被定义在一个类的内部:
public class Car {
enum Type {
CAR, TRUCK, BUS;
}
}
常量可以看做是枚举类的一个对象,枚举类中至少需要定义一个常量,并且只能在开头定义。
枚举类中允许定义成员变量和静态常量:
public enum Signal {
OK, FAIL;
private String message;
public static final String DESC = "这是一个枚举类";
}
枚举类中允许定义构造方法,但构造方法必须被private
修饰符修饰,因为枚举类的常量数量是固定的,不允许再创建常量:
public enum Signal {
OK, FAIL;
private String message;
private Signal() {
}
Signal(String message) {
this.message = message;
}
}
由于枚举类中的构造方法必须被private
修饰符修饰,因此定义构造方法时可以不写private
关键字,默认权限就为private
枚举类一般不写无参构造方法,枚举类的常量直接在枚举类中初始化,这样这些常量可以直接使用:
public enum Signal {
OK("成功"), FAIL("失败");
private String message;
Signal(String message) {
this.message = message;
}
}
按照有参构造方法的参数列表,在常量后加括号,在括号里传入构造方法的参数。
枚举类中允许定义成员方法和静态方法:
public enum Signal {
OK("成功"), FAIL("失败");
private String message;
Signal(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public static void foo() {
System.out.println("调用了静态方法");
}
}
对于被private
修饰的成员变量,为了实现封装性可以为这个成员变量定义getter和setter方法。用来向外部提供访问。但是枚举类的常量一般是不能更改的,所以setter方法可以不写。
例子中的成员变量可以以Signal.OK.getMessage()
形式访问。
Enum
类位于java.lang
包下,它是所有枚举类(使用enum
关键字定义的枚举类)的父类。
它有两个成员变量:
变量名 | 类型 | 说明 |
---|---|---|
name |
String |
枚举类中每一个变量的名称 |
ordinal |
int |
枚举类中每一个变量的序数 |
比如章节1.2的例子中的常量OK
,它的名称是OK
,序数为0
;常量FAIL
的名称为FAIL
,序数为1
。
Enum
类提供了很多常用方法:
方法名 | 返回值类型 | 功能 |
---|---|---|
name() |
String |
返回枚举类中指定常量的名称 |
ordinal() |
int |
返回枚举类中指定常量的序数 |
toString() |
String |
继承于Object 类,返回枚举类中指定常量的字符串形式,可重写 |
valueOf(Class |
|
将字符串name 解析为指定枚举类enumType 中的常量T |
compareTo(E o) |
int |
返回枚举类的两个常量的序数差,如果这两个常量不是来自同一个枚举类,将抛出ClassCastException 异常 |
equals(Object other) |
boolean |
继承于Object 类,将枚举类中指定常量与对象other 进行比较,可重写 |
枚举类本身提供的方法:
方法名 | 返回值类型 | 功能 |
---|---|---|
values() |
|
返回当前枚举类中所有常量,使用数组表示 |
valueOf(String name) |
|
将字符串name 解析为当前枚举类中的常量T |
编写代码进行测试:
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
System.out.println(Signal.OK.name());
System.out.println(Signal.OK.ordinal());
System.out.println(Signal.OK.toString());
System.out.println(Enum.valueOf(Signal.class, "OK").name());
System.out.println(Signal.OK.compareTo(Signal.FAIL));
System.out.println(Signal.OK.equals(Signal.FAIL));
System.out.println(Signal.OK.equals(Signal.OK));
System.out.println(Arrays.toString(Signal.values()));
System.out.println(Signal.valueOf("OK"));
}
}
运行结果:
OK
0
OK
OK
-1
false
true
[OK, FAIL]
OK
Java泛型(Generics)是JDK1.5中引入的新特性,泛型提供了编译时类型安全检测机制,该机制允许在编译时检测到非法的类型。
在没有出现泛型之前,Java提供了对Object
的泛指引用,原理是对继承的向上转型和向下转型操作。但是向下转型操作由于使用了强制类型转换,容易出现异常,存在安全隐患。
编写代码演示使用Object类的泛指引用:
public class Test {
private Object obj;
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
public static void main(String[] args) {
Test test = new Test();
test.setObj(new Float(457.484167F)); // Float向上转型为Object
System.out.println(test.obj);
Integer i = (Integer) test.getObj(); // Object向下转型为Integer
System.out.println(i);
}
}
运行结果:
457.48416
Exception in thread "main" java.lang.ClassCastException: java.lang.Float cannot be cast to java.lang.Integer
at Test.main(Test.java:13)
可以看到进行向下转型操作是出现了异常,而代码通过了编译。为了解决这个问题,Java引入了泛型机制。
泛型又叫参数化类型(Parameterized Class),在类名后或者方法的返回值前可以使用尖括号(<>
)进行泛型的声明,格式为:
<泛型标记符>
如果要声明多个泛型标记符,使用逗号隔开:
<泛型标记符1, 泛型标记符2>
泛型标记符和标识符命名规则相同,实际开发中一般以单个大写字母表示。Java中常用的泛型标记符:
E
:一般在集合类使用,代表集合的元素类型(Element);T
:Java类(Type);K
:键值对的键(Key);V
:键值对的值(Value);N
:数值类型(Number);?
(特殊):类型通配符,表示不确定的Java类型。泛型标记符的实质是一个参数,这个参数代表泛型的类型,因此存在作用范围。在类名后声明作用范围是整个类,在方法的返回值名前声明作用范围仅限方法内使用。
泛型的类型只能是引用数据类型,不能是基本数据类型。
泛型可以在类中定义,也可以在方法中定义。泛型不可以直接在类中或方法中使用new
关键字进行实例化(类似T t = new T();
),因为它们还不是某个具体类型。
在类名后声明泛型,使用这个类时必须提供具体类型:
public class GenericsClass<T> {
private T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
public static void main(String[] args) {
GenericsClass<Float> f = new GenericsClass<>();
f.setObj(new Float(4857.46));
System.out.println(f.getObj());
Integer i = (Integer) f.getObj();
System.out.println(i);
}
}
尝试编译,编译失败:
使用泛型进行非法强制类型转换会直接报错,无法进行编译,所以使用泛型更加安全。
一旦指定泛型为某个具体类型后,泛型的对象等引用的类型就是指定的具体类型。
在方法的返回值前声明泛型,使用这个方法时必须提供具体类型:
public class Test {
public static <T> void getTClass(T t) {
System.out.println("传入的参数的类型为" + t.getClass().getName());
}
public static void main(String[] args) {
getTClass(457);
getTClass("asd");
getTClass(999.99);
}
}
运行结果:
传入的参数的类型为java.lang.Integer
传入的参数的类型为java.lang.String
传入的参数的类型为java.lang.Double
可以声明多个泛型,编写代码进行测试:
public class GenericEntry<K, V> {
private K key;
private V value;
public GenericEntry(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
public static void main(String[] args) {
GenericEntry<Integer, String> entry = new GenericEntry<>(1, "示例代码");
System.out.println(entry.getValue());
}
}
// 运行结果
// 示例代码
可以通过对泛型的限制进一步限制类型。使用extends
关键字和super
关键字来限制泛型的类型:
使用extends
关键字进行向上限定,限制泛型只能使用指定类型及其子类:
<泛型标识符 extends Object>
编写代码进行测试:
public class GenericExtends<T extends Number> {
private T number;
public T getNumber() {
return number;
}
public void setNumber(T number) {
this.number = number;
}
public static void main(String[] args) {
GenericExtends<String> obj = new GenericExtends<>(); // 尝试将泛型设置为String类型
}
}
尝试编译,编译失败:
Java的泛型机制提供了类型通配符(?
),表示未知类型的泛型,需要使用extends
关键字或super
关键字加以限制。此机制只能在指定泛型的具体类型时(即使用泛型类或泛型方法时)使用。
使用super
关键字进行向下限定,需要配合使用类型通配符(?
),限制泛型只能使用指定类型及其父类:
<? super Object>
编写代码进行测试:
public class GenericSuper<T> {
private T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
public static void main(String[] args) {
GenericSuper<? super Integer> obj = new GenericSuper<>();
obj.setObj(57.3); // 尝试将泛型设置为其他类型
System.out.println(obj.getObj());
}
}
尝试编译,编译失败: