发布新日志

  • 经济危机下的事业危机

    2009-03-27 12:09:29

    毕业的时候对于做开发和做测试并没有想好,比较幸运地进入了软件业的顶级企业拿上了高薪,但也是身不由己地开始了自己测试生涯,一年半的工作,在外企学到了很多,但在在外企的弊端也很多......外企并不是很多人想的那种福地,有时候也是会迷失了自己!

    当裁员出乎意料地降临到自己身上后自己才意识到轻松的环境让自己忽略了很多,自主的管理让自己的上进消退了很多;但对于工作觉得自己一向是一丝不苟,完全胜任的,担负的责任也让自己没有觉得会有裁员的危险;然而一切都不是自己想像的那样,外企不是一个讲人情的地方,做的好不一定就能保住饭碗;当一边开始为新的工作而努力一边才开始考虑自己长远的职业发的时候,才发现之前自己为个人考虑的的确太少....

    经济危机带来的影响对我来说是远远超过预期的,初期找工作时候觉得很简单--不就是相当于跳一次槽嘛,联系猎头,发简历,安排面试; 当自己信心满满地要求薪水要有20%的涨幅的时候,觉得自己可选择的机会还挺多的;当经历了几次失败,经历了几次不了了之的应聘过程,当电话那头说“对不起,总部通知当前所有的Head count将要freeze”, 才觉得自己一个多月的努力和期待都成了泡影....

    慢慢地调整心态,降低了期望,也做好了与经济危机长期做斗争的准备....

    学习充电? 考认证? 抑或是随便找个愿意要自己的企业只求有份工作?

    只要努力,一切都会过去,未来掌握在自己手中,只要足够强壮,就不怕任何危机....当下一次危机来临的时候我会说,"我已经准备好了!"

  • 孙鑫JAVA教学视频学习笔记(9-10)

    2009-03-27 11:39:09

    九. Applet编程
    内容:
    Applet编程,详细讲解Applet的生命周期,子体的设置,applet从网页中获取信息,applet和浏览器的通信,更新显示的原理,用Applet来制作动画程序,以及动画程序的优化。

    1.Applet是什么?
    Applet又称为Java小应用程序,是能够嵌入到一个HTML页面中,并且可通过Web浏览器下载和执行的一种Java类 。
    Applet不需要main()方法,由Web浏览器中内嵌的Java虚拟机调用执行。-- 测试时可以用appletviewer调用!

    2.Applet的安全限制
    因为applet是从远端服务器上下载并且在本地执行,所以安全性就显得格外重要。
    通过限制applet在沙箱(applet的运行环境)中运行,保证了对本地系统而言applet是安全的。
    applet在沙箱中运行时:
    ⑴不能运行任何本地可执行程序;
    ⑵除了存放下载的applet的服务器外,applet不能和其它主机进行通信。
    ⑶不能对本地文件系统进行读写。

    编写Applet就是创建Applet类;

    java.lang.Object
      java.awt.Component
          java.awt.Container
              java.awt.Panel
                  java.applet.Applet
    它是由awt的Panel继承而来的,所以也可以添加button等!

    init(),destroy(), start(), stop()

    3. Applet的生命周期 (init(),destroy(), start(), stop()组成)
    init():当浏览器加载applet,进行初始化的时候调用该方法。--初始化工作都写这里面,applet无需构造方法
    start():在init()方法之后调用。当用户从其它页面转到包含applet的页面时,该方法也被调用。
    stop():在用户离开包含applet的页面时被调用。-- 不可见时即被调用!
    destroy():当applet不再被使用,或浏览器退出的时候,该方法被调用。

    appletviewer.exe: --- sun提供用来测试java小应用程序的!
    e:\java>appletviewer TestApplet.html
    或:e:\java>appletviewer TestApplet.java //将applet调用标记写入java源文件即可

    import java.applet.*;
    public class TestApplet extends Applet //Applet必须是public,否则浏览器无法加载
    {
        public void init()
        {
            System.out.println("init");
        }
            public void start()
        {
            System.out.println("start");
        }
            public void stop()
        {
            System.out.println("stop");
        }
            public void destroy()
        {
            System.out.println("destroy");
        }
    }
    //appletviewer.exe 可以识别以下代码:测试时候用,非常方便! -- 无需调用html
    //<applet code="TestApplet.class" width=600 height=400>
    //</applet>

    4.Paint()方法  -- Component中继承的
    Applet本质上是图形方式的,我们应该在图形环境中绘制我们的显示内容。
    我们可以通过创建一个paint()方法在Applet的panel上绘图。只要Applet的显示需要刷新,paint()方法就会被浏览器环境调用。例如,当Applet的显示尺寸发生变化的时候,或浏览器窗口被最小化或被要求以图标方式显示时,这种调用就会发生。
    我们应该编写自己的paint()方法,以使它在任何时候被调用,都能正常地工作。对它的调用是异步产生的,且由Applet的运行环境而不是程序来驱动 。
    paint()方法带有一个参数,它是java.awt.Graphics类的一个实例。这个参数总是建立该Applet的panel的图形上下文,我们可以用这个图形上下文在Applet中绘图或写入文本。

    public void paint(Graphics g)
    参数 Graphics
    public abstract class Graphicsextends ObjectThe Graphics class is the abstract base class for all graphics contexts that allow an application to draw onto components that are realized on various devices, as well as onto off-screen images.

     
    在上面的applet类中增加方法paint即可: -- 显示字符,指定字体字号颜色!
        public void paint(Graphics g)  -- 是Applet从Container类中继承来的方法!g是形参,该方法由applet调用!
        {
            Font f = new Font("楷体_2312",Font.BOLD,30);
            g.setFont(f);
            g.setColor(Color.blue);
            g.drawString("维新科学技术培训中心",0,30);
        }

    5.applet从网页中获取信息
    正如应用程序可以通过命令行参数来获取信息一样,通过使用param标记,applet可以从网页中获取信息。
    网页中增加font参数
    <applet code="TestApplet.class" width=600 height=400>
    <param name="font" value="DialogInput">
    </applet>
    用Applet的getParameter()方法 获取网页中的参数!
    在init方法中增加:
        String strFont;
        public void init()
        {       
            strFont=getParameter("font");
            System.out.println("init");
        }
    然后在paint方法中设置字体时直接用从网页中取得的字体:
            Font f = new Font(strFont,Font.BOLD,30);
    --- 提供了便利性,修改网页即可!

    6.Applet和用户互动 --- 在Applet上画线
    鼠标左键按下--获取了一个点;
    松开鼠标--获取另一个点;
    API -- java.awt.event.MouseAdapter
    方法:
    void mousePressed(MouseEvent e)
              Invoked when a mouse button has been pressed on a component.
     void mouseReleased(MouseEvent e)
              Invoked when a mouse button has been released on a component.
    MouseEvent类有方法getX,getY;
    Component类有方法getGraphics;
    Graphics有drawLine方法;

    Code:
    import java.applet.*;
    import java.awt.*;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    public class TestApplet extends Applet //Applet必须是public,否则浏览器无法加载
    {
        String strFont;
        int xStart, yStart;
        public void init()
        {       
            strFont=getParameter("font");
            addMouseListener(new MouseAdapter() //它是个抽象类
            {
                public void mousePressed(MouseEvent e)
                {
                    xStart=e.getX();
                    yStart=e.getY();
                }
                      public void mouseReleased(MouseEvent e)
                  {
                      Graphics g=getGraphics();  //getGraphics Applet的方法!获得图形上下文
                      g.setColor(Color.red);
                      g.drawLine(xStart,yStart,e.getX(),e.getY());
                  }
            }); //匿名内部类实现所注册的监听器!
            System.out.println("init");
        }
        public void start()
        {
            System.out.println("start");
        }
        public void stop()
        {
            System.out.println("stop");
        }
        public void destroy()
        {
            System.out.println("destroy");
        }
        public void paint(Graphics g)  //调用applet时候会产生一个Graph类型的对象
        {
            //Font f = new Font("楷体_2312",Font.BOLD,30);
            Font f = new Font(strFont,Font.BOLD,30);
            g.setFont(f);
            g.setColor(Color.blue);
            g.drawString("维新科学技术培训中心",0,30);
        }
    }
    //appletviewer.exe 可以识别以下代码:测试时候用,非常方便!
    //<applet code="TestApplet.class" width=600 height=400>
    //</applet>
    TestApplet.HTML:
    <applet code="TestApplet.class" width=600 height=400>
    <param name="font" value="DialogInput">
    </applet>

    7.Applet和浏览器的通信
    在浏览器中显示信息
       调用AppletContext接口中的showStatus()方法 -- 在状态栏显示信息,但可能会被覆盖
    showDocument():打开新的网页显示信息!
    请求浏览器显示指定的网页
       调用AppletContext接口中的showDocument()方法。
    Applet有方法可以得到AppletContext:
    AppletContext getAppletContext()
              Determines this applet's context, which allows the applet to query and affect the environment in which it runs.
    加载过的Applet会被浏览器保存到缓存中重复使用!可以在java控制台中清除!
    例子--- 点击applet按钮打开新链接:
        public void init()
        {       
            Button btn=new Button("链接");
            add(btn);
            btn.addActionListener(new ActionListener()
            {
                public void actionPerformed(ActionEvent e)
                {
                    try
                    {
                        getAppletContext().showDocument(       //getAppletCon是Applet的方法;
                            new URL("http://www.163.com"),"_blank");
                    }
                    catch(Exception ex)
                    {
                        ex.printStackTrace();
                    }               
                }
            });

    8.Applet中显示动画
    Graphics中的drawImage方法来绘制一个图像;
    abstract  boolean drawImage(Image img, int x, int y, ImageObserver observer)     ----      Draws as much of the specified image as is currently available.
    -- Image是个抽象类(如何获得对象? -- Applet中提供了一个getImage方法获取image对象);ImageObserver 接口,图像是否可以改变,Applet对象本身也实现了这个接口;
    Code for draw a image:
    package imagetest;
    import java.applet.Applet;
    import java.awt.*;
    public class ShowImage extends Applet
    {
      private Image img;
      public void init()
      {
        img=getImage(getDocumentBase(),"T1.gif");  //参数1:URL
      }
      public void paint(Graphics g)
      {
        g.drawImage(img,0,0,this);  //传入当前Applet对象作为ImageObserver 接口的实现
      }
    }
    Execute the html:
    <applet code="imagetest.ShowImage.class" width=600 height=400>
    </applet>

    b.动画效果-- 多幅图片

    repaint,update方法;

    双缓冲技术,提前缓冲下一幅图像;
    public Image createImage(int width,
                             int height)Creates an off-screen drawable image to be used for double buffering.

    Image.getGraphics()

    package imagetest;

    import java.applet.Applet;
    import java.awt.HeadlessException;
    import java.awt.*;
    public class ShowImage extends Applet
    {
      private Image[] imgs;
      private int totalImages=10;
      private int currentImage;
      public void init()
      {
        imgs=new Image[totalImages];  //创建10个引用
        for(int i=0;i<totalImages;i++)
        {
          imgs[i]=getImage(getDocumentBase(),"T"+(i+1)+".gif");
        }
        //img=getImage(getDocumentBase(),"T1.gif");  //参数1:URL
      }
      public void start()
      {
        currentImage=0;
      }
      public void paint(Graphics g)
      {
        //g.drawImage(img,0,0,this);  //传入当前Applet对象
        g.drawImage(imgs[currentImage],0,0,this);
        currentImage=++currentImage%10;
        try {
          Thread.sleep(500);
        }
        catch (Exception ex) {
          ex.printStackTrace();
        }
        repaint();
      }
    }

    MediaTracker类用来追踪image的载入情况,方法waitForID();

    AWT绘图
    更新显示由一种被称为AWT线程的独立的线程来完成。这个线程可用来处理与显示更新相关的两种情况。
                第一种情况是显露(exposure),它在首次显示时,或在部分显示已被破坏而必须刷新时出现。显示的破坏可能发生在任何时刻,因此,我们的程序必须能在任意时刻更新显示。
               第二种情况是在程序重画带有新内容的画面时。这种重画可能会要求首先擦除原来的图像。
    paint(Graphics g)方法
         当组件第一次显示,或受损的部分需要修复时被调用。除非必要,更新不会完全覆盖整个图形区域,而是严格限制在被破坏的范围内。
    repaint()方法
         对repaint()的调用可通知系统:你想改变显示,于是系统将调用paint()。
    update(Graphics g)方法
         repaint()实际上产生了一个调用另一方法update()的AWT线程。update方法通常清除当前的显示并调用paint()。update()方法可以被修改,例如:为了减少闪烁可不清除显示而直接调用paint()。

    repaint()、 update()和paint() 的关系
     

    9.Applet的发布:
    将类文件目录连同图片资源文件打成Jar包发布,将html文件和jar文件放到同一目录即可;
    html中必须加入标识:
    <applet code="imagetest.ShowImage.class" width=600 height=400 archive="imagetest.jar">
    </applet>

    10.Applet的HTML标记和属性
    用于定位的applet属性
    ⑴width和height:必要的属性,以象素为单位,设定applet的宽度和高度。
    ⑵align:可选的属性,指定applet对齐的方式。
    left:       把applet放在网页左边,后面的文本会移至applet的右边。
    right:      把applet放在网页右边,后面的文本会移至applet的左边。
    bottom:     把applet的底部与当前行文本底部对齐。
    top:        把applet的顶部与当前行顶部对齐。
    texttop:    把applet的顶部与当前行文本顶部对齐。
    middle:     把applet的中部与当前行基线对齐。
    absmiddle:  把applet的中部与当前行中部对齐。
    baseline:   把applet的底部与当前行基线对齐。
    absbottom:  把applet的底部与当前行底部对齐。
    ⑶vspace和hspace:可选的属性,指定在applet上/下的象素数目(vspace)和applet两边的象素数目(hspace)。

    用于编码的applet属性
    ⑴code:指定applet类文件的名字。该名字要么是相对于codebase,那么是相对于当前页面。
    ⑵codebase:可选的属性,告诉浏览器到哪个目录下去寻找类文件。
    ⑶archive:可选的属性,列出Java存档文件、包含类文件的文件或者applet需要的其它资源。
    ⑷object:用来指定applet类文件的另外一个方法。
    ⑸name:可选的属性,页面脚本编写人员希望赋予applet名字属性,这样,在编写脚本的时候,就可以用为该属性指定的名字来代表这个applet。

    播放音乐applet:
    package imagetest;

    import java.applet.*;
    import java.awt.HeadlessException;

    public class AudioTest extends Applet
    {
      private AudioClip ac; //该类有循环播放功能,java.awt.
      public void init()
      {
        ac=getAudioClip(getDocumentBase(),"1.au");
      }
      public void start()
      {
    //    play(getDocumentBase(),"1.au");  //非循环播放!
        ac.loop();
      }
      public void stop()
      {
        ac.stop();
      }
    }






    十. JAVA网络编程

    1.网络,协议,IP地址,端口号
    -- IP地址相当于公司总机,端口号相当于分机号!


    网络体系结构

    网络体系结构就是使这些用不同媒介连接起来的不同设备和网络系统在不同的应用环境下实现互操作性,并满足各种业务需求的一种粘合剂,它营造了一种“生存空间” -- 任何厂商的任何产品、以及任何技术只要遵守这个空间的行为规则,就能够在其中生存并发展。
    网络体系结构解决异质性问题采用的是分层方法 -- 把复杂的网络互联问题划分为若干个较小的、单一的问题,在不同层上予以解决。
        就像我们在编程时把问题分解为很多小的模块来解决一样。

    2.OSI七层协议
    对应层不能直接通信!
     
    通信实体的对等层之间不允许直接通信。 -- 虚拟通信
    各层之间是严格单向依赖。
     上层使用下层提供的服务 - Service user ;
     下层向上层提供服务 - Service provider 。

    3.OSI各层所使用的协议
    应用层:远程登录协议Telnet、文件传输协议FTP、 超文本传输协议HTTP、域名服务DNS、简单邮件传输协议SMTP、邮局协议POP3等。
    传输层:传输控制协议TCP、用户数据报协议UDP。
        TCP:面向连接的可靠的传输协议。-- 数据确认,数据重传机制;
        UDP:是无连接的,不可靠的传输协议。-- 直接向IP地址传送数据;无须确认,实时性比较高!
    网络层:网际协议IP、Internet互联网控制报文协议ICMP、Internet组管理协议IGMP。

    4.数据封装
    一台计算机要发送数据到另一台计算机,数据首先必须打包,打包的过程称为封装。
    封装就是在数据前面加上特定的协议头部。

    OSI参考模型中,对等层协议之间交换的信息单元统称为协议数据单元(PDU,Protocol Data Unit)。
    OSI参考模型中每一层都要依靠下一层提供的服务。
    为了提供服务,下层把上层的PDU作为本层的数据封装,然后加入本层的头部(和尾部)。头部中含有完成数据传输所需的控制信息。
    这样,数据自上而下递交的过程实际上就是不断封装的过程。到达目的地后自下而上递交的过程就是不断拆封的过程。由此可知,在物理线路上传输的数据,其外面实际上被包封了多层“信封”。
    但是,某一层只能识别由对等层封装的“信封”,而对于被封装在“信封”内部的数据仅仅是拆封后将其提交给上层,本层不作任何处理。

    5. TCP/IP模型
    TCP/IP起源于美国国防部高级研究规划署(DARPA)的一项研究计划--实现若干台主机的相互通信。
    现在TCP/IP已成为Internet上通信的工业标准。
    TCP/IP模型包括4个层次:(对应OSI层)
    应用层    -- 5,6,7
    传输层    -- 4
    网络层    -- 3
    网络接口  -- 1,2层

    6.端口
    端口是一种抽象的软件结构(包括一些数据结构和I/O缓冲区)。应用程序通过系统调用与某端口建立连接(binding)后,传输层传给该端口的数据都被相应的进程所接收,相应进程发给传输层的数据都通过该端口输出。
    端口用一个整数型标识符来表示,即端口号。端口号跟协议相关,TCP/IP传输层的两个协议TCP和UDP是完全独立的两个软件模块,因此各自的端口号也相互独立,端口通常称为协议端口(protocol port) ,简称端口。
    端口使用一个16位的数字来表示,它的范围是0~65535,1024以下的端口号保留给预定义的服务。例如:http使用80端口。

    7.套接字(socket)的引入
    为了能够方便的开发网络应用软件,由美国伯克利大学在Unix上推出了一种应用程序访问通信协议的操作系统调用socket(套接字)。socket的出现,使程序员可以很方便地访问TCP/IP,从而开发各种网络应用的程序。
    随着Unix的应用推广,套接字在编写网络软件中得到了极大的普及。后来,套接字又被引进了Windows等操作系统中。Java语言也引入了套接字编程模型。

    8.基于TCP的socket编程 -- 双方必须就需,等待通信

     
    服务器程序编写:
    ①调用ServerSocket(int port)创建一个服务器端套接字,并绑定到指定端口上;②调用accept(),监听连接请求,如果客户端请求连接,则接受连接,返回通信套接字。③调用Socket类的getOutputStream()和getInputStream获取输出流和输入流,开始网络数据的发送和接收。④最后关闭通信套接字。
    客户端程序编写:
    ①调用Socket()创建一个流套接字,并连接到服务器端; ②调用Socket类的getOutputStream()和getInputStream获取输出流和输入流,开始网络数据的发送和接收。 ③最后关闭通信套接字。
    套接字是作为通信链路的端点,看做电话机!

    网络编程:java.net
    服务器端和客户端:
    ServerSocket
    Socket
    静态类InetAddress有方法:
    getByName()
    getLocalHost()
    -- 获取IP地址

    package lesson10;

    import java.net.*;
    import java.io.*;
    public class Lesson10 {

        public static void main(String[] args) {

            if (args.length>0)
            {
                server();
            }
            else
            {
                client();
            }
        }
        public static void server()
        {
            try{
                ServerSocket ss = new ServerSocket(6000);
                Socket s = ss.accept();
                OutputStream s = s.getOutputStream();
                InputStream is = s.getInputStream();
                os.write("Hello, welcome you!".getBytes());
                byte[] buf = new byte[100];
                int len = is.read(buf);
                System.out.println(new String(buf,0,len));
                os.close();
                is.close();
                ss.close();
                s.close();
            }
            catch (Exception ex){
                ex.printStackTrace();
            }
        }

        public static void client()
        {
            try{
                Socket s = new Socket(InetAddress.getByName(null),6000);  //or "127.0.0.1" or null 都返回本地IP地址;
                OutputStream s = s.getOutputStream();
                InputStream is = s.getInputStream();
                byte[] buf = new byte[100];
                int len = is.read(buf);
                System.out.println(new String(buf,0,len));
                os.write("Hello, this is zhangsan!".getBytes());
                os.close();
                is.close();
                s.close();

            }
            catch (Exception ex){
                ex.printStackTrace();
            }       
        }
    }
    //虽然写到一个程序中了,但是C/S通信的是进程,只要两边同时运行就可以了!

    b. 用多线程方法实现;Server一个,client多线程;

    9.基于UDP的socket编程
    -- 无须建立连接,有一方必须先启动,否则发送的数据会丢失
    -- 接收者:server 先启动; 发送者:client

    由DatagramSocket实现UDP -- 数据报套接字
    receive(DatagramPacket p) 需构造数据包
    send(DatagramPacket p)
    构造接收端数据包:
    DatagramPacket(byte[] buf, int length)
              Constructs a DatagramPacket for receiving packets of length length.
    构造发送端数据包:
    DatagramPacket(byte[] buf, int length, InetAddress address, int port)
              Constructs a datagram packet for sending packets of length length to the specified port number on the specified host.
     
    接收端程序编写:
    ①调用DatagramSocket(int port)创建一个数据报套接字,并绑定到指定端口上;②调用DatagramPacket(byte[] buf, int length),建立一个字节数组以接收UDP包 。③调用DatagramSocket类的receive(),接收UDP包。④最后关闭数据报套接字。
    发送端程序编写:
    ①调用DatagramSocket()创建一个数据报套接字; ②调用DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port),建立要发送的UDP包。 ③调用DatagramSocket类的send(),发送UDP包。④最后关闭数据报套接字。

    简单的receive和send方法编写:
      public static void recv()
      {
        try {
          DatagramSocket ds=new DatagramSocket(6000);
          byte[] buf=new byte[100];
          DatagramPacket dp=new DatagramPacket(buf,100);
          ds.receive(dp);
          System.out.println(new String(buf,0,dp.getLength()));
          ds.close();
        }
        catch (Exception ex) {
          ex.printStackTrace();
        }
      }
        public static void send()
      {
        try {
          DatagramSocket ds=new DatagramSocket();
          String str="Hello,this is zhangsan";
          DatagramPacket dp=new DatagramPacket(str.getBytes(),str.length(),
                                               InetAddress.getByName("localhost"),
                                               6000);
          ds.send(dp);
          ds.close();
        }
        catch (Exception ex) {
          ex.printStackTrace();
        }
      }

    接收端给发送端回复一个信息: -- 也需要知道发送端的地址和端口
      public static void recv()
      {
        try {
          DatagramSocket ds=new DatagramSocket(6000);
          byte[] buf=new byte[100];
          DatagramPacket dp=new DatagramPacket(buf,100);
          ds.receive(dp);
          System.out.println(new String(buf,0,dp.getLength()));
          String str="Welcome you!";  //回复信息
    //receive dp后通过getAddress(),getPort()就能得到发送端的地址和端口 -- 端口是OS任意分配的一个!
          DatagramPacket dpSend=new DatagramPacket(str.getBytes(),str.length(),
                                               dp.getAddress(),dp.getPort());
          ds.send(dpSend); //发送数据包
          ds.close();
        }
        catch (Exception ex) {
          ex.printStackTrace();
        }
      }
      public static void send()
      {
        try {
          DatagramSocket ds=new DatagramSocket();
          String str="Hello,this is zhangsan";
          DatagramPacket dp=new DatagramPacket(str.getBytes(),str.length(),
                                               InetAddress.getByName("localhost"),
                                               6000);
          ds.send(dp);
          byte[] buf=new byte[100]; //用来接收数据包: 以下三行
          DatagramPacket dpRecv=new DatagramPacket(buf,100);
          ds.receive(dpRecv);
          System.out.println(new String(buf,0,dpRecv.getLength()));
          ds.close();
        } //第一次发送成功后相当于建立了一个虚连接,可以发送和接收数据;
        catch (Exception ex) {
          ex.printStackTrace();
        }
      }

    URL与URI
    URL(Uniform. Resource Locator ),通用资源定位符。<http://www.mybole.com.cn/index.asp>就是一个URL。
    URI(Uniform. Resource Identifier),通用资源标识符。
    URI纯粹是个符号结构,用于指定构成Web资源的字符串的各个不同部分。URL是一种特殊类型的URI,它包含了用于查找某个资源的足够信息。其它的URI,例如:mailto:myoble@mybole.com.cn则不属于定位符,因为它里面不存在根据该标识符来查找的任何数据。这种URI称为URN(通用资源名)。
    在Java库中,URI类不包含用于访问通用资源标识符设定的任何方法,它的唯一作用是进行分析。相反,URL类则可以打开到达资源的一个字符串。

    Class URLConnection
    The abstract class URLConnection is the superclass of all classes that represent a communications link between the application and a URL.

    Download程序:

    package lesson10;

    import java.net.*;
    import javax.swing.*;
    import java.awt.event.*;
    import java.io.*;
    public class Download
    {
      public static void main(String[] args)
      {
        JFrame. jf=new JFrame("维新下载程序");
        jf.setSize(600,400);
        jf.setLocation(100,100);
        JPanel p=new JPanel();
        JLabel l=new JLabel("Please input URL:");
        final JTextField tf=new JTextField(30);
        p.add(l);
        p.add(tf);
        jf.getContentPane().add(p,"North");
        final JTextArea ta=new JTextArea();
        jf.getContentPane().add(ta,"Center");
        JButton btn=new JButton("Download");
        jf.getContentPane().add(btn,"South");//以上制作界面
        btn.addActionListener(new ActionListener() {
              public void actionPerformed(ActionEvent e) {
                String str=tf.getText(); //取得文本框中输入的内容来创建URL对象;
                try {
                  URL url=new URL(str);
                  URLConnection urlConn=url.openConnection(); //该URL方法返回一个URLConnection的对象!
                  String line=System.getProperty("line.separator");  //系统无关的换行符!!!
                  ta.append("Host: "+url.getHost());          //将URL信息显示到文本区
                  ta.append(line);    //行分隔符!
                  ta.append("Port: "+url.getDefaultPort());
                  ta.append(line);
                  ta.append("ContentType: "+urlConn.getContentType()); //目标类型
                  ta.append(line);
                  ta.append("ContentLength: "+urlConn.getContentLength());
                  InputStream is=urlConn.getInputStream();  //Returns an input stream that reads from this open connection.
                  //InputStreamReader isr=new InputStreamReader(is); //由于读取的是文本文件,定义个字符流!
                  //BufferedReader br=new BufferedReader(isr);       //提高效率
                  FileOutputStream fos=new FileOutputStream("1.html");  //保存到本地硬盘
                  //String strLine;
                  //while((strLine=br.readLine())!=null)    //按行写入本地文件;不会读取行分隔符
                  int data;
                  while((data=is.read())!=-1)   //读取数据文件(如图片)时,用字节流;
                  {
    //                fos.write(strLine.getBytes());
      //              fos.write(line.getBytes());  //补上行分隔符!
                    fos.write(data);   //写入字节,一个字节一个字节地读、写!
                  }
                  //br.close();
                  is.close();
                  fos.close();
                }
                catch (Exception ex) {
                  ex.printStackTrace();
                }

              }
            });
        jf.addWindowListener(new WindowAdapter() {
             public void windowClosing(WindowEvent e) {
               System.exit(0);
             }
           });
        jf.show();
      }
    }














  • 15件事为你的职业生涯助力(转载)

    2009-03-23 14:53:14

    15件事为你的职业生涯助力
    From:
    http://blog.sina.com.cn/tbbi

    大部分关于职业生涯的建议都把目光放在宏大的规划上,比如:告诉我们该如何领先、如何“赢”等等。比起我们每天实际生活中面对的琐事,这些都属于宏观的范 围;然而在现实中,真正拖住我们的而是那些日常的琐事——我们每天早上去上班,晚上拖着疲惫的身体回到家,常常感到自己只是日复一日的重复同样的事情。
     

        如果希望在职业生涯上以及生活中 不断前进,你需要从一些小事开始,关注那些你可以立即着手去做的事情,由此改变你的现状。如果你现在正坐在书桌前面,思绪枯竭的寻思,做点什么更好的事情 来改善你的生活,那么可以考虑从下面的15件事做起。它们都是易于立即实践的事情,却能够引领你走向更美好的职业生涯。

    • 列出你今天/本周/本月为部门做过的所有事情,并把它们归档

    每当你有点空闲时间的时候,列出一个清单,记下你在做的所有实际工作。记录下它们的时间,以及详细的举例说明。完成之后,把它存放在一个文件夹里。下次工 作成果回顾或提出升职请求的时候,就会用得到了。我规律性的进行这项工作,它使我能够为我的职业生涯留下广泛而良好的记录。

    • 寄一封感谢信

    如果你最近在事业上或个人发展上得到某人的帮助,花点时间亲笔写一封感谢信,寄给这个人。手写的感谢信所传达的敬重、诚意以及它本身所带有的人与人之间的触感会在收信人的心中留下难忘而美好印象。这些在长远意义上,会为你带来很多帮助。

    • 提高写作技巧

    对我来说,写博客文章,实际上就是一种改进写作能力的活跃方法。以自己感兴趣并且与工作相关的主题为内容,写一篇博客文章,是一种练习写作技能的好方法。但是,不要产生惰性。尽量多使用一些能够吸引别人的强有力的材料,因为如果没有它们,你就不会真正改进写作技能,而只是单调的重复咀嚼事实而已。参考阅读《15条技巧提高你的写作技巧

    • 为自己设计一些美观的商务卡片

    我有一小堆商务卡片,他们只属于我个人,而非任何一家我代表或就职的公司。我经常把卡片放进寄出的信件中,或在任何面对面的机会中送给他人。这些卡片反映 着我希望传达的个人形象。它们的高品质,为我在收到的人心中留下一个积极的印象。不要去办公用品商店买那种有一排小孔,便于用手撕开成小卡片的卡纸,而要 用专业的方法,制作高品质的卡片。

    • 发一封简短的联系邮件

    是不是有一些业内人士,你最近没有与他们保持联系?给他们发封电子邮件,让他们知道你的近况,并且问一问,他们最近在做些什么。这些人,我指的不是你的客 户,而是你在行业内认识的专业人士(联系顾客应当是你的工作流程中的一部分内容)。给你在去年的会议上认识的同行发一封这样的简短邮件联系他们,是一件很 有益的事情。说不定他们刚好能够提供给你一个对你的事业来说很好的机会。

    • 为工作场所里一直没有解决的问题提供解决方法

    并非仅仅关注企业范围内的大型问题,关心小事也是尤为必要的。比如,人们是不是总在抱怨打印机的问题?记下可选用的打印机规格,向你的老板提交一份购买新 打印机的申请。有两个同事的工作关系有些紧张?想一想如何用明智而谨慎的方法把他们分开。把你的想法写下来,呈递给老板。这里的关键是,尽量把你的解决方 法考虑的周全,这样你的老板就能够意识到,你发现了问题、思考过如何去解决,而且自行的提出了解决方案。

    • 打个电话

    团队整 体保持联系,以保证自己了解团队里正在发生的事情,这样当开会的时候,你就会掌握更清楚的信息。打个电话给别人,看看有什么新鲜的事情该了解一下,或者在 工作场所的不同部门转一转,看看别人在做些什么。千万不要孤傲自大,而是应该一直清楚,正在发生什么事情,它们会对你有什么影响,你又如何能利用它们发展 自己的事业。

    • 努力提高语言能力

    你的工作场所中所使用的语言是你的母语么?如果不是的话,花一些空闲的时间,去提高语言技能。我发现,当你已经了解了一门语言的基本知识之后,再听一些听 力材料以及播客材料,会很好的帮助你理解这一语言与你所掌握的语言之间的细微差别。如果你还没有掌握该语言的基本知识,那就从一些听力教程开始学习。就我 而言,我发现自学一门外语的一种非常有效的方法是,阅读一部你已经十分熟悉的作品的翻译版本,比方说我读过《哈利波特》。

    • 让自己外表更整洁得体

    保持整洁清新的仪容总会很有益处。我的旅行包里总是装着一些个人卫生用品:香体液、牙膏、牙刷、漱口水、洁面纸、梳子以及科隆香水。我做到每天至少清洁一次,但只要有空,常常是一天两次。这样,我在下午三点半开会的时候,也能保持自己看起来整洁清新——刚刚好好洗了一把脸,我会感到更加清醒。

    • 润色你的简历

    任何时候都可以考虑润色一下你的简历。确保上面包含大量详细记录你的工作成果的文字和文件。事实上,你的个人工作记录,是扮靓简历的一种绝好材料。

    • 阅读与未来职业生涯会遇到的有关话题的材料

    不要浪费时间去读无关的新潮杂志。你应该去寻找和关注与你的职业有关的信息资源。如果你常常以浏览网络的方式消磨工作中的闲暇,那至少应该考虑把这些时间用来浏览那些有可能助你发展事业的内容。

    • 为你的下一次会议提前准备一些评论

    过会儿将要有个会议么?与其感到怯场,不如把它当作一次让自己闪光的机会。研读一下会议的议程,准备一些想法,把它们写下来,以备在会议上发言。如果你将要听一个演讲,那么可以查一查它是关于什么的,提前做一些初步的调查,这使你有可能在听完演讲后提出一些锐利的精彩问题。

    • 提高你在公众面前的表达能力

    这看起来好像不是那么容易即刻去做事情,但事实并非如此。去网上听一听著名演说家的演讲,刻录下来一两张你非常喜欢的演讲CD,在回家的路上边听边跟着练习。你可以通过这种方法很好的学到如何在演讲中清楚的表达要点和感染听众。

    • 清理你的工作区域

    这不仅可以在别人眼中为自己留下一个更好的印象,还可以帮你找到一些你以为丢失了的重要资料。另外,也可以使用文件归档的方法,它能使你更快的找到需要的资料。参考阅读《运用GTD来整理书桌

    • 思考你希望未来的五年在什么地方工作——制定实现计划

    花些时间想象一下,你希望未来在哪里工作,然后思考,为此需要付出什么。清楚了这些以后,把实现计划的过程中的每一项分解,直到分解成你目前就可以迅速去做的事情——然后,开始行动。千里之行,始于足下。道理不过如此。

     

    原文:15 Things You Can Do Right Now To Help Your Career - Life Hacks
  • 孙鑫JAVA教学视频学习笔记(7-8)

    2009-03-12 12:31:29

    七. I/O操作,字节流,字符流,字符集编码

    1.I/O操作
    是放在java.io包中的;

    2.File类
    一个File类的对象,表示了磁盘上的文件或目录。
    File类提供了与平台无关的方法来对磁盘上的文件或目录进行操作。

    import java.io.*;
    class FileTest
    {
        public static void main(String[] args) throws Exception //IO操作会经常抛出异常,我们交给JVM来处理
        {
            //File f=new File("1.txt");
            // f.createNewFile(); //创建一个文件1.txt
            //f.mkdir();  //创建一个目录:1.txt
            /*指定绝对路径:\在JAVA是特殊的转义字符;所以输出\用“\\” */
            //File f=new File("E:\\JavaLesson\\Lesson7\\1.txt"); //--但它只能在当前windows系统有效;在Linux下无效
            File fDir = new File(File.separator); //定义父目录为根目录;在windows下表示当前运行盘符的根目录!
            String strFile="JavaLesson"+File.separator+
                "Lesson7"+File.separator+"1.txt"; //win和Linux系统中编译成不同符合;
            File f = new File(fDir,strFile); //根目录+当前目录和文件;可移植到Linux;
            f.createNewFile();
        }
    }

    属性和方法:
    a.separator
    separator属性用来代表路径斜杠;通用的!
    构造函数可以采用父目录+子文件名的方式:
    File(File parent, String child)
              Creates a new File instance from a parent abstract pathname and a child pathname string.

    b.删除文件:delete()
    boolean delete()
              Deletes the file or directory denoted by this abstract pathname.
    f.delete() //将f文件删除

    c.deleteOnExit
    deleteOnExit(); 在程序退出的时候删除文件!
    可以用来删除垃圾文件;临时文件等;

    d.createTempFile 创建临时文件到临时目录(返回File对象);
    static File createTempFile(String prefix, String suffix)
              Creates an empty file in the default temporary-file directory, using the given prefix and suffix to generate its name.
    e.g.:        for(int i=0;i<5;i++)
            {
                File f = File.createTempFile("winsun",".tmp"); //会创建到系统默认的临时文件夹中;
                f.deleteOnExit();   //必须手动删除
            }
    Thread.sleep(3000);

    e.List()方法:
    String[] list() ---返回字符串数组; 列出所有文件和子目录;
              Returns an array of strings naming the files and directories in the directory denoted by this abstract pathname.

    e.g.:
            File fDir=new File(File.separator);
            String strFile="JavaLesson"+File.separator+"Lesson6";
            File f=new File(fDir,strFile);
            String[] names=f.list(); //返回一个数组
            for(int i=0;i<names.length;i++)
            {
                System.out.println(names[i]); //打印内容
            }

    e.2 增加过滤器: -- 增加条件
     String[] list(FilenameFilter filter) --增加一个文件过滤器,选择文件
              Returns an array of strings naming the files and directories in the directory denoted by this abstract pathname that satisfy the specified filter.

    其中,FilenameFilter 是一个接口;包含一个方法accept:
    boolean accept(File dir, String name) ---- Tests if a specified file should be included in a file list.

    e.g.:
            File fDir=new File(File.separator);
            String strFile="JavaLesson"+File.separator+"Lesson6";
            File f=new File(fDir,strFile);
    //利用内部类来实现FilenameFilter接口,accept方法使用了一个Sting的indexOf方法:
            String[] names=f.list(new FilenameFilter()
            {
                public boolean accept(File dir,String name)
                {
                    return name.indexOf(".java")!=-1;
                } //有.java子串时返回真,选所有java文件!
            });  //作为方法参数传递
            for(int i=0;i<names.length;i++)
            {
                System.out.println(names[i]);
            }

    e.3: String.indexOf()
     int indexOf(String str)
              Returns the index within this string of the first occurrence of the specified substring.
    功能: 返回子串的索引值,当没有该子串时返回-1

    在File类中,没有给我们提供对文件进行读写操作的方法!

    3.输入流,输出流 --- 流式I/O

    流(Stream)是字节的源或目的。
    两种基本的流是:输入流(Input Stream)和输出流(Output Stream)。可从中读出一系列字节的对象称为输入流。而能向其中写入一系列字节的对象称为输出流。
    流的分类:
    节点流:从特定的地方读写的流类,例如:磁盘或一块内存区域。
    过滤流:使用节点流作为输入或输出。过滤流是使用一个已经存在的输入流或输出流连接创建的。 --- 能够获得一些额外的功能!

    4.InputStream -- 抽象类!
    三个基本的读方法
          abstract int read() :读取一个字节数据,并返回读到的数据,如果返回-1,表示读到了输入流的末尾。 -- 返回一个整型,所以int中第一个字节有效,后三个无效!
          int read(byte[] b) :将数据读入一个字节数组,同时返回实际读取的字节数。如果返回-1,表示读到了输入流的末尾。
          int read(byte[] b, int off, int len) :将数据读入一个字节数组,同时返回实际读取的字节数。如果返回-1,表示读到了输入流的末尾。off指定在数组b中存放数据的起始偏移位置;len指定读取的最大字节数。
    其它方法
          long skip(long n) :在输入流中跳过n个字节,并返回实际跳过的字节数。
          int available() :返回在不发生阻塞的情况下,可读取的字节数。
          void close() :关闭输入流,释放和这个流相关的系统资源。-- 创建一个输入流时,OS会分配一些资源;
          void mark(int readlimit) :在输入流的当前位置放置一个标记,如果读取的字节数多于readlimit设置的值,则流忽略这个标记。 -- 在InputStream中是个空实现!
          void reset() :返回到上一个标记。-- 在InputStream中是个空实现!
          boolean markSupported() :测试当前流是否支持mark和reset方法。如果支持,返回true,否则返回false。-- 然后再实现

     

    5. OutputStream
    三个基本的写方法
         abstract void write(int b) :往输出流中写入一个字节。
         void write(byte[] b) :往输出流中写入数组b中的所有字节。
         void write(byte[] b, int off, int len) :往输出流中写入数组b中从偏移量off开始的len个字节的数据。
    其它方法
         void flush() :刷新输出流,强制缓冲区中的输出字节被写出。-- 为了加快输出设备的出入速度,一般先写到缓冲区,然后一次性与I/O设备交互进行输出,但是为了立即输出,则采用此方法!
         void close() :关闭输出流,释放和这个流相关的系统资源。

     

    System.out 就是一个PrintStream类型!

    System还有两个对象: in, out 从标准输入设备读,从标准输出设备写

    6.基本的流类
    FileInputStream和FileOutputStream
        节点流,用于从文件中读取或往文件中写入字节流。如果在构造FileOutputStream时,文件已经存在,则覆盖这个文件。
    e.g.:
    import java.io.*;
    class StreamTest
    {
        public static void main(String[] args) throws Exception
        {
            FileOutputStream fos=new FileOutputStream("1.txt");
            fos.write("http://www.mybole.com.cn".getBytes()); //write(byte[] b) -- 需要个字节数组,利用String的方法得到!神奇!
            fos.close();  //关闭资源   
           
            FileInputStream fis=new FileInputStream("1.txt"); //用已存在的文件构造文件输入流对象;
            byte[] buf=new byte[100];
            int len=fis.read(buf);  //从文件中读取数据流到buf数组
            System.out.println(new String(buf,0,len)); //用字节数组构造String串,用另一个构造函数
            fis.close();   
        }
    }

    BufferedInputStream和BufferedOutputStream
        过滤流,需要使用已经存在的节点流来构造,提供带缓冲的读写,提高了读写的效率。
    BufferedInputStream将mark和reset方法实现了!BufferedOutputStream实现了flush方法
    需要用已经存在的FileInputStream和FileOutputStream 来构建它们!
    import java.io.*;
    class StreamTest
    {
        public static void main(String[] args) throws Exception
        {
            FileOutputStream fos=new FileOutputStream("1.txt");
            BufferedOutputStream bos=new BufferedOutputStream(fos);  //用fos来构建bos
            bos.write("http://www.winsun.com.cn".getBytes());  //将字符串写入bos对象
            bos.flush(); //或者用 bos.close(); 也能将缓冲区的数据写入磁盘! 关了它就无需关闭fos了!-- 连锁
           
            FileInputStream fis=new FileInputStream("1.txt");
            BufferedInputStream bis=new BufferedInputStream(fis); //用fis构建bis
            byte[] buf=new byte[100];
            int len=bis.read(buf); //读出对象内容到buf中
            System.out.println(new String(buf,0,len));
            bis.close();    //无需再fis.close;
        }
    }

    DataInputStream和DataOutputStream
          过滤流,需要使用已经存在的节点流来构造,提供了读写Java中的基本数据类型的功能。
    是由FilterInputStream继承而来,实现了DataInput接口(基本类型的读取);Output类似!
    Constructor:
    DataOutputStream(OutputStream out)
    e.g.
    import java.io.*;
    class StreamTest
    {
        public static void main(String[] args) throws Exception
        {
            FileOutputStream fos=new FileOutputStream("1.txt");
            BufferedOutputStream bos=new BufferedOutputStream(fos);
            DataOutputStream dos=new DataOutputStream(bos); //也可以用fos!
            byte b=3;
            int i=78;
            char ch='a';
            float f=4.5f;
            dos.writeByte(b);  //dos中实现的方法
            dos.writeInt(i);
            dos.writeChar(ch);
            dos.writeFloat(f);
            dos.close();  //由于我们链接了一个bos,所以有buffer的功能!关闭才能写入!
           
            FileInputStream fis=new FileInputStream("1.txt");
            BufferedInputStream bis=new BufferedInputStream(fis);
            DataInputStream dis=new DataInputStream(bis);
            System.out.println(dis.readByte());
            System.out.println(dis.readInt());
            System.out.println(dis.readChar());
            System.out.println(dis.readFloat());
            dis.close();
        }
    }


    PipedInputStream和PipedOutputStream
        管道流,用于线程间的通信。一个线程的PipedInputStream对象从另一个线程的PipedOutputStream对象读取输入。要使管道流有用,必须同时构造管道输入流和管道输出流。

    可用无参构造,然后用connect()方法进行连接;
    也可在构造的时候直接指定管道输出流进行连接!
    PipedInputStream(PipedOutputStream src)
              Creates a PipedInputStream so that it is connected to the piped output stream src.

    同样,PipedOutputStream也有类似的方法!

    注意管道流是由输入输出流继承而来的而不是过滤流FilterInput(Output)Stream继承而来的!
    e.g.
    import java.io.*;
    class PipedStreamTest
    {
        public static void main(String[] args) throws Exception
        {
            PipedOutputStream pos=new PipedOutputStream();
            PipedInputStream pis=new PipedInputStream();
            try
            {
                pos.connect(pis);  //也可以反过来连接,或者构造的时候连接!
                new Producer(pos).start();
                new Consumer(pis).start();
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }
        }
    }

    class Producer extends Thread   //用管道输出流定义生产者
    {
        private PipedOutputStream pos;
        public Producer(PipedOutputStream pos)
        {
            this.pos=pos;
        }
        public void run()
        {
            try
            {
                pos.write("Hello,welcome you!".getBytes());
                pos.close();
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }
        }
    }

    class Consumer extends Thread  //用管道输入流定义消费者
    {
        private PipedInputStream pis;
        public Consumer(PipedInputStream pis)
        {
            this.pis=pis;
        }
        public void run()
        {
            try
            {
                byte[] buf=new byte[100];
                int len=pis.read(buf);    //无实体文件,直接从输入流中读(得到的是对应输出流中的数据)
                System.out.println(new String(buf,0,len));   //字节数组,偏移量,长度
                pis.close();
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }
        }
    }

    7.Java I/O库的设计原则
    Java的I/O库提供了一个称做链接的机制,可以将一个流与另一个流首尾相接,形成一个流管道的链接。这种机制实际上是一种被称为Decorator(装饰)设计模式的应用。
    通过流的链接,可以动态的增加流的功能,而这种功能的增加是通过组合一些流的基本功能而动态获取的。
    我们要获取一个I/O对象,往往需要产生多个I/O对象,(如buffer流产生就需要先构造基本输入输出流)这也是Java I/O库不太容易掌握的原因,但在I/O库中Decorator模式的运用,给我们提供了实现上的灵活性。

     

    之前的都是字节流;下面介绍字符流:

    8.Reader和Writer
    Java程序语言使用Unicode来表示字符串和字符。
    Reader和Writer这两个抽象类主要用来读写字符流。

     
    BufferedRead并没有从FilterReader中派生而来(不同于字节流中的层次),是sun的一个bug!
    InputStreamReader起到了一个桥梁的作用,用来进行字符流与字节流之间的相互转换!

     
    BufferedWriter以及OutputStreamWriter与上面对应!

    BufferedReader,BufferedWriter提供了对字符的快速读取

    构造函数:
    OutputStreamWriter(OutputStream out)
              Creates an OutputStreamWriter that uses the default character encoding.

    import java.io.*;
    class StreamTest
    {
        public static void main(String[] args) throws Exception
        {
            FileOutputStream fos=new FileOutputStream("1.txt");
            OutputStreamWriter sw=new OutputStreamWriter(fos); //字节流到字符流的桥梁!
            BufferedWriter bw=new BufferedWriter(osw); //写操作一般用这个而不用OSW,更高效!
    /*Constructor:BufferedWriter(Writer out) Creates a buffered character-output stream
    that uses a default-sized output buffer. -- OSW是Writer的子类,所以也可以用来构造*/
            bw.write("http://www.mybole.com.cn");
            bw.close();
           
            FileInputStream fis=new FileInputStream("1.txt");
            InputStreamReader isr=new InputStreamReader(fis);
            BufferedReader br =new BufferedReader(isr);
            System.out.println(br.readlLine());
            br.close();
        }
    }


    从键盘接收多行输入,然后输出到屏幕:
            InputStreamReader isr=new InputStreamReader(System.in); //不用文件输入流(fis)构造了,而直接用in
            BufferedReader br=new BufferedReader(isr);
            String strLine;
            while((strLine=br.readLine())!=null)  //按行读取
            {
                System.out.println(strLine);
            }
            br.close();

    9.字符集的编码
    ASCII(American Standard Code for Information Interchange,美国信息互换标准代码),是基于常用的英文字符的一套电脑编码系统。我们知道英文中经常使用的字符、数字符号被计算机处理时都是以二进制码的形式出现的。这种二进制码的集合就是所谓的ASCII码。每一个ASCII码与一个8位(bit)二进制数对应。其最高位是0,相应的十进制数是0-127。如,数字¡°0¡±的编码用十进制数表示就是48。另有128个扩展的ASCII码,最高位都是1,由一些制表符和其它符号组成。ASCII是现今最通用的单字节编码系统。
    GB2312:GB2312码是中华人民共和国国家汉字信息交换用编码,全称《信息交换用汉字编码字符集-基本集》。主要用于给每一个中文字符指定相应的数字,也就是进行编码。一个中文字符用两个字节的数字来表示,为了和ASCII码有所区别,将中文字符每一个字节的最高位置都用1来表示。
    GBK:为了对更多的字符进行编码,国家又发布了新的编码系统GBK(GBK的K是¡°扩展¡±的汉语拼音第一个字母)。在新的编码系统里,除了完全兼容GB2312 外,还对繁体中文、一些不常用的汉字和许多符号进行了编码。
    ISO-8859-1:是西方国家所使用的字符编码集,是一种单字节的字符集 ,而英文实际上只用了其中数字小于128的部分。

    Unicode:这是一种通用的字符集,对所有语言的文字进行了统一编码,对每一个字符都用2个字节来表示,对于英文字符采取前面加“0”字节的策略实现等长兼容。如 “a” 的ASCII码为0x61,UNICODE就为0x00,0x61。
        UTF-8:Eight-bit UCS Transformation Format,(UCS,Universal Character Set,通用字符集,UCS 是所有其他字符集标准的一个超集)。一个7位的ASCII码值,对应的UTF码是一个字节。如果字符是0x0000,或在0x0080与0x007f之间,对应的UTF码是两个字节,如果字符在0x0800与0xffff之间,对应的UTF码是三个字节。

    10.CharsetTest
    Charset类有个方法能列出可以使用的字符集:
    static SortedMap<String,Charset> availableCharsets()
              Constructs a sorted map from canonical charset names to charset objects.
    import java.util.*;
    import java.nio.charset.*;
    class CharsetTest
    {
        public static void main(String[] args) throws Exception
        {
            Map m=Charset.availableCharsets(); //调用静态方法
            Set names=m.keySet(); //map接口的keyset方法返回key的集合
            Iterator it=names.iterator();  //集合没有get方法,用迭代器
            while(it.hasNext())
            {
                System.out.println(it.next());
            }
        }
    }

    java.lang.System.有个getProperty方法

    Properties pps=System.getProperties(); //属性类
    pps.list(System.out);  //流出所有系统属性 file.encoding

    在java中,字符,字符串都是用unicode来构成的;所以得到unicode的过程就是解码,用unicode转化成字节的过程就是编码!

    import java.util.*;
    import java.nio.charset.*;  //new io
    class CharsetTest
    {
        public static void main(String[] args) throws Exception
        {
            Properties pps=System.getProperties(); //属性类
            pps.list(System.out);  //流出所有系统属性 file.encoding
            pps.put("file.encoding","ISO-8859-1");  //设置属性的方法!
            int data;
            byte[] buf=new byte[100];
            int i=0;
            while((data=System.in.read())!='q') //从键盘读取中文字符,q退出;读取使用OS默认charset:中文GBK!
                                                //在标准设备读取时,依赖于本地操作系统字符集
            {
                buf[i]=(byte)data;  //中文会以GBK码值存储;默认charset
                i++;
            }
            String str=new String(buf,0,i);    //获取字符 --- 解码!解码使用JVM指定的编码:ISO...
            System.out.println(str);  //打印出乱码!  -- Java 1.6中正常打印, Why?
            String strGBK=new String (str.getBytes("ISO-8859-1"),"GBK");   //用ISO先编码回去,然后再用GBK重新解码!!!!
            System.out.println(strGBK);
        }
    }

    11. RandomAccessFile
    RandomAccessFile类同时实现了DataInput和DataOutput接口(-- 所以实现了对基本数据类型的读写!),提供了对文件随机存取的功能,利用这个类可以在文件的任何位置读取或写入数据。
    RandomAccessFile类提供了一个文件指针,用来标志要进行读写操作的下一数据的位置。

    Constructor:
    RandomAccessFile(String name, String mode)
              Creates a random access file stream to read from, and optionally to write to, a file with the specified name.
    --- mode:Value Meaning:
     "r" Open for reading only. Invoking any of the write methods of the resulting object will cause an IOException to be thrown. 
    "rw" Open for reading and writing. If the file does not already exist then an attempt will be made to create it. 
    "rws" Open for reading and writing, as with "rw", and also require that every update to the file's content or metadata be written synchronously to the underlying storage device. 
    "rwd"   Open for reading and writing, as with "rw", and also require that every update to the file's content be written synchronously to the underlying storage device. 

    writeBytes(String str)  //中文字符会
    writeChars(String str)
    writeUTF(String str)   //在前两个字节中记录了字符的长度! 常用这个!

    11.1: long length()
              Returns the length of this file.
    11.2:  long getFilePointer()
              Returns the current offset in this file.  offset:偏移量!
    11.3: void seek(long pos)
              Sets the file-pointer offset, measured from the beginning of this file, at which the next read or write occurs.

    e.g.:
    import java.io.*;
    class RandomFileTest
    {
        public static void main(String[] args) throws Exception
        {
            Student s1=new Student(1, "zhangsan", 98.5);
            Student s2=new Student(2, "sdsan", 74.5);
            Student s3=new Student(3, "adsfn", 81.5);
            RandomAccessFile raf=new RandomAccessFile("student.txt","rw");
            s1.writeStudent(raf);
            s2.writeStudent(raf);
            s3.writeStudent(raf);
           
            Student s=new Student();
            raf.seek(0);
            for(long i=0;i<raf.length();i=raf.getFilePointer())        //因为length返回值为long故i也定义为long!
            //getFilePointer() 获得文件中当前的偏移量!
            {
                s.readStudent(raf);
                System.out.println(s.num+":"+s.name+":"+s.score);
            }
            raf.close();
        }
    }

    class Student
    {
        int num;
        String name;
        double score;
        public Student()
        {
        }
        public Student(int num, String name, double score)
        {
            this.num=num;
            this.name=name;
            this.score=score;
        }
        public void writeStudent(RandomAccessFile raf) throws IOException
        {
            raf.writeInt(num);
            raf.writeUTF(name);
            raf.writeDouble(score);
        }
        public void readStudent(RandomAccessFile raf) throws IOException
        {
            num=raf.readInt();
            name=raf.readUTF();
            score=raf.readDouble();
        }
    } //随时注意文件指针!

    12.对象序列化
    将对象转换为字节流保存起来,并在日后还原这个对象,这种机制叫做对象序列化。
    将一个对象保存到永久存储设备上称为持续性。
    一个对象要想能够实现序列化,必须实现Serializable接口或Externalizable接口。--IO包内; Serializable接口中没有方法,是个空接口!标示接口!Externalizable是从Serializable继承而来的!
    要想实现对象的序列化,我们可以利用两个类:ObjectOutputStream/ObjectInputStream 它们实现了DataOutput/DataInput对象,可以读写基本数据类型!
    其中有个方法writeObject(),写入对象成员!

    ObjectOutputStream(OutputStream out)  -- 将对象信息转化成字节流写入文件中保存!
              Creates an ObjectOutputStream that writes to the specified OutputStream.

    import java.io.*;

    class ObjectSerialTest
    {
        public static void main(String[] args) throws Exception
        {
            Employee e1=new Employee("zhangsan",25,3000.50);
            Employee e2=new Employee("lisi",24,3200.40);
            Employee e3=new Employee("wangwu",27,3800.55);
           
            FileOutputStream fos=new FileOutputStream("employee.txt");
            ObjectOutputStream os=new ObjectOutputStream(fos);
            oos.writeObject(e1);
            oos.writeObject(e2);
            oos.writeObject(e3);
            oos.close();
           
            FileInputStream fis=new FileInputStream("employee.txt");
            ObjectInputStream is=new ObjectInputStream(fis);
            Employee e;
            for(int i=0;i<3;i++)
            {
                e=(Employee)ois.readObject();  // 反序列化的时候并不会调用构造函数!
                System.out.println(e.name+":"+e.age+":"+e.salary);
            }
            ois.close();
        }
    }

    class Employee implements Serializable
    {
        String name;
        int age;
        double salary;
        transient Thread t=new Thread();
        public Employee(String name,int age,double salary)
        {
            this.name=name;
            this.age=age;
            this.salary=salary;
        }
        private void writeObject(java.io.ObjectOutputStream oos) throws IOException   //重新定义写方法,实现自己的控制,如薪水加密后再保存,可无
        {
            oos.writeInt(age);
            oos.writeUTF(name);
            System.out.println("Write Object");
        }
        private void readObject(java.io.ObjectInputStream ois) throws IOException   //重新定义读方法,可无!
        {
            age=ois.readInt();
            name=ois.readUTF();
            System.out.println("Read Object");
        }

    }

    当一个对象被序列化时,只保存对象的非静态成员变量,不能保存任何的成员方法和静态的成员变量。
    如果一个对象的成员变量是一个对象,那么这个对象的数据成员也会被保存。
    如果一个可序列化的对象包含对某个不可序列化的对象的引用,那么整个序列化操作将会失败,并且会抛出一个NotSerializableException。我们可以将这个引用标记为transient,那么对象仍然可以序列化。

    13.总结
    InputStream和OutputStream:字节流的输入输出。--抽象基类!
    -- 派生了
    FileInputStream和FileOutputStream: 用来对文件进行读写操作;
    BufferedInputStream和BufferedOutputStream: 提供了带缓冲的读写;
    DataInputStream和DataOutputStream: 提供了读写JAVA基本数据类型的功能;
    PipedInputStream和PipedOutputStream:在两个线程中间通信
    ObjectInputStream和ObjectOutputStream:完成对象的序列化和反序列化;
    Reader和Writer:字符流的输入输出。-- 抽象基类!:1.0中只提供字节流操作;JDK1.1以后新出现字符流;
    -- 派生了:
    InputStreamRead/OutputStreamWriter:提供了字节流到字符流转换的桥梁!
    BufferedReader/Writer: 提高了读写字符流的效率!比较常用
    流的链接(Java I/O库的设计原则) -- 功能组合在一起形成功能更加强大的机制!




    八. Java图形界面编程

    内容:
    Java图形界面编程,AWT、布局管理器、事件模型,JFC、Swing编程。应用JBuilder快速开发图形界面程序。

    1.AWT
    AWT(Abstract Window Toolkit),抽象窗口工具包,SUN公司提供的用于图形界面编程(GUI)的类库。基本的AWT库处理用户界面元素的方法是把这些元素的创建和行为委托给每个目标平台上(Windows、Unix、Macintosh等)的本地GUI工具进行处理。例如:如果我们使用AWT在一个Java窗口中放置一个按钮,那么实际上使用的是一个具有本地外观和感觉的按钮。这样,从理论上来说,我们所编写的图形界面程序能运行在任何平台上,做到了图形界面程序的跨平台运行。

    Class Component --JAVA中所有图形界面组件的抽象基类
    =》派生:Container  -- 容器对象是可以容纳其它组件的组件
      =》派生: Window  -- 顶层窗口,无边框无菜单
        =》派生: Frame. -- 顶层窗口,有一个边框和一个标题

    Component.setVisible(boolean b) //为真则显示
    Window.show() //显示窗口并带到前端!

    e.g.
        public static void main(String[] args)
        {
            Frame. f = new Frame("mybole");
            f.setSize(600,400);
            f.setLocation(100,100);
            f.setBackground(Color.CYAN);
            f.show();
            Button btn = new Button("winsun");
            f.add(btn);
            f.show();
        }

    2.布局管理器
    容器里组件的位置和大小是由布局管理器来决定的。容器对布局管理器的特定实例保持一个引用。当容器需要定位一个组件时,它将调用布局管理器来完成。当决定一个组件的大小时,也是如此。
    在AWT中,给我们提供了五种布局管理器:
    a. BorderLayout   ---- Frame. 的缺省布局管理器;分为东西南北中五个方向!
     
       
    BorderLayout (int 水平间隙, int 水平间隙)
    package lesson8;
    import java.awt.*;
    public class MyFrame
    {
        public static void main(String[] args)
        {
            Frame. f = new Frame("mybole");
            f.setSize(600,400);
            f.setLocation(100,100);
            f.setBackground(Color.BLUE);
            f.setLayout(new BorderLayout(10,10)); //设置Layout:BorderLayout,设置间隙!
            Button btn1 = new Button("North");
            Button btn2 = new Button("South");
            Button btn3 = new Button("West");
            Button btn4 = new Button("East");
            Button btn5 = new Button("Center");
            f.add(btn1,"North");
            f.add(btn2,"South");
            f.add(btn3,"West");
            f.add(btn4,"East");
            f.add(btn5,"Center");
            f.show();
        }


    b. FlowLayout //依次摆放每个组件;
    f.setLayout(new FlowLayout(FlowLayout.LEFT)); //添加参数,靠左开始;

    c.  GridLayout -- 网格状摆放
    构造函数可无参;
    GridLayout(int rows, int cols)  -- 参数指定几行几列
              Creates a grid layout with the specified number of rows and columns.
        CardLayout
       
    e. GridBagLayout -- 五种里最复杂的;平时很少使用;(很少直接使用)

    我们可以通过设置空布局管理器,来控制组件的大小和位置。调用setLayout(null)。
    在设置空布局管理器后,必须对所有的组件调用setLocation(),setSize()或setBounds(),将它们定位在容器中。

    3.如何关闭窗口;
    AWT事件模型
    Events(事件):描述发生了什么的对象。
    Event source(事件源):事件的产生器。
    Event handlers(事件处理器):接收事件对象、解释事件对象并处理用户交互的方法。

    JDK1.0的事件模型:层次模型;
    Frame->Panel->Button -- 产生事件:可以传递给它的容器,返回给上一层;
    JDK1.1的事件模型:委托模型 -- 将事件的执行分开
    事件监听器:实现了监听器接口的类。一个监听器对象是一个实现了专门的监听器接口的类的实例。-- awt.envent.windowListener
    窗口事件必须委托给窗口监听器!
    法1:
    ...
    f.addWindowListener(new MyWindowListener());
    f.show();
    ...
    class MyWindowListener implements WindowListener
    {
      public void windowOpened(WindowEvent e)
      {

      }
      public void windowClosing(WindowEvent e)
      {
        System.exit(0);
      }
      public void windowClosed(WindowEvent e)
      {
      }
      public void windowIconified(WindowEvent e)
      {

      }
      public void windowDeiconified(WindowEvent e)
      {

      }
      public void windowActivated(WindowEvent e)
      {

      }
      public void windowDeactivated(WindowEvent e)
      {

      }
    }

    法2:JBuilder提供了一个接口实现向导可以直接实现所有方法;
    法3:JAVA 提供了一个抽象类WindowAdapter(实现了监听器WindowListener--空实现!) -- 继承它并实现需要的方法即可:

    class HisWindowListener extends WindowAdapter
    {
      public void windowClosing(WindowEvent e)
      {
        System.exit(0);
      }
    }

    法4:匿名内部类 -- 用Window适配器类实现;
    //使用了一个匿名的内部类对象
        f.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
              System.exit(0);
            }
          });
        f.show();

    CTRL+G --- JBuilder提供的代码生成功能!!
    空白处按得到全部列表;
    out CTRL+g 得到一个println语句!

    想用四种布局管理器,用新的容器Panel--它是Frame的子类;
    package lesson8;

    import java.awt.*;
    import java.awt.event.*;

    public class YourFrame. extends Frame
    {
      private Panel borderPanel;
      private Panel flowPanel;
      private Panel gridPanel;
      private Panel cardPanel;
      public YourFrame(String title) //Constructor
      {
        super(title);  //传递基类标题
        setSize(600,400);
        setLocation(100,100);
        setBorderLayoutPanel(); //对4个panel初始化
        setFlowLayoutPanel();
        setGridLayoutPanel();
        setCardLayoutPanel();
        setLayout(new GridLayout(2,2)); //框架窗口初始化,两行两列,刚好4个panel
        add(borderPanel);
        add(flowPanel);
        add(gridPanel);
        add(cardPanel);
        addWindowListener(new WindowAdapter() {
          public void windowClosing(WindowEvent e) {
            System.exit(0);
          }
        });
      }

      public void setBorderLayoutPanel()
      {
        borderPanel=new Panel();
        borderPanel.setLayout(new BorderLayout());
        Button btn1=new Button("North");
        Button btn2=new Button("South");
        Button btn3=new Button("West");
        Button btn4=new Button("East");
        Button btn5=new Button("Center");
        borderPanel.add(btn1,BorderLayout.NORTH);
        borderPanel.add(btn2,BorderLayout.SOUTH);
        borderPanel.add(btn3,BorderLayout.WEST);
        borderPanel.add(btn4,BorderLayout.EAST);
        borderPanel.add(btn5,BorderLayout.CENTER);
      }
      public void setFlowLayoutPanel()
      {
        flowPanel=new Panel();
        Button btn1=new Button("mybole");
        btn1.addActionListener(new ActionListener() {  //增加按钮响应
               public void actionPerformed(ActionEvent e) {
                 ((Button)e.getSource()).setLabel("weixin"); //改变button上的文本!
               }
             });
        Button btn2=new Button("winsun");
        flowPanel.add(btn1);
        flowPanel.add(btn2);
      }
      public void setGridLayoutPanel()
      {
        gridPanel=new Panel();
        gridPanel.setLayout(new GridLayout(2,2));
        Button btn1=new Button("Button1");
        Button btn2=new Button("Button2");
        Button btn3=new Button("Button3");
        Button btn4=new Button("Button4");
        gridPanel.add(btn1);
        gridPanel.add(btn2);
        gridPanel.add(btn3);
        gridPanel.add(btn4);
      }
      public void setCardLayoutPanel()
      {
        final CardLayout cl=new CardLayout();
        cardPanel=new Panel();
        cardPanel.setLayout(cl);
        Button btn1=new Button("黑桃A");
        Button btn2=new Button("红桃K");
        ActionListener al=new ActionListener() //ActionListener是个接口,所以不能实例化,直接实现
            {
              public void actionPerformed(ActionEvent e)
              {
                cl.next(cardPanel);  //内部类访问cl变量,它必须定义为final的!
              }
            };

        btn1.addActionListener(al);  //注册监听器
        btn2.addActionListener(al);  //可以使用同一个

        cardPanel.add(btn1,"1");
        cardPanel.add(btn2,"2");
      }
      public static void main(String[] args) throws HeadlessException
      {
        YourFrame. yf = new YourFrame("http://www.mybole.com.cn");
        yf.show();
      }

    }

    4.在AWT中创建一个菜单需要创建:
    MenuBar-》Menu-》MenuItem
    SetMenubar()

    package lesson8;

    import java.awt.*;

    public class HisFrame
    {
      public static void main(String[] args)
      {
        Frame. f=new Frame("http://www.mybole.com.cn");
        f.setSize(600,400);
        f.setLocation(100,100);
        f.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
              System.exit(0);
            }
          });
        MenuBar mb=new MenuBar();
        Menu m1=new Menu("File");
        Menu m2=new Menu("Edit");
        MenuItem mi1=new MenuItem("New");
        MenuItem mi2=new MenuItem("Open");
        MenuItem mi3=new MenuItem("Save");
        MenuItem mi4=new MenuItem("Exit");
        MenuItem mi5=new MenuItem("Copy");
        MenuItem mi6=new MenuItem("Paste");
        m1.add(mi1);
        m1.add(mi2);
        m1.add(mi3);
        m1.add(mi4);
        m2.add(mi5);
        m2.add(mi6);
        mb.add(m1);
        mb.add(m2);
        f.setMenuBar(mb);
        f.show();
      }
    }

    a.实现功能打开并显示一个文件:
    MenuItem mi2=new MenuItem("Open");
        mi2.addActionListener(new ActionListener() {
              public void actionPerformed(ActionEvent e) {
                FileDialog fd=new FileDialog(f,"Weixin Open File Dialog",
                                             FileDialog.LOAD); //(父Frame,title,model)
                fd.show();
                String strFile=fd.getDirectory()+fd.getFile(); //路径+文件名
                if(strFile!=null)
                {
                  try {
                    FileInputStream fis=new FileInputStream(strFile);
                    byte[] buf=new byte[10*1024];
                    int len=fis.read(buf);
                    tf.append(new String(buf,0,len));
                    fis.close();
                  }
                  catch (Exception ex) {
                    ex.printStackTrace();
                  }
                }
              }
            });

    创建对话框用FileDialog类,需指定一个父Frame;

    显示文本的方法:
    1.TextField --- 可以指定初始文本,但是只能显示单行文本
    2.TextArea -- 多行文本

    b.退出
        MenuItem mi4=new MenuItem("Exit");
        mi4.addActionListener(new ActionListener() {
              public void actionPerformed(ActionEvent e) {
                System.exit(0);
              }
            });

    其它常用组件,看例子代码即可:
    Chois -- 下拉列表框
    Checkbox -- 复选框
    Label -- 标签

    5.Swing

    JAVA基础类:
    JFC(Java Foundation Classes):Java基础类,是关于GUI组件和服务的完整集合,主要包含5个API:AWT、Java2D、Accessibility、Drag & Drop、Swing。JFC提供了帮助开发人员设计复杂应用程序的一整套应用程序开发包。
    Java2D是一套图形API,它为Java应用程序提供了一套高级的有关二维(2D)图形图像处理的类。Java2D API扩展了java.awt和java.awt.image类,并提供了丰富的绘图风格,定义了复杂图形的机制和精心调节绘制过程的方法和类。这些API使得独立于平台的图形应用程序的开发更加简便。
    Accessibility API提供了一套高级工具,用以辅助开发使用非传统输入和输出的应用程序。它提供了一个辅助的技术接口,如:屏幕阅读器,屏幕放大器,听觉文本阅读器(语音处理)等等。
    Drag & Drop技术提供了Java和本地应用程序之间的互操作性,用来在Java应用程序和不支持Java技术的应用程序之间交换数据。
          JFC模块的重点在Swing。Swing用来进行基于窗口的应用程序开发,它提供了一套丰富的组件和工作框架,以指定GUI如何独立于平台地展现其视觉效果。

    Swing的所有元素都是基于JComponent的; -- 由它派生!
    包javax.swing
    所有swing元素都是J开头:JButton,JCheckBox

    6.可以用图形界面生成器生成一个框架;(Swing)
    用Application向导生成appli并在图形界面手动设计界面

    AWT.event.*;  事件
    MouseEvent类的方法:
    isPopupTrigger()是否是弹出事件的触发事件;
    getComponent()获取事件发生的父组件;
    getX(),getY() 获取鼠标当前位置;

    a.鼠标右键弹出菜单:
    void contentPane_mouseReleased(MouseEvent e) //鼠标键松开事件
    {
        if(e.isPopupTrigger()) //判断是否为右键;
        {
          jPopupMenu1.show(e.getComponent(), e.getX(), e.getY());
        }
    }

    b.右键菜单show增加事件响应:
    void jMenuItem3_actionPerformed(ActionEvent e) //弹出消息框
    {
        JOptionPane.showMessageDialog(null,"show"); //Swing类方法 -- JOptionPane.showMessageDialog
    }

    static void showMessageDialog(Component parentComponent, Object message)
              Brings up an information-message dialog titled "Message".


    图形界面的编程不是java的强项;而且运行速度较慢!

    awt和swing用法不用强记,会用就
    5行,使用时利用帮助文档和相关书籍现学现卖就可以了!
  • 孙鑫JAVA教学视频学习笔记(4-6)

    2009-02-26 22:23:06

    四.(上)常用包,引用类型,克隆

    基本内容:
    Java的常用包
    “==”和“equals”的用法
    基本数据类型与引用类型
    String和StringBuffer
    对象的克隆(clone)
    数组的相关操作
    封装类
    Runtime类与Process类
    Class类
    设计模式


    1。JAVA的常用包:

    java.applet:包含一些用于创建Java小应用程序的类。
    java.awt:包含一些用于编写与平台无关的图形界面(GUI)应用程序的类。
    java.io:包含一些用作输入输出(I/O)处理的类。
    java.lang:包含一些Java语言的基本类与核心类,如String、Math、Integer、System和Runtime,提供常用的功能,这个包中的所有类是被隐式导入的。
    java.net:包含用于建立网络连接的类,与java.io同时使用完成与网络有关的读写。
    java.util:包含一些实用工具类和数据结构类。

    2.== 和equals的区别
    在Java中,boolean、byte、short、int、long、char、float、double这八种是基本数据类型,其余的都是引用类型。  --- String 和数组都是引用类型!
    “==”是比较两个变量的值是否相等,“equals”是比较两个对象变量所代表的对象的内容是否相等。

    string是引用类型,所以==比的是变量中 所存的地址是否相同;

    String str1 = new String("abc");
    String str2 = new String("abc");


     
    str1 == str2  // false
    str1.equals(str2)   //true
    当我们声明一个引用类型变量时,系统只为该变量分配了引用空间,并未创建
    一个具体的对象;当用new为对象分配空间后,将对象的引用赋值给引用变量。

    String str=“abc”;
        int i=3;
        float f=4.5f;
        char ch='a';
        boolean b=true;
        System.out.println(str + i + f + ch + b);
    针对String的“+”和“+=”,是Java中唯一被重载的操作符;在Java中,不允许程序员重载操作符。
    String类对象一个常量对象。
        String str=“abc”;
        str=“def”;
    在处理大量字符串的程序中,我们通常用StringBuffer来替代String。


    3.StringBuffer

    String是个常量,the value can not be changed.    
    StringBuffer -- 可以对同一块内存进行操作,可以被修改,不会造成内存浪费!

        int i=3;
            float f=1.5f;
            char ch='f';
            boolean b=false;
            System.out.println(str1+i+f+ch+b);
            StringBuffer sb=new StringBuffer();
            sb.append(str1).append(i).append(f).append(ch).append(b);
            //System.out.println(sb.toString());
            System.out.println(sb);   //在打印语句中系统会自动调用toString()转换成string

    StringBuffer()默认容量是16 characters,超过了之后系统会自动增加;
    StringBuffer(int n)
    StringBuffer.Delete(4,8); //删除部分串,位置:4<=substr<8, 所以删除4,5,6,7 四个位置的字符;

    4.数组
    int[] num=new int[3];
            for(int i=0;i<num.length;i++)
            {
                System.out.println(num[i]);
            }
    num=null;  //使引用失效,变成垃圾内存,让jvm回收;

    类对象数组,二层引用的关系;初始值为三个null;
    /*Student[] students; //声明数组
            students=new Student[3];  //三个对象
            for(int i=0;i<students.length;i++)
            {
                System.out.println(students[i]);
            }*/
            Student s1=new Student("zhangsan",18,p);
            Student s2=(Student)s1.clone();
            /*s2.name="lisi";
            s2.age=20;*/
            s2.p.name="lisi";
            s2.p.age=30;
            //System.out.println("name="+s1.name+","+"age="+s1.age);
            System.out.println("name="+s1.p.name+","+"age="+s1.p.age);
        }
    }
     

    5.main 函数
    main由JVM调用,所以是public的;调用它的时候不用产生任何对象,所以是static的;
    JVM也无需返回值;
    参数是个String[]类型的;
    args 是用来接收命令行参数的; 如 java StringTest weixin mybole

    6.函数调用:
    不用第三个变量,交换两个变量值:
    public static void change(int x,int y)
        {
            x=x+y;
            y=x-y;
            x=x-y;
        }   //调用后并没有改变;
    在Java中,传参时,都是以传值的方式进行。
    对于基本数据类型,传递的是数据的拷贝;对于引用类型,传递的引用的拷贝。
    所以数组和对象作为参数进行change操作能够成功改变实际内容!

    Point pt(3,4); //定义对象
    System.out.println(pt); //直接打印对象(会调用toString方法);打印出类名+@+一个哈西值;
    所以想直接打印pt对象,可以重新定义Object的toString方法,
    class Point
    {
        int x,y;
        public String toString()
        {
            return "x="+x+","+"y="+y;
        }
    }
    再使用System.out.println(pt);的时候就可以直接打印出对象成员的内容了!  --- 好方法!

    7.对象的克隆(clone)
    使用方法时如果不想改变引用对象本身时,可以用clone
    为了获取对象的一份拷贝,我们可以利用Object类的clone()方法。
    在派生类中覆盖基类的clone()方法,并声明为public。
    在派生类的clone()方法中,调用super.clone()。
    在派生类中实现Cloneable接口。
    Cloneable --- 表示接口,没有抽象方法,仅仅是为了告诉该对象可以克隆了。

    class Student implements Cloneable  //空实现该接口,代表该对象可以克隆;
    {
        String name;
        int age;
        public Object clone()
        {
            Object o=null;
            try
            {
                o=super.clone();
            }
            catch(CloneNotSupportedException e)   //会抛出异常
            {
                System.out.println(e.toString());
            }
            return o;
        }
    }  //浅克隆   ---- 无成员对象! 只进行值拷贝!

    但是,当克隆时候,只copy对象成员的值,但是对引用成员(如成员对象),它只copy一个引用;所以对clone成员修改的时候也会将源对象的成员对象内容修改了!!

    String对象是一个常量对象,所以进行s2.name="lisi"时,是先new了一个“lisi”然后赋的值;

    深克隆:

    为什么我们在派生类中覆盖Object的clone()方法时,一定要调用super.clone()呢?在运行时刻,Object中的clone()识别出你要复制的是哪一个对象,然后为此对象分配空间,并进行对象的复制,将原始对象的内容一一复制到新对象的存储空间中。
    Student 成员对像有个professor
    class Professor implements Cloneable
    {
        String name;
        int age;
        Professor(String name,int age)
        {
            this.name=name;
            this.age=age;
        }
        public Object clone()
        {
            Object o=null;
            try
            {
                o=super.clone();
            }
            catch(CloneNotSupportedException e)
            {
                System.out.println(e.toString());
            }
            return o;
        }
    }
    class Student implements Cloneable
    {
        String name;
        int age;
        Professor p;
        Student(String name,int age,Professor p)
        {
            this.name=name;
            this.age=age;
            this.p=p;
        }
        public Object clone()
        {
            //Object o=null;
            Student o=null;
            try
            {
                o=(Student)super.clone();
            }
            catch(CloneNotSupportedException e)
            {
                System.out.println(e.toString());
            }
            o.p=(Professor)p.clone();
            return o;
        }
    }



    四.(下)数组Runtime类与Process类


    1. 数组的相关操作
    在Java中,所有的数组都有一个缺省的属性length,用于获取数组中元素的个数。
    数组的复制:System.arraycopy()。
    数组的排序:Arrays.sort()。  --- java.util包之中!
    在已排序的数组中查找某个元素:Arrays.binarySearch()。

    import java.util.Arrays;

    length是一个属性,不是方法;不要加()!!

    arr1.length
    对象数组的初始化方法:
    Point[] pts1 = new Point[]{new Point(1,1),new Point(2,2), new Point (3,3)};
    Point[] pts2 = new Point[3];

    arraycopy对与对象数组的copy实际上是一个引用copy,数组里面存的是对象的地址,所以对第二个数组对象进行修改操作时还是会改变第一个数组的元素(操作的是同一组对象)!

    b.排序
    int num[] = new int[]{3,1,2};
    Arrays.sort(num);  //直接调用,直接传参数;无返回值

    c.查找 (返回所查找元素的位置)
    int index = Array.binarySearch(num,3);

    d.对象的排序
    return num>s.num ? 1 : (num==s.num ? 0 : -1); //问号表达式,大于返回1,=返0,<返-1;
    对于Arrays.sort()对类对象数组的排序,该类必须实现Comparable接口,
    在类中重写compareTo()方法,实现对象之间的比较,如:
    class Student implements Comparable
    {
        int num;
        String name;
        Student(int num,String name)
        {
            this.num=num;
            this.name=name;
        }
        public String toString()  //用来打印对象
        {
            return "number="+num+","+"name="+name;
        }
        public int compareTo(Object o)   //接口函数的实现
        {
            Student s=(Student)o;   //转换成该对象
            return num>s.num ? 1 : (num==s.num ? 0 : -1);  //!!!
           
        }
    }
    方法2: 当序号相同时再按名字排序:
    public int compareTo(Object o)
        {
            Student s=(Student)o;
            //return num>s.num ? 1 : (num==s.num ? 0 : -1);
            int result=num>s.num ? 1 : (num==s.num ? 0 : -1);
            if(0==result)  //序号相同,则比较name;String类已经实现了compareTo
            {
                result=name.compareTo(s.name);
            }
            return result;
        }

    e.数组对象的搜索:
        int index=Arrays.binarySearch(ss,new Student(2,"lisi"));
            System.out.println("index="+index);
            System.out.println(ss[index]);

    2.封装类:
    调用函数时,参数是引用类型,但我们要传递的又是基本类型;
    java.lang中提供了八种基本类型的引用类型--封装类!
    基本数据类型    封装类
    boolean    Boolean
    byte    Byte
    short    Short
    int    Integer
    long    Long
    char    Character
    float    Float
    double    Double

    class Test
    {
        public static void main(String[] args)
        {
            int i=3;
            Integer in=new Integer(i);  //取得封装类
            int j=in.intValue();         //取回整形值
            System.out.println("j="+j);
            String str=in.toString();   //Integer.toString()可以将Integer转换为String
            System.out.println("str="+str);
           
            String str2="134";
            System.out.println(Integer.valueOf(str2)); //Integer.valusOf()将String转换成Integer
        }
    }

    parseInt(String s) -- 转换为基本数据类型int
    toString 也支持基本数据类型int!
    其它基本类型也都有类似的;

    所有的封装类都是只读类!

    3.Class类  -- java.lang
    在Java中,每个class都有一个相应的Class对象。也就是说,当我们编写一个类,编译完成后,在生成的.class文件中,就会产生一个Class对象,用于表示这个类的类型信息。
    获取Class实例的三种方式:
        (1)利用对象调用getClass()方法获取该对象的Class实例; -- Object的方法!
            Point pt=new Point();
            Class c1=pt.getClass();
            System.out.println(c1.getName()); //getName得到一个String
        (2)使用Class类的静态方法forName(),用类的名字获取一个Class实例;
                Class c2=Class.forName("Point");  //会抛出异常
                System.out.println(c2.getName());
        (3)运用.class的方式来获取Class实例,对于基本数据类型的封装类,还可以采用.TYPE来获取相对应的基本数据类型的Class实例。
            Class c3=Point.class;
            System.out.println(c3.getName());
    基本数据类型:
            Class c4=int.class;
            System.out.println(c4.getName());
    封装类:       
            Class c5=Integer.TYPE;
            System.out.println(c5.getName());

    在运行期间,如果我们要产生某个类的对象,Java虚拟机(JVM)会检查该类型的Class对象是否已被加载。如果没有被加载,JVM会根据类的名称找到.class文件并加载它。一旦某个类型的Class对象已被加载到内存,就可以用它来产生该类型的所有对象。
    newInstance() 调用类中缺省的构造方法。 -- 可以用来在不知道一个类的名字的时候创建它的实例!
    if(args.length!=1)
            {
                return;
            }
            try
            {
                Class c=Class.forName(args[0]);
                Point pt=(Point)c.newInstance();
                pt.output();
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }

    4.反射API
    用来表示或反射类、接口和对象在当前的JVM上;

    import java.lang.*; //不能导入lang的子包,必须增加:
    import java.lang.reflect.*;

    java.lang.class.isPrimitive()用来判断是否是基本数据类型!
    反射API 动态地创建了类的实例,动态地使用类的方法!

    尽量不要用反射API当能够以别的工具实现时。

    调试器,类的浏览器,图形界面的类的构建器的时候可能会用到反射API;

    5.runtime类
    每一个Java程序都有一个Runtime类的单一实例。
    通过Runtime.getRuntime()获取Runtime类的实例。 //无构造方法!
        Runtime类是使用单例模式的一个例子。

    成为了一个程序和JVM的接口,提供了应用程序和环境之间的接口!

    public static void main(String[] args)
        {
            Runtime rt=Runtime.getRuntime(); //得到一个runtime实例
            System.out.println(rt.freeMemory());  //查看当前空闲内存和总内存!
            System.out.println(rt.totalMemory());
    }
    exec()方法可以来执行外部程序;会throw exception
            try
            {
                rt.exec("notepad"); //成为当前进程的子进程
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }
    也可以用来编译源文件:
    rt.exec("javac ArrayTest.java");
    为了更好看到执行过程: 定义一个Process类:
    Process类: 抽象类
                Process p=rt.exec("javac ArrayTest.java"); //获取了这个子进程;
                InputStream is=p.getInputStream();
                int data;
                while((data=is.read())!=-1)
                {
                    System.out.print((char)data);
                }
        Runtime类是使用单例模式的一个例子。

    6.设计模式
    在我们进行程序设计时,逐渐形成了一些典型问题和问题的解决方案,这就是软件模式。
    每一个模式描述了一个在我们程序设计中经常发生的问题,以及该问题的解决方案。
    当我们碰到模式所描述的问题,就可以直接用相应的解决方法去解决这个问题,这就是设计模式。

    对于JAVA语言的学习:
    1.语法规则和它本身的优越性(相对于其它语言)
    2.解决方法时的一些前人的解法的经验

    a.单例模式
    (1)一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类。
       (2)单例类的一个最重要的特点是类的构造方法是私有的,从而避免了外部利用构造方法直接创建多个实例。
    实现:
    class Singleton
    {
        private static final Singleton st=new Singleton();
        private Singleton(){}   //必须自己写了私有构造,才是单例模式
        public static Singleton getInstance()
        {
            return st;
        }
    }
    主要用于解决什么问题呢?
    只需要一个实例时...

    设计模式推荐书:《java与模式》阎宏 电子工业


    五. 程序,进程,线程

    1.程序,进程,线程
    程序是计算机指令的集合,它以文件的形式存储在磁盘上。
    进程:是一个程序在其自身的地址空间中的一次执行活动。
    进程是资源申请、调度和独立运行的单位,因此,它使用系统中的运行资源;而程序不能申请系统资源,不能被系统调度,也不能作为独立运行的单位,因此,它不占用系统的运行资源。
    线程:是进程中的一个单一的连续控制流程。一个进程可以拥有多个线程。
       线程又称为轻量级进程,它和进程一样拥有独立的执行控制,由操作系统负责调度,区别在于线程没有独立的存储空间,而是和所属进程中的其它线程共享一个存储空间,这使得线程间的通信远较进程简单。

    操作系统来控制多线程的时间片轮询执行;
    当程序被移植到多CPU的平台下,就能做到真正的多线程并发执行!

    当需要完成多任务的程序时,采用多线程要比多进程高效的多!

    2.JAVA对多线程的支持
    Java在语言级提供了对多线程程序设计的支持。
    实现多线程程序的两种方式:
        (1)从Thread类继承;
        (2)实现Runnable接口。

    java.lang.thread
    main方法默认是单线程非后台的;

    Thread.currentThread().getName(); //获取当前线程的名字

    There are two ways to create a new thread of execution. One is to declare a class to be a subclass of Thread. This subclass should override the run() method of class Thread.

    启动线程start();

    run方法可以理解为线程的入口函数;

    class MultiThread
    {
        public static void main(String[] args)
        {
            MyThread mt = new MyThread();
            mt.start();
            System.out.println("main:"+Thread.currentThread().getName();
           
        }
    }

    class MyThread extends Thread
    {
        public void run()
        {
            System.out.println(getName());
        }
    }

    setDaemon() //设为后台进程,在调用之前使用
    yield() //暂停当前线程,让其它线程执行(一个时间片)
    setPriority() //设置线程的优先级; 常量MAX/MIN/NORM_PRIORITY:  10/1/5
    getPriority()

    class MultiThread
    {
        public static void main(String[] args)
        {
            MyThread mt=new MyThread();
            //mt.setDaemon(true);  //设置为后台线程
            //mt.setPriority(Thread.MAX_PRIORITY);  //设为高优先级,也可以在它运行之后设置
            mt.start();
            int index=0;
            while(true)
            {
                if(index++==1000)
                    break;
                System.out.println("main:"+Thread.currentThread().getName());
            }
        }
    }

    class MyThread extends Thread
    {
        public void run()
        {
            while(true)
            {
                System.out.println(getName());
                //yield();   //让其它线程先执行  
            }
        }
    }

    3.Java对多线程的支持
    Java运行时系统实现了一个用于调度线程执行的线程调度器,用于确定某一时刻由哪一个线程在CPU上运行。
    在java技术中,线程通常是抢占式的而不需要时间片分配进程(分配给每个线程相等的CPU时间的进程)。抢占式调度模型就是许多线程处于可以运行状态(等待状态),但实际上只有一个线程在运行。该线程一直运行到它终止进入可运行状态(等待状态),或者另一个具有更高优先级的线程变成可运行状态。在后一种情况下,低优先级的线程被高优先级的线程抢占,高优先级的线程获得运行的机会。
    Java线程调度器支持不同优先级线程的抢先方式,但其本身不支持相同优先级线程的时间片轮换。
    Java运行时系统所在的操作系统(例如:Windows2000)支持时间片的轮换,则线程调度器就支持相同优先级线程的时间片轮换。

    4.线程的第二种实现方法就是实现runable接口;
    .runable接口只有一个方法就是run()
    class MultiThread
    {
        public static void main(String[] args)
        {
            MyThread mt = new MyThread();
            new Thread(mt).start();   //good way!!!
            int index=0;
            while(true)
            {
                System.out.println("main:"+Thread.currentThread().getName());
            }
        }
    }

    class MyThread implements Runnable
    {
        public void run()
        {
            while(true)
            {
                System.out.println(Thread.currentThread().getName());
            } //不是继承的Thread类,所以不能直接用getName()方法了
        }
    }

    什么时候用那个方法实现线程?
        --如果说我们并不需要修改Thread的除了run之外的其它方法时,最好用Runable接口的方法;
    两个好处:
    --不允许多继承,所以子类用Runable好
    --Runable接口可以共享一个变量;
    new Thread(mt).start();
    new Thread(mt).start();
    new Thread(mt).start();
    new Thread(mt).start();

    除了Runable外,也可以用内部类来实现(外部类是普通类):
    int index = 0;  // 成员变量;
    private class InnerThread extends Thread
    {
        public void run()
        {
            while(true)
                {
                System.out.println(Thread.currentThread            ().getName());
                }
        Thread getThread() //该类成员方法用来生成内部类
            {
            new InnerThread();
            }
        }
    }
    主函数中用
    来多线程共享一个对象变量:
    mt.getThread().start();
    mt.getThread().start();
    mt.getThread().start();
    mt.getThread().start(); //启动多个线程共享变量;

    5.线程的同步
    The code segments within a program that access the same object from separate, concurrent threads are called “critical sections”。
    同步的两种方式:同步块和同步方法
    每一个对象都有一个监视器,或者叫做锁。
    同步方法利用的是this所代表的对象的锁。
    每个class也有一个锁,是这个class所对应的Class对象的锁。

    a.同步块用synchronized实现:
    联网售票系统的实现:  --- 设定了同步保护块
    class Ticket
    {
        public static void main(String[] args)
        {
            SellThread st = new SellThread();       
            new Thread(st).start();   // Thread(Runnable target) Allocates a new Thread object.
            new Thread(st).start();
            new Thread(st).start();
            new Thread(st).start(); //不能new 4个对象,只能new 4个线程;
        }
    }

    class SellThread implements Runnable
    {
        int tickets = 100;
        Object bj = new Object();  //for synchronized use
        public void run()
        {
            while(true)
            {
                synchronized(obj) //进程进入后给obj加锁,退出后再解锁
                {
                    if(tickets>0)
                    {
                        //try{ Thread.sleep(10); } //让线程睡眠10毫秒,造成资源的重复读取;
                        //catch(Exception e){ e.printStackTrace(); }
                        System.out.println(Thread.currentThread().getName()+ " sell tickets:"+tickets); //获取当前线程
                        tickets--;
                    }
                }
            }
        }
    }
    b.同步方法:
    public synchronized void sell(){....}
    不需要object,是给this对象加锁!

    如果是静态的同步方法,则用的是class类的锁;

    6. 线程的死锁:
    哲学家进餐的问题
    线程1锁住了对象A的监视器,等待对象B的监视器,线程2锁住了对象B的监视器,等待对象A的监视器,就造成了死锁。
    Thread的suspend() 和 resume()方法容易造成死锁;

    7.生产者,消费者问题:
    wait、notify、notifyAll
    每一个对象除了有一个锁之外,还有一个等待队列(wait set),当一个对象刚创建的时候,它的对待队列是空的。
    我们应该在当前线程锁住对象的锁后,去调用该对象的wait方法。
    当调用对象的notify方法时,将从该对象的等待队列中删除一个任意选择的线程,这个线程将再次成为可运行的线程。
    当调用对象的notifyAll方法时,将从该对象的等待队列中删除所有等待的线程,这些线程将成为可运行的线程。
        wait和notify主要用于producer-consumer这种关系中。 -- 必须放在同步方法或者同步块之中!
    -- 此wait和notify方法必须调用的是同一个同步对象!
    class Test
    {
        public static void main(String[] args)
        {
            Queue q = new Queue();
            Producer p= new Producer(q);
            Consumer c=new Consumer(q);
            p.start();
            c.start();
        }
    }
    class Producer extends Thread
    {
        Queue q;
        Producer(Queue q)
        {
            this.q = q;
        }
        public void run()
        {
            for (int i=0;i<10;i++)
            {
                q.put(i);
                System.out.println("Producer put "+i);
            }
        }
    }

    class Consumer extends Thread
    {
        Queue q;
        Consumer(Queue q)
        {
            this.q=q;
        }
        public void run()
        {
            while(true)
            {
                System.out.println("Consumer get "+q.get());
            }
        }
    }

    class Queue
    {
        int value;
        boolean bFull=false;
        public synchronized void put (int i)
        {
            if(!bFull)
            {
                value = i;
                bFull = true;
                notify();  //通知消费者
            }
            try{wait();}  //等待消费
            catch(Exception e){e.printStackTrace();}
        }
        public synchronized int get()
        {
            if(!bFull)
            {
                try{wait();}  //等待生产者
                catch(Exception e){e.printStackTrace();}
            }
            bFull=false;
            notify();       //通知生产
            return value;
        }
    }

    线程的状态:
     

    调用了wait()的线程必须由 notify()来唤醒! 或者由interrupt()方法来终止!

    线程的终止:
    a。设置一个flag变量;
    b。结合interrupt()方法;




    六.集合类;栈、对列、链表

    1.集合类
    集合框架 --- JDK doc:guide
    Introduction of collections framework:
    The Java 2 platform. includes a collections framework. A collection is an object that represents a group of objects (such as the classic Vector class). A collections framework is a unified architecture for representing and manipulating collections, allowing them to be manipulated independently of the details of their representation.

     

    a.集合框架中的接口:
    Collection:集合层次中的根接口,JDK没有提供这个接口直接的实现类。
    Set:不能包含重复的元素。SortedSet是一个按照升序排列元素的Set。
    List:是一个有序的集合,可以包含重复的元素。提供了按索引访问的方式。
    Map:包含了key-value对。Map不能包含重复的key。SortedMap是一个按照升序排列key的Map。


    以上几个接口都在util包中:JAVA.util.

     
    篮筐为接口,白框为实现类;

    2.ArrayList  -- 后台是用对象数组实现的!
    jdk/src.zip --解压缩后看源代码 java/util/ArrayList.java
    ArrayList:我们可以将其看作是能够自动增长容量的数组。-- 与对象数组的区别!

    成员方法有: add(),size()--元素数目,get();
    例子:
    import java.util.*;
    class ArrayListTest
    {
        public static void main(String[] args)
        {
            ArrayList al=new ArrayList();
            al.add("winsun");  //成员方法add增加成员;
            al.add("weixin");
            al.add("mybole");
            for(int i=0;i<al.size();i++)  //size()相当于数组的length属性;
            {
                System.out.println(al.get(i));
            }
            System.out.println(al);  //自动调用toString方法;
        }
    }

    利用ArrayList的toArray()返回一个对象数组。 --- 所以它们二者是不同的!
    例子:
    import java.util.*;
    class ArrayListTest
    {
        public static void main(String[] args)
        {
            ArrayList al=new ArrayList();
            al.add(new Point(1,1));
            al.add(new Point(2,2));
            al.add(new Point(3,3));
            System.out.println(al);  //自动调用toString方法;
            Object[] bjs=al.toArray();  //toArray()返回一个对象数组
            for(int i=0;i<objs.length;i++)
            {
                System.out.println(objs[i]);
            }
        }
    }
    class Point
    {
        int x,y;
        Point(int x,int y)
        {
            this.x=x;
            this.y=y;
        }
        public String toString()
        {
            return "x= "+x+","+" y= "+y;
        }
    }


    Arrays.asList()返回一个列表。
            List l=Arrays.asList(objs); //列表大小是确定了的;
            System.out.println(l);

    3.迭代器
    迭代器(Iterator) 给我们提供了一种通用的方式来访问集合中的元素。  Collection.iterator()
    有三个方法:hasNext,next,remove();
     
    next()返回一个值,remove删除上一个返回的值;
        Iterator it=al.iterator();
        while(it.hasNext())
            {System.out.println(it.next());}

    remove()使用方法:
            Iterator it=al.iterator();
            it.next();  //必须先返回一个再删除
            it.remove();
                    while(it.hasNext())
                System.out.println(it.next());

    集合框架中的实现类支持迭代器的所有方法;
    但是asList返回的列表没有实现迭代器的remove方法!

    迭代器的作用:
    1.有些对象没有实现get方法;
    2.提供了一个统一的操作方法;

    定义统一函数:
    public static void printElements(Collection c) //参数类型是Collection,所有它的子类、实现类都可以用此函数;
        {
            Iterator it = c.iterator();  //!! 用方法iterator()生成迭代器对象
            while(it.hasNext())
            {
                System.out.println(it.next())
            }
        }
    调用的时候可以传递一个ArrayList对象;
    printElements(al);       或者其它集合对象;

    4.Collections 类; 不是Collection接口
    它提供了一系列有用的静态方法,如排序!  所以可以直接拿来用,不用定义对象!
    排序:Collections.sort()
    (1)自然排寻(natural ordering );
    (2)实现比较器(Comparator)接口。
    取最大和最小的元素:Collections.max()、Collections.min()。
    在已排序的List中搜索指定的元素:Collectons.binarySearch()。

    排序必须实现Comparable接口,该接口中的函数为:
    int compareTo(object o); 返回1,0, -1

    使用Collection类排序方法对数组列表排序并使用迭代器统一输出的例子如下:
    import java.util.*;
    class Test
    {
        public static void printElements(Collection c)
        {
            Iterator it = c.iterator();  //!! 用方法iterator()生成迭代器对象
            while(it.hasNext())
            {
                System.out.println(it.next());
            }
        }
        public static void main(String[] args)
        {
            Student s1=new Student(2,"zhangsan");
            Student s2=new Student(4,"lisi");
            Student s3=new Student(1,"wangwu");
            Student s4=new Student(3,"zhaoliu");
            ArrayList al = new ArrayList();
            al.add(s1);
            al.add(s2);
            al.add(s3);
            al.add(s4);
            Collections.sort(al);  //静态类方法直接调用!
            printElements(al);   //形参为Collection;自动调用类的toString
            }
    }

    class Student implements Comparable
    {
        int num;
        String name;
        Student(int i,String name)
        {
            num=i;
            this.name=name;
        }
        public int compareTo(Object o) //实现接口中的public函数,需加public
        {
            Student st=(Student) o;
            return num>st.num ? 1 :(num==st.num ? 0 : -1);
        }
        public String toString()
        {
            return num+":"+name;
        }
    }

    5.比较器排序
    在对列表排序的时候可以传递一个比较器comparator;
    sort(List ls, Comparator cp)

    比较器总是和一个类相关;
    比较器接口有两个方法:
    compare()
    equals()
     
    所以也可以用内部类实现:StudentComparator
    static class StudentComparator implements Comparator
        {
            public int compare(Object o1,Object o2)
            {
                Student s1=(Student)o1;
                Student s1=(Student)o1;
                return s1.num>s2.num ? 1 : (s1.num==s2.num ? 0 : -1);
            }
        }

    String类型已经实现了compareTo方法:
    result = s1.name.comareTo(s2.name);  //0,1,-1;

    6.Collections类的反序排序 -- static Comparator reverseOrder()
    返回的就是比较器对象;
    Collections.sort(al, Collections.reverseOrder());
    排序:Collections.sort()
    (1)自然排寻(natural ordering );
    (2)实现比较器(Comparator)接口。
    取最大和最小的元素:Collections.max()、Collections.min()。
      在已排序的List中搜索指定的元素:Collectons.binarySearch()。

    7.LinkedList
    LinkedList是采用双向循环链表实现的。
    利用LinkedList实现栈(stack)、队列(queue)、双向队列(double-ended queue )。

    数据结构:
    一般将数据结构分为两大类:线性数据结构和非线性数据结构。线性数据结构有线性表、栈、队列、串、数组和文件;非线性数据结构有树和图。

    8.线性表
    线性表的逻辑结构是n个数据元素的有限序列:
            (a1, a2 ,a3,…an)
    n为线性表的长度(n≥0),n=0的表称为空表。
    数据元素呈线性关系。必存在唯一的称为“第一个”的数据元素;必存在唯一的称为“最后一个”的数据元素;除第一个元素外,每个元素都有且只有一个前驱元素; 除最后一个元素外,每个元素都有且只有一个后继元素。
    所有数据元素在同一个线性表中必须是相同的数据类型。
    线性表按其存储结构可分为顺序表和链表。用顺序存储结构存储的线性表称为顺序表;用链式存储结构存储的线性表称为链表。
        将线性表中的数据元素依次存放在某个存储区域中,所形成的表称为顺序表。一维数组就是用顺序方式存储的线性表。

    9.链表:单向链表,循环链表,双向循环链表
    链表中一个节点删除后,是让前一个节点指向了后一个节点,该节点就变成了一块垃圾内存.

    10.栈 -- 后进先出
    栈(Stack)也是一种特殊的线性表,是一种后进先出(LIFO)的结构。
    栈是限定仅在表尾进行插入和删除运算的线性表,表尾称为栈顶(top),表头称为栈底(bottom)。
    栈的物理存储可以用顺序存储结构,也可以用链式存储结构

    LinkedList有getfirst,getlast, addfirst,addlast等方法

    用LinkedList来实现栈:
    import java.util.*;
    class MyStack //一个LinkedList成员加出入栈方法等
    {
        private LinkedList ll = new LinkedList();
        public void push(Object o)  //形参元素用Object对象,实参可以任意对象
        {
            ll.addFirst(o);
        }
        public Object pop()
        {
            return ll.removeFirst();
        }
        public Object peek()  //查看栈定元素
        {
            return ll.getFirst();
        }
        public boolean empty() //栈是否空
        {
            return ll.isEmpty();  //Collection方法
        }
        public static void main(String[] args)
        {
            MyStack ms = new MyStack();
            ms.push("one");
            ms.push("two");
            ms.push("three");
            System.out.println(ms.pop());   //three
            System.out.println(ms.peek());  //two
            System.out.println(ms.pop());   //two
            System.out.println(ms.empty()); //false
        }
    }

    11.队列(Queue) -- 先进先出FIFO
    队列(Queue)是限定所有的插入只能在表的一端进行,而所有的删除都在表的另一端进行的线性表。
    表中允许插入的一端称为队尾(Rear),允许删除的一端称为队头(Front)。
    队列的操作是按先进先出(FIFO)的原则进行的。
    队列的物理存储可以用顺序存储结构,也可以用链式存储结构。
    用LinkedList可类似实现队列。

    12.ArrayList和LinkedList的比较
    ArrayList底层采用数组完成,而LinkedList则是以一般的双向链表(double-linked list)完成,其内每个对象除了数据本身外,还有两个 引用,分别指向前一个元素和后一个元素。
    如果我们经常在List的开始处增加元素,或者在List中进行插入和删除操作,我们应该使用LinkedList,否则的话,使用ArrayList将更加快速。
    如果在数组开头插入一个数据,则需要移动整个数组,删除操作也需要移动其后所有数据;
    所以,常插入和删除用LinkedList,常随机访问则用ArrayList!

    13.HashSet
    实现Set接口的hash table(哈希表),依靠HashMap来实现的。-- set中没有重复元素!
    我们应该为要存放到散列表的各个对象定义hashCode()和equals()。

    14.散列表(哈希表)
    散列表又称为哈希表。散列表算法的基本思想是:
      以结点的关键字为自变量,通过一定的函数关系(散列函数)计算出对应的函数值,以这个值作为该结点存储在散列表中的地址。
    当散列表中的元素存放太满,就必须进行再散列,将产生一个新的散列表,所有元素存放到新的散列表中,原先的散列表将被删除。在Java语言中,通过负载因子(load factor)来决定何时对散列表进行再散列。例如:如果负载因子是0.75,当散列表中已经有75%的位置已经放满,那么将进行再散列。
    负载因子越高(越接近1.0),内存的使用效率越高,元素的寻找时间越长。负载因子越低(越接近0.0),元素的寻找时间越短,内存浪费越多。
    HashSet类的缺省负载因子是0.75。

    HashSetTest.JAVA:
    import java.util.*;
    class HashSetTest
    {
        public static void main(String[] args)
        {
            HashSet hs = new HashSet();
            hs.add("one");     //add方法来增加元素
            hs.add("two");
            hs.add("three");
            hs.add("one");       //failed,实现了Set接口,所以不能有重复元素
            //HashSet没有get方法,所以用Iterator来实现
            Iterator it = hs.iterator();
            while(it.hasNext())
            {
                System.out.println(it.next()); //三个成员
            }
        }
    }

    散列地址是由Object.hashCode()方法实现的,而它是取对象的存储地址来计算的;所以当new了两个相同对象时,HashSet是把它们当作不同的值了;

    public int hashCode()  //equals也得同时自己定义
    {
        return num*name.hashCode(); //String实现了hashCode
    }

    15.TreeSet
    TreeSet是依靠TreeMap来实现的。
    TreeSet是一个有序集合,TreeSet中元素将按照升序排列,缺省是按照自然顺序进行排列,意味着TreeSet中元素要实现Comparable接口。
    我们可以在构造TreeSet对象时,传递实现了Comparator接口的比较器对象。

    Sample for TreeSet:
    import java.util.*;
    class TreeSetTest
    {
        public static void main(String[] args)
        {
            TreeSet ts = new TreeSet();
            ts.add("winsun");
            ts.add("weixin");
            ts.add("mybole");
           
            Iterator it = ts.iterator();
            while(it.hasNext())
            {
                System.out.println(it.next());
            }
        }
    } //如果成员是类对象,则该类必须实现了Comparable接口;

    HashSet和TreeSet的比较
    HashSet是基于Hash算法实现的,其性能通常都优于TreeSet。我们通常都应该使用HashSet,在我们需要排序的功能时,我们才使用TreeSet。

    16.HashMap
    .HashMap就是实现了Map接口的Hash表; key,value
    import java.util.*;
    class HashMapTest
    {
        public static void main(String[] args)
        {
            HashMap hm=new HashMap();
            hm.put("one","zhangsan");   //Key, value;
            hm.put("two","lisi");
            hm.put("three","wangwu");
           
            System.out.println(hm.get("one"));
            System.out.println(hm.get("two"));
            System.out.println(hm.get("three"));
        }
    }
    HashMap的方法
    keySet()  -- 返回键
    valueS(),-- 返回值
    entrySet() -- 返回键值对;

            Set keys=hm.keySet();
            System.out.println("Key:");
            printElements(keys);
           
            Collection values=hm.values();
            System.out.println("Value:");
            printElements(values);
           
            Set entry=hm.entrySet();
            printElements(entry);
    或者将entrySet()返回的对象(类型是Map.Entry)进行单独处理:
            Set entry=hm.entrySet();
            Iterator it=entry.iterator();
            while(it.hasNext())
            {
                Map.Entry me=(Map.Entry)it.next();
                System.out.println(me.getKey()+":"+me.getValue());
            }

    17. TreeMap
    TreeMap按照key进行排序。

    HashMap和TreeMap的比较
    和Set类似,HashMap的速度通常都比TreeMap快,只有在需要排序的功能的时候,才使用TreeMap。

    18.Java1.0/1.1的集合类
    Vector:用ArrayList代替Vector。-- 单线程总是用ArrayList,多线程时Vector稍微快一些,但使用要非常小心,可以用synchronizedList代替Vector;
    当需要用线程时候可以用Collections类提供的同步列表:
    synchronizedList(List<T> list)  //also for Map/Set
              Returns a synchronized (thread-safe) list backed by the specified list.


    Hashtable:用HashMap代替Hashtable。-- Hashtable是同步的,HashMap不是同步的(但可以使用synchronizedMap实现)
    只要不是多线程情况,就要始终使用HashMap来代替HashTable!

    Satck:用LinkedList代替Stack。-- 是从Vector继承而来的,继承了一个elementAt()方法从而有了按索引访问的权限 -- 与栈属性不符! 所以应该自己用LinkedList实现栈!

    Properties -- 它从HashTable继承而来,属性列表的键和值都是String类型的;
    列出系统属性:
    import java.util.*;
    class PropTest
    {
        public static void main(String[] args)
        {
            Properties pps = System.getProperties();
            pps.list(System.out);
        }
    }


    这个blog对缩进显示的不好,代码看起来费劲一些;另外笔记中一些截图也看不到了,大家凑合着看看吧,希望对大家有所帮助!       sonwaves








  • 孙鑫JAVA教学视频学习笔记(1-3) (应该是以前叫JAVA无难事吧...)

    2009-02-22 16:42:50

    。 JAVA基础

    javac 编译器  -》字节码         >javac hello.java
    java 解释器                     >java  Hello      (加载的是Hello类,是由javac产生的字节码类;必须大写头)

    所有java程序都是以类来执行的;

    .多个class可以编辑到一个.java文件中,但编译后会分别产生各个.class字节码文件;可以将多个.class打包成.jar文件.
    .如果在源文件中定义了一个public的class之后,此文件名必须为这个类名; -- 一个源文件中最多只能有一个Public的类

    . classpath 设置为 E:/JAVA,.   点代表当前路径
    语法:
    基本类型
    byte -128~+127

    boolean 取值: true/false 不能是0,1
    while(1)  不能编译
    int num[3] JAVA中不能这样定义
    必须:
    int num[];
    num = new int[3];
    或者
    int num[] = new int[3]

    建议的数组定义方式
    int[] num;
    定义时可以分配元素:
    int[] num = {1,2,3}

    int[] num = new int[] {1,2,3}
    但:
    int[] num;
    num = {1,2,3}; error

    最基本的方式:
    int num[];
    num = new int[3];
    num[1] = 11
    num[2] = 22
    num[3] = 33;

    二维数组的每行的列数可以不同;类似与c的指针数组
    int [][] num;
    num = new int[3][];
    num[0] = new int[5];
    num[1] = new int[3];
    num[2] = new int[2];

    int[][] num = {{1,2,3}, {4,5}, {6}}  // OK, 最大下标分别是2,1,0

    for (int i=0; i <= 10; i++)  //在java中这个i只在for循环内部有效; 不像C语言
    {
    }

     

     

    二. 面向对象基础

    1.构造方法和类同名,必须定义,如果没有则系统自动产生一个不带参数的构造函数如: point();
    .javap.exe --反编译工具;将字节码类反编译成源文件;
    用缺省构造函数初始化,则成员变量被初始化为默认值:
    数值型: 0
    boolean: false
    char     '\0'
    对象:   null

    2.重载 -- 构造函数的重载
      必要条件:参数类型和参数个数必须不同!

    void output (int x, int y)
    {
       x = x; //error;  this.x=x;
       y = y; //error;  this.y=y;
    }   this在成员函数中指向当前对象;

    无参构造函数可以调用带参数的构造函数:
    Point()
    {
    this(1,1)   //已定义 Point(int a, int b)
    }

    3. 静态成员函数 -- 类方法-- 从类名可直接调用; Point.output(); not pt1.out();
    static void output()
    {
    }
    静态成员函数只能使用静态成员变量:
    static int z;
    静态方法和静态变量是属于某一个类,而不属于类的对象。
    静态方法和静态变量的引用直接通过类名引用。 (也可用对象名)
    在静态方法中不能调用非静态的方法和引用非静态的成员变量。反之,则可以。
    可以用类的对象obj去调用静态的方法method(),如:obj.method()
    之所以main方法是static的;

    虽然静态方法和非静态的成员函数在内存中都只有一个copy,但它们还是不同的;

    4.常量
    JAVA中没有define语句;所以常量可以用final声明:
    final double PI = 3.1415926  (声明时必须赋初值)  -- 也可以将初始化放在构造函数中!
    使用关键字final定义常量,例如:
        final double PI=3.1415926;
    作为一种约定,在定义常量时,通常采用大写形式。
    final常量可以在声明的同时赋初值,也可以在构造函数中赋初值。
    为了节省内存,我们通常将常量声明为静态的(static)
    static final double PI = 3.14;

    5.继承 -- extends
    父类,超类,基类
    子类,派生类;
    java不允许多继承;

    6.覆盖
    子类函数覆盖父类函数;
    在子类中定义一个与父类同名、返回类型、参数类型均相同一个方法,称为方法的覆盖。
    覆盖发生在子类与父类之间。

    用super可以调用父类被覆盖的成员函数或者变量;
    super.eat();

    特殊变量super,提供了对父类的访问。
    可以使用super访问父类被子类隐藏的变量或覆盖的方法。
    每个子类构造方法的第一条语句,都是隐含地调用super(),如果父类没有这种形式的构造函数,那么在编译的时候就会报错.

    子类自动调用父类构造函数,然后执行自身构造函数; 父类必须有无参的构造;不然自动执行会出错;

    7.多态
    通过覆盖父类的方法来实现,在运行时根据传递的对象引用,来调用相应的方法。
    根据运行时传递参数的对象引用类型,来决定调用哪个方法;

    8.instanceof用法
    if(fh instanceof Animal)
      {
       System.out.println("fh is an animal's instance");
      }
      else
      {
       System.out.println("fh isn't animal's instance");
      }
    输出: fh is animal's instance; 子类对象可以是父类的一个实例;

     

    三(上).包,对象,方法,接口...

    包,类,对象,方法,接口,垃圾回收,异常处理;

    1.包 -- 包相当于类的限定名;

    java.lang.String

    package 名用小写;

    >javac test.java

    >java mybole/Test   //执行,并且必须创建mybole目录,将Test.class 放入目录;

    package语句必须是文件中的第一条语句。也就是说,在package语句之前,除了空白和注释之外不能有任何语句。
    如果不加package语句,则指定为缺省包或无名包。
    包对应着文件系统的目录层次结构。
    在package语句中,用“.”来指明包(目录)的层次。

    在当前目录下生成包
        javac -d . Test.java
    在指定目录下生成包
        javac -d E:\JavaLesson Test.java
    -d 还能对应的生成目录结构,如:
    >javac -d  . Test.java
    则自动生成目录cn/mybole;

    set classpath = %classpath%;D\cn\mybole;

    1.1 import
    引入包中的类。
        import java.io.File;
    引入整个包。
        import java.io.*;
    在同一包中的类可以互相引用,无需import语句。
    java.lang 是默认就导入的,可以直接用;
    如果没有导入一个类,也可以用:
    java.io.File f;        //指定包路径进行外部类的使用
    或者直接导入所有类: import java.io.*;

    类的访问说明符
     (1)public   -- 跨包引用必须加上public关键字;
     (2)default(不加访问说明符时) -- 只能包内被访问
     -- 在不同的包之间访问类的时候,只能访问到声明为public的类;

    类的其它修饰符
     (1)final  -- 表明它是最终的类;不能再派生出其它子类;
     (2)abstract  -- 抽象类

    方法的访问说明符
     (1)public
     (2)protected
     (3)default(不加访问说明符时)
     (4)private
    方法的其它修饰符
     (1)static
     (2)final
     (3)abstract
     (4)native
     (5)synchronized

    访问说明符:     public   protected   default   private
    同类               Y       Y         Y    Y    
    同包               Y       Y         Y    N     
    子类(不同包)     Y       Y         N    N    
    通用性(不同包)   Y       N         N    N    

    final:
    为了确保某个函数的行为在继承过程中保持不变,并且不能被覆盖(overridden),可以使用final方法。
    为了效率上的考虑,将方法声明为final,让编译器对此方法的调用进行优化。要注意的是:编译器会自行对final方法进行判断,并决定是否进行优化。通常在方法的体积很小,而我们确实不希望它被覆盖时,才将它声明为final。
    class中所有的private和static方法自然就是final。
    子类中不能覆盖父类的final方法;
    子类中定义新的private和static方法,虽然编译不报错,但是只是产生了新的函数,并没有覆盖父类的函数;

    抽象方法和抽象类:
    在类中没有方法体的方法,就是抽象方法。
    含有抽象方法的类,即为抽象类。 -- 如果类中声明了抽象方法,则类也必须声明为抽象的;
    如果一个子类没有实现抽象基类中所有的抽象方法,则子类也成为一个抽象类。
    我们可以将一个没有任何抽象方法的类声明为abstract,是为了避免由这个类产生任何的对象。
    构造方法、静态方法、私有方法、final方法不能被声明为抽象的方法。
    如果抽象类的子类没有实现父类的抽象方法,则它也是抽象的,必须加abstract;   可以用空实现 void fn(){}

    JNI - JAVA Native Interface
    tutorial...

    Linux 平台 .SO文件为: 动态链接库

    native方法:
    native方法是用户在Java中可以使用,但不能编写的方法。
    JNI(Java Native Interface),它允许Java虚拟机(JVM)内部运行的Java代码能够与用其它编程语言(如C、C++、汇编语言)编写的应用程序和库进行互操作。
    JNI最大的好处是它没有对底层Java虚拟机的实现施加任何限制,因此,Java虚拟机厂商可以在不影响虚拟机其它部分的情况下添加对JNI的支持。程序员只需编写一种版本的本地(Native)应用程序和库,就能够与所有支持JNI的Java虚拟机协同工作。
    JNI可以理解为Java和本地应用程序之间的中介。

    可以给类定义一个静态变量,在构造函数里让其自增来记录生成对象的数量;

    垃圾回收:
    垃圾回收器是一个低优先级的进程,在后台运行...当内存不足是才会调用;
    System.gc(); 强制执行垃圾回收器

    接口:
    接口中所有的方法都是public abstract。
    在接口中声明方法时,不能使用native、static 、final 、synchronized 、private 、protected等修饰符。
    和public类一样,public接口也必须定义在与接口同名的文件中。
    接口中可以有数据成员,这些成员默认都是public static final。
    接口通常定义为public的
    在类中实现接口的方法,必须也定义成public的;
    实现类中必须实现接口中所有的方法,才能实例对象;否则它还是抽象类;
    类中方法的形参可以用接口名;而实参调用的时候再使用实现类的对象来实例化;

    接口的成员变量默认就是静态的公用常量!
    访问方法有如下三种:
    System.out.println(Math.PI);  //接口名称调用
      System.out.println(Arithmetic.PI); //实现类名调用
      System.out.println(a.PI);  //实现类对象调用

    一个接口可以继承自另一个接口。
    Java中不允许类的多继承,但允许接口的多继承。
    interface Sofa extends Sittable, Lie
    在Java中,一个类可以实现多个接口。
    一个类在继承另外一个类的同时,可以实现多个接口。
    class Sofa extends Chair implements Lie, HealthCare
    {
     public void sleep(){};
     public void massage(){};
    }
    注意先后顺序!先继承再实现 --- extends放implements之前

     

    三.(下)内部类,异常处理

    1. 内部类
    在一个类中定义另外一个类,这个类就叫做内部类或内置类 (inner class) 。
    内部类可以让我们将逻辑上相关的一组类组织起来,并由外部类(outer class)来控制内部类的可见性。
    当我们建立一个inner class时,其对象就拥有了与外部类对象之间的一种关系,这是通过一个特殊的this reference形成的,使得内部类对象可以随意的访问外部类中所有的成员。
    内部类产生的字节码类名: outer$inner.class

    new 产生的对象都在堆内存中!

    Outer.this 来引用外部类成员变量; 外部类名.THIS
     
        class Inner
     {
      private int index=50;
      static void print()
      {
       int index=30;
       System.out.println(index);
       System.out.println(this.index);
       System.out.println(Outer.index);
       
      }
     }

    静态的main方法不能直接new一个非静态的inner对象!
    inner对象不能直接用new产生!

    必须先产生一个外部类才能有内部类!
    Outer.Inner inner = outer1.new Inner();// 必须与一个具体的外部类对象关联。

    内部类也可以放在外部类的一个方法内定义,但是它的有效范围也只有方法内部!
    内部类也可以放在一个条件语句中,或者语句块内;-- 无论它嵌套多深,它都能访问外部类的成员;

    方法中声明的内部类如果要访问方法的局部变量,则该局部变量必须声明为final的常量!

    当内部类声明为static后,生成时就无需先产生外部类的对象了,但也不能访问外部类对象的成员变量了; 只能访问静态的成员变量或者静态的成员方法了...

    非静态的内部类不能有静态的方法;

    在方法中定义的内部类,如果要访问方法中定义的本地变量或方法的参数,则变量必须被声明final。
    内部类可以声明为private或protected;还可以声明为abstract或final。
    内部类可以声明为static的,但此时就不能再使用外部类的非static的成员变量和非static的成员方法;
    非static的内部类中的成员不能声明为static的,只有在顶层类或static的内部类中才可声明static成员。

    我们为什么使用内部类
    1、在内部类(inner class)中,可以随意的访问外部类的成员,这可以让我们更好地组织管理我们的代码,增强代码的可读性。
    2、内部类可以用于创建适配器类,适配器类是用于实现接口的类。使用内部类来实现接口,可以更好地定位与接口关联的方法在代码中的位置。
    3、内部类的更多用法。

    匿名内置类:
    /*Animal getAnimal()
     {
      return new Animal()
      {
       public void eat()
       {
        System.out.println("animal eat");
       }
       public void sleep()
       {
        System.out.println("animal sleep");
       }
      };            //在;前加入实现

    其它用法: --- 接口中的方法和基类方法同名,但意义不同:
    interface Machine
    {
     void run();
    }

    class Person
    {
     void run()
     {
      System.out.println("run");
     }
    }

    class Robot extends Person
    {
     private class MachineHeart implements Machine     //用来实现接口中的run方法
     {
      public void run()
      {
       System.out.println("heart run");
      }
     }
     Machine getMachine()  //相当于也继承了Machine类
     {
      return new MachineHeart();
     }
    }

    class Test
    {
     public static void main(String[] args)
     {
      Robot robot=new Robot();
      Machine m=robot.getMachine();  //返回一个实现类对象
      m.run();
      robot.run();
     }
    }

    JAVA中只能单继承,可以用接口和内部类的方法来解决C++中多继承实现的问题!

    C类想继承A类和B类,可以继承A类然后定义一个getB()方法来返回一个匿名内部类对象(在这个getB()之后直接实现抽象类B;) 则运行时就可以用C类对象来实现A,B类对象作为形参的方法了。 例子如下:
    class A
    {
     void fn1()
     {
     }
    }

    abstract class B
    {
     abstract void fn2();
    }

    class C extends A
    {
     B getB()
     {
      return new B()
      {
       public void fn2()
       {
       }
      };
     }
    }

    class Test
    {
     static void method1(A a)
     {
      a.fn1();
     }
     static void method2(B b)
     {
      b.fn2();
     }
     public static void main(String[] args)
     {
      C c=new C();
      method1(c);
      method2(c.getB());
     }
    }

    2. 异常处理

    程序运行是遇到的错误;
    通过异常类来实现的;
    所有异常都有个基类:exception

    异常类的层次结构:
     


    打开一个不存在的文件、网络连接中断、数组下标越界、正在加载的类文件丢失等都会引发异常。
    Java中的异常类定义了程序中遇到的轻微的错误条件。
       Java中的错误类定义了程序中不能恢复的严重错误条件。如内存溢出、类文件格式错误等。这一类错误由Java运行系统处理,不需要我们去处理。

    Java程序在执行过程中如出现异常,会自动生成一个异常类对象,该异常对象将被提交给Java运行时系统,这个过程称为抛出(throw)异常。
    当Java运行时系统接收到异常对象时,会寻找能处理这一异常的代码并把当前异常对象交给其处理,这一过程称为捕获(catch)异常。
    如果Java运行时系统找不到可以捕获异常的方法,则运行时系统将终止,相应的Java程序也将退出。
    try/catch/finally语句。

    try
      {
       return excep.division(5,0);
      }
      catch(Exception e) //四种方法
      {
       System.out.println(“不能被0除”);    //用户定义异常信息
       System.out.println(e.getMessage() );  //直接返回该exception的错误信息
       System.out.println(e.toString());   //Returns a short description of this throwable.
       e.printStackTrace();  //打印Exception。实际上如果不处理,JVM会直接调用此函数(然后终止);
      }

    catch 语句中一定要填入处理语句,如果留空{},则一旦发生异常,将无任何输出!

    捕获异常可以在方法调用处,也可以在方法声明处!

    如果方法声明处抛出异常,则报错在调用处! 如:public int fn1(int a,int b) throws Exception {}

    如果在运行主函数后也加入 throws Exception, 则异常将交给JVM来处理,最省事的做法!!

    最好还是自己处理异常,打印出详细错误或者进行正确处理!

    catch语句可以写多个,如:
    catch(ArithmeticException e) {....}
    catch(Exception e) {....}    //但必须将具体异常写之前,通用的这个写后边!

    runtimeException之外的异常都要求捕获,或者抛出;

    声明中抛出异常用 throws
    而抛出语句用 throw
    catch(ArithmeticException e)
     {
      e.printStackTrace();
      throw e;
      //return 0;
     }

    用户也可以自己定义额外的异常,如:除数小于零。
    class DivisorIsMinusException extends Exception
    {
     DivisorIsMinusException(String str)
     {
      super(str);
     }
    }

    finally:
    try{}catch{}finally{} ----无论是否产生异常,finally语句都要执行,一般用来释放资源(网络,数据库连接等)。即便try或catch语句最后return了;
    当程序终止运行后:System.exit(-1),finally就不执行了!


    对于RuntimeException,通常不需要我们去捕获,这类异常由Java运行系统自动抛出并自动处理。
    如果父类中的方法抛出多个异常,则子类中的覆盖方法要么抛出相同的异常,要么抛出异常的子类,但不能抛出新的异常(注:构造方法除外--构造方法不被覆盖)。
    我们可以在方法声明时,声明一个不会抛出的异常,Java编译器就会强迫方法的使用者对异常进行处理。这种方式通常应用于abstract base class和interface中。

    JAVA 编程规范:
    package的命名
    package 的名字由全部小写的字母组成,例如:cn.mybole。
    class和interface的命名
    class和interface的名字由大写字母开头而其他字母都小写的单词组成,例如:Person,RuntimeException。
    class变量的命名
    变量的名字用一个小写字母开头,后面的单词用大写字母开头,例如:index,currentImage。
    class方法的命名
       方法的名字用一个小写字母开头,后面的单词用大写字母开头,例如:run(),getBalance()。
    static final变量的命名
    static final变量的名字所有字母都大写,并且能表示完整含义。例如:PI,PASSWORD。
    参数的命名
    参数的名字和变量的命名规范一致。
    数组的命名
    。   数组应该总是用这样的方式来命名:byte[] buffer。

     

     

     

     

     

     

     

     


     

  • 字符集编码问题的两篇好文

    2007-11-18 18:28:47

    unicode,ansi,utf-8,unicode big endian这些编码有什么区别

    来源 http://www.51testing.com/?121965

    快下班时,爱问问题的小朋友Nico又问了一个问题:
    "sqlserver里面有char和nchar,那个n据说是指unicode的数据,这个是什么意思。"
    并不是所有简单的问题都很容易回答,就像这个问题一样。于是我答应专门写一篇BLOG来从头讲讲编码的故事。那么就让我们找个草堆坐下,先抽口烟,看看夜晚天空上的银河,然后想一想要从哪里开始讲起。嗯,也许这样开始比较好……


    很久很久以前,有一群人,他们决定用8个可以开合的晶体管来组合成不同的状态,以表示世界上的万物。他们看到8个开关状态是好的,于是他们把这称为"字节"。
    再后来,他们又做了一些可以处理这些字节的机器,机器开动了,可以用字节来组合出很多状态,状态开始变来变去。他们看到这样是好的,于是它们就这机器称为"计算机"。

    开始计算机只在美国用。八位的字节一共可以组合出256(2的8次方)种不同的状态。
    他们把其中的编号从0开始的32种状态分别规定了特殊的用途,一但终端、打印机遇上约定好的这些字节被传过来时,就要做一些约定的动作。遇上00x10, 终端就换行,遇上0x07, 终端就向人们嘟嘟叫,例好遇上0x1b, 打印机就打印反白的字,或者终端就用彩色显示字母。他们看到这样很好,于是就把这些0x20以下的字节状态称为"控制码"。
    他们又把所有的空格、标点符号、数字、大小写字母分别用连续的字节状态表示,一直编到了第127号,这样计算机就可以用不同字节来存储英语的文字了。大家看到这样,都感觉很好,于是大家都把这个方案叫做 ANSI 的"Ascii"编码(American Standard Code for Information Interchange,美国信息互换标准代码)。当时世界上所有的计算机都用同样的ASCII方案来保存英文文字。
    后来,就像建造巴比伦塔一样,世界各地的都开始使用计算机,但是很多国家用的不是英文,他们的字母里有许多是ASCII里没有的,为了可以在计算机保存他们的文字,他们决定采用127号之后的空位来表示这些新的字母、符号,还加入了很多画表格时需要用下到的横线、竖线、交叉等形状,一直把序号编到了最后一个状态255。从128到255这一页的字符集被称"扩展字符集"。从此之后,贪婪的人类再没有新的状态可以用了,美帝国主义可能没有想到还有第三世界国家的人们也希望可以用到计算机吧!
    等中国人们得到计算机时,已经没有可以利用的字节状态来表示汉字,况且有6000多个常用汉字需要保存呢。但是这难不倒智慧的中国人民,我们不客气地把那些127号之后的奇异符号们直接取消掉, 规定:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1用到0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了。在这些编码里,我们还把数学符号、罗马希腊的字母、日文的假名们都编进去了,连在 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的"全角"字符,而原来在127号以下的那些就叫"半角"字符了。
    中国人民看到这样很不错,于是就把这种汉字方案叫做 "GB2312"。GB2312 是对 ASCII 的中文扩展。
    但是中国的汉字太多了,我们很快就就发现有许多人的人名没有办法在这里打出来,特别是某些很会麻烦别人的国家领导人。于是我们不得不继续把 GB2312 没有用到的码位找出来老实不客气地用上。
    后来还是不够用,于是干脆不再要求低字节一定是127号之后的内码,只要第一个字节是大于127就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字符集里的内容。结果扩展之后的编码方案被称为 GBK 标准,GBK 包括了 GB2312 的所有内容,同时又增加了近20000个新的汉字(包括繁体字)和符号。
    后来少数民族也要用电脑了,于是我们再扩展,又加了几千个新的少数民族的字,GBK 扩成了 GB18030。从此之后,中华民族的文化就可以在计算机时代中传承了。
    中国的程序员们看到这一系列汉字编码的标准是好的,于是通称他们叫做 "DBCS"(Double Byte Charecter Set 双字节字符集)。在DBCS系列标准里,最大的特点是两字节长的汉字字符和一字节长的英文字符并存于同一套编码方案里,因此他们写的程序为了支持中文处理,必须要注意字串里的每一个字节的值,如果这个值是大于127的,那么就认为一个双字节字符集里的字符出现了。那时候凡是受过加持,会编程的计算机僧侣们都要每天念下面这个咒语数百遍:
    "一个汉字算两个英文字符!一个汉字算两个英文字符……"

    因为当时各个国家都像中国这样搞出一套自己的编码标准,结果互相之间谁也不懂谁的编码,谁也不支持别人的编码,连大陆和台湾这样只相隔了150海里,使用着同一种语言的兄弟地区,也分别采用了不同的 DBCS 编码方案——当时的中国人想让电脑显示汉字,就必须装上一个"汉字系统",专门用来处理汉字的显示、输入的问题,但是那个台湾的愚昧封建人士写的算命程序就必须加装另一套支持 BIG5 编码的什么"倚天汉字系统"才可以用,装错了字符系统,显示就会乱了套!这怎么办?而且世界民族之林中还有那些一时用不上电脑的穷苦人民,他们的文字又怎么办?
    真是计算机的巴比伦塔命题啊!
    正在这时,大天使加百列及时出现了——一个叫 ISO (国际标谁化组织)的国际组织决定着手解决这个问题。他们采用的方法很简单:废了所有的地区性编码方案,重新搞一个包括了地球上所有文化、所有字母和符号的编码!他们打算叫它"Universal Multiple-Octet Coded Character Set",简称 UCS, 俗称 "UNICODE"。
    UNICODE 开始制订时,计算机的存储器容量极大地发展了,空间再也不成为问题了。于是 ISO 就直接规定必须用两个字节,也就是16位来统一表示所有的字符,对于ascii里的那些“半角”字符,UNICODE 包持其原编码不变,只是将其长度由原来的8位扩展为16位,而其他文化和语言的字符则全部重新统一编码。由于"半角"英文符号只需要用到低8位,所以其高8位永远是0,因此这种大气的方案在保存英文文本时会多浪费一倍的空间。
    这时候,从旧社会里走过来的程序员开始发现一个奇怪的现象:他们的strlen函数靠不住了,一个汉字不再是相当于两个字符了,而是一个!是的,从 UNICODE 开始,无论是半角的英文字母,还是全角的汉字,它们都是统一的"一个字符"!同时,也都是统一的"两个字节",请注意"字符"和"字节"两个术语的不同,“字节”是一个8位的物理存贮单元,而“字符”则是一个文化相关的符号。在UNICODE 中,一个字符就是两个字节。一个汉字算两个英文字符的时代已经快过去了。
    从前多种字符集存在时,那些做多语言软件的公司遇上过很大麻烦,他们为了在不同的国家销售同一套软件,就不得不在区域化软件时也加持那个双字节字符集咒语,不仅要处处小心不要搞错,还要把软件中的文字在不同的字符集中转来转去。UNICODE 对于他们来说是一个很好的一揽子解决方案,于是从 Windows NT 开始,MS 趁机把它们的操作系统改了一遍,把所有的核心代码都改成了用 UNICODE 方式工作的版本,从这时开始,WINDOWS 系统终于无需要加装各种本土语言系统,就可以显示全世界上所有文化的字符了。
    但是,UNICODE 在制订时没有考虑与任何一种现有的编码方案保持兼容,这使得 GBK 与UNICODE 在汉字的内码编排上完全是不一样的,没有一种简单的算术方法可以把文本内容从UNICODE编码和另一种编码进行转换,这种转换必须通过查表来进行。
    如前所述,UNICODE 是用两个字节来表示为一个字符,他总共可以组合出65535不同的字符,这大概已经可以覆盖世界上所有文化的符号。如果还不够也没有关系,ISO已经准备了UCS-4方案,说简单了就是四个字节来表示一个字符,这样我们就可以组合出21亿个不同的字符出来(最高位有其他用途),这大概可以用到银河联邦成立那一天吧!

    UNICODE 来到时,一起到来的还有计算机网络的兴起,UNICODE 如何在网络上传输也是一个必须考虑的问题,于是面向传输的众多 UTF(UCS Transfer Format)标准出现了,顾名思义,UTF8就是每次8个位传输数据,而UTF16就是每次16个位,只不过为了传输时的可靠性,从UNICODE到UTF时并不是直接的对应,而是要过一些算法和规则来转换。
    受到过网络编程加持的计算机僧侣们都知道,在网络里传递信息时有一个很重要的问题,就是对于数据高低位的解读方式,一些计算机是采用低位先发送的方法,例如我们PC机采用的 INTEL 架构,而另一些是采用高位先发送的方式,在网络中交换数据时,为了核对双方对于高低位的认识是否是一致的,采用了一种很简便的方法,就是在文本流的开始时向对方发送一个标志符——如果之后的文本是高位在位,那就发送"FEFF",反之,则发送"FFFE"。不信你可以用二进制方式打开一个UTF-X格式的文件,看看开头两个字节是不是这两个字节?

    讲到这里,我们再顺便说说一个很著名的奇怪现象:当你在 windows 的记事本里新建一个文件,输入"联通"两个字之后,保存,关闭,然后再次打开,你会发现这两个字已经消失了,代之的是几个乱码!呵呵,有人说这就是联通之所以拼不过移动的原因。
    其实这是因为GB2312编码与UTF8编码产生了编码冲撞的原因。
    从网上引来一段从UNICODE到UTF8的转换规则:



    Unicode
    UTF-8

    0000 - 007F
    0xxxxxxx

    0080 - 07FF
    110xxxxx 10xxxxxx

    0800 - FFFF
    1110xxxx 10xxxxxx 10xxxxxx




    例如"汉"字的Unicode编码是6C49。6C49在0800-FFFF之间,所以要用3字节模板:1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是:0110 1100 0100 1001,将这个比特流按三字节模板的分段方法分为0110 110001 001001,依次代替模板中的x,得到:1110-0110 10-110001 10-001001,即E6 B1 89,这就是其UTF8的编码。
    而当你新建一个文本文件时,记事本的编码默认是ANSI, 如果你在ANSI的编码输入汉字,那么他实际就是GB系列的编码方式,在这种编码下,"联通"的内码是:
    c1 1100 0001
    aa 1010 1010
    cd 1100 1101
    a8 1010 1000
    注意到了吗?第一二个字节、第三四个字节的起始部分的都是"110"和"10",正好与UTF8规则里的两字节模板是一致的,于是再次打开记事本时,记事本就误认为这是一个UTF8编码的文件,让我们把第一个字节的110和第二个字节的10去掉,我们就得到了"00001 101010",再把各位对齐,补上前导的0,就得到了"0000 0000 0110 1010",不好意思,这是UNICODE的006A,也就是小写的字母"j",而之后的两字节用UTF8解码之后是0368,这个字符什么也不是。这就是只有"联通"两个字的文件没有办法在记事本里正常显示的原因。
    而如果你在"联通"之后多输入几个字,其他的字的编码不见得又恰好是110和10开始的字节,这样再次打开时,记事本就不会坚持这是一个utf8编码的文件,而会用ANSI的方式解读之,这时乱码又不出现了。

    好了,终于可以回答NICO的问题了,在数据库里,有n前缀的字串类型就是UNICODE类型,这种类型中,固定用两个字节来表示一个字符,无论这个字符是汉字还是英文字母,或是别的什么。
    如果你要测试\"abc汉字"这个串的长度,在没有n前缀的数据类型里,这个字串是7个字符的长度,因为一个汉字相当于两个字符。而在有n前缀的数据类型里,同样的测试串长度的函数将会告诉你是5个字符,因为一个汉字就是一个字符。
     
     
     
     

    [转]字符集编码

    2007-10-25 13:16:48 / 天气: 阴雨 / 心情: 郁闷 / 个人分类:工作积累

    这是一篇程序员写给程序员的趣味读物。所谓趣味是指可以比较轻松地了解一些原来不清楚的概念,增进知识,类似于打RPG游戏的升级。整理这篇文章的动机是两个问题: 51Testing软件测试网+u?n_.Uf3~

    问题一:

    使用Windows记事本的“另存为”,可以在GBK、Unicode、Unicode big endian和UTF-8这几种编码方式间相互转换。同样是txt文件,Windows是怎样识别编码方式的呢? 51Testing软件测试网xFQ?m%U$m YQ0S

    我很早前就发现Unicode、Unicode big endian和UTF-8编码的txt文件的开头会多出几个字节,分别是FF、FE(Unicode),FE、FF(Unicode big endian),EF、BB、BF(UTF-8)。但这些标记是基于什么标准呢? 51Testing软件测试网O:^y$z2d

    问题二:

    最近在网上看到一个ConvertUTF.c,实现了UTF-32、UTF-16和UTF-8这三种编码方式的相互转换。对于Unicode(UCS2)、GBK、UTF-8这些编码方式,我原来就了解。但这个程序让我有些糊涂,想不起来UTF-16和UCS2有什么关系。 51Testing软件测试网s5W pnc8Y

    查了查相关资料,总算将这些问题弄清楚了,顺带也了解了一些Unicode的细节。作者写成一篇文章,送给有过类似疑问的朋友。本文在写作时尽量做到通俗易懂,但要求读者知道什么是字节,什么是十六进制。
    7s6dC YQz \(u/O,d150252

    0、big endian和little endian

    big endian和little endian是CPU处理多字节数的不同方式。例如“汉”字的Unicode编码是6C49。那么写到文件里时,究竟是将6C写在前面,还是将49写在前面?如果将6C写在前面,就是big endian。如果将49写在前面,就是little endian。
    Y1_G8E7j4Z150252

    “endian”这个词出自《格列佛游记》。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开,由此曾发生过六次叛乱,一个皇帝送了命,另一个丢了王位。
    aF&NH3w.|150252

    我们一般将endian翻译成“字节序”,将big endian和little endian称作“大尾”和“小尾”。 51Testing软件测试网y#Scef0w7Y

    1、字符编码、内码,顺带介绍汉字编码

    字符必须编码后才能被计算机处理。计算机使用的缺省编码方式就是计算机的内码。早期的计算机使用7位的ASCII编码,为了处理汉字,程序员设计了用于简体中文的GB2312和用于繁体中文的big5。
    6VU,C8h f_150252

    GB2312(1980年)一共收录了7445个字符,包括6763个汉字和682个其它符号。汉字区的内码范围高字节从B0-F7,低字节从A1-FE,占用的码位是72*94=6768。其中有5个空位是D7FA-D7FE。
    TC*w!]d+V6S150252

    GB2312支持的汉字太少。1995年的汉字扩展规范GBK1.0收录了21886个符号,它分为汉字区和图形符号区。汉字区包括 21003个字符。2000年的GB18030是取代GBK1.0的正式国家标准。该标准收录了27484个汉字,同时还收录了藏文、蒙文、维吾尔文等主要的少数民族文字。现在的PC平台必须支持GB18030,对嵌入式产品暂不作要求。所以手机、MP3一般只支持GB2312。
    ef_ R+e:Q]~Hb3V{1K150252

    从ASCII、GB2312、GBK到GB18030,这些编码方法是向下兼容的,即同一个字符在这些方案中总是有相同的编码,后面的标准支持更多的字符。在这些编码中,英文和中文可以统一地处理。区分中文编码的方法是高字节的最高位不为0。按照程序员的称呼,GB2312、GBK到 GB18030都属于双字节字符集 (DBCS)。 51Testing软件测试网"N^ ^T,vGE C

    有的中文Windows的缺省内码还是GBK,可以通过GB18030升级包升级到GB18030。不过GB18030相对GBK增加的字符,普通人是很难用到的,通常我们还是用GBK指代中文Windows内码。
    iA-Q'iM%Y150252

    这里还有一些细节:
    Y:zAl0f wI150252

    GB2312的原文还是区位码,从区位码到内码,需要在高字节和低字节上分别加上A0。
    }3G@'t{KL6Z150252

    在DBCS中,GB内码的存储格式始终是big endian,即高位在前。 51Testing软件测试网'lV-n?}"oTs

    GB2312的两个字节的最高位都是1。但符合这个条件的码位只有128*128=16384个。所以GBK和GB18030的低字节最高位都可能不是1。不过这不影响DBCS字符流的解析:在读取DBCS字符流时,只要遇到高位为1的字节,就可以将下两个字节作为一个双字节编码,而不用管低字节的高位是什么。 51Testing软件测试网H-F N$nk4Y}

    2、Unicode、UCS和UTF

    前面提到从ASCII、GB2312、GBK到GB18030的编码方法是向下兼容的。而Unicode只与ASCII兼容(更准确地说,是与ISO-8859-1兼容),与GB码不兼容。例如“汉”字的Unicode编码是6C49,而GB码是BABA。 51Testing软件测试网RI\;K$n j

    Unicode也是一种字符编码方法,不过它是由国际组织设计,可以容纳全世界所有语言文字的编码方案。Unicode的学名是 "Universal Multiple-Octet Coded Character Set",简称为UCS。UCS可以看作是"Unicode Character Set"的缩写。 51Testing软件测试网%m?"Z7WQlB5D

    根据维基百科全书( http://zh.wikipedia.org/wiki/ )的记载:历史上存在两个试图独立设计Unicode的组织,即国际标准化组织(ISO)和一个软件制造商的协会(unicode.org)。ISO开发了ISO 10646项目,Unicode协会开发了Unicode项目。
    3Xq*_x+M(xh150252

    在1991年前后,双方都认识到世界不需要两个不兼容的字符集。于是它们开始合并双方的工作成果,并为创立一个单一编码表而协同工作。从Unicode2.0开始,Unicode项目采用了与ISO 10646-1相同的字库和字码。 51Testing软件测试网LP h6n_ ^ L

    目前两个项目仍都存在,并独立地公布各自的标准。Unicode协会现在的最新版本是2005年的Unicode 4.1.0。ISO的最新标准是ISO 10646-3:2003。
    3y)o7gW:R4Bi150252

    UCS只是规定如何编码,并没有规定如何传输、保存这个编码。例如“汉”字的UCS编码是6C49,我可以用4个ascii数字来传输、保存这个编码;也可以用utf-8编码:3个连续的字节E6 B1 89来表示它。关键在于通信双方都要认可。UTF-8、UTF-7、UTF-16都是被广泛接受的方案。UTF-8的一个特别的好处是它与ISO- 8859-1完全兼容。UTF是“UCS Transformation Format”的缩写。 51Testing软件测试网n9u8^4t-er O][I

    IETF的RFC2781和RFC3629以RFC的一贯风格,清晰、明快又不失严谨地描述了UTF-16和UTF-8的编码方法。我总是记不得IETF是Internet Engineering Task Force的缩写。但IETF负责维护的RFC是Internet上一切规范的基础。 51Testing软件测试网0xq"ab.[

    2.1、内码和code page

    目前Windows的内核已经采用Unicode编码,这样在内核上可以支持全世界所有的语言文字。但是由于现有的大量程序和文档都采用了某种特定语言的编码,例如GBK,Windows不可能不支持现有的编码,而全部改用Unicode。 51Testing软件测试网Fu{){)J tn

    Windows使用代码页(code page)来适应各个国家和地区。code page可以被理解为前面提到的内码。GBK对应的code page是CP936。
    HW#Q { f0G3K G150252

    微软也为GB18030定义了code page:CP54936。但是由于GB18030有一部分4字节编码,而Windows的代码页只支持单字节和双字节编码,所以这个code page是无法真正使用的。 51Testing软件测试网6N$D5~f\%{Fl@

    3、UCS-2、UCS-4、BMP

    UCS有两种格式:UCS-2和UCS-4。顾名思义,UCS-2就是用两个字节编码,UCS-4就是用4个字节(实际上只用了31位,最高位必须为0)编码。下面让我们做一些简单的数学游戏:
    F.MU*q&t)sI150252

    UCS-2有2^16=65536个码位,UCS-4有2^31=2147483648个码位。 51Testing软件测试网7r4~ L&p0d7\

    UCS-4根据最高位为0的最高字节分成2^7=128个group。每个group再根据次高字节分为256个plane。每个 plane根据第3个字节分为256行(rows),每行包含256个cells。当然同一行的cells只是最后一个字节不同,其余都相同。
    DL `kAiU150252

    group 0的plane 0被称作Basic Multilingual Plane, 即BMP。或者说UCS-4中,高两个字节为0的码位被称作BMP。 51Testing软件测试网` Z9iL0D@j

    将UCS-4的BMP去掉前面的两个零字节就得到了UCS-2。在UCS-2的两个字节前加上两个零字节,就得到了UCS-4的BMP。而目前的UCS-4规范中还没有任何字符被分配在BMP之外。
    N,OK bgH)b150252

    4、UTF编码

    UTF-8就是以8位为单元对UCS进行编码。从UCS-2到UTF-8的编码方式如下:
    3M9DL'QY150252

    UCS-2编码(16进制) UTF-8 字节流(二进制) 0000 - 007F 0xxxxxxx 0080 - 07FF 110xxxxx 10xxxxxx 0800 - FFFF 1110xxxx 10xxxxxx 10xxxxxx 51Testing软件测试网#DL,V{{T jf+t)k

    例如“汉”字的Unicode编码是6C49。6C49在0800-FFFF之间,所以肯定要用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是:0110 110001 001001,用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。 51Testing软件测试网f&yWB*X

    读者可以用记事本测试一下我们的编码是否正确。需要注意,UltraEdit在打开utf-8编码的文本文件时会自动转换为UTF-16,可能产生混淆。你可以在设置中关掉这个选项。更好的工具是Hex Workshop。 51Testing软件测试网*Z}-s+ca8]

    UTF-16以16位为单元对UCS进行编码。对于小于0x10000的UCS码,UTF-16编码就等于UCS码对应的16位无符号整数。对于不小于0x10000的UCS码,定义了一个算法。不过由于实际使用的UCS2,或者UCS4的BMP必然小于0x10000,所以就目前而言,可以认为UTF-16和UCS-2基本相同。但UCS-2只是一个编码方案,UTF-16却要用于实际的传输,所以就不得不考虑字节序的问题。 51Testing软件测试网XZs'kR/j2U3w

    5、UTF的字节序和BOM

    UTF-8以字节为编码单元,没有字节序的问题。UTF-16以两个字节为编码单元,在解释一个UTF-16文本前,首先要弄清楚每个编码单元的字节序。例如“奎”的Unicode编码是594E,“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”,那么这是“奎” 还是“乙”?
    N[Y[7q'O @150252

    Unicode规范中推荐的标记字节顺序的方法是BOM。BOM不是“Bill Of Material”的BOM表,而是Byte Order Mark。BOM是一个有点小聪明的想法: 51Testing软件测试网wh O1Z&?

    在UCS编码中有一个叫做"ZERO WIDTH NO-BREAK SPACE"的字符,它的编码是FEFF。而FFFE在UCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前,先传输字符"ZERO WIDTH NO-BREAK SPACE"。 51Testing软件测试网a'n9W5}Qo

    这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little-Endian的。因此字符"ZERO WIDTH NO-BREAK SPACE"又被称作BOM。
    8x8u n1N1ixH150252

    UTF-8不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8编码是EF BB BF(读者可以用我们前面介绍的编码方法验证一下)。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。
    7D(T$CL:i? t150252

    Windows就是使用BOM来标记文本文件的编码方式的。
    'q0f q-U0@ZDbJM150252

    6、进一步的参考资料

    本文主要参考的资料是 "Short overview of ISO-IEC 10646 and Unicode" ( http://www.nada.kth.se/i18n/ucs/unicode-iso10646-oview.html )。 51Testing软件测试网([r"rl2z%wt

    我还找了两篇看上去不错的资料,不过因为我开始的疑问都找到了答案,所以就没有看:
    z,z-cd7kBU5S150252

    "Understanding Unicode A general introduction to the Unicode Standard" ( http://scrīpts.sil.org/cms/scrīpts/page.php?site_id=nrsi&item_id=IWS-Chapter04a ) "Character set encoding basics Understanding character set encodings and legacy encodings" ( http://scrīpts.sil.org/cms/scrīpts/page.php?site_id=nrsi&item_id=IWS-Chapter03 ) 我写过UTF-8、UCS-2、GBK相互转换的软件包,包括使用Windows API和不使用Windows API的版本。以后有时间的话,我会整理一下放到我的个人主页上( http://fmddlmyy.home4u.china.com )。
    5o1f:K%r x%n b150252

    附录1 再说说区位码、GB2312、内码和代码页

    有的朋友对文章中这句话还有疑问: “GB2312的原文还是区位码,从区位码到内码,需要在高字节和低字节上分别加上A0。”
    S6T6@$n.?X!O150252

    我再详细解释一下: 51Testing软件测试网5V8vp4l-H+[N9G%D

    “GB2312的原文”是指国家1980年的一个标准《中华人民共和国国家标准 信息交换用汉字编码字符集 基本集 GB 2312-80》。这个标准用两个数来编码汉字和中文符号。第一个数称为“区”,第二个数称为“位”。所以也称为区位码。1-9区是中文符号,16-55 区是一级汉字,56-87区是二级汉字。现在Windows也还有区位输入法,例如输入1601得到“啊”。 51Testing软件测试网\+D,~N~ u;r{C o&jj

    内码是指操作系统内部的字符编码。早期操作系统的内码是与语言相关的.现在的Windows在内部统一使用Unicode,然后用代码页适应各种语言,“内码”的概念就比较模糊了。微软一般将缺省代码页指定的编码说成是内码,在特殊的场合也会说自己的内码是Unicode,例如在 GB18030问题的处理上。
    G MFO;|_s150252

    所谓代码页(code page)就是针对一种语言文字的字符编码。例如GBK的code page是CP936,BIG5的code page是CP950,GB2312的code page是CP20936。
    -?'r5l6@J T150252

    Windows中有缺省代码页的概念,即缺省用什么编码来解释字符。例如Windows的记事本打开了一个文本文件,里面的内容是字节流:BA、BA、D7、D6。Windows应该去怎么解释它呢? 51Testing软件测试网j0N,]-k]

    是按照Unicode编码解释、还是按照GBK解释、还是按照BIG5解释,还是按照ISO8859-1去解释?如果按GBK去解释,就会得到“汉字”两个字。按照其它编码解释,可能找不到对应的字符,也可能找到错误的字符。所谓“错误”是指与文本作者的本意不符,这时就产生了乱码。
    #^-Lv$B$IHA2a4B)z_150252

    答案是Windows按照当前的缺省代码页去解释文本文件里的字节流。缺省代码页可以通过控制面板的区域选项设置。记事本的另存为中有一项ANSI,其实就是按照缺省代码页的编码方法保存。 51Testing软件测试网:\*R,W)x~3D

    Windows的内码是Unicode,它在技术上可以同时支持多个代码页。只要文件能说明自己使用什么编码,用户又安装了对应的代码页,Windows就能正确显示,例如在HTML文件中就可以指定charset。 51Testing软件测试网p,g3lq)~#U

    有的HTML文件作者,特别是英文作者,认为世界上所有人都使用英文,在文件中不指定charset。如果他使用了0x80-0xff之间的字符,中文Windows又按照缺省的GBK去解释,就会出现乱码。这时只要在这个html文件中加上指定charset的语句,例如:如果原作者使用的代码页和ISO8859-1兼容,就不会出现乱码了。
    2`UK-? z"z)Z ]4pe150252

    再说区位码,啊的区位码是1601,写成16进制是0x10,0x01。这和计算机广泛使用的ASCII编码冲突。为了兼容00-7f的 ASCII编码,我们在区位码的高、低字节上分别加上A0。这样“啊”的编码就成为B0A1。我们将加过两个A0的编码也称为GB2312编码,虽然 GB2312的原文根本没提到这一点。 51Testing软件测试网nAl$Ay R7X-j

    http://fmddlmyy.home4u.china.com /text6.html

  • My first job...

    2007-11-18 18:22:52

    2007年6月11日,是我在甲骨文软件研发中心报到工作的第一天; 20年寒窗,职业生涯从这里开始了。之后被分配到了RM(release management)部门,负责面向日文的Oracle产品测试工作,负责应用服务器产品。  从此我开始了我的测试之旅,工作开始之初的混沌和迷茫让我迫切地感到补充知识的重要性....

    希望从这里我能得到提高,成长,变得越来越强壮...

Open Toolbar