随笔-295  评论-26  文章-1  trackbacks-0
 
public class ssss {
   
    /** Creates a new instance of ssss */
    public ssss() {
    }
    public static void main(String s[]) throws IOException{
 //       java.net.URL  m=new java.net.URL("http://www.baidu.com/index.html");
   //     HttpURLConnection sst=(HttpURLConnection) m.openConnection();
//           DataInputStream in = new DataInputStream(connection.getInputStream());
  //          DataOutputStream out = new DataOutputStream(new FileOutputStream(fileName));
      DataInputStream  tttt= new   DataInputStream((InputStream)new FileInputStream("c://ffffff.java.bak"));
  //      DataInputStream tttt=new DataInputStream(sst.getInputStream());
        DataOutputStream out = new DataOutputStream(new FileOutputStream("D://ffffff.java.bak"));
        byte[] buffer = new byte[4096];
        int count=0;
        while ((count = tttt.read(buffer))>0){
            out.write(buffer,0,count);
        }
        out.close();
        tttt.close();
    }
posted @ 2007-10-31 16:32 华梦行 阅读(183) | 评论 (0)编辑 收藏
http://www.java2s.com
http//www.dxiaoshuo.com
http://www.mdianying.com
posted @ 2007-10-26 16:51 华梦行 阅读(200) | 评论 (0)编辑 收藏

1.表名要以模块简称为前缀, 如:Bse_, Crm_, Sys_, Scm_, OA_, Mrp_, Ftm_等,后面部分与字段名称都尽量使用有意义的英文单词,第一个字母要大写,单词之间直接连接,不需要连接符号;

2.每个表必须要有主键,而且对经常使用的字段建立索引,除了必要的字段,尽可能设置字段允许为空而且不需要缺省值;

3.主键规则:对于经常增删的数据表,主键字段建议为varchar(36),用GUID生成,其它表原则上以性能为参考来建立主键,但要考虑可移植性,并发性与易初始化;主键本身在业务逻辑上不体现,不具有实际意义;

4.避免使用复合主键,只比较一个字段比比较多个字段快很多;

5.除了必要的冗余数据外,尽量避免冗余数据的出现,容易保证数据的一致性;

6.完全符合建立聚集索引要求:“既不能绝大多数都相同,又不能只有极少数相同”的规则;

7.用聚合索引比用不是聚合索引的主键速度快;

8.用聚合索引比用一般的主键作order by时速度快;

9.使用聚合索引内的字段,搜索时间会按数据占整个数据表的百分比成比例减少;

10.索引有助于提高检索性能,但过多或不当的索引也会导致系统低效,过多的索引甚至会导致索引碎片;

11.Like条件中,通配符%在字符串的开头使得索引无法使用;

12.OR条件,会引起全表扫描;

13.不推荐使用以下条件语句:NOT、!=、<>、!<、!>、NOT EXISTS、NOT IN、NOT LIKE;

14.IN 的作用相当与OR;

15.尽量少用NOT;

16.exists 和 in 的执行效率是一样的;

17.用函数charindex()和前面加通配符%的LIKE执行效率一样;

18.union在通常情况下比用or的效率要高的多,但如果or两边的查询列是一样的话,那么用union则反倒和用or的执行速度差很多;

19.字段提取要按照“需多少、提多少”的原则,避免“select *”;

20.count(*)不比count(主键)慢,而count(*)却比其他任何除主键以外的字段汇总速度要快,而且字段越长,汇总速度就越慢;

21.order by按聚集索引列排序效率最高;

22.高效的TOP,提高性能;

23.聚集索引有两个最大的优势:1、以最快的速度缩小查询范围;2、以最快的速度进行字段排序;

24.一定要将聚集索引建立在:1、您最频繁使用的、用以缩小查询范围的字段上;2、您最频繁使用的、需要排序的字段上;

25. 由于改变一个表的内容,将会引起索引的变化。频繁的insert,update,delete语句将导致系统花费较大的代价进行索引更新,引起整体性能的下降。一般来讲,在对查询性能的要求高于对数据维护性能要求时,应该尽量使用索引,否则,就要慎重考虑一下付出的代价;

26. 不同类型的索引效能是不一样的,应尽可能先使用效能高的,比如:数字类型的索引查找效率高于字符串类型,定长字符串char,nchar的索引效率高于变长字符串varchar,nvarchar的索引;

27. 有嵌套查询时,尽可能在内层过滤掉数据;

28. 多表关联查询时,需注意表顺序,并尽可能早的过滤掉数据,只将必须的数据带到后续的操作中去;

29. 任何对列的操作都导致全表扫描,如数据库函数、计算表达式等,查询时应尽可能将操作移至等号的某一边;

30. 存储过程如果有返回结果集的话, 就不能通过返回一个状态值来进行逻辑判断,否则会导致执行两次存储过程,影响性能;

31. Where条件中如果存在以下情况,则索引无效,严重影响性能: 索引列不要使用函数; 索引列不要使用NOT; 索引列不要用NULL; 不要对索引列进行计算;

posted @ 2007-10-26 15:34 华梦行 阅读(214) | 评论 (0)编辑 收藏

1. JSP页面的编码标准: UTF-8;

2. 有调用request对象的页面加上: <%request.setCharacterEncoding("UTF-8");%>;

3. 有调用response对象的页面加上: <%response.setCharacterEncoding("UTF-8");%>;

4. sendRedirect的调用需对中文等多字节参数进行编码: URLEncoder.encode(argname,"UTF-8");

5. 不要在页面中调用以下函数: out.close();

6. 不要在页面中调用以下函数: response.getOutputStream(),应该使用out对象,或者使用Servlet;

7. 页面文件转换为UTF-8文件的方法: 先用记事本打开,另存为UTF-8字符集, 加入编码UTF-8字符集, 然后再用NB IDE打开即可;

8. 不要调用标记为废弃的函数;

9. 所有可以点击查看内容的文字, 使用类: LinkText; 如: <span class="LinkText" ...

10. 取记录集显示时,循环中不能再进行数据库连接操作, 用Left Join或存储过程替换;

11. 程序文件目录与文件名称,命名一定要统一,采用JAVA的命名规则,用英文且大小写分开,同样功能的页面命名保持统一, 以便维护;

posted @ 2007-10-26 15:30 华梦行 阅读(235) | 评论 (0)编辑 收藏

1. 每个文件的头部注释: 版权说明、版本号、生成日期、作者、内容、功能、与其它文件的关系、修改日志等,头文件的注释中还应有函数功能简要说明;

2. 函数头部应进行注释:函数的目的/功能、输入参数、输出参数、返回值、调用关系(函数、表)、作者或修改人等;

3. 对变量的定义和分支语句(条件分支、循环语句等)必须编写注释;

4. 自己特有的命名风格,要自始至终保持一致,不可来回变化;

5. 类命名标准: 由两个或以上的相近意义的英文单词组成,首字母大写;

6. 一个类里面只能实现一个单据或一个功能的不同操作方面,尽可能减少类之间的耦合性;

7. 一些通用功能,都分类封装成不同的实用操作类;

8. 对以前的对象或函数重构或重命名时, 必须由修改人搜索以前的所有调用并替换,同时通知其它人以后使用新的调用;

9. 注释格式 /** * Setting to true will enable the display of debug information. * * @param d A boolean. * @return An Email. * @throws EmailException * @version Version * @author Author * @since 1.0 */

10. 类包规范: path.crm(itm,system,basic,oa,wms,mrp,scm).entity(manager),按模块分开类,同时实体类与操作类分开,映射文件跟实体类同目录;path.util下为实用类;Servlet映射为二级目录,目前为/path/system;

11. 页面不允许RS对象引用,全部用List或HashMap来实现;

12. 参数传递尽可能使用实体类, 没有实体类的使用HashMap;

13. 方法重载时, 可替换的不能使用重载;

14. 严格按照代码缩进格式进行代码书写,包括各种操作与分隔符号两边的空格;

15. 模糊搜索关键字,可以输入如"ab,abc"以逗号分隔的多个关键字进行搜索;

16. 变量命名规则: 数据类型简写+首字母大写的变量英文名称, 如果是全局变量加上小写g, 如: gszLimitString, gnModuleFlag, gnPageSize;

17. 程序中,SQL语句不能包含方括号, SQL语句中的表别名不能加AS,用空格代替;

18. SQL字符串,不能直接拼接字符串变量,用SET方法实现变量赋值,避免非法字符串变量引起安全问题;

19. 必须随时关闭所用的ResultSet,Statement对象,最好用finally实现,避免错误时没有关闭的问题;

20. 附件管理: 文件大小统一用text.formatFileSize()进行格式化;查看与下载链接用SPAN标签;

21. 其它详细的请参见公司的“软件编程规范检查”;

posted @ 2007-10-26 15:29 华梦行 阅读(145) | 评论 (0)编辑 收藏

1. 有"+"操作的字符串, 使用StringBuffer代替String;

2. 生成对象时,分配合理的空间和大小,如StringBuffer,Vector的初始化大小;

3. 优化循环体,避免在循环中生成同一个变量或调用同一个函数(参数变量也一样);

4. 尽量在使用时才创建该对象; 应该尽量重复使用一个对象,而不是声明新的同类对象;

5. 尽量使用局部变量; 尽量使用静态变量,从而使他所有的实例都共享这个变量;

6. 尽量减少方法的调用, 可事先判断, 同时用变量替换;

7. 尽量使用Java系统API,如复制大量数据时,使用System.arraycopy();

8. 尽可能使用带有Buffer的类代替没有Buffer的类;

9. 不用保存太多的信息在HttpSession中, 同时注意清除Session;

10. 大型数据量处理或批处理数据记录,尽量使用直接访问数据库的方法,用SQL直接存取数据;

11. 在生产环境下,禁止servlet和jsp的自动重载;

12. 插入下述“显式”计时代码,对程序进行评测:

long start = System.currentTimeMillis(); // 要计时的运算代码放在这儿,返回的时间以千分之一秒(1毫秒)为单位 long time = System.currentTimeMillis() - start;

13. 常用运算时间单位: 运算 示例 标准时间本地赋值 i=n; 1.0 实例赋值 this.i=n; 1.2 int增值 i++; 1.5 byte增值 b++; 2.0 short增值 s++; 2.0 float增值 f++; 2.0 double增值 d++; 2.0 空循环 while(true) n++; 2.0 三元表达式 (x<0) ?-x : x 2.2 算术调用 Math.abs(x); 2.5 数组赋值 a[0] = n; 2.7 long增值 l++; 3.5 方法调用 funct(); 5.9 throw或catch异常 try{ throw e; }或catch(e){} 320 同步方法调用 synchMehod(); 570 新建对象 new Object(); 980 新建数组 new int[10]; 3100

posted @ 2007-10-26 15:27 华梦行 阅读(262) | 评论 (0)编辑 收藏
多线程编程——基础篇 (二)

时间:2006-08-16
作者:axman
浏览次数: 3723
本文关键字:Java多线程线程线程对象单线程go deep into java
文章工具
推荐给朋友 推荐给朋友
打印文章 打印文章

