java学习笔记系列:

java学习笔记9--内部类总结

java学习笔记8--接口总结

java学习笔记7--抽象类与抽象方法

java学习笔记6--类的继承、Object类

java学习笔记5--类的方法

java学习笔记4--对象的初始化与回收

java学习笔记3--类与对象的基础

java学习笔记2--数据类型、数组

java学习笔记1--开发环境平台总结

本文地址:http://www.cnblogs.com/archimedes/p/java-study-note10.html,转载请注明源地址。

集合类中的数据类型

集合类中可以存储各种数据,数据一旦存入,其类型均会转化为Object类型。从集合类中取出数据时,一般均需要将Object类型转换回存入之前的实际类型

  1. Vector v=new Vector();
  2. v.add("张三"); //存入字符串
  3. String name=(String)v.get(0); //强制类型转换,OK
  4. v.add(new Date()); //存入当前时间对象,OK
  5.  
  6. // 由于Date类型不能转换为String,下面语句会在运行时发生错误,但这种错误在编译时不会被检查出来
  7. String date=(String)v.get(1); //编译器不会发现这里有问题

强类型集合

传统的集合类的实例中可以存储任意类型数据,这种集合类称为弱类型集合类。JDK1.5以后,引入了强类型集合类:

  • 强类型集合类中,只能存储指定类型的数据

  • 在强类型集合类中取出数据时,无需进行类型转换处理,如果数据类型不配备,编译时会直接报错

  • 强类型集合并没有引入新的类名,只需在定义原有集合对象时,用尖括号(<>)指明其存储的数据类型名称即可。

举个例子:

  1. //下面的向量类的实例中只能存储字符串类型数据
  2. Vector<String> v=new Vector<String>();
  3. v.add("hello"); //加入的是字符串,OK
  4. String name=v.get(0); //取出时,无需做类型转换,如果想在这种强类型集合中加入日期数据,在编译时就会报告错误
  5. v.add(new Date()); //编译器会直接报告类型不匹配错误

泛型类

定义泛型(Generics)类

强类型集合采用了JDK1.5引入的泛型语法。泛型相当于类中一种特殊的类型,这种类型的特点是在实例化该类时可指定为某个具体的实际类型。

声明包含泛型的类的格式如下:

  1. [访问修饰符] class 类名<泛型1,泛型2,…> {
  2. 泛型1 泛型成员1;
  3. 泛型2 泛型成员2;
  4. //....
  5. }

声明中的泛型1、泛型2等等泛型符号可以是任意合法的Java标识符。

泛型类的声明示例

  1. //此处声明了一个包含泛型T的泛型类,T代表所有可能的类型,而T的实际类型在Generic类实例化时指定。
  2. class Generic<T> {
  3. private T f; //f为泛型成员
  4. public void setF(T f) {//setF方法的参数类型为泛型T
  5. this.f = f;
  6. }
  7. public T getF() {//getF方法的返回类型为泛型T
  8. return f;
  9. }
  10. }

泛型类的实例化

创建泛型类的实例时,可以使用一对尖括号指定泛型的真正类型

  1. public class test {
  2. public static void main(String args[ ]) {
  3. //f1中的泛型T在此指定为Boolean类型
  4. Generic<Boolean> f1 = new Generic<Boolean>();
  5.  
  6. //f2中的泛型T在此指定为Integer类型
  7. Generic<Integer> f2 = new Generic<Integer>();
  8.  
  9. //f1的setF方法只能接受Boolean类型的数据
  10. f1.setF(new Boolean(true));
  11. Boolean b = f1.getF();
  12. System.out.println(b);
  13.  
  14. //f2的setF方法只能接受Integer类型的数据
  15. f2.setF(new Integer(10));
  16. Integer i = f2.getF();
  17. System.out.println(i);
  18. }
  19. }

实例化时的泛型的默认类型

泛型类实例化时,并不一定要指明泛型对应的实际类型,此时会使用Object作为泛型的默认类型

  1. Generic f3 = new Generic();
  2. f3.setF(new Boolean(false));

编译时编译器会发出警告:

Note: Generic.java uses unchecked or unsafe operations.

Note: Recompile with -Xlint:unchecked for details.

建立类型为泛型类的数组

