随笔-46  评论-64  文章-2  trackbacks-0
  2008年1月30日
     摘要: 废话不多说,先看看我们最终达到的效果.  源码下载在文章最后。Style1:Style2:上面的tag cloud实现思想如下:1. Server端提供Tag的相关信息,包括TagName,Posts等,使用JSON格式传输数据    这个例子中,我使用Servlet,使用json-lib将Bean转成JSON字符串。当然Tag的相关信息这里只是演示,真实环境中可能就需要从数据库取出来再处理了。 ...  阅读全文
posted @ 2008-09-28 16:10 jht 阅读(3142) | 评论 (3)编辑 收藏
1. response.setHeader("Cache-Control","no-cache");

This is used to prevent the browser from caching your dynamic content generated by a JSP or Servlet.

You set this attribute in the HTTP header of the response object which would tell the browser not to cache this content. So everytime you request the page again, the browser would make a new request, instead of showing you a cached page.

2.使用服务器端控制AJAX页面缓存:
    response.setHeader( "Pragma", "no-cache" );
    response.addHeader( "Cache-Control", "must-revalidate" );
    response.addHeader( "Cache-Control", "no-cache" );
    response.addHeader( "Cache-Control", "no-store" );
    response.setDateHeader("Expires", 0);
单纯的使用 xmlhttp.setRequestHeader("Cache-Control","no-cache")无效。

3.Cache-Control头域
  Cache-Control指定请求和响应遵循的缓存机制。在请求消息或响应消息中设置Cache-Control并不会修改另一个消息处理过程中的缓存处理过程。请求时的缓存指令包括no-cache、no-store、max-age、max-stale、min-fresh、only-if-cached,响应消息中的指令包括public、private、no-cache、no-store、no-transform、must-revalidate、proxy-revalidate、max-age。各个消息中的指令含义如下:
  Public指示响应可被任何缓存区缓存。
  Private指示对于单个用户的整个或部分响应消息,不能被共享缓存处理。这允许服务器仅仅描述当用户的部分响应消息,此响应消息对于其他用户的请求无效。
  no-cache指示请求或响应消息不能缓存
  no-store用于防止重要的信息被无意的发布。在请求消息中发送将使得请求和响应消息都不使用缓存。
  max-age指示客户机可以接收生存期不大于指定时间(以秒为单位)的响应。
  min-fresh指示客户机可以接收响应时间小于当前时间加上指定时间的响应。
  max-stale指示客户机可以接收超出超时期间的响应消息。如果指定max-stale消息的值,那么客户机可以接收超出超时期指定值之内的响应消息。

Read more:
   http://www.jiehoo.com/browser-cache-problem.htm (作者: Cherami 原载: 浏览器缓存)
   再论怎么有效利用浏览器缓存之怎么避免浏览器缓存静态文件
   HTTP协议header头域- PetitPrince - 博客园
posted @ 2008-09-27 10:23 jht 阅读(16571) | 评论 (3)编辑 收藏
< link id = " css "  rel = StyleSheet type = " text/css "  href = " ./button.css "   />

< script type = " text/javascript " >
 
var  cssArray  =   new  Array( " button.css " , " button1.css " , " button2.css " , " button3.css " , " button4.css " , " button5.css " , " button6.css " );
 
var  index  =   0 ;
 
function  changeCssFile()
 
{
  
var  css  =  document.getElementById( " css " );
  index
++ ;
  
if (index < cssArray.length)
  
{
   css.href 
=  cssArray[index];
  }

  
else
  
{
   index 
= 0 ;
   css.href 
=  cssArray[index];
  }

 }

</ script >

  < class ="button"  href ="#"  onclick ="changeCssFile()" >
  
< span  id ="buttonText" > Change another style </ span >
 
</ a >
posted @ 2008-09-25 12:26 jht 阅读(752) | 评论 (0)编辑 收藏
A:浏览器问题,可能会把透明区域显示成有灰度的区域,解决办法,转成gif格式吧,支持比较好

see: GIF or PNG? - Quality Web Tips
posted @ 2008-09-24 23:02 jht 阅读(1175) | 评论 (1)编辑 收藏
比如说下面的这个header.jsp中有中文,那么包含这个文件的网页可能就会出现乱码
<jsp:include page="header.jsp"></jsp:include>

