JAVA高级编程

数据结构

Java 数据结构

Java工具包提供了强大的数据结构。在Java中的数据结构主要包括以下几种接口和类

  • 枚举(Enumeration)
  • 位集合(BitSet)
  • 向量(Vector)
  • 栈(Stack)
  • 字典(Dictionary)
  • 哈希表(Hashtable)
  • 属性(Properties)

以上这些类是传统遗留的,在Java2中引入了一种新的框架-集合框架(Collection),我们后面再讨论。

枚举(Enumeration)

枚举(Enumeration)接口虽然它本身不属于数据结构,但它在其他数据结构的范畴里应用很广。 枚举(The Enumeration)接口定义了一种从数据结构中取回连续元素的方式。

例如,枚举定义了一个叫nextElement 的方法,该方法用来得到一个包含多元素的数据结构的下一个元素。

Enumeration接口

Enumeration接口中定义了一些方法,通过这些方法可以枚举(一次获得一个)对象集合中的元素。

这种传统接口已被迭代器取代,虽然Enumeration 还未被遗弃,但在现代代码中已经被很少使用了。尽管如此,它还是使用在诸如Vector和Properties这些传统类所定义的方法中,除此之外,还用在一些API类,并且在应用程序中也广泛被使用。 下表总结了一些Enumeration声明的方法 -

序号 方法描述
1 boolean hasMoreElements( )
测试此枚举是否包含更多的元素。
2 Object nextElement( )
如果此枚举对象至少还有一个可提供的元素,则返回此枚举的下一个元素。

以下实例演示了Enumeration的使用 -

import java.util.Vector;
import java.util.Enumeration;
	
public class EnumerationTester {
	
	public static void main(String args[]) {
		Enumeration days;
		Vector dayNames = new Vector();
	
		dayNames.add("Sunday");
		dayNames.add("Monday");
		dayNames.add("Tuesday");
		dayNames.add("Wednesday");
		dayNames.add("Thursday");
		dayNames.add("Friday");
		dayNames.add("Saturday");
		days = dayNames.elements();
	
		while (days.hasMoreElements()) {
			System.out.println(days.nextElement());
		}
	}
}

以上实例编译运行结果如下 -

Sunday
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday

位集合(BitSet)

位集合类实现了一组可以单独设置和清除的位或标志。

该类在处理一组布尔值的时候非常有用,你只需要给每个值赋值一"位",然后对位进行适当的设置或清除,就可以对布尔值进行操作了。

Bitset类

一个Bitset类创建一种特殊类型的数组来保存位值。BitSet中数组大小会随需要增加。这和位向量(vector of bits)比较类似。

这是一个传统的类,但它在Java 2中被完全重新设计。

BitSet定义了两个构造方法。

第一个构造方法创建一个默认的对象 -

BitSet()

第二个方法允许用户指定初始大小。所有位初始化为0。

BitSet(int size)

下面的程序说明这个数据结构支持的几个方法 -

import java.util.BitSet;
public class BitSetDemo {
	
	public static void main(String args[]) {
		BitSet bits1 = new BitSet(16);
		BitSet bits2 = new BitSet(16);
	
		// set some bits
		for(int i = 0; i < 16; i++) {
			if((i % 2) == 0) bits1.set(i);
			if((i % 5) != 0) bits2.set(i);
		}
	
		System.out.println("Initial pattern in bits1: ");
		System.out.println(bits1);
		System.out.println("\nInitial pattern in bits2: ");
		System.out.println(bits2);
	
		// AND bits
		bits2.and(bits1);
		System.out.println("\nbits2 AND bits1: ");
		System.out.println(bits2);
	
		// OR bits
		bits2.or(bits1);
		System.out.println("\nbits2 OR bits1: ");
		System.out.println(bits2);
	
		// XOR bits
		bits2.xor(bits1);
		System.out.println("\nbits2 XOR bits1: ");
		System.out.println(bits2);
	}
}

以上实例编译运行结果如下 -

Initial pattern in bits1:
{0, 2, 4, 6, 8, 10, 12, 14}

Initial pattern in bits2:
{1, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14}

bits2 AND bits1:
{2, 4, 6, 8, 12, 14}

bits2 OR bits1:
{0, 2, 4, 6, 8, 10, 12, 14}

bits2 XOR bits1:
{}

向量(Vector)

向量(Vector)类和传统数组非常相似,但是Vector的大小能根据需要动态的变化。

和数组一样,Vector对象的元素也能通过索引访问。

使用Vector类最主要的好处就是在创建对象的时候不必给对象指定大小,它的大小会根据需要动态的变化。

Vector 类

Vector类实现了一个动态数组。和ArrayList和相似,但是两者是不同的:

  • Vector是同步访问的。
  • Vector包含了许多传统的方法,这些方法不属于集合框架。

Vector主要用在事先不知道数组的大小,或者只是需要一个可以改变大小的数组的情况。

下面的程序说明这个集合所支持的几种方法 -

import java.util.*;
public class VectorDemo {
	
	public static void main(String args[]) {
		// initial size is 3, increment is 2
		Vector v = new Vector(3, 2);
		System.out.println("Initial size: " + v.size());
		System.out.println("Initial capacity: " + v.capacity());
	
		v.addElement(new Integer(1));
		v.addElement(new Integer(2));
		v.addElement(new Integer(3));
		v.addElement(new Integer(4));
		System.out.println("Capacity after four additions: " + v.capacity());
	
		v.addElement(new Double(5.45));
		System.out.println("Current capacity: " + v.capacity());
	
		v.addElement(new Double(6.08));
		v.addElement(new Integer(7));
		System.out.println("Current capacity: " + v.capacity());
	
		v.addElement(new Float(9.4));
		v.addElement(new Integer(10));
		System.out.println("Current capacity: " + v.capacity());
	
		v.addElement(new Integer(11));
		v.addElement(new Integer(12));
		System.out.println("First element: " + (Integer)v.firstElement());
		System.out.println("Last element: " + (Integer)v.lastElement());
	
		if(v.contains(new Integer(3)))
			System.out.println("Vector contains 3.");
	
		// enumerate the elements in the vector.
		Enumeration vEnum = v.elements();
		System.out.println("\nElements in vector:");
	
		while(vEnum.hasMoreElements())
			System.out.print(vEnum.nextElement() + " ");
		System.out.println();
	}
}

以上实例编译运行结果如下 -

Initial size: 0
Initial capacity: 3
Capacity after four additions: 5
Current capacity: 5
Current capacity: 7
Current capacity: 9
First element: 1
Last element: 12
Vector contains 3.

Elements in vector:
1 2 3 4 5.45 6.08 7 9.4 10 11 12

栈(Stack)

栈(Stack)实现了一个后进先出(LIFO)的数据结构。

你可以把栈理解为对象的垂直分布的栈,当你添加一个新元素时,就将新元素放在其他元素的顶部。

当你从栈中取元素的时候,就从栈顶取一个元素。换句话说,最后进栈的元素最先被取出。

Stack类

栈是Vector的一个子类,它实现了一个标准的后进先出的栈。

堆栈只定义了默认构造函数,用来创建一个空栈。 堆栈除了包括由Vector定义的所有方法,也定义了自己的一些方法。

Stack()

下面的程序说明这个集合所支持的几种方法

import java.util.*;
public class StackDemo {
	
	static void showpush(Stack st, int a) {
		st.push(new Integer(a));
		System.out.println("push(" + a + ")");
		System.out.println("stack: " + st);
	}
	
	static void showpop(Stack st) {
		System.out.print("pop -> ");
		Integer a = (Integer) st.pop();
		System.out.println(a);
		System.out.println("stack: " + st);
	}
	
	public static void main(String args[]) {
		Stack st = new Stack();
		System.out.println("stack: " + st);
		showpush(st, 42);
		showpush(st, 66);
		showpush(st, 99);
		showpop(st);
		showpop(st);
		showpop(st);
		try {
			showpop(st);
		} catch (EmptyStackException e) {
			System.out.println("empty stack");
		}
	}
}

以上实例编译运行结果如下-

stack: [ ]
push(42)
stack: [42]
push(66)
stack: [42, 66]
push(99)
stack: [42, 66, 99]
pop -> 99
stack: [42, 66]
pop -> 66
stack: [42]
pop -> 42
stack: [ ]
pop -> empty stack

字典(Dictionary)

字典(Dictionary) 类是一个抽象类,它定义了键映射到值的数据结构。

当你想要通过特定的键而不是整数索引来访问数据的时候,这时候应该使用Dictionary。

由于Dictionary类是抽象类,所以它只提供了键映射到值的数据结构,而没有提供特定的实现。

Dictionary 类

Dictionary 类是一个抽象类,用来存储键/值对,作用和Map类相似。

给出键和值,你就可以将值存储在Dictionary对象中。一旦该值被存储,就可以通过它的键来获取它。所以和Map一样, Dictionary 也可以作为一个键/值对列表。

Dictionary类已经过时了。在实际开发中,你可以实现Map接口来获取键/值的存储功能。

哈希表(Hashtable)

Hashtable类提供了一种在用户定义键结构的基础上来组织数据的手段。

例如,在地址列表的哈希表中,你可以根据邮政编码作为键来存储和排序数据,而不是通过人名。

哈希表键的具体含义完全取决于哈希表的使用情景和它包含的数据。

Hashtable 类

Hashtable是原始的java.util的一部分, 是一个Dictionary具体的实现 。

然而,Java 2 重构的Hashtable实现了Map接口,因此,Hashtable现在集成到了集合框架中。它和HashMap类很相似,但是它支持同步。

像HashMap一样,Hashtable在哈希表中存储键/值对。当使用一个哈希表,要指定用作键的对象,以及要链接到该键的值。

然后,该键经过哈希处理,所得到的散列码被用作存储在该表中值的索引。

下面的程序说明这个数据结构支持的几个方法 -

import java.util.*;
	
public class HashTableDemo {
	
