Java SE第五十三讲:泛型详解

在讲新知识之前先复习上一讲内容 JavaSE第五十二讲HashSet和HashMap源代码剖析 在上一讲的基础上扩充集合中的另外知识内容:

1. Map.Entry类

    Map.Entry类使得可以操作映射的输入。回想由Map接口说明的entrySet()方法,调用该方法返回一个包含映射输入的集合(Set)。这些集合元素的每一个都是一个Map.Entry对象。这边的Map.Entry可以理解为上一讲最后图52-3内容中数组的中的一个个元素了,之前使用Map.Entry类都是通过new这个类,得到一个对象,通过这个对象调用getKey(),getValue()方法。如以下程序所示

Map.Entry entry = (Map.Entry)iter.next();    
String key = (String)entry.getKey();  
String value = (String)entry.getValue();  
【说明】:之所以要这么用,结合上一讲图示,是因为Entry对象里面已经封装好了key 和 value了,所以可以使用这种方式直接get到他们的信息了。

2. 从以前版本遗留下来的类和接口:

    java.util的最初版本中不包括类集框架。取而代之,它定义了几个类和接口提供专门的方法用于存储对象。随着在Java 2中引入类集,有几种最初的类被重新设计成支持类集接口。因此它们与框架完全兼容。尽管实际上没有类被摈弃,但其中某些仍被认为是过时的。当然,在那些重复从以前版本遗留下来的类的功能性的地方,通常都愿意用类集编写新的代码程序。一般地,对从以前版本遗留下来的类的支持是因为仍然存在大量使用它们的基本代码。包括现在仍在被Java 2的应用编程接口(API)使用的程序。

1) Vector

    Vector实现动态数组。这与ArrayList相似,但两者不同的是:Vector是同步的,并且它包含了许多不属于类集框架的从以前版本遗留下来的方法。随着Java 2的公布,Vector被重新设计来扩展AbstractList和实现List接口,因此现在它与类集是完全兼容的。

package com.ahuier3;

import java.util.Vector;

public class VectorTest {
	public static void main(String[] args) {
		Vector vector = new Vector();
		vector.add("Hello");
		vector.add("World");
		vector.add("Hello World");
		for(int i = 0; i < vector.size(); i++){
			System.out.println(vector.get(i));
		}
	}
}
编译执行结果:

Hello
World
Hello World

【说明】:由于Vector已经不再经常使用了,它与ArrayList所表现出来的在多线程环境下有所不同,查看Vector的源代码,而可以发现它与ArrayList的实现方式基本上一样的,所以Vector的源代码就不做剖析了。

2) Hashtable

    哈希表(Hashtable)[散列表]是原始java.util中的一部分同时也是Dictionary的一个具体实现。然而,Java2重新设计了散列表(Hashtable)以便它也能实现映射(Map)接口。因此现在Hashtable也被集成到类集框架中。它与HashMap相似,但它是同步的。

HashMap 与 ArrayList是比较新的产物,要详细剖析。

Hashtable 与 Vector 是旧时代的产物,做了解即可。

3) Properties

    它是Hashtable的子类Properties,虽然Hashtable已经不经常使用了,但是它的子类Properties却很经常使用。

属性(Properties)是Hashtable的一个子类。它用来保持值的列表,在其中关键字和值都是字符串(String)。Properties类被许多其他的Java类所使用。例如,当获得系统环境值时,System.getProperties()返回对象的类型。

查看JDK Doc文档中Properties类及其相应的方法,它与HashMap中的很多方法都是一样的。

String getProperty(String key) 
          Searches for the property with the specified key in this property list. 
String getProperty(String key, String defaultValue) 
          Searches for the property with the specified key in this property list. 

它的取键和取值的返回类型都是String类型。

Properties一般用在项目的配置文件中,一般定义在例如文本文件中,这些文本文件是以 .properties为后缀名,它都是以key = value的形式来实现的。它的文件常以如下格式写

name = zhangsan 

age = 10

address = shanghai

properties文件(属性文件)

用这种方式实现根据key打印value值比较方便。

package com.ahuier3;

import java.util.Iterator;
import java.util.Properties;
import java.util.Set;

public class PropertiesTest {
	public static void main(String[] args) {
		//System.getProperties();打印系统的环境变量
		Properties p = System.getProperties();
		
		//这边不知道key的信息,所以只能遍历整个整个key,取出相对应的value值的方法
		Set set = p.keySet();
		for(Iterator iter = set.iterator(); iter.hasNext();){
			String key = (String)iter.next();
			
			//法一:使用get(key)方法 返回的时候Object类型,强制转换为String类型。
			//String value = (String)p.get(key);
			
			//法二:直接使用 getProperty(key)方法,返回类型是String,则不需要强制类型转换。
			String value = p.getProperty(key);
			
			System.out.println(value);
			
		}
		
	}
}
编译执行结果会输出系统的环境变量。


2.  掌握jdk5.0中出现的新特性
1)泛型(Generics)
2)增强的“for”循环(Enhanced For loop)
3)自动装箱/自动拆箱(Autoboxing/unboxing)
4)类型安全的枚举(Type safe enums)
5)静态导入(Static import)
6)可变参数(Var args)

其中泛型是最重要的,在实际开发中最常见.

泛型是JDK1.5中一个最重要的特征。通过引入泛型,我们将获得编译时类型的安全和运行时更小地抛出ClassCastExceptions的可能.

package com.ahuier3;

import java.util.ArrayList;
import java.util.List;