  在进入java平台的线程对象之前,基于基础篇(一)的一些问题,我先插入两个基本概念。

[线程的并发与并行]

  在单CPU系统中,系统调度在某一时刻只能让一个线程运行,虽然这种调试机制有多种形式(大多数是时间片轮巡为主),但无论如何,要通过不断切换需要运行的线程让其运行的方式就叫并发(concurrent)。而在多CPU系统中,可以让两个以上的线程同时运行,这种可以同时让两个以上线程同时运行的方式叫做并行(parallel)

  在上面包括以后的所有论述中,请各位朋友谅解,我无法用最准确的词语来定义储如并发和并行这类术语,但我以我的经验能通俗地告诉大家它是怎么一回事,如果您看到我说的一些"标准"文档上说的不一样,只要意思一致,那您就不要挑刺了。

[JAVA线程对象]

  现在我们来开始考察JAVA中线程对象。

  在JAVA中,要开始一个线程,有两种方式。一是直接调用Thread实例的start()方法,二是
将Runable实例传给一个Thread实例然后调用它的start()方法。

  在前面已经说过,线程对象和线程是两个完全不同的概念。这里我们再次深入一下,生成一个线程的实例,并不代表启动了线程。而启动线程是说在某个线程对象上启动了该实例对应的线程,当该线程结束后,并不会就立即消失。

  对于从很多书籍上可以看到的基础知识我就不用多说了。既然是基础知识,我也着重于从普通文档上读不到的内容。所以本节我重点要说的是两种线程对象产生线程方式的区别。

class MyThread extends Thread{
  public int x = 0;
  
  public void run(){
    
    for(int i=0;i<100;i++){
      try{
        Thread.sleep(10);
      }catch(Exception e){}
      System.out.println(x++);
      
    }
  }
}

  如果我们生成MyThread的一个实例,然后调用它的start()方法,那么就产生了这个实例对应的线程:

public class Test {
  public static void main(String[] args) throws Exception{
    MyThread mt = new MyThread();
    mt.start();
  }
}

  不用说,最终会打印出0到99,现在我们稍微玩一点花样:

public class Test {
  public static void main(String[] args) throws Exception{
    MyThread mt = new MyThread();
    mt.start();
    System.out.println(101);
  }
}

  也不用说,在基础篇(一)中我们知道由于单CPU的原因,一般会先打印101,然后打印0到99。不过我们可以控制线程让它按我们的意思来运行:

public class Test {
  public static void main(String[] args) throws Exception{
    MyThread mt = new MyThread();
    mt.start();
    mt.join();
    System.out.println(101);
  }
}

  好了,我们终于看到,mt实例对应的线程(假如我有时说mt线程请你不要怪我,不过我尽量不这么说)。在运行完成后,主线程才打印101。因为我们让当前线程(这里是主线程)等待mt线程的运行结束。"在线程对象a上调用join()方法,就是让当前正在执行的线程等待线程对象a对应的线程运行完成后才继续运行。" 请大家一定要深刻理解并熟记这句话,而我这里引出这个知识点的目的是为了让你继续看下面的例子:

public class Test {
  public static void main(String[] args) throws Exception{
    MyThread mt = new MyThread();
    mt.start();
    mt.join();
    Thread.sleep(3000);
    mt.start();
  }
}

  当线程对象mt运行完成后,我们让主线程休息一下,然后我们再次在这个线程对象上启动线程。结果我们看到:

  Exception in thread "main" java.lang.IllegalThreadStateException

  也就是这种线程对象一时运行一次完成后,它就再也不能运行第二次了。我们可以看一下它有具体实现:

    public synchronized void start() {
        if (started)
            throw new IllegalThreadStateException();
        started = true;
        group.add(this);
        start0();
    }

  一个Thread的实例一旦调用start()方法,这个实例的started标记就标记为true,事实中不管这个线程后来有没有执行到底,只要调用了一次start()就再也没有机会运行了,这意味着:

[通过Thread实例的start(),一个Thread的实例只能产生一个线程]

  那么如果要在一个实例上产生多个线程(也就是我们常说的线程池),我们应该如何做呢?这就是Runnable接口给我们带来的伟大的功能。

class R implements Runnable{
  private int x = 0;
  public void run(){

    for(int i=0;i<100;i++){
      try{
        Thread.sleep(10);
      }catch(Exception e){}
      System.out.println(x++);

    }
  }
}

  
正如它的名字一样,Runnable的实例是可运行的,但它自己并不能直接运行,它需要被Thread对象来包装才行运行:

public class Test {
  public static void main(String[] args) throws Exception{
    new Thread(new R()).start();
  }
}

  当然这个结果和mt.start()没有什么区别。但如果我们把一个Runnable实例给Thread对象多次包装,我们就可以看到它们实际是在同一实例上启动线程:

public class Test {
  public static void main(String[] args) throws Exception{
    R r = new R();
    for(int i=0;i<10;i++)
      new Thread(r).start();
  }
}

  x是实例对象,但结果是x被加到了999,说明这10个线程是在同一个r对象上运行的。请大家注意,因为这个例子是在单CPU上运行的,所以没有对多个线程同时操作共同的对象进行同步。这里是为了说明的方便而简化了同步,而真正的环境中你无法预知程序会在什么环境下运行,所以一定要考虑同步。

  到这里我们做一个完整的例子来说明线程产生的方式不同而生成的线程的区别:

package debug;

import java.io.*;
import java.lang.Thread;


class MyThread extends Thread{
  public int x = 0;

  public void run(){
    System.out.println(++x);
  }
}

class R implements Runnable{
  private int x = 0;
  public void run(){
    System.out.println(++x);
  }
}

public class Test {
  public static void main(String[] args) throws Exception{
    
    for(int i=0;i<10;i++){
      Thread t = new MyThread();
      t.start();
    }
    Thread.sleep(10000);//让上面的线程运行完成
    R r = new R();
    for(int i=0;i<10;i++){
      Thread t = new Thread(r);
      t.start();
    }
  }
}

  上面10个线程对象产生的10个线程运行时打印了10次1。下面10个线程对象产生的10个线程运行时打印了1到10。我们把下面的10个线程称为同一实例(Runnable实例)的多个线程

  下节我们将研究线程对象方法,还是那句话,一般文档中可以读到的内容我不会介绍太多
请大家自己了解。

转载自dev2dev网友axman的go deep into java专栏。

个人自述

一个男人.
一个写程序的男人.
一个写程序并正在从程序中寻找快乐的男人.
一个写程序并正在从程序中寻找快乐并把快乐传递给大家的男人.

一个书生.
一个寂寞的书生.
一个寂寞的梅香竹影下敲声写韵的书生.
一个寂寞的梅香竹影下敲声写韵晨钟暮鼓中逸气扬剑的书生.

那个男人是位书生。没有人知道他的姓名,居无定所,行无定踪,亦耕变读,或渔或樵。
所以有人叫他樵夫(Axman),有人叫他渔郎(fisher)。

posted @ 2007-10-22 11:25 华梦行 阅读(231) | 评论 (0)编辑 收藏
多线程编程——基础篇 (一)

时间:2006-08-08
作者:axman
浏览次数: 5279
本文关键字:Java多线程线程线程对象单线程go deep into java
文章工具
推荐给朋友 推荐给朋友
打印文章 打印文章

  [写在前面]

  随着计算机技术的发展,编程模型也越来越复杂多样化。但多线程编程模型是目前计算机系统架构的最终模型。随着CPU主频的不断攀升,X86架构的硬件已经成为瓶,在这种架构的CPU主频最高为4G。事实上目前3.6G主频的CPU已经接近了顶峰。