如果要建立泛型类的数组,需要注意new关键字后面不要加入泛型的实际类型名,如下所示:

  1. Generic<String>[] gs; //声明泛型类的数组
  2.  
  3. //先对泛型数组进行初始化
  4. gs = new Generic[5]; //不要写成new Generic<String>[5]
  5.  
  6. //再分别为每一个数组元素进行初始化
  7. gs[0] = new Generic<String>();//为第一个数组元素赋值
  8. //....

包含多个泛型的类定义示例

包含有两个泛型定义的类声明和实例化:

  1. class Generic2<T1, T2> {
  2. private T1 f1;
  3. private T2 f2;
  4. //...
  5. }
  6.  
  7. //给出泛型T1, T2的实际类型
  8. Generic<Integer, Boolean> f = new Generic<Integer, Boolean>();
  9.  
  10. //没有给出泛型T1, T2的实际类型
  11. Generic f1 = new Generic(); //T1, T2将默认为是Object类型

泛型成员的使用

在泛型类中的泛型成员不能直接实例化,其实例必须要通过方法的参数传递给泛型成员:

  1. class Generic<T> {
  2. private T[] array; //此处不能用new T[]实例化array
  3. public void setArray(T[] array) {
  4. this.array = array;
  5. }
  6. public T[] getArray() {
  7. return array;
  8. }
  9. }

测试程序:

  1. public class test {
  2. public static void main(String args[ ]) {
  3. String[] strs = {"red", "black", "green"};
  4. Generic<String> f = new Generic<String>();
  5. //向泛型成员array传递实际的字符串数组
  6. f.setArray(strs);
  7. //读取泛型成员array的值,将其赋给字符串数组变量strs
  8. strs = f.getArray();
  9. }
  10. }

泛型成员的可用方法

由于泛型类型只有在类实例化后才能确定,类中的泛型成员只能使用Object类型中的方法:

  1. class Generic<T>{
  2. T f;
  3. void setF(T f){ this.f = f; }
  4. //....
  5. void doSome(){
  6. //getClass和toString都是Object中的方法
  7. System.out.println(f.getClass().getName());
  8. System.out.println(f.toString());
  9. }
  10. }

测试程序:

  1. public class javatest {
  2. public static void main(String args[ ]) {
  3. String strs = "hello";
  4. Generic<String> f = new Generic<String>();
  5. f.setF(strs);
  6. f.doSome();
  7. }
  8. }

限制泛型上限类型

extends关键字用来指定泛型的上限,在实例化泛型类时,为该泛型指定的实际类型必须是指定类的子类或指定接口的子接口

  1. import java.util.List;
  2. public class ListGeneric<T extends List> {
  3. private T list;
  4. public void setList(T list) {
  5. this.list = list;
  6. }
  7. public T getList() {
  8. return list;
  9. }
  10. }

在限定泛型的类型时,无论要限定的是接口或是类,都要使用extends关键词

测试例子:

  1. ListGeneric<Vector> f1 = new ListGeneric<Vector>();
  2. ListGeneric<ArrayList> f2 = new ListGeneric<ArrayList>();

如果不是List的类型,编译时就会发生错误:

  1. ListGeneric<HashMap> f3 = new ListGeneric<HashMap>();
  2. type parameter java.util.HashMap is not within its bound
  3. ListGeneric<HashMap> f3 = new ListGeneric<HashMap>();

默认的泛型限制类型

定义泛型类别时,如果只写以下代码:

  1. class Generic<T> {
  2. //...
  3. }

相当于下面的定义方式:

  1. class Generic<T> extends Object {
  2. //...
  3. }

限定泛型上限后的成员可用方法:

泛型类型的上限一经限定,类中的泛型成员就可使用上限类型中的方法和其他可用成员:
  1. class ListGeneric<T extends List>{
  2. private T list;
  3. public void setList(T list) {
  4. this.list = list;
  5. }
  6. public void doSome() {
  7. //ad、get方法都是List接口中定义的方法
  8. list.add(new Integer(0));
  9. System.out.println(list.get(0));
  10. }
  11. }

Object是所有类的父类,因此,所有的类型的实例都可赋值给声明为Object类型的变量

  1. Boolean f1 = new Boolean(true);
  2. Integer f2 = new Integer(1);
  3. Object f = f1; //ok
  4. f = f2; //ok

在实例化泛型类时,将泛型指定为Object类型却不存在着和其他类型之间的兼容性:

  1. Generic<Boolean> f1 = new Generic<Boolean>();
  2. Generic<Integer> f2 = new Generic<Integer>();
  3. Generic<Object> f=f1; //f1和f类型并不兼容,发生编译错误
  4. f=f2; //f2和f类型同样不兼容,也会发生编译错误

