java编码规范

Java 编码规范

翻译:王士勇

(转载请保留作者,谢谢)

1.   引言

1.1. 为什么要编码规范

编码规范为什么是重要的?有以下一些理由:

l         一份软件80%的生命周期是维护期

l         任何软件都很难说他的整个生命周期都是由他的原始作者来维护

l         编码规范改善软件的可读性,使得软件工程师充分理解新的代码变得非常的快速。

l         如果你要把你的原码作为产品发布,你需要确保他像你的其他产品一样干净并且封装的好。

为了按照规范工作,每个人写软件的时候,都必须遵守编码规范。记住,是每个人!

1.1.1.    致谢

这本书是反映的是Java Language Specification 中关于java语言编码规范的。在这里要着重对Peter king ,Patrick Naughton,Mike DeMoney,Jonni Kanerva,Kathy Walrath,Scott Hommel表示感谢。

2.   文件名

这一节列出了本书所用的大部分文件名和后缀。

2.1. 文件后缀

        .java    java 源文件后缀

        .class     java 字节码文件后缀

2.2. 常用的文件名

        经常使用的文件名包括以下:

       GNUmakefile             首选的makefile的名字,我们使用gnumakebuild我们的软件

       README                    那些专门概述特定文件夹内容的文件的首选的名字

3.   文件的组织

一个文件的各个部分之间应该用空行隔开,并且应该用一个可选的注解来标示每个不同的部分。

文件超过2000行,是非常笨重讨厌(cumbersome)的,应该避免。

至于java 编程的正确格式的示例,请参看18页上的JAVA Source File Example(Java 原码文件示例)”

3.1. Java 源码文件

        每一个Java源码文件都包括一个唯一的public 类或interface。当私有的类和interface 都和这个public 类有关联时,你可以把它们放到这个public 类的源文件中。这个public 类或interface 应当是这个文件的第一个类或interface

 

Java 源文件有以下的顺序:

l         文件开头注解(参见第二页的“Beginning Comments(开头注解)

l          声明package 的语句和载入语句。

l         类和interface的声明(参见page 3的“Class and Interface Declarations”

3.1.1.    开头注解

       所有的源文件都应该以一个C语言风格的注解开头。这个注解应该列出类名,版本信息,日期和版权声明:

       /*

       *Classname

       *

       *Version information

       *

       * Date

*

 * copyright notice

 *

 */

3.1.2.    声明包的语句和import 语句

       绝大多数java 源文件中的第一非注释行应该是声明包的语句。此后,紧接着是import 语句。例如:

              package java.awt;

              import java.awt.peer.CanvasPeer;

3.1.3.    类和接口的声明

下面的表格描述了部分的类和接口的声明,他们应该按照表格的顺序。参看“Java Source File Example” on page 18

                           

部分类/接口声明  

注释

/接口文档注解/**…*/

如何做此类注解请参看“Documentation Comments”

类或接口声明

 

/接口实现的注解(/*。。。*/),如果有必要的话

这个注解应该包括任何整个类或接口范围内的不适合在类/接口文档注解中出现的内容。

类(静态)变量

首先是public类变量,然后是protected类变量,然后是friendly(package level,即默认)。然后是private变量。

Instance variables(译注:实体变量?不会翻译了,意即普通的变量。)

首先是public,其次protected,接着package level。最后是private变量。

类的构造函数

 

方法,(译注:即类的成员函数)

这些方法应该以功能相近为标准,组织在一块,而不是看其作用域和可存取性。例如:一个private class 方法(译注:意即private static method)可以被放在两个public instance 方法(译注:意即public method)中间。其目的是为了代码可读性和可理解性增加。

4.   缩进

应该以四个空格为缩进的最小单位,缩进的精确结构没有被详细定义。Tabs必须被精确指定为8个空格(而不是4个)。

4.1. 代码行的长度

应避免行的长度超过80个字符,因为很多的终端和工具都不支持超过80个字符。(译注:个人认为现在一般都支持行超过80个字符,但是由于超过80个字符,一般要滚屏,所以尽量还是不要超过此限制。)

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

4.2. Wrapping lines(断行的方法)

当一个表达式不适合一个单行时,依照以下这些一般性的原理来断行:

l         在逗号后断行

l         在运算操作符前断行

l         在较高的层次断行比在低层次断行要好

l         把新行的表达式的开头和上一行的表达式开头对齐。

l         如果上一条规则导致代码的混乱或者代码超出了正常的限度。那么就仅仅以缩进8个空格来替代。

这儿有一些调用方法的断行例子:

       someMethod(longExpression1,longExpression2,longExpression3,

                        longExpression4,longExpression5);

       var=someMethod1(longExpression1,

                                   someMethod2(longExpression2,

                                                        longExpression3));

  下面是两个算术表达式的断行的例子,第一个比第二个好,因为它的断行发生在括号的外面,这是在高层次上的断行。

              longName1 = longName2 * (longName3 + longName4 –longName5)

                              + 4 * longName6;

             

              longName1 = longName2 * ( longName3 + longName4

                               - longName5) + 4 * longName6;     //       AVOID(应该避免这样)

下面是两个方法声明的断行的例子,第一个是规范的例子。第二个如果要使用规范的缩进的话,将会使第二和第三行缩进的非常向右。所以仅仅使用8个空格来替代。

              //规范的缩进

              someMethod ( int anArg, Object anotherArg, String yetAnotherArg,

                                   Object andStillAnother){

                    

              }

 

              //8个空格来缩进,以避免非常纵深的缩进

              private static synchronized horkingLongMethodName(int anArg,

                        Object anotherArg, String yetAnotherArg,

                        Object andStillAnother) {

                   

              }

       行的包装(Line wrapping for)对if 语句一般应该用8个空格规则,因为标准的(4个空格)缩进使得if 的主体部分非常难看明白。例如:

              //不要使用这种缩进

              if ( ( condition1 && condition2)

                  || (condition3 && condition4)

                     || ( condition5 && condition6)) { //差的包装

                     doSomethingAboutIt();

       }

              //以这种缩进方式代替

              if ( ( condition1 && condition2)

                      || ( condition3 && conditin4 )

                                   || ! (condition5 && condition6)) {

                            doSomethingAboutIt();

              }

              //或者这样使用

              if ((condition1 && condition2) || (conditin3 && condition4)

                        || ! ( condition5 && condition6 )) {

                     doSomethingAboutIt();

              }

       这儿有三种可接受的三元运算符的缩进格式:

              alpha = ( aLongBooleanExpression) ? beta : gamma;

              alpha = ( aLongBooleanExpression) ? beta

                                                                : gamma;

              alpha = ( aLongBooleanExpression)

                       ? beta

                       : gamma;

 

5.   注释(注解)

待译。

 

 

 

 

 

 

 

 

 

asp?id=14234" width="1" height="1">



CNBIE BLOG

java编码规范(2)

原文: java编码规范(2)
       

Java 编码规范(2)

翻译:王士勇

(转载请保留作者,谢谢)

比上一个版本多了很多,而且排了一下版面。希望能替换掉上一篇文章

1.   引言

1.1. 为什么要编码规范

编码规范为什么是重要的?有以下一些理由:

l         一份软件80%的生命周期是维护期

l         任何软件都很难说他的整个生命周期都是由他的原始作者来维护

l         编码规范改善软件的可读性,使得软件工程师充分理解新的代码变得非常的快速。

l         如果你要把你的原码作为产品发布,你需要确保他像你的其他产品一样干净并且封装的好。

为了按照规范工作,每个人写软件的时候,都必须遵守编码规范。记住,是每个人!

1.1.1.    致谢

这本书是反映的是Java Language Specification 中关于java语言编码规范的。在这里要着重对Peter king ,Patrick Naughton,Mike DeMoney,Jonni Kanerva,Kathy Walrath,Scott Hommel表示感谢。

2.   文件名

这一节列出了本书所用的大部分文件名和后缀。

2.1. 文件后缀

        .java    java 源文件后缀

        .class     java 字节码文件后缀

2.2. 常用的文件名

        经常使用的文件名包括以下:

       GNUmakefile             首选的makefile的名字,我们使用gnumakebuild我们的软件

       README                    那些专门概述特定文件夹内容的文件的首选的名字

3.   文件的组织

一个文件的各个部分之间应该用空行隔开,并且应该用一个可选的注解来标示每个不同的部分。

文件超过2000行,是非常笨重讨厌(cumbersome)的,应该避免。

至于java 编程的正确格式的示例,请参看18页上的JAVA Source File Example(Java 原码文件示例)”

3.1. Java 源码文件

        每一个Java源码文件都包括一个唯一的public 类或interface。当私有的类和interface 都和这个public 类有关联时,你可以把它们放到这个public 类的源文件中。这个public 类或interface 应当是这个文件的第一个类或interface

 

Java 源文件有以下的顺序:

l         文件开头注解(参见第二页的“Beginning Comments(开头注解)

l          声明package 的语句和载入语句。

l         类和interface的声明(参见page 3的“Class and Interface Declarations”

3.1.1.    开头注解

       所有的源文件都应该以一个C语言风格的注解开头。这个注解应该列出类名,版本信息,日期和版权声明:

       /*

       *Classname

       *

       *Version information

       *

       * Date

*

 * copyright notice

 *

 */

3.1.2.    声明包的语句和import 语句

       绝大多数java 源文件中的第一非注释行应该是声明包的语句。此后,紧接着是import 语句。例如:

              package java.awt;

              import java.awt.peer.CanvasPeer;

3.1.3.    类和接口的声明

下面的表格描述了部分的类和接口的声明,他们应该按照表格的顺序。参看“Java Source File Example” on page 18

                           

部分类/接口声明  

注释

/接口文档注解/**…*/

如何做此类注解请参看“Documentation Comments”

类或接口声明

 

/接口实现的注解(/*。。。*/),如果有必要的话

这个注解应该包括任何整个类或接口范围内的不适合在类/接口文档注解中出现的内容。

类(静态)变量

首先是public类变量,然后是protected类变量,然后是friendly(package level,即默认)。然后是private变量。

Instance variables(译注:实体变量?不会翻译了,意即普通的变量。)

首先是public,其次protected,接着package level。最后是private变量。

类的构造函数

 

方法,(译注:即类的成员函数)

这些方法应该以功能相近为标准,组织在一块,而不是看其作用域和可存取性。例如:一个private class 方法(译注:意即private static method)可以被放在两个public instance 方法(译注:意即public method)中间。其目的是为了代码可读性和可理解性增加。

4.   缩进

应该以四个空格为缩进的最小单位,缩进的精确结构没有被详细定义。Tabs必须被精确指定为8个空格(而不是4个)。

4.1. 代码行的长度

应避免行的长度超过80个字符,因为很多的终端和工具都不支持超过80个字符。(译注:个人认为现在一般都支持行超过80个字符,但是由于超过80个字符,一般要滚屏,所以尽量还是不要超过此限制。)

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

4.2. Wrapping lines(断行的方法)

当一个表达式不适合一个单行时,依照以下这些一般性的原理来断行:

l         在逗号后断行

l         在运算操作符前断行

l         在较高的层次断行比在低层次断行要好

l         把新行的表达式的开头和上一行的表达式开头对齐。

l         如果上一条规则导致代码的混乱或者代码超出了正常的限度。那么就仅仅以缩进8个空格来替代。

这儿有一些调用方法的断行例子:

       someMethod(longExpression1,longExpression2,longExpression3,

                        longExpression4,longExpression5);

       var=someMethod1(longExpression1,

                                   someMethod2(longExpression2,

                                                        longExpression3));

  下面是两个算术表达式的断行的例子,第一个比第二个好,因为它的断行发生在括号的外面,这是在高层次上的断行。

              longName1 = longName2 * (longName3 + longName4 –longName5)

                              + 4 * longName6;

             

              longName1 = longName2 * ( longName3 + longName4

                               - longName5) + 4 * longName6;     //       AVOID(应该避免这样)

下面是两个方法声明的断行的例子,第一个是规范的例子。第二个如果要使用规范的缩进的话,将会使第二和第三行缩进的非常向右。所以仅仅使用8个空格来替代。

              //规范的缩进

              someMethod ( int anArg, Object anotherArg, String yetAnotherArg,

                                   Object andStillAnother){

                    

              }

 

              //8个空格来缩进,以避免非常纵深的缩进

              private static synchronized horkingLongMethodName(int anArg,

                        Object anotherArg, String yetAnotherArg,

                        Object andStillAnother) {

                   

              }

       行的包装(Line wrapping for)对if 语句一般应该用8个空格规则,因为标准的(4个空格)缩进使得if 的主体部分非常难看明白。例如:

              //不要使用这种缩进

              if ( ( condition1 && condition2)

                  || (condition3 && condition4)

                     || ( condition5 && condition6)) { //差的包装

                     doSomethingAboutIt();

       }

              //以这种缩进方式代替

              if ( ( condition1 && condition2)

                      || ( condition3 && conditin4 )

                                   || ! (condition5 && condition6)) {

                            doSomethingAboutIt();

              }

              //或者这样使用

              if ((condition1 && condition2) || (conditin3 && condition4)

                        || ! ( condition5 && condition6 )) {

                     doSomethingAboutIt();

              }

       这儿有三种可接受的三元运算符的缩进格式:

              alpha = ( aLongBooleanExpression) ? beta : gamma;

              alpha = ( aLongBooleanExpression) ? beta

                                                                : gamma;

              alpha = ( aLongBooleanExpression)

                       ? beta

                       : gamma;

 

5.   注释(注解)

待译。

asp?id=14270" width="1" height="1">



CNBIE BLOG

java编码中的一些经验和教训

原文: java编码中的一些经验和教训
       

04-11-29 19:54
 这几天集中时间重拾388备份文件格式研究,使用的工具主要是NetBeans、010 Editor、Excel。NetBeans用来测试分析处的规律,用java语言实现;010 Editor主要是二进制或者说十六进制的形式显示被分析文件,功能较强,主要表现在显示和组合方面;Excel用来格式化显示十六进制的块,显示相应的文本或者数值,并可以添加注释,标注未知区域,为下步分析留下痕迹并做好准备。java语言远远还没有用熟,从本次应用级别的操作展现的非常充分,汗。下面记录一些java使用中犯下的错误以及个人认为需要重点关注的地方,都是小东西,但是me认为对以后少犯这种低级错误有帮助^_^


 一、byte[]中的值。

从文件中读取512个字节到byte[] ba;byte应该是0到255,但是在直接转换为int类型的时候出现了负值。这个问题刚开始搞的me狼狈不堪,后来才发现是这个原因。用int i = ba[1]&0xff;才算解决问题。


 二、String和StringBuffer。

前者是不可变的

CNBIE BLOG

Java编写的计算器程序及源代码

原文:Java编写的计算器程序及源代码
       
//frame版程序源代码如下,疏漏之处,望批评指正。asp?id=39581" width="1" height="1">

//数字分组没有编写,科学型计算器没有编写,其他已经完善。
import java.awt.*;import java.lang.*;import javax.swing.*;import javax.swing.event.*;import java.awt.event.*;import java.text.DecimalFormat;
public class Calculator    implements ActionListener { //导入动作监听接口  //设计面板中的单位  JFrame frame;  JTextField textAnswer;  JPanel panel, panel1, panel2, panel3;  JMenuBar mainMenu;  JTextField textMemory;  JLabel labelMemSpace; //labelMemSpace单纯做摆设,控制面板的形状  JButton buttonBk, buttonCe, buttonC;  JButton button[];  JButton buttonMC, buttonMR, buttonMS, buttonMAdd;  JButton buttonDot, buttonAddAndSub, buttonAdd, buttonSub, buttonMul,      buttonDiv, buttonMod;  JButton buttonSqrt, buttonDao, buttonEqual;  JMenu editMenu, viewMenu, helpMenu;  JMenuItem copyItem, pasteItem, tItem, sItem, numberGroup, topHelp, aboutCal;  DecimalFormat df; //设置数据输出精度  boolean clickable; //控制当前能否按键  double memoryd; //使用内存中存储的数字  int memoryi;  double vard, answerd; //用来保存double型数据的中间值(vard)和最后结果(answerd)  short key = -1, prekey = -1; //key用来保存当前进行何种运算,prekey用来保存前次进行何种运算  String copy; //做复制用  JTextArea help; //帮助  JScrollPane scrollHelp;
  //构造函数  public Calculator() {    clickable = true;    answerd = 0;    frame = new JFrame("计算器");    df = new DecimalFormat("0.##############"); //设置数据输出精度(对于double型值)    textAnswer = new JTextField(15);    textAnswer.setText("");    textAnswer.setEditable(false);    textAnswer.setBackground(new Color(255, 255, 255));    panel = new JPanel();    frame.getContentPane().add(panel);    panel1 = new JPanel();    panel2 = new JPanel();    panel.setLayout(new BorderLayout());
    //设计整个面板    mainMenu = new JMenuBar();    editMenu = new JMenu("编辑(E)");    viewMenu = new JMenu("查看(V)");    helpMenu = new JMenu("帮助(H)");    copyItem = new JMenuItem("   复制(C) Ctrl+C");    copyItem.addActionListener(this);    pasteItem = new JMenuItem("   粘贴(V) Ctrl+V");    pasteItem.addActionListener(this);    editMenu.add(copyItem);    editMenu.add(pasteItem);    tItem = new JMenuItem("●标准型(T)");    tItem.addActionListener(this);    sItem = new JMenuItem("   科学型(S)");    sItem.addActionListener(this);    numberGroup = new JMenuItem("   数字分组(I)");    numberGroup.addActionListener(this);    viewMenu.add(tItem);    viewMenu.add(sItem);    viewMenu.add(numberGroup);    topHelp = new JMenuItem("   帮助主题(H)");    topHelp.addActionListener(this);    help = new JTextArea(5, 20);    scrollHelp = new JScrollPane(help);    help.setEditable(false);    help.append("执行简单计算/n");    help.append("1.  键入计算的第一个数字。/n");    help.append("2.  单击“+”执行加、“-”执行减、“*”执行乘或“/”执行除。/n");    help.append("3.  键入计算的下一个数字。/n");    help.append("4.  输入所有剩余的运算符和数字。/n");    help.append("5.  单击“=”。/n");
    aboutCal = new JMenuItem("   关于计算器(A)");    aboutCal.addActionListener(this);    helpMenu.add(topHelp);    helpMenu.add(aboutCal);    mainMenu.add(editMenu);    mainMenu.add(viewMenu);    mainMenu.add(helpMenu);    panel.add(mainMenu, BorderLayout.NORTH);    panel.add(textAnswer, BorderLayout.CENTER);    panel.add(panel1, BorderLayout.SOUTH);    panel1.setLayout(new BorderLayout());    textMemory = new JTextField(3);    textMemory.setEditable(false);    textMemory.setBackground(new Color(217, 217, 217));    labelMemSpace = new JLabel("                   ");    buttonBk = new JButton("Backspace");    buttonBk.setForeground(new Color(255, 0, 0));    buttonCe = new JButton("CE");    buttonCe.setForeground(new Color(255, 0, 0));    buttonC = new JButton("C");    buttonC.setForeground(new Color(255, 0, 0));    buttonBk.addActionListener(this);    buttonCe.addActionListener(this);    buttonC.addActionListener(this);    panel1.add(panel2, BorderLayout.NORTH);    panel2.setLayout(new FlowLayout(FlowLayout.RIGHT));    panel2.add(textMemory);    panel2.add(labelMemSpace);    panel2.add(buttonBk);    panel2.add(buttonCe);    panel2.add(buttonC);    panel3 = new JPanel();    panel1.add(panel3, BorderLayout.CENTER);    button = new JButton[10];    for (int i = 0; i < button.length; i++) {      button[i] = new JButton(Integer.toString(i));      button[i].setForeground(new Color(0, 0, 255));    }    buttonMC = new JButton("MC");    buttonMC.setForeground(new Color(255, 0, 0));    buttonMR = new JButton("MR");    buttonMR.setForeground(new Color(255, 0, 0));    buttonMS = new JButton("MS");    buttonMS.setForeground(new Color(255, 0, 0));    buttonMAdd = new JButton("M+");    buttonMAdd.setForeground(new Color(255, 0, 0));    buttonDot = new JButton(".");    buttonDot.setForeground(new Color(0, 0, 255));    buttonAddAndSub = new JButton("+/-");    buttonAddAndSub.setForeground(new Color(0, 0, 255));    buttonAdd = new JButton("+");    buttonAdd.setForeground(new Color(255, 0, 0));    buttonSub = new JButton("-");    buttonSub.setForeground(new Color(255, 0, 0));    buttonMul = new JButton("*");    buttonMul.setForeground(new Color(255, 0, 0));    buttonDiv = new JButton("/");    buttonDiv.setForeground(new Color(255, 0, 0));    buttonMod = new JButton("%");    buttonMod.setForeground(new Color(0, 0, 255));    buttonSqrt = new JButton("sqrt");    buttonSqrt.setForeground(new Color(0, 0, 255));    buttonDao = new JButton("1/x");    buttonDao.setForeground(new Color(0, 0, 255));    buttonEqual = new JButton("=");    buttonEqual.setForeground(new Color(255, 0, 0));    //将所有行为与监听绑定    panel3.setLayout(new GridLayout(4, 6));    panel3.add(buttonMC);    buttonMC.addActionListener(this);    panel3.add(button[7]);    button[7].addActionListener(this);    panel3.add(button[8]);    button[8].addActionListener(this);    panel3.add(button[9]);    button[9].addActionListener(this);    panel3.add(buttonDiv);    buttonDiv.addActionListener(this);    panel3.add(buttonSqrt);    buttonSqrt.addActionListener(this);    panel3.add(buttonMR);    buttonMR.addActionListener(this);    panel3.add(button[4]);    button[4].addActionListener(this);    panel3.add(button[5]);    button[5].addActionListener(this);    panel3.add(button[6]);    button[6].addActionListener(this);    panel3.add(buttonMul);    buttonMul.addActionListener(this);    panel3.add(buttonMod);    buttonMod.addActionListener(this);    panel3.add(buttonMS);    buttonMS.addActionListener(this);    panel3.add(button[1]);    button[1].addActionListener(this);    panel3.add(button[2]);    button[2].addActionListener(this);    panel3.add(button[3]);    button[3].addActionListener(this);    panel3.add(buttonSub);    buttonSub.addActionListener(this);    panel3.add(buttonDao);    buttonDao.addActionListener(this);    panel3.add(buttonMAdd);    buttonMAdd.addActionListener(this);    panel3.add(button[0]);    button[0].addActionListener(this);    panel3.add(buttonAddAndSub);    buttonAddAndSub.addActionListener(this);    panel3.add(buttonDot);    buttonDot.addActionListener(this);    panel3.add(buttonAdd);    buttonAdd.addActionListener(this);    panel3.add(buttonEqual);    buttonEqual.addActionListener(this);    frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);    frame.pack();    frame.show();  }
  //设置各个按钮行为  public void actionPerformed(ActionEvent event) {    boolean sign = false; //判断是否是double型数参与运算,是为true,不是为false    Object temp = event.getSource();    try {      //如果按下数据按钮,将按下的按钮代表的数据插入的当前文本框字符串之后      for (int i = 0; i <= 9; i++)        if (temp == button[i] && clickable == true)          textAnswer.setText(textAnswer.getText() + Integer.toString(i));          //按下'.'按钮时,判断当前文本框内字符串中含不含'.',如果已含,则不允许再插入'.'      if (temp == buttonDot && clickable == true) {        boolean isDot = false;        if (textAnswer.getText().length() == 0)          isDot = true;        for (int i = 0; i < textAnswer.getText().length(); i++)          if ('.' == textAnswer.getText().charAt(i)) {            isDot = true;            break;          }        if (isDot == false)          textAnswer.setText(textAnswer.getText() + ".");      }      if ( (temp == buttonAdd || temp == buttonSub || temp == buttonMul ||            temp == buttonDiv) && clickable == true) {        //'+'操作        if (temp == buttonAdd) {          switch (prekey) {            case 0:              answerd += Double.parseDouble(textAnswer.getText());              break;            case 1:              answerd -= Double.parseDouble(textAnswer.getText());              break;            case 2:              answerd *= Double.parseDouble(textAnswer.getText());              break;            case 3:              if (Double.parseDouble(textAnswer.getText()) == 0) {                textAnswer.setText("除数不能为零");                clickable = false;              }              else                answerd /= Double.parseDouble(textAnswer.getText());              break;            default:              answerd = Double.parseDouble(textAnswer.getText());          }          textAnswer.setText("");          prekey = key = 0;        }        //'-'操作        if (temp == buttonSub) {          switch (prekey) {            case 0:              answerd += Double.parseDouble(textAnswer.getText());              break;            case 1:              answerd -= Double.parseDouble(textAnswer.getText());              break;            case 2:              answerd *= Double.parseDouble(textAnswer.getText());              break;            case 3:              if (Double.parseDouble(textAnswer.getText()) == 0) {                textAnswer.setText("除数不能为零");                clickable = false;              }              else                answerd /= Double.parseDouble(textAnswer.getText());              break;            default:              answerd = Double.parseDouble(textAnswer.getText());          }          textAnswer.setText("");          prekey = key = 1;        }        //'*'操作        if (temp == buttonMul) {          switch (prekey) {            case 0:              answerd += Double.parseDouble(textAnswer.getText());              break;            case 1:              answerd -= Double.parseDouble(textAnswer.getText());              break;            case 2:              answerd *= Double.parseDouble(textAnswer.getText());              break;            case 3:              if (Double.parseDouble(textAnswer.getText()) == 0) {                textAnswer.setText("除数不能为零");                clickable = false;              }              else                answerd /= Double.parseDouble(textAnswer.getText());              break;            default:              answerd = Double.parseDouble(textAnswer.getText());          }          textAnswer.setText("");          prekey = key = 2;        }        //'/'操作        if (temp == buttonDiv) {          switch (prekey) {            case 0:              answerd += Double.parseDouble(textAnswer.getText());              break;            case 1:              answerd -= Double.parseDouble(textAnswer.getText());              break;            case 2:              answerd *= Double.parseDouble(textAnswer.getText());              break;            case 3:              if (Double.parseDouble(textAnswer.getText()) == 0) {                textAnswer.setText("除数不能为零");                clickable = false;              }              else                answerd /= Double.parseDouble(textAnswer.getText());              break;            default:              answerd = Double.parseDouble(textAnswer.getText());          }          textAnswer.setText("");          prekey = key = 3;        }      }      //'='操作      if (temp == buttonEqual && clickable == true) {        //如果连续按'=',则进行连续运算        if (prekey == 5) {          if (key == 0) {            answerd += vard;            textAnswer.setText(df.format(answerd));          }          if (key == 1) {            answerd -= vard;            textAnswer.setText(df.format(answerd));          }          if (key == 2) {            answerd *= vard;            textAnswer.setText(df.format(answerd));          }          if (key == 3) {            if (Double.parseDouble(textAnswer.getText()) == 0) {              textAnswer.setText("除数不能为零");              clickable = false;            }            else {              answerd /= vard;              textAnswer.setText(df.format(answerd));            }          }        }        else {          vard = Double.parseDouble(textAnswer.getText());          if (key == 0) {            prekey = -1;            answerd += Double.parseDouble(textAnswer.getText());            textAnswer.setText(df.format(answerd));          }          if (key == 1) {            prekey = -1;            answerd -= Double.parseDouble(textAnswer.getText());            textAnswer.setText(df.format(answerd));          }          if (key == 2) {            prekey = -1;            answerd *= Double.parseDouble(textAnswer.getText());            textAnswer.setText(df.format(answerd));          }          if (key == 3) {            prekey = -1;            if (Double.parseDouble(textAnswer.getText()) == 0) {              textAnswer.setText("除数不能为零");              clickable = false;            }            else {              answerd /= Double.parseDouble(textAnswer.getText());              textAnswer.setText(df.format(answerd));            }          }        }        prekey = 5;      }      //'%'操作,对第二个操作数除以100      if (temp == buttonMod && clickable == true) {        if (answerd == 0) {          String s = textAnswer.getText();          textAnswer.setText(s);        }        else {          boolean isDot = false;          for (int i = 0; i < textAnswer.getText().length(); i++)            if ('.' == textAnswer.getText().charAt(i)) {              isDot = true;              break;            }          //如果是double数,除100          if (isDot == true) {            double dtemp = Double.parseDouble(textAnswer.getText());            dtemp = dtemp / 100.0;            textAnswer.setText(Double.toString(dtemp));          }          else {            //如果是int数但能被100整除,则去掉末尾两个零            if (Integer.parseInt(textAnswer.getText()) % 100 == 0) {              int itemp = Integer.parseInt(textAnswer.getText());              itemp /= 100;              textAnswer.setText(Integer.toString(itemp));            }            //如果是int数,但不能被100整除,则按double数处理            else {              double dtemp = Double.parseDouble(textAnswer.getText());              dtemp = dtemp / 100.0;              textAnswer.setText(Double.toString(dtemp));            }          }        }      }      //开根号运算      if (temp == buttonSqrt && clickable == true) {        String s = textAnswer.getText();        if (s.charAt(0) == '-') {          textAnswer.setText("负数不能开根号");          clickable = false;        }        else          textAnswer.setText(Double.toString(java.lang.Math.sqrt(Double.              parseDouble(textAnswer.getText()))));      }      //倒数运算      if (temp == buttonDao && clickable == true) {        if (textAnswer.getText().charAt(0) == '0' &&            textAnswer.getText().length() == 1) {          textAnswer.setText("零不能求倒数");          clickable = false;        }        else {          boolean isDec = true;          int i, j, k;          String s = Double.toString(1 / Double.parseDouble(textAnswer.getText()));          for (i = 0; i < s.length(); i++)            if (s.charAt(i) == '.')              break;          for (j = i + 1; j < s.length(); j++)            if (s.charAt(j) != '0') {              isDec = false;              break;            }          if (isDec == true) {            String stemp = "";            for (k = 0; k < i; k++)              stemp += s.charAt(k);            textAnswer.setText(stemp);          }          else            textAnswer.setText(s);        }      }      //按下'+/-'按钮时处理      if (temp == buttonAddAndSub && clickable == true) {        boolean isNumber = true;        String s = textAnswer.getText();        for (int i = 0; i < s.length(); i++)          if (! (s.charAt(i) >= '0' && s.charAt(i) <= '9' || s.charAt(i) == '.' ||                 s.charAt(i) == '-')) {            isNumber = false;            break;          }        if (isNumber == true) {          //如果当前字符串首字母有'-'号,代表现在是个负数,再按下时,则将首符号去掉          if (s.charAt(0) == '-') {            textAnswer.setText("");            for (int i = 1; i < s.length(); i++) {              char a = s.charAt(i);              textAnswer.setText(textAnswer.getText() + a);            }          }          //如果当前字符串第一个字符不是符号,则添加一个符号在首字母处          else            textAnswer.setText('-' + s);        }      }      //计算器有关内存操作      //'MC'的操作,将内存清0      if (temp == buttonMC && clickable == true) {        memoryd = memoryi = 0;        textMemory.setText("");      }      //'MS'的操作,将当前文本框内容保存入内存,显示'M'      if (temp == buttonMS && clickable == true) {        boolean isDot = false;        textMemory.setText("   M");        for (int i = 0; i < textAnswer.getText().length(); i++)          if ('.' == textAnswer.getText().charAt(i)) {            isDot = true;            break;          }        //如果是double,则存入memoryd(double存储器)        if (isDot == true) {          memoryd = Double.parseDouble(textAnswer.getText());          memoryi = 0; //保证存储器中存放最新的值        }        //如果是int,则存入memoryi(int存储器)        else {          memoryi = Integer.parseInt(textAnswer.getText());          memoryd = 0; //保证存储器中存放最新的值        }      }      //'MR'的操作,将存储器中的信息输出      if (temp == buttonMR && clickable == true) {        if (memoryd != 0)          textAnswer.setText(Double.toString(memoryd));        if (memoryi != 0)          textAnswer.setText(Integer.toString(memoryi));      }      //'M+'的功能,将当前文本框里的数据和存储器中数据相加后,再存入存储器      if (temp == buttonMAdd && clickable == true) {        boolean isDot = false;        for (int i = 0; i < textAnswer.getText().length(); i++)          if ('.' == textAnswer.getText().charAt(i)) {            isDot = true;            break;          }        if (memoryi != 0) { //存储中是一个int型数          if (isDot == false) //被加数是一个int型数            memoryi += Integer.parseInt(textAnswer.getText());          else { //被加数是一个double型数,则将int存储器中数传入double存储器与当前数相加,int存储器清零            memoryd = memoryi + Double.parseDouble(textAnswer.getText());            memoryi = 0;          }        }        else          memoryd += Double.parseDouble(textAnswer.getText());      }      //按下'Backspace'键,利用循环将当前字符串中的最后一个字母删除      if (temp == buttonBk && clickable == true) {        String s = textAnswer.getText();        textAnswer.setText("");        for (int i = 0; i < s.length() - 1; i++) {          char a = s.charAt(i);          textAnswer.setText(textAnswer.getText() + a);        }      }      //按下'CE'按钮,将当前文本框内数据清除      if (temp == buttonCe) {        textAnswer.setText("");        clickable = true;      }      //按下'C'按钮,文本框内数据清除,同时var,answer清0      if (temp == buttonC) {        vard = answerd = 0;        textAnswer.setText("");        clickable = true;      }
      //按下'复制'菜单栏      if (temp == copyItem) {        copy = textAnswer.getText();      }
      //按下'粘贴'菜单栏      if (temp == pasteItem) {        textAnswer.setText(copy);      }
      if (temp == sItem) {        JOptionPane.showMessageDialog(panel, "当前是标准型计算器,/n科学型计算器有待更新。");      }      //按下'帮助主题'菜单栏      if (temp == topHelp) {
        JOptionPane.showMessageDialog(panel, scrollHelp);      }
      //按下'数字分组'菜单栏      if (temp == numberGroup) {        if (numberGroup.getText().compareTo("   数字分组(I)") == 0)          numberGroup.setText("√数字分组(I)");        else          numberGroup.setText("   数字分组(I)");      }
      //按下'关于'菜单栏      if (temp == aboutCal) {        JOptionPane.showMessageDialog(panel, "计算器1.00版/n开发者:楼竞");      }    }    //输入中如果有操作非法,比如按下两次'+',捕获异常    catch (Exception e) {      textAnswer.setText("操作非法");      clickable = false;    }  }
  //主函数  public static void main(String args[]) {    new Calculator();  }}


CNBIE BLOG

Java变量的缺省(默认)值--只有类的instance变量和static变量才有

原文:Java变量的缺省(默认)值--只有类的instance变量和static变量才有

JVM将为类的instance和static变量赋上缺省值(默认值),包括数组array中的每一个元素--而不用再写初始化赋值语句。

切记:局部变量是没有缺省值的,必须手动初始化!

这一缺省赋值过程是在对象的构造函数调用之前完成的,如果程序写了对instance和static变量的赋初值语句,且给的值就是JVM默认的值,那么无疑是画蛇添足,重复劳动了一遍。

如下面的代码说明了怎样画蛇添足的:

情况一:

public class Foo {
   private int count=0;   //多余
   private static boolean dd=false; //多余
    public Foo()
    {
        super();
    }     
}

情况二:

public class Foo {
   private int count;
   private static boolean dd;
    public Foo()
    {
        super();
        count=0;   //多余
        dd=false;   //多余
       
    }     
}

变量或对象引用的缺省值如下:

int : 0
byte : 0
long : 0
shor : 0
float : 0.0
double : 0.0
boolean : false
char : '/u0000'
object reference : null



CNBIE BLOG

java辨析(3) :is a 和 like a

原文:java辨析(3) :is a 和 like a
               abstract class和interface是Java语言中对于抽象类定义进行支持的两种机制,正是由于这两种机制的存在,才赋予了Java强大的面向对象能力。abstract class和interface之间在对于抽象类定义的支持方面具有很大的相似性,甚至可以相互替换,因此很多开发者在进行抽象类定义时对于abstract class和interface的选择显得比较随意。其实,两者之间还是有很大的区别的,对于它们的选择甚至反映出对于问题领域本质的理解、对于设计意图的理解是否正确、合理。本文将对它们之间的区别进行一番剖析,试图给开发者提供一个在二者之间进行选择的依据。

理解抽象类

abstract class和interface在Java语言中都是用来进行抽象类(本文中的抽象类并非从abstract class翻译而来,它表示的是一个抽象体,而abstract class为Java语言中用于定义抽象类的一种方法,请读者注意区分)定义的,那么什么是抽象类,使用抽象类能为我们带来什么好处呢?

在面向对象的概念中,我们知道所有的对象都是通过类来描绘的,但是反过来却不是这样。并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。抽象类往往用来表征我们在对问题领域进行分析、设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象。比如:如果我们进行一个图形编辑软件开发,就会发现问题领域存在着圆、三角形这样一些具体概念,它们是不同的,但是它们又都属于形状这样一个概念,形状这个概念在问题领域是不存在的,它就是一个抽象概念。正是因为抽象的概念在问题领域没有对应的具体概念,所以用以表征抽象概念的抽象类是不能够实例化的。

在面向对象领域,抽象类主要用来进行类型隐藏。我们可以构造出一个固定的一组行为的抽象描述,但是这组行为却能够有任意个可能的具体实现方式。这个抽象描述就是抽象类,而这一组任意个可能的具体实现则表现为所有可能的派生类。模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体,因此它可以是不允许修改的;同时,通过从这个抽象体派生,也可扩展此模块的行为功能。熟悉OCP的读者一定知道,为了能够实现面向对象设计的一个最核心的原则OCP(Open-Closed Principle),抽象类是其中的关键所在。


从语法定义层面看abstract class和interface

在语法层面,Java语言对于abstract class和interface给出了不同的定义方式,下面以定义一个名为Demo的抽象类为例来说明这种不同。

使用abstract class的方式定义Demo抽象类的方式如下:

abstract class Demo {
abstract void method1();
abstract void method2();

使用interface的方式定义Demo抽象类的方式如下:

interface Demo {
void method1();
void method2();

}

在abstract class方式中,Demo可以有自己的数据成员,也可以有非abstarct的成员方法,而在interface方式的实现中,Demo只能够有静态的不能被修改的数据成员(也就是必须是static final的,不过在interface中一般不定义数据成员),所有的成员方法都是abstract的。从某种意义上说,interface是一种特殊形式的abstract class。

从编程的角度来看,abstract class和interface都可以用来实现"design by contract"的思想。但是在具体的使用上面还是有一些区别的。

首先,abstract class在Java语言中表示的是一种继承关系,一个类只能使用一次继承关系。但是,一个类却可以实现多个interface。也许,这是Java语言的设计者在考虑Java对于多重继承的支持方面的一种折中考虑吧。

其次,在abstract class的定义中,我们可以赋予方法的默认行为。但是在interface的定义中,方法却不能拥有默认行为,为了绕过这个限制,必须使用委托,但是这会 增加一些复杂性,有时会造成很大的麻烦。

在抽象类中不能定义默认行为还存在另一个比较严重的问题,那就是可能会造成维护上的麻烦。因为如果后来想修改类的界面(一般通过abstract class或者interface来表示)以适应新的情况(比如,添加新的方法或者给已用的方法中添加新的参数)时,就会非常的麻烦,可能要花费很多的时间(对于派生类很多的情况,尤为如此)。但是如果界面是通过abstract class来实现的,那么可能就只需要修改定义在abstract class中的默认行为就可以了。

同样,如果不能在抽象类中定义默认行为,就会导致同样的方法实现出现在该抽象类的每一个派生类中,违反了"one rule,one place"原则,造成代码重复,同样不利于以后的维护。因此,在abstract class和interface间进行选择时要非常的小心。


从设计理念层面看abstract class和interface

上面主要从语法定义和编程的角度论述了abstract class和interface的区别,这些层面的区别是比较低层次的、非本质的。本小节将从另一个层面:abstract class和interface所反映出的设计理念,来分析一下二者的区别。作者认为,从这个层面进行分析才能理解二者概念的本质所在。

前面已经提到过,abstarct class在Java语言中体现了一种继承关系,要想使得继承关系合理,父类和派生类之间必须存在"is a"关系,即父类和派生类在概念本质上应该是相同的(参考文献〔3〕中有关于"is a"关系的大篇幅深入的论述,有兴趣的读者可以参考)。对于interface 来说则不然,并不要求interface的实现者和interface定义在概念本质上是一致的,仅仅是实现了interface定义的契约而已。为了使论述便于理解,下面将通过一个简单的实例进行说明。

考虑这样一个例子,假设在我们的问题领域中有一个关于Door的抽象概念,该Door具有执行两个动作open和close,此时我们可以通过abstract class或者interface来定义一个表示该抽象概念的类型,定义方式分别如下所示:

使用abstract class方式定义Door:

abstract class Door {
abstract void open();
abstract void close();
}


使用interface方式定义Door:


interface Door {
void open();
void close();
}


其他具体的Door类型可以extends使用abstract class方式定义的Door或者implements使用interface方式定义的Door。看起来好像使用abstract class和interface没有大的区别。

如果现在要求Door还要具有报警的功能。我们该如何设计针对该例子的类结构呢(在本例中,主要是为了展示abstract class和interface反映在设计理念上的区别,其他方面无关的问题都做了简化或者忽略)?下面将罗列出可能的解决方案,并从设计理念层面对这些不同的方案进行分析。

解决方案一:

简单的在Door的定义中增加一个alarm方法,如下:

abstract class Door {
abstract void open();
abstract void close();
abstract void alarm();
}


或者

interface Door {
void open();
void close();
void alarm();
}


那么具有报警功能的AlarmDoor的定义方式如下:

class AlarmDoor extends Door {
void open() { … }
void close() { … }
void alarm() { … }
}


或者

class AlarmDoor implements Door {
void open() { … }
void close() { … }
void alarm() { … }

这种方法违反了面向对象设计中的一个核心原则ISP(Interface Segregation Priciple),在Door的定义中把Door概念本身固有的行为方法和另外一个概念"报警器"的行为方法混在了一起。这样引起的一个问题是那些仅仅依赖于Door这个概念的模块会因为"报警器"这个概念的改变(比如:修改alarm方法的参数)而改变,反之依然。

解决方案二:

既然open、close和alarm属于两个不同的概念,根据ISP原则应该把它们分别定义在代表这两个概念的抽象类中。定义方式有:这两个概念都使用abstract class方式定义;两个概念都使用interface方式定义;一个概念使用abstract class方式定义,另一个概念使用interface方式定义。

显然,由于Java语言不支持多重继承,所以两个概念都使用abstract class方式定义是不可行的。后面两种方式都是可行的,但是对于它们的选择却反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理。我们一一来分析、说明。

如果两个概念都使用interface方式来定义,那么就反映出两个问题:1、我们可能没有理解清楚问题领域,AlarmDoor在概念本质上到底是Door还是报警器?2、如果我们对于问题领域的理解没有问题,比如:我们通过对于问题领域的分析发现AlarmDoor在概念本质上和Door是一致的,那么我们在实现时就没有能够正确的揭示我们的设计意图,因为在这两个概念的定义上(均使用interface方式定义)反映不出上述含义。

如果我们对于问题领域的理解是:AlarmDoor在概念本质上是Door,同时它有具有报警的功能。我们该如何来设计、实现来明确的反映出我们的意思呢?前面已经说过,abstract class在Java语言中表示一种继承关系,而继承关系在本质上是"is a"关系。所以对于Door这个概念,我们应该使用abstarct class方式来定义。另外,AlarmDoor又具有报警功能,说明它又能够完成报警概念中定义的行为,所以报警概念可以通过interface方式定义。如下所示:

abstract class Door {
abstract void open();
abstract void close();
}
interface Alarm {
void alarm();
}
class AlarmDoor extends Door implements Alarm {
void open() { … }
void close() { … }
void alarm() { … }
}


这种实现方式基本上能够明确的反映出我们对于问题领域的理解,正确的揭示我们的设计意图。其实abstract class表示的是"is a"关系,interface表示的是"like a"关系,大家在选择时可以作为一个依据,当然这是建立在对问题领域的理解上的,比如:如果我们认为AlarmDoor在概念本质上是报警器,同时又具有Door的功能,那么上述的定义方式就要反过来了。

 

结论

abstract class和interface是Java语言中的两种定义抽象类的方式,它们之间有很大的相似性。但是对于它们的选择却又往往反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理,因为它们表现了概念间的不同的关系(虽然都能够实现需求的功能)。这其实也是语言的一种的惯用法,希望读者朋友能够细细体会。

 

asp?id=35628" width="1" height="1">



CNBIE BLOG

Java参数传递的一些心得

原文:Java参数传递的一些心得

      Java是种面向对象的语言,可以说,Java所有一切都是对象,这句话很好的描述的Java和别的面向对象的编程语言,例如Object Pascl的区别。而且,这个特性,也对函数的参数传递有着重要的影响。
用过dephi的人都知道,在object Pascal中,函数的参数传递有两种方式,值传递和变参传递,简单的说,就是加不加var的区别。加了var,就是变参传递,实质上传递的是这个参数的指针,也就是说,在函数中对参数的任何修改,实际上就是参数对应的变量的修改,这样程序执行完后,作为参数传进来的变量的值可能发生改变。而不加var,则就是值传递,实质上,就是将值先拷贝一份,然后,将拷贝的传递给函数,这样,修改实际上对原来的变量没有影响,(因为只是对拷贝的修改)而且,等到函数返回时,函数调用的空间退栈,拷贝的参数空间也被收回。
Java来说,由于所有的皆是对象,所以,参数传递有所不同。其中,没有了var的区别。但实际上,由于对象作为参数,也就是相当于var式传递,因为,传递给函数的是原对象的指针,这样,对传入的参数的修改,实际上就是作为参数的对象的修改,在这一点上,Java的参数传递实际上和var方式很象。但在简单数据类型做参数上,Java却是遵循值传递的原则,即在函数中修改不会对传入的简单数据类型的变量产生影响,比如象int类型的参数,即使函数中有过修改,也不会对传入的原变量有影响。由于这样,我们在对简单类型的变量作修改时(比如,想让它们作函数参数,然后在函数中修改它们),会误以为真的修改了变量,其实不然,知道值参的传递就会很容易明白这个道理。所以,我们需要将变量包装成一个对象(例如,设一个对象,以一个int值的变量作参数)这样,以此对象作参数,当在函数中对对象中的int变量修改后,原对象中的值便得到修改了。
由此,我们可以更加深入的看看递归函数的问题。递归函数通过不断的调用自身,其实在做一个不断的入栈然后出栈的过程,随着递归的进行,新递归函数不断的入栈,等到条件满足返回时,再不断的出栈。这样,对于值参的话,每次递归都要将其现在的数据保存到栈中,而变参,则只保存引用,所以,递归函数在不同的编程语言中的应用要相应的注意。比如,数组类型的数据,在object pascal中要定义数组类型的type,这样,每次递归时,实际上是将当前的数组参数作值参存储,即每次递归就要存储相应的数组,这样,每次递归过程实际上是对一个新的数组进行操作。而java就不同,它是以数组变量作参数,这样,每次的递归操作实际上是对同一个数组进行操作。由于有这样的不同,所以,需要在对数组改变后作还原操作,这样,每次才能正确地递归。
以上是点小小的心得,呵呵,如果有错,请与我联系。



CNBIE BLOG

Java操作文本文件的方法

原文:Java操作文本文件的方法
       
  最初 java是不支持对文本文件的处理的,为了弥补这个缺憾而引入了Reader和Writer两个类,这两个类都是抽象类,Writer中write(char[] ch,int off,int length),flush()和close()方法为抽象方法,Reader中read(char[] ch,int off,int length)和close()方法是抽象方法。子类应该分别实现他们。

  当我们读写文本文件的时候,采用Reader是非常方便的,比如FileReader,InputStreamReader和BufferedReader。其中最重要的类是InputStreamReader,它是字节转换为字符的桥梁。你可以在构造器重指定编码的方式,如果不指定的话将采用底层操作 系统的默认编码方式,例如GBK等。当使用FileReader读取文件的时候。

  FileReader fr = new FileReader("ming.txt");
  int ch = 0;
  while((ch = fr.read())!=-1 )
  {
  System.out.print((char)ch);
  }

  其中read()方法返回的是读取得下个字符。当然你也可以使用read(char[] ch,int off,int length)这和处理二进制文件的时候类似,不多说了。如果使用InputStreamReader来读取文件的时候

  while((ch = isr.read())!=-1)
  {
  System.out.print((char)ch);
  }

  这和FileReader并没有什么区别,事实上在FileReader中的方法都是从InputStreamReader中继承过来的。read()方法是比较好费时间的,如果为了提高效率我们可以使用BufferedReader对Reader进行包装,这样可以提高读取得速度,我们可以一行一行的读取文本,使用readLine()方法。

  BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("ming.txt")));
  String data = null;
  while((data = br.readLine())!=null)
  {
  System.out.println(data);
  }

  当你明白了如何用Reader来读取文本文件的时候那么用Writer写文件同样非常简单。有一点需要注意,当你写文件的时候,为了提高效率,写入的数据会先放入缓冲区,然后写入文件。因此有时候你需要主动调用flush()方法。与上面对应的写文件的方法为:

  FileWriter fw = new FileWriter("hello.txt");
  String s = "hello world";
  fw.write(s,0,s.length());
  fw.flush();

  OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("hello2.txt"));
  osw.write(s,0,s.length());
  osw.flush();

  PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream("hello3.txt")),true);
  pw.println(s);

  不要忘记用完后关闭流!下面是个小例子,帮助新手理解。其实有的时候 java的IO 系统是需要我们多记记的,不然哪天就生疏了。

  hello world i like java language

  import java.io.*;

  public class TestFile2
  {
  public static void main(String[] args) throws IOException
  {
  FileReader fr = new FileReader("ming.txt");
  char[] buffer = new char[1024];
  int ch = 0;
  while((ch = fr.read())!=-1 )
  {
   System.out.print((char)ch);
  }

  InputStreamReader isr = new InputStreamReader(new FileInputStream("ming.txt"));
  while((ch = isr.read())!=-1)
  {
   System.out.print((char)ch);
  }

  BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("ming.txt")));
  String data = null;
  while((data = br.readLine())!=null)
  {
   System.out.println(data);
  }

  FileWriter fw = new FileWriter("hello.txt");
  String s = "hello world";
  fw.write(s,0,s.length());
  fw.flush();

  OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("hello2.txt"));
  osw.write(s,0,s.length());
  osw.flush();

  PrintWriter pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream("hello3.txt")),true);
  pw.println(s);

  fr.close();
  isr.close();
  br.close();
  fw.close();
  osw.close();
  pw.close();
  }
  }
           

