Skip navigation.

exploreopera

| Help

Sign up | Help

王志军个人空间

工作、生活、健康

Posts tagged with "Java"

使用Java操作Windows注册表

Cons:
使用这种方法只能对HKCU\Software\Javasoft\Prefs\(调用Preferences.userRoot())或者HKLM\Software\Javasoft\Prefs\(调用Preferences.systemRoot())下Node操作。

JDK1.4引入了Preferences类, 用于设置用户的首选项,对于Windows平台就是操作注册表了, 下面的程序就可以往注册表里写几个值,运行完毕后, 打开regedit, 搜寻一下, 看看注册表里是不是有了变化了。

  (下面注释掉的那段代码给恢复, 就能把写入的注册键值删除, 很方便啊)

  至于读注册表项, 比写还方便, 查JDK API文档吧..
// PrefsDemo.java

import java.io.*;
import java.util.prefs.*;

public class PrefsDemo {
public static void main(String args[])
{
 String keys[] ={"sunway","copyright","author"};
 String values[] ={"sunway technology company","copyright 2002","turbochen@163.com"};

 /* 建立一个位于user root下的/com/sunway/spc节点参数项*/
 Preferences prefsdemo =Preferences.userRoot().node("/com/sunway/spc");

 /* 储存参数项*/
 for (int i=0 ; i < keys.length; i++)
 {
  prefsdemo.put(keys[i], values[i]);
 }

 /* 导出到XML文件 */
 try
 {
  FileOutputStream fos = new FileOutputStream("prefsdemo.xml");
  prefsdemo.exportNode(fos);
 } catch (Exception e)
 {
  System.err.println("Cannot export nodes: " + e);
 }

 /* 去掉注释可以清除注册表中的参数项*/
 /*try
 {
  prefsdemo.removeNode();
 } catch (BackingStoreException e)
 {
 }*/
}
}

在Java中获取环境变量

URL: http://wonderow.cnblogs.com/archive/2004/12/17/78358.html

