Hashtable与Properties类
-
Hashtable也是一种高级数据库结构,用来快速检索数据。Hashtable不仅可以像Vector一样动态存储一系的对象,而且对存储的每一个对象(称之为值)都要安排另一个对象(称之为关键字)与之相关联。
-
向Hashtable对象中存储数据,使用的是Hashtable.put(Object key,Object value)方法,从Hashtable中检索数据,使用Hashtable.get(Object key)方法。值和关键字都可以是任何类型的非空的对象。Hashtable中的关键字不能相同
Hashtable numbers=new Hashtable();
numbers.put(“one”,new Integer(1));
numbers.put(“two”,new Integer(2));
numbers.put(“three”,new Integer(3));
要检索其中”tow”关键字对应的数据,看下面的代码就能明白:
Integer n=(Integer)numbers.get(“two”);
if(n!=null)
{
System.out.println(“two = ”+n);
}
-
想要成功地从Hashtable中检索数据,用作关键字的对象必须正确覆盖了Object.hashCode方法和Object.equals方法。覆盖Object.equals道理不难想象,检索数据时必须比较所用关键字是否与存储在Hashtable中的某个关键字相等,如果两个关键字对象不能正确判断是否相等,检索是不可能正确的。Object.hashCode方法返回一个叫散列码的值,这个值是由对象的地址以某种方式转换来的。内容相同的两个对象,既然是两个对象,地址就不可能一样,所以Object.hashCode返回的值也不一样。要想两个内容相同的Object子类对象的hashCode方法返回一样的散列码,子类必须覆盖Object.hashCode方法。用于关键字的类,如果它的两个String对象的内容不相等,它们的hashCode的返回值也不相等,如果两个String对象的内容相等,它们的hashCode的返回值也相等,所以,我们在实现自己编写的关键字类的hashCode方法时,可以调用这个关键字类的String类型的成员变量的hashCode方法来计算关键字类的hashCode返回值。注意:StringBuffer类没有按照关键字类的要求覆盖hashCode方法,即使两个StringBuffer类对象的内容相等,但这两个对象的hashCode方法返回值却不相等。所以,我们不能用StringBuffer作为关键字类。
实例:
MyKey.java
class MyKey
{
private String name;
private int age;
public MyKey(String name,int age)
{
this.name=name;
this.age=age;
}
public String toString()
{
return new String(name+","+age);
}
public boolean equals(Object obj)
{
if(obj instanceof MyKey)
{
MyKey objTem=(MyKey)obj;
if(name.equals(objTem.name)&&age==objTem.age)
return true;
else
return false;
}
else
return false;
}
public int hashCode()
{
return name.hashCode()+age;
}
}
上面的代码实现了我们的目的:如果两个人名字和年龄都相同,我们就认为他们是同一个人。
下面的类使用MyKey 类作为关键字类,取出所有关键字的集合和取出所有值的集合:
HashtableTest.java
import java.util.*;
public class HashtableTest
{
public static void main(String[] args)
{
Hashtable numbers=new Hashtable();
numbers.put(new MyKey("Zhangsan",18),new Integer(1));
numbers.put(new MyKey("Lisi",15),new Integer(2));
numbers.put(new MyKey("Wangwu",20),new Integer(3));
Enumeration e=numbers.keys();//Hashtable中的keys方法以Enumeration接口的方式返回Hashtable对象的所有关键字
while(e.hasMoreElements())
{
MyKey key=(MyKey)e.nextElement();
System.out.print(key+"=");//打印key时,会默认调用key.toString()方法,这个方法里面是什么内容打印出来的就是什么内容
System.out.println(numbers.get(key));//在MyKey类中重写的equals与hashCode方法是在这个get方法使用时要调用的,由系统自动调用
}
System.out.println(numbers.get(new MyKey("Wangwu",20)));//新建一个对象作为关键字传给get方法,用于取出numbers对象中的这个关键字所对应的值
//如果没有在MyKey类中覆盖equals、hashCode方法,则取不出对应的值
testABC abc=new testABC();
System.out.println(abc);//打印abc对象时,会默认调用这个对象的toString()方法,这个方法里面是什么内容打印出来的就是什么内容
}
}
class testABC
{
String a="戴振良";
String b="你好";
public String toString()
{
return a+b;
}
}
Properties
Properties是Hashtable的子类,它增加了将Hashtable对象中的关键字、值保存到文件和文件中读取关键字、值到Hashtable对象中的方法。在大多数应用程序都有选项设置,就是在程序退出时将功能/设置值存储到文件,程序启动时将功能/设置值读取到了内存,程序按新的设置运行,如:
-
如果要用Properties.store方法存储Properties对象中的内容,每个属性的关键字和值都必须是String类型。
-
实例:编写一个程序,每次运行时打印出运行的次数。
import java.util.Properties;
import java.io.*;
public class PropertiesFile
{
Properties settings=new Properties();
try{ //FileInputStream类:从文件系统中的某个文件中获得输入字节,该类是InputStream类的子类
settings.load(new FileInputStream("count.txt"));}//load( )方法功能:从输入流中读取属性列表(键和元素对)
catch(Exception e){
settings.setProperty("count",String.valueOf(0)); //setProerty方法是调用Hashtable的put方法,但是这个方法的参数必须是String类型的。这里把0转换为String对象用String.valueOf 方法,这比new Integer(0).toString()方法要方便,而String.valueOf方法实际上是调用Integer的toString方法来进行转换的
}
//settings.get("count");//get方法是从Hashtable中继承来的,该方法返回的值是Oject类型的
//getProperty方法与get方法功能类似,只不过getProperty方法返回的值是String类型的
int c=Integer.parseInt(settings.getProperty("count"))+1;
System.out.println("这是第"+c+"次运行");
//settings.put("count",new Integer(c).toString());//这个方法可以接受非字符串的数据作为参数,因为Properties中存储的关键字和值都必须是字符串,所以用下面的setProperty方法比较好。
settings.setProperty("count",new Integer(c).toString());//这个方法的参数必须都是字符串类型的
try{
settings.store(new FileOutputStream("count.txt"),"Program is used:");//在这句代码里,如果系统中没有count.txt文件存在,则系统会自动产生这个文件
}
catch(Exception a){
a.printStackTrace();
}
}
}
程序每次启动时都去读取那个记录文件,直接取出文件中所记录的运行次数并加1后,又重新将新的运行次数存回文件。由于第一次运行时硬盘上还没有那个记录文件,程序去读取那个记录文件时会报出一个异常,我们就在处理异常的语句中将属性的值设置为0,表示程序以前还没有运行过。如果要用到Properties类的store方法进行存储,每个属性的关键字和值都必须是字符串类型的,所以上面的程序没有用从父类Hashtable继承到的put、get方法进行属性的设置与读取,而直接用了Properties类的setProperty、getProperty方法进行属性的设置与读取。一般有使用次数限制的共享软件的程序代码基本上都是这么做的,只不过它们把记录次数的这个文件隐藏在某个不容易发现的地方,例如保存在注册表里或某个系统文件里,只要找到这个地方,删除这个文件或注册表项,这个软件又可以被使用了。
System类与Runtime类
System类
java不支持全局函数和变量,java设计者将一些系统相关的重要函数和变量收集到了一个统一的类中,这就是System类,System类中的所有成员都是静态的,当我们要引用这些变量和方法时,直接使用System类名作前缀,如前面已经使用到的标准输入和输出的in和out变量。
System类的常用方法:
-
exit(int status)方法,提前终止虚拟机的运行。对于发生了异常情况而想终止虚拟机的运行,传递一个非零值作为参数。对于用户正常操作下想终止虚拟机的运行,则传递零作为参数。
-
long startTime=System.currentTimeMillis( );
…… //代码段
long endTime=System.currentTimeMillis( );
System.out.println(“总计用时:”+(endTime-startTime)+”毫秒”); -
getProperties方法与Java的环境属性
-
getProperties方法获得当前Java虚拟机的系统属性。如果大家明白Windows的环境属性,如path和classpath就是其中的两个环境变量,每一个属性都是变量与值成对的形式出现的。
-
setProperties方法设置当前Java虚拟机的系统属性。
-
getProperties、setProperties这两个方法返回值和参数分别是Properties类的一个实例。Properties实例对象中存储着Java虚拟机所有的系统属性的变量和值对的情况。
-
同样的道理,Java作为一个虚拟的操作系统,它也有自己的环境属性,Properties是Hashtable的子类,正好可以用于存储环境属性中的多个变量、值成对格式的数据,getProperties方法返回值是包含了当前虚拟机的所有环境属性的Properties类型的对象。
-
实例:打印出当前虚拟机的所有环境属性的变量和值。
import java.util.*;//Properties位于此包中
public class TestProperties {
public static void main(String args[]){
System.setProperty("haha","hello");//增加系统属性,第一个参数为变量名,第二个为值
Properties sp=System.getProperties();//获取当前java虚拟机的系统属性
Enumeration e=sp.propertyNames();//获取所有的属性名称
while(e.hasMoreElements()){
String key=(String)e.nextElement();//获取e中的元素,也就是java虚拟机的系统属性名称
System.out.println(key+"="+sp.getProperty(key));//输出(名称+"="+值)
}
Process p=null; //Process类代表Java虚拟机启动的进程启动的子进程
try{
p=Runtime.getRuntime().exec("notepad.exe TestProperties.java");
Thread.sleep(5000);//进程睡眠5秒
}
catch(Exception a){
a.printStackTrace();
}
p.destroy();//这个方法为关闭实例p所对应的进程
}
}
-
在Windows中,很容易增加一个新的环境属性,但如何为Java虚拟机增加一个新的环境属性呢?在命令行窗口中直接运行Java命令,会看到有一个“-D<name>=<value>格式的选项可以设置新的系统环境属性。如:
java –Dname=hello TestProperties
-
增加两个环境属性的格式:java –DAAA=bbb –DCCC=ddd TestProperties
Runtime类
Runtime类封装了Java命令本身运行的进程,也就是封装了Java虚拟机进程,一个Java 虚拟机对应一个Runtime实例对象,其中的许多方法与System中的方法相重复。不能直接创建Runtime实例,但是可以通过静态方法Runtime.getRuntime获得正在运行的Runtime对象的引用。
Exec方法,Java命令运行后,本身是多任务操作系统上的一个进程,在这个进程中启动一个新的进程(这个进程就叫子进程),即执行其他程序时使用exec方法。exec方法返回一个代表子进程的Process类对象,通过这个对象,Java进程可以与子进程交互。
实例:
运行后程序启动一个子进程:用Windows的记事本程序打开了我们的源程序,并在5秒种后销毁该子进程,记事本程序被关掉。
public class TestRunTime {
public static void main(String [] args) {
Process p=null; //Process类代表Java虚拟机启动的进程启动的子进程
try{
p=Runtime.getRuntime().exec("notepad.exe TestRuntime.java");
Thread.sleep(5000);//把线程暂停5秒
}catch(Exception e){
System.out.println(e.getMessage());
}
p.destroy();//这个方法为关闭实例p所对应的进程
}
}
由于程序不能直接创建类Runtime的实例,所以可以保证我们只会产生一个Runtime的实例对象,而不能产生多个实例对象,这种情况就是单态设计模式。我们可以按照单态设计模式思想来设想一下Runtime类在内部是如何构造Runtime类的对象实例的。