CNBIE BLOG

Java常见问题集锦(来自Sun中国官方站)

原文:Java常见问题集锦(来自Sun中国官方站)
出自:sun中国 2002年11月14日 09:35 
Java常见问题集锦 

问: 
如何设置Java 2(JDK1.2)的环境变量? 

答: 
Java 2安装后,需要设置PATH和JAVA_HOME环境变量.与JDK1.1不同的是:设置好JAVA_HOME环境变量后,JVM将自动搜索系统类库以及用户的当前路径. 

Java 2环境变量的设置如下例所示: 

Solaris平台: setenv JAVA_HOME Java2的安装路径 

setenv PATH /bin:/p/2002-11-14/6302.html 

Windows平台: set JAVA_HOME=Java2的安装路径 

set PATH=;%PATH% 

问: 哪些Java集成开发工具支持Java 2? 

答: 
目前流行的Java集成开发环境,如Inprise的JBuilder,Symantec的Visual Cafe, Sybase的PowerJ,都支持Java 2. 

问: 
如果在Netscape或IE浏览器中运行Java applet时出现了错误,如何确定错误范围? 

答: 
java applet在浏览器中运行时,使用的是浏览器本身的缺省JVM.而不同浏览器对JDK的支持程度也不尽相同. 因此,在Netscape或IE浏览器中运行Java applet出现了错误,建议使用JDK提供的工具appletviewer或Sun公司的Hotjava浏览器来测试该applet,以确定错误的产生是与浏览器相关. 