  如果不能从根本上更新当前CPU的架构(在很长一段时间内还不太可能),那么继续提高CPU性能的方法就是超线程CPU模式。那么,作业系统、应用程序要发挥CPU的最大性能,就是要改变到以多线程编程模型为主的并行处理系统和并发式应用程序。

  所以,掌握多线程编程模型,不仅是目前提高应用性能的手段,更是下一代编程模型的核心思想。多线程编程的目的,就是"最大限度地利用CPU资源",当某一线程的处理不需要占用CPU而只和I/O,OEMBIOS等资源打交道时,让需要占用CPU资源的其它线程有机会获得CPU资源。从根本上说,这就是多线程编程的最终目的。

  [第一需要弄清的问题]

  如同程序和进程的区别,要掌握多线程编程,第一要弄清的问题是:线程对象和线程的区别

  线程对象是可以产生线程的对象。比如在java平台中Thread对象,Runnable对象。线程,是指正在执行的一个指点令序列。在java平台上是指从一个线程对象的start()开始,运行run方法体中的那一段相对独立的过程。

  鉴于作者的水平,无法用更确切的词汇来描述它们的定义。但这两个有本质区别的概念请初学者细细体会,随着介绍的深入和例程分析的增加,就会慢慢明白它们所代表的真实含义。

  天下难事必始于易,天下大事必始于细。

  让我们先从最简单的"单线程"来入手:(1)带引号说明只是相对而言的单线程,(2)基于java。

    class BeginClass{
        public static void main(String[] args){
            for(int i=0;i<100;i++)
                System.out.println("Hello,World!");
        }
    }

  如果我们成功编译了该java文件,然后在命令行上敲入:

  java BeginClass

  现在发生了什么呢?每一个java程序员,从他开始学习java的第一分钟里都会接触到这个问

  题,但是,你知道它到底发生发什么?

  JVM进程被启动,在同一个JVM进程中,有且只有一个进程,就是它自己。然后在这个JVM环境中,所有程序的运行都是以线程来运行。JVM最先会产生一个主线程,由它来运行指定程序的入口点。在这个程序中,就是主线程从main方法开始运行。当main方法结束后,主线程运行完成。JVM进程也随之退出。

  我们看到的是一个主线程在运行main方法,这样的只有一个线程执行程序逻辑的流程我们称

  之为单线程。这是JVM提供给我们的单线程环境,事实上,JVM底层还至少有垃圾回收这样的后台线程以及其它非java线程,但这些线程对我们而言不可访问,我们只认为它是单线程的。

  主线程是JVM自己启动的,在这里它不是从线程对象产生的。在这个线程中,它运行了main方法这个指令序列。理解它,但它没有更多可以研究的内容。

  [接触多线程]

    class MyThread extends Thread{
        public void run(){
            System.out.println("Thread say:Hello,World!");
        }
    }

    public class MoreThreads{
        public static void main(String[] args){
            new MyThread();
            new MyThread().start();
            System.out.println("Main say:Hello,World");
        }
    }

  执行这个程序,main方法第一行产生了一个线程对象,但并没有线程启动。

  main方法第二行产生了一个线程对象,并启动了一个线程。

  main方法第三行,产生并启动一个线程后,主线程自己也继续执行其它语句。

  我们先不研究Thread对象的具体内容,稍微来回想一下上面的两个概念,线程对象线程。在JAVA中,线程对象是JVM产生的一个普通的Object子类。而线程是CPU分配给这个对象的一个运行过程。我们说的这个线程在干什么,不是说一个线程对象在干什么,而是这个运行过程在干什么。如果一时想不明白,不要急,但你要记得它们不是一回事就行了。

  累了吧?为不么不继续了?

  基于这种风格来介绍多线程,并不是每个人都喜欢和接受的,如果你不喜欢,正好不浪费你的时间了,而如果你接受的话,那就看下一节吧。

  转载自dev2dev网友axman的go deep into java专栏

个人自述

一个男人.
一个写程序的男人.
一个写程序并正在从程序中寻找快乐的男人.
一个写程序并正在从程序中寻找快乐并把快乐传递给大家的男人.

一个书生.
一个寂寞的书生.
一个寂寞的梅香竹影下敲声写韵的书生.
一个寂寞的梅香竹影下敲声写韵晨钟暮鼓中逸气扬剑的书生.

那个男人是位书生。没有人知道他的姓名,居无定所,行无定踪,亦耕变读,或渔或樵。
所以有人叫他樵夫(Axman),有人叫他渔郎(fisher)。

posted @ 2007-10-22 11:17 华梦行 阅读(245) | 评论 (0)编辑 收藏

多线程设计要点

板桥里人 http://www.jdon.com 2002/01/10

 

1 .多线程中有主内存和工作内存之分, 在JVM中,有一个主内存,专门负责所有线程共享数据;而每个线程都有他自己私有的工作内存, 主内存和工作内存分贝在JVM的stack区和heap区。

2. 线程的状态有'Ready', 'Running', 'Sleeping', 'Blocked', 和 'Waiting'几个状态,
'Ready' 表示线程正在等待CPU分配允许运行的时间。

3. 线程运行次序并不是按照我们创建他们时的顺序来运行的,CPU处理线程的顺序是不确定的,如果需要确定,那么必须手工介入,使用setPriority()方法设置优先级。

4. 我们无从知道一个线程什么时候运行,两个或多个线程在访问同一个资源时,需要synchronized

5. 每个线程会注册自己,实际某处存在着对它的引用,因此,垃圾回收机制对它就“束手无策”了。

6. Daemon线程区别一般线程之处是:主程序一旦结束,Daemon线程就会结束。

7. 一个对象中的所有synchronized方法都共享一把锁,这把锁能够防止多个方法对通用内存同时进行的写操作。synchronized static方法可在一个类范围内被相互间锁定起来。

8. 对于访问某个关键共享资源的所有方法,都必须把它们设为synchronized,否则就不能正常工作。

9. 假设已知一个方法不会造成冲突,最明智的方法是不要使用synchronized,能提高些性能。

10 . 如果一个"同步"方法修改了一个变量,而我们的方法要用到这个变量(可能是只读),最好将自己的这个方法也设为 synchronized。

11. synchronized不能继承, 父类的方法是synchronized,那么其子类重载方法中就不会继承“同步”。

12. 线程堵塞Blocked有几个原因造成:

(1)线程在等候一些IO操作
(2)线程试图调用另外一个对象的“同步”方法,但那个对象处于锁定状态,暂时无法使用。

13. 原子型操作(atomic), 对原始型变量(primitive)的操作是原子型的atomic. 意味着这些操作是线程安全的, 但是大部分情况下,我们并不能正确使用,来看看 i = i + 1 , i是int型,属于原始型变量:

(1)从主内存中读取i值到本地内存.
(2)将值从本地内存装载到线程工作拷贝中.
(3)装载变量1.
(4)将i 加 1.
(5)将结果给变量i.
(6)将i保存到线程本地工作拷贝中.
(7)写回主内存.

注意原子型操作只限于第1步到第2步的读取以及第6到第7步的写, i的值还是可能被同时执行i=i+1的多线程中断打扰(在第4步)。

double 和long 变量是非原子型的(non-atomic)。数组是object 非原子型。

14. 由于13条的原因,我们解决办法是:

class xxx extends Thread{

//i会被经常修改
private int i;

public synchronized int read(){ return i;}

public synchronized void update(){ i = i + 1;}

..........

}

15. Volatile变量, volatile变量表示保证它必须是与主内存保持一致,它实际是"变量的同步", 也就是说对于volatile变量的操作是原子型的,如用在long 或 double变量前。

16. 使用yield()会自动放弃CPU,有时比sleep更能提升性能。

17. sleep()和wait()的区别是:wait()方法被调用时会解除锁定,但是我们能使用它的地方只是在一个同步的方法或代码块内。

18. 通过制造缩小同步范围,尽可能的实现代码块同步,wait(毫秒数)可在指定的毫秒数可退出wait;对于wait()需要被notisfy()或notifyAll()踢醒。

19. 构造两个线程之间实时通信的方法分几步:
(1). 创建一个PipedWriter和一个PipedReader和它们之间的管道;
PipedReader in = new PipedReader(new PipedWriter())
(2). 在需要发送信息的线程开始之前,将外部的PipedWriter导向给其内部的Writer实例out
(3). 在需要接受信息的线程开始之前,将外部的PipedReader导向给其内部的Reader实例in
(4). 这样放入out的所有东西度可从in中提取出来。

20. synchronized带来的问题除性能有所下降外,最大的缺点是会带来死锁DeadLock,只有通过谨慎设计来防止死锁,其他毫无办法,这也是线程难以驯服的一个原因。不要再使用stop() suspend() resume()和destory()方法

21. 在大量线程被堵塞时,最高优先级的线程先运行。但是不表示低级别线程不会运行,运行概率小而已。

22. 线程组的主要优点是:使用单个命令可完成对整个线程组的操作。很少需要用到线程组。

23. 从以下几个方面提升多线程的性能:

检查所有可能Block的地方,尽可能的多的使用sleep或yield()以及wait();

尽可能延长sleep(毫秒数)的时间;

运行的线程不用超过100个,不能太多;

不同平台linux或windows以及不同JVM运行性能差别很大。

24. 推荐几篇相关英文文章:

posted @ 2007-10-22 10:21 华梦行 阅读(228) | 评论 (1)编辑 收藏
 名称 Java语言编码规范(Java Code Conventions)
 译者 晨光(Morning)
 简介 本文档讲述了Java语言的编码规范,较之陈世忠先生《c++编码规范》的浩繁详尽,此文当属短小精悍了。而其中所列之各项条款,从编码风格,到注意事项,不单只Java,对于其他语言,也都很有借鉴意义。因为简短,所以易记,大家不妨将此作为handbook,常备案头,逐一对验。
 声明 如需复制、传播,请附上本声明,谢谢。
原文出处:http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html,
译文出处:http://morningspace.51.net/,moyingzz@etang.com