解决办法是在header.jsp里加上下面这段话:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
posted @ 2008-09-23 12:46 jht 阅读(445) | 评论 (0)编辑 收藏
     摘要: log4j 支持运行时修改日志的相关配置,看了一下他的source code, 用FileWatchdog这个类来做的,代码也很简单,通过循环在一定时间间隔读取配置文件,如果文件变更,调用一个doOnChange()方法。如果自己要做一个支持运行时修改配置的系统可参考上面的做法。下面是一段支持运行时修改配置的系统Prototype代码,和log4j的做法稍有不同,使用Observer模式,使其更加...  阅读全文
posted @ 2008-09-04 14:49 jht 阅读(1306) | 评论 (0)编辑 收藏

可以用下面这个方法来做到

import  org.apache.log4j.Logger;
import  org.apache.log4j.PropertyConfigurator;

public   class  DemoRunTimeChangeLog4J  {

 
private   static   final  Logger logger  =  Logger.getLogger(DemoRunTimeChangeLog4J. class );

 
public   static   void  main(String[] args)  {
  PropertyConfigurator.configureAndWatch(
" src/log4j.configureAndWatch " , 60000 );
  
  
while  ( true {
   
if  (logger.isDebugEnabled())  {
    logger.debug(
" DEBUG MESSAGE " );
   }


   logger.info(
" Info Message " );

   
try   {
    Thread.sleep(
5000 );
   }
  catch  (InterruptedException e)  {
   }

  }

 }

}


posted @ 2008-09-03 17:35 jht 阅读(619) | 评论 (0)编辑 收藏
自定义TagLib的时候碰到这个错误,原因是编辑tld文件的时候没有使用DTD或者Schema文件验证,拼写错误导致最终报这个错误消息

如果遇到同样问题的同学,不妨检查一下自己的tld文件对不对。

BTW: tld类的异常需要处理好
posted @ 2008-08-19 15:53 jht 阅读(7713) | 评论 (1)编辑 收藏
这个问题可能由两个地方导致

1. 你的POJO类不是一个JavaBean,最可能的原因是没有一个无参构造函数
2. 在dwr.xml里这个类的<convert>指定的不对,或者是没有指定
posted @ 2008-08-11 11:21 jht 阅读(4162) | 评论 (4)编辑 收藏
数独游戏小程序

数独的游戏规则很简单:在九个九宫格里,填入1到9的数字,让每个数字在每个行、列及九宫格里都 只出现一次就可以过关了! 虽然游戏的规则十分简单,但很多人在没有计算机辅助时,常常会不小心造成违规状况。

下面是我做的一个数独小程序 1.0 版本,目前功能如下:

选择外观;
数独自动出题;
数独自动解题;
游戏计时功能;
开启解答辅助功能,出错提示;
开启解答辅助功能,辅助线;

程序基于Java Swing,运行需要JRE1.6以上版本。
 
可双击执行的JAR包,点此下载。(Napkin的LookAndFeel蛮好玩,我就一起弄在里面了)
程序的源码,点此下载。(有兴趣修改源码的朋友可以DIY出自己喜欢的版本。)

或者也可以从 http://groups.google.com/group/afun 这个group的files里面找到上面的两个文件。

对数独有兴趣的朋友可以玩一下这个小工具,欢迎留下宝贵意见,让这个小程序更完善。 可能我会另开一篇日志说一下Source Code相关的东西。
 
Figure: 填值的出错提示
Figure: 填值的出错提示2
演示如何使用辅助线功能:
首先选中Enable help system选项, 然后选择按钮 1 ,这时候关于 1 的辅助线开启,把不能填1的单元格都画了线,画线规则是,1所在的行画桃红色横线,1所在的列画橙色竖线,1所在的九宫格画粉色X线,如下图,这时候我们发现,在第五行只有第二列这一单元格格没有被画上线,那么这一个肯定只能填上1。
这时候我们就可以把1填入到 第五行第二列,这时辅助线会自动更新。如下图,此时没有可以确定填入1的单元格了,我们可以换一个数看辅助线。
 OK,我们选择3,这时候辅助线如下,我们发现第五行第四格可以确定填入3这个值。
填入3之后的图如下:
解答成功之后,会弹出恭喜解答成功的对话框。
自动解答功能可能出现的异常情况
当题目比较复杂的时候,可能会弹出如下的提示异常的对话框,这是因为解答数独用了递归的解法,难度大时递归深度很深,导致栈溢出,可以通过运行JVM时指定线程栈大小的方法来解决这个问题。
posted @ 2008-08-02 17:32 jht 阅读(2697) | 评论 (4)编辑 收藏
new & valueof & 直接赋值的区别
首先来看下面这段代码
public static void main(String[] args) {
  String s1 = "s1";
  String s2 = new String("s2");
  String s3 = String.valueOf(12345);
}
 
编译成class文件之后,使用eclipse class file viewer查看
 