如果applet在appletviewer或Hotjava中运行一切正常,则错误的产生是由于浏览 器不完全兼容JDK而引起的. 此时,解决方法可以是使用Hotjava浏览器或者安装 Sun公司的Java Plugin. 

如果applet在Hotjava浏览器或appletviewer中运行即发生错误,则应当根据错误 提示检查applet程序

问: 
当用JDBC向数据库中插入数据或从数据库中提取数据时,为何有时中文字符会显示为乱码? 

答: 
这个问题的实现通常与各个JDBC driver的实现有关. 目前大多数JDBC driver采用本地编码格式来传输中文字符,例如中文字符"0x4175"会被转成"0x41"和"0x75"进行传输. 因此我们需要对JDBC driver返回的字符以及要发给JDBC driver的字符进行转换. 

当用JDBC driver向数据库中插入数据时,需要先将Unicode转成native code; 当 JDBC driver从数据库中查询数据时,则需要将native code转换成Unicode. 下面给出了这两种转换的实现: 

String native2Unicode(String s) { 

if (s == null || s.length() == 0) { 

return null; 



byte[] buffer = new byte[s.length()]; 

for (int i = 0; i s.length(); i++) { if (s.charAt(i)>= 0x100) { 

c = s.charAt(i); 

byte []buf = (""+c).getBytes(); 

buffer[j++] = (char)buf[0]; 

buffer[j++] = (char)buf[1]; 



else { 

buffer[j++] = s.charAt(i); 





return new String(buffer, 0, j); 



除使用以上两个方法之外,有些JDBC driver如果对jdbc driver Manager设置了正确 的字符集属性,以上2个方法就不需要了. 

问: 
当用Servlet来处理http请求并产生返回的HTML页面时,如何使HTML页面中的中文字符能够正常显示? 

答: 
javax.servlet.http.HttpResponse类用于产生返回页面.通过HttpResponse定义的方法getOutputStream()可以获得ServletOutputStream的实例,这样用户就可以利用ServletOutputStream.write方法向输出流中写入返回页面的内容. 但是ServletOutputStream使用的是缺省的编码方式,如果要使返回页面中的中文字 符能够正常显示,最好显示地指定所用的字符编码方式. 通常需要构造一个 OutputStreamWriter , 例程如下: 

public void doGet (HttpServletRequest req, HttpServletResponse res) 

throws ServletException, IOException 



res.setContentType("text/html"); 

ServletOutputStream out = res.getOutputStream(); 

OutputStreamWriter ow = new OutputStreamWriter(out,"GB2312"); 

ow.write("这是测试"); 

ow.flush(); 

ow.close(); 



问: 
如何设置Java WebServer的CLASSPATH,以包含用户的class文件? 

答: 
有两种方法可以设置Java WebServer的CLASSPATH环境变量,以使用户编写的Servlet能够调用用户的class文件. 
将用户的class文件放到 JavaWebServer_Dir/classes目录下,在Java WebServer 启动时,classes目录被自动加入到CLASSPATH环境变量中了. 
修改httpd.nojre文件,将用户class文件所在的路径名加到CLASSPATH环境变量中. 

问: 
为什么在Windows平台上用Naming.lookup来获取远程RMI对象时会很慢? 

答: 
机器的网络设置不正确很可能会引起该问题的发生. 
RMI使用了Java网络类,特别是java.net.InetAddress类,它将查询TCP/IP的主机名, 包括IP地址到主机名的映射和主机名到IP地址的映射.在Windows平台,这种查询功能 是由本地的Windows Socket库来实现的. 因此延时是发生在Windows库中,而非RMI中. 

如果你的机器设置成使用DNS,问题通常是DNS服务器查不到主机名,你所发现的延时 是DNS查询的延时. 请尝试将RMI通信中涉及到的所有主机名/IP地址加到本地文件 winntsystem32driversetchosts或windowshosts中. 格式如下: 

IP地址 主机名 

如此设置应当可以明显地减少查询所花的时间. 

问: 编写Java application时,如何设置proxy的信息,以便访问外部网站? 

答: 
若在java application中访问外部网站,首先应设置proxy信息,样例代码如下: 


import java.util.properties; 

..... 

Properties sys = System.getProperties(); 

sys.put("proxySet","true"); 

sys.put("proxyHost","myHTTP.proxyserver.com"); 

sys.put("proxyPort","80"); 

System.setProperties(sys); 

u = new URL(website); 

connect = (HttpURLConnection)u.openConnection(); 

..... 

问: Swing组件JList的列表数据修改了,如何通知JList改变显示? 

答: 
JList组件有一个单独的显示模式ListModel来表示JList的显示数据. 
JList创建以后,JList数据元素的值及数据元素的数量可以动态地改变. 
JList在它的数据模式ListModel中观察数据的改变.因此,一个ListModel 的正确实现应当在每次数据发生改变时,通知事件的监听者. 
当使用构造函数JList(Object[])创建一个JList的实例时,系统将自动 创建一个DefaultListModel的实例来存储JList的显示数据, 可以调用 DefaultListModel中定义的简便方法来动态地修改JList的数据,如 removeElementAt(index),addElement(Object)等. DefaultListModel 在修改数据的同时,将通知JList关于数据的改变. 

问: 
Java applet中如何实现一个模式对话框? 

答: 
Java applet中实现模式对话框的关键就是在创建一个对话框的时候 要为该对话框指定一个正确的父窗口.因为Applet是Panel类的子类,不 可以作为对话框的父窗口,所以首先要获得applet所在的窗口,作为模式 对话框的父窗口. 样例代码如下: 
..... 

Dialog d = new Dialog( getParentWindow(comp),title); 

// comp为applet上的任意一个组件 

.... 
public void getParentWindow(Component compOnApplet,String title){ 
Container c = compOnApplet.getParent(); 
while (c != null) { 
if (c instanceof Frame) 
return (Frame) c; 
c = c.getParent(); 

return null; 


问: 在Java applet中如何显示另外一个HTML页面? 

答: 
通过java.applet.Applet.getAppletContext()方法可以获得与该applet相关的AppletContext, AppletContext.showDocument(URL)方法就可以使applet所在的浏览器显示另外一个网页. 

问: 
用JDK实现的签名applet,可否在Netscape或IE中运行? 

答: 
用JDK实现的签名applet,不可以在Netscape或IE中运行,但是可以在Hotjava浏览器中运行. 

不同的浏览器提供了不同的签名applet机制,如Netscape提供了zigbert工具和 Capability API, 而IE则需要使用CAB文件. 但是,无论是Netscape工具产生的 签名applet,还是用IE产生的签名applet,都不可以在其它的浏览器中运行. 

如果要使JDK产生的签名applet能够在Netscape或IE中运行,解决方法是在 Netscape或IE中安装Java Plugin,则用JDK实现的签名applet就可以在这两种 浏览器中运行. 

问: 
用JNI技术可以从Java应用中调用C程序库,但是如何使该C程序库可以调用另外的C程序库? 

答: 
如果一个被Java调用的C程序库C1仍需要调用另外一个C程序库C2,那么在编译C1的时候应当联接程序库C2,步骤如下(Solaris平台): 
编写调用C库的Java文件,并编译. 
javajava文件名 

产生C程序头文件 
javah -jni java文件名(不带后缀.java

编写被Java调用的C程序C1.c,以及被C1调用的C2.c,并编译. 
cc -G -Iinclude路径名 C2.c -o libC2.so 
cc -G -Iinclude路径名 -lC2 C1.c -o libC1.so 

设置环境变量 
setenv LD_LIBRARY_PATH libC1.so,libC2.so所在路径 


运行java应用 

问: 
Java语言中,如何列出PC机文件系统中的所有驱动器名? 

答: 
Java 2版本中,java.io包中的File类新增加了方法listRoots()可以实现这一功能. 

问: 
为什么Runtime.exec("ls")没有任何输出? 

答: 
调用Runtime.exec方法将产生一个本地的进程,并返回一个Process子类的实例,该实例可用于控制进程或取得进程的相关信息. 由于调用Runtime.exec方法所创建的子进程没有自己的终端或控制台,因此该子进程的标准IO(如stdin,stdou,stderr)都通过Process.getOutputStream(),Process.getInputStream(), Process.getErrorStream()方法重定向给它的父进程了.用户需要用这些stream来向 子进程输入数据或获取子进程的输出. 所以正确执行Runtime.exec("ls")的例程如下: 

try 



process = Runtime.getRuntime().exec (command); 

InputStreamReader ir=newInputStreamReader(process.getInputStream()); 

LineNumberReader input = new LineNumberReader (ir); 

String line; 

while ((line = input.readLine ()) != null) 

System.out.println(line); 



catch (java.io.IOException e){ 

System.err.println ("IOException " + e.getMessage()); 



问: 
如何产生签名applet,以使applet能够访问本地资源? 

答: 
在jdk1.1中,可以使用javakey命令来产生公钥,私钥,证书和签名的jar文件,详细资料 请参考: java.sun.com/security/usingJavakey.html">http://java.sun.com/security/usingJavakey.html而java 2对签名机制做了比较大的改进,允许用户更灵活地设置安全权限.Java 2提供了三个工具:keytool,policytool和jarsigner来实现签名applet.例如,Joe编写了一个签名applet:SignedApplet.java,那么产生一个简单的签名applet的过程如下: 

//产生密钥,密钥别名为joe,口令为sign12,存放在密钥库joestore中 

keytool -genkey -alias joe -keypass sign12 -keystore joestore 

//将SignedApplet.class及相关文件打包成jar文件 

jar cvf SignedAppletDemo.jar 

//利用keytool生成的自签名的证书产生签名applet(jar文件) 

jarsigner -keystore joestore -signedjar joe.jar SignedAppletDemo.jar joe 

//将自签名证书从keystore中输出到文件 

keytool -export -keystore joestore -alias joe -file joe.cer 

而对于签名applet的接受方Susan,需要通过如下步骤来安全地执行 

Joe编写的签名applet: 

//得到Joe的证书并将之读入到密钥库中susanstore中 

keytool -import -alias joe -file joe.cer -keystore susanstore 

//运行policytool产生满足Susan要求的policy文件 

policytool 

//用appletviewer运行之,或在浏览器中安装java plugin来运行之. 

关于签名applet在Java Plugin中的部署请参考以下网页: 

java.sun.com/security/signExample12/">http://java.sun.com/security/signExample12/ 

注:以上的例子为简单起见,使用了keytool产生的自签名证书.其实,用户也可以 

使用keytool -certreq向商业CA中心申请电子证书. 

问: 
若通过ObjectOutputStream向一个文件中多次以追加方式写入object,为什么用ObjectInputStream读取这些object时会产生StreamCorruptedException? 

答: 
使用缺省的serializetion的实现时,一个ObjectOutputStream的构造和一个ObjectInputStream的构造必须一一对应.ObjectOutputStream的构造函数会向输出流中写入一个标识头,而ObjectInputStream会首先读入这个标识头.因此,多次以追加方式向一个文件中写入object时,该文件将会包含多个标识头.所以用ObjectInputStream来deserialize这个ObjectOutputStream时,将产生StreamCorruptedException.一种解决方法是可以构造一个ObjectOutputStream的子类,并覆盖writeStreamHeader()方法.被覆盖后的writeStreamHeader()方法应判断是否为首次向文件中写入object,羰?则调用super.writeStreamHeader();若否,即以追加方式写入object时,则应调用ObjectOutputStream.reset()方法. 

问: 
对象的序列化(serialization)类是面向流的,应如何将对象写入到随机存取文件中? 

答: 
目前,没有直接的方法可以将对象写入到随机存取文件中. 
但是可以使用ByteArray输入/输出流作为中介,来向随机存取文件中写入或从随机存取文件中读出字节,并且可以利用字节流来创建对象输入/输出流,以用于读写对象.需要注意的是在字节流中要包含一个完整的对象,否则读写对象时将发生错误. 例如,java.io.ByteArrayOutputStream可用于获取ObjectOutputStream的字节流,从中可得到byte数组并可将之写入到随机存取文件中.相反,我们可以从随机存取文件中读出字节数组,利用它可构造ByteArrayInputStream,进而构造出ObjectInputStream,以读取对象. 

问: 
运行RMI应用时,可不可以不手工启动名字服务rmiregistry,而是从程序中启动之? 

答: 
可以. java.rmi包中提供了类java.rmi.registry.LocateRegistry,用于获取名字服务或创建名字服务.调用LocateRegistry.createRegistry(int port)方法可以在某一特定端口创建名字服务,从而用户无需再手工启动rmiregistry.此外,LocateRegistry.getRegistry(String host,int port)方法可用于获取名字服务. 

问: 
使用类PrintJob进行打印操作时,应如何设置打印机名等打印属性? 

答: 
使用如下方法可以获得PrintJob的实例用于控制打印操作: 

Toolkit.getPrintJob(Frame f, String jobtitle, Properties prop) 

那么对于打印属性的设置可以通过对prop的属性设置来实现,打印属性包括: 

awt.print.destination: 可以是"printer"或"file" 

awt.print.printer: 打印机名 

awt.print.fileName: 打印文件名 

awt.print.numCopies: 打印份数 

awt.print.options: 打印命令的打印选项 

awt.print.orientation: 打印方向,可以是"portrait"或"landscape" 

awt.print.paperSize: 纸张大小,可以是"letter","legal","executive"或"a4" 

问: 
在JDK1.1中Thread类定义了suspend()和resume()方法,但是在JDK1.2中已经过时,应使用什么方法来替代之? 

答: 
Thread.suspend本身易于产生死锁.如果一个目标线程对某一关键系统资源进行了加锁操作,然后该线程被suspend,那么除非该线程被resume,否则其它线程都将无法访问该系统资源.如果另外一个线程将调用resume,使该线程继续运行,而在此之前,它也需要访问这一系统资源,则将产生死锁. 

因此,在Java 2中,比较流行的方式是定义线程的状态变量,并使目标线程轮询该状态变量,当状态为悬挂状态时,可以使用wait()方法使之处于等待状态.一旦需要该线程继续运行,其它线程会调用notify()方法来通知它. 

问: 
使用JDBC编程,应如何控制结果集ResultSet的指针,使之能够上下移动,以及移动到结果集的第一行和最后一行? 

答: 
在JDK1.1中,ResultSet类中只定义了next()方法支持数据指针的下移.但在Java 2中,ResultSet类增加了如下方法支持数据指针的移动,包括: 

ResultSet.first():将数据指针移到结果集的第一行 

ResultSet.last(): 将数据指针移到结果集的最后一行 

ResultSet.previous(): 将数据指针上移一行 

以上的方法定义在JDBC2.0的规范中,所有支持JDBC 2.0的JDBC驱动程序都可以支持上述方法.目前Intersolv和OpenLink等JDBC驱动程序厂商均有产品支持JDBC 2.0 . 

问: 
哪几种Web Server支持Servlet?如何使IIS支持Servlet? 

答: 
目前,支持Servlet的服务器端产品主要有: Sun公司的Java WebServer,Lotus DominoGo WebServer,BEA weblogic Tengah Server,Jigsaw,NetForge,AcmeServer和Mot Bays Jetty等. 

此外,一些第三方厂商也开发了Servlet engine,以使其它WebServer(如Netscape Web Server,IIS等)能够运行Servlet,如LiveSoftware的Jrun(http://www.livesoftware.com/ products/jrun/)等. 

问: 
如何在Java应用中将图像存储到图像文件中? 

答: 
Java Advanced Imaging API(包含在Java Media API中)允许在Java应用中执行复杂的,高性能的图像处理.JAI API提供了存储图像的能力.目前,JAI API支持以下几种图像文件格式:BMP,JEPG,PNG,PNM,TIFF.下面给出了将图像存储到BMP文件的一段代码: 


OutputStream os = new FileOutputStream(fileToWriteTo); 

BMPEncodeParam param = new BMPEncodeParam(); 

ImageEncoder enc = ImageCodec.createImageEncoder("BMP", os, param); 

enc.encode(img); 

os.close(); 

有关存储图像文件的编程指南请参考以下网页: 

java.sun.com/products/java-media/jai/forDevelopers/jai-guide/">http://java.sun.com/products/java-media/jai/forDevelopers/jai-guide/ 

问: 
如何用Java语言向串口读写数据? font> 

答: 
Sun公司的Java Communication API2.0可用于读写串口,它支持RS232串口和IEEE 1284 并口,提供了一种与平台无关的串/并口通信机制. 

详细文档,请访问:java.sun.com/products/javacomm/">http://java.sun.com/products/javacomm/ 
 


CNBIE BLOG

java程序,保存csdnblog的文章

原文:java程序,保存csdnblog的文章


CSDN 的blog经常出问题, 大家可能都领教过了

过年了,可能几天不能来上网了

就想把csdn blog自己的文章备份一下

于是就写了这个程序,用java写的

大家可以看看, 代码如下:



/**
使用的时候,需要修改strurl为自己的blog地址

程序会在当前目录下保存文件


具体,可以根据自己的需要适当修改

慈勤强编写
*/


import java.io.*;

import java.net.*;


class csdn
{
 
 public static void main(String[] args)  throws Exception
 {
  String strUrl;  
  String ss;
  int icount=0;


  //处理月份开始,连接首页,分析月份
  StringBuffer sbMonth=new StringBuffer("");

  strUrl="http://blog.csdn.net/cqq/";

  URL url = new URL(strUrl);

  URLConnection conn = url.openConnection();

  BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream(),"UTF8"));

  System.out.println("连接成功。");

  int i=0;
  int j=0;

  while ((ss=rd.readLine())!=null)
  {

   if(ss.indexOf("年")>0&&ss.indexOf("id")>0&&ss.indexOf("SingleColumn")>0)
   {
    i=ss.indexOf("archive");
    if(i>0)
    {
     j=ss.indexOf(".aspx",i);
     if(j>0)
     {
      sbMonth.append(strUrl+"/"+ss.substring(i,j)+".aspx");
      sbMonth.append(",");
     }
    }        
   }
  }
  rd.close();
  System.out.println("分析月份列表成功");
  //处理月份结束,得到一个包含每月URL地址的sbMonth字符串



  //处理每月,分析每月文章列表
  StringBuffer sbArticle=new StringBuffer("");
  String[] str=sbMonth.toString().split(",");

  for(int i_m=0;i_m<str.length;i_m++)
  {
       strUrl=str[i_m];
    url = new URL(strUrl);
          conn = url.openConnection();
          rd = new BufferedReader(new InputStreamReader(conn.getInputStream(),"UTF8"));
    boolean bPrint=false;


    while ((ss=rd.readLine())!=null)
    {
     if(ss.indexOf("postTitle")>0)
     {
      bPrint=true;
     }
     if(ss.indexOf("postText")>0)
     {
      bPrint=false;
     }
     if(bPrint)
     {
      
      i=ss.indexOf("http");
      if(i>0)
      {
       j=ss.indexOf(".aspx",i);
       if(j>0)
       {
        sbArticle.append(ss.substring(i,j)+".aspx");
        sbArticle.append(",");
       }
      }  
     }
    }
  }
  System.out.println("获取所有文章的URL地址列表成功");
  //获取每月文章列表结束,得到所有文章的URL地址



  //获取每篇文章内容开始
  System.out.println("/r/n保存文章开始...");
  String[] str1=sbArticle.toString().split(",");
  for(int i_n=0;i_n<str1.length;i_n++)
  {
          icount++;
    strUrl=str1[i_n];
    StringBuffer sb=new StringBuffer("");
    System.out.println(strUrl);
    url = new URL(strUrl);
          conn = url.openConnection();
          rd = new BufferedReader(new InputStreamReader(conn.getInputStream(),"UTF8"));
    boolean bPrint1=false;
    boolean bPrint2=false;


    RandomAccessFile rf=new RandomAccessFile("csdn_cqq_"+icount+".htm","rw");


    boolean bb=true;
    while ((ss=rd.readLine())!=null&&bb)
    {


     if(ss.indexOf("postTitle")>0)
     {
      bPrint1=true;
     }
     if(ss.indexOf("postfoot")>0)
     {
      bPrint1=false;
     }
     if(bPrint1)
     {
      sb.append(ss);
     }     
    }
    
    byte [] b;
    b=sb.toString().getBytes();
    rf.write(b);
    rf.close();
  }
  System.out.println("完成,总共保存 "+icount+" 篇文章");
 } 
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值