Java常用类(API)
石凤贵
【知识点】
理解Java API的概念,了解Object类,基本类型的封装类,熟悉String类和StringBuffer类,Hashtable与Properties类,System类与Runtime类,Date类、Calendar类、Math类、BigInteger类,Math与Random类。
提示:查阅JDK API
【内容】
-
一、Object类
类 Object 是类层次结构的根类。每个类都使用 Object 作为超类。所有对象(包括数组)都实现这个类的方法。。
Object类的常用方法:equals()、getClass()、hashCode()、notify()、notifyAll()、toString()、wait();
当用户自定义类时,要覆盖从父类Object继承过来的toString()方法,否则将得不到用户可理解的运行结果。
二、基本类型的封装类
1、基本类型
Java基本类型,或者叫做内置类型,是JAVA中不同于类的特殊类型。它们是我们编程中使用最频繁的类型。
基本类型共有九种,它们分别都有相对应的包装类。对于基本类型void以及它的包装类java.lang.Void,我们都无法直接进行操作。基本类型可以分为三类,字符类型char,布尔类型boolean以及数值类型byte、short、int、long、float、double。数值类型又可以分为整数类型byte、short、int、long和浮点数类型float、double。JAVA中的数值类型不存在无符号的,它们的取值范围是固定的,不会随着机器硬件环境或者操作系统的改变而改变。对于数值类型的基本类型的取值范围,我们无需强制去记忆,因为它们的值都已经以常量的形式定义在对应的包装类中了
基本类型存储在栈中,因此它们的存取速度要快于存储在堆中的对应包装类的实例对象。从Java5.0(1.5)开始,JAVA虚拟机(Java Virtual Machine)可以完成基本类型和它们对应包装类之间的自动转换。因此我们在赋值、参数传递以及数学运算的时候像使用基本类型一样使用它们的包装类,但这并不意味着你可以通过基本类型调用它们的包装类才具有的方法。另外,所有基本类型(包括void)的包装类都使用了final修饰,因此我们无法继承它们扩展新的类,也无法重写它们的任何方法。
下图显示了几种基本类型之间的默认逻辑转换关系:
图中的实线表示无精度损失的转换,而虚线则表示这样的转换可能会损失一定的精度。如果我们想把一个能表示更大范围或者更高精度的类型,转换为一个范围更小或者精度更低的类型时,就需要使用强制类型转换(Cast)了。不过我们要尽量避免这种用法,因为它常常引发错误。
运算符对基本类型的影响:
当使用+、-、*、/、%运算符对基本类型进行运算时,遵循如下规则:
只要两个操作数中有一个是double类型的,另一个将会被转换成double类型,并且结果也是double类型;只要两个操作数中有一个是float类型的,另一个将会被转换成float类型,并且结果也是float类型;只要两个操作数中有一个是long类型的,另一个将会被转换成long类型,并且结果也是long类型;两个操作数(包括byte、short、int、char)都将会被转换成int类型,并且结果也是int类型。
当使用+=、-=、*=、/=、%=、运算符对基本类型进行运算时,遵循如下规则:
运算符右边的数值将首先被强制转换成与运算符左边数值相同的类型,然后再执行运算,且运算结果与运算符左边数值类型相同。
2、封装类(包装类)
(1)Java中的包装类、装箱和拆箱
在Java中,数据类型总共可分为两大种,基本数据类型(值类型)和类类型(引用数据类型)。基本类型的数据不是对象,不能作为对象调用其toString()、hashCode()、getClass()、equals()等等方法。所以在java中,针对8种基本数据类型,提供了针对每个基本数据类型的包装类。如下:
INDEX |
基本数据类型 |
包装类 |
1 |
int |
Integer |
2 |
char |
Character |
3 |
float |
Float |
4 |
double |
Double |
5 |
byte |
Byte |
6 |
short |
Short |
7 |
long |
Long |
8 |
boolean |
Boolean |
所谓装箱,就是把基本类型用它们相对应的引用类型包起来,使它们可以具有对象的特质,如我们可以把int型包装成Integer类的对象,或者把double包装成Double,等等。 所谓拆箱,就是跟装箱的方向相反,将Integer及Double这样的引用类型的对象重新简化为值类型的数据。
//JDK1.5 之前使用手动方式进行装箱 和拆箱的 操作
包装类的另外一个比较有用的是可以将String 字符串类型转换为相应的基本数据类型。下面以将String转为int为例:
总结在包装类中将String转换为包装类所对应的基本数据类型的方法如下,所有的方法都将抛出NumberFormatException异常,需要在调用时候使用try ..catch 进行捕获
index |
包装类 |
返回的基本数据类型 |
方法名 |
1 |
Integer |
int |
static int Integer.parseInt(Sting s) |
2 |
Character |
char |
static char Character.parseChar(String s) |
3 |
Float |
float |
static float Float.parseFloat(String s) |
4 |
Dobule |
double |
static double Double.parseDouble(String s) |
5 |
Byte |
byte |
static byte Byte.parseByte(String s) |
6 |
Short |
short |
static short Short.parseShort(String s) |
7 |
Long |
long |
static long Long.parseLong(String s) |
8 |
Boolean |
boolean |
static boolean Boolean.parseBoolean(String s) |
类似的一个方法为 parseXXX(String s,int radix),对于Integer包装类,这个radix表示进制. parseInt(String s)默认为十进制。 其他包装类的radix参数意义,请参见API文档
可以自动装包,也可以自动解包。
三、String和StringBuffer
java.lang.StringBuffer线程安全的可变字符序列。一个类似于 String 的字符串缓冲区,但不能修改。虽然在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容。
可将字符串缓冲区安全地用于多个线程。可以在必要时对这些方法进行同步,因此任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致。
StringBuffer 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的点添加字符。
例如,如果 z 引用一个当前内容是“start”的字符串缓冲区对象,则此方法调用 z.append("le") 会使字符串缓冲区包含“startle”,而 z.insert(4, "le") 将更改字符串缓冲区,使之包含“starlet”。
通常,如果 sb 引用 StringBuilder 的一个实例,则 sb.append(x) 和 sb.insert(sb.length(), x) 具有相同的效果。
只要发生有关源序列(如在源序列中追加或插入)的操作,该类就只在执行此操作的字符串缓冲区上而不是在源上实现同步。每个字符串缓冲区都有一定的容量。只要字符串缓冲区所包含的字符序列的长度没有超出此容量,就无需分配新的内部缓冲区数组。如果内部缓冲区溢出,则此容量自动增大。从 JDK 5 开始,为该类补充了一个单个线程使用的等价类,即 StringBuilder。与该类相比,通常应该优先使用 StringBuilder 类,因为它支持所有相同的操作,但由于它不执行同步,所以速度更快。
java.lang.StringBuilder一个可变的字符序列。此类提供一个与 StringBuffer 兼容的 API,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。
在 StringBuilder 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串生成器中。 append 方法始终将这些字符添加到生成器的末端;而 insert 方法则在指定的点添加字符。
例如,如果 z 引用一个当前内容为“start”的字符串生成器对象,则该方法调用 z.append("le") 将使字符串生成器包含“startle”,而 z.insert(4, "le") 将更改字符串生成器,使之包含“starlet”。
通常,如果 sb 引用 StringBuilder 的实例,则 sb.append(x) 和 sb.insert(sb.length(), x) 具有相同的效果。 每个字符串生成器都有一定的容量。只要字符串生成器所包含的字符序列的长度没有超出此容量,就无需分配新的内部缓冲区。如果内部缓冲区溢出,则此容量自动增大。
将 StringBuilder 的实例用于多个线程是不安全的。如果需要这样的同步,则建议使用 StringBuffer。
String的创建
String s = "hello";
JVM先根据内容"hello"查找对象,如果没有找到,则在heap上创建新对象,并将其赋予s1,否则使用已经存在的对象
String s = new String("hello");
JVM直接在heap上创建新的对象,所以在heap中会出现内容相同,地址不同的String对象"hello"就是一个对象
因此才可以
String s1="hello";
才可以
String s2=new String("hello");
String的比较
"==" 比较地址
"equals" 比较内容
==是判断两个变量或实例是不是指向同一个内存空间,即引用
equals是判断两个变量或实例所指向的内存空间的值是不是相同
举例:
String s1 = "hello";
String s2 = "hello";
String s3 = new String("hello");
s1 == s2; // true 地址相同
s1 == s3; // false 地址不同
s1.equals(s2); // true 内容相同
s1.equals(s3); // true 内容相同
intern() 方法
查找内容相同(equals())的字符串
String s1 = "hello"; // hello不存在,jvm创建新对象 (1)
String s2 = new String("hello"); // 创举新对象 (2),这时heap中存在两个内容为hello的对象
s1 == s2; // false // 地址不同
s1.equals(s2); // true // 内容相同
s2 = s1.intern(); // true // 找到对象(1) 并赋予s2
s1 == s2; // true !! // 注意:此时s1,s2同指向(1)
效率:String 与 StringBuffer
情景1:
(1) String result = "hello" + " world";
(2) StringBuffer result = new String().append("hello").append(" world");
(1) 的效率好于 (2),不要奇怪,这是因为JVM会做如下处理
编译前 String result = "hello" + " world";
编译后 String result = "hello world";
情景2:
(1) public String getString(String s1, String s2) {
return s1 + s2;
}
(2) public String getString(String s1, String s2) {
return new StringBuffer().append(s1).append(s2);
}
(1) 的效率与 (2) 一样,这是因为JVM会做如下处理
编译前 return s1 + s2;
编译后 return new StringBuffer().append(s1).append(s2);
情景3:
(1) String s = "s1";
s += "s2";
s += "s3";
(2) StringBuffer s = new StringBuffer().append("s1").append("s2").append("s3");
(2) 的效率好于(1),因为String是不可变对象,每次"+="操作都会造成构造新的String对象
情景4:
(1) StringBuffer s = new StringBuffer();
for (int i = 0; i < 50000; i ++) {
s.append("hello");
}
(2) StringBuffer s = new StringBuffer(250000);
for (int i = 0; i < 50000; i ++) {
s.append("hello");
}
(2) 的效率好于 (1),因为StringBuffer内部实现是char数组,默认初始化长度为16,每当字符串长度大于char
数组长度的时候,JVM会构造更大的新数组,并将原先的数组内容复制到新数组,(2)避免了复制数组的开销
关键点
1). 简单的认为 .append() 效率好于 "+" 是错误的!
2). 不要使用 new 创建 String
3). 注意 .intern() 的使用
4). 在编译期能够确定字符串值的情况下,使用"+"效率最高
5). 避免使用 "+=" 来构造字符串
6). 在声明StringBuffer对象的时候,指定合适的capacity,不要使用默认值(18)
7). 注意以下二者的区别不一样
- String s = "a" + "b";
- String s = "a";
s += "b";
四、Hashtable与Properties类
1、Hashtable类
Hashtable也是一种高级数据结构,用以快速检索数据。Hashtable不仅可以像Vector一样动态存储一系列对象,而且对存储的每一个对象(称为值)都要安排另一个对象(称为关键字)与之相关联。例如,我们可以在Hashtable中存储若干个国家的中文和英文名,并且可以通过英文名称检索对应的中文名称,在这种情况中中文就是值,英文就是关键字。向Hashtable对象中存储数据,使用的是Hashtable.put(Object key, Object value)方法,从Hashtable中检索数据,使用Hashtable.get(Object key)方法。值和关键字都可以是任何类型的非空对象。下面代码生成一个存储数字的Hashtable,用英文数字作为关键字:
Hashtable numbers = new Hashtabel();
numbers.put("one", new Integer(1));
numbers.put("two", new Integer(2));
numbers.put("three", new Integer(3));
要想检索其中"two"关键字对应的数据,看下面的代码就能明白。
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返回的值也不一样。要想两个内容相同的对象的hashCode方法返回一样的散列码,子类必须覆盖Object.hashCode方法,用于关键字的类,如果它的两个对象用equals方法比较相等的,那么这两个对象的hashcode方法返回值也要一样,所以我们也要覆盖hashCode方法。
这里可以提一下String类,因为String类已按照关键字类的要求覆盖了这两个方法,如果两个String对象内容不相等,它们的hashCode的返回值也不会相等。如果两个String对象的内容相等,它们的hashCode的返回值也相等
注意:StringBuffer类没有按照关键字类的要求覆盖hashCode方法,即使两个StringBuffer类对象的内容相等,但这两个对象的hashCode方法的返回值却不相等。
2、Hashtable与HashMap的相同之处
Java包含两个类,java.util.Hashtable 和java.util.HashMap,它们提供了一个多种用途的hashtable机制。Hashtable和HashMap对象可以让你把一个key和一个value结合起来,并用put() 方法把这对key/value输入到表中。然后你可以通过调用get()方法,把key作为参数来得到这个value(值)。只要满足两个基本的要求,key和value可以是任何对象。注意,因为key和value必须是对象,所以原始类型(primitive types)必须通过运用诸如Integer(int)的方法转换成对象。
下面是一段简单的示例代码:
先创建一个hashtable,保存了1, 2, 3三个对象。
Hashtable numbers = new Hashtable();
numbers.put("one", new Integer(1));
numbers.put("two", new Integer(2));
numbers.put("three", new Integer(3));
查找
Integer n = (Integer)numbers.get("two");
if (n != null) {
System.out.println("two = " + n);
}
为了将一个特定类的对象用做一个key,这个类必须提供两个方法,equals() 和 hashCode()。这两个方法在java.lang.Object中,所以所有的类都可以继承这两个方法;但是,这两个方法在Object类中的实现一般没什么用,所以你通常需要自己重载这两个方法。
Equals()方法把它的对象同另一个对象进行比较,如果这两个对象代表相同的信息,则返回true。该方法也查看并确保这两个对象属于相同的类。如果两个参照对象是完全一样的对象,Object.equals()返回true,这就说明了为什么这个方法通常不是很适合的原因。在大多数情况下,你需要一个方法来一个字段一个字段地进行比较,所以我们认为代表相同数据的不同对象是相等的。
hashCode()方法通过运用对象的内容执行一个哈希函数来生成一个int值。Hashtable和HashMap用这个值来算出一对key/value位于哪个bucket(哈希元)(或列表)中。如果你想创建一个hashtable,这个hashtable运用你自己定义的一个类的对象作为key,那么你应该确信这个类的equals()和hashCode()方法提供有用的值。首先查看你扩展的类,确定它的实现是否满足你的需求。如果没有,你应该重载方法。任何equals()方法的基本设计约束是,如果传递给它的对象属于同一个类,而且它的数据字段设定为表示同样数据的值,那么它就应该返回true。你也应该确信,如果传递一个空的参数给该方法,那么你的代码返回。
另外,在设计一个hashCode()方法时,应该记住一些规则。首先,该方法必须为一个特定的对象返回相同的值,而不管这个方法被调用了多少次(当然,只要对象的内容在调用之间没有改变,在将一个对象用做一个hashtable的key时,应该避免这一点)。第二,如果由你的equals()方法定义的两个对象是相等的,那么它们也必须生成相同的哈希码。第三,这更像是一个方针,而不是一个原则,你应该设法设计方法,使它为不同的对象内容生成不同的结果。如果偶尔不同的对象正好生成了相同的哈希码,这也不要紧。但是,如果该方法只能返回范围在1到10的值,那么只能用10个列表,而不管在hashtable中有多少个列表。
String类已经按照要求实现了hashCode(),因为在设计自己的关键字类时,可以调用String.hash(),但是StringBuffer()不能用作关键字类,其并没按要求实现hashCode().
3、Hashtable和HashMap不同之处:
Hashtable和HashMap类有三个重要的不同之处。
第一个不同主要是历史原因。Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现。
也许最重要的不同是Hashtable的方法是同步的,而HashMap的方法不是。这就意味着,虽然你可以不用采取任何特殊的行为就可以在一个多线程的应用程序中用一个Hashtable,但你必须同样地为一个HashMap提供外同步。一个方便的方法就是利用Collections类的静态的synchronizedMap()方法,它创建一个线程安全的Map对象,并把它作为一个封装的对象来返回。这个对象的方法可以让你同步访问潜在的HashMap。这么做的结果就是当你不需要同步时,你不能切断Hashtable中的同步(比如在一个单线程的应用程序中),而且同步增加了很多处理费用。
第三点不同是,只有HashMap可以让你将空值作为一个表的条目的key或value。HashMap中只有一条记录可以是一个空的key,但任意数量的条目可以是空的value。这就是说,如果在表中没有发现搜索键,或者如果发现了搜索键,但它是一个空的值,那么get()将返回null。如果有必要,用containKey()方法来区别这两种情况。
一些资料建议,当需要同步时,用Hashtable,反之用HashMap。但是,因为在需要时,HashMap可以被同步,HashMap的功能比Hashtable的功能更多,而且它不是基于一个陈旧的类的,所以有人认为,在各种情况下,HashMap都优先于Hashtable。
4、Hashtable性能
影响hashtable功效的主要因素就是表中列表的平均长度,因为平均搜索时间与这个平均长度直接相关。很显然,要减小、平均长度,你必须增加hashtable中列表的数量;如果列表数量非常大,以至于大多数列表或所有列表只包含一条记录,你就会获得最佳的搜索效率。然而,这样做可能太过分了。如果你的hashtable的列表数远远多于数据条目,那你就没有必要做这样的内存花费了,而在一些情况下,人们也不可能接受这样的做法。
我们预先知道我们有多少条记录1,000。知道这点后,我们就可以决定我们的hashtable应该包含多少个列表,以便达成搜索速度和内存使用效率之间最好的折中方式。然而,在许多情况下,你预先不知道你要处理多少条记录;数据被读取的文件可能会不断扩大,或者记录的数量可能一天一天地发生很大的变化。
随着条目的增加,Hashtable和HashMap类通过动态地扩展表来处理这个问题。这两个类都有接受表中列表最初数量的构造器,和一个作为参数的负载系数(load factor):
public Hashtable(int initialCapacity,float loadFactor)
public HashMap(int initialCapacity,float loadFactor)
将这两个数相乘计算出一个临界值。每次给哈希表添加一个新的条目时,计数就被更新,当计数超过临界值时,表被重新设置(rehash)。(列表数量增加到以前数量的两倍加1,所有的条目转移到正确的列表中。)缺省的构造器设定最初的容量为11,负载系数是0.75,所以临界值是8。当第九条记录被添加到表中时,就重新调整哈希表,使其有23个列表,新的临界值将是17(23*0.75的整数部分)。你可以看到,负载系数是哈希表中平均列表数量的上限,这就意味着,在缺省情况下,哈希表很少会有许多包含不只一条记录的列表。比较我们最初的例子,在那个例子中,我们有1,000条记录,分布在10个列表中。如果我们用缺省值,这个表将会扩展到含有1,500多个列表。但你可以控制这点。如果用负载系数相乘的列表数量大于你处理的条目数,那么表永远不会重制,所以我们可以仿效下面的例子:// Table will not rehash until it
// has 1,100 entries (10*110):
Hashtable myHashTable = new Hashtable(10, 110.0F);
你可能不想这么做,除非你没有为空的列表节省内存,而且不介意额外的搜索时间,这可能在嵌入系统中会出现这种情况。然而,这种方法可能很有用,因为重新设置很占用计算时间,而这种方法可以保证永远不会发生重新设置这种情况。
注意,虽然调用put()可以使表增大(列表数量增加),调用remove()不会有相反的结果。所以,如果你有一个大的表,而且从中删除了大部分条目,结果你会有一个大的但是大部分是空的表。
Hashtable测试:
//MyKey.java
class MyKey {
private String name = null;
private int age = 0;
public MyKey(String name, int age)
{
this.name=name;
this.age=age;
}
public boolean equals(Object obj) {
// TODO: Add your code here
if(obj instanceof MyKey)
{
MyKey objTemp=(MyKey)obj; //编译器不能自动将Object类转换为MyKey
if(name.equals(objTemp.name))
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
public int hashCode() {
// TODO: Add your code here
return name.hashCode()+age;
}
public String toString() {
// TODO: Add your code here
return name+","+age;
}
}
//HashtableTest.java
import java.util.*;
public class HashtableTest {
public static void main(String[] args) {
// TODO: Add your code here
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();
while(e.hasMoreElements())
{
MyKey key=(MyKey)e.nextElement();
System.out.print(key.toString()+"=");
System.out.println(numbers.get(key));
}
System.out.println(numbers.get(new MyKey("zhangsan",18)));
}
}
HashMap测试:
//HsshMapTest.java
import java.util.*;
class HashMapTest
{
public static void printElements(Collection c,HashMap hm)
{
Iterator it=c.iterator();
while(it.hasNext())
{
Object key1=it.next();
System.out.println(key1+" = "+hm.get(key1));
}
}
public static void main(String[] args)
{
HashMap hm=new HashMap();
Student s1=new Student(1,"zhang3");
Student s2=new Student(2,"li4");
Student s3=new Student(3,"wang5");
Student s4=new Student(1,"zhang3");
hm.put(s1,"123");
hm.put(s2,"456");
hm.put(s3,"789");
hm.put(s4,"321");
Set keys=hm.keySet();
System.out.println("Key:");
printElements(keys,hm);
}
}
class Student
{
int num;
String name;
Student(int num,String name)
{
this.num=num;
this.name=name;
}
public int hashCode()
{
return num*name.hashCode();
}
public boolean equals(Object o)
{
Student s=(Student)o;
return num==s.num && name.equals(s.name);
}
public String toString()
{
return num+":"+name;
}
}
5、关于Properties
有时侯,你可能想用一个hashtable来映射key的字符串到value的字符串。DOS、Windows和Unix中的环境字符串就有一些例子,如key的字符串PATH被映射到value的字符串C:WINDOWS;C:WINDOWSSYSTEM。Hashtables是表示这些的一个简单的方法,但Java提供了另外一种方法。
java.util.Properties类是Hashtable的一个子类,设计用于String keys和values。Properties对象的用法同Hashtable的用法相象,但是类增加了两个节省时间的方法,你应该知道。
Store()方法把一个Properties对象的内容以一种可读的形式保存到一个文件中。Load()方法正好相反,用来读取文件,并设定Properties对象来包含keys和values。
注意,因为Properties扩展了Hashtable,你可以用超类的put()方法来添加不是String对象的keys和values。这是不可取的。另外,如果你将store()用于一个不包含String对象的Properties对象,store()将失败。作为put()和get()的替代,你应该用setProperty()和getProperty(),它们用String参数。
Properties测试:
//PropertiesTest.java
package PropertiesTest;
import java.io.*;
import java.util.Properties;
public class PropertiesTest {
public static void main(String[] args) {
// TODO: Add your code here
Properties settings=new Properties();
try
{
settings.load(new FileInputStream("count.txt"));
}catch(Exception e)
{
//e.printStackTrace();
settings.setProperty("count",String.valueOf(0));
}
//settings.get("count");
int c=Integer.parseInt(settings.getProperty("count"))+1;//getProperties返回字符串,parseInt()将字符串转换为整数
System.out.println("This is the "+ c +"st");
//settings.put("count",new Integer(c).toString());
settings.setProperty("count",new Integer(c).toString());
try
{
settings.store(new FileOutputStream("count.txt"),"Program is used:");
}catch(Exception e)
{
e.printStackTrace();
}
}
}
Properties 类表示一个持久的属性集.Properties 可保存在流中或从流中加载.属性列表中每个键及其对应值都是一个字符串.
如何从输入流中加载属性文件,使用load(InputStream is)方法:
-
Properties properties = new Properties();
-
InputStream is = new FileInputStream("conn.properties");
-
properties.load(is);
-
is.close();
-
如何读属性文件中的值,使用getProperties(String key)方法:
String temp = properties.getProperties(String key);
<注>重载的方法getProperties(String key, String default)方法 将在查询不到值的情况下,返回default.
即: 如果 null == properties.getProperties(String key);
则有 default == properties.getProperties(String key, String default);
如何获取属性文件中的所有的键 ,使用propertyNames()方法,该方法返回是键的枚举.
Enumeration enumeration = properties.propertyNames();
如何修改属性文件中的值 ,使用
setProperties(String key, String value)
方法.
<注>该方法调用的 Hashtable 的put方法.如果键存在,则修改值;如果键不存在,则添加值.
如何存储属性文件到输出流 ,使用
store(OutputStream os, String description)方法:
-
Properties properties = new Properties();
-
OutputStream os = new FileOutputStream("test.properties");
-
String description = "store properties to test.properties";
-
properties.store(os, description);
-
os.close();
如何清空所有值,使用
clear()
方法.
<注>该方法继承自 Hashtable 的clear()方法.清空哈希表。
五、System类与Runtime类
1、System类
System类代表当前程序运行平台,提供了代表标准输入(System.in)输出(System.out)和错误输出(System.err)的类属性;并提供一些静态方法用于访问环境变量、系统属性;还提供了加载文件和动态链接库的方法
示例代码:(访问环境变量和系统属性)
public class TestSystem {
/**
* @param args
* @throws IOException
* @throws FileNotFoundException
*/
public static void main(String[] args) throws FileNotFoundException, IOException {
// TODO Auto-generated method stub
//获取系统所有的环境变量
Map<String,String> env = System.getenv();
for(String name : env.keySet()){
System.out.println(name+"------>"+env.get(name));
}
//获取指定的环境变量的值
System.out.println(System.getenv("JAVA_HOME"));
//获取系统所有属性
Properties pro = System.getProperties();
//将所有属性保存到pro.txt中
pro.store(new FileOutputStream("pro.txt"), "System Properties");
//输出特定的系统属性
System.out.println(System.getProperty("os.name"));
}
}
//文件名SystemProperty.java
public class SystemProperty
{
public void property()
{
String str;
str = System.getProperty("java.version"); // Java运行时环境的版本
System.out.println("java.version" + ": " + str);
str = System.getProperty("java.vendor.url"); // Java提供商的URL
System.out.println("java.vendor.url" + ": " + str);
str = System.getProperty("java.home"); // Java的安装目录
System.out.println("java.home" + ":" + str);
str = System.getProperty("java.vm.specification.version");// Java虚拟机规范版本
System.out.println("java.vm.specification.version" + ": " + str);
str = System.getProperty("java.class.path"); // Java类的路径
System.out.println("java.class.path" + ": " + str);
str = System.getProperty("javaext.dirs"); // 目录或扩展目录的路径
System.out.println("java.ext.dirs" + ": " + str);
str = System.getProperty("os.name"); // 操作系统的名称
System.out.println("os.name" + ": " + str);
str = System.getProperty("file.separator"); // 文件分隔符
System.out.println("file.separator" + ": " + str);
str = System.getProperty("user.dir"); // 用户的当前工作目录
System.out.println("user.dir" + ": " + str);
}
}
//文件名SystemPropertyTest.java
public class SystemPropertyTest
{
public static void main(String[] args)
{
SystemProperty sp = new SystemProperty();
sp.property();
}
}
System 类是一个抽象类,所有的字段和方法都是静态的。其中包含一些有用的类字段和方法,它不能被实例化。
在 System 类中,有三个静态的变量in、out、err,分别对应标准输入、标准输出和错误输出流;有对外部定义的属性和环境变量的访问的方法;加载文件和库的方法;还有快速复制数组的一部分的实用方法。因此,System.in、System.out、System.err实际上表示三个对象,这也就是为什么可以用System.out.println("Hello World!")的原因。
System常用方法:
(1)public static long currentTimeMillis()
返回当前的格林威治时间(从1970年开始,以毫秒数计算)。
(2)public static void arraycopy(Object source, int sourceStart, Object target, int targetStart,int size)
复制数组:source表示源数组,source中开始复制数组时的下标由sourceStart表示源数组起始下标;target表示目标数组,target中开始复制数组时的下标由targetStart表示;Size是被复制的元素的个数。
(3)public static void exit(int status)
终止当前正在运行的 Java 虚拟机。
(4)public void gc()
清理内存“垃圾”,当系统内存缺少时,可用它来释放无用内存。
(5)public static Properties getProperties()
获取当前系统属性并通过Properties类对象返回结果。
(6)public static String getProperty(String key)
获取系统某个属性,返回参数key指定的属性。
(7)public static String getProperty(String key , String def)
获取系统某个属性,返回参数key指定的属性;未找到参数key指定的属性时,返回参数def的缺省值。
Java中定义了当前运行环境的系统属性。如表5-1所示:
表 系统属性及含义
系统属性名 |
含义 |
os.name |
操作系统名称 |
java.class.path |
Java类库的路径 |
java.home |
Java的安装目录 |
user.dir |
用户当前的工作目录 |
user.home |
用户的根目录 |
user.name |
用户名 |
file.separator |
文件分隔符 |
line.separator |
行结束符 |
path.separator |
路径分隔符 |
2、Runtime类
Runtime类代表Java程序的运行时环境,每个Java程序都有一个与之对应的Runtime实例,应用程序通过该对象与其运行时环境相连,该类可以访问JVM的相关信息,如处理器数量、内存信息等。
示例代码:public class TestRuntime {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Runtime rt = Runtime.getRuntime();
System.out.println("处理器数量:"+rt.availableProcessors());
System.out.println("空闲内存数:"+rt.freeMemory() );
System.out.println("总内存数:"+rt.totalMemory());
System.out.println("可用最大内存数:"+rt.maxMemory());
}
}
六、Date类、Calendar类
1、Date类
在JDK1.0中,Date类是唯一的一个代表时间的类,但是由于Date类不便于实现国际化,所以从JDK1.1版本开始,推荐使用Calendar类进行时间和日期处理。这里简单介绍一下Date类的使用。
(1)使用Date类代表当前系统时间
Date d = new Date();
System.out.println(d);
使用Date类的默认构造方法创建出的对象就代表当前时间,由于Date类覆盖了toString方法,所以可以直接输出Date类型的对象,显示的结果如下:
Sun Mar 08 16:35:58 CST 2009
在该格式中,Sun代表Sunday(周日),Mar代表March(三月),08代表8号,CST代表China Standard Time(中国标准时间,也就是北京时间(东八区))。
(2)使用Date类代表指定的时间
Date d1 = new Date(2009-1900,3-1,9);
System.out.println(d1);
使用带参数的构造方法,可以构造指定日期的Date类对象,Date类中年份的参数应该是实际需要代表的年份减去1900,实际需要代表的月份减去1以后的值。例如上面的示例代码代表就是2009年3月9号。
(3)获得Date对象中的信息
Date d2 = new Date();
//年份
int year = d2.getYear() + 1900;
//月份
int month = d2.getMonth() + 1;
//日期
int date = d2.getDate();
//小时
int hour = d2.getHours();
//分钟
int minute = d2.getMinutes();
//秒
int second = d2.getSeconds();
//星期几
int day = d2.getDay();
System.out.println("年份:" + year);
System.out.println("月份:" + month);
System.out.println("日期:" + date);
System.out.println("小时:" + hour);
System.out.println("分钟:" + minute);
System.out.println("秒:" + second);
System.out.println("星期:" + day);
使用Date类中对应的get方法,可以获得Date类对象中相关的信息,需要注意的是使用getYear获得是Date对象中年份减去1900以后的值,所以需要显示对应的年份则需要在返回值的基础上加上1900,月份类似。在Date类中还提供了getDay方法,用于获得Date对象代表的时间是星期几,Date类规定周日是0,周一是1,周二是2,后续的依次类推。
(4)Date对象和相对时间之间的互转
Date d3 = new Date(2009-1900,3-1,10);
long time = 1290876532190L;
//将Date类的对象转换为相对时间
long t = d3.getTime();
System.out.println(t);
//将相对时间转换为Date类的对象
Date d4 = new Date(time);
System.out.println(d4);
使用Date对象中的getTime方法,可以将Date类的对象转换为相对时间,使用Date类的构造方法,可以将相对时间转换为Date类的对象。经过转换以后,既方便了时间的计算,也使时间显示比较直观了。
2、Calendar类
从JDK1.1版本开始,在处理日期和时间时,系统推荐使用Calendar类进行实现。在设计上,Calendar类的功能要比Date类强大很多,而且在实现方式上也比Date类要复杂一些,下面就介绍一下Calendar类的使用。
Calendar类是一个抽象类,在实际使用时实现特定的子类的对象,创建对象的过程对程序员来说是透明的,只需要使用getInstance方法创建即可。
(1)使用Calendar类代表当前时间
Calendar c = Calendar.getInstance();
由于Calendar类是抽象类,且Calendar类的构造方法是protected的,所以无法使用Calendar类的构造方法来创建对象,API中提供了getInstance方法用来创建对象。
使用该方法获得的Calendar对象就代表当前的系统时间,由于Calendar类toString实现的没有Date类那么直观,所以直接输出Calendar类的对象意义不大。
(2)使用Calendar类代表指定的时间
Calendar c1 = Calendar.getInstance();
c1.set(2009, 3 - 1, 9);
(3)使用Calendar类代表特定的时间,需要首先创建一个Calendar的对象,然后再设定该对象中的年月日参数来完成。
set方法的声明为:
public final void set(int year,int month,int date)
(4)以上示例代码设置的时间为2009年3月9日,其参数的结构和Date类不一样。Calendar类中年份的数值直接书写,月份的值为实际的月份值减1,日期的值就是实际的日期值。
如果只设定某个字段,例如日期的值,则可以使用如下set方法:
public void set(int field,int value)
在该方法中,参数field代表要设置的字段的类型,常见类型如下:
Calendar.YEAR——年份
Calendar.MONTH——月份
Calendar.DATE——日期
Calendar.DAY_OF_MONTH——日期,和上面的字段完全相同
Calendar.HOUR——12小时制的小时数
Calendar.HOUR_OF_DAY——24小时制的小时数
Calendar.MINUTE——分钟
Calendar.SECOND——秒
Calendar.DAY_OF_WEEK——星期几
后续的参数value代表,设置成的值。例如:
c1.set(Calendar.DATE,10);
该代码的作用是将c1对象代表的时间中日期设置为10号,其它所有的数值会被重新计算,例如星期几以及对应的相对时间数值等。
(5)获得Calendar类中的信息
Calendar c2 = Calendar.getInstance();
//年份
int year = c2.get(Calendar.YEAR);
//月份
int month = c2.get(Calendar.MONTH) + 1;
//日期
int date = c2.get(Calendar.DATE);
//小时
int hour = c2.get(Calendar.HOUR_OF_DAY);
//分钟
int minute = c2.get(Calendar.MINUTE);
//秒
int second = c2.get(Calendar.SECOND);
//星期几
int day = c2.get(Calendar.DAY_OF_WEEK);
System.out.println("年份:" + year);
System.out.println("月份:" + month);
System.out.println("日期:" + date);
System.out.println("小时:" + hour);
System.out.println("分钟:" + minute);
System.out.println("秒:" + second);
System.out.println("星期:" + day);
使用Calendar类中的get方法可以获得Calendar对象中对应的信息,get方法的声明如下:
public int get(int field)
其中参数field代表需要获得的字段的值,字段说明和上面的set方法保持一致。需要说明的是,获得的月份为实际的月份值减1,获得的星期的值和Date类不一样。在Calendar类中,周日是1,周一是2,周二是3,依次类推。
(6)其它方法说明
其实Calendar类中还提供了很多其它有用的方法,下面简单的介绍几个常见方法的使用。
a、add方法
public abstract void add(int field,int amount)
该方法的作用是在Calendar对象中的某个字段上增加或减少一定的数值,增加是amount的值为正,减少时amount的值为负。
例如在计算一下当前时间100天以后的日期,代码如下:
Calendar c3 = Calendar.getInstance();
c3.add(Calendar.DATE, 100);
int year1 = c3.get(Calendar.YEAR);
//月份
int month1 = c3.get(Calendar.MONTH) + 1;
//日期
int date1 = c3.get(Calendar.DATE);
System.out.println(year1 + "年" + month1 + "月" + date1 + "日");
这里add方法是指在c3对象的Calendar.DATE,也就是日期字段上增加100,类内部会重新计算该日期对象中其它各字段的值,从而获得100天以后的日期,例如程序的输出结果可能为:
2009年6月17日
b、after方法
public boolean after(Object when)
该方法的作用是判断当前日期对象是否在when对象的后面,如果在when对象的后面则返回true,否则返回false。例如:
Calendar c4 = Calendar.getInstance();
c4.set(2009, 10 - 1, 10);
Calendar c5 = Calendar.getInstance();
c5.set(2010, 10 - 1, 10);
boolean b = c5.after(c4);
System.out.println(b);
在该示例代码中对象c4代表的时间是2009年10月10号,对象c5代表的时间是2010年10月10号,则对象c5代表的日期在c4代表的日期之后,所以after方法的返回值是true。
另外一个类似的方法是before,该方法是判断当前日期对象是否位于另外一个日期对象之前。
c、getTime方法
public final Date getTime()
该方法的作用是将Calendar类型的对象转换为对应的Date类对象,两者代表相同的时间点。
类似的方法是setTime,该方法的作用是将Date对象转换为对应的Calendar对象,该方法的声明如下:
public final void setTime(Date date)
转换的示例代码如下:
Date d = new Date();
Calendar c6 = Calendar.getInstance();
//Calendar类型的对象转换为Date对象
Date d1 = c6.getTime();
//Date类型的对象转换为Calendar对象
Calendar c7 = Calendar.getInstance();
c7.setTime(d);
(7)、Calendar对象和相对时间之间的互转
Calendar c8 = Calendar.getInstance();
long t = 1252785271098L;
//将Calendar对象转换为相对时间
long t1 = c8.getTimeInMillis();
//将相对时间转换为Calendar对象
Calendar c9 = Calendar.getInstance();
c9.setTimeInMillis(t1);
在转换时,使用Calendar类中的getTimeInMillis方法可以将Calendar对象转换为相对时间。在将相对时间转换为Calendar对象时,首先创建一个Calendar对象,然后再使用Calendar类的setTimeInMillis方法设置时间即可。
应用示例
下面以两个简单的示例介绍时间和日期处理的基本使用。
1、计算两个日期之间相差的天数
例如计算2010年4月1号和2009年3月11号之间相差的天数,则可以使用时间和日期处理进行计算。
该程序实现的原理为:首先代表两个特定的时间点,这里使用Calendar的对象进行代表,然后将两个时间点转换为对应的相对时间,求两个时间点相对时间的差值,然后除以1天的毫秒数(24小时X60分钟X60秒X1000毫秒)即可获得对应的天数。实现该示例的完整代码如下:
import java.util.*;
/**
* 计算两个日期之间相差的天数
*/
public class DateExample1 {
public static void main(String[] args) {
//设置两个日期
//日期:2009年3月11号
Calendar c1 = Calendar.getInstance();
c1.set(2009, 3 - 1, 11);
//日期:2010年4月1号
Calendar c2 = Calendar.getInstance();
c2.set(2010, 4 - 1, 1);
//转换为相对时间
long t1 = c1.getTimeInMillis();
long t2 = c2.getTimeInMillis();
//计算天数
long days = (t2 - t1)/(24 * 60 * 60 * 1000);
System.out.println(days);
}
}
2、输出当前月的月历
该示例的功能是输出当前系统时间所在月的日历,例如当前系统时间是2009年3月10日,则输出2009年3月的日历。
该程序实现的原理为:首先获得该月1号是星期几,然后获得该月的天数,最后使用流程控制实现按照日历的格式进行输出即可。即如果1号是星期一,则打印一个单位的空格,如果1号是星期二,则打印两个单位的空格,依次类推。打印完星期六的日期以后,进行换行。实现该示例的完整代码如下:
import java.util.*;
/**
* 输出当前月的日历
*/
public class DateExample2{
public static void main(String[] args){
//获得当前时间
Calendar c = Calendar.getInstance();
//设置代表的日期为1号
c.set(Calendar.DATE,1);
//获得1号是星期几
int start = c.get(Calendar.DAY_OF_WEEK);
//获得当前月的最大日期数
int maxDay = c.getActualMaximum(Calendar.DATE);
//输出标题
System.out.println("星期日 星期一 星期二 星期三 星期四 星期五 星期六");
//输出开始的空格
for(int i = 1;i < start;i++){
System.out.print(" ");
}
//输出该月中的所有日期
for(int i = 1;i <= maxDay;i++){
//输出日期数字
System.out.print(" " + i);
//输出分隔空格
System.out.print(" ");
if(i < 10){
System.out.print(' ');
}
//判断是否换行
if((start + i - 1) % 7 == 0){
System.out.println();
}
}
//换行
System.out.println();
}
}
关于时间和日期的处理就介绍这么多,更多的实现方法还需要根据具体问题进行对应的实现。
七、BigInteger类
在用C或者C++处理大数时感觉非常麻烦,但是在JAVA中有两个类BigInteger和BigDecimal分别表示大整数类和大浮点数类,至于两个类的对象能表示最大范围不清楚,理论上能够表示无线大的数,只要计算机内存足够大。
这两个类都在java.math.*包中,因此每次必须在开头处引用该包。
Ⅰ基本函数:
1.valueOf(parament); 将参数转换为制定的类型
比如 int a=3;
BigInteger b=BigInteger.valueOf(a);
则b=3;
String s=”12345”;
BigInteger c=BigInteger.valueOf(s);
则c=12345;
2.add(); 大整数相加
BigInteger a=new BigInteger(“23”);
BigInteger b=new BigInteger(“34”);
a. add(b);
3.subtract(); 相减
4.multiply(); 相乘
5.divide(); 相除取整
6.remainder(); 取余
7.pow(); a.pow(b)=a^b
8.gcd(); 最大公约数
9.abs(); 绝对值
10.negate(); 取反数
11.mod(); a.mod(b)=a%b=a.remainder(b);
12.max(); min();
13.punlic int comareTo();
14.boolean equals(); 是否相等
15.BigInteger构造函数:
一般用到以下两种:
BigInteger(String val);
将指定字符串转换为十进制表示形式;
BigInteger(String val,int radix);
将指定基数的 BigInteger 的字符串表示形式转换为 BigInteger
Ⅱ.基本常量:
A=BigInteger.ONE 1
B=BigInteger.TEN 10
C=BigInteger.ZERO 0
Ⅲ.基本操作
1. 读入:
用Scanner类定义对象进行控制台读入,Scanner类在java.util.*包中
Scanner cin=new Scanner(System.in);// 读入
while(cin.hasNext()) //等同于!=EOF
{
int n;
BigInteger m;
n=cin.nextInt(); //读入一个int;
m=cin.BigInteger();//读入一个BigInteger;
System.out.print(m.toString());
}
Ⅳ.运用
八、Math类、Random类
1 Random类
Java实用工具类库中的类java.util.Random提供了产生各种类型随机数的方法。它可以产生int、long、float、double类型的随机数。这也是它与java.lang.Math中的方法random()最大的不同之处,后者只能产生double型的随机数。类Random中的方法十分简单,它只有两个构造方法和六个普通方法。
(1)Random类的构造器
public Random()
public Random(long seed)
Java产生随机数需要有一个种子数seed。第一个构造方法没有参数,它使用系统时间作为种子数seed。
在创建一个新的Random对象的时候,如果使用带有种子数的构造器,那么生成的随机数是一个定值,因为这个随机数是根据种子数计算出来的。如果直接使用不带种子数的构造器,那么产生的随机数每次都是不同的。
(2)Random类的常用方法
u public int nextInt()
u public long nextLong()
第一个方法是产生一个32位整型随机数,而第二个方法是产生一个64位整型随机数。如果不断调用上面两个方法产生随机数,当调用次数足够多时,就会发现不同的随机数出现的几率数基本相同。
u public float nextFloat()
该方法是产生一个Float型随机数。
u public double nextDouble()
该方法是产生一个Double型随机数。
上面的两个方法分别从Random类对象中生成下一个双精度或单精度浮点数,其取值范围在0.0到1.0之间且总小于1.0,所获得的浮点数按概率平均分布。
u public synchronized void setSeed(long seed)
该方法是设定种子数seed。
u public double nextGaussian()
该方法可以获得0.0到1.0之间的双精度浮点随机数,且浮点数按高斯分布。
2、Math类
在编写程序时,有时需要进行数学运算,如求某数的平方、绝对值、对数运算,或者生成一个随机数等等。java.lang包中的Math类提供了许多用来进行科学计算的方法,这些方法都是static类的方法,所以在使用时不需要创建Math类的对象,而直接用类名作为前缀,就可以调用这些方法。另外,Math类中还有两个静态常量E和PI,它们的值分别是:2.718282828284590452354和3.14159265358979323846。Math类中的方法很简单,其常用的方法有以下几种:
(1) public static double abs(double a) //返回 double 值的绝对值
(2) public static double sin(double a) //返回角的三角正弦
(3) static float max(double a, double b) //返回两个 float 值中较大的一个
(4) public static double min(double a, double b);//返回两个 double 值中较小的一个
(5) public static long round(double a) //返回最接近参数的 long
(6) public static double sqrt(double a) //返回舍入的 double 值的正平方根
(7) public static double pow(double a, double b)//返回a的第b次幂的值
(8) public static double random() //返回0~1之间的随机数(不含0和1)
(9) public static double floor(double a) //返回比a小的最大整数,即取整
更详细的Math类的方法请查看API文档。