	public static void main(String args[]) {
		// Create a hash map
		Hashtable balance = new Hashtable();
		Enumeration names;
		String str;
		double bal;
	
		balance.put("Zara", new Double(3434.34));
		balance.put("Mahnaz", new Double(123.22));
		balance.put("Ayan", new Double(1378.00));
		balance.put("Daisy", new Double(99.22));
		balance.put("Qadir", new Double(-19.08));
	
		// Show all balances in hash table.
		names = balance.keys();
		while(names.hasMoreElements()) {
			str = (String) names.nextElement();
			System.out.println(str + ": " +
			balance.get(str));
		}
		System.out.println();
		// Deposit 1,000 into Zara's account
		bal = ((Double)balance.get("Zara")).doubleValue();
		balance.put("Zara", new Double(bal+1000));
		System.out.println("Zara's new balance: " +
		balance.get("Zara"));
	}
}

以上实例编译运行结果如下 -

Qadir: -19.08
Zara: 3434.34
Mahnaz: 123.22
Daisy: 99.22
Ayan: 1378.0

Zara's new balance: 4434.34

属性(Properties)

Properties 继承于 Hashtable.Properties 类表示了一个持久的属性集.属性列表中每个键及其对应值都是一个字符串。

Properties 类被许多Java类使用。例如,在获取环境变量时它就作为System.getProperties()方法的返回值。

Properties 类

Properties 继承于 Hashtable.表示一个持久的属性集.属性列表中每个键及其对应值都是一个字符串。

Properties 类被许多Java类使用。例如,在获取环境变量时它就作为System.getProperties()方法的返回值。

Properties 定义如下实例变量.这个变量持有一个Properties对象相关的默认属性列表。

下面的程序说明这个数据结构支持的几个方法 -

import java.util.*;
	
public class PropDemo {
	
	public static void main(String args[]) {
		Properties capitals = new Properties();
		Set states;
		String str;
	
		capitals.put("Illinois", "Springfield");
		capitals.put("Missouri", "Jefferson City");
		capitals.put("Washington", "Olympia");
		capitals.put("California", "Sacramento");
		capitals.put("Indiana", "Indianapolis");
	
		// Show all states and capitals in hashtable.
		states = capitals.keySet(); // get set-view of keys
		Iterator itr = states.iterator();
		while(itr.hasNext()) {
			str = (String) itr.next();
			System.out.println("The capital of " +
			str + " is " + capitals.getProperty(str) + ".");
		}
		System.out.println();
	
		// look for state not in list -- specify default
		str = capitals.getProperty("Florida", "Not Found");
		System.out.println("The capital of Florida is "
		+ str + ".");
	}
}

以上实例编译运行结果如下 -

The capital of Missouri is Jefferson City.
The capital of Illinois is Springfield.
The capital of Indiana is Indianapolis.
The capital of California is Sacramento.
The capital of Washington is Olympia.

The capital of Florida is Not Found.

集合

Java 集合

早在Java 2中之前,Java就提供了特设类。比如:Dictionary, Vector, Stack, 和Properties这些类用来存储和操作对象组。

虽然这些类都非常有用,但是它们缺少一个核心的,统一的主题。由于这个原因,使用Vector类的方式和使用Properties类的方式有着很大不同。

集合框架被设计成要满足以下几个目标。

  • 该框架必须是高性能的。基本集合(动态数组,链表,树,哈希表)的实现也必须是高效的。
  • 该框架允许不同类型的集合,以类似的方式工作,具有高度的互操作性。
  • 对一个集合的扩展和适应必须是简单的。

为此,整个集合框架就围绕一组标准接口而设计。你可以直接使用这些接口的标准实现,诸如: LinkedList, HashSet, 和 TreeSet等,除此之外你也可以通过这些接口实现自己的集合。

集合框架是一个用来代表和操纵集合的统一架构。所有的集合框架都包含如下内容 -

  • 接口 -是代表集合的抽象数据类型。接口允许集合独立操纵其代表的细节。在面向对象的语言,接口通常形成一个层次。
  • 实现(类)-是集合接口的具体实现。从本质上讲,它们是可重复使用的数据结构。
  • 算法 -是实现集合接口的对象里的方法执行的一些有用的计算,例如:搜索和排序。这些算法被称为多态,那是因为相同的方法可以在相似的接口上有着不同的实现。

除了集合,该框架也定义了几个Map接口和类。Map里存储的是键/值对。尽管Map不是collections,但是它们完全整合在集合中。

集合接口

集合框架定义了一些接口。本节提供了每个接口的概述 -

序号 接口描述
1 Collection 接口
Collection 是最基本的集合接口,一个 Collection 代表一组 Object,即 Collection 的元素, Java不提供直接继承自Collection的类,只提供继承于的子接口(如List和set)。
2 List 接口
List接口是一个有序的 Collection,使用此接口能够精确的控制每个元素插入的位置,能够通过索引(元素在List中位置,类似于数组的下标)来访问List中的元素,第一个元素的索引为 0,而且允许有相同的元素。
3 Set
Set 具有与 Collection 完全一样的接口,只是行为上不同,Set 不保存重复的元素。
4 SortedSet
继承于Set保存有序的集合。
5 Map
将唯一的键映射到值。
6 Map.Entry
描述在一个Map中的一个元素(键/值对)。是一个Map的内部类。
7 SortedMap
继承于Map,使Key保持在升序排列。
8 Enumeration
这是一个传统的接口和定义的方法,通过它可以枚举(一次获得一个)对象集合中的元素。这个传统接口已被迭代器取代。

Set和List的区别

  • 1. Set 接口实例存储的是无序的,不重复的数据。List 接口实例存储的是有序的,可以重复的元素。
  • 2. Set检索效率低下,删除和插入效率高,插入和删除不会引起元素位置改变 <实现类有HashSet,TreeSet>
  • 3. List和数组类似,可以动态增长,根据实际存储的数据的长度自动增长List的长度。查找元素效率高,插入删除效率低,因为会引起其他元素位置改变 <实现类有ArrayList,LinkedList,Vector>

集合类

Java提供了一套实现了Collection接口的标准集合类。其中一些是具体类,这些类可以直接拿来使用,而另外一些是抽象类,提供了接口的部分实现。

标准集合类汇总于下表 -

序号 类描述
1 AbstractCollection
实现了大部分的集合接口。
2 AbstractList
继承于AbstractCollection 并且实现了大部分List接口。
3 AbstractSequentialList
继承于 AbstractList ,提供了对数据元素的链式访问而不是随机访问。
4 LinkedList
该类实现了List接口,允许有null(空)元素。
5 ArrayList
该类也是实现了List的接口,实现了可变大小的数组,随机访问和遍历元素时,提供更好的性能。该类也是非同步的,在多线程的情况下不要使用。ArrayList 增长当前长度的50%,插入删除效率低。
6 AbstractSet
继承于AbstractCollection 并且实现了大部分Set接口。
7 HashSet
该类实现了Set接口,不允许出现重复元素,不保证集合中元素的顺序,允许包含值为null的元素,但最多只能一个。
8 LinkedHashSet
具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。
9 TreeSet
该类实现了Set接口,可以实现排序等功能。
10 AbstractMap
实现了大部分的Map接口。
11 HashMap
HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。
该类实现了Map接口,根据键的HashCode值存储数据,具有很快的访问速度,最多允许一条记录的键为null,不支持线程同步。
12 TreeMap
继承了AbstractMap,并且使用一颗树。
13 WeakHashMap
继承AbstractMap类,使用弱密钥的哈希表。
14 LinkedHashMap
继承于HashMap,使用元素的自然顺序对元素进行排序.
15 IdentityHashMap
继承AbstractMap类,比较文档时使用引用相等。

在前面的教程中已经讨论通过java.util包中定义的类,如下所示 -

序号 类描述
1 Vector
该类和ArrayList非常相似,但是该类是同步的,可以用在多线程的情况,该类允许设置默认的增长长度,默认扩容方式为原来的2倍。
2 Stack
栈是Vector的一个子类,它实现了一个标准的后进先出的栈。
3 Dictionary
Dictionary 类是一个抽象类,用来存储键/值对,作用和Map类相似。
4 Hashtable
Hashtable 是 Dictionary(字典) 类的子类,位于 java.util 包中。
5 Properties
Properties 继承于 Hashtable,表示一个持久的属性集,属性列表中每个键及其对应值都是一个字符串。
6 BitSet
一个Bitset类创建一种特殊类型的数组来保存位值。BitSet中数组大小会随需要增加。

一个Bitset类创建一种特殊类型的数组来保存位值。BitSet中数组大小会随需要增加。

集合算法

集合框架定义了几种算法,可用于集合和映射。这些算法被定义为集合类的静态方法。

在尝试比较不兼容的类型时,一些方法能够抛出 ClassCastException异常。当试图修改一个不可修改的集合时,抛出UnsupportedOperationException异常。

集合定义三个静态的变量:EMPTY_SET,EMPTY_LIST,EMPTY_MAP的。这些变量都不可改变。

序号 算法描述
1 Collection Algorithms
这里是一个列表中的所有算法实现。

集合框架定义了几种可应用于collections和maps的算法。

这些算法被定义为Collections类中的静态方法。 有些方法可能会抛出一个ClassCastException,当尝试比较不兼容的类型时会发生这种情况,或者是在尝试修改不可修改的集合时发生的UnsupportedOperationException。

例子以下是一个演示各种算法的示例。