泛型通配字符(Wildcard)

泛型类实例之间的不兼容性会带来使用的不便。使用泛型通配符(?)声明泛型类的变量可以解决这个问题

  1. Generic<Boolean> f1 = new Generic<Boolean>();
  2. Generic<Integer> f2 = new Generic<Integer>();
  3. Generic<Object> f3 = new Generic<Object>();
  4. Generic<?> f;
  5. f = f1; //ok
  6. f = f2; //ok
  7. f = f3; //ok

通配符也可以用于方法的参数类型的声明,表示该参数可接受对应泛型类型的任意实例。

以下类定义中的printCollection方法可以打印任意强类型集合中的内容

  1. class test {
  2. //Collection<?>可以匹配任意强类型的集合
  3. static void printCollection(Collection<?> c) {
  4. for(Object o : c)
  5. System.out.println(o);
  6. }
  7. }

和限制泛型的上限相似,同样可以使用extends关键字限定通配符匹配类型的上限:

  1. Generic<? extends List> f = null;
  2. f = new Generic<ArrayList>(); //ok
  3. //...
  4. f = new Generic<Vector>(); //ok
  5. //...
  6. //以下语句会发生编译错误,因为HashMap没有实现List接口
  7. f = new Generic<HashMap>();
  8. incompatible types
  9. found : Generic<java.util.HashMap>
  10. required: Generic<? extends java.util.List>
  11. f = new Generic<HashMap>();

限定通配符匹配类型的下限

还可以使用super关键词将通配符匹配类型限定为某个类型及其父类型:

  1. //将f限定为只能代表采用java.sql.Date的父类实例化的
  2. Generic<? super java.sql.Date> f = null;
  3. f = new Generic<java.sql.Date>(); //ok
  4.  
  5. //OK,java.util.Date是java.sql.Date的父类
  6. f = new Generic<java.util.Date>();
  7.  
  8. //错误,因为String不是java.sql.Date的父类
  9. f = new Generic<String>();

泛型方法

不仅类可以声明泛型,类中的方法也可以声明仅用于自身的泛型,这种方法叫做泛型方法。其定义格式为:

访问修饰符 <泛型列表> 返回类型 方法名(参数列表){

实现代码

}

其中泛型列表为用逗号分隔的合法Java标识符。

在泛型列表中声明的泛型,可用于该方法的返回类型声明、参数类型声明和方法代码中的局部变量的类型声明。类中其他方法不能使用当前方法声明的泛型。使用泛型方法可以解决上述的泛型通配符造成的问题

泛型方法声明示例:

  1. class cc{
  2. /*
  3. 方法fun声明了一个泛型T,该方法将任意类型的数组a中的所有
  4. 元素复制到相应的强类型集合c当中而不会导致编译错误。
  5. 此处的泛型声明T仅作用于fun方法的声明部分和实现代码部分。
  6. */
  7. public static <T> void fun(T[] a, Collection<T> c){
  8. for(T o : a)
  9. c.add(o); //不会出现类似通配符的编译错误
  10. }
  11. }

调用泛型方法和调用普通方法没有任何不同,只需要传递含有具体类型的实参即可:

泛型方法的调用示例:

  1. //对cc中定义的泛型方法fun进行调用测试
  2. public class javatest {
  3. public static void main(String args[ ]) {
  4. String[] sa = new String[100];
  5. Collection<String> cs = new Vector<String>();
  6. Collection<Object> co = new Vector<Object>();
  7.  
  8. cc.fun(sa, cs); //fun中的泛型T此时匹配类型String
  9. cc.fun(sa, co); //fun中的泛型T此时匹配类型Object
  10. }
  11. }

限定泛型方法中泛型类型

泛型方法中的声明的泛型,同样可以使用extends关键字限定其类型的下限:

  1. class cc{
  2. //限定aToC方法中的泛型T必须是实现了序列化接口的类型
  3.   public static <T extends java.io.Serializable> void fun(T[] a,Collection<T> c){
  4. for(T o : a)
  5. c.add(o);
  6. }
  7. }

原始类型和向后兼容