  // Method descriptor #15 ([Ljava/lang/String;)V
  // Stack: 3, Locals: 4
  public static void main(java.lang.String[] args);
     0  ldc <String "s1"> [16]
     2  astore_1 [s1]

     3  new java.lang.String [18]
     6  dup
     7  ldc <String "s2"> [20]
     9  invokespecial java.lang.String(java.lang.String) [22]
    12  astore_2 [s2]
    13  sipush 12345
    16  invokestatic java.lang.String.valueOf(int) : java.lang.String [25]
    19  astore_3 [s3]

    20  return
      Line numbers:
        [pc: 0, line: 12]
        [pc: 3, line: 13]
        [pc: 13, line: 14]
        [pc: 20, line: 20]
      Local variable table:
        [pc: 0, pc: 21] local: args index: 0 type: java.lang.String[]
        [pc: 3, pc: 21] local: s1 index: 1 type: java.lang.String
        [pc: 13, pc: 21] local: s2 index: 2 type: java.lang.String
        [pc: 20, pc: 21] local: s3 index: 3 type: java.lang.String
}
 
对于第一行代码 String s1 = "s1"; 编译成字节码之后,对应两条指令,
  1. ldc指令从运行时常量池push一个值到Frame的操作数栈上面,这个值在这里就是"s1"字符串的引用,
  2. astore指令将objectref存储到局部变量,这里也就是存储到局部变量s1。
 
对于第二行代码   String s2 = new String("s2");编译成字节码之后,对于的指令也用高亮标注出来了,这里把操作数栈的情况画了出来,希望能帮助理解。橙色标注的为栈顶元素。
  1. new指令会在堆上创建对象,操作数栈里压入创建的objectref,
     
    objectref
    ...
  2. dup指令复制操作数栈顶的元素, 
    objectref
    objectref
    ...
  3. ldc指令依然是从常量池push一个值到Frame的操作数栈上,这个值是"s2"字符串的引用。 
    "s2"_ref
    objectref
    objectref
    ...
  4. invokespecial 指令调用一个方法,这里就是调用String的构造函数,调用完成之后栈上还有一个objectref 
    objectref
    ...
  5. astore指令将objectref存储到局部变量,这里也就是存储到局部变量s2。 
                
    ...
 
对于第三行代码  String s3 = String.valueOf(12345); 编译成字节码之后对应的指令,
  1. sipush 将 12345 压栈
  2. invokestatic 调用 String.valueof(int) 方法
  3. astore 将栈顶的对象引用存储到本地变量s3 (这里不再深究这个栈顶元素是怎么来的了)
 
PMD检查代码的时候,有这样的warning: Avoid instantiating String objects.Call String.valueOf() instead. PMD给出的原因是In JDK 1.5, calling new String() causes memory allocation. String.valueOf() is more memory friendly.
 
经过上面的分解,我们应该知道原因了,以后写代码的时候,初始化一个字符串,  String s1 = "s1"; 这样的代码肯定比  String s2 = new String("s2");代码强,将其他类型的值转换成String的时候,valueof方法比new方法效率也高。

 
备注:
A frame is used to store data and partial results(局部变量,操作数栈), as well as to perform dynamic linking , return values for methods, and dispatch exceptions.
 
ldc指令的操作数栈: ...->...,value (value是int,float 或者 string 类型的引用)
astore的操作数栈: ...,objectref->...
new指令的操作数栈: ...->...,objectref
dup指令的操作数栈: ...,value->...,value,value
invokespecial的操作数栈: ...,objectref, [agr1,[arg2...]]->...
invloestatic的操作数栈:..., [arg1, [arg2...]] -> ...

 
如果要理解的更透彻建议阅读以下参考资料:
posted @ 2008-07-28 14:27 jht 阅读(1698) | 评论 (1)编辑 收藏

java.awt.Component.requestFocusInWindow

posted @ 2008-07-22 14:34 jht 阅读(380) | 评论 (1)编辑 收藏
http://www.blogjava.net/Files/jht/MyScreenSnap_2.0.zip

截图程序,设计目标:简单易用

支持全屏截图和选择截图,运行需要JRE1.5以上版本

在1.0版http://www.blogjava.net/Files/jht/MyScreenSnap.zip的基础上,

   * 修改了一下主界面,增加图片保存为BMP,GIF,JPG,PNG格式的功能

   * 参考了千里冰封的代码,加上了调整选择区域的功能

Source Code已经包含在压缩包里面了,有兴趣的可以down下来改改。