 目录

1 介绍 2 文件名 3 文件组织 4 缩进排版 5 注释 6 声明 7 语句 8 空白 9 命名规范
10 编程惯例 11 代码范例

1 介绍(Introduction)

1.1 为什么要有编码规范(Why Have Code Conventions)

编码规范对于程序员而言尤为重要,有以下几个原因:

- 一个软件的生命周期中,80%的花费在于维护
- 几乎没有任何一个软件,在其整个生命周期中,均由最初的开发人员来维护
- 编码规范可以改善软件的可读性,可以让程序员尽快而彻底地理解新的代码
- 如果你将源码作为产品发布,就需要确任它是否被很好的打包并且清晰无误,一如你已构建的其它任何产品

为了执行规范,每个软件开发人员必须一致遵守编码规范。每个人。

1.2 版权声明(Acknowledgments)

本文档反映的是Sun MicroSystems公司,Java语言规范中的编码标准部分。主要贡献者包括:Peter King,Patrick Naughton,Mike DeMoney,Jonni Kanerva,Kathy Walrath以及Scott Hommel。

本文档现由Scott Hommel维护,有关评论意见请发至shommel@eng.sun.com

2 文件名(File Names)

这部分列出了常用的文件名及其后缀。

2.1 文件后缀(File Suffixes)

Java程序使用下列文件后缀:

文件类别 文件后缀
Java源文件 .java
Java字节码文件 .class

2.2 常用文件名(Common File Names)

常用的文件名包括:

文件名 用途
GNUmakefile makefiles的首选文件名。我们采用gnumake来创建(build)软件。
README 概述特定目录下所含内容的文件的首选文件名

3 文件组织(File Organization)

一个文件由被空行分割而成的段落以及标识每个段落的可选注释共同组成。超过2000行的程序难以阅读,应该尽量避免。"Java源文件范例"提供了一个布局合理的Java程序范例。

3.1 Java源文件(Java Source Files)

每个Java源文件都包含一个单一的公共类或接口。若私有类和接口与一个公共类相关联,可以将它们和公共类放入同一个源文件。公共类必须是这个文件中的第一个类或接口。

Java源文件还遵循以下规则:

- 开头注释(参见"开头注释")
- 包和引入语句(参见"包和引入语句")
- 类和接口声明(参见"类和接口声明")

3.1.1 开头注释(Beginning Comments)

所有的源文件都应该在开头有一个C语言风格的注释,其中列出类名、版本信息、日期和版权声明:

  /*
   * Classname
   *
   * Version information
   *
   * Date
   *
   * Copyright notice
   */
	  

3.1.2 包和引入语句(Package and Import Statements)

在多数Java源文件中,第一个非注释行是包语句。在它之后可以跟引入语句。例如:

  package java.awt;

  import java.awt.peer.CanvasPeer;
	  

3.1.3 类和接口声明(Class and Interface Declarations)

下表描述了类和接口声明的各个部分以及它们出现的先后次序。参见"Java源文件范例"中一个包含注释的例子。

  类/接口声明的各部分 注解
1 类/接口文档注释(/**……*/) 该注释中所需包含的信息,参见"文档注释"
2 类或接口的声明  
3 类/接口实现的注释(/*……*/)如果有必要的话 该注释应包含任何有关整个类或接口的信息,而这些信息又不适合作为类/接口文档注释。
4 类的(静态)变量 首先是类的公共变量,随后是保护变量,再后是包一级别的变量(没有访问修饰符,access modifier),最后是私有变量。
5 实例变量 首先是公共级别的,随后是保护级别的,再后是包一级别的(没有访问修饰符),最后是私有级别的。
6 构造器  
7 方法 这些方法应该按功能,而非作用域或访问权限,分组。例如,一个私有的类方法可以置于两个公有的实例方法之间。其目的是为了更便于阅读和理解代码。

4 缩进排版(Indentation)

4个空格常被作为缩进排版的一个单位。缩进的确切解释并未详细指定(空格 vs. 制表符)。一个制表符等于8个空格(而非4个)。

4.1 行长度(Line Length)

尽量避免一行的长度超过80个字符,因为很多终端和工具不能很好处理之。

注意:用于文档中的例子应该使用更短的行长,长度一般不超过70个字符。

4.2 换行(Wrapping Lines)

当一个表达式无法容纳在一行内时,可以依据如下一般规则断开之:

- 在一个逗号后面断开
- 在一个操作符前面断开
- 宁可选择较高级别(higher-level)的断开,而非较低级别(lower-level)的断开
- 新的一行应该与上一行同一级别表达式的开头处对齐
- 如果以上规则导致你的代码混乱或者使你的代码都堆挤在右边,那就代之以缩进8个空格。

以下是断开方法调用的一些例子:

  someMethod(longExpression1, longExpression2, longExpression3, 
                   longExpression4, longExpression5);

  var = someMethod1(longExpression1, 
                            someMethod2(longExpression2, 
                                               longExpression3));
	  

以下是两个断开算术表达式的例子。前者更好,因为断开处位于括号表达式的外边,这是个较高级别的断开。

  longName1 = longName2 * (longName3 + longName4 - longName5)
                     + 4 * longname6; //PREFFER

  longName1 = longName2 * (longName3 + longName4 
                                         - longName5) + 4 * longname6; //AVOID
	  

以下是两个缩进方法声明的例子。前者是常规情形。后者若使用常规的缩进方式将会使第二行和第三行移得很靠右,所以代之以缩进8个空格

  //CONVENTIONAL INDENTATION
  someMethod(int anArg, Object anotherArg, String yetAnotherArg, 
                    Object andStillAnother) {
    ...
  }

  //INDENT 8 SPACES TO AVOID VERY DEEP INDENTS
  private static synchronized horkingLongMethodName(int anArg,
          Object anotherArg, String yetAnotherArg,
          Object andStillAnother) {
    ...
  }
	  

if语句的换行通常使用8个空格的规则,因为常规缩进(4个空格)会使语句体看起来比较费劲。比如:

  //DON’T USE THIS INDENTATION
  if ((condition1 && condition2)
      || (condition3 && condition4)
      ||!(condition5 && condition6)) { //BAD WRAPS
      doSomethingAboutIt();             //MAKE THIS LINE EASY TO MISS
  }

  //USE THIS INDENTATION INSTEAD
  if ((condition1 && condition2)
          || (condition3 && condition4)
          ||!(condition5 && condition6)) {
      doSomethingAboutIt();
  }

  //OR USE THIS
  if ((condition1 && condition2) || (condition3 && condition4)
          ||!(condition5 && condition6)) {
      doSomethingAboutIt();
  }
	  

这里有三种可行的方法用于处理三元运算表达式:

  alpha = (aLongBooleanExpression) ? beta : gamma;

  alpha = (aLongBooleanExpression) ? beta
                                   : gamma;

  alpha = (aLongBooleanExpression)
          ? beta
          : gamma;
	  

5 注释(Comments)

Java程序有两类注释:实现注释(implementation comments)和文档注释(document comments)。实现注释是那些在C++中见过的,使用/*...*/和//界定的注释。文档注释(被称为"doc comments")是Java独有的,并由/**...*/界定。文档注释可以通过javadoc工具转换成HTML文件。

实现注释用以注释代码或者实现细节。文档注释从实现自由(implementation-free)的角度描述代码的规范。它可以被那些手头没有源码的开发人员读懂。

注释应被用来给出代码的总括,并提供代码自身没有提供的附加信息。注释应该仅包含与阅读和理解程序有关的信息。例如,相应的包如何被建立或位于哪个目录下之类的信息不应包括在注释中。

在注释里,对设计决策中重要的或者不是显而易见的地方进行说明是可以的,但应避免提供代码中己清晰表达出来的重复信息。多余的的注释很容易过时。通常应避免那些代码更新就可能过时的注释。

注意:频繁的注释有时反映出代码的低质量。当你觉得被迫要加注释的时候,考虑一下重写代码使其更清晰。

注释不应写在用星号或其他字符画出来的大框里。注释不应包括诸如制表符和回退符之类的特殊字符。

5.1 实现注释的格式(Implementation Comment Formats)

程序可以有4种实现注释的风格:块(block)、单行(single-line)、尾端(trailing)和行末(end-of-line)。

5.1.1 块注释(Block Comments)

块注释通常用于提供对文件,方法,数据结构和算法的描述。块注释被置于每个文件的开始处以及每个方法之前。它们也可以被用于其他地方,比如方法内部。在功能和方法内部的块注释应该和它们所描述的代码具有一样的缩进格式。

块注释之首应该有一个空行,用于把块注释和代码分割开来,比如:

  /*
   * Here is a block comment.
   */
	  

块注释可以以/*-开头,这样indent(1)就可以将之识别为一个代码块的开始,而不会重排它。

  /*-
    * Here is a block comment with some very special
    * formatting that I want indent(1) to ignore.
    *
    *    one
    *        two
    *            three
    */
	  

注意:如果你不使用indent(1),就不必在代码中使用/*-,或为他人可能对你的代码运行indent(1)作让步。

参见"文档注释"

5.1.2 单行注释(Single-Line Comments)

短注释可以显示在一行内,并与其后的代码具有一样的缩进层级。如果一个注释不能在一行内写完,就该采用块注释(参见"块注释")。单行注释之前应该有一个空行。以下是一个Java代码中单行注释的例子:

  if (condition) {

    /* Handle the condition. */
    ...
  }
	  

5.1.3 尾端注释(Trailing Comments)

极短的注释可以与它们所要描述的代码位于同一行,但是应该有足够的空白来分开代码和注释。若有多个短注释出现于大段代码中,它们应该具有相同的缩进。

以下是一个Java代码中尾端注释的例子:

  if (a == 2) {
      return TRUE;              /* special case */
  } else {
      return isPrime(a);         /* works only for odd a */
  }
	  

5.1.4 行末注释(End-Of-Line Comments)

注释界定符"//",可以注释掉整行或者一行中的一部分。它一般不用于连续多行的注释文本;然而,它可以用来注释掉连续多行的代码段。以下是所有三种风格的例子:

  if (foo > 1) {

      // Do a double-flip.
      ...
  }
  else {
      return false;          // Explain why here.
  }

  //if (bar > 1) {
  //
  //    // Do a triple-flip.
  //    ...
  //}
  //else {
  //    return false;
  //}
	  

5.2 文档注释(Documentation Comments)

注意:此处描述的注释格式之范例,参见"Java源文件范例"

若想了解更多,参见"How to Write Doc Comments for Javadoc",其中包含了有关文档注释标记的信息(@return, @param, @see):

http://java.sun.com/javadoc/writingdoccomments/index.html

若想了解更多有关文档注释和javadoc的详细资料,参见javadoc的主页:

http://java.sun.com/javadoc/index.html