环境变量是操作系统中与应用程序交互重要的部分,获取或设置环境变量是编程中经常要用到的技术,这里转载了一篇Java中获取环境变量的文章:
(zz From http://www.javaidea.net/list.jsp?topic=3)

在运行时设置一个环境变量 debug 为 true: java -Ddebug=true YourClass
在程序中设置一个环境变量 debug 为 true: System.setProperty( "debug", "true" );
获取一个环境变量 debug : String debug = System.getProperty( "debug" );

下表中列出了一部分环境变量,这些是 Java 已经定义好的,可以在程序中通过 System.getProperty( "key" ) 来获取,更多的环境变量请参阅 JDK 文档。
属性名 描 述
java.version Java 运行时版本
java.home Java 的安装目录
java.class.version Java 类格式的版本号
java.class.path Java 类的查找路径
java.io.tmpdir 默认的临时目录
java.compiler Java 所使用的及时编译器
java.ext.dirs Java 扩展包的目录
os.name 操作系统的名称
os.arch 操作系统的体系结构
os.version 操作系统的版本
file.separator 文件分隔符(Unix 下为'/')
path.separator 路径分隔符(Unix 下为':')
line.separator 换行符(Unix 下为'\n')
user.name 用户帐号名
user.home 用户目录
user.dir 用户当前的工作目录


http://java.sun.com/j2se/1.4.2/docs/api/java/lang/System.html#getProperties()

Java同步调用external app

我们可以根据Runtime和Process类在Java中调用外部程序。
更多资料参见:
http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html
http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Process.html
try {
Process process = Runtime.getRuntime().exec("start a.doc");
int exitVal = process.waitfor( );
System.out.println("Process exitValue: " + exitVal);
} catch (IOException e) {
e.printStackTrace();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch (Throwable t) {
t.printStackTrace();
}

可以使用下面代码显示子进程的输出内容:
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream());
String line = null;
while ( (line=bufferedReader.readLine()) != null)
System.out.println(line);  

JAVA高级编程:Java中的多线程

JAVA 的多线程特性是它的一大优点。多线程是相对单线程而言的,单线程是指任何时候只能有一个程序在运行,其他程序必须等待。而有了多线程这个特性后,JAVA 可以支持多个程序并发执行。当你要写一个能同时执行多个功能的程序时,就需要用到JAVA 的多线程功能。JAVA 的多线程功能被封装在线程类中。现在介绍一下线程的使用方法。

  一、线程的创建

  有两种方法可以创建线程。第一种方法是通过继承类“Thread”来创建线程类。如:

  class aaa extends Thread {

  //aaa 是类的名称

  public void run() {

  //run 是整个线程类代码的入口

  // 与C 语言中的main 类似

  // 源程序

  }

  }

  第二种方法是建立一个具有Runnable 接口的类。如:

  class aaa implements Runnable {

  //aaa 是类的名称

  public void run() {

  //run 是整个线程类代码的入口

  // 与C 语言中的main 类似

  // 源程序

  }

  }

  二、线程的调用

  如果采用第一种方法,创建的线程类的调用格式如下:

  aaa test=new aaa();

  //test 是线程类aaa 的一个实例

  test.start();

  //start 是线程类的成员函数,将产生一

  // 个新的线程,这个线程自动调用run

  如果采用第二种方法,创建的线程类的调用格式如下:

  aaa test=new aaa();

  //test 是类aaa 的一个实例

  new Thread(test).start();

  // 通过Thread 创建

  // 一个新的线程

  三、线程类的变量及成员函数

  变量:

  MAX_PRIORITY 规定了线程优先级所能设置的最大值
  MIN_PRIORITY 规定了线程优先级所能设置的最小值
  NORM_PRIORITY 线程优先级的缺省值成员函数:
  activeCount() 返回该线程组中当前激活的线程的数目
  checkAccess() 检测当前线程是否可以被修改
  countStackFrames() 返回该线程的堆栈框的数目
  currentThread() 返回当前正在执行的线程对象
  destroy() 终止一个线程,不清除其他相关内容
  dumpStack() 一个调试过程,输出当前线程的堆栈使用情况
  enumerate(Thread[]) 将所有该线程组中激活的线程复制到一个特殊的数组中
  getName() 返回线程的名称
  getPriority() 返回线程的优先级
  getTheradGroup() 返回线程组
  interrupt() 向一个线程发送一个中断信息
  interrupted() 检查该线程是否被中断
  isAlive() 检查线程是否处于激活状态
  isDaemon() 检查该线程是否常驻内存
  isInterrupted() 检查另一个线程是否被中断
  join(long) 在long 毫秒中等待线程中止
  join(long,int) 在long 毫秒int 纳秒内等待线程中止
  join() 永远等待线程的中止
  resume() 重新开始执行该线程
  run() 整个代码的入口与C 语言中的main 类似
  setDaemon() 将该线程设置为Daemon( 常驻内存)
  setName(String) 设置线程的名称
  setPriority(int) 设置线程的优先级
  sleep(long) 使一个线程休眠long 毫秒
  sleep(long,int) 使一个线程休眠long 毫秒int 纳秒
  start()

  启动一个线程,这个线程将自动调用run 函数。同时,在新的线程开始执行时,调用start 的的那个线程将立即返回执行主程序stop() 终止一个线程

  stop(Throwable) 终止一个线程,该线程是由Throwable 类继承过来的
  suspend() 暂停线程的执行
  toString() 返回一个字符串,包括线程的名称、优先级和线程组
  yield() 使线程放弃对CPU 的控制,使其它处于等待状态的线程运行
  

  四、同步设定

  当两个函数需要使用同一个资源时,不能同时执行。JAVA 提供了同步设定的功能。每个类对自己代码中不能与其它程序同时运行的部分进行同步设定。同步设定的功能是通知JAVA 哪些代码段已被加锁而只能单独运行。有以下两种方法进行同步设定:

  1、将函数设置为同步方式

  public synchronized void fun1() {

  // 源代码

  }

  public synchronized void fun2() {

  // 源代码

  }

  对于上述被设置为同步方式的函数,只有在获得资源后,方可运行。对于每一个类来说,只有一个资源。任何时刻都只有一个获得资源的程序在运行,函数在获得资源前处于等待状态。

  2、在调用函数中说明此函数需要被同步

  synchronized(res) {

  res.fun1();

  }

  synchronized(res) {

  res.fun2();

  }

  在上述例子中,fun1 与fun2 只能获得res 的资源后才能运行。函数调用结束后,资源才被释放,从而使其它等待这个资源的线程得以运行。

  五、线程变量的安全

  可以把线程中的变量设置为安全的,这将使该线程使用这个变量时其它线程不能改变这个变量的值。这个功能使用编译器更易于实现代码优化。如果不使用这种设置,编译器很难将一个线程中已被修改的变量区别开来。这种安全变量的定义方式如下:

  threadsafe int ttt;

  //threadsafe 是线程安全变量的类型,ttt 是变量名

  常用的static 变量就是线程安全变量的典型例子。

更多资料:
http://scv.bu.edu/Doc/Java/tutorial/java/threads/index.html
http://g.oswego.edu/dl/cpj/mechanics.html
http://uk.builder.com/programming/java/0,39026606,20276813,00.htm
http://www.javaworld.com/javaworld/jw-10-1998/jw-10-toolbox.html
http://www.javaworld.com/javaworld/jw-07-2002/jw-0703-java101.html
http://www.javaworld.com/jw-04-1996/jw-04-synch.html
http://www.javaworld.com/javaworld/jw-07-2002/jw-0703-java101guide.html
http://www.javaworld.com/javaworld/javaqa/1999-11/02-qa-semaphore.html
http://www.javaworld.com/javaworld/jw-12-2000/jw-1215-threadbooks.html

从java内核看性能分析与设计

URL: http://www.codechina.net/resource/html/2006-01/16/131964.html

引言
java语言自90年代出现以来,因为它的安全性和跨平台性(即所谓的”Write Once,Run Anywhere”)等特点,深得广大程序员的青睐,但是同时,Java程序的运行效率的低下也是程序员的心病。Java是介于解释型和编译型之间的一种语言,同样的程序,如果用编译型语言C来实现,其运行速度一般要比Java快一倍以上。怎样提高java应用程序的效率是广大程序员关心问题。本文将从与 Java字节码的运行过程中影响性能的相关因素的分析入手,然后,探讨一些在Java代码的设计过程中具体的有助于提高性能的策略。
一、性能分析
JVM运行时的负载主要集中在字节码的执行,内存管理,线程管理和其他的操作几个方面。
1.1 JVM的结构
JVM中运行的是Java字节码(Bytecode).class文件,这种class文件除了准确定义一个类或接口的表示外,还定义了一些与平台相关的诸如字节顺序的详细信息。
Java 的数据类型分为primitive和reference,对于不同的数据类型的运算在JVM中的有不同的指令去执行,比如iadd,ladd,fadd就是分别针对int,long,float的加法运算,当然,它们的执行效率也不一样,运行时的数据区,在一个程序运行时,JVM都要为它定义不同的运行数据区,有些数据区在JVM启动时就创建好了,直到整个JVM退出时才释放掉,还有一些数据区的是属于每个线程的,它的生命周期与线程相等。
JVM中的逻辑结构有:
PC(program counter)寄存器,每个线程有自己的PC(program counter)寄存器,当JVM执行的方法不是本地(Native)的时,这里存放当前线程运行的指令的地址,如果是本地(Native)的,PC (program counter)寄存器的值没有定义。
JVM栈(stack),当创建线程时,每个线程都创建一个属于自己的栈,用来存放frames(见下面),它存有本地变量,方法调用中的部分结果。
堆(heap),JVM中所有线程共享这个堆,类的实例和数组都是从堆中分配内存的,堆是在整个JVM启动时初始化的。
方法区(Method Area),线程间共享,它存放每个类中的运行时常数池(runtime constant pool),域值和方法数据,以及方法和类的构造函数的代码,其中包括用于类的特殊方法,实例初始化和接口类型的初始化,
运行时常数池(runtime constant pool),是每个类或接口的class文件中的常数池表在运行时的表示,它包括各种常数如编译时就知道的数字常量,还有运行时才能确定的方法和域的引用,类似传统语言的符号表,
本地(Native )方法栈(Stack),用来支持本地(Native)方法调用,这些方法用非Java的语言编写,需要传统的\"C\"栈。
帧(Frames), 存放方法调用中的数据和部分结果及返回值,执行动态连接,分派例外,一个新的Frame在方法被调用时创建,方法调用正常或非正常完成时销毁,Frame 从每个线程创建的JVM的栈中分配内存,它属于每个线程,每个Frame有自己的本地变量组,自己的操作栈(Operand Stack)和指向当前方法的运行时常数池的引用,本地变量组和操作栈的大小在编译的时候就已经确定,在一个获得控制的线程中只有一个Frame是激活的,这个Frame为当前Frame,它的方法为当前方法,方法所属的类为当前类,当这个方法又调用别的方法或结束时,这个当前Frame不再激活,一个新的Frame被创建并成为当前Frame,直到当前方法调用完成后,这个Frame被释放并返回结果,前一个方法的Frame成为当前的Frame,
本地变量,每个方法的Frame包含一组在方法中定义的本地变量,它们的大小在Java编译时就已确定。
动态连接 (Dynamic Linking),每个Frame包含一个指向当前方法的运行时常数池的引用,它通过符号引用(symbolic references)访问变量和指向被引用的方法,动态连接(Dynamic Linking)在运行时将这些方法的符号引用转为具体的方法引用,并加载相应的类,它还将变量影射到当前运行时的变量的内存偏移上。
1.2 字节码(Bytecode)的执行
JVM 动态地加载(Loads),连接(Links)和初始化(Initializes)类和接口的字节码,加载(Loading)就是JVM发现具有某一特定名字的类或接口的二进制表示,并从这个二进制表示在内存中创建出一个类或接口,连接(Linking)就是使一个类或接口与JVM的运行时状态很好的结合,以便执行它,一个类或接口的初始化就是执行它的初始化方法。
1.3 内存管理
Java是一个面向对象的语言,因此,在JVM的内存中大部分是对象,从上面的分析我们知道,对象的内存是从堆(heap)分配的,对象内存的回收是由自动内存管理系统(由叫垃圾收集器-Garbage Collector)来完成的,编程人员是不用显式的释放内存的,垃圾收集器Garbage Collector通过记录指向对象的引用的数目来决定是否释放对象所占据的内存空间,当指向某个对象的引用数为零时,这个对象就可以释放了。
1.4 线程管理
Java 是一个支持多线程的语言,因此线程的管理是JVM的一个主要工作,每个线程都有自己的工作内存,线程间的共享变量是存放在整个JVM的主内存中的,线程间数据的同步通过lock来共享数据并保证数据的一致性,线程间控制的转移通过对wait,notify等方法的调用来实现。
二、性能设计
通过以上的分析,我们就以下几个方面提出一些有关性能设计的策略。
2.1 对象的构造
从上面我们知道,Java对象的内存是自动管理的,因此,一般认为,程序员是不用担心内存的分配的,但这种想法是不完全正确的,java通过垃圾收集器 (Garbage Collector)来处理内存分配与释放的底层操作,程序员不用直接管理内存,这样防止了由于内存的错误操作导致的数据破坏(corruption),但并不意味着程序员不用担心内存的使用,内存的使用不但会给系统带来很大的负担,比如,Java并不阻止程序占用过多的内存,当对象向堆所请求的内存不足时,垃圾收集器(Garbage Collector)就会自动启动,释放那些引用数为零的对象所占用的内存,Java也不会自动释放无用的对象的引用,如果程序忘记释放指向对象的引用, 则程序运行时的内存随着时间的推移而增加,发生所谓内存泄漏(memory leaks),创建对象不但消耗CPU的时间和内存,同时,为释放对象内存JVM需不停地启动垃圾收集器(Garbage Collector),这也会消耗大量的CPU时间。
策略:尽量避免在被经常调用的代码中创建对象。
对于集合类(collection),应尽量初始化它的大小,如果不初始化它的大小,JVM自动给它一个缺省的大小,当你的要求大于这个缺省的大小时,JVM就会重新创建一个新的collection对象,原来的对象就释放掉,这样必然会增加JVM的负担。
当一个类的多个实例在其本地的变量里访问一个特定的对象时,最好将这个变量设计为静态(static)的,而不是每个实例中变量里都存放那个对象的引用。
因为对象的创建是非常昂贵的,所以应尽量重用,少用new来获得对象的引用,尽量重用容器对象(Vector,Hashtable)等而不总是创建新的对象抛弃旧的对象,但一定要注意释放容器对象中所保存的指向别的对象的引用。
尽量使用primitive数据类型。
当只是访问一个类的某个方法时,不要创建该类的对象,而是将该方法设计成一个static的方法。
尽量简化类的继承关系和设计简单的构造函数。
创建简单数据类型的数组要比初始化一个这样的数组快,创建一个复杂类型的数组要比克隆一个这样的数组快。
2.2 字符串(String)
String 在Java程序中被广泛使用,String对象是不可改变的,例如: String str=\"testing\"; str=str+\"string\"; 这个\"testing\"String一旦创建,就不能更改,但指向这个String的引用str可以改变,str原来指向\"testing\",经过第二个运算后,改为指向新的String\"testingstring\"了。针对String的这个特性,对于String的使用,我们有如下策略: 
如果字符串在程序中可能被改变,比如增加,接或删除字符,就应使用StringBuffer,创建具有初始大小的StringBuffer对象,尽量重用该对象,而不使用\"+\"操作。
当我们要分析字符串中的字符时,就不要使用String或StringBuffer,而是使用字符(cbar)数组,别是在循环中分析字符时,更应如此。 
尽量少用StringTokenizer,它的方法的性能比较差。
2.3 输入输出(Input/Output)
程序的I/O往往是性能的瓶颈所在,java io定义了两个基本的抽象类:InputStream和OutputStream,对于不同的数据类型比如磁盘,网络又提供了不同的实现,javaio也提供了一些缓冲流(Buffered Stream),使硬盘可以很快的读写一大块的数据, 而Java基本的I/O类一次只能读写一个字节,但缓冲流(Buffered Stream)可以一次读写一批数据,,缓冲流(Buffered Stream)大大提高了I/O的性能,对象的序列化(serialization)是一个将处于生成期的对象序列化成可以在流(stream)中读写的数据的过程,象的序列化是一个非常复杂,昂贵的过程,要一个类implements接口 java io Serializable,它就可以被自动的序列化,针对以上分析,我们对I/O有如下对策:
·小块小块的读写数据会非常慢,因此,尽量大块的读写数据
·使用BufferedInputStream和BufferedOutputStream来批处理数据以提高性能
·对象的序列化(serialization)非常影响I/O的性能,尽量少用
·对不需序列化的类的域使用transient关键字,以减少序列化的数据量
2.4 循环(Loop)
因为循环中的代码会被反复的执行,所以循环中经常是寻找有关性能问题的地方,嵌套的循环更容易产生性能问题, 在循环中,我们应该注意如下问题:
·循环常量(Loop Constant),在循环中它的值不会改变,因此,它的值应该在循环外先计算出来。
·本地变量(Local Variable),从上面的分析可知,在方法中使用本地变量比使用对象的属性消耗较少的资源,在循环中却不一样, 因为循环中的代码要反复地被运行,因此,尽量少地在循环中创建对象和变量。
·尽早结束循环,如果循环体在满足一定条件就可以结束,就应尽快结束。
2.5 集合类(Collections)
集合类在此Java编程中被广泛地使用,大致上,一个集合类就是将一组对象组装成一个对象,Java的集合类框架由一些接口和一些为通用目的而实现(implementation)的类组成,集合类的基本结构由六个在java.util包内的接口组成,主要有如下结构:
Collection 这是集合类的基本接口,它为一组对象提供了一些简单的方法,
List 具有可以控制的顺序,但并没有定义或限制按什么排序。
Set 不能包含重复的元素,
Map 将一个键(Key)影射到一个值(Value),不允许有重复的键,
除了上述接口之外,java.util还提供了一些为通用目的而实现的类,如Vector,ArrayList,Hashtable等等,这些类里,有些提供了某种排序算法,有的提供了同步的方法,有如此多的集合类,在具体使用过程中,我们如何根据自己的需要选择合适的集合类,将对程序的性能产生很大的影响,下面将一些常用的类进行比较, Vector和ArrayList Vector和ArrayList在使用上非常相似,都可用来表示一组数量可变的对象应用的集合,并且可以随机地访问其中的元素。
它们的区别如下:
Vector的方法都是同步的(Synchronized),是线程安全的(thread-safe),而ArrayList的方法不是,由于线程的同步必然要影响性能,因此,ArrayList的性能比Vector好。
当Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而ArrayList只增加50%的大小,这样,ArrayList就有利于节约内存空间。
Hashtable和HashMap
它们的性能方面的比较类似 Vector和ArrayList,比如Hashtable的方法是同步的,而HashMap的不是。
ArrayList和LinkedList
对于处理一列数据项,Java提供了两个类ArrayList和LinkedList,ArrayList的内部实现是基于内部数组Object[],所以从概念上讲,它更象数组,但LinkedList的内部实现是基于一组连接的记录,所以,它更象一个链表结构,所以,它们在性能上有很大的差别。
(1)从上面的分析可知,在ArrayList的前面或中间插入数据时,你必须将其后的所有数据相应的后移,这样必然要花费较多时间,所以,当你的操作是在一列数据的后面添加数据而不是在前面或中间,并且需要随机地访问其中的元素时,使用ArrayList会提供比较好的性能。
(2)而访问链表中的某个元素时,就必须从链表的一端开始沿着连接方向一个一个元素地去查找,直到找到所需的元素为止,所以,当你的操作是在一列数据的前面或中间添加或删除数据,并且按照顺序访问其中的元素时,就应该使用LinkedList了。
(3)如果在编程中,1,2两种情形交替出现,这时,你可以考虑使用List这样的通用接口,而不用关心具体的实现,在具体的情形下,它的性能由具体的实现来保证。
设置集合类的初始大小
在Java 集合框架中的大部分类的大小是可以随着元素个数的增加而相应的增加的,我们似乎不用关心它的初始大小,但如果我们考虑类的性能问题时,就一定要考虑尽可能地设置好集合对象的初始大小,这将大大提高代码的性能,比如,Hashtable缺省的初始大小为101,载入因子为0.75,即如果其中的元素个数超过 75个,它就必须增加大小并重新组织元素,所以,如果你知道在创建一个新的Hashtable对象时就知道元素的确切数目如为110,那么,就应将其初始大小设为110/0.75=148,这样,就可以避免重新组织内存并增加大小。
2.6 方法(Methods)
从上面的JVM的结构分析可以看出,Java程序在执行的过程中就是一个初始化对象和调用其方法过程,其中对方法的调用花费了很多资源,这些资源都用来转移线程控制,传递参数,返回结果和创建用于存放本地变量及中间结果的帧栈(stack frame)。
代码嵌入(Inlining)
由于方法的调用需要消耗大量的资源,因此,Java编译器可以将一些方法调用转化为代码嵌入(Inlining),就是将一段代码对一个方法的调用转化为将该方法的代码在编译时嵌入到调用处,这样,由于减少了方法的调用,就可以大大提高代码的性能,当将一个方法声明为final,static, private时,编译器就会自动的使用代码嵌入技术将该方法代码在编译时嵌入到调用处。
同步(Synchronized)方法
在多线程访问共享数据时,为了保证数据的一致性,就必然要使用同步技术,但从上面的分析可知,使用同步方法比使用非同步方法的性能要低,因此,我们应尽量少使用同步方法?调用同步方法的代码本身就不需要再同步了。

JSP中Cookie的使用

set-cookie.jsp

&lt;%
{
  String u = request.getParameter("username");
  String p = request.getParameter("password");

  if(u.equals("u1") && p.equals("p1"))
  {
    Cookie info1 = new Cookie("up", u+'/'+p);
    info1.setPath("/");
    info1.setMaxAge(60*10); 
    response.addCookie(info1);

    response.sendRedirect("http://dlsun1710/dataSet_form1/protected.jsp");
  }
  else
    response.sendRedirect("http://dlsun1710/dataSet_form1/login.html");

}
%&gt;



protected.jsp
&lt;%@ page import="javax.servlet.http.*" %&gt;

&lt;%
{
  Cookie cookies[] = request.getCookies();
  
  if(cookies != null)
  {
    for (int i=0; i&lt;cookies.length; i++) 
    {
      if(cookies[ i].getName().equals("up"))
      {
        if(cookies[ i].getValue().equals("u1/p1"))
          response.sendRedirect("http://dlsun1710/dataSet_form1/success.html");
        else
          response.sendRedirect("http://dlsun1710/dataSet_form1/login.html");
      }
    }
  }
  response.sendRedirect("http://dlsun1710/dataSet_form1/login.html");
}

%&gt;


Java正则表达式详解

,

Java两则常见错误详析及解决

重点推荐:小议学习Java的浮躁心态 详述:JAVA也能克隆!

  1、空指针错误 Java.lang.NullPointerException

  使用基本的Java数据类型,变量的值要么已经是默认值,如果没有对其正常赋值,程序便不能通过编译,因此使用基本的Java数据类型(double,float,boolean,char,int,long)一般不会引起空指针异常。由此可见,空指针异常主要跟与对象的操作相关。

  下面先列出了可能发生空指针异常的几种情况及相应解决方案:

  不管对象是否为空就直接开始使用。

  (JSP)代码段1:


out.println(request.getParameter("username"));

  描述:

  代码段1的功能十分简单,就是输出用户输入的表域"username"的值。

  说明:

  看上去,上面的语句找不出什么语法错误,而且在大多数情况下也遇不到什么问题。但是,如果某个用户在输入数据时并没有提供表单域"username"的值,或通过某种途径绕过表单直接输入时,此时request.getParameter("username")的值为空(不是空字符串,是空对象null。),out对象的println方法是无法直接对空对象操作,因此代码段1所在的JSP页面将会抛出"Java.lang.NullPointerException"异常。

  即使对象可能为空时,也调用Java.lang.Object或Object对象本身的一些方法如toString(), equals(Object obj)等操作。

  (JSP)代码段2:
String userName = request.getParameter("username");

If (userName.equals("root"))

{....}

  描述:

  代码段2的功能是检测用户提供的用户名,如果是用户名称为"root"的用户时,就执行一些特别的操作。

  说明:

  在代码段2中,如果有用户没有提供表单域"username"的值时,字符串对象userName为null值,不能够将一个null的对象与另一个对象直接比较,同样,代码段2所在的JSP页面就会抛出(Java.lang.NullPointerException)空指针错误。

  (JSP)代码段3:
String userName = session.getAttribute
("session.username").toString();

  描述:

  代码段3的功能是将session中session.username的值取出,并将该值赋给字符串对象 userName。

  说明:

  在一般情况下,如果在用户已经进行某个会话,则不会出现什么问题;但是,如果此时应用服务器重新启动,而用户还没有重新登录,(也可能是用户关闭浏览器,但是仍打开原来的页面。)那么,此时该session的值就会失效,同时导致session中的session.username的值为空。对一个为null的对象的直接执行toString()操作,就会导致系统抛出(Java.lang.NullPointerException)空指针异常。

  解决方案:

  为了确保进行操作或引用的对象非空,假若我们要对某对象进行操作或引用,我们首先去检查该对象是否已经实例化且不为空;并且在系统中加入针对对象为空时情况的处理。

  如:采用String对象保存用户提交的结果;在如果涉及对象的操作时,先检测其是否为空后,检查到对象为空后,可再选择进行以下任一种处理方式:

  处理方式 1) 检查到对象为空时,设置对象值为空字符串或一个默认值;

  处理方式 2) 检测到对象为空时,根本不执行某操作,直接跳转到其他处理中。

  处理方式 3) 检查到对象为空时,提示用户操作有错误。

  将代码段2按以上方式进行改写,得到:

  方式1:
String userName = request.getParameter("username");

// 该变量值为空时,转化为默认空字符串

If (userName == null)

userName = "";

If (userName.equals("root"))

{..........}

  方式2:
String userName = request.getParameter("username");

// 该变量值为空时,转化为默认空字符串,不执行有关操作。

If (usreName != null)

{

If (userName.equals("root"))

{..........}

}

  方式3:
String userName = request.getParameter("username");

// 该变量值为空时,转化为默认空字符串,不执行有关操作。

If (usreName == null)

{

// 提示用户输入信息为空

}

  实际中,上面提供到三种处理方式也同样适用于其他异常的处理:

  异常处理方式 1) 检查到异常出现,设置对象值为空字符串或一个默认值;

  异常处理方式 2) 检测到异常出现,根本不执行某操作,直接跳转到其他处理中。

  异常处理方式 3) 检查到异常出现,提示用户操作有错误。

2,格式化数字错误 Java.lang.NumberFormatException分析

  (JSP)代码段3:
String s_memberid = request.getParameter("memberid");

int i_memberid = Integer.parseInt(s_memberid);

  描述:

  以上代码段的作用是将用户提交的表单域memberid的值转化为整数。

  说明:

  如果用户输入正确的数字如:1082,不会有什么问题。然而,如果用户输入T1082时,由于T1082不是合法的数字格式,Java无法将其转化为合适的数字,导致抛出Java.lang.NumberFormatException数字格式化异常。

  解决方案:

  在任何用到字符串转化为数字时,捕捉异常,对异常情况进行处理按异常处理方式1:检查到异常发生,即赋给某变量一个默认值;(可能在某些情况下导致一其他程序错误[比方说其他模块中并未处理您所赋予的默认值情况,可能导致一些异常或错误出现。])按异常处理方式3:检查到异常发生,提示用户使用正确的数字格式输入。(实现稍微麻烦一点,但是将错误阻挡在您的模块前[即您提供给其他模块的值均是安全的]。)按这种方法对程序进行改写,在编程时稍微麻烦一点,但这的确会您的模块更加健壮。将代码段3按以上要求进行改写,得到:
String s_memberid = request.getParameter("memberid");

int i_memberid;

try

{

i_memberid = Integer.parseInt(s_memberid);

...

}

catch(NumberFormatException nfe)

{

//方式1:(简单,直接给该编号为一个默认值0;)

i_memberid = 0;

//方式2:(很简陋的做法,建议使用更友好的提示方式)

out.println("<script>alert(’您提供的用户编号有误,请重新输入。’;history.go(-1);</script>");

}

J2EE面试题集锦(附答案)

/**
* By metaphy 2005-11-12
* Version: 0.01
* 注:题目答案来源于metaphy过去的知识或网络,metaphy不能保证其正确或完整性,仅供参考
**/

一、基础问答

  1.下面哪些类可以被继承?

   java.lang.Thread (T)
   java.lang.Number (T)
   java.lang.Double (F)
   java.lang.Math (F)
   java.lang.Void (F)
   java.lang.Class (F)
   java.lang.ClassLoader (T)

  2.抽象类和接口的区别

  (1)接口可以被多重implements,抽象类只能被单一extends
  (2)接口只有定义,抽象类可以有定义和实现
  (3)接口的字段定义默认为:public static final, 抽象类字段默认是"friendly"(本包可见)

  3.Hashtable的原理,并说出HashMap与Hashtable的区别

  HashTable的原理:通过节点的关键码确定节点的存储位置,即给定节点的关键码k,通过一定的函数关系H(散列函数),得到函数值H(k),将此值解释为该节点的存储地址.