下面是1.0版本和2.0版本的两个界面。

1.0vs2.0.png
posted @ 2008-07-02 09:17 jht 阅读(327) | 评论 (1)编辑 收藏
其实是个简单的小问题,仅在此做个记录。

遇到这个问题的不妨读一下这篇文章先:Access查询和过滤条件 http://www.fontstuff.com/access/acctut06.htm

我遇到的报错语句如下:

update monitor_table set logoffTime ='2008-04-06 16:58:54', keyClickCount ='17' where userName ='abcd' and logonTime ='2008-04-06 16:56:36' 

读了上面的文章,知道 MS Access的时间分隔符号为 # ,而 MS SQL Server的时间分隔符为 '

所以正确的语句应该是:

update monitor_table set logoffTime ='2008-04-06 16:58:54', keyClickCount ='17' where userName ='abcd' and logonTime =#2008-04-06 16:56:36

posted @ 2008-05-19 15:39 jht 阅读(1905) | 评论 (1)编辑 收藏
http://download-west.oracle.com/docs/cd/B10501_01/appdev.920/a96616/arxml24.htm
posted @ 2008-05-19 15:38 jht 阅读(186) | 评论 (0)编辑 收藏
如题,有人真的遇到过这个问题,开始被问到的时候还真觉得奇怪,心想不会啊,从JDK5开始就支持这个枚举类型了啊,为什么呢?

开始还怀疑他的JDK版本太低导致的,但是一看是JDK1.6的,迷惑了几秒。。。

然后我打开了Eclipse的Java编译选项,发现原来遇到这个问题的人的Java编译级别设置的是1.4,如下图,当然就会出问题了。

compile_setting.png

呵呵,虽然,这是个小问题,但是如果遇到了还真够新手郁闷一阵的。
posted @ 2008-04-02 20:56 jht 阅读(890) | 评论 (2)编辑 收藏

作者简介

徐皓,北京航空航天大学计算机系本科生,你可以通过ertri@163.com与他联系。

正文

不灵敏的图形用户界面会降低应用程序的可用性。当以下现象出现的时候,我们通常说这个用户界面反应不灵敏。

  1. 不响应事件的现象;
  2. 没有更新的现象
[@more@]

这些现象在很大程度上与事件的处理方法相关,而在编写Swing应用程序的时候,我们几乎必然要编写方法去响应鼠标点击按钮,键盘回车等事件。在这些方法中我们要编写一些代码,在运行时去触发一些动作。常见动作包括查找,更新数据库等。在这篇文章中通过对一个实例的分析,介绍了一些基本概念,常见的错误以及提出了一个解决方案。

event-dispatching thread

我们一定要记住,事件响应方法的代码都是在event-dispatching thread中执行的,除非你启用另一个线程。

那么,什么是event-dispatching thread呢?在《Java Tutorial》[1]中,作者给出了一条单一线程规则:一旦一个Swing组件被实现(realized),所有的有可能影响或依赖于这个组件的状态的代码都应该在event-dispatching thread中被执行。而实现一个组件有两种方式:

  1. 对顶层组件调用show(), pack(), 或者setVisible(true);
  2. 将一个组件加到一个已经被实现的容器中。

单一线程规则的根源是由于Swing组件库的大部分方法是对多线程不安全的,尽管存在一些例外。这些例外的情况可以在《Java Tutorial》[1]的相关章节找到,这里不再展开。

为了支持单一线程模型,Swing组件库提供了一个专门来完成这些与Swing组件相关的操作的线程,而这一线程就是event-dispatching thread。我们的事件响应方法通常都是由这一线程调用的,除非你自己编写代码来调用这些事件响应方法。在这里初学者经常犯的一个错误就是在事件响应方法中完成过多的与修改组件没有直接联系的代码。其最有可能的效果就是导致组件反应缓慢。比如以下响应按钮事件的代码:

String str = null;
this.textArea.setText("Please wait...");
try {
//do something that is really time consuming
str = "Hello, world!";
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.textArea.setText(str);    

执行之后的效果就是按钮似乎定住了一段时间,直到Done.出现之后才弹起来。原因就是Swing组件的更新和事件的响应都是在event-dispatching thread中完成的,而事件响应的时候,event-dispatching thread被事件响应方法占据,所以组件不会被更新。而直到事件响应方法退出时才有可能去更新Swing组件。

为了解决这个问题,有人也许会试图通过调用repaint()方法来更新组件:

final String[] str = new String[1];
this.jTextArea1.setText("Please wait...");
this.repaint();

try {
Thread.sleep(1000L);
}catch(InterruptedException e) {
e.printStackTrace();
}
str[0] = "Done.";

jTextArea1.setText(str[0]);

但是这一个方法没有起到预期的作用,按钮仍然定住一段时间,在察看了repaint()方法的源代码之后就知道原因了。

PaintEvent e = new PaintEvent(this, PaintEvent.UPDATE,
new Rectangle(x, y, width, height));
Toolkit.getEventQueue().postEvent(e);        

repaint()方法实际上是在事件队列里加了一个UPDATE的事件,而没有直接去重画组件,而且这一个事件只能等待当前的事件响应方法结束之后才能被分配。因此只有绕过分配机制直接调用paint方法才能达到目的。

final String[] str = new String[1];
this.jTextArea1.setText("Please wait...");
this.paint(this.getGraphics());

try {
Thread.sleep(1000L);
}catch(InterruptedException e) {
e.printStackTrace();
}
str[0] = "Done.";

jTextArea1.setText(str[0]);

这样却是实现了更新,但是还存在着以下的问题。虽然从感觉上,按钮已经弹起来了,但是在Done.出现之前,我们却无法按下这个按钮。可以说按钮还是定住了,只不过定在了弹起的状态。调用重绘方法无法从根本上解决问题,因此我们需要寻求其他的方法。

使用多线程

有效的解决方法是使用多线程。首先看一看一个更好的解决方案,这一方案是在参考《Rethinking Swing Threading》[3]的一个程序片段完成的:

final String[] str = new String[1];
this.jTextArea1.setText("Please wait...");
this.repaint();

new Thread() {
public void run() {
try {
Thread.sleep(1000L);
}catch(InterruptedException e) {
e.printStackTrace();
}
str[0] = "Done.";
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
jTextArea1.setText(str[0]);
}
});
}
}.start();

在这个程序中,要花费大量时间的操作被放到另一个线程当中,从而使事件响应方法能快速返回,event-dispatching thread就可以更新UI和响应其它事件了。注意到这个程序使用了invokeLater()方法。invokeLater()方法的作用是让event-dispatching thread去运行制定的代码。当然也可以不使用invokeLater()方法,但是这样就违背了单一线程原则,同时带来了一定程度的相对多线程的不安全性。到现在,解决方案似乎是完美的了,但是我们看一看在原来的程序添加下面的代码,尽管我们通常不这样做。

public void paint(java.awt.Graphics g) {
super.paint(g);
g.drawRect(1, 1, 100, 100);
}

我们会发现以前画的矩形被覆盖了一部分,原因是由于我们没用重画这一个矩形,因此在结尾加上对repaint()方法的调用。

final String[] str = new String[1];
this.jTextArea1.setText("Please wait...");
this.repaint();

new Thread() {
public void run() {
try {
Thread.sleep(1000L);
}catch(InterruptedException e) {
e.printStackTrace();
}
str[0] = "Done.";
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
jTextArea1.setText(str[0]);
repaint();
}
});
}
}.start();

如果你认为这段代码过于缺乏可读性,通过在《Java Tutorial》[1]里面介绍的SwingWorker来简化编程的方法。可以通过实现一个construct()方法来实现花费大量时间的操作和重写finished()方法来完成组件更新的工作。

this.jTextArea1.setText("Please wait...");

final SwingWorker worker = new SwingWorker() {
public Object construct() {
try {
Thread.sleep(1000L);
}catch(InterruptedException e) {
e.printStackTrace();
}
return "Done.";
}
public void finished() {
jTextArea1.setText(getValue().toString());
repaint();
}
};
worker.start();

在《Rethinking Swing Threading》[3],作者将以上的编程方式称为同步方式。另外作者提出了一个通过消息机制来实现相同功能的更清晰,但是需要编写更多代码的"异步"的方法。

结论

总之,我们在编写使用Swing组件的程序是要记住以下几点:

1、不要过多地占用event-dispatching thread;

2、与更新组件相关的代码要使用event-dispatching thread去执行;

3、要更新组件。

编写反应灵敏的图形用户界面还需要考虑很多问题,以上只是最基本的一部分。欢迎有兴趣的读者来信进行讨论。

posted @ 2008-03-13 17:53 jht 阅读(565) | 评论 (0)编辑 收藏

打开注册表编辑器,进入主键[HKEY_CURRENT_USER\Software\Microsoft\Command Processor],将“CompletionChar”键值设置为9。  

posted @ 2008-01-30 16:40 jht 阅读(704) | 评论 (0)编辑 收藏