先看一个泛型类定义

  1. public class GenericStack<E> {
  2. ArrayList<E> list = new ArrayList<E>();
  3. public int getSize() {
  4. return list.size();
  5. }
  6. public E peek() {
  7. return list.get(getSize() - 1);
  8. }
  9. public void push(E o) {
  10. list.add(o);
  11. }
  12. public E pop() {
  13. E o = list.get(getSize() - 1);
  14. list.remove(getSize() - 1);
  15. return o;
  16. }
  17. public boolean isEmpty() {
  18. return list.isEmpty();
  19. }
  20. }

可以使用泛型类而无需指定具体类型:

  1. GenericStack stack = new GenericStack();
  2.  
  3. //大体等价于于下面的语句
  4. GenericStack<Object> stack = new GenericStack<Object>();

再看下面的一个例子:

  1. class Max {
  2. public static Comparable max(Comparable o1, Comparable o2) {
  3. if(o1.compareTo(o2) > 0)
  4. return o1;
  5. return o2;
  6. }
  7. }
  8.  
  9. public class javatest {
  10. public static void main(String args[]) {
  11. Max.max("welcome", 12);
  12. }
  13. }

得到一个运行错误:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

结论:原始类型是不安全的,尽量使用泛型类型

一个更好的编写max方法的方式是使用泛型:

  1. class Max {
  2. public static <E extends Comparable<E>> E max(E o1, E o2) {
  3. if(o1.compareTo(o2) > 0)
  4. return o1;
  5. return o2;
  6. }
  7. }

再次调用上面的命令就会显示一个编译错误,由于max方法的两个参数必须是相同的类型

继承中的泛型

继承时如需保留父类泛型,需要在声明时加入父类泛型

  1. class subGeneric<T1, T2, T3> extends Generic<T1, T2> {
  2. private T3 f3;
  3. public void setF3(T3 f3) {
  4. this.f3 = f3;
  5. }
  6. public T3 getF3() {
  7. return f3;
  8. }
  9. }

如果不保留父类中的泛型声明,则继承下来的T1与T2自动变为Object类型。建议父类中的泛型声明在子类中都要保留

继承时指定父类的泛型类型

  1. public class SubGeneric<T3> extends Generic<String, Object> {
  2. private T3 f3;
  3. public void setF3(T3 f3) {
  4. this.f3 = f3;
  5. }
  6. public T3 getF3() {
  7. return f3;
  8. }
  9. }

泛型接口

接口也可包含泛型的声明:

  1. interface I<T1, T2> {
  2. T1 getT1();
  3. T2 getT2();
  4. //...
  5. }

实现泛型接口时,类在定义时可以不声明泛型接口中的泛型,此时接口中的泛型也会自动变为Object类型:

  1. class IC implements I {
  2. public Object getT1() { }
  3. public Object getT2() { }
  4. //...
  5. }

泛型接口的实现

  1. class IC<T1, T2> implements I<T1, T2> {
  2. public T1 getT1() { }
  3. public T2 getT2() { }
  4. //...
  5. }
  6.  
  7. I<String, Integer> i = new IC<String, Integer>();

实现泛型接口时指定泛型类型

在实现泛型接口时,也可直接指定接口中的泛型的实际类型:

  1. interface I<T1, T2> {
  2. T1 getT1();
  3. T2 getT2();
  4. //...
  5. }
  6. //实现接口I时,直接指定泛型T1、T2的类型
  7. class IC implements I<String, Integer> {
  8. //由于指定接口I中T1类型为String,getT1返回类型必须为String
  9. public String getT1() { }
  10. //由于指定接口I中T2类型为Integer,getT2返回类型必须为Integer
  11. public Integer getT2() { }
  12. //...
  13. }

泛型和枚举

由于枚举类型不能直接实例化,所以枚举的定义中不能含有泛型的声明,但枚举中可包含泛型方法的定义。

  1. public enum TrafficLight{
  2. Red,Amber,Green;
  3. private int duration;
  4. public static <T> void avgDuration(Collection<T> carType){
  5. //....
  6. }
  7. //....
  8. }

