目录
11.1枚举
JDK1.5中新增了枚举,枚举是一种数据类型,它是一系列具有名称的常量的集合。比如在数
学中所学的集合:A=(12,3},当使用这个集合时只能使用集合中的1、2、3这3个元素,不是这
3个元素的值就无法使用Java中的枚举是同样的道理,比如在程序中定义了一个性别枚举,里面
只有两个值:男、女,那么在使用该枚举时,只能使用男和女这两个值,其他的任何值都是无法使
用的。本节将详细介绍枚举类型
11.1.1使用枚举类型设置常量
以往设置常量,通常将常量放置在接口中,这样在程序中就可以直接使用,并且该常量不能被
修改,因为在接口中定义常量时,该常量的修饰符为 final与 static
例如,在项目中创建 Constants接口,在接口中定义常量的常规方式。
例11.1在项目创建Constants接口,在该接口中定义两个整数变量,其修饰都是static和final;之后定义名称为 Constants的放举类,将 Constants接口的常量放置在该枚举类中;最后建名称Constants的类文件,在该类中先通过doto和doit20进行不同方式的调用,再通过主方法会量进行调用,体现枚举类型定义常量的方式。
public interface Constants {
public static final int Constants_A = 1;
public static final int Constants_B=12;
public static Constants[] values() {
// TODO Auto-generated method stub
return null;
}
}
public class ConstantsTest {
enum Constants2 { //将常量放置在枚举类型中
Constants_A, Constants_B;
}
//使用接口定义常量
public static void doit(int c) { //定义一个方法,这里的参数为
//根据常量的值做不同操作
switch (c){
case Constants.Constants_A:
System.out.println("doit() Constants_A");
break;
case Constants.Constants_B:
System.out.println("doit() Constants B");
break;
}
}
public static void doit2(Constants2 c) { //定义一个参数对象是枚举类型
switch (c){ //根据枚举类型对象做不同操作
case Constants_A:
System.out.println("doit2() Constants_A"); break;
case Constants_B:
System.out.println("doit2() Constants_B"); break;
}
}
public static void main(String[] args){
ConstantsTest.doit(Constants.Constants_A); //使用接口中定义的常量
ConstantsTest.doit2(Constants2.Constants_A); //使用枚举类型中的常量
ConstantsTest.doit2(Constants2.Constants_B); //使用枚举类型中的常量
ConstantsTest.doit(3);
//ConstantsTest.doit2(3);
}
}
结果
11.1.2深入了解枚举类型
1.操作枚举类型成员的方法
枚举类型较传统定义常量的方式,除了具有参数类型检测的优势之外,还具有其他面的优
用户可以将一个枚举类型看作是一个类,它继承于 java lang.um类,当定一个枚类型时
每一个枚举类型成员都可以看作是枚举类型的一个实例,这些枚举类型成员都默认被flp
static修饰,所以当使用枚举类型成员时直接使用枚举类型名称调用枚举类型成员即可,
由于枚举类型对象继承于javalang.Eum类,以该类中一些操作故类型的方法都可以应
到枚举类型中表11.1中列举了枚举类型中的常用方法
例11.2在类中使用枚举类型中的values()方法获取枚举类型 中的成员变量
public enum Constants2 {
//将常量放置在枚举类型中
Constants_A,Constants_B,Constants_c
}
public class DDD2 {
enum Constants{
Constants_A,Constants_B,Constants_C,Constants_D
}
public static void main(String[] args) {
Constants enumArray[] = Constants.values();
for(int i = 0;i<enumArray.length;i++) {
//将枚举成员变量打印
System.out.println("枚举类型成员变量:"+ enumArray[i]);
}
}
}
结果
例11.3
public enum Constants2 {
//将常量放置在枚举类型中
Constants_A,Constants_B,Constants_c
}
public class EnumMethodTest {
//定义比较枚举类型方法,参数类型为枚举类型
public static void compare(Constants2 c){
// 根据values()方法返回的数组
Constants2[] array=Constants2.values();
for (int i=0; i<array.length;i++){
// 将比较结果返回
System.out.println(c+"与"+array[i]+"的比较结果为:"+c.compareTo(array[i]));
}
}
// 在主方法中调用compare()方法
public static void main(String[] args){
compare(Constants2.valueOf("Constants_B"));
}
}
结果
例11.4
代码
public enum Constants2 {
//将常量放置在枚举类型中
Constants_A,Constants_B,Constants_c
}
public class DDD4 {
public static void main(String[] args){
for (int i = 0; i< Constants2.values().length;i++) {
//在循环中获取枚举类型成员的索引位置
System.out.println(Constants2.values()[i]+"在枚举类型中位置索引值"
+Constants2. values()[i].ordinal());
}}
}
结果
2.枚举类型中的构造方法
从枚举类型构造方法的语法中可以看出,无论是无参构造方法还是有参构造方法,
修饰权限都为 priva定义一个有参构造方法后,需要对枚举类型成员相应地使用该构造方法,如 Constants
我是举成员A)和 Constants(3)语句,相应地使用了参数为 String型和参数为int型的构造方法
然后可以在枚举类型中定义两个成员变量,在构造方法中为这两个成员变量赋值,这样就可以在枚举类型中定义该成员变量的 getXxx方法了
例11.5
public class DDD5 {
enum Constants2{//将常量放置在枚举类型中
Constants_A("我是枚举成员A"),//定义带参数的枚举类型成员
Constants_B("我是枚举成员B"),
Constants_C("我是枚举成员C"),
Constants_D(3);
private String description;
private int i=4;
//定义参数为String型的构造方法
private Constants2(String description){
this.description = description;
}
private Constants2(int i){//定义参数为int型的构造方法
this.i = this.i + i;
}
public String getDescription() {//获取description的值
return description;
}
public int getI() { // 获取i的值
return i;
}
}
public static void main(String[] args){
Constants2 array[] =Constants2.values();//获取枚举成员数组
for (int i=0; i<array.length; i++) {
System.out.println(array[i]+"调用getDescription()方法为:"+array[i].getDescription());
}
Constants2 c2 = Constants2.valueOf("Constants_D");//将字符串转换成枚举对象
System.out.println(c2+"调用getI()方法为:"+c2.getI());
}
}
结果
例11.6使每个枚举类型成员实现接口
interface d {
public String getDescription();
public int getI();
}
public enum AnyEnum implements d{
Constants_A("我是枚举成员A"),
Constants_B("我是枚举成员B"),
Constants_C("我是枚举成员C"),
Constants_D("我是枚举成员D");
private static int i = 5;
private String description;
private AnyEnum(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
public int getI() {
return i;
}
public static void main(String[] args) {
AnyEnum[] anyEnumArray = AnyEnum.values();
for (int i = 0; i < anyEnumArray.length; i++) {
System.out.println(anyEnumArray[i] + "调用getDescription()方法为:" + anyEnumArray[i].getDescription());
System.out.println(anyEnumArray[i] + "调用getI()方法为:" + anyEnumArray[i].getI());
}
}
}
结果
11.1.3使用枚举类型的优势
枚举类型声明提供了一种用户友好的变量定义方法,枚举了某种数据类型所有可能出现的值
总结枚举类型,它具有以下特点:
(1)类型安全
(2)紧凑有效的数据定义
(3)可以和程序其他部分完美交互
11.2泛型
在JDK1.5版本中提供了泛型概念,泛型实质上就是使程序员定义安全的类型。在没有出现泛型之前,Java也提供了对 Object的引用“任意化操作,这种任意化操作就是对 Object引用进行“向下转型”及“向上转型”操作,但某些强制类型转换的错误也许不会被编译器捕捉,而在运行后出现异常,可见强制类型转换存在安全隐患所以提供了泛型机制。本节就来探讨泛型机制
11.2.1回顾“向上转型”与“向下转型”
- Java语言允许用父类申明变量,用子类实例化,这种将子类对象直接赋值给父类对象的语法称为向上转型,而且这个过程是自动化的
- 但反过来用子类申明变量用父类实例化是不允许的,因为子类往往比父类更加复杂,计算机无法将简单的对象自动的转化为复杂的对象
- 向下转型需要做强制的转化,在父类对象前使用强制转换的语法才能实现向下转型,而且转型之后也有可能发生数据缺失
例11.7在项目中创建类,在该类中使基本类型向上转型为Object类型
public class DDD7 {
private Object b; // 定义Object类型成员变量
public Object getB() { // 设置相应的getXXX()方法
return b;
}
public void setB(Object b) { // 设置相应的setXXX()方法
this.b = b;
}
public static void main(String[] args) {
DDD7 t = new DDD7();
t.setB(new Boolean(true)); // 向上转型操作
System.out.println(t.getB());
t.setB(new Float(12.3));
Float f = (Float) (t.getB()); // 向下转型操作
System.out.println(f);
}
}
结果
11.2.2定义泛型类
Object类为最上层的父类,很多程序员为了使程序更为通用,设计程序时通常使传入的值与
回的值都以 Object类型为主,当需要使用这些实例时,必须正确地将该实例转换为原来的类型,否则在运行时将会发生 ClassCastException异常
改写11.7定义类时使用泛型的形式
public class DDD7 <T>{
private T over; //定义泛型成员变量
public T getover() { //设置getxxx()方法
return over;
}
public void setover(T over) { //设置setxxx()方法
this.over = over;
}
public static void main(String[] args) {
//实例化一个Boolean型的对象
DDD7<Boolean> overl = new DDD7<Boolean>();//实例化一个Float型的对象
DDD7<Float> over2 =new DDD7<Float>();
overl.setover(true); //不需要进行类型转换
over2.setover(12.3f);
Boolean b=overl.getover();//不需要进行类型转换
Float f=over2.getover();
System.out.println(b);
System.out.println(f);
}
}
结果
11.2.3泛型的常规用法
1.定义泛型类时声明多个类型
在定义泛型类时,可以声明多个类型。语法如下
Mutioverclass<T1,T2>
Mutioverclass:泛型类名称
其中,T1和T2为可能被定义的类型。这样在实例化指定类型的对象时就可以指定多个类型
例如:
Mutioverclass<Boolean, Float>-new MutioverClass<Boolean, Float>)
2.定义泛型类时声明数组类型
定义泛型类时也可以声明数组类型,下面的实例中定义泛型时便声明了数组类型
例11.8在项目中创建类,在该类中定义泛型类声明数组类型
public class ArrayClass<T> {
private T[] array; // 定义泛型数组
public void SetT(T[] array) { // 设置SetXXX()方法为成员数组赋值
this.array = array;
}
public T[] getT() { // 获取成员数组
return array;
}
public static void main(String[] args) {
ArrayClass<String> a = new ArrayClass<String>();
String[] array = { "成员1", "成员2", "成员3", "成员4", "成员5" };
a.SetT(array); // 调用SetT()方法
for (int i = 0; i < a.getT().length; i++) {
System.out.println(a.getT()[i]); // 调用getT()方法返回数组中的值
}
}
}
结果
3.集合类声明容器的元素
在第10章中学习了集合类,实际应用中,通过在集合类中应用泛型可以使集合类中的元素类型保证唯一性,这样在运行时就不会产生 ClassCastException异常,提高了代码的安全性和可维护性,可以使用K和V两个字符代表容器中的键值和与键值相对应的具体值。
例11.9在项目中创建 MutiOverClass类,在该类中使用集合类声明容器的元素
import java.util.HashMap;
import java.util.Map;
public class MutiOverClass <K, V> {
public Map<K, V> m = new HashMap<K, V>(); // 定义一个集合HashMap实例
// 设置put()方法,将对应的键值与键名存入集合对象中
public void put(K k, V v) {
m.put(k, v);
}
public V get(K k) { // 根据键名获取键值
return m.get(k);
}
public static void main(String[] args) {
// 实例化泛型类对象
MutiOverClass<Integer, String> mu = new MutiOverClass<Integer, String>();
for (int i = 0; i < 5; i++) {
// 根据集合的长度循环将键名与具体值放入集合中
mu.put(i, "我是集合成员" + i);
}
for (int i = 0; i < mu.m.size(); i++) {
// 调用get()方法获取集合中的值
System.out.println(mu.get(i));
}
}
}
结果
例11.10在项目中创建类,在该类中使用泛型实例化常用集合类
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
public class DDD10 {
public static void main(String[] args) {
// 定义ArrayList容器,设置容器内的值类型为Integer
ArrayList<Integer> a = new ArrayList<Integer>();
a.add(1); // 为容器添加新值
for (int i = 0; i < a.size(); i++) {
// 根据容器的长度循环显示容器内的值
System.out.println("获取ArrayList容器的值:" + a.get(i));
}
// 定义HashMap容器,设置容器的键名与键值类型分别为Integer与String型
Map<Integer, String> m = new HashMap<Integer, String>();
for (int i = 0; i < 5; i++) {
m.put(i, "成员" + i); // 为容器填充键名与键值
}
for (int i = 0; i < m.size(); i++) {
// 根据键名获取键值
System.out.println("获取Map容器的值" + m.get(i));
}
// 定义Vector容器,使容器中的内容为String型
Vector<String> v = new Vector<String>();
for (int i = 0; i < 5; i++) {
v.addElement("成员" + i); // 为Vector容器添加内容
}
for (int i = 0; i < v.size(); i++) {
// 显示容器中的内容
System.out.println("获取Vector容器的值" + v.get(i));
}
}
}
结果
11.2.4泛型的高级用法
泛型的高级用法主要包括通过类型参数T的继承和通过类型通配符的继承来限制泛型类型,另外,开发人员还可以继承泛型类或者实现泛型接口,本节将对泛型的一些高级用法进行讲解
1.通过类型参数T的继承限制泛型类型
默认可以使用任何类型来实例化一个泛型类对象,但Java中也对泛型类实例的类型作了限制,这主
要通过对类型参数T实现继承来体现,语法如下:class类名称< extends anyclass>
其中, anyClass指某个接口或类。
使用泛型限制后,泛型类的类型必须实现或继承了 anyClass这个接口或类,无论 nyClass是按
口还是类,在进行泛型限制时都必须使用 extends关键字
2.通过类型通配符的继承限制泛型类型
在泛型机制中,提供了类型通配符,其主要作用是在创建一个泛型类对象时,限制这个泛型类
的类型,或者限制这个泛型类型必须继承某个接口或某个类(或其子类),要声明这样一个对象可
以使用“?”通配符,同时使用 extends关键字来对泛型加以限制
例11.11通配符
import java.util.List;
import java.util.ArrayList;
import java.util.LinkedList;
public class DDD10 {
public static void main(String[] args) {
List<String> l1 = new ArrayList<String>(); // 创建一个ArrayList对象
l1.add("成员"); // 在集合中添加内容
List<?> l2 = l1; // 使用通配符
List<?> l3 = new LinkedList<Integer>();
System.out.println("l1:" + l1.get(0)); // 获取l1集合中第一个值
System.out.println("l2:" + l2.get(0)); // 获取l2集合中第一个值
l1.set(0, "成员改变"); // 没有使用通配符的对象调用set()方法
// l2.set(0, "成员改变"); // 使用通配符的对象调用set()方法,不能被调用
// l3.set(0, 1);
System.out.println("l1:" + l1.get(0)); // 可以使用l1的实例获取集合中的值
System.out.println("l2:" + l2.get(0)); // 可以使用l2的实例获取集合中的值
}
}
结果
3.继承泛型类与实现泛型接口
11.2.5泛型总结
使用泛型需遵守以下原则
- 泛型的类型参数只能是类类型,不可以是简单类型,如A<int>这种泛型定义就是错误的
- 泛型的类型个数可以是多个
- 可以使用extends关键字限制泛型的类型
- 可以使用通配符限制泛型的类型
11.3小结
本章主要讲述了枚举类型以及泛型的用法。枚举类型与泛型都为JDK15版本新增的内容。虽然枚举类型与泛型的语法比较简单,但是展开后的写法比较复杂,所以初学者应该仔细揣摩,并且对这两种机制做到简单掌握。此外,读者应该积极了解每个JDK版本新增的内容,而查看相本
的API便是一种极为有效的手段。