HashMap 与Hashtable很相似,但HashMap 是非同步(unsynchronizded)和可以以null为关键码的.

  4.forward和redirect的区别

  forward: an internal transfer in servlet
  redirect: 重定向,有2次request,第2次request将丢失第一次的attributs/parameters等

  5.什么是Web容器?

  实现J2EE规范中web协议的应用.该协议定义了web程序的运行时环境,包括:并发性,安全性,生命周期管理等等.

  6.解释下面关于J2EE的名词

  (1)JNDI:Java Naming & Directory Interface,JAVA命名目录服务.主要提供的功能是:提供一个目录系统,让其它各地的应用程序在其上面留下自己的索引,从而满足快速查找和定位分布式应用程序的功能.
  (2)JMS:Java Message Service,JAVA消息服务.主要实现各个应用程序之间的通讯.包括点对点和广播.
  (3)JTA:Java Transaction API,JAVA事务服务.提供各种分布式事务服务.应用程序只需调用其提供的接口即可.
  (4)JAF: Java Action FrameWork,JAVA安全认证框架.提供一些安全控制方面的框架.让开发者通过各种部署和自定义实现自己的个性安全控制策略.
  (5)RMI:Remote Method Interface,远程方法调用

  7.EJB是基于哪些技术实现的?并说 出SessionBean和EntityBean的区别,StatefulBean和StatelessBean的区别.

  EJB包括Session Bean、Entity Bean、Message Driven Bean,基于JNDI、RMI、JAT等技术实现.

  SessionBean在J2EE应用程序中被用来完成一些服务器端的业务操作,例如访问数据库、调用其他EJB组件.EntityBean被用来代表应用系统中用到的数据.对于客户机,SessionBean是一种非持久性对象,它实现某些在服务器上运行的业务逻辑;EntityBean是一种持久性对象,它代表一个存储在持久性存储器中的实体的对象视图,或是一个由现有企业应用程序实现的实体.

  Session Bean 还可以再细分为 Stateful Session Bean 与 Stateless Session Bean .这两种的 Session Bean都可以将系统逻辑放在 method之中执行,不同的是 Stateful Session Bean 可以记录呼叫者的状态,因此通常来说,一个使用者会有一个相对应的 Stateful Session Bean 的实体.Stateless Session Bean 虽然也是逻辑组件,但是他却不负责记录使用者状态,也就是说当使用者呼叫 Stateless Session Bean 的时候,EJB Container 并不会找寻特定的 Stateless Session Bean 的实体来执行这个 method.换言之,很可能数个使用者在执行某个 Stateless Session Bean 的 methods 时,会是同一个 Bean 的 Instance 在执行.从内存方面来看, Stateful Session Bean 与 Stateless Session Bean 比较, Stateful Session Bean 会消耗 J2EE Server 较多的内存,然而 Stateful Session Bean 的优势却在于他可以维持使用者的状态.

  8.XML的解析方法

  Sax,DOM,JDOM

  9.什么是Web Service?

  Web Service就是为了使原来各孤立的站点之间的信息能够相互通信、共享而提出的一种接口。