import java.util.*;
public class AlgorithmsDemo {
	
public static void main(String args[]) {
	
		// Create and initialize linked list
		LinkedList ll = new LinkedList();
		ll.add(new Integer(-8));
		ll.add(new Integer(20));
		ll.add(new Integer(-20));
		ll.add(new Integer(8));
		
		// Create a reverse order comparator
		Comparator r = Collections.reverseOrder();
		
		// Sort list by using the comparator
		Collections.sort(ll, r);
		
		// Get iterator
		Iterator li = ll.iterator();
		System.out.print("List sorted in reverse: ");
		
		while(li.hasNext()) {
			System.out.print(li.next() + " ");
		}
		System.out.println();
		Collections.shuffle(ll);
		
		// display randomized list
		li = ll.iterator();
		System.out.print("List shuffled: ");
		
		while(li.hasNext()) {
			System.out.print(li.next() + " ");
		}
		
		System.out.println();
		System.out.println("Minimum: " + Collections.min(ll));
		System.out.println("Maximum: " + Collections.max(ll));
	}
}

这将产生以下结果 −

List sorted in reverse: 20 8 -8 -20
List shuffled: 20 -20 8 -8
Minimum: -20
Maximum: 20

如何使用迭代器

通常情况下,你会希望遍历一个集合中的元素。例如,显示集合中的每个元素。

一般遍历数组都是采用for循环或者增强for,这两个方法也可以用在集合框架,但是还有一种方法是采用迭代器遍历集合框架,它是一个对象,实现了Iterator 接口或ListIterator接口。

迭代器,使你能够通过循环来得到或删除集合的元素。ListIterator 继承了Iterator,以允许双向遍历列表和修改元素。

序号 迭代器方法描述
1 使用 Java Iterator,这里通过实例列出Iterator和listIterator接口提供的所有方法。

通常情况下,您需要循环访问集合中的元素。例如,您可能想要显示每个元素。最简单的方法是使用迭代器,它是一个实现Iterator或ListIterator接口的对象。

迭代器使您能够遍历集合,获取或删除元素。 ListIterator扩展Iterator以允许双向遍历列表以及修改元素。

在通过迭代器访问集合之前,您必须先获得一个集合。每个集合类都提供了一个将迭代器返回到集合开始的iterator()方法。通过使用这个迭代器对象,您可以访问集合中的每个元素,每次只能访问一个元素。

通常,要使用迭代器遍历集合的内容,请遵循以下步骤 -

  • 通过调用集合的iterator()方法获取集合开始的迭代器。
  • 设置一个调用hasNext()的循环。只要hasNext()返回true,就让循环迭代。
  • 在循环中,通过调用next()获取每个元素。

对于实现List的集合,您还可以通过调用ListIterator来获取迭代器。

例子这里是一个演示Iterator和ListIterator的例子。 它使用ArrayList对象,但一般原则适用于任何类型的集合。
当然,ListIterator仅适用于那些实现List接口的集合。

import java.util.*;
public class IteratorDemo {
	
public static void main(String args[]) {
		// Create an array list
		ArrayList al = new ArrayList();
		
		// add elements to the array list
		al.add("C");
		al.add("A");
		al.add("E");
		al.add("B");
		al.add("D");
		al.add("F");
		
		// Use iterator to display contents of al
		System.out.print("Original contents of al: ");
		Iterator itr = al.iterator();
		
		while(itr.hasNext()) {
			Object element = itr.next();
			System.out.print(element + " ");
		}
		System.out.println();
		
		// Modify objects being iterated
		ListIterator litr = al.listIterator();
		
		while(litr.hasNext()) {
			Object element = litr.next();
			litr.set(element + "+");
		}
		System.out.print("Modified contents of al: ");
		itr = al.iterator();
		
		while(itr.hasNext()) {
			Object element = itr.next();
			System.out.print(element + " ");
		}
		System.out.println();
		
		// Now, display the list backwards
		System.out.print("Modified list backwards: ");
		
		while(litr.hasPrevious()) {
			Object element = litr.previous();
			System.out.print(element + " ");
		}
		System.out.println();
	}
}

这将产生以下结果 −

Original contents of al: C A E B D F
Modified contents of al: C+ A+ E+ B+ D+ F+
Modified list backwards: F+ D+ B+ E+ A+ C+

如何使用比较器

TreeSet和TreeMap的按照排序顺序来存储元素. 然而,这是通过比较器来精确定义按照什么样的排序顺序。

这个接口可以让我们以不同的方式来排序一个集合。

序号 比较器方法描述
1 使用 Java Comparator
这里通过实例列出Comparator接口提供的所有方法

TreeSet和TreeMap都按排序顺序存储元素。 但是,它是比较器,它精确地定义了排序顺序的含义。
比较器接口定义了两个方法:compare()和equals()。 这里显示的compare()方法比较两个元素的顺序 -

compare方法

int compare(Object obj1, Object obj2)

obj1和obj2是要比较的对象。 如果对象相等,此方法返回零。 如果obj1大于obj2,它返回一个正值。 否则,返回负值。
通过重写compare(),可以改变对象排序的方式。 例如,要以相反的顺序排序,可以创建一个比较器来反转比较结果。

equals方法

boolean equals(Object obj)

obj是要测试平等的对象。 如果obj和调用对象都是Comparator对象并使用相同的顺序,则该方法返回true。 否则,它返回false。
重写equals()是不必要的,大多数简单的比较器都不会这样做。

import java.util.*;
	
class Dog implements Comparator<Dog>, Comparable<Dog> {
	private String name;
	private int age;
	Dog() {
	}
	
	Dog(String n, int a) {
		name = n;
		age = a;
	}
	
	public String getDogName() {
		return name;
	}
	
	public int getDogAge() {
		return age;
	}
	
	// Overriding the compareTo method
	public int compareTo(Dog d) {
		return (this.name).compareTo(d.name);
	}
	
	// Overriding the compare method to sort the age
	public int compare(Dog d, Dog d1) {
		return d.age - d1.age;
	}
}
	
public class Example {
	
	public static void main(String args[]) {
		// Takes a list o Dog objects
		List<Dog> list = new ArrayList<Dog>();
		
		list.add(new Dog("Shaggy", 3));
		list.add(new Dog("Lacy", 2));
		list.add(new Dog("Roger", 10));
		list.add(new Dog("Tommy", 4));
		list.add(new Dog("Tammy", 1));
		Collections.sort(list);   // Sorts the array list
		
		for(Dog a: list)   // printing the sorted list of names
			System.out.print(a.getDogName() + ", ");
		
		// Sorts the array list using comparator
		Collections.sort(list, new Dog());
		System.out.println(" ");
		
		for(Dog a: list)   // printing the sorted list of ages
			System.out.print(a.getDogName() +"  : "+ a.getDogAge() + ", ");
	}
}

这将产生以下结果 −

Lacy, Roger, Shaggy, Tammy, Tommy,
Tammy  : 1, Lacy  : 2, Shaggy  : 3, Tommy  : 4, Roger  : 10,

− Arrays类的排序与Collections相同。

总结

Java集合框架为程序员提供了预先包装的数据结构和算法来操纵他们。

集合是一个对象,可容纳其他对象的引用。集合接口声明对每一种类型的集合可以执行的操作。

集合框架的类和接口均在java.util包中。

泛型

Java 泛型

如果我们可以编写一个单独的排序方法,可以对整数数组,字符串数组或支持排序的任何类型的数组进行排序,那将会很好。

Java通用方法和泛型类使程序员能够分别使用单个方法声明,一组相关方法或单个类声明来指定一组相关类型。

泛型还提供编译时类型安全性,允许程序员在编译时捕获无效类型。

使用Java Generic概念,我们可以编写一个泛型方法来对一个对象数组进行排序,然后使用Integer数组,Double数组,字符串数组等来调用泛型方法来对数组元素进行排序。

泛型方法

你可以写一个泛型方法,该方法在调用时可以接收不同类型的参数。根据传递给泛型方法的参数类型,编译器适当地处理每一个方法调用。

下面是定义泛型方法的规则 -

  • 所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的<E>)。
  • 每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
  • 类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
  • 泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像int,double,char的等)。

例子下面的例子演示了如何使用泛型方法打印不同字符串的元素 -

public class GenericMethodTest {
	// generic method printArray
	public static < E > void printArray( E[] inputArray ) {
		// Display array elements
		for(E element : inputArray) {
			System.out.printf("%s ", element);
		}
		System.out.println();
	}
	
	public static void main(String args[]) {
		// Create arrays of Integer, Double and Character
		Integer[] intArray = { 1, 2, 3, 4, 5 };
		Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
		Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };
		
		System.out.println("Array integerArray contains:");
		printArray(intArray);   // pass an Integer array
		
		System.out.println("\nArray doubleArray contains:");
		printArray(doubleArray);   // pass a Double array
		
		System.out.println("\nArray characterArray contains:");
		printArray(charArray);   // pass a Character array
	}
}

这将产生以下结果 −

Array integerArray contains:
1 2 3 4 5

Array doubleArray contains:
1.1 2.2 3.3 4.4

Array characterArray contains:
H E L L O

有界类型参数

有时您可能需要限制允许传递给类型参数的类型。 例如,对数字进行操作的方法可能只想接受Number或其子类的实例。 这是有界类型参数的用途。

要声明有界的类型参数,请列出类型参数的名称,其后跟随extends关键字,后跟上边界。

例子下面的例子说明了如何在一般意义上使用扩展来表示“扩展”(如在类中)或“实现”(如在接口中)。 此示例是返回三个Comparable对象中最大的一个的泛型方法 -

public class MaximumTest {
	// determines the largest of three Comparable objects
	
	public static <T extends Comparable<T>> T maximum(T x, T y, T z) {
		T max = x;   // assume x is initially the largest
	
		if(y.compareTo(max) > 0) {
		max = y;   // y is the largest so far
		}
	
		if(z.compareTo(max) > 0) {
			max = z;   // z is the largest now
		}
		return max;   // returns the largest object
	}
	
	public static void main(String args[]) {
		System.out.printf("Max of %d, %d and %d is %d\n\n", 3, 4, 5, maximum( 3, 4, 5 ));
		System.out.printf("Max of %.1f,%.1f and %.1f is %.1f\n\n", 6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 ));
		System.out.printf("Max of %s, %s and %s is %s\n", "pear", "apple", "orange", maximum("pear", "apple", "orange"));
	}
}