文档注释描述Java的类、接口、构造器,方法,以及字段(field)。每个文档注释都会被置于注释定界符/**...*/之中,一个注释对应一个类、接口或成员。该注释应位于声明之前:

  /**
    * The Example class provides ...
    */
  public class Example { ...
	  

注意顶层(top-level)的类和接口是不缩进的,而其成员是缩进的。描述类和接口的文档注释的第一行(/**)不需缩进;随后的文档注释每行都缩进1格(使星号纵向对齐)。成员,包括构造函数在内,其文档注释的第一行缩进4格,随后每行都缩进5格。

若你想给出有关类、接口、变量或方法的信息,而这些信息又不适合写在文档中,则可使用实现块注释(见5.1.1)或紧跟在声明后面的单行注释(见5.1.2)。例如,有关一个类实现的细节,应放入紧跟在类声明后面的实现块注释中,而不是放在文档注释中。

文档注释不能放在一个方法或构造器的定义块中,因为Java会将位于文档注释之后的第一个声明与其相关联。

6 声明(Declarations)

6.1 每行声明变量的数量(Number Per Line)

推荐一行一个声明,因为这样以利于写注释。亦即,

  int level;  // indentation level
  int size;   // size of table
	  

要优于,

int level, size;

不要将不同类型变量的声明放在同一行,例如:

  int foo,  fooarray[];   //WRONG!
	  

注意:上面的例子中,在类型和标识符之间放了一个空格,另一种被允许的替代方式是使用制表符:

  int		level;         // indentation level
  int		size;          // size of table
  Object	currentEntry;  // currently selected table entry
	  

6.2 初始化(Initialization)

尽量在声明局部变量的同时初始化。唯一不这么做的理由是变量的初始值依赖于某些先前发生的计算。

6.3 布局(Placement)

只在代码块的开始处声明变量。(一个块是指任何被包含在大括号"{"和"}"中间的代码。)不要在首次用到该变量时才声明之。这会把注意力不集中的程序员搞糊涂,同时会妨碍代码在该作用域内的可移植性。

  void myMethod() {
      int int1 = 0;         // beginning of method block

      if (condition) {
          int int2 = 0;     // beginning of "if" block
          ...
      }
  }
	  

该规则的一个例外是for循环的索引变量

  for (int i = 0; i < maxLoops; i++) { ... }
	  

避免声明的局部变量覆盖上一级声明的变量。例如,不要在内部代码块中声明相同的变量名:

  int count;
  ...
  myMethod() {
      if (condition) {
          int count = 0;     // AVOID!
          ...
      }
      ...
  }
	  

6.4 类和接口的声明(Class and Interface Declarations)

当编写类和接口是,应该遵守以下格式规则:

- 在方法名与其参数列表之前的左括号"("间不要有空格
- 左大括号"{"位于声明语句同行的末尾
- 右大括号"}"另起一行,与相应的声明语句对齐,除非是一个空语句,"}"应紧跟在"{"之后

  class Sample extends Object {
      int ivar1;
      int ivar2;

      Sample(int i, int j) {
          ivar1 = i;
          ivar2 = j;
      }

      int emptyMethod() {}

      ...
  }
	  
- 方法与方法之间以空行分隔

7 语句(Statements)

7.1 简单语句(Simple Statements)

每行至多包含一条语句,例如:

  argv++;       // Correct
  argc--;       // Correct
  argv++; argc--;       // AVOID!
	  

7.2 复合语句(Compound Statements)

复合语句是包含在大括号中的语句序列,形如"{ 语句 }"。例如下面各段。

- 被括其中的语句应该较之复合语句缩进一个层次
- 左大括号"{"应位于复合语句起始行的行尾;右大括号"}"应另起一行并与复合语句首行对齐。
- 大括号可以被用于所有语句,包括单个语句,只要这些语句是诸如if-else或for控制结构的一部分。这样便于添加语句而无需担心由于忘了加括号而引入bug。

7.3 返回语句(return Statements)

一个带返回值的return语句不使用小括号"()",除非它们以某种方式使返回值更为显见。例如:

  return;

  return myDisk.size();

  return (size ? size : defaultSize);
	  

7.4 if,if-else,if else-if else语句(if, if-else, if else-if else Statements)

if-else语句应该具有如下格式:

  if (condition) {
      statements;
  }

  if (condition) {
      statements;
  } else {
      statements;
  }

  if (condition) {
      statements;
  } else if (condition) {
      statements;
  } else{
      statements;
  }
	  

注意:if语句总是用"{"和"}"括起来,避免使用如下容易引起错误的格式:

  if (condition) //AVOID! THIS OMITS THE BRACES {}!
      statement;
	  

7.5 for语句(for Statements)

一个for语句应该具有如下格式:

  for (initialization; condition; update) {
      statements;
  }
	  

一个空的for语句(所有工作都在初始化,条件判断,更新子句中完成)应该具有如下格式:

  for (initialization; condition; update);
	  

当在for语句的初始化或更新子句中使用逗号时,避免因使用三个以上变量,而导致复杂度提高。若需要,可以在for循环之前(为初始化子句)或for循环末尾(为更新子句)使用单独的语句。

7.6 while语句(while Statements)

一个while语句应该具有如下格式

  while (condition) {
      statements;
  }
	  

一个空的while语句应该具有如下格式:

  while (condition);
	  

7.7 do-while语句(do-while Statements)

一个do-while语句应该具有如下格式:

  do {
      statements;
  } while (condition);
	  

7.8 switch语句(switch Statements)

一个switch语句应该具有如下格式:

  switch (condition) {
  case ABC:
      statements;
      /* falls through */
  case DEF:
      statements;
      break;

  case XYZ:
      statements;
      break;

  default:
      statements;
      break;
  }
	  

每当一个case顺着往下执行时(因为没有break语句),通常应在break语句的位置添加注释。上面的示例代码中就包含注释/* falls through */。

7.9 try-catch语句(try-catch Statements)

一个try-catch语句应该具有如下格式:

  try {
      statements;
  } catch (ExceptionClass e) {
      statements;
  }
	  

一个try-catch语句后面也可能跟着一个finally语句,不论try代码块是否顺利执行完,它都会被执行。

  try {
      statements;
  } catch (ExceptionClass e) {
      statements;
  } finally {
      statements;
  }
	  

8 空白(White Space)

8.1 空行(Blank Lines)

空行将逻辑相关的代码段分隔开,以提高可读性。

下列情况应该总是使用两个空行:

- 一个源文件的两个片段(section)之间
- 类声明和接口声明之间

下列情况应该总是使用一个空行:

- 两个方法之间
- 方法内的局部变量和方法的第一条语句之间
- 块注释(参见"5.1.1")或单行注释(参见"5.1.2")之前
- 一个方法内的两个逻辑段之间,用以提高可读性

8.2 空格(Blank Spaces)

下列情况应该使用空格:

- 一个紧跟着括号的关键字应该被空格分开,例如:

  while (true) {
      ...
  }
	  
注意:空格不应该置于方法名与其左括号之间。这将有助于区分关键字和方法调用。
- 空白应该位于参数列表中逗号的后面
- 所有的二元运算符,除了".",应该使用空格将之与操作数分开。一元操作符和操作数之间不因该加空格,比如:负号("-")、自增("++")和自减("--")。例如:
    a += c + d;
    a = (a + b) / (c * d);

    while (d++ = s++) {
        n++;
    }
    printSize("size is " + foo + "\n");
	  
- for语句中的表达式应该被空格分开,例如:
    for (expr1; expr2; expr3)
	  
- 强制转型后应该跟一个空格,例如:
    myMethod((byte) aNum, (Object) x);
    myMethod((int) (cp + 5), ((int) (i + 3)) + 1);
	  

9 命名规范(Naming Conventions)

命名规范使程序更易读,从而更易于理解。它们也可以提供一些有关标识符功能的信息,以助于理解代码,例如,不论它是一个常量,包,还是类。

标识符类型命名规则例子
包(Packages)一个唯一包名的前缀总是全部小写的ASCII字母并且是一个顶级域名,通常是com,edu,gov,mil,net,org,或1981年ISO 3166标准所指定的标识国家的英文双字符代码。包名的后续部分根据不同机构各自内部的命名规范而不尽相同。这类命名规范可能以特定目录名的组成来区分部门(department),项目(project),机器(machine),或注册名(login names)。com.sun.eng
com.apple.quicktime.v2
edu.cmu.cs.bovik.cheese
类(Classes)命名规则:类名是个一名词,采用大小写混合的方式,每个单词的首字母大写。尽量使你的类名简洁而富于描述。使用完整单词,避免缩写词(除非该缩写词被更广泛使用,像URL,HTML)class Raster;
class ImageSprite;
接口(Interfaces)命名规则:大小写规则与类名相似interface RasterDelegate;
interface Storing;
方法(Methods)方法名是一个动词,采用大小写混合的方式,第一个单词的首字母小写,其后单词的首字母大写。run();
runFast();
getBackground();
变量(Variables)除了变量名外,所有实例,包括类,类常量,均采用大小写混合的方式,第一个单词的首字母小写,其后单词的首字母大写。变量名不应以下划线或美元符号开头,尽管这在语法上是允许的。
变量名应简短且富于描述。变量名的选用应该易于记忆,即,能够指出其用途。尽量避免单个字符的变量名,除非是一次性的临时变量。临时变量通常被取名为i,j,k,m和n,它们一般用于整型;c,d,e,它们一般用于字符型。
char c;
int i;
float myWidth;
实例变量(Instance Variables)大小写规则和变量名相似,除了前面需要一个下划线int _employeeId;
String _name;
Customer _customer;
常量(Constants)类常量和ANSI常量的声明,应该全部大写,单词间用下划线隔开。(尽量避免ANSI常量,容易引起错误)static final int MIN_WIDTH = 4;
static final int MAX_WIDTH = 999;
static final int GET_THE_CPU = 1;

10 编程惯例(Programming Practices)

10.1 提供对实例以及类变量的访问控制(Providing Access to Instance and Class Variables)

若没有足够理由,不要把实例或类变量声明为公有。通常,实例变量无需显式的设置(set)和获取(gotten),通常这作为方法调用的边缘效应 (side effect)而产生。

一个具有公有实例变量的恰当例子,是类仅作为数据结构,没有行为。亦即,若你要使用一个结构(struct)而非一个类(如果java支持结构的话),那么把类的实例变量声明为公有是合适的。

10.2 引用类变量和类方法(Referring to Class Variables and Methods)

避免用一个对象访问一个类的静态变量和方法。应该用类名替代。例如:

  classMethod();             //OK
  AClass.classMethod();      //OK
  anObject.classMethod();    //AVOID!
	  

10.3 常量(Constants)

位于for循环中作为计数器值的数字常量,除了-1,0和1之外,不应被直接写入代码。

10.4 变量赋值(Variable Assignments)

避免在一个语句中给多个变量赋相同的值。它很难读懂。例如:

  fooBar.fChar = barFoo.lchar = 'c'; // AVOID!
	  

不要将赋值运算符用在容易与相等关系运算符混淆的地方。例如:

  if (c++ = d++) {        // AVOID! (Java disallows)
      ...
  }
	  

应该写成

  if ((c++ = d++) != 0) {
    ...
  }
	  

不要使用内嵌(embedded)赋值运算符试图提高运行时的效率,这是编译器的工作。例如:

  d = (a = b + c) + r;        // AVOID!
	  

应该写成

  a = b + c;
  d = a + r;
	  

10.5 其它惯例(Miscellaneous Practices)

10.5.1 圆括号(Parentheses)

一般而言,在含有多种运算符的表达式中使用圆括号来避免运算符优先级问题,是个好方法。即使运算符的优先级对你而言可能很清楚,但对其他人未必如此。你不能假设别的程序员和你一样清楚运算符的优先级。

  if (a == b && c == d)     // AVOID!
  if ((a == b) && (c == d))  // RIGHT
	  

10.5.2 返回值(Returning Values)

设法让你的程序结构符合目的。例如:

  if (booleanExpression) {
      return true;
  } else {
      return false;
  }
	  

应该代之以如下方法:

  return booleanExpression;
	  

类似地:

  if (condition) {
      return x;
  }
  return y;
	  

应该写做:

  return (condition ? x : y);
	  

10.5.3 条件运算符"?"前的表达式(Expressions before '?' in the Conditional Operator)

如果一个包含二元运算符的表达式出现在三元运算符" ? : "的"?"之前,那么应该给表达式添上一对圆括号。例如:

  (x >= 0) ? x : -x;
	  

10.5.4 特殊注释(Special Comments)

在注释中使用XXX来标识某些未实现(bogus)的但可以工作(works)的内容。用FIXME来标识某些假的和错误的内容。

11 代码范例(Code Examples)

11.1 Java源文件范例(Java Source File Example)

下面的例子,展示了如何合理布局一个包含单一公共类的Java源程序。接口的布局与其相似。更多信息参见"类和接口声明"以及"文挡注释"。

/*
 * @(#)Blah.java        1.82 99/03/18
 *
 * Copyright (c) 1994-1999 Sun Microsystems, Inc.
 * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of Sun
 * Microsystems, Inc. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Sun.
 */


package java.blah;

import java.blah.blahdy.BlahBlah;

/**
 * Class description goes here.
 *
 * @version 	1.82 18 Mar 1999
 * @author 	Firstname Lastname
 */
public class Blah extends SomeClass {
    /* A class implementation comment can go here. */

    /** classVar1 documentation comment */
    public static int classVar1;

    /**
     * classVar2 documentation comment that happens to be
     * more than one line long
     */
    private static Object classVar2;

    /** instanceVar1 documentation comment */
    public Object instanceVar1;

    /** instanceVar2 documentation comment */
    protected int instanceVar2;

    /** instanceVar3 documentation comment */
    private Object[] instanceVar3;

    /**
     * ...constructor Blah documentation comment...
     */
    public Blah() {
        // ...implementation goes here...
    }

    /**
     * ...method doSomething documentation comment...
     */
    public void doSomething() {
        // ...implementation goes here...
    }

    /**
     * ...method doSomethingElse documentation comment...
     * @param someParam description
     */
    public void doSomethingElse(Object someParam) {
        // ...implementation goes here...
    }
}
	  
posted @ 2007-10-15 11:21 华梦行 阅读(127) | 评论 (0)编辑 收藏
世界上并没有成为高手的捷径,但一些基本原则是可以遵循的。  

1、扎实的基础  


  数据结构、离散数学、编译原理,这些是所有计算机科学的基础,如果不掌握它们,很难写出高水平的程序。程序人人都会写,但当你发现写到一定程度很难再提高的时候,就应该想想是不是要回过头来学学这些最基本的理论。不要一开始就去学OOP,即使你再精通OOP,遇到一些基本算法的时候可能也会束手无策。因此多读一些计算机基础理论方面的书籍是非常有必要的。  


2、丰富的想像力  


  不要拘泥于固定的思维方式,遇到问题的时候要多想几种解决问题的方案,试试别人从没想过的方法。丰富的想像力是建立在丰富的知识的基础上,除计算机以外,多涉猎其他的学科,比如天文、物理、数学等等。开阔的思维对程序员来说很重要。  


3、最简单的是最好的  


  这也许是所有科学都遵循的一条准则,复杂的质能转换原理在爱因斯坦眼里不过是一个简单得不能再简单的公式:E=mc2。简单的方法更容易被人理解,更容易实现,也更容易维护。遇到问题时要优先考虑最简单的方案,只有简单方案不能满足要求时再考虑复杂的方案。  


4、不钻牛角尖  


  当你遇到障碍的时候,不妨暂时远离电脑,看看窗外的风景,听听轻音乐,和朋友聊聊天。当我遇到难题的时候会去玩游戏,当负责游戏的那部分大脑细胞极度亢奋的时候,负责编程的那部分大脑细胞就得到了充分的休息。当重新开始工作的时候,我会发现那些难题现在竟然可以迎刃而解。  


  5、对答案的渴求  


  人类自然科学的发展史就是一个渴求得到答案的过程,即使只能知道答案的一小部分也值得我们去付出。只要你坚定信念,一定要找到问题的答案,你才会付出精力去探索,即使最后没有得到答案,在过程中你也会学到很多东西。  


  6、多与别人交流  


  三人行必有我师,也许在一次和别人不经意的谈话中,就可以迸出灵感的火花。多上上网,看看别人对同一问题的看法,会给你很大的启发。  


  7、良好的编程风格  


  注意养成良好的习惯,代码的缩进编排,变量的命名规则要始终保持一致。大家都知道如何排除代码中错误,却往往忽视了对注释的排错。注释是程序的一个重要组成部分,它可以使你的代码更容易理解,而如果代码已经清楚地表达了你的思想,就不必再加注释了,如果注释和代码不一致,那就更加糟糕。  


  8、韧性和毅力  


  这也许是“高手”和一般程序员最大的区别。高手们并不是天才,他们是在无数个日日夜夜中磨炼出来的。成功能给我们带来无比的喜悦,但过程却是无比的枯燥乏味。你不妨做个测试,找个10000以内的素数表,把它们全都抄下来,然后再检查三遍,如果能够不间断地完成这一工作,你就可以满足这一条。
posted @ 2007-10-15 11:09 华梦行 阅读(97) | 评论 (0)编辑 收藏
select S_10994_1_SYS_MODELTYPE.nextval,a.typeid,'SYS','Mail_Forward','邮件转发模板','Mail Forward Model' from
bse_organization a where not exists (select orgtypeid from SYS_MODELTYPE b where b.orgtypeid=a.typeid and modelcode ='Mail_Forward')
--

select S_10994_1_SYS_MODELTYPE.nextval,typeid,'SYS','Mail_Forward','邮件转发模板','Mail Forward Model' from
bse_organization where typeid not in (
select orgtypeid from SYS_MODELTYPE where modelcode='Mail_Forward'
)

请注意not in 逻辑上不完全等同于not exists,如果你误用了not in,小心你的程序存在致命的BUG:


请看下面的例子:
create table t1 (c1 number,c2 number);
create table t2 (c1 number,c2 number);

insert into t1 values (1,2);
insert into t1 values (1,3);
insert into t2 values (1,2);
insert into t2 values (1,null);

select * from t1 where c2 not in (select c2 from t2);
no rows found
select * from t1 where not exists (select 1 from t2 where t1.c2=t2.c2);
c1 c2
1 3

正如所看到的,not in 出现了不期望的结果集,存在逻辑错误。如果看一下上述两个select语句的执行计划,也会不同。后者使用了hash_aj。
因此,请尽量不要使用not in(它会调用子查询),而尽量使用not exists(它会调用关联子查询)。如果子查询中返回的任意一条记录含有空值,则查询将不返回任何记录,正如上面例子所示。
除非子查询字段有非空限制,这时可以使用not in ,并且也可以通过提示让它使用hasg_aj或merge_aj连接。

posted @ 2007-10-11 16:51 华梦行 阅读(174) | 评论 (0)编辑 收藏

public class PlatContextListener implements ServletContextListener{
    private Timer timer = null;
    /** 应用程序的根目录 */
    private static String contextPath = null;
   
    /** Creates a new instance of PlatContextListener */
    public PlatContextListener() {
    }

    public void contextInitialized(ServletContextEvent sce) {
        ServletContext sc = sce.getServletContext();
        contextPath = sc.getRealPath("").replace("\\", "/");
       
        int nInterval=24*60*60*1000;
       
        timer = new Timer(true);
       
        //每天零晨3点执行清理工作
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY,3);
        calendar.set(Calendar.MINUTE,0);
        calendar.set(Calendar.SECOND,0);
        Date time = calendar.getTime();
   
        timer.scheduleAtFixedRate(new PlatTimerClearTask(), time,nInterval);
       

    }

    public void contextDestroyed(ServletContextEvent sce) {
        timer.cancel();
    }
   
    public static String getContextPath() {
        return contextPath;
    }
   
}