public class ArrayListTest {
	public static void main(String[] args) {
		List list = new ArrayList();
		list.add("string");
		list.add(new Integer(1));
		list.add(new Boolean(false));
		
		String str = (String)list.get(0);
		Integer in = (Integer)list.get(1);
		String b = (String)list.get(2);
	}
}
这个程序以前讲过,编译结果正常,执行会出现类转换异常,因为boolean类型转换在编译过程中没有发现,但是在取这个元素时候,就会出现这种异常了。所以JDK 1.5中的泛型就是为了解决这种问题。为遵循这样一个特点,只要编译的时候不出问题,那么它的执行也不会出问题。在JDK1.5中,你可以声明一个集合将接收/返回的对象的类型

程序一:

package com.ahuier3;

public class BooleanFoo {
	private Boolean foo;

	public Boolean getFoo() {
		return foo;
	}

	public void setFoo(Boolean foo) {
		this.foo = foo;
	}
	
}
package com.ahuier3;

public class IntegerFoo {
	private Integer foo;

	public Integer getFoo() {
		return foo;
	}

	public void setFoo(Integer foo) {
		this.foo = foo;
	}
	
}
对这样的两个类,我们可以知道,他们的结构是相同的,他们的方法功能也是一样的,唯一不同的是他们的成员变量是不一样的,在这种情况下,如果我添加一个String的存取,就要新添加这种类型的类,如果继续添加,则这种情况下代价是很大的。所以可以使用另外一种方式,就是写一个类,就可以实现传递任何类型的成员变量存和取。

package com.ahuier3;

public class ObjectFoo {
	private Object foo;
	
	public Object getFoo() {
		return foo;
	}

	public void setFoo(Object foo) {
		this.foo = foo;
	}

	public static void main(String[] args) {
		ObjectFoo foo1 = new ObjectFoo();
		ObjectFoo foo2 = new ObjectFoo();
		
		//传进入boolean类型的对象
		foo1.setFoo(new Boolean(false));
		Boolean b = (Boolean)foo1.getFoo();
		
		//传进入Integer类型的对象
		foo2.setFoo(new Integer(10));
		Integer i = (Integer)foo2.getFoo();
		
		ObjectFoo foo3 = new ObjectFoo();
		foo3.setFoo(new Boolean(false));
		String str = (String)foo3.getFoo();
	
	}
}
【说明】:以上这个程序就是用现有的 知识,实现了一个类传递不同类型对象的存和取的用,显然这个程序虽然符合了程序的需求,但是他跟集合一样还是存在弊端的,还是可能会出现类型转换异常。此时引入泛型,看一下泛型的实现。

泛型是JDK 5.0以上的特性,所以要Eclipse中的JDK编译级别要改为1.5以上的。

写程序,掌握如何定义泛型。

package com.ahuier.jdk5;
/*
 * T 并不是Java中的一个类,T就是泛型,T不代表数值本身,对象之类,T代表的是类型信息,比如传一个String,传一个Integer
 * 所以这个类的全称应该是 GenericFoo<T>
 * 这个定义T当然可以改写为其他如E,K,V之类的,最好是一个大写字母。
 */
public class GenericFoo<T> {
	private T foo;

	public T getFoo() {
		return foo;
	}

	public void setFoo(T foo) {
		this.foo = foo;
	}
	public static void main(String[] args) {
		
		// foo1指向的成员变量类型是 Boolean类型的,foo2指向的成员变量类型是Integer类型的。
		GenericFoo<Boolean> foo1 = new GenericFoo<Boolean>();
		GenericFoo<Integer> foo2 = new GenericFoo<Integer>();
		foo1.setFoo(new Boolean(false));
		foo2.setFoo(new Integer(10));
		
		//注意这边在编译的时候已经确定好了,getFoo()后的返回类型是Boolean,所以如果即便强制类型转换为其他的,会出现提示错误。
		//这就间接防止类转换异常的发生。
		Boolean b = foo1.getFoo();
		Integer i = foo2.getFoo();
		System.out.println(b);
		System.out.println(i);
		
		//foo1 = foo2;这种写法是错误,当使用泛型之后,foo1 的类型是GenericFoo<Boolean>, foo2的类型是GenericFoo<Integer>;
		//这边已经把泛型当做类型了。
		
		/*
		 * 这边在new的时候不适用泛型也是可以的,但是会出现警告。
		 * 用还是可以用的,但是需要进行强制类型转换
		 */
		GenericFoo foo3 = new GenericFoo();
		foo3.setFoo("hello");
		String str = (String)foo3.getFoo();
		System.out.println(str);
		
	}
}

编译 执行结果:

false
10
hello

【说明】:具体泛型的定义以及注意事项,可以结合程序以及程序注释进行掌握。

现在我们查看JDK Doc文档查看ArrayList类,

java.util
Class ArrayList<E>

boolean add(E e)
          Appends the specified element to the end of this list.

E set(int index, E element)
          Replaces the element at the specified position in this list with the specified element.

[可见,在ArrayList定义中也定义了泛型E,查看它的add()方法可以看到,它接受的也是一个泛型取出来也是E类型。]

我们之前在使用ArrayList过程都没有使用到它的泛型特性,所以在使用过程中[例如new ArrayList]的时候,经常会出现警告:


    - GenericFoo is a raw[原始] type. References to generic type GenericFoo<T>should beparameterized

[GenericFoo是一个原始类型,对GenericFoo<T> 的引用应该被参数化]


【总结】:所谓泛型:就是变量类型的参数化。







你可能感兴趣的:(java,泛型,properties,vector,ArrayList)