这将产生以下结果 −

Max of 3, 4 and 5 is 5
	
Max of 6.6,8.8 and 7.7 is 8.8
	
Max of pear, apple and orange is pear

泛型类

泛型类声明看起来像一个非泛型类声明,除了类名后跟一个类型参数部分。

与泛型方法一样,泛型类的类型参数部分可以有一个或多个用逗号分隔的类型参数。 这些类被称为参数化类或参数化类型,因为它们接受一个或多个参数。

例子以下示例说明了我们如何定义泛型类 -

public class Box<T> {
	private T t;
	
	public void add(T t) {
		this.t = t;
	}
	
	public T get() {
		return t;
	}
	
	public static void main(String[] args) {
		Box<Integer> integerBox = new Box<Integer>();
		Box<String> stringBox = new Box<String>();
	
		integerBox.add(new Integer(10));
		stringBox.add(new String("Hello World"));
	
		System.out.printf("Integer Value :%d\n\n", integerBox.get());
		System.out.printf("String Value :%s\n", stringBox.get());
	}
}

这将产生以下结果 −

Integer Value :10
String Value :Hello World

序列化

Java 序列化

Java提供了一种称为对象序列化的机制,其中对象可以表示为包含对象数据的字节序列,以及有关对象类型和存储在对象中的数据类型的信息。

序列化对象写入文件后,可以从文件中读取并反序列化,即可以使用表示对象及其数据的类型信息和字节在内存中重新创建对象。

最令人印象深刻的是整个过程独立于JVM,这意味着一个对象可以在一个平台上序列化,并在完全不同的平台上进行反序列化。

类ObjectInputStream和ObjectOutputStream是包含用于序列化和反序列化对象的方法的高级流。

ObjectOutputStream类包含许多写入各种数据类型的写入方法,但特别突出了一种方法 -

public final void writeObject(Object x) throws IOException

上述方法序列化一个对象并将其发送到输出流。 同样,ObjectInputStream类包含以下反序列化对象的方法 -

public final Object readObject() throws IOException, ClassNotFoundException

此方法从流中检索下一个对象并将其反序列化。 返回值是Object,因此您需要将其转换为适当的数据类型。

为了演示序列化在Java中的工作原理,我将使用本书早期讨论过的Employee类。 假设我们有下面的Employee类,它实现了Serializable接口 -

public class Employee implements java.io.Serializable {
	public String name;
	public String address;
	public transient int SSN;
	public int number;
	
	public void mailCheck() {
	System.out.println("Mailing a check to " + name + " " + address);
	}
}

请注意,要成功序列化一个类,必须满足两个条件 -

  • 该类必须实现java.io.Serializable接口。
  • 该类中的所有字段都必须是可序列化的。 如果某个字段不可序列化,则必须标记为瞬态。

如果您想知道Java标准类是否可序列化,请检查该类的文档。 测试很简单:如果类实现java.io.Serializable,那么它是可序列化的; 否则,它不是。

序列化对象

ObjectOutputStream 类用来序列化一个对象,如下的 SerializeDemo 例子实例化了一个 Employee 对象,并将该对象序列化到一个文件中。

该程序执行后,就创建了一个名为 employee.ser 文件。该程序没有任何输出,但是你可以通过代码研读来理解程序的作用。

 

注意: 当序列化一个对象到文件时, 按照 Java 的标准约定是给文件一个 .ser 扩展名。

例子

import java.io.*;
public class SerializeDemo {
	
	public static void main(String [] args) {
		Employee e = new Employee();
		e.name = "Reyan Ali";
		e.address = "Phokka Kuan, Ambehta Peer";
		e.SSN = 11122333;
		e.number = 101;
	
		try {
			FileOutputStream fileOut =
				new FileOutputStream("/tmp/employee.ser");
			ObjectOutputStream out = new ObjectOutputStream(fileOut);
			out.writeObject(e);
			out.close();
			fileOut.close();
			System.out.printf("Serialized data is saved in /tmp/employee.ser");
		} catch (IOException i) {
			i.printStackTrace();
		}
	}
}

反序列化对象

下面的 DeserializeDemo 程序实例了反序列化,/tmp/employee.ser 存储了 Employee 对象。

例子

import java.io.*;
public class DeserializeDemo {

	public static void main(String [] args) {
		Employee e = null;
		try {
			FileInputStream fileIn = new FileInputStream("/tmp/employee.ser");
			ObjectInputStream in = new ObjectInputStream(fileIn);
			e = (Employee) in.readObject();
			in.close();
			fileIn.close();
		} catch (IOException i) {
			i.printStackTrace();
			return;
		} catch (ClassNotFoundException c) {
			System.out.println("Employee class not found");
			c.printStackTrace();
			return;
		}

		System.out.println("Deserialized Employee...");
		System.out.println("Name: " + e.name);
		System.out.println("Address: " + e.address);
		System.out.println("SSN: " + e.SSN);
		System.out.println("Number: " + e.number);
	}
}

这将产生以下结果 −

Deserialized Employee...
Name: Reyan Ali
Address:Phokka Kuan, Ambehta Peer
SSN: 0
Number:101

以下是需要注意的重要问题 -

  • try / catch块试图捕获由readObject()方法声明的ClassNotFoundException。 为了使JVM能够反序列化对象,它必须能够找到该类的字节码。 如果JVM在对象的反序列化过程中找不到类,则会引发ClassNotFoundException。
  • 注意readObject()的返回值被转换为一个Employee引用。
  • 对象序列化时,SSN字段的值为11122333,但由于该字段是瞬态的,因此该值未发送到输出流。 反序列化的Employee对象的SSN字段为0。

网络编程

Java 网络编程

网络编程是指编写运行在多个设备(计算机)的程序,这些设备都通过网络连接起来。

java.net 包中 J2SE 的 API 包含有类和接口,它们提供低层次的通信细节。你可以直接使用这些类和接口,来专注于解决问题,而不用关注通信细节。

java.net 包中提供了两种常见的网络协议的支持 -

  • TCP - TCP 是传输控制协议的缩写,它保障了两个应用程序之间的可靠通信。通常用于互联网协议,被称 TCP / IP。
  • UDP - UDP 是用户数据报协议的缩写,一个无连接的协议。提供了应用程序之间要发送的数据的数据包。

本教程主要讲解以下两个主题。

  • Socket 编程 - 这是使用最广泛的网络概念,它已被解释地非常详细。
  • URL 处理 - 这部分会在另外的篇幅里讲,点击这里更详细地了解在Java 语言中的 URL 处理。

Socket 编程

套接字使用TCP提供了两台计算机之间的通信机制。 客户端程序创建一个套接字,并尝试连接服务器的套接字。

当连接建立时,服务器会创建一个 Socket 对象。客户端和服务器现在可以通过对 Socket 对象的写入和读取来进行通信。

java.net.Socket 类代表一个套接字,并且 java.net.ServerSocket 类为服务器程序提供了一种来监听客户端,并与他们建立连接的机制。

以下步骤在两台计算机之间使用套接字建立TCP连接时会出现 -

  • 服务器实例化一个 ServerSocket 对象,表示通过服务器上的端口通信。
  • 服务器调用 ServerSocket 类的 accept() 方法,该方法将一直等待,直到客户端连接到服务器上给定的端口。
  • 服务器正在等待时,一个客户端实例化一个 Socket 对象,指定服务器名称和端口号来请求连接。
  • Socket 类的构造函数试图将客户端连接到指定的服务器和端口号。如果通信被建立,则在客户端创建一个 Socket 对象能够与服务器进行通信。
  • 在服务器端,accept() 方法返回服务器上一个新的 socket 引用,该 socket 连接到客户端的 socket。

连接建立后,通过使用 I/O 流在进行通信,每一个socket都有一个输出流和一个输入流,客户端的输出流连接到服务器端的输入流,而客户端的输入流连接到服务器端的输出流。

TCP 是一个双向的通信协议,因此数据可以通过两个数据流在同一时间发送.以下是一些类提供的一套完整的有用的方法来实现 socket。

ServerSocket 类的方法

服务器应用程序通过使用 java.net.ServerSocket 类以获取一个端口,并且侦听客户端请求。

ServerSocket 类有四个构造方法 -

序号 方法描述
1 public ServerSocket(int port) throws IOException
创建绑定到特定端口的服务器套接字。
2 public ServerSocket(int port, int backlog) throws IOException
利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号。
3 public ServerSocket(int port, int backlog, InetAddress address) throws IOException
使用指定的端口、侦听 backlog 和要绑定到的本地 IP 地址创建服务器。
4 public ServerSocket() throws IOException
创建非绑定服务器套接字。

创建非绑定服务器套接字。 如果 ServerSocket 构造方法没有抛出异常,就意味着你的应用程序已经成功绑定到指定的端口,并且侦听客户端请求。

这里有一些 ServerSocket 类的常用方法 -

序号 方法描述
1 public int getLocalPort()
返回此套接字在其上侦听的端口。
2 public Socket accept() throws IOException
侦听并接受到此套接字的连接。
3 public void setSoTimeout(int timeout)
通过指定超时值启用/禁用 SO_TIMEOUT,以毫秒为单位。
4 public void bind(SocketAddress host, int backlog)
将 ServerSocket 绑定到特定地址(IP 地址和端口号)。

Socket 类的方法

java.net.Socket 类代表客户端和服务器都用来互相沟通的套接字。客户端要获取一个 Socket 对象通过实例化 ,而 服务器获得一个 Socket 对象则通过 accept() 方法的返回值。

Socket 类有五个构造方法 -

序号 方法描述
1 public Socket(String host, int port) throws UnknownHostException, IOException.
创建一个流套接字并将其连接到指定主机上的指定端口号。
2 public Socket(InetAddress host, int port) throws IOException
创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
3 public Socket(String host, int port, InetAddress localAddress, int localPort) throws IOException.
创建一个套接字并将其连接到指定远程主机上的指定远程端口。
4 public Socket(InetAddress host, int port, InetAddress localAddress, int localPort) throws IOException.
创建一个套接字并将其连接到指定远程地址上的指定远程端口。
5 public Socket()
通过系统默认类型的 SocketImpl 创建未连接套接字

