发布新日志

  • JAVA与正则表达式(一年级)

    2011-05-05 22:56:14Top 3 Digest 3

    转贴:http://blog.csdn.net/yqj2065/archive/2005/01/20/261790.aspx

    §1黑暗岁月

           有一个String,如何查询其中是否有yf字符?最黑暗的办法就是:

    程序1:我知道iffor语句和charAt()啊。

    class Test{

      public static void main(String args[]) {

             String str="For my money, the important thing "+

             "about the meeting was bridge-building";

             char x='y';

             char y='f';

             boolean result=false;

             for(int i=0;i<str.length();i++){

                    char z=str.charAt(i);  //System.out.println(z);

                    if(x==z||y==z) {

                           result=true;

                           break;

                    }

                    else result=false;

             }  

             System.out.println(result);

           }

    }

           好像很直观,但这种方式难以应付复杂的工作。如查询一段文字中,是否有is?是否有thingting等。这是一个讨厌的工作。

    §2 Javajava.util.regex

           按照面向对象的思路,把希望查询的字符串如isthingting封装成一个对象,以这个对象作为模板去匹配一段文字,就更加自然了。作为模板的那个东西就是下面要讨论的正则表达式。先不考虑那么复杂,看一个例子:

    程序2:不懂。先看看可以吧?

    import java.util.regex.*;

    class Regex1{

      public static void main(String args[]) {

             String str="For my money, the important thing "+

             "about the meeting was bridge-building";

             String regEx="a|f";   //表示af

             Pattern p=Pattern.compile(regEx);

             Matcher m=p.matcher(str);

             boolean result=m.find();

             System.out.println(result);

           }

    }

           如果str匹配regEx,那么resulttrue,否则为flase。如果想在查找时忽略大小写,则可以写成:

    Pattern p=Pattern.compile(regEx,Pattern.CASE_INSENSITIVE);

           虽然暂时不知道Pattern(模板、模式)和Matcher(匹配器)的细节,程序的感觉就比较爽,如果先查询is、后来又要查询thingting,我们只需要修改一下模板Pattern,而不是考虑if语句和for语句,或者通过charAt()

    1、写一个特殊的字符串——正则表达式如a|f

    2、将正则表达式编译成一个模板:p

    3、用模板p去匹配字符串str

           思路清楚了,现在看Java是如何处理的(Java程序员直到JDK1.4才能使用这些类。

    §3 Pattern类与查找

           public final class java.util.regex.Pattern正则表达式编译后的表达法。下面的语句将创建一个Pattern对象并赋值给句柄pPattern p=Pattern.compile(regEx);

           有趣的是,Pattern类是final类,而且它的构造器是private。也许有人告诉你一些设计模式的东西,或者你自己查有关资料。这里的结论是:Pattern类不能被继承,我们不能通过new创建Pattern类的对象。

           因此在Pattern类中,提供了2个重载的静态方法,其返回值是Pattern对象(的引用)。如:

        public static Pattern compile(String regex) {

            return new Pattern(regex, 0);

        }

           当然,我们可以声明Pattern类的句柄,如Pattern p=null

           p.matcher(str)表示以用模板p去生成一个字符串str的匹配器,它的返回值是一个Matcher类的引用,为什么要这个东西呢?按照自然的想法,返回一个boolean值不行吗?

           我们可以简单的使用如下方法:

           boolean result=Pattern.compile(regEx).matcher(str).find();

           呵呵,其实是三个语句合并的无句柄方式。无句柄常常不是好方式。后面再学习Matcher类吧。先看看regEx——这个怪咚咚。

    §4 正则表达式之限定符

           正则表达式(Regular Expression是一种生成字符串的字符串。晕吧。比如说,String regEx="me+";这里字符串me+能够生成的字符串是:memeemeeemeeeeeeeeee等等,一个正则表达式可能生成无穷的字符串,所以我们不可能(有必要吗?)输出正则表达式产生的所有东西。

    反过来考虑,对于字符串:memeemeeemeeeeeeeeee等等,我们能否有一种语言去描述它们呢?显然,正则表达式语言是这种语言,它是一些字符串的模式——简洁而深刻的描述。

    我们使用正则表达式,用于字符串查找、匹配、指定字符串替换、字符串分割等等目的。

     

           生成字符串的字符串——正则表达式,真有些复杂,因为我们希望由普通字符(例如字符 a z)以及特殊字符(称为元字符)描述任意的字符串,而且要准确。

           先搞几个正则表达式例子:

           程序3:我们总用这个程序测试正则表达式。

           import java.util.regex.*;

    class Regex1{

      public static void main(String args[]) {

             String str="For my money, the important thing "

             String regEx="ab*";

             boolean result=Pattern.compile(regEx).matcher(str).find();

             System.out.println(result);

           }

    }//ture

    "ab*"——能匹配aababbabbb……。所以,*表示前面字符可以有零次或多次。如果仅仅考虑查找,直接用"a"也一样。但想想替换的情况。 问题regEx="abb*"结果如何?

    "ab+"——能匹配ababbabbb……。等价于"abb*"问题regEx="or+"结果如何?

    "or?"——能匹配oor? 表示前面字符可以有零次或一次。

           这些限定符*+?方便地表示了其前面字符(子串)出现的次数(我们用{}来描述):

    x*

    零次或多次 ≡{0,}

    x+

    一次或多次 ≡{1,}

    x?

    零次或一次 ≡{0,1}

    x{n}

    n次(n>0

    x{n,m}

    最少n次至最多m次(0<n<m

    x{n,}

    最少n,

          

    现在我们知道了连续字符串的查找、匹配。下面的是一些练习题:

    ①查找粗体字符串(不要求精确或要求精确匹配),写出其正则表达式:

    str

    regEX(不要求精确)

    regEX(要求精确)

    试一试

    abcffd

    bbcffbcf*bc*bc+

  • java按值传递还是按引用传递小结

    2011-05-05 22:59:43Top 2 Digest 2

    转贴:http://blog.csdn.net/yqj2065/archive/2008/11/12/3279116.aspx

    CSDN上的一篇《java按值传递还是按引用传递详细解说》,于是发了一个帖子,看看CSDN上的反映,

    为什么? 关于Java只有按值传递

    -----------------------------------------------------------------------------

    Java只有按值传递,但是看见一些人反复讨论这个问题,不禁要问一下为什么?

    1、说“按引用传递”的人也是经过思考的,是不是他们使用了不同的术语,或者他们的术语不同于我们的术语?
    2、说“按引用传递”的人,有哪些典型人物?
    我知道的有TIJ,忘记第几版了,最新版改了没有,知道的说一下。
    还有CSDN上的一篇《java按值传递还是按引用传递详细解说》
    有没有正规教材上说“按引用传递”的?
    3、C语言也讨论“按引用传递”这个问题吗?
    4、为什么“按引用传递”是错误的?[不要讨论它错误之处,而是问一问他们的思路为什么错误】?

    --------------------------------------------------------------------

    反馈如下:

    1、有人说:

       1 我把房子搬着,然后把房子给你,这就是传值
       2 我把写有房子地址的纸条给你,这就是传引用

    房子、纸条寓意很好,直观。可以借鉴。虽然他的概念错误。

    2、《详细解说》不看也罢,都是术语混用带来的问题,一点不新鲜。

    3、Thinking in Java作者居然在第4版还坚持“按引用传递”,有一天他会后悔的。关于Thinking in Java,假设世界上没有C++,只有C和fortran语言,我不知道他说的那些东西那里准确.

    4、“问题还是出在java设计时的术语选择上,比如Object obj;把这个obj称为“引用”,同时强调传参数时不是"pass by reference",本身就怪怪的。”术语混用带来的问题,注意永远不能够混用变量和它的值。

    int i;i是值吗?不是。
    Object obj;obj是值(引用)吗?不是。

    要强调这一点!

    5、我说的:Java中接口的术语也讨厌的很,因为我们在非Java的环境中广泛使用的接口概念遇到它的干扰。
    虽然Java中接口的确是个很好的术语。

    6、【《java2入门经典》也说的是基本类型传的是值得副本,对象传的引用的副本。在那一页,我忘了,反正着本书讲的挺好的,用了3页讲这个知识点,还带图着呢,】传的引用的副本,没有问题。他理解出问题了。

    7、注意:

    对比几种译本 VS 原版讲解
    基础概念 Vs 奇技淫巧(茴香豆的第五种写法)
    8、传引用 VS 传的是Java引用

    foo(int i){}
    obj.foo(jjjj)我送你一个盒子jjjj如果我把jjjj这个盒子搬着,然后把盒子给你,典型的传引用!
    9、“无视这个问题就行了”,常见的说法,为什么会这样?考虑,,

    10、“他们有争议的,是这种传递方式的抽象命名而已.”

    这是一句话就能够解决的问题,“按引用传递”是各种各样不同语言的传参数的方式之一,意思是方法操作实参本身.Java的引用和C语言的指针,它们都是copy后传递的.

    11、“可能誤解,就由這種高深見解而來的
    引用也是一個值,只是對引用的操作即是對引用所對應的實際對象進行操作而己
    從來java中就不能直接對對象進行操,只能以引用進行
    你何時傳遞過對象了?”这个正解。

    12、“我了解的是基本类型按值传递,对象是引用传递。”、“Java只有按值传递?谁说的???”毒害很广,

    13、“JAVA中当然有搬房子啊! 比如数字1,作为参数传递时”,提醒了我,需要介绍参数传递的来源,它为什么重要?因为模块化。

    int i =5;
    A a
    = new A();
    obj.doSth(i);
    //doSth把纸条加10
    obj.doOtherSth(a);//doOtherSth把a指向我自己买的房子,
    print(i);//5 VS 15?
    a.装修();// 我自己 Vs 你写的


  • JAVA如何调用DOS命令

    2011-09-13 22:54:05

    用Java编写应用时,有时需要在程序中调用另一个现成的可执行程序或系统命令,这时可以通过组合使用Java提供的Runtime类和Process类的方法实现。下面是一种比较典型的程序模式:
    ...
    Process process = Runtime.getRuntime().exec(".\\p.exe");
    process.waitfor( );
    ...
    在 上面的程序中,第一行的“.\\p.exe”是要执行的程序名,Runtime.getRuntime()返回当前应用程序的Runtime对象,该对象 的exec()方法指示Java虚拟机创建一个子进程执行指定的可执行程序,并返回与该子进程对应的Process对象实例。通过Process可以控制 该子进程的执行或获取该子进程的信息。第二条语句的目的等待子进程完成再往下执行。
    但在windows平台上,如果处理不当,有时并不能得到预期的结果。下面是笔者在实际编程中总结的几种需要注意的情况:
    1、执行DOS的内部命令
    如 果要执行一条DOS内部命令,有两种方法。一种方法是把命令解释器包含在exec()的参数中。例如,执行dir命令,在NT上, 可写成exec("cmd.exe /c dir"),在windows 95/98下,可写成“command.exe /c dir”,其中参数“/c”表示命令执行后关闭Dos立即关闭窗口。另一种方法是,把内部命令放在一个批命令my_dir.bat文件中,在Java程序 中写成exec("my_dir.bat")。如果仅仅写成exec("dir"),Java虚拟机则会报运行时错误。前一种方法要保证程序的可移植性, 需要在程序中读取运行的操作系统平台,以调用不同的命令解释器。后一种方法则不需要做更多的处理。
    2、打开一个不可执行的文件
    打开一个不可执行的文件,但该文件存在关联的应用程序,则可以有两种方式。 以打开一个word文档a.doc文件为例,Java中可以有以下两种写法:
    exec("start .\\a.doc");
    exec(" c:\\Program Files\\Microsoft Office\\office\\winword.exe .\\a.doc");
    显然,前一种方法更为简捷方便。
    3、执行一个有标准输出的DOS可执行程序
    在 windows平台上,运行被调用程序的DOS窗口在程序执行完毕后往往并不会自动关闭,从而导致Java应用程序阻塞在waitfor( )。导致该现象的一个可能的原因是,该可执行程序的标准输出比较多,而运行窗口的标准输出缓冲区不够大。解决的办法是,利用Java提供的Process 类提供的方法让Java虚拟机截获被调用程序的DOS运行窗口的标准输出,在waitfor()命令之前读出窗口的标准输出缓冲区中的内容。一段典型的程 序如下:
    ...
    String ls_1;
    Process process = Runtime.getRuntime().exec("cmd /c dir \\windows");
    BufferedReader bufferedReader = new BufferedReader( \
    new InputStreamReader(process.getInputStream());
    while ( (ls_1=bufferedReader.readLine()) != null)
    System.out.println(ls_1);

    process.waitfor( );
    ...

    以上内容为转载~下面内容为原创!

    今天在做客户端程序的自动更新,简单描述一下,就是从服务器上将更新包下载下来,然后在本地解压缩,最后删掉~功能很简单~

    但 是问题出在使用JAVA的ZIP模块做文件的解压缩不是想象的那么简单,资源需要释放,一个不小心就没有办法删除掉原有ZIP文件了~资源的占用确实是个 大问题,但是好在,客户端程序更新完是要重启的,一切都烟消云散了~对于删除不掉ZIP文件的问题,我也流氓一下~用DEL硬删除~此处一定要注意!

    Process process = Runtime.getRuntime().exec("cmd /c del f:\\aaa.doc");
    这样的调用是没有问题~

    Process process = Runtime.getRuntime().exec("del f:\\aaa.doc");
    这样写是不可能对的~

  • [转贴]马士兵Java学习笔记

    2011-05-08 17:32:28

    1115

    键盘事件

     

    当按下一个键时对应一个虚拟的码

    例子:

    import java.awt.*;

    import java.awt.event.*;

     

    public class TestKey {

           public static void main(String[] args) {

                  new KeyFrame().launchFrame();

           }

    }

     

    class KeyFrame. extends Frame. {

           public void launchFrame() {

                  setSize(200, 200);

                  setLocation(300,300);

                  addKeyListener(new MyKeyMonitor());

                  setVisible(true);

           }

          

           class MyKeyMonitor extends KeyAdapter {

                  public void keyPressed(KeyEvent e) {

                         int keyCode = e.getKeyCode();

                         if(keyCode = = KeyEvent.VK_UP) {

                                System.out.println("UP");

                         }

                  }

           }

    }

     

    KeyEvent类的方法public int getKeyCode()返回与此事件中的键关联的整数 keyCode。也就是判断敲击的是哪一个键,返回值是整形。

    KeyEvent类的字段摘要public static final int VK_UP用于非数字键盘向上方向键的常量。
    
  • [转贴]马士兵Java学习笔记

    2011-05-08 17:31:59

    1113

    Windows和匿名类

     

    例一:

    import java.awt.*;

    import java.awt.event.*;

    public class TestWindowClose {

      public static void main(String args[]) {

          new MyFrame55("MyFrame");

      }

    }

    class MyFrame55 extends Frame. {

      MyFrame55(String s) {

        super(s);

        setLayout(null);

        setBounds(300, 300, 400, 300);

        this.setBackground(new Color(204, 204, 255));

        setVisible(true);

        this.addWindowListener(new MyWindowMonitor());

         }

     

      class MyWindowMonitor extends WindowAdapter {//此为内部类

          public void windowClosing(WindowEvent e) {

                 setVisible(false);//让窗口不可见

                 System.exit(0);//退出

          }

      }

    }

     

     

    例二:

    利用了匿名类,也叫局部类,匿名的内部类

    逻辑较简单,语句较少,变动较少时可以使用内部类。很少使用。

    import java.awt.*;

    import java.awt.event.*;

    public class TestWindowClose {

      public static void main(String args[]) {

        new MyFrame55("MyFrame");

      }

    }

    class MyFrame55 extends Frame. {

      MyFrame55(String s) {

        super(s);

        setLayout(null);

        setBounds(300, 300, 400, 300);

        this.setBackground(new Color(204, 204, 255));

        setVisible(true);   

        this.addWindowListener(

        new WindowAdapter() {//此为一个内部类,注意这里new出来的是一个父类的对象!

          public void windowClosing(WindowEvent e) {

            setVisible(false);

            System.exit(-1);

          }

        });

       }

     }

  • [转贴]马士兵Java学习笔记

    2011-05-08 17:31:22

    1112

    Adapter and repaint

     

    软件包 java.awt.event 提供处理由 AWT 组件所激发的各类事件的接口和类。

    比如ActionListener用于接收操作事件的侦听器接口。对处理操作事件感兴趣的类可以实现此接口,而使用该类创建的对象可使用组件的 addActionListener 方法向该组件注册。
    
    比如KeyListener用于接收键盘事件(击键)的侦听器接口。
    
    比如MouseListener用于接收组件上感兴趣的鼠标事件(按下、释放、单击、进入或离开)的侦听器接口
    
    比如TextListener用于接收文本事件的侦听器接口。
    
    比如WindowListener用于接收窗口事件的侦听器接口。
    
     
    
    注意到这个问题:ActionListene接口有一个方法public void actionPerformed(ActionEvent e)所以实现这个接口时要重写这个方法。但是MouseListener接口有mouseClickedmousePressed等五个方法,如果实现这个接口则要全部重写这五个方法。哪怕是没有用的,也要写空操作。
    
     
    
    为了解决这种麻烦可使用适配器Adapter。适配器都实现了相应的接口(相当于适配器是这些接口的子类)。比且在这些适配器里都有被实现的接口里的方法的空操作。使用适配器时根据自身需要重写某方法即可。比如java.awt.event.MouseAdapter实现MouseListener接口,可使用其子类(MouseAdapter)作为MouseEvent的监听器,只要重写其相应的方法即可,即public abstract class MouseAdapter extends Object implements MouseListener, MouseWheelListener, MouseMotionListener
    

    接收鼠标事件的抽象适配器类。此类中的方法为空。此类存在的目的是方便创建侦听器对象。对于其他的监听器,也有对应的适配器。使用适配器可以避免监听器类定义没有必要的空方法。

     

    例子:

    import java.awt.*;

    import java.awt.event.*;

    import java.util.*;

    public class MyMouseAdapter{

      public static void main(String args[]) {

        new MyFrame("drawing...");

      }

    }

     

    class MyFrame. extends Frame. {

      ArrayList points = null;

      MyFrame(String s) {

        super(s);

        points = new ArrayList();

        setLayout(null);

        setBounds(300,300,400,300);

        this.setBackground(new Color(204,204,255));

    setVisible(true);

       this.addMouseListener(new Monitor());//给此Frame添加监听器。Frame的父类Component有此方法public void addMouseListener(MouseListener l)添加指定的鼠标侦听器,以接收发自此组件的鼠标事件。参数:l - 鼠标侦听器

     

           }

          

           public void paint(Graphics g) {

        Iterator i = points.iterator();

        while(i.hasNext()){

          Point p = (Point)i.next();

          g.setColor(Color.BLUE);

          g.fillOval(p.x,p.y,10,10);// p.x,p.y即鼠标点在哪里则在哪开始画圆

        }

      }

     

      public void addPoint(Point p){

        points.add(p);//把点都加进points

      }

    }

     

    class Monitor extends MouseAdapter {//不实现MouseListener而是继承MouseAdapter

      public void mousePressed(MouseEvent e) {

        MyFrame. f = (MyFrame)e.getSource();

        f.addPoint(new Point(e.getX(),e.getY()));

        f.repaint();//

      }

    }

     

    分析f.repaint()Component类的方法public void repaint()重绘此组件。在此f.repaint()使此Frame强制重画,若不repaint()那么你虽然点击了鼠标,但是看不见这些点,因为没有重画。深层原因:调用repaint()时先调用了update()再调用了paint()(这是双缓冲的基础)。
    
  • [转贴]马士兵Java学习笔记

    2011-05-08 17:30:44

    1110

    内部类

     

    利用内部类在1109的两个方法的基础上改进加法运算的实现

     

    import java.awt.*;

    import java.awt.event.*;

    public class TFMath {

           public static void main(String[] args) {

                  new TFFrame().launchFrame();

           }

    }

     

    class TFFrame. extends Frame. {

           TextField num1, num2, num3;

           public void launchFrame() {

                  num1 = new TextField(10);

                  num2 = new TextField(10);

                  num3 = new TextField(15);

                  Label lblPlus = new Label("+");

                  Button btnEqual = new Button("=");

                  btnEqual.addActionListener(new MyMonitor());

                  setLayout(new FlowLayout());

                  add(num1);

                  add(lblPlus);

                  add(num2);

                  add(btnEqual);

                  add(num3);

                  pack();

                  setVisible(true);

           }

          

           class MyMonitor implements ActionListener  { //MyMonitor类声明在TFFrame类的内部,与成员变量的位置并列等同即声明在包装类内且方法外(注意不可以设置在TFFrame类的成员方法中)。这样就可以访问其包装类的成员变量和成员方法(这是内部类经常使用的方式)。这样做也避免了其他的类来访问监听器类,即保护了监听器类。也可声明为private class

                  public void actionPerformed(ActionEvent e) {

                         int n1 = Integer.parseInt(num1.getText());

                         int n2 = Integer.parseInt(num2.getText());

                         num3.setText("" + (n1+n2));

                  }

           }

    }

     

     

     

    1111

    Paint方法

    Graphics 类是所有图形上下文的抽象基类,允许应用程序在组件(已经在各种设备上实现)以及闭屏图像上进行绘制。

    每个Component都有一个paintGraphics g)用于实现绘图目的,每次重画该Component时都自动调用paint方法。什么是重画呢?就是需要再次显示这个Component时(比如一个Frame)比如在最小化,然后最大化或者复原时。因为在一个Component重新显示时,这些图像需要重新显示

     

    例子:

    import java.awt.*;

    public class TestPaint {

           public static void main(String[] args) {

                  new PaintFrame().launchFrame();

           }

    }

     

    class PaintFrame. extends Frame. {

           public void launchFrame() {

                  setBounds(200,200,640,480);

                  setVisible(true);

           }

          

           public void paint(Graphics g) {//见下。具体的Component具体重写此方法

                  Color c = g.getColor();//得到画笔的颜色

                  g.setColor(Color.red);//将画笔颜色重新设置为红色

                  g.fillOval(50, 50, 30, 30);

                  g.setColor(Color.green);

                  g.fillRect(80,80,40,40);

                  g.setColor(c);//将画笔颜色还原即恢复现场

           }

    }

     

    分析将Graphics想象为一个画笔类,那么g是一支画笔。
    
    一定要注意paint()方法在哪个类里面,则画笔在哪个类中被使用。且这个paint方法是被自动调用的
    
    paint()方法来自于Frame的父类Container
    
    Container类方法public void paint(Graphics g)绘制容器。该方法将 paint 转发给任意一个此容器子组件的轻量级组件
    
  • [转贴]马士兵Java学习笔记

    2011-05-08 17:30:12

    1109

    持有对方引用

     

    实现加法方法一:

    import java.awt.*;

    import java.awt.event.*;

    public class TFMath {

           public static void main(String[] args) {

                  new TFFrame().launchFrame();//new一个对象,然后调用方法

           }

    }

     

    class TFFrame. extends Frame. {

    public void launchFrame() {//用来运行TFFrame

           TextField  num1 = new TextField(10);//设置对话框宽度为10个字符

           TextField  num2 = new TextField(10);

           TextField  num3 = new TextField(15);

           Label lblPlus = new Label("+");

           Button btnEqual = new Button("=");

           btnEqual.addActionListener(new MyMonitor(num1num2num3));

           setLayout(new FlowLayout());//Frame默认布局管理是BorderLayout此设为FlowLayout

           add(num1);

           add(lblPlus);

           add(num2);

           add(btnEqual);

           add(num3);

           pack();

           setVisible(true);

             }

           }

            Class MyMonitor implements ActionListener  {//在等号上实现监听

            TextField  num1num2num3

            public MyMonitor(TextField num1,TextField num2,TextField num3){

               this.num1=num1;

    this.num2=num2;

    this.num3=num3;

    }

                  public void actionPerformed(ActionEvent e) {//所有接口里的方法均为public

                         int n1 = Integer.parseIn2t(num1.getText());

                         int n2 = Integer.parseInt(num2.getText());

                         num3.setText("" + (n1+n2));

                  }

    }

     

    主要问题是要拿到num1num2,但是ActionEvent e是来自于等号,与他们无关;所以通过e.getSource()时不可能的。所以通过Class MyMonitor implements ActionListener{}来实现。但是需要注意几个问题:1在等号实现监听时必须new出来一个以TFFrame 类中的num1num2num3为参数的监视器即MyMonitor btnEqual.addActionListener(new MyMonitor(num1num2num3))。只有这样才可以实现与Class MyMonitor implements ActionListener{}的连接,这也间接得到了num1num2num3。(再次强调:一定要注意num1num2num3就是class TFFrame的局部变量(成员变量)且是TextField类型的)2 Class MyMonitor implements ActionListener{}中必须有三个TextField类型的成员函数。这样才可以实现形参和实参的匹配。

    所以这种方法的主要问题就是在写监视器时要保留好多其他类的局部变量(成员变量)。这就是问题2体现出来的。

    如果把TFFrame 类中的num1num2num3定义为成员变量则还有更好的办法,如方法二:在监听器对象里面持有TFFrame类的引用

     

    实现加法方法二:

    import java.awt.*;

    import java.awt.event.*;

    public class TFMath {

           public static void main(String[ ] args) {

                  new TFFrame().launchFrame();

           }

    }

     

    class TFFrame. extends Frame. {

           TextField num1, num2, num3;

           public void launchFrame() {

                  num1 = new TextField(10);

                  num2 = new TextField(10);

                  num3 = new TextField(15);

                  Label lblPlus = new Label("+");

                  Button btnEqual = new Button("=");

                  btnEqual.addActionListener(new MyMonitor(this));

                  setLayout(new FlowLayout());

                  add(num1);

                  add(lblPlus);

                  add(num2);

                  add(btnEqual);

                  add(num3);

                  pack();

                  setVisible(true);

           }

          

           class MyMonitor implements ActionListener {

                  TFFrame. tf = null;//作为MyMonitor的成员变量tfTFFrame类型的,所以tfnum1num2num3。但是tf的值目前是空的。

                  public MyMonitor(TFFrame. tf) {//通过构造方法来拿到引用,和“大管家”取得联系

                  this.tf = tf;

           }

                  public void actionPerformed(ActionEvent e) {

                  int n1 = Integer.parseInt(tf.num1.getText());

                  int n2 = Integer.parseInt(tf.num2.getText());

                  tf.num3.setText("" + (n1+n2));

               }

    }

     

    注意:1、关于this参见0317

          2、在新建一个MyMonitor 对象时使用thisbtnEqual.addActionListener(new MyMonitor(this));

  • [转贴]马士兵Java学习笔记

    2011-05-08 17:29:32

    1107

    事件模型2

     

    范例  名称:Java事件处理举例

    源文件名称:TestActionEvent2.java

          点:

                  1. 一个事件源组件上可以同时注册多个监听器

                  2. 一个监听器对象可以同时注册到多个事件源组件上

                  3. 事件源的信息可以随它所触发的事件自动传递到所有注册过的监听器

     

    import java.awt.*;

    import java.awt.event.*;

    public class TestActionEvent2 {

        public static void main(String args[]) {

                         Frame. f = new Frame("Test");

                         Button b1 = new Button("Start");

                         Button b2 = new Button("Stop");

                         Monitor2 bh = new Monitor2();

                         b1.addActionListener(bh);      

                         b2.addActionListener(bh);//一个监听器监听了两个按钮

                         b2.setActionCommand("game over");

                         f.add(b1,"North");      

                         f.add(b2,"Center");

                         f.pack();                

                         f.setVisible(true);

        }

    }

     

    class Monitor2 implements ActionListener {

        public void actionPerformed(ActionEvent e) {

               System.out.println("a button has been pressed," +

               "the relative info is:\n " + e.getActionCommand());   

           }

    }

     

    Button类的方法public void setActionCommand(String command)设置此按钮激发的动作事件的命令名称。在默认情况下,此动作命令设置为与按钮标签相匹配。

     

    Button类的方法public String getActionCommand()返回此按钮激发的动作事件的命令名称(与setActionCommand对应)。如果命令名称为 null(默认),则此方法返回按钮的标签。

     

     

    1108

    TextFiled

    Java.awt.TextFiled类用来创建文本框对象

     

    例子:

    import java.awt.*;

    import java.awt.event.*;

    public class TFActionEvent {

           public static void main(String[] args) {

                                new TFFrame();

           }

    }

     

    class TFFrame. extends Frame{

           TFFrame( ){//构造方法

                  TextField tf = new TextField( );

                  add(tf);//tf添加到TFFrame

                  tf.addActionListener(new TFActionListener( ));

                  pack( );

                  setVisible(true);

           }

    }

     

    class TFActionListener implements ActionListener{

               public void actionPerformed(ActionEvent e){//读出对话框里的内容

                  TextField tf = (TextField)e.getSource();//强制转换为TextField

                  System.out.println(tf.getText());//拿到内容

                  tf.setText("");//读出后,再将tf设置为空

           }

    }

     

    当某件事发生的时候,必须把此事封装成一个对象作为一个参数传递给监听器所实现接口里的特定方法。此例中当有输入时,它发出的这一件事叫ActionEvent e。所以可以在TFActionListener类中使用TFFrame类的局部变量tf

     

    ActionEvent 的父类EventObject 有方法public Object getSource( )返回:最初发生 Event 的对象。注意其返回值是Object

    javax.swing.text.JTextComponent类的方法public String getText()返回此 TextComponent 中包含的文本

     

    TextField类的方法public char getEchoChar( )获取用于回显的字符。*回显字符对于不应将用户输入回显到屏幕上的文本字段有用,例如输入密码的文本字段。例如tf.setEchoChar('*');将输入文本框tf的东西均显示为*
    
    TextField类的方法public void setText(String t)将此文本组件显示的文本设置为指定文本。
    
  • [转贴]马士兵Java学习笔记

    2011-05-08 17:28:59

    1104

    布局管理器2

    FlowLayout例子:

    import java.awt.*;

    public class TestFlowLayout2 {

        public static void main(String args[]) {

            Frame. f = new Frame("Java Frame");

            FlowLayout l = new FlowLayout(FlowLayout.CENTER, 20, 40);//20为水平间距,40为垂直间距

            f.setLayout(l);

            f.setLocation(300,400);

            f.setSize(300,200);

            f.setBackground(new Color(204,204,255));

            for(int i = 1; i<=7; i++){

                f.add(new Button("BUTTON"));

            }

            f.setVisible(true);

        }

    }

     

     

     

    BorderLayout布局管理器

    BorderLayoutFrame类的默认布局管理器

     

    BorderLayout将这个容器分为,东西南北中五个区域,组件只能添加到指定的区域,默认的是中(CENTER)。每个区域只能添加一个组件,若再次添加则会覆盖。

     

    例子:

           称:BorderLayout应用举例

          源文件名称 TestBorderLayout.java

              FlowLayout布局管理器的性质及用法

     

     

    import java.awt.*;

    public class TestBorderLayout {

           public static void main(String args[]) {

                  Frame. f;

                  f = new Frame("Border Layout");

                  Button bn = new Button("BN");

                  Button bs = new Button("BS");

                  Button bw = new Button("BW");

                  Button be = new Button("BE");

                  Button bc = new Button("BC");

                 

                  f.add(bn, "North");

                  f.add(bs, "South");

                  f.add(bw, "West");

                  f.add(be, "East");

                  f.add(bc, "Center");

                 

                 

                  // 也可使用下述语句,这种比较规范

                 

                 

                  f.setSize(200,200);

                  f.setVisible(true);

           }

    }

     

     

     

     

    GridLayout布局管理器

    GridLayout布局管理器将空间划分成规则的矩形网络,每个区域大小相等。排列顺序从左到右,从上至下

     

    例子:

           范例  名称:GridLayout应用举例

          源文件名称:TestGridLayout

                点:GridLayout布局管理器的性质及用法

     

     

    import java.awt.*;

    public class TestGridLayout {

           public static void main(String args[]) {

            Frame. f = new Frame("GridLayout Example");

            Button b1 = new Button("b1");

            Button b2 = new Button("b2");

            Button b3 = new Button("b3");

            Button b4 = new Button("b4");

            Button b5 = new Button("b5");

            Button b6 = new Button("b6");

            f.setLayout (new GridLayout(3,2));//指定了行列

            f.add(b1);       

            f.add(b2);

            f.add(b3);       

            f.add(b4);

            f.add(b5);       

            f.add(b6);

            f.pack(); //自动包住这些Button        

            f.setVisible(true);

           }

    }

     

    1106

    事件模型1

     

    范例  名称:Java事件处理举例

    源文件名称:TestActionEvent.java

          点:

                   1. Java事件处理机制

                      2. 事件源、事件监听器概念及作用

                      3. 如何在一个现有组件上注册监听器

    import java.awt.*;

    import java.awt.event.*;

    public class TestActionEvent {

        public static void main(String args[]) {

                         Frame. f = new Frame("Test");

                         Button b = new Button("Press Me!");

                         Monitor bh = new Monitor();//新建一个监听器

                         b.addActionListener(bh);//监听器bhButton这注册

                         f.add(b,BorderLayout.CENTER);

                         f.pack();

                         f.setVisible(true);

        }

    }

     

    class Monitor implements ActionListener {//Monitor要监听的是ActionListener类型的事件

        public void actionPerformed(ActionEvent e) {

            System.out.println("a button has been pressed");   

        }

    }

     

    要监听什么事,就得实现什么接口。所以事件有很多,接口也有很多,它们之间一一对应

    当某件事情发生,事件源对象会调用实现了某种监听器接口的类的对象。所以实现了某种监听器接口的类的对象必须把自己注册到事件源对象,告知对方:我在监听

    当某件事发生的时候,必须把此事封装成一个对象作为一个参数传递给监听器所实现接口里的特定方法

    此例中当press me被点击时,它发出的这一件事叫ActionEvent e

     

    Button类的方法public void addActionListener(ActionListener l)添加指定的动作侦听器,以接收发自此按钮的动作事件。当用户在此按钮上按下或释放鼠标时,发生动作事件。
    
     
    
    ActionEvent类指示发生了组件定义的动作的语义事件。当特定于组件的动作(比如被按下)发生时,由组件(比如 Button)生成此高级别事件。事件被传递给每一个 ActionListener 对象,这些对象是使用组件的 addActionListener 方法注册的,用以接收这类事件。 
    
     
  • [转贴]马士兵Java学习笔记

    2011-05-08 17:28:19

    1101

    GUIGraphics User Interfaca)图形用户界面

    AWTAbstract Window Toolkit)包括许多类和接口用于实现GUI编程

    ContainerComponent(可以显示的图形元素)是AWT的两个核心类。ContainerComponent的子类。Container用来容纳其他Component元素

    Container包含Window(包含FrameDialog)和Panel。但是Window作为一个应用程序的出口可以独立显示出来,其表示对象自由停泊的顶级窗口;Panel不能作为一个应用程序的出口可以独立显示出来,要想显示得把自己装到其他的Container里(比如WindowApplet)。

     

    Frame

    例子:

    范例名称:Frame 应用举例

    源文件名称:TestFrame.java

      点:Frame组件的创建及显示设置

     

    import java.awt.*;

    public class TestFrame. {

    public static void main( String args[]) {

                                        Frame. f = new Frame("My First Test");

    f.setLocation(300, 300);//指定Frame左上角的坐标

    f.setSize( 170,100);//单位是像素

    f.setBackground( Color.blue);

    f.setResizable(false);//设置是否可以更改大小

    f.setVisible( true);//设置可见

    }

    }

     

     

    1102

    Frame

    例子:

    import java.awt.*;

    public class TestMultiFrame. {

        public static void main(String args[]) {

            MyFrame. f1 = new MyFrame(100,100,200,200,Color.BLUE);

            MyFrame. f2 = new MyFrame(300,100,200,200,Color.YELLOW);

            MyFrame. f3 = new MyFrame(100,300,200,200,Color.GREEN);

            MyFrame. f4 = new MyFrame(300,300,200,200,Color.MAGENTA);

        }

    }

     

    class MyFrame. extends Frame{

        static int id = 0;

        MyFrame(int x,int y,int w,int h,Color color){

            super("MyFrame. " + (++id));

            setBackground(color);

            setLayout(null);

            setBounds(x,y,w,h);

            setVisible(true);

        }

    }

     

     

    Panel

    Panel对象可以看成可以容纳Component的空间

    Panel对象可以拥有自己的布局管理器

     

    例一:

    import java.awt.*;

    public class TestPanel {

         public static void main(String args[]) {

               Frame. f = new Frame("Java Frame. with Panel");

             Panel p = new Panel(null);

             f.setLayout(null);

             f.setBounds(300,300,500,500);//相对于屏幕的坐标

             f.setBackground(new Color(0,0,102));// 0,0,102代表红绿蓝三原色的分量

             p.setBounds(50,50,400,400);//相对于frame的坐标

             p.setBackground(new Color(204,204,255));

             f.add(p);//panel添加到frame

             f.setVisible(true);

        }

    }

    例二:

    import java.awt.*;

    public class TestMultiPanel {

        public static void main(String args[]) {

            new MyFrame2("MyFrameWithPanel",300,300,400,300);

        }

    }

     

    class MyFrame2 extends Frame{

        private Panel p1,p2,p3,p4;

        MyFrame2(String s,int x,int y,int w,int h){

            super(s);

            setLayout(null);

            p1 = new Panel(null);

     p2 = new Panel(null);

            p3 = new Panel(null);

    p4 = new Panel(null);//null表示此panel不带自己的布局管理器

            p1.setBounds(0,0,w/2,h/2);

            p2.setBounds(0,h/2,w/2,h/2);

            p3.setBounds(w/2,0,w/2,h/2);

            p4.setBounds(w/2,h/2,w/2,h/2);

            p1.setBackground(Color.BLUE);

            p2.setBackground(Color.GREEN);

            p3.setBackground(Color.YELLOW);

            p4.setBackground(Color.MAGENTA);

            add(p1);

    add(p2);

    add(p3);

    add(p4);

            setBounds(x,y,w,h);

            setVisible(true);

        }

    }

     

    1103

    布局管理器1

    Awt提供了五种布局管理器

    FlowLayout

    BorderLayout

    GridLayout

    CardLayout

    GridBagLayout

     

    FlowLayoutPanel类的默认布局管理器,逐行开始排列从左到右。FlowLayout默认对齐方式是居中

     

    例子:

    范例名称:FlowLayout 用法举例

    源文件名称:TestFlowLayout.java

      点:

                 1. 布局管理器的概念和作用

             2. FlowLayout的性质及用法

     

    import java.awt.*;

    public class TestFlowLayout {

        public static void main(String args[]) {

                 Frame. f = new Frame("Flow Layout");

            Button button1 = new Button("Ok");

            Button button2 = new Button("Open");

            Button button3 = new Button("Close");

            f.setLayout(new FlowLayout(FlowLayout.LEFT));//指定布局管理器,且左对齐

            f.add(button1);

            f.add(button2);

            f.add(button3);

            f.setSize(100,100);

            f.setVisible(true);

        }

    }

     
  • [转贴]马士兵Java学习笔记

    2011-05-08 17:27:41

    1006

     

    利用UDP传送一个long类型的数,服务器端收到后再次将其转换为long类型

    例子:

    import java.net.*;

    import java.io.*;

    public class TestUDPClient{

           public static void main(String args[]) throws Exception

           {

                  long n = 10000L;

                  ByteArrayOutputStream baos = new ByteArrayOutputStream();

                  DataOutputStream dos = new DataOutputStream(baos);//只有 DataOutputStream才可以直接往外写一个long类型的数

                  dos.writeLong(n);//把写到字节数组。

                  byte[] buf = baos.toByteArray();//baos里的东西转换为字节数组,即拿到字节数组

                  DatagramPacket dp=new DatagramPacket(buf,buf.length,new InetSocketAddress("127.0.0.1", 5678));//因为UDP本身没有连接,所以需要在包裹上写目的地址,端口

                  DatagramSocket ds = new DatagramSocket(9999);//自己用的是9999端口,向对方127.0.0.1 5678发数据

                  ds.send(dp);//发送数据

                  ds.close();

                 

           }

    }

     

     

    import java.net.*;

    import java.io.*;

    public class TestUDPServer{

           public static void main(String args[]) throws Exception{

                  byte buf[] = new byte[1024];//实际存数据的地方是此数组

                  DatagramPacket dp = new DatagramPacket(buf, buf.length);//数组外套了一个包裹

                  DatagramSocket ds = new DatagramSocket(5678);//指定了监听端口

                  while(true)

                  {

                         ds.receive(dp);//只要ds收到数据就扔到dp里面

                         ByteArrayInputStream bais = new ByteArrayInputStream(buf);//将字节数组里的东西输入程序

                         DataInputStream dis = new DataInputStream(bais);// DataInputStream可以把字节转换为long类型

                         System.out.println(dis.readLong());//包裹里所存数据的长度

                  }

           }

    }

    ByteArrayOutputStream类:实现了一个输出流,其中的数据被写入一个 byte 数组。
    
    ByteArrayOutputStream类方法public byte[] toByteArray()创建一个新分配的 byte 数组。其大小是此输出流的当前大小,并且缓冲区的有效内容已复制到该数组中。返回:以 byte 数组的形式返回此输出流的当前内容。即拿到字节数组
    
     
    
    DataOutputStream类:数据输出流允许应用程序以适当方式将基本 Java 数据类型写入输出流中。
    
    DataOutputStream类方法public final void writeLong(long v)throws IOException将一个 long 值以 8-byte 值形式写入基础输出流中,先写入高字节。
    
     
    
    DataInputStream类:数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型
    

    DataInputStream类方法public final long readLong()throws IOException从包含的输入流中读取此操作需要的字节。返回:此输入流的下八个字节,将它们解释为一个 long

     

     

    DatagramSocket类构造方法public DatagramSocket(int port)throws SocketException创建数据报套接字并将其绑定到本地主机上的指定端口。
    
    DatagramSocket类方法public void receive(DatagramPacket p)throws IOException从此套接字接收数据报包。当此方法返回时,DatagramPacket 的缓冲区填充了接收的数据。数据报包也包含发送方的 IP 地址和发送方机器上的端口号。 此方法在接收到数据报前一直阻塞
    

    DatagramSocket类方法public void send(DatagramPacket p)throws IOException从此套接字发送数据报包。DatagramPacket 包含的信息指示:将要发送的数据、其长度、远程主机的 IP 地址和远程主机的端口号。

     
    
    DatagramPacket类构造方法public DatagramPacket(byte[] buf,int length)构造 DatagramPacket,用来接收长度为 length 的数据包。length 参数必须小于等于 buf.length。参数:buf - 保存传入数据报的缓冲区。len - 要读取的字节数。
    

    DatagramPacket类构造方法public DatagramPacket(byte[] buf,int length,SocketAddress address)

    throws SocketException构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。length 参数必须小于等于 buf.length

    DatagramPacket类方法public int getLength()返回将要发送或接收到的数据的长度
    
     
    
    String类构造方法public String(byte[] bytes,int offset,int length)通过使用平台的默认字符集解码指定的 byte 子数组,构造一个新的 String。新 String 的长度是字符集的函数,因此可能不等于该子数组的长度。
  • [转贴]马士兵Java学习笔记

    2011-05-08 17:26:21

    1005

    UDP

    严格来讲UDP没有客服端,服务器端的概念;它不区分这个

     

    例子:

    import java.net.*;

    public class TestUDPClient{

           public static void main(String args[]) throws Exception{

                     byte[] buf = (new string (“Hello”)).getBytes();//利用getBytes()把一个字符串转换成为一个字节数组

              DatagramPacket dp=new DatagramPacket (buf, buf.length, new InetSocketAddress("127.0.0.1", 5678));//把字节数组扔到一个包裹里面。因为UDP本身没有连接,所以需要在包裹上写目的地址,端口

                  DatagramSocket ds = new DatagramSocket(9999);//自己用的是9999端口,向对方127.0.0.15678发数据

                  ds.send(dp);//发送数据

                  ds.close( );

                 

           }

    }

     

     

    import java.net.*;

    public class TestUDPServer{

           public static void main(String args[]) throws Exception{

                  byte buf[] = new byte[1024];//实际存数据的地方是此数组

                  DatagramPacket dp = new DatagramPacket(buf, buf.length);//数组外套了一个包裹

                  DatagramSocket ds = new DatagramSocket(5678);//指定了监听端口

                  while(true) {

    ds.receive(dp);//只要ds收到数据就扔到dp里面

                             System.out.println(new string (buf,0,dp.getLength() ) );// dp.getLength()是此数组实际收到的字节数

                  }

           }

    }

     

     

    DatagramSocket类构造方法public DatagramSocket(int port)throws SocketException创建数据报套接字并将其绑定到本地主机上的指定端口。
    
    DatagramSocket类方法public void receive(DatagramPacket p)throws IOException从此套接字接收数据报包。当此方法返回时,DatagramPacket 的缓冲区填充了接收的数据。数据报包也包含发送方的 IP 地址和发送方机器上的端口号。 此方法在接收到数据报前一直阻塞
    

    DatagramSocket类方法public void send(DatagramPacket p)throws IOException从此套接字发送数据报包。DatagramPacket 包含的信息指示:将要发送的数据、其长度、远程主机的 IP 地址和远程主机的端口号。

     
    
    DatagramPacket类构造方法public DatagramPacket(byte[] buf,int length)构造 DatagramPacket,用来接收长度为 length 的数据包。length 参数必须小于等于 buf.length。参数:buf - 保存传入数据报的缓冲区。len - 要读取的字节数。
    

    DatagramPacket类构造方法public DatagramPacket(byte[] buf,int length,SocketAddress address)

    throws SocketException构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。length 参数必须小于等于 buf.length

    DatagramPacket类方法public int getLength()返回将要发送或接收到的数据的长度
    
     
    
    String类构造方法public String(byte[] bytes,int offset,int length)通过使用平台的默认字符集解码指定的 byte 子数组,构造一个新的 String。新 String 的长度是字符集的函数,因此可能不等于该子数组的长度。
  • [转贴]马士兵Java学习笔记

    2011-05-08 17:25:44

    1003

    TCP Socket通信模型

    Server

    1新建一个ServerSocket s (指定端口)

    2 s.accept( )//等待连接

    3 将对方的连接封装为一个socket对象

    4 它有自己的OutputStreamInputStream

    5 socket.close( )

     

    Client

    1 新建一个Socket(指定对方IP,指定对方端口号)

    2 建立连接

    3 通过自己的自己的OutputStreamInputStream和对方联系

    4 socket.close( )

    注意:socket两端都有自己的OutputStreamInputStream

     

    范例名称:简单的client/server程序

    源文件名:TestClient.java/TestServer.java

      点:

                 1. Java Socket编程步骤

                 2. Socket/ServerSocket类用法

                 3. 通过Socket对象可以获取通信对方Socket的信息

     

    例一:

    import java.net.*;

    import java.io.*;

    public class TestServer {

           public static void main(String args[]) {

                  try {             

                         ServerSocket s = new ServerSocket(8888);

                         while (true) {

                                Socket s1 = s.accept();

                                OutputStream s = s1.getOutputStream();

                                DataOutputStream dos = new DataOutputStream(os);

                                dos.writeUTF("Hello," + s1.getInetAddress() +

                                              "port#" +s1.getPort() + "  bye-bye!");//获得客户端的IPport

                                dos.close();

                                s1.close();

                         }

                  }catch (IOException e) {

                         e.printStackTrace();

                         System.out.println("程序运行出错:" + e);                 

                  }

           }

    }

     

     

    import java.net.*;

    import java.io.*;

     

    public class TestClient {

           public static void main(String args[]) {

                  try {

                         Socket s1 = new Socket("127.0.0.1", 8888);

                         InputStream is = s1.getInputStream();

                         DataInputStream dis = new DataInputStream(is);

                         System.out.println(dis.readUTF());

                         dis.close();

                         s1.close();

                  } catch (ConnectException connExc) {

                         connExc.printStackTrace();

                         System.err.println("服务器连接失败!");

                  } catch (IOException e) {

                         e.printStackTrace();

                  }

           }

    }

     

     

    ServerSocket类的方法

    构造方法public ServerSocket(int port)throws IOException创建绑定到特定端口的服务器套接字
    
    方法public Socket accept()throws IOException侦听并接受到此套接字的连接。一定要注意它的返回值是Socket类型
    

     

    Socket类的方法
    
    构造方法public Socket(InetAddress address,int port)throws IOException创建一个流套接字并将其连接到指定 IP 地址的指定端口号。 
    
    方法public InputStream getInputStream()throws IOException返回此套接字的输入流。 
    
    方法public OutputStream getOutputStream()throws IOException返回此套接字的输出流。 
    
    方法public InetAddress getInetAddress()返回套接字连接的地址。返回:此套接字连接到的远程 IP 地址;如果套接字是未连接的,则返回 null
    
    方法public int getPort()返回此套接字连接到的远程端口。返回:此套接字连接到的远程端口号;如果尚未连接套接字,则返回0
    
     
    

    DataInputStream类的方法

    方法public final String readUTF()throws IOException从包含的输入流中读取此操作需要的字节
    
     
    
    DataOutputStream类的方法
    
    方法public final void writeUTF(String str)throws IOException以与机器无关方式使用 UTF-8 修改版编码将一个字符串写入基础输出流。
    
     
    

      

     

     

    例二:

    import java.io.*;

    import java.net.*;

    public class TestSockServer {

      public static void main(String[] args) {

        InputStream in = null;

        OutputStream ut = null;

        try {

          ServerSocket ss = new ServerSocket(5888);

          Socket socket = ss.accept();

          in = socket.getInputStream();

          ut = socket.getOutputStream();

          DataOutputStream dos = new DataOutputStream(out);

          DataInputStream dis = new DataInputStream(in);//将输入输出均包装好

          String s = null;

          if((s=dis.readUTF())!=null) {//只要不为空就一直读取

                 System.out.println(s);

                 System.out.println("from: "+socket.getInetAddress());

                 System.out.println("Port: "+socket.getPort());

               }

          dos.writeUTF("hihello");

          dis.close();

          dos.close();

          socket.close();

        } catch (IOException e) {e.printStackTrace();}

      }

    }

     

     

     

    import java.net.*;

    import java.io.*;

    public class TestSockClient {

      public static void main(String[] args) {

        InputStream is = null; OutputStream s = null;

        try {

          Socket socket = new Socket("localhost",5888);

          is = socket.getInputStream();

          s = socket.getOutputStream();

          DataInputStream dis = new DataInputStream(is);

          DataOutputStream dos = new DataOutputStream(os);

          dos.writeUTF("hey");

          String s = null;

          if((s=dis.readUTF())!=null);

              System.out.println(s);

          dos.close();

          dis.close();

          socket.close();

        } catch (UnknownHostException e) {

           e.printStackTrace();

        } catch (IOException e) {e.printStackTrace();}

      }

    }

    注意:若服务器先读入后写出,则客户端先写出后读入。否则容易堵塞

  • [转贴]马士兵Java学习笔记

    2011-05-08 17:25:09

    1001

    网络基础

     

    TCPtransmission control protocol)是专门设计用于在因特网上提供可靠的,端到端的字节流通信的协议。它是面向连接的协议,是可靠的即可靠但是慢。TCP连接是字节流而非报文流

    UDPuser data protocol)向应用程序提供了一种发送封装的原始IP数据报的方法,并且发生时无需建立连接。是一种不可靠的连接;即不可靠但是快

     

    以前学过谢希仁的《计算机网络》,所以这部分没有怎么做笔记

     

    1002

    Socket

    两个应用程序可以通过一个双向的网络通信连接实现数据交换,这个双向链路的一端称为一个socket

    Socket通常用来实现clientserver连接

    Java.net包中定义的两个类SocketServerSocket(实现TCP连接时才用的),分别用来实现双向连接的clientserver端。

    应用程序建立连接时需要计算机的IP地址和端口号(port number

    TCP,UDP的端口是分开的,各有65536

     

    例子:

    import java.net.*;

    import java.io.*;

    public class TCPServer {

    public static void main(String[] args) throws Exception {

    ServerSocket ss = new ServerSocket(6666);// 首先要建立一个ServerSocke类的对象,并且指定了端口号来监听连接。一旦建立便等着客户端来连接

    while(true) {

    Socket s = ss.accept();//接受客服端的连接,并且服务器端再建立一个socket来和一个特定的客户端连接。这样的话就处理了多个申请连接的情况。

    System.out.println("a client connect!");

    DataInputStream dis = new DataInputStream(s.getInputStream());//注意s.getInputStream()返回的是InputStream类型,即在InputStream类型上又包了一个DataInputStream类型的管道dis

    System.out.println(dis.readUTF());//readUTF()是阻塞式的,所以此时若客户端不发东西,那么它就一直在这里阻塞,也就是说这个while循环就停在这里了即其他的客户无法再连接进来。

    dis.close();

    s.close();

    }

    }

    }

     

     

    import java.net.*;

    import java.io.*;

    public class TCPClient {

    public static void main(String[] args) throws Exception {

    Socket s = new Socket("127.0.0.1", 6666);//指定了要建立连接的IP和端口号,127.0.0.1表本机

    OutputStream s = s.getOutputStream( );

    DataOutputStream dos = new DataOutputStream(os);//类似于服务器端的分析

    dos.writeUTF("hello server!");

    dos.flush();

    dos.close();

    s.close();

    }

    }

    ServerSocket类的方法

    构造方法public ServerSocket(int port)throws IOException创建绑定到特定端口的服务器套接字
    
    方法public Socket accept()throws IOException侦听并接受到此套接字的连接。一定要注意它的返回值是Socket类型
    

     

    Socket类的方法
    
    构造方法public Socket(InetAddress address,int port)throws IOException创建一个流套接字并将其连接到指定 IP 地址的指定端口号。 
    
    方法public InputStream getInputStream()throws IOException返回此套接字的输入流。 
    
    方法public OutputStream getOutputStream()throws IOException返回此套接字的输出流。 
    
     
    

    DataInputStream类的方法

    方法public final String readUTF()throws IOException从包含的输入流中读取此操作需要的字节
    
     
    
    DataOutputStream类的方法
    
    方法public final void writeUTF(String str)throws IOException以与机器无关方式使用 UTF-8 修改版编码将一个字符串写入基础输出流。
    
     
    

    注意:(1)要先运行server再运行client

     2accept()readUTF()都是阻塞式的

     3)交流的信息,相对于服务器端是输入,相对于客户端是输出

  • [转贴]马士兵Java学习笔记

    2011-05-08 17:24:36

    0911

    关于死锁的一道题目(3)

    public class TT implements Runnable {

           int b = 100;

           public synchronized void m1() throws Exception{

                  b = 1000;

                  Thread.sleep(5000);

                  System.out.println("b = " + b);

           }

          

           public synchronized void m2() throws Exception {

                  Thread.sleep(2500);

                  b = 2000;

           }

          

           public void run() {

                  try {

                         m1();

                  } catch(Exception e) {

                         e.printStackTrace();

                  }

           }

          

           public static void main(String[] args) throws Exception {

                  TT tt = new TT();

                  Thread t = new Thread(tt);

                  t.start();

                  tt.m2();

                  System.out.println(tt.b);

           }

    }

    打印结果:1000b=10001000是由System.out.println(tt.b);得到的,b=1000是由System.out.println("b = " + b);得到的。按理说先是t.start();输出b=1000然后是System.out.println(tt.b);所以输出2000。这是为什么呢?

     

    马老师的分析过程:主函数执行到t.start();时产生一个新的线程,调用了start()即执行了m1( )方法。但是并没有给对象加锁。当然主函数继续往下执行,同时这个新的线程也是在执行的。接着执行tt.m2();m2()执行的时候也锁定了当前对象,所以把b置为2000.当,m2()执行完了释放锁,m1()才可能执行。此时把b置为1000.因为m1()中Thread.sleep(5000)在这个时间段执行了主函数的System.out.println(tt.b);输出1000;然后执行m1()中的System.out.println("b = " + b);所以输出b=1000。逼人觉得马老师的这个过程有些不太准确,应该是这样的:

    分析过程:主函数执行到t.start();时产生一个新的线程,调用了start()即执行了m1( )方法。并且给对象加锁,故此时b=1000,然后睡眠。当然主函数继续往下执行,同时这个新的线程也是在执行的(目前是睡眠状态)。接着执行tt.m2();m2()只有等待。然后执行主函数的System.out.println(tt.b);所以输出1000;然后执行m1()中的System.out.println("b = " + b);所以输出b=1000。然后才执行m2()。但是主函数没有输出语句了,所以虽然b的值在内存中再次被改变,但是没有显示

     

    关于生产者消费者问题,没有仔细做笔记只是看了一遍视频。

     

     

    0912

    Waitsleep的区别

     

    Wait属于Object

    Sleep属于Thread

     

    Wait时别的线程也可以访问锁定的对象

    Sleep时别的线程不可以访问锁定的对象

  • [转贴]马士兵Java学习笔记

    2011-05-08 17:24:04

    关于死锁的一道题目(1)

    public class TT implements Runnable {

           int b = 100;

           public synchronized void m1() throws Exception{// m1()加锁

                  b = 1000;

                  Thread.sleep(5000);//睡眠5

                  System.out.println("b = " + b);

            }

          

           public void m2() {// m2()不加锁

                  Thread.sleep(2500);

                  b = 2000;

             }

           }

          

    问题:当一个线程执行m1( )时,另外的一个线程可以执行m2()么?测试程序如下:

    public class TT implements Runnable {

           int b = 100;

           public synchronized void m1() throws Exception{

                  b = 1000;

                  Thread.sleep(5000);

                  System.out.println("b = " + b);

           }

          

           public void m2(){

                  System.out.println(b);

           }

          

           public void run() {

                  try {

                         m1( );

                  } catch(Exception e) {

                         e.printStackTrace();

                  }

           }

          

           public static void main(String[] args) throws Exception {

                  TT tt = new TT();

                  Thread t = new Thread(tt);

                  t.start();

                  Thread.sleep(1000);//确保线程已经开始执行,即已经锁定。

            tt.m2();//调用m2( )

           }

    }

    分析:当执行tt.m2( );时另外的一个线程使b=1000,但是没有解锁。于是输出的是100,b=1000。但是输出的是1000,b=1000。其实是这样的

    public synchronized void m1() throws Exception{

                  b = 1000;

                  Thread.sleep(5000);

                  System.out.println("b = " + b);

           }

    锁定当前对象只是锁住了这一段话即其他的线程不可以再访问这段话。但是其他线程完全可以访问那些没有被锁定的方法

    补充一点孙鑫讲的东西:每一个对象有一个锁,或者叫监视器。除此,它还有一个等待队列(wait set),当一个对象创建的时候,它的等待队列是空的。

     

    0909

    关于死锁的一道题目(2)

    public class TT implements Runnable {

           int b = 100;

     

           public synchronized void m1() throws Exception{

                  b = 1000;

                  Thread.sleep(5000);

                  System.out.println("b = " + b);

           }

          

           public void m2() throws Exception {

                  Thread.sleep(2500);

                  b = 2000;

           }

          

           public void run() {

                  try {

                         m1( );

                  } catch(Exception e) {

                         e.printStackTrace( );

                  }

           }

          

           public static void main(String[] args) throws Exception {

                  TT tt = new TT();

                  Thread t = new Thread(tt);

                  t.start();

                  tt.m2();

     

                         }

    }

    问题:打印出来的结果是1000还是2000?结果是2000.它的原理还是:锁定当前对象只是锁住了这一段话即其他的线程不可以再访问这段话。但是其他线程完全可以访问那些没有被锁定的方法

     

    加入修改为:public synchronized void m1() throws Exception{其余的不变}

    public synchronized void m2() throws Exception{其余的不变}

    这样的话m1先加锁,m2是无法再去锁住的(我们这几个例子里讨论的加锁对象都是b。所以输出的是b=1000.注意这里和修改前的

    public synchronized void m1() throws Exception{其余的不变}

    public void m2() throws Exception{其余的不变}

    还不一样!修改前是可以执行m2()的,但是现在不可以。因为public synchronized void m2() throws Exception这样的写法是想去加锁,独占对象。

  • [转贴]马士兵Java学习笔记

    2011-05-08 17:23:29

    0905

    线程的同步问题

    线程的同步解决的是不同线程访问同一资源的数据处理和协调的问题

    例子:

    public class TestSync implements Runnable {

      Timer timer = new Timer();//见下内存分析

      public static void main(String[] args) {

        TestSync test = new TestSync();//见下内存分析

        Thread t1 = new Thread(test);// t1test的对象的线程

        Thread t2 = new Thread(test); // t2test的对象的线程

        t1.setName("t1"); //给线程起名

        t2.setName("t2");//给线程起名

        t1.start();

        t2.start();//由于t1,t2都要调用start()即执行run方法,所以两个线程访问一个类的同一个对象的同一方法即Timer timeradd方法

      }

      public void run(){//因为实现了Runnable接口所以再实现run()方法

        timer.add(Thread.currentThread().getName());//得到当前线程的名字

      }

    }

     

    class Timer{

      private static int num = 0;

      public void add(String name){

              num ++;

               try {Thread.sleep(1);} //睡眠1毫秒

               catch (InterruptedException e) {}

               System.out.println(name+", 你是第"+num+"个使用timer的线程");

             }

    }

     

    Thread类的方法public final void setName(String name)改变线程名称,使之与参数 name 相同。
    

    内存分析: TestSync test = new TestSync();当执行完词句后:stack中的test指向heap中的test对象。并且在heap中的test对象里存在一个timer引用,它指向heap里的另外一块内存即timer对象。之所以有这样的包含关系因为Timer timer = new Timer()TestSync的成员变量。理解这点很重要

     
    
    按理说输出结果是:t1你是第2使用timer的线程, t2你是第2使用timer的线程.但是结果不是这样的
    
     
    

    分析原因:

    class Timer{

      private static int num = 0;

      public void add(String name){

              num ++;

               try {Thread.sleep(1);}

               catch (InterruptedException e) {}

               System.out.println(name+", 你是第"+num+"个使用timer的线程");

              }

    }

    t1 执行num ++;num变为1,此时t1睡眠(这时候t2有时间执行了),t2接着执行;t2num变为2,此时t2睡眠,t1接着执行。于是打印出:t1你是第2使用timer的线程, t2你是第2使用timer的线程

     

    出现问题的原因是在一个线程的执行过程中被另外一个线程打断了!

    解决办法:在执行这个原子操作的过程中,把当前对象锁住即可;执行完以后再释放锁,即修改为

    class Timer{

      private static int num = 0;

      public synchronized void add(String name){

          synchronized (this) {

               num ++;

               try {Thread.sleep(1);}

               catch (InterruptedException e) {}

               System.out.println(name+", 你是第"+num+"个使用timer的线程");

             }

      }

    }

     

    它的简便写法是:

    class Timer{

      private static int num = 0;

      public synchronized void add(String name){ //在执行这个方法的过程之中,当前对象被锁定

              num ++;

               try {Thread.sleep(1);}

               catch (InterruptedException e) {}

               System.out.println(name+", 你是第"+num+"个使用timer的线程");

              }

    }

     

    0907

    关于死锁

    当两个线程均需要锁住两个对象A,B才可以完成操作。但是线程1先锁住了A,线程2先锁住了B。于是两者均无法继续执行,出现了死锁。例子:

    public class TestDeadLock implements Runnable {

           public int flag = 1;

           static Object o1 = new Object(), o2 = new Object();

           public void run() {

    System.out.println("flag=" + flag);

                  if(flag = = 1) {

                         synchronized(o1) {

                                try {

                                       Thread.sleep(500);

                                } catch (Exception e) {

                                       e.printStackTrace();

                                }

                                synchronized(o2) {

                                       System.out.println("1");

                                }

                         }

                  }

                  if(flag = = 0) {

                         synchronized(o2) {

                                try {

                                       Thread.sleep(500);

                                } catch (Exception e) {

                                       e.printStackTrace();

                                }

                                synchronized(o1) {

                                       System.out.println("0");

                                }

                         }

                  }

           }    

                  public static void main(String[] args) {

                  TestDeadLock td1 = new TestDeadLock();

                  TestDeadLock td2 = new TestDeadLock();

                  td1.flag = 1;

                  td2.flag = 0;

                  Thread t1 = new Thread(td1);

                  Thread t2 = new Thread(td2);

                  t1.start();

                  t2.start();

                         }

    }

    这个程序比较简单,主要体现了死锁的原理。

     

    Thread类的构造方法public Thread(Runnable target)分配新的 Thread 对象。参数targettarget run 方法被调用

  • [转贴]马士兵Java学习笔记

    2011-05-08 17:22:56

    0902

    线程状态的转换

    线程的状态有创建,就绪,运行,阻塞,终止。运行时可能阻塞,然后再回到就绪。

    当线程类的对象调用start()时不是进入运行,而是就绪状态

     

    Thread类的方法:

    方法public final boolean isAlive( )测试线程是否处于活动状态。如果线程已经启动且尚未终止,则为活动状态。返回:如果该线程处于活动状态,则返回 true;否则返回 false。在此注意就绪,运行,阻塞算活着。

    方法public final int getPriority( )返回线程的优先级。 
    
    方法public final void setPriority (int newPriority)更改线程的优先级。 
    
    方法public static void sleep(long millis)throws InterruptedException在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)。注意它是一个静态的方法
    
    方法public final void join( )throws InterruptedException等待该线程终止。 
    
    方法public static void yield( )暂停当前正在执行的线程对象,并执行其他线程。即让出CPU,当前线程进入就绪队列等待调度
    
    方法public void interrupt( )中断线程。 
    
     
    

    例一:

    import java.util.*;

    public class TestInterrupt {

      public static void main(String[] args) {

        MyThread thread = new MyThread();

        thread.start();

        try {Thread.sleep(10000);}//注意在哪里调用sleep方法则哪个方法睡眠。所以这里主方法睡眠,时间为10秒。注意它是一个静态的方法,所以写Thread.sleep(10000)

        catch (InterruptedException e) {}

        thread.interrupt();//子线程被中断。子线程发现异常,所以return即中断。

      }

    }

     

    class MyThread extends Thread {

           public void run(){

        while(true){//相当于一个死循环,每隔1秒打印一下当前时间。

          System.out.println("==="+new Date()+"===");

          try {

            sleep(1000);//睡眠1

          } catch (InterruptedException e) {

            return;//当有异常发生时,直接return,即该线程结束了。

          }

        }

      }

    前面的死循环,处理得不好,不太容易让它停下来,所以这样:

    class MyThread extends Thread {

    boolean flag=true   

    public void run(){

    while(flag= =true) {其余不变}

     

    然后写一个方法:

    public void shutdown( ){

      flag=false

    }

    在主函数使用线程的对象调用shutdown( )则线程停止

     

    0903

    join方法

    查阅API可知方法public final void join( )throws InterruptedException等待该线程终止。即合并线程

     

    例子:

    public class TestJoin {

      public static void main(String[] args) {

        MyThread2 t1 = new MyThread2("yy");//给该线程起名为yy

        t1.start();

        try {

               t1.join();//主线程想合并子线程t1。所以t1.join()即等t1执行完以后,主线程再执行。这样的话没有分支,即叫线程的合并

        } catch (InterruptedException e) {}

              

        for(int i=1;i<=10;i++){

          System.out.println("i am main thread");

        }

      }

    }

    class MyThread2 extends Thread {

      MyThread2(String s){//调用了父类的构造方法给子类MyThread2的对象起名字。没什么用

          super(s);

      }

     

      public void run(){

        for(int i =1;i<=10;i++){

          System.out.println("i am "+getName());

          try {

              sleep(1000);

          } catch (InterruptedException e) {

              return;

          }

        }

      }

    }

     

     

    Thread类的构造方法public Thread(String name)分配新的 Thread 对象。参数:name - 新线程的名称。
    

     

     

    yield( )方法

    方法public static void yield( )暂停当前正在执行的线程对象,并执行其他线程。即让出CPU,当前线程进入就绪队列等待调度
    
    例子:
    
    public class TestYield {
    
      public static void main(String[] args) {
    
        MyThread3 t1 = new MyThread3("t1");
    
        MyThread3 t2 = new MyThread3("t2");
    
        t1.start(); t2.start();
    
      }
    
    }
    
    class MyThread3 extends Thread {
    
      MyThread3(String s){
    
    super(s);
    
    }
    
      public void run(){
    
        for(int i =1;i<=100;i++){
    
          System.out.println(getName()+": "+i);
    
          if(i%10==0){//当这个数可以被10整除的时候,让出CPU。但只是让一小会
    
            yield();
    
          }
    
        }
    
      }
    
    }
    

    程序有三条路径在执行,有maint1t2

     

    线程的优先级

    优先级最低为1,最高为10,默认为5

    例子:

    public class TestPriority {

           public static void main(String[] args) {

                  Thread t1 = new Thread(new T1());

                  Thread t2 = new Thread(new T2());这一句相当于T2 r = new T2();Thread t2 = new Thread(r);在实现Runnable接口后要启动一个线程,则要new一个Thread类的对象!!!

                  t1.setPriority(Thread.NORM_PRIORITY + 3);//t1的优先级提高3

                  t1.start();

                  t2.start();

           }

    }

     

    class T1 implements Runnable {

           public void run() {

                  for(int i=0; i<1000; i++) {

                         System.out.println("T1: " + i);

                  }

           }

    }

     

    class T2 implements Runnable {

           public void run() {

                  for(int i=0; i<1000; i++) {

                         System.out.println("------T2: " + i);

                  }

           }

    }

     

    Thread类的构造方法public Thread(Runnable target)分配新的 Thread 对象。参数targettarget run 方法被调用

  • [转贴]马士兵Java学习笔记

    2011-05-08 17:22:21

    0901

    线程的基本概念

    线程是一个程序里面不同的执行路径

    例子:

    public class {

           public static void main(String[] args) {

                  m1( );

           }

          

           public static void m1( ) {

                  m2();

                  m3();

           }

          

           public static void m2( ) {}

           public static void m3( ) {}

    }

     

    这个程序只有一条执行路径,执行到main时调用m1,主函数停下来。m1调用m2,m1停下来,待m2返回时m1继续执行。m1调用m3, m1停下来,待m3返回时m1继续执行.随即主函数结束。

    我们前面讲的东西都是这种情况:只有一个分支,就是main方法也叫主线程

    当一个class(放在代码区)或者.exe放进内存还没有执行时,它是静态的。但是线程是一个程序里面不同的执行路径,它是动态的。

    平时说的进程的执行指的是进程里面主线程(主函数)开始执行了

    操作系统是支持多线程,多进程的。但是实际是在一个特定的时间点CPU只运行一个线程。

    Java的线程是通过java.lang.Thread类来实现的。

    虚拟机启动时会有一个由主方法(即main函数)所定义的线程

    通过调用Thread类的start()方法来启动一个线程

     

    线程创建的两个方法:

    一:定义线程类实现Runnable接口

    二:可以定义Thread的子类并且重写run( )方法,然后生成该类的对象。即使用了继承

     

    通过下面的例子来看看方法调用和多线程的区别

     

    例一:

    public class TestThread1 {

           public static void main(String args[]) {

                  Runner1 r = new Runner1( );

                  r.run( );

                  for(int i=0; i<100; i++) {

                     System.out.println("Main Thread:------" + i);

                  }

           }

    }

    class Runner1 implements Runnable {

           public void run() {

                  for(int i=0; i<100; i++) {      

                         System.out.println("Runner1 :" + i);

                  }

           }

     

    注意例一只是方法的调用,根本不涉及多线程

     

    例二:

    public class TestThread1 {

           public static void main(String args[]) {

                  Runner1 r = new Runner1();

                  Thread t = new Thread(r);//要启动一个线程,则要new一个Thread类的对象!!!

                  t.start();//主函数执行到t.start()时,新的一个线程启动即另外的一个分支产生了,但是main方法继续往下执行。此时两者交替执行。参见下面的start( )API

                  for(int i=0; i<100; i++) {

                         System.out.println("Main Thread:------" + i);

                  }

           }

    }

     

    class Runner1 implements Runnable {//实现了Runnable接口表明Runner1是一个线程类

           public void run( ) {//重写了run方法

                  for(int i=0; i<100; i++) {      

                         System.out.println("Runner1 :" + i);

                  }

           }

     

    Thread类的构造方法public Thread(Runnable target)分配新的 Thread 对象。参数targettarget run 方法被调用

    Thread类的方法public void run( )如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。Thread 的子类应该重写该方法。

    Thread类的方法public void start( )使该线程开始执行并且Java 虚拟机调用该线程的 run 方法。结果是两个线程并发地运行;当前线程(从调用返回给 start 方法)和另一个线程(执行其 run 方法)。多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。

    接口 Runnable只有一个方法void run( )使用实现接口 Runnable 的对象创建一个线程时,启动该线程将导致在独立执行的线程中调用对象的 run 方法。
    

     

    此例子就是线程创建的方法之定义线程类实现Runnable接口。一定要注意实现接口 Runnable时当要启动一个线程时,则要new一个Thread类的对象!!!由此对象来调用start()方法

    例三:

    public class TestThread1 {

           public static void main(String args[]) {

                  Runner1 r = new Runner1();

                  r.start();

                  //r.run();

                  //Thread t = new Thread(r);

                  //t.start();

                 

                  for(int i=0; i<100; i++) {

                         System.out.println("Main Thread:------" + i);

                  }

           }

    }

     

    class Runner1 extends Thread {//既然是继承了,那么Runner1就是一个线程。所以主函数中的Thread t = new Thread(r);t.start();是多余的。直接r.start();即可

           public void run() {

                  for(int i=0; i<100; i++) {      

                         System.out.println("Runner1 :" + i);

                  }

           }

     此例子就是线程创建的方法之使用继承。
    

    综合上面两例,在线程创建时尽量使用实现接口会好些,因为Java只有单继承。一旦继承了Thread其余的就不可以再继承了。

561/3123>
Open Toolbar