posted @ 2007-10-11 14:50 华梦行 阅读(156) | 评论 (0)编辑 收藏

在 IE6 中,可以很方便地利用 img 的 src 属性,实现本地图片预览,然而在 IE7 中,这种办法却行不通。需要用 AlphaImageLoader


AlphaImageLoader

说明:

在对象容器边界内,在对象的背景和内容之间显示一张图片。并提供对此图片的剪切和改变尺寸的操作。如果载入的是PNG(Portable Network Graphics)格式,则0%-100%的透明度也被提供。

语法:

filter : progid:DXImageTransform.Microsoft.AlphaImageLoader ( enabled=bEnabled , sizingMethod=sSize , src=sURL )

enabled:可选项。布尔值(Boolean)。设置或检索滤镜是否激活。
true:默认值。滤镜激活。
false:滤镜被禁止。

sizingMethod:可选项。字符串(String)。设置或检索滤镜作用的对象的图片在对象容器边界内的显示方式。
crop:剪切图片以适应对象尺寸。
image:默认值。增大或减小对象的尺寸边界以适应图片的尺寸。
scale:缩放图片以适应对象的尺寸边界。

src:必选项。字符串(String)。使用绝对或相对 url 地址指定背景图像。假如忽略此参数,滤镜将不会作用。


具体操作:

  1. 为预览区域(比如要在某个 div 中预览)添加样式:filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale);。
  2. 为 AlphaImageLoader 设置 src 属性。

 

示例代码:

<? xml version="1.0" encoding="gb2312" ?>
<! DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" >
< html  xmlns ="http://www.w3.org/1999/xhtml" >

< head >
< meta  http-equiv ="Content-Type"  content ="text/html; charset=gb2312"   />
< title > 本地图片预览代码(支持 IE6、IE7) </ title >
< style  type ="text/css" >
#newPreview
{
    filter
: progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale) ;
}

</ style >
< script  type ="text/javascript"  language ="javascript" >
<!--
function  PreviewImg(imgFile)
{
    
// 原来的预览代码,不支持 IE7。
     var  oldPreview  =  document.getElementById( " oldPreview " );
    oldPreview.innerHTML 
=   " <img src=\ " file:\\\\ "  + imgFile.value +  " \ "  width=\ " 80 \ "  height=\ " 60 \ "  /> " ;
    
    
// 新的预览代码,支持 IE6、IE7。
     var  newPreview  =  document.getElementById( " newPreview " );
    newPreview.filters.item(
" DXImageTransform.Microsoft.AlphaImageLoader " ).src  =  imgFile.value;
    newPreview.style.width 
=   " 80px " ;
    newPreview.style.height 
=   " 60px " ;
}

-->
</ script >
</ head >

< body >

< p > 说明:以下针对的是互联网情况,如果您在本地作测试,比如输入的地址是:http://127.0.0.1/,则可以看到全部预览。 </ p >

< hr  />

< p > 如果您使用的是 IE6,则可以看到以下预览;如果您使用的是 IE7,则看不到以下预览。 </ p >
< div  id ="oldPreview" ></ div >

< hr  />

< p > 不论您使用的是 IE6 还是 IE7,均可以看到以下预览。 </ p >
< div  id ="newPreview" ></ div >

< hr  />

< p > 请选择一个图片进行预览: < input  type ="file"  size ="20"  onchange ="javascript:PreviewImg(this);"   /></ p >

</ body >

</ html >
posted @ 2007-09-29 13:16 华梦行 阅读(2702) | 评论 (4)编辑 收藏
<!--#include file = conn.asp -->
posted @ 2007-09-25 14:55 华梦行 阅读(110) | 评论 (0)编辑 收藏
Oracle  查询如果  a.typeName=a.typeName  查出所有的a.typeName  不为null的记录。这点一定要注意null问题
posted @ 2007-09-14 18:19 华梦行 阅读(120) | 评论 (0)编辑 收藏

异常控制

  Author: 江南白衣

1.业务异常类

      1.所以业务异常类派生于BusinessException基类。

      2.原则上,要进行相同处理的异常分为一类,用ERROR_CODE标识不同。

      3.出错信息统一写在errors.properties,以ERROR_CODE为主键,支持i18N,由基类提供默认的getMessage()函数。

      参考BussinessException.java和OrderException.java。

2.Servlet规范里的异常控制

 2.1按error-code统一定义错误页面

<error-page>
  <error-code>404</error-code>
  <location>/404.jsp</location>
</error-page>
<error-page>
  <error-code>500</error-code>
  <location>/error.jsp</location>
</error-page>

2.2按异常类型定义单独错误页面

<error-page>
  <exception-type>org.sprngside.bookstore.UserNotFound</exception-type>
  <location>/userNotFound.jsp</location>
</error-page>

2.3 在JSP里单独定义错误页面

<@ errorPage="error.jsp">

3.Spring MVC里的异常控制

   spring-mvc可在xxx-serverlet.xml里定义default和 按Excepiton类型影射的错误页面, 和Servlet规范比,主要作了Spring特色的JSP路径转向和日志记录.参见bookstore-servlet.xml

  <bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="defaultErrorView" value="/error.jsp"/>
        <property name="exceptionMappings">
            <props>
                <prop key="org.springside.framework.base.BusinessException">/businessError.jsp</prop>
            </props>
        </property>
    </bean>

4. error.jsp的处理

         error.jsp会同时处理jsp,servlet,和spring抛过来的异常

         其中jsp的异常在exception 变量中.

         servlet的异常在(Exception)request.getAttribute("javax.servlet.error.exception")

         spring的异常在(Exception) request.getAttribute("exception")

         使用 (String) request.getAttribute("javax.servlet.error.request_uri")获得 request_uri
         使用 logger.error(exception.getMessage(), exception); 记录整个异常栈

posted @ 2007-09-13 11:23 华梦行 阅读(1819) | 评论 (0)编辑 收藏
http://www.cnpack.org/upgrade/cvstracnt/CVSTracNT_1.2.1_20060112.exe
posted @ 2007-09-12 18:22 华梦行 阅读(100) | 评论 (0)编辑 收藏
Class FilterChainProxy
java.lang.Objectextended by org.acegisecurity.util.FilterChainProxy
All Implemented Interfaces:
Filter, InitializingBean, ApplicationContextAware