当 Socket 构造方法返回,并没有简单的实例化了一个 Socket 对象,它实际上会尝试连接到指定的服务器和端口。

下面列出了一些感兴趣的方法,注意客户端和服务器端都有一个 Socket 对象,所以无论客户端还是服务端都能够调用这些方法。

序号 方法描述
1 public void connect(SocketAddress host, int timeout) throws IOException
将此套接字连接到服务器,并指定一个超时值。
2 public InetAddress getInetAddress()
返回套接字连接的地址。
3 public int getPort()
返回此套接字连接到的远程端口。
4 public int getLocalPort()
返回此套接字绑定到的本地端口。
5 public SocketAddress getRemoteSocketAddress()
返回此套接字连接的端点的地址,如果未连接则返回 null。
6 public InputStream getInputStream() throws IOException
返回此套接字的输入流。
7 public OutputStream getOutputStream() throws IOException
返回此套接字的输出流。
8 public void close() throws IOException
关闭此套接字。

InetAddress 类的方法

这个类表示互联网协议(IP)地址。下面列出了 Socket 编程时比较有用的方法 -

序号 方法描述
1 static InetAddress getByAddress(byte[] addr)
在给定原始 IP 地址的情况下,返回 InetAddress 对象。
2 static InetAddress getByAddress(String host, byte[] addr)
根据提供的主机名和 IP 地址创建 InetAddress。
3 static InetAddress getByName(String host)
在给定主机名的情况下确定主机的 IP 地址。
4 String getHostAddress() 
返回 IP 地址字符串(以文本表现形式)。
5 String getHostName() 
获取此 IP 地址的主机名。
6 static InetAddress getLocalHost()
返回本地主机。
7 String toString()
将此 IP 地址转换为 String。

Socket 客户端实例

如下的 GreetingClient 是一个客户端程序,该程序通过 socket 连接到服务器并发送一个请求,然后等待一个响应。

// File Name GreetingClient.java
import java.net.*;
import java.io.*;

public class GreetingClient {

   public static void main(String [] args) {
	  String serverName = args[0];
	  int port = Integer.parseInt(args[1]);
	  try {
		 System.out.println("Connecting to " + serverName + " on port " + port);
		 Socket client = new Socket(serverName, port);
		 
		 System.out.println("Just connected to " + client.getRemoteSocketAddress());
		 OutputStream outToServer = client.getOutputStream();
		 DataOutputStream out = new DataOutputStream(outToServer);
		 
		 out.writeUTF("Hello from " + client.getLocalSocketAddress());
		 InputStream inFromServer = client.getInputStream();
		 DataInputStream in = new DataInputStream(inFromServer);
		 
		 System.out.println("Server says " + in.readUTF());
		 client.close();
	  } catch (IOException e) {
		 e.printStackTrace();
	  }
   }
}

Socket 服务端实例

如下的GreetingServer 程序是一个服务器端应用程序,使用 Socket 来监听一个指定的端口。

// File Name GreetingServer.java
import java.net.*;
import java.io.*;

public class GreetingServer extends Thread {
   private ServerSocket serverSocket;
   
   public GreetingServer(int port) throws IOException {
	  serverSocket = new ServerSocket(port);
	  serverSocket.setSoTimeout(10000);
   }

   public void run() {
	  while(true) {
		 try {
			System.out.println("Waiting for client on port " + 
			   serverSocket.getLocalPort() + "...");
			Socket server = serverSocket.accept();
			
			System.out.println("Just connected to " + server.getRemoteSocketAddress());
			DataInputStream in = new DataInputStream(server.getInputStream());
			
			System.out.println(in.readUTF());
			DataOutputStream out = new DataOutputStream(server.getOutputStream());
			out.writeUTF("Thank you for connecting to " + server.getLocalSocketAddress()
			   + "\nGoodbye!");
			server.close();
			
		 } catch (SocketTimeoutException s) {
			System.out.println("Socket timed out!");
			break;
		 } catch (IOException e) {
			e.printStackTrace();
			break;
		 }
	  }
   }
   
   public static void main(String [] args) {
	  int port = Integer.parseInt(args[0]);
	  try {
		 Thread t = new GreetingServer(port);
		 t.start();
	  } catch (IOException e) {
		 e.printStackTrace();
	  }
   }
}

编译客户端和服务器,然后按如下所示启动服务器 −

$ java GreetingServer 6066
Waiting for client on port 6066...

检查客户端程序如下 −

$ java GreetingClient localhost 6066
Connecting to localhost on port 6066
Just connected to localhost/127.0.0.1:6066
Server says Thank you for connecting to /127.0.0.1:6066
Goodbye!

发送邮件

Java 发送邮件

使用Java应用程序发送电子邮件非常简单,但首先应在计算机上安装JavaMail API和Java Activation Framework(JAF)。

  • 您可以从Java的标准网站下载最新版本的JavaMail(版本1.2)。
  • 您可以从Java的标准网站下载最新版本的JAF(版本1.1.1)。

下载并解压缩这些文件,在新创建的顶级目录中,您将找到两个应用程序的许多jar文件。 您需要在CLASSPATH中添加mail.jar和activation.jar文件。

发送一封简单的 E-mail

下面是一个发送简单E-mail的例子。假设你的localhost已经连接到网络。

// File Name SendEmail.java

import java.util.*;
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;

public class SendEmail {

   public static void main(String [] args) {    
	  // Recipient's email ID needs to be mentioned.
	  String to = "abcd@gmail.com";

	  // Sender's email ID needs to be mentioned
	  String from = "web@gmail.com";

	  // Assuming you are sending email from localhost
	  String host = "localhost";

	  // Get system properties
	  Properties properties = System.getProperties();

	  // Setup mail server
	  properties.setProperty("mail.smtp.host", host);

	  // Get the default Session object.
	  Session session = Session.getDefaultInstance(properties);

	  try {
		 // Create a default MimeMessage object.
		 MimeMessage message = new MimeMessage(session);

		 // Set From: header field of the header.
		 message.setFrom(new InternetAddress(from));

		 // Set To: header field of the header.
		 message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));

		 // Set Subject: header field
		 message.setSubject("This is the Subject Line!");

		 // Now set the actual message
		 message.setText("This is actual message");

		 // Send message
		 Transport.send(message);
		 System.out.println("Sent message successfully....");
	  } catch (MessagingException mex) {
		 mex.printStackTrace();
	  }
   }
}

编译并运行此程序以发送简单的电子邮件 −

$ java SendEmail
Sent message successfully....

如果你想发送一封e-mail给多个收件人,那么使用下面的方法来指定多个收件人ID -

void addRecipients(Message.RecipientType type, Address[] addresses)
   throws MessagingException

这里是参数的描述 -

  • type - 这将被设置为TO,CC或BCC。 此处CC代表Carbon Copy,BCC代表Black Carbon Copy。 示例:Message.RecipientType.TO。
  • addresses - 这是一组电子邮件ID。 在指定电子邮件ID时,您需要使用InternetAddress()方法。

发送一封 HTML E-mail

以下是从您的机器发送HTML电子邮件的示例。 这里假定你的本地主机连接到互联网并且能够发送电子邮件。

这个例子与前一个例子非常相似,除了这里我们使用setContent()方法来设置第二个参数是“text / html”的内容来指定HTML内容包含在消息中。

使用这个示例,您可以发送与您喜欢的HTML内容一样大的内容。

// File Name SendHTMLEmail.java

import java.util.*;
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;

public class SendHTMLEmail {

   public static void main(String [] args) {
	  // Recipient's email ID needs to be mentioned.
	  String to = "abcd@gmail.com";

	  // Sender's email ID needs to be mentioned
	  String from = "web@gmail.com";

	  // Assuming you are sending email from localhost
	  String host = "localhost";

	  // Get system properties
	  Properties properties = System.getProperties();

	  // Setup mail server
	  properties.setProperty("mail.smtp.host", host);

	  // Get the default Session object.
	  Session session = Session.getDefaultInstance(properties);

	  try {
		 // Create a default MimeMessage object.
		 MimeMessage message = new MimeMessage(session);

		 // Set From: header field of the header.
		 message.setFrom(new InternetAddress(from));

		 // Set To: header field of the header.
		 message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));

		 // Set Subject: header field
		 message.setSubject("This is the Subject Line!");

		 // Send the actual HTML message, as big as you like
		 message.setContent("<h1>This is actual message</h1>", "text/html");

		 // Send message
		 Transport.send(message);
		 System.out.println("Sent message successfully....");
	  } catch (MessagingException mex) {
		 mex.printStackTrace();
	  }
   }
}

编译并运行此程序以发送HTML电子邮件 −

$ java SendHTMLEmail
Sent message successfully....

发送带有附件的 E-mail

这里是一个例子,用你的机器发送带有附件的电子邮件。 这里假设你的本地主机已连接到互联网并且能够发送电子邮件。

// File Name SendFileEmail.java

import java.util.*;
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;

public class SendFileEmail {