Web Service所使用的是Internet上统一、开放的标准,如HTTP、XML、SOAP(简单对象访问协议)、WSDL等,所以Web Service可以在任何支持这些标准的环境(Windows,Linux)中使用。

  注:SOAP协议(Simple Object Access Protocal,简单对象访问协议),它是一个用于分散和分布式环境下网络信息交换的基于XML的通讯协议。在此协议下,软件组件或应用程序能够通过标准的HTTP协议进行通讯。它的设计目标就是简单性和扩展性,这有助于大量异构程序和平台之间的互操作性,从而使存在的应用程序能够被广泛的用户访问。

  优势:

  (1).跨平台。
  (2).SOAP协议是基于XML和HTTP这些业界的标准的,得到了所有的重要公司的支持。
  (3).由于使用了SOAP,数据是以ASCII文本的方式而非二进制传输,调试很方便;并且由于这样,它的数据容易通过防火墙,不需要防火墙为了程序而单独开一个“漏洞”。
  (4).此外,WebService实现的技术难度要比CORBA和DCOM小得多。
  (5).要实现B2B集成,EDI比较完善与比较复杂;而用WebService则可以低成本的实现,小公司也可以用上。
  (6).在C/S的程序中,WebService可以实现网页无整体刷新的与服务器打交道并取数。

  缺点:

  (1).WebService使用了XML对数据封装,会造成大量的数据要在网络中传输。
  (2).WebService规范没有规定任何与实现相关的细节,包括对象模型、编程语言,这一点,它不如CORBA。

  10.多线程有几种实现方法,都是什么?同步有几种实现方法,都是什么?

  答:多线程有两种实现方法,分别是继承Thread类与实现Runnable接口
  同步的实现方面有两种,分别是synchronized,wait与notify

  11.JSP中动态INCLUDE与静态INCLUDE的区别?

  动态INCLUDE用jsp:include动作实现

<jsp:include page="included.jsp" flush="true"/>

  它总是会检查所含文件中的变化,适合用于包含动态页面,并且可以带参数

  静态INCLUDE用include伪码实现,定不会检查所含文件的变化,适用于包含静态页面

  <%@ include file="included.htm" %>

二、Java编程与程序运行结果

  1.Java编程,打印昨天的当前时刻

public class YesterdayCurrent{
public void main(String[] args){
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DATE, -1);
System.out.println(cal.getTime());
}
}
  2.文件读写,实现一个计数器

public int getNum(){
int i = -1;
try{
String stri="";
BufferedReader in = new BufferedReader(new FileReader(f));
while((stri=in.readLine())!=null){
i = Integer.parseInt(stri.trim());
}
in.close();
}catch(Exception e){
e.printStackTrace();
}
return i;
}
public void setNum(){
int i = getNum();
i++;
try{
PrintWriter out=new PrintWriter(new BufferedWriter(new FileWriter(f,false)));
out.write(String.valueOf(i)); //可能是编码的原因,如果直接写入int的话,将出现java编码和windows编码的混乱,因此此处写入的是String
out.close() ;
}catch(Exception e){
e.printStackTrace();
}
}
  3. 指出下面程序的运行结果:

class A{
static{
System.out.print("1");
}
public A(){
System.out.print("2");
}
}
class B extends A{
static{
System.out.print("a");
}
public B(){
System.out.print("b");
}
}
public class Hello{
public static void main(String[] ars){
A ab = new B(); //执行到此处,结果: 1a2b
ab = new B(); //执行到此处,结果: 1a2bab
}
}
  注:类的static 代码段,可以看作是类首次加载(被虚拟机加载)执行的代码,而对于类的加载,首先要执行其基类的构造,再执行其本身的构造

  4.写一个Singleton模式的例子

public class Singleton{
private static Singleton single = new Singleton();
private Singleton(){}
public Singleton getInstance(){
return single;
}
}
三、数据库

  1.删除表的重复记录

  如果记录完全相同才算重复记录,那么: (sql server2000下测试通过)

select distinct * into #tmpp from tid
delete from tid
insert into tid select * from #tmpp
drop table #tmpp
  如果有id主键(数字,自增1的那种),那么:(sql server2000下测试通过)

delete from tableA where id not in
(select id = min(id) from tableA group by name)
  2.delete from tablea & truncate table tablea的区别

  truncate 语句执行速度快,占资源少,并且只记录页删除的日志;
  delete 对每条记录的删除均需要记录日志

初学者 学好JAVA最关键几点

转贴自www.csdn.net

第一个是path 和 CLASSPATH的问题
第二个是package和import问题

第三个是public,protected,private,static,什 么时候用,为什么要用,怎么用

第四个是制作jar文件



第一个,path 和 CLASSPATH的问题

什么是path

是你的JDK的工作路径

例如 你的jdk安装在C:/jdk/ 此时的你的path(如果原来没有PATH)设置为 PATH ;C:/jdk/ bin

什么是Classpath

Classpath是Java中的重要概念,它描述了Java虚拟机在运行一个Class时在哪些路径中加载要运行的类以及运行的类要用到的类。简单的说,当一个程序找不到他所需的其他类文件时,系统会自动到CLASSPATH环境变量所指明的路径中去查找第三方提供的类和用户定义的类,

Classpath和Java包的关系

