第十二章的学习
泛型类、泛型方法和泛型接口
泛型其实质就是将数据的类型参数化。使用泛型的主要优点是能够在编译时而不是在运行时检测出错误。
1、泛型类的定义是在类名后面加上 <>(<>里面是T,因打出来无法显示,标注说明)
格式为:[修饰符]class类名<>
泛型类的应用:
package 泛型与容器类;
public class 例题12_1<T> {
private T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
public static void main(String[] args) {
例题12_1<String>name=new 例题12_1<String>();
例题12_1<Integer>age=new 例题12_1<Integer>();
name.setObj("张三");
String newName=name.getObj();
System.out.println("姓名:"+newName);
age.setObj(19);
Integer newAge=age.getObj();
System.out.println("年龄:"+newAge);
}
}
注意:在实例化泛型类的过程中,实际类型必须是引用类型,即必须是类类型,不能用如int,double或char等这样的基本类型来替换类型参数T。
2、泛型接口的定义是在接口名后面加上<>
格式为:[public]interface 接口名<>
3、泛型方法的定义
格式为:[public][static]返回值类型 方法名(T参数)
package 泛型与容器类;
public class 例题12_2泛型方法 {
public static void main(String[] args) {
//定义数组
Integer[]num={2,3,4,5,6};
String[]str={"红","橙","黄","绿","青","蓝","紫"};
//用类名调用静态泛型方法
例题12_2泛型方法.display(num);
例题12_2泛型方法.display(str);
}
//定义泛型方法,E为类型参数
public static < E> void display(E[] list) {
for (int i=0;i<list.length;i++) {
System.out.print(list[i]+" ");
}
System.out.println();
}
}
注意:一个static方法,无法访问泛型类的类型参数,所以如果static方法需要使用泛型能力,必须使其成为泛型方法。
泛型限制和泛型通配符
1、限制泛型的可用类型
在定义泛型类时,默认可以使用任何类型来实例化一个泛型类对象,但JAVA语言也可以在用泛型类创建对象时对数据类型作出限制,其语法如下:
class ClassName<T extends anyClass 》
其中anyClass是指某个类或接口。
注意:对于实现了某接口的有限制泛型,也是用extends关键字,而不是用implements关键字。
有限制的泛型类:
package 泛型与容器类;
//定义泛型类
class GenMet<T> {
//定义泛型变量
private T t;
//定义泛型类的方法getObj
public T getObj() {
return t;
}
public void setObj(T t) {
this.t = t;
}
//定义泛型方法
public <U> void display( U u) {
System.out.println("泛型类的类型参数T:"+t.getClass().getName());
System.out.println("泛型方法的类型参数U:"+u.getClass().getName());
}
}
public class 例题12_3 {
public static void main(String[] args) {
GenMet<Integer> gen=new GenMet<Integer>();
gen.setObj(5);
System.out.println("第一次输出:");
//用字符串调用方法
gen.display("我是文本");
System.out.println("第二次输出:");
//用实数调用方法
gen.display(8.0);
}
}
说明:在定义泛型类时若没有使用extends关键字限制泛型的类型参数时,默认是Object类下的所有子类。
2、泛型的类型通配符和泛型数组的应用
通配符"?“的主要作用有两方面:一是用于创建可重新赋值但不可修改其内容的泛型对象;二是用在方法的参数中,限制传入不想要的类型实参。
类型通配符”?"的使用:
package 泛型与容器类;
class GeneralType<T>{
T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
//下面的方法接收的泛型类对象参数中的类型参数只能是String或String的子类
public static void showObj(GeneralType<?extends String>o) {
System.out.println("给出的值是:"+o.getObj());
}
}
public class 例题12_5 {
public static void main(String[] args) {
GeneralType<String>n=new GeneralType<String>();
n.setObj("贾璐璐");
//用类名调用showObj()方法输出
GeneralType.showObj(n);
GeneralType<Double>num=new GeneralType<Double>();
num.setObj(19.0);
System.out.println("数值型值;"+num.getObj());
}
}
在创建泛型类对象时,如果只使用了“?”通配符,则默认是“?extends Object”,所以“?”也被称为非受限通配。
由于JVM只是在编译时对泛型进行安全检查,所以特别强调一下几点:
(1)不能使用泛型的类型参数T创建对象。如T obj=new T()是错误的。
(2)不能使用类型参数T创建数组对象。如T[]a=new T[个数]是错误的。
(3)不能在静态环境中使用泛型类的类型参数T。例如:
public class Test<T>
{
public static T obj;//非法,使用了泛型类的类型参数T
public static void m(T obj1)//非法,使用了泛型类的类型参数T
{ }
static
{T obj2} //非法,使用了泛型类的类型参数T
(4)异常类不能是泛型的,即泛型类不能继承java.lang.Throwable类。
容器类
一、分类:大类型主要分为以下两种类型
1)Collection:一个独立元素的序列,这些元素都服从一条或者多条规则。 List必须按照插入的顺序保存元素,而set不能有重复的元素。Queue按照排队规则来确定对象产生的顺序(通常与它们被插入的顺序相同)。
2)Map:一组成对的“键值对”对象,允许你使用键来查找值。
二、具体区别
Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。一些Collection允许相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection的“子接口”如List和Set。
1、List接口
实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。
(1)LinkedList类
基于链表的数据结构,允许null元素,增加、删除、修改元素方面效率比ArrayList高。
注意:LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:List list = Collections.synchronizedList(new LinkedList(…));
(2) ArrayList类
基于数组的数据结构,不同步,线程不安全,查询(get set)效率高。
如果涉及到堆栈,队列等操作,应该考虑用List,对于需要快速插入,删除元素,应该使用LinkedList类,如果需要快速随机访问元素,应该使用ArrayList类。
利用LinkedList类构造一个先进后出的堆栈。
package 泛型与容器类;
import java.util.LinkedList;
import java.util.Scanner;
class StringStack {
//创建LinkedList对象ld
private LinkedList<String>ld=new LinkedList<String>();
//入栈操作
public void push(String name) {
//将name增加到链表的头
ld.addFirst(name);
}
//出栈操作
public String pop() {
//获取并移出堆栈中的第一个元素
return ld.removeFirst();
}
//判断堆栈是否为空
public boolean isEmpty() {
return ld.isEmpty();
}
}
public class 例题12_7 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
StringStack stack=new StringStack();
System.out.println("请输入数据(quit结束):");
while(true) {
//输入数据
String input=sc.next();
if(input.equals("quit"))
break;
//入栈
stack.push(input);
}
System.out.println("先进后出的顺序:");
while(!stack.isEmpty()) {
System.out.println(stack.pop()+" ");
}
}
}
2、Set接口
Set容器类主要有HashSet和TreeSet等。
共同点:
元素不重复
实现了Java.util.Set接口。
1)HashSet类
-> 不保证集合中元素的顺序
->允许包含值为null的元素,但最多只能有一个null元素。
public class TestHashSet
{
public static void main(String [] args)
{
HashSet h=new HashSet();
h.add("1st");
h.add("2nd");
h.add(new Integer(3));
h.add(new Double(4.0));
h.add("2nd"); //重复元素,未被添加
h.add(new Integer(3)); //重复元素,未被添加
h.add(new Date());
System.out.println("开始:size="+h.size());
Iterator it=h.iterator();
while(it.hasNext())
{
Object o=it.next();
System.out.println(o);
}
h.remove("2nd");
System.out.println("移除元素后:size="+h.size());
System.out.println(h);
}
}
2)TreeSet
TreeSet描述的是Set的一种变体——可以实现排序等功能的集合,它在讲对象元素添加到集合中时会自动按照某种比较规则将其插入到有序的对象序列中,并保证该集合元素组成的读uixiangxulie时刻按照“升序”排列。
三、Map集合接口
Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个 value。Map接口提供3种集合的视图,Map的内容可以被当作一组key集合,一组value集合,或者一组key-value映射。映射接口Map常用的实现类有HashMap和TreeMap。
(1)HashMap类是基于哈希表的Map接口的实现,允许使用null值和null键,但必须保证键的唯一性,HashMap是无序的。
(2)TreeMap类中的映射关系存在一定的顺序,不允许键对象是null。TreeMap是有序的。