   public static void main(String [] args) {     
	  // Recipient's email ID needs to be mentioned.
	  String to = "abcd@gmail.com";

	  // Sender's email ID needs to be mentioned
	  String from = "web@gmail.com";

	  // Assuming you are sending email from localhost
	  String host = "localhost";

	  // Get system properties
	  Properties properties = System.getProperties();

	  // Setup mail server
	  properties.setProperty("mail.smtp.host", host);

	  // Get the default Session object.
	  Session session = Session.getDefaultInstance(properties);

	  try {
		 // Create a default MimeMessage object.
		 MimeMessage message = new MimeMessage(session);

		 // Set From: header field of the header.
		 message.setFrom(new InternetAddress(from));

		 // Set To: header field of the header.
		 message.addRecipient(Message.RecipientType.TO,new InternetAddress(to));

		 // Set Subject: header field
		 message.setSubject("This is the Subject Line!");

		 // Create the message part 
		 BodyPart messageBodyPart = new MimeBodyPart();

		 // Fill the message
		 messageBodyPart.setText("This is message body");
		 
		 // Create a multipar message
		 Multipart multipart = new MimeMultipart();

		 // Set text message part
		 multipart.addBodyPart(messageBodyPart);

		 // Part two is attachment
		 messageBodyPart = new MimeBodyPart();
		 String filename = "file.txt";
		 DataSource source = new FileDataSource(filename);
		 messageBodyPart.setDataHandler(new DataHandler(source));
		 messageBodyPart.setFileName(filename);
		 multipart.addBodyPart(messageBodyPart);

		 // Send the complete message parts
		 message.setContent(multipart );

		 // Send message
		 Transport.send(message);
		 System.out.println("Sent message successfully....");
	  } catch (MessagingException mex) {
		 mex.printStackTrace();
	  }
   }
}

>编译并运行此程序以发送HTML电子邮件 −

$ java SendFileEmail
Sent message successfully....

用户认证部分

如果需要为电子邮件服务器提供用户标识和密码以进行身份验证,则可以按如下方式设置这些属性 -

props.setProperty("mail.user", "myuser");
props.setProperty("mail.password", "mypwd");

其余的电子邮件发送机制将保持如上所述。

多线程编程

Java 多线程编程

Java是一种多线程编程语言,这意味着我们可以使用Java开发多线程程序。 多线程程序包含两个或多个可以同时运行的部分,并且每个部分可以同时处理不同的任务,特别是在计算机有多个CPU时,可以优化使用可用资源。

根据定义,多任务就是多个进程共享公共处理资源(如CPU)的时候。 多线程将多任务的想法扩展到可以将单个应用程序中的特定操作细分为单独线程的应用程序。 每个线程都可以并行运行。 操作系统不仅在不同的应用程序之间划分处理时间,而且在应用程序内的每个线程之间划分处理时间

多线程技术使您能够以同一程序中多个活动同时进行的方式进行写入。

线程的生命周期

线程经历其生命周期的各个阶段。 例如,线程诞生,启动,运行,然后死亡。 下图显示了线程的完整生命周期。

以下是生命周期的各个阶段 -

  • 新建状态 - 新的线程在新的状态下开始其生命周期。 在程序启动线程之前,它一直处于这种状态。 它也被称为天生的线索。
  • 运行状态 - 在新生线程启动后,线程变为可运行。 处于这种状态的线程被认为正在执行其任务。
  • 等待状态 - 有时,当线程等待另一个线程执行任务时,线程将转换到等待状态。 只有当另一个线程指示等待线程继续执行时,线程才会转换回可运行状态。
  • 定时等待 - 可运行线程可以在指定的时间间隔内进入定时等待状态。 当该时间间隔到期或其正在等待的事件发生时,此状态中的线程将转换回可运行状态。
  • 死亡状态 - 一个可运行线程完成其任务或以其他方式终止时进入终止状态。

线程优先级

每个Java线程都有一个优先级,可帮助操作系统确定线程的排定顺序。

Java线程优先级在MIN_PRIORITY(常量1)和MAX_PRIORITY(常量10)之间。 默认情况下,每个线程的优先级为NORM_PRIORITY(常量为5)。

具有更高优先级的线程对于程序来说更重要,并且应该在低优先级线程之前分配处理器时间。 但是,线程优先级不能保证线程执行的顺序,并且依赖于平台。

通过实现可运行接口创建线程

如果您的类想要作为线程执行,那么您可以通过实现Runnable接口来实现此目的。 你需要遵循三个基本步骤 -

第1步
作为第一步,您需要实现Runnable接口提供的run()方法。 此方法为该线程提供入口点,并且您将在此方法中放入完整的业务逻辑。 以下是run()方法的简单语法 -

public void run( )

第2步
作为第二步,您将使用以下构造函数实例化一个Thread对象 -

Thread(Runnable threadObj, String threadName);

第3步
一旦创建了一个Thread对象,就可以通过调用start()方法来启动它,该方法执行对run()方法的调用。 以下是start()方法的简单语法 -

void start();

这是一个创建一个新线程并开始运行的例子 -

class RunnableDemo implements Runnable {
   private Thread t;
   private String threadName;
   
   RunnableDemo( String name) {
	  threadName = name;
	  System.out.println("Creating " +  threadName );
   }
   
   public void run() {
	  System.out.println("Running " +  threadName );
	  try {
		 for(int i = 4; i > 0; i--) {
			System.out.println("Thread: " + threadName + ", " + i);
			// Let the thread sleep for a while.
			Thread.sleep(50);
		 }
	  } catch (InterruptedException e) {
		 System.out.println("Thread " +  threadName + " interrupted.");
	  }
	  System.out.println("Thread " +  threadName + " exiting.");
   }
   
   public void start () {
	  System.out.println("Starting " +  threadName );
	  if (t == null) {
		 t = new Thread (this, threadName);
		 t.start ();
	  }
   }
}

public class TestThread {

   public static void main(String args[]) {
	  RunnableDemo R1 = new RunnableDemo( "Thread-1");
	  R1.start();
	  
	  RunnableDemo R2 = new RunnableDemo( "Thread-2");
	  R2.start();
   }   
}

这将产生以下结果 -

Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.

通过扩展一个线程类创建一个线程

创建线程的第二种方法是使用以下两个简单步骤创建一个扩展Thread类的新类。 这种方法在处理使用Thread类中可用方法创建的多个线程时提供了更大的灵活性。

第1步
您将需要重写Thread类中可用的run()方法。 此方法为该线程提供入口点,并且您将在此方法中放入完整的业务逻辑。 以下是run()方法的简单语法 -

public void run()

第2步
一旦创建了Thread对象,就可以通过调用start()方法来启动它,该方法执行对run()方法的调用。 以下是start()方法的简单语法 -

void start();

这是一个创建一个新线程并开始运行的例子 -

class ThreadDemo extends Thread {
   private Thread t;
   private String threadName;
   
   ThreadDemo( String name) {
	  threadName = name;
	  System.out.println("Creating " +  threadName );
   }
   
   public void run() {
	  System.out.println("Running " +  threadName );
	  try {
		 for(int i = 4; i > 0; i--) {
			System.out.println("Thread: " + threadName + ", " + i);
			// Let the thread sleep for a while.
			Thread.sleep(50);
		 }
	  } catch (InterruptedException e) {
		 System.out.println("Thread " +  threadName + " interrupted.");
	  }
	  System.out.println("Thread " +  threadName + " exiting.");
   }
   
   public void start () {
	  System.out.println("Starting " +  threadName );
	  if (t == null) {
		 t = new Thread (this, threadName);
		 t.start ();
	  }
   }
}

public class TestThread {

   public static void main(String args[]) {
	  ThreadDemo T1 = new ThreadDemo( "Thread-1");
	  T1.start();
	  
	  ThreadDemo T2 = new ThreadDemo( "Thread-2");
	  T2.start();
   }   
}

这将产生以下结果 -

Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.

Thread 方法

下表列出了Thread类的一些重要方法 -

序号 方法描述
1 public void start()
使该线程开始执行;Java虚拟机调用该线程的 run 方法。
2 public void run()
如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。
3 public final void setName(String name)
改变线程名称,使之与参数 name 相同。
4 public final void setPriority(int priority)
更改线程的优先级。
5 public final void setDaemon(boolean on)
将该线程标记为守护线程或用户线程。
6 public final void join(long millisec)
等待该线程终止的时间最长为 millis 毫秒。
7 public void interrupt()
中断线程。
8 public final boolean isAlive()
测试线程是否处于活动状态。

测试线程是否处于活动状态。 上述方法是被Thread对象调用的。下面的方法是Thread类的静态方法。

序号 方法描述
1 public static void yield()
暂停当前正在执行的线程对象,并执行其他线程。
2 public static void sleep(long millisec)
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
3 public static boolean holdsLock(Object x)
当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。
4 public static Thread currentThread()
返回对当前正在执行的线程对象的引用。
5 public static void dumpStack()
将当前线程的堆栈跟踪打印至标准错误流。

以下ThreadClassDemo程序演示了Thread类的一些方法。 考虑一个实现Runnable的类DisplayMessage -

// File Name : DisplayMessage.java
// Create a thread to implement Runnable

public class DisplayMessage implements Runnable {
   private String message;
   
   public DisplayMessage(String message) {
	  this.message = message;
   }
   
   public void run() {
	  while(true) {
		 System.out.println(message);
	  }
   }
}

以下是扩展Thread类的另一个类 −

// File Name : GuessANumber.java
// Create a thread to extentd Thread

public class GuessANumber extends Thread {
   private int number;
   public GuessANumber(int number) {
	  this.number = number;
   }
   
   public void run() {
	  int counter = 0;
	  int guess = 0;
	  do {
		 guess = (int) (Math.random() * 100 + 1);
		 System.out.println(this.getName() + " guesses " + guess);
		 counter++;
	  } while(guess != number);
	  System.out.println("** Correct!" + this.getName() + "in" + counter + "guesses.**");
   }
}

以下是主要程序,它使用上面定义的类 −

// File Name : ThreadClassDemo.java
public class ThreadClassDemo {