Java的包(Package)和classpath关系密切。包是以“.”分割的,SUN建议使用域名的逆向排列来区分不同的包,以避免冲突,如com.company.util。在一个包里的类在存储的时候需要存储在和包名相同的目录里,如上述com.company.util包中的 Sample.class,要存储在com\company\util目录中。
Classpath有两种表达方式,一种是指向目录的classpath,如C:\work\classes,表示C:\work\ classes目录是一个classpath条目;另一种方式是指向压缩文件的classpath,如C:\work\util.jar,表示C:\ work\util.jar文件是一个classpath条目,任何一个包含Java类的zip格式的压缩文件都可以作为classpath的条目。
那么classpath和包到底是什么关系呢?简单的说,就是Java虚拟机在加载类的时候以这样一种方式查找具体的类文件: classpath+包存储的目录+具体的类文件。如classpath中有一个c:\work\classes条目,需要加载的类是 com.company.util.Sample.class,那么在加载这个类的时候,虚拟机会查找c:\work\classes\com\ company\util目录,如果Sample.class在这个目录中,虚拟机就可以找到,如果这个类不在这个目录中,同时也不在任何一个其它 classpath中,那么虚拟机会抛出一个ClassNotFoundException。

Classpath的顺序和类版本冲突

Java虚拟机在加载类的时候查找classpath是有顺序的,如果在classpath中有多个条目都有同一个名称的类,那么在较前位置的类会被加载,后面的会被忽略。这种按照顺序的类加载可能会导致类的版本冲突。例如classpath=c:\servlet2.2\ servlet.jar;c:\servlet2.3\servlet.jar,那么在实际应用的过程中,你使用的是servlet2.2,而不是 servlet2.3。很多时候如果不注意这一点,可能会导致奇怪的异常。

命令行状态下的classpath设置

命令行状态下的classpath可以通过两种方式设置。
一种是直接设置环境变量,例如在windows环境下,我们使用set命令:
set classpath=c:\work\classes;c:\work\util.jar
另一种方式是在执行javac、java或者其它Java命令时直接指定classpath:
java -classpath c:\work\classes;c:\work\util.jar com.company.util.Sample

二 package和import问题

Java中的包(Package)其实指的就是目录,它是为了更好地管理Java类(Class)和接口(Interface)。Java语言的包可以被另一个Java开发包所使用。如果我们要引用某个包中的类,用import关键字来标明即可,Package行要在 import 行之前 , Package打包后将生成在Classpath 指明的路径下
例如 Classpath 为 C:/test
Package com.dir
结果为 将在C:/test目录下生成com子目录,目录com包含dir目录 所有的文件将放在dir目录

在Java中对象的体现在包的定义和应用中,对于已经编好的类,如果在新类中要使用,应将已经定义的类包含近来,Java中用import语句实现.
使用import 语句的加入所有类(以上面Package打包为例)
import com.dir.*;

使用import 语句的加入特指类(以上面Package打包为例,假如目录中存在hello.class)

import com.dir.hello;

第三,public,protected,private,static,什 么时候用,为什么要用,怎么用

每个类都创造了有自己的名字空间,指方法和变量可以知道彼此的存在,可以使用。
public类不但可以被同一程序包中的其它类使用,别的程序包中的类也可以使用;
变量和方法的修饰字public、protected、private:
public:任何其他类、对象只要可以看到这个类的话,那么它就可以存取变量的数据,或使用方法。
class ABC{
public int pub_i=5;
public void show)(){
System.out.println("pub_i"+pub_i);
}
class Demo{
public static void main(String args[]){
ABC abc=new ABC();
System.out.println("abc.pub_i"+abc.pub_i);
abc.pub_i=10;
abc.show();
}
}
protected变量和方法:
如果一个类中变量或方法有修饰字protected,同一类、同一包可以使用。不同包的类要使用,必须是该类的子类,可以存取变量或调用。
public class ABC{
protected int pro_i=5;
protected void show(){
System.out.println("pro_i=" +pro_i);}
}
同包的类:
class DEF{
public static void main(String args[]){
ABC abc=new ABC();
System.out.println("abc.pro_i="+abc.pro_i);
abc.pub_i=10;
abc.show();
}
}
不同包但是是子类:
import mytest.pack.ABC;
class DEF extends ABC{
public static void main(String agrs[]){
DEF def=new DEF();
System.out.println(def.i);
def.i=10;
def.show();}
}
private不允许任何其他类存取和调用;
当子类中的变量名与父类的相同,原来的变量被遮盖。
方法的覆盖(overriding)和重载(overloading)。子孙类中定义的方法和祖先类中某个方法同名、同参数行,则祖先类中的该方法被覆盖;方法的重载是指一个对象的多态性,即多个方法用相同的名称,但参数行不同。
final:
final在方法之前,防止该方法被覆盖;
final在类之前,标是该类不能被继承;
final在变量之前,定义一个常量。
static:
在变量或方法之前,表明它们是属于类的;
静态变量在各实例间共享,如果是public静态变量,则其它类可以不通过实例化访问它们;
静态方法称为类的方法,因此不用实例化即可调用(面向过程)
一个对象的方法可以访问对象的数据成员,尽管不属于方法的局部变量;一个类的方法只能访问自己的局部变量。
例:不正确的引用
class StaticError{
String mystring="hello";
public static void main(String args[]){
System.out.println(mystring);}
}
错误信息:can’t make a static reference to nonstatic variable.
为什么不正确?只有对象的方法可以访问对象的变量。
解决的办法:
1) 将变量改称类变量
class StaticError{
static String mystring="hello";
public static void main(String args[]){
System.out.println(mystring);}
}
2) 先创建一个类的实例
class NoStaticError{
public static void main(String args[]){
String mystring="hello";
System.out.println(mystring);}
}
}

第四个,制作jar文件

JAR 文件就是 Java Archive File,顾名思意,它的应用是与 Java 息息相关的,是 Java 的一种文档格式。 JAR 文件非常类似 ZIP 文件??准确的说,它就是 ZIP 文件,所以叫它文件包。JAR 文件与 ZIP 文件唯一的区别就是在 JAR 文件的内容中,包含了一个 META-INF/MANIFEST.MF 文件,这个文件是在生成 JAR 文件的时候自动创建的

jar 命令详解

jar 是随 JDK 安装的,在 JDK 安装目录下的 bin 目录中,Windows 下文件名为 jar.exe,Linux 下文件名为 jar。它的运行需要用到 JDK 安装目录下 lib 目录中的 tools.jar 文件。不过我们除了安装 JDK 什么也不需要做,因为 SUN 已经帮我们做好了。我们甚至不需要将 tools.jar 放到 CLASSPATH 中。

使用不带任何的 jar 命令我们可以看到 jar 命令的用法如下:

jar {ctxu}[vfm0M] [jar-文件] [manifest-文件] [-C 目录] 文件名 ...

其中 {ctxu} 是 jar 命令的子命令,每次 jar 命令只能包含 ctxu 中的一个,它们分别表示:

-c 创建新的 JAR 文件包

-t 列出 JAR 文件包的内容列表

-x 展开 JAR 文件包的指定文件或者所有文件

-u 更新已存在的 JAR 文件包 (添加文件到 JAR 文件包中)

[vfm0M] 中的选项可以任选,也可以不选,它们是 jar 命令的选项参数

-v 生成详细报告并打印到标准输出

-f 指定 JAR 文件名,通常这个参数是必须的

-m 指定需要包含的 MANIFEST 清单文件

-0 只存储,不压缩,这样产生的 JAR 文件包会比不用该参数产生的体积大,但速度更快

-M 不产生所有项的清单(MANIFEST〕文件,此参数会忽略 -m 参数

[jar-文件] 即需要生成、查看、更新或者解开的 JAR 文件包,它是 -f 参数的附属参数

[manifest-文件] 即 MANIFEST 清单文件,它是 -m 参数的附属参数

[-C 目录] 表示转到指定目录下去执行这个 jar 命令的操作。它相当于先使用 cd 命令转该目录下再执行不带 -C 参数的 jar 命令,它只能在创建和更新 JAR 文件包的时候可用。  

文件名 ... 指定一个文件/目录列表,这些文件/目录就是要添加到 JAR 文件包中的文件/目录。如果指定了目录,那么 jar 命令打包的时候会自动把该目录中的所有文件和子目录打入包中。

下面举一些例子来说明 jar 命令的用法:

1) jar cf test.jar test

该命令没有执行过程的显示,执行结果是在当前目录生成了 test.jar 文件。如果当前目录已经存在 test.jar,那么该文件将被覆盖。

2) jar cvf test.jar test

该命令与上例中的结果相同,但是由于 v 参数的作用,显示出了打包过程,如下:

标明清单(manifest)

增加:test/(读入= 0) (写出= 0)(存储了 0%)

增加:test/Test.class(读入= 7) (写出= 6)(压缩了 14%)

3) jar cvfM test.jar test

该命令与 2) 结果类似,但在生成的 test.jar 中没有包含 META-INF/MANIFEST 文件,打包过程的信息也略有差别:

增加:test/(读入= 0) (写出= 0)(存储了 0%)

增加:test/Test.class(读入= 7) (写出= 6)(压缩了 14%)

4) jar cvfm test.jar manifest.mf test

运行结果与 2) 相似,显示信息也相同,只是生成 JAR 包中的 META-INF/MANIFEST 内容不同,是包含了 manifest.mf 的内容

5) jar tf test.jar

在 test.jar 已经存在的情况下,可以查看 test.jar 中的内容,如对于 2) 和 3) 生成的 test.jar 分别应该此命令,结果如下;

对于 2)

META-INF/

META-INF/MANIFEST.MF

test/

test/Test.class

对于 3)

test/

test/Test.class

6) jar tvf test.jar

除显示 5) 中显示的内容外,还包括包内文件的详细信息,如:

0 Wed Jun 19 15:39:06 GMT 2002 META-INF/

86 Wed Jun 19 15:39:06 GMT 2002 META-INF/MANIFEST.MF

0 Wed Jun 19 15:33:04 GMT 2002 test/

7 Wed Jun 19 15:33:04 GMT 2002 test/Test.class

7) jar xf test.jar

解开 test.jar 到当前目录,不显示任何信息,对于 2) 生成的 test.jar,解开后的目录结构如下:

  ==

  |-- META-INF

  |  `-- MANIFEST

  `-- test

    `--Test.class

8) jar xvf test.jar

运行结果与 7) 相同,对于解压过程有详细信息显示,如:

创建:META-INF/

展开:META-INF/MANIFEST.MF

创建:test/

展开:test/Test.class

9) jar uf test.jar manifest.mf

在 test.jar 中添加了文件 manifest.mf,此使用 jar tf 来查看 test.jar 可以发现 test.jar 中比原来多了一个 manifest。这里顺便提一下,如果使用 -m 参数并指定 manifest.mf 文件,那么 manifest.mf 是作为清单文件 MANIFEST 来使用的,它的内容会被添加到 MANIFEST 中;但是,如果作为一般文件添加到 JAR 文件包中,它跟一般文件无异。

10) jar uvf test.jar manifest.mf

与 9) 结果相同,同时有详细信息显示,如:

增加:manifest.mf(读入= 17) (写出= 19)(压缩了 -11%)

关于 JAR 文件包的一些技巧

1) 使用 unzip 来解压 JAR 文件

在介绍 JAR 文件的时候就已经说过了,JAR 文件实际上就是 ZIP 文件,所以可以使用常见的一些解压 ZIP 文件的工具来解压 JAR 文件,如 Windows 下的 WinZip、WinRAR 等和 Linux 下的 unzip 等。使用 WinZip 和 WinRAR 等来解压是因为它们解压比较直观,方便。而使用 unzip,则是因为它解压时可以使用 -d 参数指定目标目录。

在解压一个 JAR 文件的时候是不能使用 jar 的 -C 参数来指定解压的目标的,因为 -C 参数只在创建或者更新包的时候可用。那么需要将文件解压到某个指定目录下的时候就需要先将这具 JAR 文件拷贝到目标目录下,再进行解压,比较麻烦。如果使用 unzip,就不需要这么麻烦了,只需要指定一个 -d 参数即可。如:

unzip test.jar -d dest/

2) 使用 WinZip 或者 WinRAR 等工具创建 JAR 文件

上面提到 JAR 文件就是包含了 META-INF/MANIFEST 的 ZIP 文件,所以,只需要使用 WinZip、WinRAR 等工具创建所需要 ZIP 压缩包,再往这个 ZIP 压缩包中添加一个包含 MANIFEST 文件的 META-INF 目录即可。对于使用 jar 命令的 -m 参数指定清单文件的情况,只需要将这个 MANIFEST 按需要修改即可。

3) 使用 jar 命令创建 ZIP 文件

有些 Linux 下提供了 unzip 命令,但没有 zip 命令,所以需要可以对 ZIP 文件进行解压,即不能创建 ZIP 文件。如要创建一个 ZIP 文件,使用带 -M 参数的 jar 命令即可,因为 -M 参数表示制作 JAR 包的时候不添加 MANIFEST 清单,那么只需要在指定目标 JAR 文件的地方将 .jar 扩展名改为 .zip 扩展名,创建的就是一个不折不扣的 ZIP 文件了,如将上一节的第 3) 个例子略作改动:

jar cvfM test.zip test