资源描述
BEA dev2dev WebLogic Web Application Development精华
sdj21 daijun@
2005-3-15
1 Java基础 2
1.1 axman考验你的Java基础 2
1.2 如何获得客户端的MAC地址? 6
1.3 axman对equal的深入研究 7
1.4 为什么要始终使用PreparedStatement代替Statement? 12
1.5 一个利己线程调度实例? 14
1.6 GBK与GB2312字符集的问题 15
1.7 怎样制作ear,war文件? 15
1.8 乱码问题 16
2 Servlet&JSP 19
2.1 加载和启动servlet后,为一个servlet创建了一个还是多个实例? 19
2.2 WebLogic里面的一个class修改了,需要重新启动WebLogic吗? 19
2.3 Tomcat关于UTF-8JSP文件的BUG 20
2.4 使用FileUpload组件上传文件? 22
2.5 session中存放数据库connection的问题 24
2.6 如果防止JSP页面刷新 25
2.7 JSP在I/O方面的一些问题 25
2.8 文件下载的时候为什么finally里面的代码没有执行? 26
2.9 请问web server怎么将配置文件参数在web application启动的时候读进内存 27
2.10 如何设置并且访问环境变量? 28
2.11 中文URL该怎么写 29
3 前台表现层 31
3.1 用JFreeReport生成PDF报表 31
3.2 POI介绍 38
3.3 使用POI创建Excel格式数据 41
4 应用服务器特殊问题 43
4.1 Tomcat中的Web应用迁移到Weblogic 8.1 43
4.2 weblogic7.0中怎样实现定时执行程序? 43
4.3 养成使用在web.xml里配置引用资源的习惯 44
4.4 关于web-inf下的JSP的引用问题 45
4.5 WebLogic7关于JSP编译的一个BUG 45
4.6 如何让一个扩展名的文件在下载时提示下载而不是打开 46
4.7 在weblogic6.1下调试时如何保留系统编译jsp文件所生成的.java文件? 46
5 其他资料 47
1 Java基础
1.1 axman考验你的Java基础
1.简述逻辑操作(&,|,^)与条件操作(&&,||)的区别。(15分)
2.下面程序运行会发生什么结果?如果有错误,如何改正? (15分)
interface A{
int x = 0;
}
class B{
int x =1;
}
class C
extends B implements A {
public void pX(){
System.out.println(x);
}
public static void main(String[] args) {
new C().pX();
}
}
}
3.简述 Java Server Page 和 Servlet 的联系和区别。(20分)
4.XML文档定义有几种形式?它们之间有何本质区别?
解析XML文档有哪几种方式?(20分)
5.简述synchronized和java.util.concurrent.locks.Lock的异同 ?(15分)
6.EJB规范规定EJB中禁止的操作有哪些?(15分)
7.最后还有一题考考你的眼力:
public String toString(){
return this + "@" + this.hashCode();
}
这个toString()方法实现有无不当的地方?(toString方法其实可以按你想输出的任何内容输出一些该类的信息)
答案:
1.简述逻辑操作(&,|,^)与条件操作(&&,||)的区别。(15分)
区别主要答两点:
a.条件操作只能操作布尔型的,而逻辑操作不仅可以操作布尔型,而且可以操作数值型
b.逻辑操作不会产生短路.如:
int a = 0;
int b = 0;
if( (a = 3) > 0 || (b = 3) > 0 ) //操后a =3,b=0.
if( (a = 3) > 0 | (b = 3) > 0 ) //操后a =3,b=3.
答对第一点得5分,答对第二点得10分.
本题考察最最基本的知识,但仍然有很多大牛级开发人员下马,任何语言在开始的部分
都会详细介绍这些基本知识,但除了学习第一种语言时,没有人在学习新的语言时愿意
花五分钟来复习一下.
2.下面程序运行会发生什么结果?如果有错误,如何改正? (15分)
interface A{
int x = 0;
}
class B{
int x =1;
}
class C
extends B implements A {
public void pX(){
System.out.println(x);
}
public static void main(String[] args) {
new C().pX();
}
}
}
本题在编译时会发生错误(错误描述不同的JVM有不同的信息,意思就是未明确的x调用,
两个x都匹配,就象在同时import java.util和java.sql两个包时直接声明Date一样)
本题主要考察对接口和类的最最基本的结构的了解.对于父类的变量,可以用super.x来
明确,而接口的属性默认隐含为 public static final.所以可以通过A.x来明确.
3.简述 Java Server Page 和 Servlet 的联系和区别。(20分)
本题不用多说,在答相同点时应该明确知道jsp编译后是"类servlet"而"不是Servlet",
答区别时应该回答出"侧重于(视图/控制逻辑)".其它可根据情况加减分值.知识很简单,
但从面试的角度看,被试者不仅要能知道它们的区别,而且要能比较准确地表达出来(以
后写文档要能让别人看得懂,不产生歧义),回答"jsp编译后就是servlet"视为错误,回答
"jsp用于视图,servlet用于控制逻辑"视为错误,应该用侧重于,主要(多数)用于等词语
表达.
4.XML文档定义有几种形式?它们之间有何本质区别?
解析XML文档有哪几种方式?(20分)
本题三个答题点:
a: 两种形式 dtd,schema
b: 本质区别:schema本身是xml的,可以被XML解析器解析(这也是从DTD上发展schema的
根本目的)
c: 两种主要方式:dom,sax.答出两种得全分,如能答出saxt,或其它(在答出dom,sax的基
础上,如果应试者认为其它方式也可以视为对xml的解析应该允许.但没有答出dom,sax把
其它方式说成是对XML的解析不得分)应该加分.
5.简述synchronized和java.util.concurrent.locks.Lock的异同 ?(15分)
主要相同点:
Lock能完成synchronized所实现的所有功能.(其它不重要)
主要不同点:
Lock有比synchronized更精确的线程语义和更好的性能(在相同点中回答此点也行)
synchronized会自动释放锁.而Lock一定要求程序员手工释放.并且必须在finally从句
中释放,如果没有答出在finally中释放不得分.就如Connection没有在finally中关闭一
样.连最基本的资源释放都做不好,还谈什么多线程编程.
6.EJB规范规定EJB中禁止的操作有哪些?(15分)
共有8点,答出下列3-4点得满分.
l 不能操作线程和线程API(线程API指非线程对象的方法如notify,wait等)
l 不能操作awt
l 不能实现服务器功能
l 不能对静态属生存取.
l 不能使用IO操作直接存取文件系统
l 不能加载本地库.
l 不能将this作为变量和返回.
l 不能循环调用.
7
public String toString(){
return this + "@" + this.hashCode();
}
会变成
public String toString(){
return String.valueOf(this) + "@" + String.valueOf(this.hashCode());
}
而valueOf方法又将去调用toString();
/**
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
*/
这样好像会有问题
原文地址:
1.2 如何获得客户端的MAC地址?
Java本身没有这个能力,不过利用操作系统的命令可以做到。
public static String getMACAddress() {
String address = "";
String os = System.getProperty("os.name");
if ( os != null && os.startsWith("Windows")) {
try {
String command = "cmd.exe /c ipconfig /all";
Process p = Runtime.getRuntime().exec(command);
BufferedReader br =
new BufferedReader(
new InputStreamReader(p.getInputStream()));
String line;
while ((line = br.readLine()) != null) {
if (line.indexOf("Physical Address") > 0) {
int index = line.indexOf(":");
index += 2;
address = line.substring(index);
break;
}
}
br.close();
return address.trim();
}
catch (IOException e) { }
}
return address;
}
原文地址:
1.3 axman对equal的深入研究
equals方法的重要性毋须多言,只要你想比较的两个对象不愿是同一对象,你就应该实现
equals方法,让对象用你认为相等的条件来进行比较.
下面的内容只是API的规范,没有什么太高深的意义,但我之所以最先把它列在这儿,是因为
这些规范在事实中并不是真正能保证得到实现.
1.对于任何引用类型, o.equals(o) == true成立.
2.如果 o.equals(o1) == true 成立,那么o1.equals(o)==true也一定要成立.
3.如果 o.equals(o1) == true 成立且 o.equals(o2) == true 成立,那么
o1.equals(o2) == true 也成立.
4.如果第一次调用o.equals(o1) == true成立再o和o1没有改变的情况下以后的任何次调用
都成立.
5.o.equals(null) == true 任何时间都不成立.
以上几条规则并不是最完整的表述,详细的请参见API文档.
对于Object类,它提供了一个最最严密的实现,那就是只有是同一对象是,equals方法才返回true,也就是人们常说的引用比较而不是值比较.这个实现严密得已经没有什么实际的意义,所以在具体子类(相对于Object来说)中,如果我们要进行对象的值比较,就必须实现自己的equals方法.
先来看一下以下这段程序:
public boolean equals(Object obj)
{
if (obj == null) return false;
if (!(obj instanceof FieldPosition))
return false;
FieldPosition other = (FieldPosition) obj;
if (attribute == null) {
if (other.attribute != null) {
return false;
}
}
else if (!attribute.equals(other.attribute)) {
return false;
}
return (beginIndex == other.beginIndex
&& endIndex == other.endIndex
&& field == other.field);
}
这是JDK中java.text.FieldPosition的标准实现,似乎没有什么可说的.
我信相大多数或绝大多数程序员认为,这是正确的合法的equals实现.毕竟它是JDK的API实现啊.
还是让我们以事实来说话吧:
package debug;
import java.text.*;
public class Test {
public static void main(String[] args) {
FieldPosition fp = new FieldPosition(10);
FieldPosition fp1 = new MyTest(10);
System.out.println(fp.equals(fp1));
System.out.println(fp1.equals(fp));
}
}
class MyTest extends FieldPosition{
int x = 10;
public MyTest(int x){
super(x);
this.x = x;
}
public boolean equals(Object o){
if(o==null) return false;
if(!(o instanceof MyTest )) return false;
return ((MyTest)o).x == this.x;
}
}
运行一下看看会打印出什么:
System.out.println(fp.equals(fp1));打印true
System.out.println(fp1.equals(fp));打印flase
两个对象,出现了不对称的equals算法.问题出在哪里(脑筋急转弯:当然出在JDK实现的BUG)?
我相信有太多的程序员(除了那些根本不知道实现equals方法的程序员外)在实现equals方法
时都用过instanceof运行符来进行短路优化的,实事求是地说很长一段时间我也这么用过。
太多的教程,文档都给了我们这样的误导。而有些稍有了解的程序员可能知道这样的优化可能
有些不对但找不出问题的关键。另外一种极端是知道这个技术缺陷的骨灰级专家就提议不要这
样应用。
我们知道,"通常"要对两个对象进行比较,那么它们"应该"是同一类型。所以首先利用instanceof
运行符进行短路优化,如果被比较的对象不和当前对象是同一类型则不用比较返回false,但事实
上,"子类是父类的一个实例",所以如果 子类 o instanceof 父类,始终返回true,这时肯定
不会发生短路优化,下面的比较有可能出现多种情况,一种是不能造型父类而抛出异常,另一种
是父类的private 成员没有被子类继承而不能进行比较,还有就是形成上面这种不对称比较。可能
会出现太多的情况。
那么,是不是就不能用 instanceof运行符来进行优化?答案是否定的,JDK中仍然有很多实现是正
确的,如果一个class是final的,明知它不可能有子类,为什么不用 instanceof来优化呢?
为了维护SUN的开发小组的声誉,我不说明哪个类中,但有一个小组成员在用这个方法优化时在后加
加上了加上了这样的注释:
if (this == obj) // quick check
return true;
if (!(obj instanceof XXXXClass)) // (1) same object?
return false;
可能是有些疑问,但不知道如何做(不知道为什么没有打电话给我......)
那么对于非final类,如何进行类型的quick check呢?
if(obj.getClass() != XXXClass.class) return false;
用被比较对象的class对象和当前对象的class比较,看起来是没有问题,但是,如果这个类的子类
没有重新实现equals方法,那么子类在比较的时候,obj.getClass() 肯定不等于XXXCalss.class,
也就是子类的equals将无效,所以if(obj.getClass() != this.getClass()) return false;才是正
确的比较。
是否equals方法一定比较的两个对象就一定是要同一类型?上面我用了"通常",这也是绝大多数程序
员的愿望,但是有些特殊的情况,我们可以进行不同类型的比较,这并不违反规范。但这种特殊情况
是非常罕见的,一个不恰当的例子是,Integer类的equals可以和Sort做比较,比较它们的value是不
是同一数学值。(事实上JDK的API中并没有这样做,所以我才说是不恰当的例子)
在完成quick check以后,我们就要真正实现你认为的“相等”。对于如果实现对象相等,没有太高
的要求,比如你自己实现的“人”类,你可以认为只要name相同即认为它们是相等的,其它的sex,
ago都可以不考虑。这是不完全实现,但是如果是完全实现,即要求所有的属性都是相同的,那么如
何实现equals方法?
class Human{
private String name;
private int ago;
private String sex;
....................
public boolean equals(Object obj){
quick check.......
Human other = (Human)ojb;
return this.name.equals(other.name)
&& this.ago == ohter.ago
&& this.sex.equals(other.sex);
}
}
这是一个完全实现,但是,有时equals实现是在父类中实现,而要求被子类继承后equals能正确的工
作,这时你并不事实知道子类到底扩展了哪些属性,所以用上面的方法无法使equals得到完全实现。
一个好的方法是利用反射来对equals进行完全实现:
public boolean equals(Object obj){
quick check.......
Class c = this.getClass();
Filed[] fds = c.getDeclaredFields();
for(Filed f:fds){
if(!f.get(this).equals(f.get(obj)))
return false;
}
return true;
}
为了说明的方便,上明的实现省略了异常,这样的实现放在父类中,可以保证你的子类的equals可以按
你的愿望正确地工作。
关于equals方法的最后一点是:如果你要是自己重写(正确说应该是履盖)了equals方法,那同时就一
定要重写hashCode().为是规范,否则.............
我们还是看一下这个例子:
public final class PhoneNumber {
private final int areaCode;
private final int exchange;
private final int extension;
public PhoneNumber(int areaCode, int exchange, int extension) {
rangeCheck(areaCode, 999, "area code");
rangeCheck(exchange, 99999999, "exchange");
rangeCheck(extension, 9999, "extension");
this.areaCode = areaCode;
this.exchange = exchange;
this.extension = extension;
}
private static void rangeCheck(int arg, int max, String name) {
if(arg < 0 || arg > max)
throw new IllegalArgumentException(name + ": " + arg);
}
public boolean equals(Object o) {
if(o == this)
return true;
if(!(o instanceof PhoneNumber))
return false;
PhoneNumber pn = (PhoneNumber)o;
return pn.extension == extension && pn.exchange == exchange && pn.areaCode == areaCode;
}
}
注意这个类是final的,所以这个equals实现没有什么问题。
我们来测试一下:
public static void main(String[] args) {
Map hm = new HashMap();
PhoneNumber pn = new PhoneNumber(123, 38942, 230);
hm.put(pn, "I love you");
PhoneNumber pn1 = new PhoneNumber(123, 38942, 230);
System.out.println(pn);
System.out.println("pn.equals(pn1) is " + pn.equals(pn1));
System.out.println(hm.get(pn1));
System.out.println(hm.get(pn));
}
既然pn.equals(pn1),那么我put(pn,"I love you");后,get(pn1)这什么是null呢?
答案是因为它们的hashCode不一样,而hashMap就是以hashCode为主键的。
所以规范要求,如果两个对象进行equals比较时如果返回true,那么它们的hashcode要求返回相等的值。
原文地址:
1.4 为什么要始终使用PreparedStatement代替Statement?
在JDBC应用中,如果你已经是稍有水平开发者,你就应该始终以PreparedStatement代替Statement.也就是说,在任何时候都不要使用Statement.
基于以下的原因:
一.代码的可读性和可维护性.
虽然用PreparedStatement来代替Statement会使代码多出几行,但这样的代码无论从可读性还是可维护性上来说.都比直接用Statement的代码高很多档次:
stmt.executeUpdate("insert into tb_name (col1,col2,col2,col4) values ('"+var1+"','"+var2+"',"+var3+",'"+var4+"')");
perstmt = con.prepareStatement("insert into tb_name (col1,col2,col2,col4) values (?,?,?,?)");
perstmt.setString(1,var1);
perstmt.setString(2,var2);
perstmt.setString(3,var3);
perstmt.setString(4,var4);
perstmt.executeUpdate();
不用我多说,对于第一种方法.别说其他人去读你的代码,就是你自己过一段时间再去读,都会觉得伤心.
二.PreparedStatement尽最大可能提高性能.
每一种数据库都会尽最大努力对预编译语句提供最大的性能优化.因为预编译语句有可能被重复调用.所以语句在被DB的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中(相当于一个涵数)就会得到执行.这并不是说只有一个 Connection中多次执行的预编译语句被缓存,而是对于整个DB中,只要预编译的语句语法和缓存中匹配.那么在任何时候就可以不需要再次编译而可以直接执行.而statement的语句中,即使是相同一操作,而由于每次操作的数据不同所以使整个语句相匹配的机会极小,几乎不太可能匹配.比如:
insert into tb_name (col1,col2) values ('11','22');
insert into tb_name (col1,col2) values ('11','23');
即使是相同操作但因为数据内容不一样,所以整个个语句本身不能匹配,没有缓存语句的意义.事实是没有数据库会对普通语句编译后的执行代码缓存.这样每执行一次都要对传入的语句编译一次.
当然并不是所以预编译语句都一定会被缓存,数据库本身会用一种策略,比如使用频度等因素来决定什么时候不再缓存已有的预编译结果.以保存有更多的空间存储新的预编译语句.
三.最重要的一点是极大地提高了安全性.
即使到目前为止,仍有一些人连基本的恶义SQL语法都不知道.
String sql = "select * from tb_name where name= '"+varname+"' and passwd='"+varpasswd+"'";
如果我们把[' or '1' = '1]作为varpasswd传入进来.用户名随意,看看会成为什么?
select * from tb_name = '随意' and passwd = '' or '1' = '1';
因为'1'='1'肯定成立,所以可以任何通过验证.更有甚者,把[';drop table tb_name;]作为varpasswd传入进来,则select * from tb_name = '随意' and passwd = '';drop table tb_name;有些数据库是不会让你成功的,但也有很多数据库就可以使这些语句得到执行.
而如果你使用预编译语句.你传入的任何内容就不会和原来的语句发生任何匹配的关系.(前提是数据库本身支持预编译,但上前可能没有什么服务端数据库不支持编译了,只有少数的桌面数据库,就是直接文件访问的那些)只要全使用预编译语句,你就用不着对传入的数据做任何过虑.而如果使用普通的statement, 有可能要对drop,等做费尽心机的判断和过虑.
上面的几个原因,还不足让你在任何时候都使用PreparedStatement吗?
原文地址:
1.5 一个利己线程调度实例?
所谓利己线程,就是在运行时不考虑其它线程的运行权限和时间分配,一心只顾自己运行完成了事的这种线程,比如:while(true){............}
但是,如果{..........}的代码中要调用一些资源,就一定要sleep,让资源有空闲的时间来让本线程获取,也许这些资源不是当前线程一方要求调用,但无论如何要空闲一定的时间,即使资源空闲下来时本线程只能调用很少一部份,不然真的就死锁了.
在图形编程中,加载一个图形是异步的,然后组件会调用观察类ImageObserver来不断让图形加载和显示进行更新直到完成.因为图形组件的顶级对象 Component 已经实现ImageObserver,所以一般来说如果要加载一个Image,只要在drawImage方法中调用this(自己)作为观察者就行.
但是,如果我们要把一个源图象缩放后输出,或把多个源图象合并成一个图象输出(图形计数器就是把很多种0-9的数字图片合并成一个图片)输出,那就是在内存中先生生成一个BufferedImage对象,然后把源图象draw到BufferedImage的一定位置,再把BufferedImage编码输出到一个输出流如文件或网页.因为drawImage是BufferedImage的Graphics对象调用的,一切都在内存中而根本没有可视组件,所以根本没有观察者对象,而MediaTracker对象同样要求一个组件对象构造,在这种时候也没法调用.
BufferedImage bi = new BufferedImage(目标宽,目标高,BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D)image.getGraphics();
Image src = Toolkit.getDefaultToolkit().getImage("d:/aaa.jpg");
不管你的src原来是多大,只要你draw时设定大小,就会以你设定的大小画到目标bi上,这就是
缩略图或合成图的方法.但在g.drawImage(src,坐标x,坐标y,宽,高,观察者);调用时,因为没有一个观察者实例,一次调用99%不会成功.所以我是这样调用的:
while (!(flag=g.drawImage(src, 0, 0, 240, 320, null))) ;
大家注意是如果flag为flase就只有一个";"也就是什么也不做直接进入下一次循环.
我试过一般画一张新图要10多秒,如果是生成计数器,这在servlet中根本不行.没有人愿意用10秒的时间等一个计数器出来.后来我想让这个servlet实现观察者.就在
while (!(flag=g.drawImage(src, 0, 0, 240, 320, null))) {
}
中调试它每次getWidth(null)的log并打印出来,结果发现如果改成
while (!(flag=g.drawImage(src, 0, 0, 240, 320, null))) {
System.out.println(src.getWidth(null));
}
却只要200MS,0.2秒左右就完成了.原来,System.out时要调用BIOS的资源,这时线程要闲置等待,到反而让src资源有了load的时间.于是改成:
while (!(flag=g.drawImage(src, 0, 0, 240, 320, null))) {
Thread.currentThread().sleep(1);
}
哈哈,成功了.sleep时1,5,10MS没有多大区别,都在200MS左右完成,一般不要再睡更多的时间.
所以即使是利己线程的也要看它是否对其它资源的请求,真的应了那句话叫"予人方便,于己方便!"
原文地址:
1.6 GBK与GB2312字符集的问题
有的用户比如叫扬璠,或是李珺。就不能正确显示她的名字,而显示一个扬?,李?,其他中文没问题,就是这些比较偏怪的中文无法显示。这就是GBK和GB2312的区别,你说的那几个字是GBK中的,GB2312中根本没有,只好??了,使用GBK就行了。
原文地址:
1.7 怎样制作ear,war文件?
方法一:
在JDK CLASSPATH下命令行打包:
用法:jar {ctxu}[vfm0M] [jar-文件] [manifest-文件] [-C 目录] 文件名 ...
选项:
展开阅读全文