   public static void main(String [] args) {
	  Runnable hello = new DisplayMessage("Hello");
	  Thread thread1 = new Thread(hello);
	  thread1.setDaemon(true);
	  thread1.setName("hello");
	  System.out.println("Starting hello thread...");
	  thread1.start();
	  
	  Runnable bye = new DisplayMessage("Goodbye");
	  Thread thread2 = new Thread(bye);
	  thread2.setPriority(Thread.MIN_PRIORITY);
	  thread2.setDaemon(true);
	  System.out.println("Starting goodbye thread...");
	  thread2.start();

	  System.out.println("Starting thread3...");
	  Thread thread3 = new GuessANumber(27);
	  thread3.start();
	  try {
		 thread3.join();
	  } catch (InterruptedException e) {
		 System.out.println("Thread interrupted.");
	  }
	  System.out.println("Starting thread4...");
	  Thread thread4 = new GuessANumber(75);
	  
	  thread4.start();
	  System.out.println("main() is ending...");
   }
}

这将产生以下结果。 你可以反复尝试这个例子,每次你都会得到不同的结果。

Starting hello thread...
Starting goodbye thread...
Hello
Hello
Hello
Hello
Hello
Hello
Goodbye
Goodbye
Goodbye
Goodbye
Goodbye
.......

Applet基础

Java Applet 基础

一个applet是一个运行在Web浏览器中的Java程序。 一个applet可以是一个全功能的Java应用程序,因为它拥有完整的Java API。

applet和独立Java应用程序之间有一些重要的区别,包括以下 -

  • Java 中 Applet 类继承了 java.applet.Applet 类。
  • Applet 类没有定义 main(),所以一个 Applet 程序不会调用 main() 方法。
  • Applet 被设计为嵌入在一个 HTML 页面。
  • 当用户浏览包含 Applet 的 HTML 页面,Applet 的代码就被下载到用户的机器上。
  • 要查看一个 Applet 需要 JVM。 JVM 可以是 Web 浏览器的一个插件,或一个独立的运行时环境。
  • 用户机器上的 JVM 创建一个 Applet 类的实例,并调用 Applet 生命周期过程中的各种方法。
  • Applet 有 Web 浏览器强制执行的严格的安全规则,Applet 的安全机制被称为沙箱安全。
  • Applet 需要的其他类可以用 Java 归档(JAR)文件的形式下载下来。

Applet的生命周期

  • init: 该方法的目的是为你的 Applet 提供所需的任何初始化。在 Applet 标记内的 param 标签被处理后调用该方法。
  • start: 浏览器调用 init 方法后,该方法被自动调用。每当用户从其他页面返回到包含 Applet 的页面时,则调用该方法。
  • stop: 当用户从包含 Applet 的页面移除的时候,该方法自动被调用。因此,可以在相同的 Applet 中反复调用该方法。
  • destroy: 此方法仅当浏览器正常关闭时调用。因为 Applet 只有在 HTML 网页上有效,所以你不应该在用户离开包含 Applet 的页面后遗漏任何资源。
  • paint: 该方法在 start() 方法之后立即被调用,或者在 Applet 需要重绘在浏览器的时候调用。paint() 方法实际上继承于 java.awt。

"Hello, World" Applet

下面是一个简单的 Applet 程序 HelloWorldApplet.java -

import java.applet.*;
import java.awt.*;

public class HelloWorldApplet extends Applet {
   public void paint (Graphics g) {
	  g.drawString ("Hello World", 25, 50);
   }
}

这些 import 语句将以下类导入到我们的 Applet 类中

  • java.applet.Applet
  • java.awt.Graphics

没有这些 import 语句,Java 编译器就识别不了 Applet 和 Graphics 类。

Applet 类

每一个 Applet 都是 java.applet.Applet 类的子类,基础的 Applet 类提供了供衍生类调用的方法,以此来得到浏览器上下文的信息和服务。

这些方法做了如下事情-

  • 得到 Applet 的参数
  • 得到包含 Applet 的 HTML 文件的网络位置
  • 得到 Applet 类目录的网络位置
  • 打印浏览器的状态信息
  • 获取一张图片
  • 获取一个音频片段
  • 播放一个音频片段
  • 调整此 Applet 的大小

除此之外,Applet 类还提供了一个接口,该接口供 Viewer 或浏览器来获取 Applet 的信息,并且来控制 Applet 的执行。

Viewer 可能是 -

  • 请求 Applet 作者、版本和版权的信息
  • 请求 Applet 识别的参数的描述
  • 初始化 Applet
  • 销毁 Applet
  • 开始执行 Applet
  • 结束执行 Applet

Applet 类提供了对这些方法的默认实现,这些方法可以在需要的时候重写。

"Hello,World" applet 都是按标准编写的。唯一被重写的方法是 paint 方法。

Applet 的调用

Applet 是一种 Java 程序。它一般运行在支持 Java 的 Web 浏览器内。因为它有完整的 Java API 支持,所以 Applet 是一个全功能的 Java 应用程序。

<applet> 标签是在HTML文件中嵌入 Applet 的基础。以下是一个调用"Hello World"applet的例子 −

<html>
   <title>The Hello, World Applet</title>
   <hr>
   <applet code = "HelloWorldApplet.class" width = "320" height = "120">
	  If your browser was Java-enabled, a "Hello, World"
	  message would appear here.
   </applet>
   <hr>
</html>

注意: 你可以参照 HTML Applet 标签来更多的了解从 HTML 中调用 applet 的方法。

<applet> 标签的属性指定了要运行的 Applet 类。width 和 height 用来指定 Applet 运行面板的初始大小。Applet 必须使用 </applet> 标签来关闭。

如果 Applet 接受参数,那么参数的值需要在 <param> 标签里添加,该标签位于 <applet> 和 </applet> 之间。浏览器忽略了 applet 标签之间的文本和其他标签。

不支持 Java 的浏览器不能执行 <applet> 和 </applet>。因此,在标签之间显示并且和 applet 没有关系的任何东西,在不支持的 Java 的浏览器里是可见的。

Viewer 或者浏览器在文档的位置寻找编译过的 Java 代码,要指定文档的路径,得使用 <applet> 标签的 codebase 属性指定。

<applet codebase = "https://test.com/applets" code = "HelloWorldApplet.class"
   width = "320" height = "120">

如果 Applet 所在一个包中而不是默认包,那么所在的包必须在 code 属性里指定,例如 −

<applet  = "mypackage.subpackage.TestApplet.class" 
   width = "320" height = "120">

获得applet参数

下面的例子演示了如何使用一个 Applet 响应来设置文件中指定的参数。该 Applet 显示了一个黑色棋盘图案和第二种颜色。

第二种颜色和每一列的大小通过文档中的 Applet 的参数指定。

CheckerApplet 在 init() 方法里得到它的参数。也可以在 paint() 方法里得到它的参数。然而,在 Applet 开始得到值并保存了设置,而不是每一次刷新的时候都得到值,这样是很方便,并且高效的。

Applet viewer 或者浏览器在 Applet 每次运行的时候调用 init() 方法。在加载 Applet 之后,Viewer 立即调用 init() 方法(Applet.init()什么也没做),重写该方法的默认实现,添加一些自定义的初始化代码。

Applet.getParameter() 方法通过给出参数名称得到参数值。如果得到的值是数字或者其他非字符数据,那么必须解析为字符串类型。

下例是 CheckerApplet.java 的修改 -

import java.applet.*;
import java.awt.*;

public class CheckerApplet extends Applet {
   int squareSize = 50;   // initialized to default size
   public void init() {}
   private void parseSquareSize (String param) {}
   private Color parseColor (String param) {}
   public void paint (Graphics g) {}
}

下面是 CheckerApplet 类的 init() 方法和私有的 parseSquareSize() 方法 -

public void init () {
   String squareSizeParam = getParameter ("squareSize");
   parseSquareSize (squareSizeParam);
   
   String colorParam = getParameter ("color");
   Color fg = parseColor (colorParam);
   
   setBackground (Color.black);
   setForeground (fg);
}

private void parseSquareSize (String param) {
   if (param == null) return;
   try {
	  squareSize = Integer.parseInt (param);
   } catch (Exception e) {
	  // Let default value remain
   }
}

该 Applet 调用 parseSquareSize(),来解析 squareSize 参数。parseSquareSize() 调用了库方法 Integer. parseInt() 该方法将一个字符串解析为一个整数,当参数无效的时候,Integer.parseInt() 抛出异常。

因此,parseSquareSize() 方法也是捕获异常的,并不允许 Applet 接受无效的输入。

Applet 调用 parseColor()方法将颜色参数解析为一个 Color 值。parseColor() 方法做了一系列字符串的比较,来匹配参数的值和预定义颜色的名字。你需要实现这些方法来使 Applet 工作。

指定 applet 参数

如下的例子是一个HTML文件,其中嵌入了 CheckerApplet 类。HTML文件通过使用 <param> 标签的方法给 applet 指定了两个参数。

<html>
   <title>Checkerboard Applet</title>
   <hr>
   <applet code = "CheckerApplet.class" width = "480" height = "320">
	  <param name = "color" value = "blue">
	  <param name = "squaresize" value = "30">
   </applet>
   <hr>
</html>

注意: 参数名字大小写不敏感。

应用程序转换成 Applet

将图形化的 Java 应用程序(是指,使用AWT的应用程序和使用 java 程序启动器启动的程序)转换成嵌入在web页面里的applet是很简单的。

下面是将应用程序转换成 Applet 的几个步骤 -

  • 编写一个 HTML 页面,该页面带有能加载 applet 代码的标签。
  • 编写一个 JApplet 类的子类,将该类设置为 public。否则,Applet 不能被加载。
  • 消除应用程序的 main()方法。不要为应用程序构造框架窗口,因为你的应用程序要显示在浏览器中。
  • 将应用程序中框架窗口的构造方法里的初始化代码移到 Applet 的 init() 方法中,你不必显示的构造 Applet 对象,浏览器将通过调用 init() 方法来实例化一个对象。
  • 移除对 setSize() 方法的调用,对于 Applet 来讲,大小已经通过 HTML 文件里的 width 和 height 参数设定好了。
  • 移除对 setDefaultCloseOperation() 方法的调用。Applet 不能被关闭,它随着浏览器的退出而终止。
  • 如果应用程序调用了 setTitle() 方法,消除对该方法的调用。applet 不能有标题栏。(当然你可以给通过 html 的 title 标签给网页自身命名)
  • 不要调用 setVisible(true),Applet 是自动显示的。