java学习笔记10--泛型总结的更多相关文章

  1. 【原】Java学习笔记027 - 泛型

    package cn.temptation.test; import java.util.ArrayList; import java.util.Iterator; public class Samp ...

  2. Java 学习笔记(10)——容器

    之前学习了java中从语法到常用类的部分.在编程中有这样一类需求,就是要保存批量的相同数据类型.针对这种需求一般都是使用容器来存储.之前说过Java中的数组,但是数组不能改变长度.Java中提供了另一 ...

  3. 【Java学习笔记】泛型

    泛型: jdk1.5出现的安全机制 好处: 1.将运行时期的问题ClassCastException转到了编译时期. 2.避免了强制转换的麻烦. <>: 什么时候用? 当操作的引用数据类型 ...

  4. java学习笔记之泛型

    "泛型"这个术语的意思就是:"使用与许多许多的类型".泛型在编程语言中出现时,其最初的目的是希望类或方法能够具备最广泛的表达能力.如何做到这一点呢,正是通过解耦 ...

  5. Java学习笔记10

    31.编写当年龄age大于13且小于18时结果为true的布尔表达式age > 13 && age < 18 32.编写当体重weight大于50或身高大于160时结果为t ...

  6. java学习笔记(10) —— ActionContext、ServletActionContext、ServletRequestAware用法

    核心思想 1.ActionContext HttpServletRequest getAttribute setAttribute ActionContext get put //ActionCont ...

  7. Java学习笔记10(面向对象三:接口)

    接口: 暂时可以理解为是一种特殊的抽象类 接口是功能的集合,可以看作是一种数据类型,是比抽象类更抽象的"类" 接口只描述所应该具备的方法,并没有具体实现,具体实现由接口的实现类(相 ...

  8. Java学习笔记-10.io流

    1.输入流,只能从中读取数据,而不能向其写出数据.输出流,只能想起写入字节数据,而不能从中读取. 2.InputStream的类型有: ByteArrayInputStream 包含一个内存缓冲区,字 ...

  9. Java学习笔记10(面对对象:构造方法)

    在开发中经常需要在创建初始化对象时候明确对象的属性值, 比如Person对象创建的时候就给Person的属性name,age赋值, 这里就要用到构造方法: 构造方法是类的一种特殊方法,它的特殊性体现在 ...

  10. 《Java学习笔记(第8版)》学习指导

    <Java学习笔记(第8版)>学习指导 目录 图书简况 学习指导 第一章 Java平台概论 第二章 从JDK到IDE 第三章 基础语法 第四章 认识对象 第五章 对象封装 第六章 继承与多 ...

随机推荐

  1. eclipse maven 配置

    http://www.cnblogs.com/little-YTMM/p/5970878.html

  2. elementUI 学习之获取输入框的内容

    <div id="app"> <el-input v-model="input1" placeholder="请输入内容" ...

  3. 关于maven工程的几个BUG

    换了个新的环境,重新导入的maven工程出现了2个BUG: 1.Could not calculate build plan: Plugin org.apache.maven.plugins:mave ...

  4. iOS 9的新的改变 iOS SDK Release Notes for iOS 9 说了些改变

    iOS 9的新的改变 iOS SDK Release Notes for iOS 9 说了些改变   看了下还算能理解!!!有兴趣可以看看哈!!!不喜勿喷!!后面的对于废除的方法什么有用感觉!!!   ...

  5. iTerm2配置

    1.颜色Solarized 首先下载 Solarized: $ git clone git://github.com/altercation/solarized.git Terminal/iTerm2 ...

  6. UOJ #35. 后缀排序 后缀数组 模板

    http://uoj.ac/problem/35 模板题,重新理了一遍关系.看注释吧.充分理解了倍增的意义,翻倍之后对上一次排序的利用是通过一种类似于队列的方式完成的. #include<ios ...

  7. 【tarjan+拓扑】BZOJ3887-[Usaco2015 Jan]Grass Cownoisseur

    [题目大意] 给一个有向图,然后选一条路径起点终点都为1的路径出来,有一次机会可以沿某条边逆方向走,问最多有多少个点可以被经过?(一个点在路径中无论出现多少正整数次对答案的贡献均为1) [思路] 首先 ...

  8. (转载)打破某些大牛比较呵呵的MySQL无file权限读root hash的谣言

    如题.比如乌云社区发帖的这位大牛http://zone.wooyun.org/content/12432 看那帖子标题就很喜感有木有,大概意思就是创建了一个没有file权限的账户test,然后不能lo ...

  9. lightoj 1296 - Again Stone Game 博弈论

    思路:由于数据很大,先通过打表找规律可以知道, 当n为偶数的时候其SG值为n/2; 当n为奇数的时候一直除2,直到为偶数m,则SG值为m/2; 代码如下: #include<stdio.h> ...

  10. nginx 访问第三方服务(1)

    nginx提供了两种全异步方式来与第三方服务通信,分别是upstream和subrequest. upstream:nginx为代理服务器,作消息透传.将第三方服务的内容原封不动的返回给用户. sub ...