public class FilterChainProxy
extends Object
implements Filter, InitializingBean, ApplicationContextAware

Delegates Filter requests to a list of Spring-managed beans.

通过一系列的由spring托管的beans ,代理过滤请求。
FilterChainProxy 通过在web.xml中定义的FilterToBeanProxy 被加载,FilterChainProxy 将会通过init(FilterConfig), destroy() and doFilter(ServletRequest, ServletResponse, FilterChain)调用,调用每一个在其中定义的过滤器。

The FilterChainProxy is loaded via a standard FilterToBeanProxy declaration in web.xml. FilterChainProxy will then pass init(FilterConfig), destroy() and doFilter(ServletRequest, ServletResponse, FilterChain) invocations through to each Filter defined against FilterChainProxy.

 
    FilterChainProxy  是通过一个标准的FilterInvocationDefinitionSource 来实现配置的,每个可能的FilterChainProxy应该服务的URI模式都必须进入
            第一个搭配的URI模式将会被用来定义处理请求的所有的过滤器,就是说只适配第一组过滤器,后面的其他过滤器将无效。
FilterChainProxy
is configured using a standard FilterInvocationDefinitionSource. Each possible URI pattern that FilterChainProxy should service must be entered. The first matching URI pattern located by FilterInvocationDefinitionSource for a given request will be used to define all of the Filters that apply to that request. NB: This means you must put most specific URI patterns at the top of the list, and ensure all Filters that should apply for a given URI pattern are entered against the respective entry. The FilterChainProxy will not iterate the remainder of the URI patterns to locate additional Filters. The FilterInvocationDefinitionSource described the applicable URI pattern to fire the filter chain, followed by a list of configuration attributes. Each configuration attribute's ConfigAttribute.getAttribute() corresponds to a bean name that is available from the application context.

FilterChainProxy respects normal handling of Filters that elect not to call Filter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain), in that the remainder of the origial or FilterChainProxy-declared filter chain will not be called.

It is particularly noted the Filter lifecycle mismatch between the servlet container and IoC container. As per FilterToBeanProxy JavaDocs, we recommend you allow the IoC container to manage lifecycle instead of the servlet container. By default the FilterToBeanProxy will never call this class' init(FilterConfig) and destroy() methods, meaning each of the filters defined against FilterInvocationDefinitionSource will not be called. If you do need your filters to be initialized and destroyed, please set the lifecycle initialization parameter against the FilterToBeanProxy to specify servlet container lifecycle management.

If a filter name of TOKEN_NONE is used, this allows specification of a filter pattern which should never cause any filters to fire.

posted @ 2007-09-12 16:20 华梦行 阅读(1922) | 评论 (0)编辑 收藏

四 Acegi ACL使用 

4.1 基本概念

      在google中搜索'acl'会找到很多相关的介绍,而且涉及的范围也特别广泛。ACL是(Access Control List)的缩写,顾名思义,ACL是‘访问控制列表’的意思。通俗点说,ACL保存了所有用户或角色对资源的访问权限。最典型的ACL实现是流行操作系统(window, unix)的文件访问控制系统,精确定义了某个用户或角色对某个特定文件的读、写、执行等权限,更通俗的例子是可以定义某个管理员只能管一部分的订单,而另一个管理员只能管另一部分的。

4.2 Acegi ACL配置

Acegi好早就实现了ACL(好像是0.5),但是使用起来确实有点麻烦,所以用的不是太广泛。这里简单的说明一下使用方法,希望有更多的朋友来试试。

首先要理解Acegi里面Voter的概念,ACL正是在一个Voter上扩展起来的。现来看一下AclVoter的配置。

     < bean  id ="aclBeanReadVoter"  class ="org.acegisecurity.vote.BasicAclEntryVoter" >
        
< property  name ="processConfigAttribute" >
            
< value > ACL_READ </ value >
        
</ property >
        
< property  name ="processDomainObjectClass" >
            
< value > org.springside.modules.security.acl.domain.AclDomainAware </ value >
        
</ property >
        
< property  name ="aclManager" >
            
< ref  local ="aclManager" />
        
</ property >
        
< property  name ="requirePermission" >
            
< list >
                
< ref  local ="org.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION" />
                
< ref  local ="org.acegisecurity.acl.basic.SimpleAclEntry.READ" />
            
</ list >
        
</ property >
    
</ bean >
  1. ACL_READ指的是这个Voter对哪些SecurityConfig起作用,我们可以把ACL_READ配置在想要拦截的Method上。比方说我们要拦截readOrder这个方法,以实现ACL控制,可以这样配置。
    orderManager.readOrder=ACL_READ
  2. processDomainObjectClass指出哪些DomainObject是要进行ACL校验的。
  3. aclManager是一个比较重要的概念,主要负责在权限列表中根据用户和DomainObject取得acl列表。
  4. requirePermission指出要进行这个操作必须具备的acl权限,比方说read操作就必须有ADMINISTRATION或READ两个权限。

其实整个过程看下来比较清晰,下面来看一下AclManager如何配置。

     <!--  ========= ACCESS CONTROL LIST LOOKUP MANAGER DEFINITIONS =========  -->

    
< bean  id ="aclManager"  class ="org.acegisecurity.acl.AclProviderManager" >
        
< property  name ="providers" >
            
< list >
                
< ref  local ="basicAclProvider" />
            
</ list >
        
</ property >
    
</ bean >

    
< bean  id ="basicAclProvider"  class ="org.acegisecurity.acl.basic.BasicAclProvider" >
        
< property  name ="basicAclDao" >
            
< ref  local ="basicAclExtendedDao" />
        
</ property >
    
</ bean >

    
< bean  id ="basicAclExtendedDao"  class ="org.acegisecurity.acl.basic.jdbc.JdbcExtendedDaoImpl" >
        
< property  name ="dataSource" >
            
< ref  bean ="dataSource" />
        
</ property >
    
</ bean >


很明显ACLManager继承了Acegi的一贯风格,Provider可以提供多种取得ACL访问列表的途径,默认的是用basicAclProvider在数据库中取得。既然提到了数据库,那我们就来看一下Acegi默认提供的ACL在数据库里的保存表结构:

CREATE   TABLE  acl_object_identity (
id 
IDENTITY   NOT   NULL ,
object_identity VARCHAR_IGNORECASE(
250 NOT   NULL ,
parent_object 
INTEGER ,
acl_class VARCHAR_IGNORECASE(
250 NOT   NULL ,
CONSTRAINT  unique_object_identity  UNIQUE (object_identity),
FOREIGN   KEY  (parent_object)  REFERENCES  acl_object_identity(id)
);
CREATE   TABLE  acl_permission (
id 
IDENTITY   NOT   NULL ,
acl_object_identity 
INTEGER   NOT   NULL ,
recipient VARCHAR_IGNORECASE(
100 NOT   NULL ,
mask 
INTEGER   NOT   NULL ,
CONSTRAINT  unique_recipient  UNIQUE (acl_object_identity, recipient),
FOREIGN   KEY  (acl_object_identity)  REFERENCES  acl_object_identity(id)
);
  1. acl_object_identity表存放了所有受保护的domainObject的信息。其中object_identity字段保存了domainObject的class和id,默认的保存格式是:domainClass:domainObjectId。
  2. acl_permission 就是ACL权限列表了,recipient 是用户或角色信息,mask表示了这个用户或角色对这个domainObject的访问权限。注意这些信息的保存格式都是可以根据自己的需要改变的。

这样读取和删除的时候Acegi就能很好的完成拦截工作,但是读取一个List的时候,如何才能把该用户不能操作的domainObject剔除掉呢?这就需要afterInvocationManager来完成这个工作。下面来看下配置:

     <!--  ============== "AFTER INTERCEPTION" AUTHORIZATION DEFINITIONS ===========  -->

    
< bean  id ="afterInvocationManager"  class ="org.acegisecurity.afterinvocation.AfterInvocationProviderManager" >
        
< property  name ="providers" >
            
< list >
                
< ref  local ="afterAclCollectionRead" />
            
</ list >
        
</ property >
    
</ bean >
    
<!--  Processes AFTER_ACL_COLLECTION_READ configuration settings  -->
    
< bean  id ="afterAclCollectionRead"  class ="org.acegisecurity.afterinvocation.BasicAclEntryAfterInvocationCollectionFilteringProvider" >
        
< property  name ="aclManager" >
            
< ref  local ="aclManager" />
        
</ property >
        
< property  name ="requirePermission" >
            
< list >
                
< ref  local ="org.acegisecurity.acl.basic.SimpleAclEntry.ADMINISTRATION" />
                
< ref  local ="org.acegisecurity.acl.basic.SimpleAclEntry.READ" />
            
</ list >
        
</ property >
    
</ bean >


afterAclCollectionRead会在拦截的方法执行结束的时候执行。主要的作用就是在返回的List中挨个检查domainObject的操作权限,然后根据requirePermission来剔除不符合的domainObject。

4.3 使用RuleEngine设置的ACL规则

在SpringSide里使用了RuleEngine来设置ACL规则,具体规则见
bookstore-sample\resources\rules\drl

posted @ 2007-09-12 14:46 华梦行 阅读(282) | 评论 (0)编辑 收藏
仅列出标题
共15页: First 上一页 5 6 7 8 9 10 11 12 13 下一页 Last