事件处理

Applet 类从 Container 类继承了许多事件处理方法。Container 类定义了几个方法,例如:processKeyEvent() 和processMouseEvent(),用来处理特别类型的事件,还有一个捕获所有事件的方法叫做 processEvent。

为了响应一个事件,Applet 必须重写合适的事件处理方法。

import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
import java.applet.Applet;
import java.awt.Graphics;

public class ExampleEventHandling extends Applet implements MouseListener {
   StringBuffer strBuffer;

   public void init() {
	  addMouseListener(this);
	  strBuffer = new StringBuffer();
	  addItem("initializing the apple ");
   }

   public void start() {
	  addItem("starting the applet ");
   }

   public void stop() {
	  addItem("stopping the applet ");
   }

   public void destroy() {
	  addItem("unloading the applet");
   }

   void addItem(String word) {
	  System.out.println(word);
	  strBuffer.append(word);
	  repaint();
   }

   public void paint(Graphics g) {
	  // Draw a Rectangle around the applet's display area.
	  g.drawRect(0, 0, 
	  getWidth() - 1,
	  getHeight() - 1);

	  // display the string inside the rectangle.
	  g.drawString(strBuffer.toString(), 10, 20);
   }

   
   public void mouseEntered(MouseEvent event) {
   }
   public void mouseExited(MouseEvent event) {
   }
   public void mousePressed(MouseEvent event) {
   }
   public void mouseReleased(MouseEvent event) {
   }
   public void mouseClicked(MouseEvent event) {
	  addItem("mouse clicked! ");
   }
}

如下调用该 Applet

<html>
   <title>Event Handling</title>
   <hr>
   <applet code = "ExampleEventHandling.class" 
	  width = "300" height = "300">
   </applet>
   <hr>
</html>

最开始运行,Applet 显示 "initializing the applet. Starting the applet.",然后你一点击矩形框,就会显示 "mouse clicked" 。

显示图片

Applet 能显示 GIF,JPEG,BMP 等其他格式的图片。为了在 Applet 中显示图片,你需要使用 java.awt.Graphics 类的drawImage()方法。

如下实例演示了显示图片的所有步骤 -

import java.applet.*;
import java.awt.*;
import java.net.*;

public class ImageDemo extends Applet {
   private Image image;
   private AppletContext context;
   
   public void init() {
	  context = this.getAppletContext();
	  String imageURL = this.getParameter("image");
	  if(imageURL == null) {
		 imageURL = "java.jpg";
	  }
	  try {
		 URL url = new URL(this.getDocumentBase(), imageURL);
		 image = context.getImage(url);
	  } catch (MalformedURLException e) {
		 e.printStackTrace();
		 // Display in browser status bar
		 context.showStatus("Could not load image!");
	  }
   }
   
   public void paint(Graphics g) {
	  context.showStatus("Displaying image");
	  g.drawImage(image, 0, 0, 200, 84, null);
	  g.drawString("www.javalicense.com", 35, 100);
   }  
}

如下调用该 applet −

<html>
   <title>The ImageDemo applet</title>
   <hr>
   <applet code = "ImageDemo.class" width = "300" height = "200">
	  <param name = "image" value = "java.jpg">
   </applet>
   <hr>
</html>

播放音频

Applet 能通过使用 java.applet 包中的 AudioClip 接口播放音频。AudioClip 接口定义了三个方法 -

  • public void play(): 从一开始播放音频片段一次。
  • public void loop(): 循环播放音频片段
  • public void stop(): 停止播放音频片段

为了得到 AudioClip 对象,你必须调用 Applet 类的 getAudioClip() 方法。无论 URL 指向的是否是一个真实的音频文件,该方法都会立即返回结果。

直到要播放音频文件时,该文件才会下载下来。

如下实例演示了播放音频的所有步骤 -

import java.applet.*;
import java.awt.*;
import java.net.*;

public class AudioDemo extends Applet {
   private AudioClip clip;
   private AppletContext context;
   
   public void init() {
	  context = this.getAppletContext();
	  String audioURL = this.getParameter("audio");
	  if(audioURL == null) {
		 audioURL = "default.au";
	  }
	  try {
		 URL url = new URL(this.getDocumentBase(), audioURL);
		 clip = context.getAudioClip(url);
	  } catch (MalformedURLException e) {
		 e.printStackTrace();
		 context.showStatus("Could not load audio file!");
	  }
   }
   
   public void start() {
	  if(clip != null) {
		 clip.loop();
	  }
   }
   
   public void stop() {
	  if(clip != null) {
		 clip.stop();
	  }
   }
}

如下调用该 applet −

<html>
   <title>The ImageDemo applet</title>
   <hr>
   <applet code = "ImageDemo.class" width = "0" height = "0">
	  <param name = "audio" value = "test.wav">
   </applet>
   <hr>
</html>

你可以在PC上使用test.wav来测试上面的例子。

文档注释

Java 文档注释

Java 支持三种注释方式。前两种分别是 // 和 /* */,第三种被称作说明注释,它以 /** 开始,以 */结束。

说明注释允许你在程序中嵌入关于程序的信息。你可以使用 javadoc 工具软件来生成信息,并输出到HTML文件中。

说明注释,使你更加方便的记录你的程序信息。

什么是Javadoc?

Javadoc是JDK附带的工具,它用于从Java源代码生成HTML格式的Java代码文档,这需要以预定义的格式进行文档编制。

以下是一个简单的例子,其中/*....*/中的行是Java多行注释。 同样,//之前的行是Java单行注释。

/**
* The HelloWorld program implements an application that
* simply displays "Hello World!" to the standard output.
*
* @author  Zara Ali
* @version 1.0
* @since   2014-03-31 
*/
public class HelloWorld {

   public static void main(String[] args) {
	  /* Prints Hello, World! on standard output.
	  System.out.println("Hello World!");
   }
}

您可以在描述部分中包含必需的HTML标签。 例如,以下示例使用<h1>....</h1>作为标题,<p> 用于创建段落分隔 -

/**
* <h1>Hello, World!</h1>
* The HelloWorld program implements an application that
* simply displays "Hello World!" to the standard output.
* <p>
* Giving proper comments in your program makes it more
* user friendly and it is assumed as a high quality code.
* 
*
* @author  Zara Ali
* @version 1.0
* @since   2014-03-31 
*/
public class HelloWorld {

   public static void main(String[] args) {
	  /* Prints Hello, World! on standard output.
	  System.out.println("Hello World!");
   }
}

javadoc 标签

标签 描述 示例
@author 标识一个类的作者 @author description
@deprecated 指名一个过期的类或成员 @deprecated description
{@docRoot} 指明当前文档根目录的路径 Directory Path
@exception 标志一个类抛出的异常 @exception exception-name explanation
{@inheritDoc} 从直接父类继承的注释 Inherits a comment from the immediate surperclass.
{@link} 插入一个到另一个主题的链接 {@link name text}
{@linkplain} 插入一个到另一个主题的链接,但是该链接显示纯文本字体 Inserts an in-line link to another topic.
@param 说明一个方法的参数 @param parameter-name explanation
@return 说明返回值类型 @return explanation
@see 指定一个到另一个主题的链接 @see anchor
@serial 说明一个序列化属性 @serial description
@serialData 说明通过writeObject( ) 和 writeExternal( )方法写的数据 @serialData description
@serialField 说明一个ObjectStreamField组件 @serialField name type description
@since 标记当引入一个特定的变化时 @since release
@throws 和 @exception标签一样. The @throws tag has the same meaning as the @exception tag.
{@value} 显示常量的值,该常量必须是static属性。 Displays the value of a constant, which must be a static field.
@version 指定类的版本 @version info

以下程序使用几个可用于文档评论的重要标签。 您可以根据您的要求使用其他标签。

有关AddNum类的文档将在HTML文件AddNum.html中生成,但同时还会创建一个名为index.html的主文件。

import java.io.*;

/**
* <h1>Add Two Numbers!</h1>
* The AddNum program implements an application that
* simply adds two given integer numbers and Prints
* the output on the screen.
* <p>
* <b>Note:</b> Giving proper comments in your program makes it more
* user friendly and it is assumed as a high quality code.
*
* @author  Zara Ali
* @version 1.0
* @since   2014-03-31
*/
public class AddNum {
   /**
   * This method is used to add two integers. This is
   * a the simplest form of a class method, just to
   * show the usage of various javadoc Tags.
   * @param numA This is the first paramter to addNum method
   * @param numB  This is the second parameter to addNum method
   * @return int This returns sum of numA and numB.
   */
   public int addNum(int numA, int numB) {
	  return numA + numB;
   }

   /**
   * This is the main method which makes use of addNum method.
   * @param args Unused.
   * @return Nothing.
   * @exception IOException On input error.
   * @see IOException
   */

   public static void main(String args[]) throws IOException {
	  AddNum obj = new AddNum();
	  int sum = obj.addNum(10, 20);

	  System.out.println("Sum of 10 and 20 is :" + sum);
   }
}

现在,使用javadoc实用程序处理上面的AddNum.java文件,如下所示 −

$ javadoc AddNum.java
Loading source file AddNum.java...
Constructing Javadoc information...
Standard Doclet version 1.7.0_51
Building tree for all the packages and classes...
Generating /AddNum.html...
AddNum.java:36: warning - @return tag cannot be used in method with void return type.
Generating /package-frame.html...
Generating /package-summary.html...
Generating /package-tree.html...
Generating /constant-values.html...
Building index for all the packages and classes...
Generating /overview-tree.html...
Generating /index-all.html...
Generating /deprecated-list.html...
Building index for all classes...
Generating /allclasses-frame.html...
Generating /allclasses-noframe.html...
Generating /index.html...
Generating /help-doc.html...
1 warning
$

 

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