枚举类型可以取代以往常量的定义方式,既将常量封装在类或接口中。此外,它还提供了安全检查功能,枚举类型本质上还是以类的形式存在的泛型的出现,不仅可以让程序员少写一些代码更重要的是。它可以解决类型安全问题。泛型提供了编译时的安全检查不会因为将对象置于某个容器中而失去其类型。
所有枚举类的方法都是私有的
13.1:枚举类型:使用枚举类型,可以取代前面学习过的定义常量的方式,代前面学习过的定义常量的方式,同时枚举类型还赋予程序在编译时进行检查的功能。
13.1.1:使用枚举类型设置常量
1:使用枚举类型设置常量设置常量时,我们通常将常量放置在接口中,这样在程序中就可以直接使用该常量不能被修改因为在接口中定义常量时,该常量的修饰符为final与static常规定义常量的代码如下。
package thirteen;
public interface Constants {
public static final int Constants_A=1;//常规定义变量代码
public static final int Constants_B=2;
}
变量代码,public static final int constants_B=2;}枚举类型出现后逐渐取代了。上述常量定义方式使用枚举类型定义常量的语法如下:
public enum Constants{
Constants_A,
Constants_B,
}
其中, enum 是定义枚举类型的关键字,当需要程序中使用该常量时,可以使用Constants和Constants_A来表示。
package thirteen;
interface SeasonInterface { // 四季接口
int SPRING = 1, SUMMER = 2, AUTUMN = 3, WINTER = 4;
}
enum SeasonEnum { // 四季枚举
SPRING, SUMMER, AUTUMN, WINTER
}
public class SeasonDemo{
public static void printSeason1(int season) {
switch (season) {
case SeasonInterface.SPRING:
System.out.println("这是春季");break;//break跳出
case SeasonInterface.SUMMER:
System.out.println("这是夏季");break;
case SeasonInterface.AUTUMN:
System.out.println("这是秋季");break;
case SeasonInterface.WINTER:
System.out.println("这是冬季");break;
default:
System.out.println("这不是四季的常量值");
}
}
public static void printSeason2(SeasonEnum season) {
switch (season) {//switch语句
case SPRING:
System.out.println("这是春季");break;
case SUMMER:
System.out.println("这是夏季");break;
case AUTUMN:
System.out.println("这是秋季");break;
case WINTER:
System.out.println("这是冬季");break;
}
}
public static void main(String[] args) {
printSeason1(SeasonInterface.SPRING); // 使用接口常量做参数
printSeason1(3); // 可以使用数字做出参数
printSeason1(-1); // 也使用接口常量值以外的数字冒充常量
printSeason2(SeasonEnum.WINTER); // 使用枚举做参数,只能用枚举有中的值,无法冒充
}
}
//例题13.1
13.1.2:深入了解枚举类型
枚举类型传统定义常量的方式,除具参数类型检测的优势外,还具有其他方面的优势。用户可以将一个枚举类型看作是一个类,它继承于Java.lang.Enum类,当定义一个枚举类型时,每个枚举类型成员都可以看作是枚举类型的一个实例,这些枚举类型成员都默认被final、 public、 static修饰,所以当使用枚举类型成员时直接使用枚举类型名称调用枚举类型成员即可。由于枚举类型对于继承Java.lang.Enum类,所以该类中一些操作枚举。类型的方法都可以应用到枚举类型中表13.1中列举了枚举类型中的常用方法。
表13.1 深入了解枚举类型
方法 具体含义 使用方法 举例
values() 该方法可以将枚举类型成员以数组的形式返回。 枚举类型名称.values() Constants2.values()
valueOf() 该方法可以实现将普通字符串转换为枚举实例 枚举类型名称.valueOf() Constants2.valueOf("abc")
comepareTo() 该方法用于比较两个枚举对象在定义时的顺序 枚举对象》compareTo()9
Constants_A.compareTo
(Constans_B)
ordinal() 该方法用于比较两个枚举对象在定义时该方法用于得到枚举成员的位置索引 枚举对象.ordinal() Constans_A.ordinal()
这里有一个错误出现:将这两张图对比,发现SeaonEnum相同,例题13.2中错误意思为这个SeaonEnum已经被定义,所以将SeaonEnum修改为另一个就好了
.values()方法:枚举类型实例包含一个values()方法,该方法将枚举中的所有枚举值以数组的形式返回
package thirteen;
enum Season{
SPRING,SUMMER,AUTUMN,WINTER //四季枚举
}
public class ShowEnum {
public static void main(String[] args) {
SeasonEnum es[] = SeasonEnum.values();
for (int i = 0; i < es.length; i++) {
System.out.println("枚举常量:" + es[i]);
}
}
}
//例题13.2
2.valuesOf()方法与compareTo()方法:枚举类型中静态方法valuesOf()可以将普通次普通转换为枚举类型,而compareTo()方法用于比较两个枚举类型的对象定义时的顺序。
package thirteen;
enum SeasonEnum { // 四季枚举
SPRING, SUMMER, AUTUMN, WINTER
}
public class EnumMethodTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
SeasonEnum tmp = SeasonEnum.valueOf("SUMMER");// 根据字符串创建一个枚举值
SeasonEnum es[] = SeasonEnum.values();// 获取所有枚举值
for (int i = 0; i < es.length; i++) {
String message = "";// 待输出的消息
int result = tmp.compareTo(es[i]);// 记录两个枚举的比较结果
if (result < 0) {
message = tmp + "在" + es[i] + "的前个" + (-result) + "位置";
} else if (result > 0) {
message = tmp + "与" + es[i] + "的后个" + result + "位置";
} else if (result == 0) {
message = tmp + "与" + es[i] + "是同一个值";
}
System.out.println(message);
}
}
}
//例题13.3
3.Ordinal()方法:枚举类型中的Oridinal()方法用于获取某个枚举对象的位置索引值。
package thirteen;
enum Season2 { // 四季枚举
SPRING, SUMMER, AUTUMN, WINTER
}
public class EnumindexTest {
// TODO Auto-generated method stub
public static void main(String[] args) {
SeasonEnum es[] = SeasonEnum.values();
for (int i = 0; i < es.length; i++) {
System.out.println(es[i] + "在枚举类型中位置索引值" + es[i].ordinal());
}
}
}
//例题13.4
.枚举类型中的构造方法:在枚举类型中可以添加构造方法但是规定这个构造方法必须被private修饰符所修饰枚举类型定义的构造方法如下:
enum 枚举类型名称{
Constants_A("我是枚举成员A"),
Constants_B("我是枚举成员B"),
Constants_C("我是枚举成员C"),
Constants_D(3);
privte String description;
private Constants2(){ //定义默认构造方法
}
private Constants2(String description){ //定义带参的构造方法,参数类型为字符串型
this.description = description;
}
private Constants2(int i){ //定义带参数的构造方法,参数类型为整形
this.i = this.i+i;
}
}
从枚举类型构造方法的语法中可以看出,无论是无参构造方法还是有参构造方法,修饰权限都为private。定义一个1有参构造方法,需要对枚举类型成员相应的使用该构造方法。
package thirteen;
enum Season5 { // 四季枚举
SPRING("万物复苏"),
SUMMER("烈日炎炎"),
AUTUMN("秋草枯黄"),
WINTER("白雪皑皑");
private String remarks;// 枚举的备注
private Season5(String remarks) {// 构造方法
this.remarks = "我是" + this.toString() + ",我来之后" + remarks + "。";
}
public String getRemarks() {// 获取备注值
return remarks;
}
}
public class EnumIConstructTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Season5 es[] = Season5.values();
for (int i = 0; i < es.length; i++) {
System.out.println(es[i].getRemarks());
}
}
}
//例题13.5
13.1.3: 使用枚举类型的优势
枚举类型声明提供了一种对用户友好的变量定义方法,枚举了某种类型所有可能出现的值。总结枚举类型,它具有以下特点:
1.类型安全
2.紧凑有效的数据定义
3..可以和程序其他部分完美交互
4.运行效率高
13.2: 泛型
泛型实质上就是使程序员定义安全的类型。在没有出现泛型之前,Java也提供了对object类型的引用“任意化” 操作,这种“任意化”操作就是对object类型引用进行向下转型及向下转型操作,但某些强制类型转换的错误也许不会被编译器捕捉,而在运行后出现异常,可见强制类型转换存在安全隐患,所以在此提供了泛型机制。
13.2.1:回顾向上转型与向下转型
在介绍泛型之前,先来看一个例子,在项目中创建Test类,在该类中使基本类型向上转型为object类型
package thirteen;
public class Test {
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) {
Test t = new Test();
t.setB(Boolean.valueOf(true)); //向上转型操作
System.out.println(t.getB());
t.setB(Float.valueOf("12.3")); //向下转型操作
Float f = (Float)t.getB();
System.out.println(f);
}
}
在本实例中,Test类中定义了私有的成员变量b,它的类型为Object类型,同时为其定义了相应的setXXX()与getXXX()方法。在类的主方法中,将Boolean.valueOf(true)作为setB()方法的参数,向下转型会出现错误,语法错误没有出现编译器会接受此段代码,,但执行时会出现ClassCastException异常,而泛型机制就有效解决了这一问题。
示例代码如图所示
public class Javap223 {
testB(Float.valuesOf("12.3"));
Integer f = (Integer)t.getB();
System.out.println(f);
}
//错误代码示范《Java从入门到精通》p223页
13.2.2:定义泛型类
Object类为最上层的父类,为了提前预防发生异常,Java提供了泛型机制其语法如下
类名<T>
package thirteen;
public class Book<T> {
private T bookInfo;
public Book(T bookInfo) {
this.bookInfo = bookInfo;
}
public T getBookInfo() {
return bookInfo;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建参数为String类型的书名对象
Book<String> bookName = new Book<String>("《Java从入门们到精通》");
// 创建参数为String类型的作者对象
Book<String> bookAuthor = new Book<String>("明日科技");
// 创建参数为Double类型的价格对象
Book<Double> bookPrice = new Book<Double>(69.8);
// 创建参数为Boolean类型的附赠源码
Book<Boolean> hasSource = new Book<Boolean>(true);
// 控制台输出书名、作者、价格和是否附赠光盘
System.out.println("书名:" + bookName.getBookInfo());
System.out.println("作者:" + bookAuthor.getBookInfo());
System.out.println("价格:" + bookPrice.getBookInfo());
System.out.println("是否附赠源码?" + hasSource.getBookInfo());
}
}
//例题13.6
13.2.3: 泛型的常规方法
1.定义泛型时声明多个类型
在定义泛型类时,可以声明多个类型。
2.定义泛型类型时声明数组类型
package thirteen;
public class ArrayClass <T> {
private T[] array; // 定义泛型数组
public T[] getArray() {
return array;
}
public void setArray(T[] array) {
this.array = array;
}
public static void main(String[] args) {
ArrayClass<String> demo = new ArrayClass<String>();
String value[] = { "成员1", "成员2", "成员3", "成员4", "成员5" };
demo.setArray(value);
String array[] = demo.getArray();
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
}
}
//例题13.7
3.集合声明容器的元素
JDK中的集合接口、集合类都被定义了泛型,其中List<E>的泛型E实际上是element元素的首字母,Map<K,V>的泛型K和V就是key键和value值的首字母。常用的被泛型化的集合类如表12.3所示。
表13.2 常用的被泛型化的集合类
集合类 泛型定义
ArraryList ArrayList<E>
HasMap
HashMap<K,V>
HasSet HasSet<E>
package thirteen;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class AnyClass {
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)); // 根据键名获取键值
}
}
}
//例题13.8
13.2.4:泛型的高级用法
泛型的高级用法包括限制泛型可用类型和使用类型通配符等。
1.限制泛型可用类型
默认可以使用任何类型来实例化一个泛型类对象,但Java中也对泛型类实例的类型做了限制,语法如下:
class 类名称<T extends anyClass>
其中anyClass指某个接口或类。
使用泛型限制后半泛型类的类型必须实现或者继承anyClass这个接口或类,无论anyclass是接口还是类,在进行泛型限制时都必须使用extends关键字。
出现异常:异常为Hasamap不能·被实例化
package thirteen;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
public class LimitClass <T extends List>{
public static void main(String[]args) {
// 可以实例化已经实现List接口的类
LimitClass<ArrayList> l1 = new LimitClass<ArrayList>();
LimitClass<LinkedList> l2 = new LimitClass<LinkedList>();
// 这句是错误的,因为HashMap没有实现List()接口
LimitClass<HashMap> l3 = new LimitClass<HashMap>();
}
}
//例题13.9
这个例题本身就是错误的,不能运行。
如果我们想它不报错的话就将hash map改为导入类中的list。但仍然无法运行,只是不报错。如下
package thirteen;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
public class LimitClass <T extends List>{
public static void main(String[]args) {
// 可以实例化已经实现List接口的类
LimitClass<ArrayList> l1 = new LimitClass<ArrayList>();
LimitClass<LinkedList> l2 = new LimitClass<LinkedList>();
// 这句是错误的,因为HashMap没有实现List()接口
LimitClass<List> l3 = new LimitClass<List>();
}
}
2.使用类型通配符
在泛型机制中,提供了类型通配符,其主要作用是在创建一个泛型类对象时,限制这个泛型类的类型,实现或继承某个接口或类的子类。要声明这样一个对象可以使用“?”通配符来表示,同时使用extends。关键字来对泛型加以限制使用泛型类型通配符的语法,如下:
泛型类名称<?extends List> a=null;
其中,<? extends List>表示类型未知,当需要使用该泛型对象时,可以单独实例化,例如:
A <?extends List> a=null;//定了上界和下界
a=new A<ArraryList>();
a=new A<ArraryLinkList>();
像上述例子的HashMap类没有实现List接口,那编译器就会报错.除了可以实例化一个限制泛型类型的实例,还可以将该实例放置在方法的参数
public void do Something(A<? extendsList>a){}
在上述代码中,定义方式有效地限制了传入do Something()方法的参数类型。如果使用A<?>这种形式实例化泛型类对象,则默认表示可以将a指定为实例化Object及以下的子类类型,例如:
List <String>l1 = new ArraryList<String>(); //实例化一个对象
l1.add("成员"); //在集合中添加内容
List<?>l2=l1;
List<?>l3 = new LinkkedList<integer>();
System.out.println(2.get(0)); //获取集合中第一个值
在上面的例子中list类型的对象可以接受时君类型的和ArraryList集合也可以接受Integer类型的LLinkedList集合。
注意:List<?>12=11和List12=11有区别
使用通配符声明的名称实例化的对象不能对其加入新的信息,只能获取或删除。例如:
l1.set(0,"成员改变"); //没有使用通配符的对象调用set()方法
//l2.set(0,"成员改变");//使用通配符的对象调用set()方法,不能被调用。
//l3.set(0,1);
l2.get(0); //可以使用l2的获取集合中的值
l2.remove(0);//根据键名删除集合中的值
3.继承泛型类与接口
定义为泛型的类和接口也可以被继承与实现。例如让Subclass类继承Extendclass类的泛型,代码入下:
class Extend Class <T1>{}
Class Sub Class <T1,T2,T3>extends Extend Class <T1>{}
如果在Sub Class类继承Extend Class类时保留父类的泛型类型,需要在继承时期指明。如果没有指明,直接使用extends Extends Class语句进行继承操作,则SubClass类中的 T1T2和T3都会自动变为object类型,所以在一般情况下都将父类的泛型类型保留。
定义为泛型的接口也可以被实现,例如让Sub Class类实现Some Interface接口,并继承接口的泛型。代码如下:
interface SomeInterface<T1>{}
class SubClass<T1,T2,T3>implements SomeInterface<T1>{}
13.2.5:泛型总结
总结一下泛型的使用方法。
1泛型类型参数只能是类类型,不可以是简单类型,比如A< int>这种泛型定义就是错误的。
2.泛型的类型个数可以是多个
3.可以使用extends关键字限制泛型的类型
4.可以使用通配符限制泛型的类型