??xml version="1.0" encoding="utf-8" standalone="yes"?>亚洲精品高清在线,亚洲av无码一区二区三区人妖,亚洲精品国偷自产在线http://www.tkk7.com/titanaly/zh-cnSun, 11 May 2025 14:05:35 GMTSun, 11 May 2025 14:05:35 GMT60JAVA的文件操作【{?/title><link>http://www.tkk7.com/titanaly/archive/2013/09/17/404191.html</link><dc:creator>不高?/dc:creator><author>不高?/author><pubDate>Tue, 17 Sep 2013 09:51:00 GMT</pubDate><guid>http://www.tkk7.com/titanaly/archive/2013/09/17/404191.html</guid><wfw:comment>http://www.tkk7.com/titanaly/comments/404191.html</wfw:comment><comments>http://www.tkk7.com/titanaly/archive/2013/09/17/404191.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.tkk7.com/titanaly/comments/commentRss/404191.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/titanaly/services/trackbacks/404191.html</trackback:ping><description><![CDATA[<h2><span style="font-size: large;"><span style="font-family: Cambria;"></span></span></h2><div>11.3 I/OcM?/div><div></div><div>         ׃在IO操作中,需要用的数据源有很多Q作Z个IO技术的初学者,从读写文件开始学习IO技术是一个比较好的选择。因为文件是一U常见的数据源,而且d文g也是E序员进行IO~程的一个基本能力。本章IOcȝ使用׃d文g开始?/div><div></div><div>11.3.1 文g操作</div><div></div><div>         文g(File)?最常见的数据源之一Q在E序中经帔R要将数据存储到文件中Q例如图片文件、声x件等数据文gQ也l常需要根据需要从指定的文件中q行数据的读取。当Ӟ 在实际用时Q文仉包含一个的格式Q这个格式需要程序员Ҏ需要进行设计,d已有的文件时也需要熟悉对应的文g格式Q才能把数据从文件中正确的读取出 来?/div><div></div><div>         文g的存储介质有很多Q例如硬盘、光盘和U盘等Q由于IOc设计时Q从数据源{换ؓ对象的操作由API实现了,所以存储介质的不同对于E序员来说是透明的,和实际编写代码无兟?/div><div></div><div>11.3.1.1 文g的概?/div><div></div><div>         文g是计机中一U基本的数据存储形式Q在实际存储数据Ӟ如果对于数据的读写速度要求不是很高Q存储的数据量不是很大时Q用文件作ZU持久数据存储的方式是比较好的选择?/div><div></div><div>         存储在文件内部的数据和内存中的数据不同,存储在文件中的数据是一U?#8220;持久存储”Q也是当程序退出或计算机关Z后,数据q是存在的,而内存内部的数据在程序退出或计算机关Z后,数据׃׃?/div><div></div><div>         在不同的存储介质中,文g中的数据都是以一定的序依次存储hQ在实际d时由g以及操作pȝ完成对于数据的控Ӟ保证E序d到的数据和存储的序保持一致?/div><div></div><div>         每个文g以一个文件\径和文g名称q行表示Q在需要访问该文g的时Q只需要知道该文g的\径以及文件的全名卛_。在不同的操作系l环境下Q文件\径的表示形式是不一LQ例如在Windows操作pȝ中一般的表示形式为C:\windows\systemQ而Unix上的表示形式?user/my。所以如果需要让JavaE序能够在不同的操作pȝ下运行,书写文g路径时还需要比较注意?/div><div></div><div>11.3.1.1.1 l对路径和相对\?/div><div></div><div>         l对路径是指书写文g的完整\径,例如d:\java\Hello.javaQ该路径中包含文件的完整路径d:\java以及文g的全名Hello.java。用该路径可以唯一的找C个文Ӟ不会产生歧义。但是用绝对\径在表示文gӞ受到的限制很大,且不能在不同的操作系l下q行Q因Z同操作系l下l对路径的表辑Ş式存在不同?/div><div></div><div>         相对路径是指书写文g的部分\径,例如\test\Hello.javaQ该路径中只包含文g的部分\径\test和文件的全名Hello.javaQ部分\径是指当前\径下的子路径Q例如当前程序在d:\abc下运行,则该文g的完整\径就是d:\abc\test。用这UŞ式,可以更加通用的代表文件的位置Q得文件\径生一定的灉|性?/div><div></div><div>         在Eclipse目中运行程序时Q当前\径是目的根目录Q例如工作空间存储在d:\javaprojectQ当前项目名U是TestQ则当前路径是:d:\javaproject\Test。在控制C面运行程序时Q当前\径是class文g所在的目录Q如果class文g包含包名Q则以该class文g最层的包名作为当前\径?/div><div></div><div>         另外在Java语言的代码内部书写文件\径时Q需要注意大写Q大写需要保持一_路径中的文g夹名U区分大写。由?#8217;\’是Java语言中的Ҏ字符Q所以在代码内部书写文g路径Ӟ例如代表“c:\test\java\Hello.java”Ӟ需要书写成“c:\\test\\java\\Hello.java”?#8220;c:/test/java/Hello.java”Q这些都需要在代码中注意?/div><div></div><div>11.3.1.1.2 文g名称</div><div></div><div>         文g名称一般采?#8220;文g?后缀?#8221;的Ş式进行命名,其中“文g?#8221;用来表示文g的作用,而用后~名来表示文g的类型,q是当前操作pȝ中常见的一UŞ式,例如“readme.txt”文gQ其中readme代表该文件时说明文gQ而txt后缀名代表文件时文本文gcdQ在操作pȝ中,q会自动特定格式的后缀名和对应的程序关联,在双击该文g时用特定的E序打开?/div><div></div><div>         其实在文件名U只是一个标C,和实际存储的文g内容没有必然的联p,只是使用q种方式方便文g的用。在E序中需要存储数据时Q如果自p计了特定的文件格式,则可以自定义文g的后~名,来标Cq文gcd?/div><div></div><div>         和文件\径一P在Java代码内部书写文g名称时也区分大小写,文g名称的大写必须和操作系l中的大写保持一致?/div><div></div><div>         另外Q在书写文g名称时不要忘C写文件的后缀名?/div><div></div><div>11.3.1.2 Filec?/div><div></div><div>         Z很方便的代表文g的概念,以及存储一些对于文件的基本操作Q在java.io包中设计了一个专门的c?#8212;—FilecR?/div><div></div><div>         在FilecM包含了大部分和文件操作的功能ҎQ该cȝ对象可以代表一个具体的文g或文件夹Q所以以前曾有h该cȝcd修改成FilePathQ因cM可以代表一个文件夹Q更准确的说是可以代表一个文件\径?/div><div></div><div>         下面介绍一下Filecȝ基本使用?/div><div></div><div>         1、File对象代表文g路径</div><div></div><div>Filecȝ对象可以代表一个具体的文g路径Q在实际代表Ӟ可以使用l对路径也可以用相对\径?/div><div>下面是创建的文g对象CZ?/div><div>                   public File(String pathname)</div><div>         该示例中使用一个文件\径表CZ个Filecȝ对象Q例如:</div><div>                   File f1 = new File(“d:\\test\\1.txt”);</div><div>                   File f2 = new File(“1.txt”);</div><div>                  File f3 = new File(“e:\\abc”);</div><div>q里的f1和f2对象分别代表一个文Ӟf1是绝对\径,而f2是相对\径,f3则代表一个文件夹Q文件夹也是文g路径的一U?/div><div>public File(String parent, String child)</div><div>                            也可以用父路径和子路径l合Q实C表文件\径,例如Q?/div><div></div><div>                                     File f4 = new File(“d:\\test\\”,”1.txt”);</div><div></div><div>                            q样代表的文件\径是Qd:\test\1.txt?/div><div></div><div>         2、Filecd用方?/div><div></div><div>FilecM包含了很多获得文件或文g夹属性的ҎQ用v来比较方便,下面常见的Ҏ介绍如下Q?/div><div>                   a、createNewFileҎ</div><div></div><div>                                     public boolean createNewFile() throws IOException</div><div></div><div>该方法的作用是创建指定的文g。该Ҏ只能用于创徏文gQ不能用于创建文件夹Q且文g路径中包含的文g夹必d在?/div><div>                   b、delectҎ</div><div></div><div>                                     public boolean delete()</div><div></div><div>该方法的作用是删除当前文件或文g夏V如果删除的是文件夹Q则该文件夹必须为空。如果需要删除一个非I的文g夹,则需要首先删除该文g夹内部的每个文g和文件夹Q然后在可以删除Q这个需要书写一定的逻辑代码实现?/div><div>                   c、existsҎ</div><div></div><div>                                     public boolean exists()</div><div></div><div>                            该方法的作用是判断当前文件或文gҎ否存在?/div><div></div><div>                   d、getAbsolutePathҎ</div><div></div><div>                                     public String getAbsolutePath()</div><div></div><div>该方法的作用是获得当前文件或文g夹的l对路径。例如c:\test\1.t则返回c:\test\1.t?/div><div>                   e、getNameҎ</div><div></div><div>                                     public String getName()</div><div></div><div>                            该方法的作用是获得当前文件或文g夹的名称。例如c:\test\1.tQ则q回1.t?/div><div></div><div>                   f、getParentҎ</div><div></div><div>                                     public String getParent()</div><div></div><div>                            该方法的作用是获得当前\径中的父路径。例如c:\test\1.t则返回c:\test?/div><div></div><div>                   g、isDirectoryҎ</div><div></div><div>                                     public boolean isDirectory()</div><div></div><div>                            该方法的作用是判断当前File对象是否是目录?/div><div></div><div>                   h、isFileҎ</div><div></div><div>                                     public boolean isFile()</div><div></div><div>                            该方法的作用是判断当前File对象是否是文件?/div><div></div><div>                   i、lengthҎ</div><div></div><div>                                     public long length()</div><div></div><div>该方法的作用是返回文件存储时占用的字节数。该数D得的是文件的实际大小Q而不是文件在存储时占用的I间数?/div><div>                   j、listҎ</div><div></div><div>                                     public String[] list()</div><div></div><div>该方法的作用是返回当前文件夹下所有的文g名和文g夹名U。说明,该名UC是绝对\径?/div><div>                   k、listFilesҎ</div><div></div><div>                                     public File[] listFiles()</div><div></div><div>                            该方法的作用是返回当前文件夹下所有的文g对象?/div><div></div><div>                   l、mkdirҎ</div><div></div><div>                                     public boolean mkdir()</div><div></div><div>该方法的作用是创建当前文件文件夹Q而不创徏该\径中的其它文件夹。假设d盘下只有一个test文g夹,则创建d:\test\abc文g夹则成功Q如果创建d:\a\b文g夹则创徏p|Q因路径中d:\a文g夹不存在。如果创建成功则q回trueQ否则返回false?/div><div>                   m、mkdirsҎ</div><div></div><div>                                     public boolean mkdirs()</div><div></div><div>该方法的作用是创建文件夹Q如果当前\径中包含的父目录不存在时Q也会自动根据需要创建?/div><div>                   n、renameToҎ</div><div></div><div>                                     public boolean renameTo(File dest)</div><div></div><div>该方法的作用是修Ҏ件名。在修改文g名时不能改变文g路径Q如果该路径下已有该文gQ则会修改失败?/div><div>                   o、setReadOnlyҎ</div><div></div><div>                                     public boolean setReadOnly()</div><div></div><div>                            该方法的作用是设|当前文件或文g夹ؓ只读?/div><div></div><div>         3、Filecd本示?/div><div></div><div>                   以上各方法实现的试代码如下Q?/div><div></div><div>                            import java.io.File;</div><div></div><div>/**</div><div> * FilecM用示?/div><div> */</div><div>public class FileDemo {</div><div>         public static void main(String[] args) {</div><div>                   //创徏File对象</div><div>                   File f1 = new File("d:\\test");</div><div>                   File f2 = new File("1.txt");</div><div>                   File f3 = new File("e:\\file.txt");</div><div>                   File f4 = new File("d:\\","1.txt");</div><div>                   //创徏文g</div><div>                   try{</div><div>                            boolean b = f3.createNewFile();</div><div>                   }catch(Exception e){</div><div>                            e.printStackTrace();</div><div>                   }</div><div>                   //判断文g是否存在</div><div>                   System.out.println(f4.exists());</div><div>                   //获得文g的绝对\?/div><div>                   System.out.println(f3.getAbsolutePath());</div><div>                   //获得文g?/div><div>                   System.out.println(f3.getName());</div><div>                   //获得父\?/div><div>                   System.out.println(f3.getParent());</div><div>                   //判断是否是目?/div><div>                   System.out.println(f1.isDirectory());</div><div>                   //判断是否是文?/div><div>                   System.out.println(f3.isFile());</div><div>                   //获得文g长度</div><div>                   System.out.println(f3.length());</div><div>                   //获得当前文g夹下所有文件和文g夹名U?/div><div>                   String[] s = f1.list();</div><div>                   for(int i = 0;i < s.length;i++){</div><div>                            System.out.println(s[i]);</div><div>                   }</div><div>                   //获得文g对象</div><div>                   File[] f5 = f1.listFiles();</div><div>                   for(int i = 0;i < f5.length;i++){</div><div>                            System.out.println(f5[i]);</div><div>                   }</div><div>                   //创徏文g?/div><div>                   File f6 = new File("e:\\test\\abc");</div><div>                   boolean b1 = f6.mkdir();</div><div>                   System.out.println(b1);</div><div>                   b1 = f6.mkdirs();</div><div>                   System.out.println(b1);</div><div>                   //修改文g?/div><div>                   File f7 = new File("e:\\a.txt");</div><div>                   boolean b2 = f3.renameTo(f7);</div><div>                   System.out.println(b2);</div><div>                   //讄文g为只?/div><div>                   f7.setReadOnly();             </div><div>         }</div><div>}</div><div>         4、Filecȝ合示?/div><div></div><div>下面以两个示例演CFilecȝl合使用。第一个示例是昄某个文g夹下的所有文件和文g夹,原理是输出当前名Uͼ然后判断当前File?象是文gq是文g夹,如果则获得该文g夹下的所有子文g和子文g夹,q归调用该方法实现。第二个CZ是删除某个文件夹下的所有文件和文g夹,原理是判?是否是文Ӟ如果是文件则直接删除Q如果是文g夹,则获得该文g夹下所有的子文件和子文件夹Q然后递归调用该方法处理所有子文g和子文g夹,然后空文g 夹删除。则试时}慎用第二个ҎQ以免删除自己有用的数据文g。示例代码如下:</div><div>                            import java.io.File;</div><div></div><div>/**</div><div> * 文gl合使用CZ</div><div> */</div><div>public class AdvanceFileDemo {</div><div>         public static void main(String[] args) {</div><div>                   File f = new File("e:\\Book");</div><div>                   printAllFile(f);</div><div>                   File f1 = new File("e:\\test");</div><div>                   deleteAll(f1);</div><div>         }</div><div>        </div><div>         /**</div><div>          * 打印f路径下所有的文g和文件夹</div><div>          * @param f 文g对象</div><div>          */</div><div>         public static void printAllFile(File f){</div><div>                   //打印当前文g?/div><div>                   System.out.println(f.getName());</div><div>                   //是否是文件夹</div><div>                   if(f.isDirectory()){</div><div>                            //获得该文件夹下所有子文g和子文g?/div><div>                            File[] f1 = f.listFiles();</div><div>                            //循环处理每个对象</div><div>                            int len = f1.length;</div><div>                            for(int i = 0;i < len;i++){</div><div>                                     //递归调用Q处理每个文件对?/div><div>                                     printAllFile(f1[i]);</div><div>                            }</div><div>                   }</div><div>         }</div><div>        </div><div>         /**</div><div>          * 删除对象f下的所有文件和文g?/div><div>          * @param f 文g路径</div><div>          */</div><div>         public static void deleteAll(File f){</div><div>                   //文g</div><div>                   if(f.isFile()){</div><div>                            f.delete();</div><div>                   }else{ //文g?/div><div>                            //获得当前文g夹下的所有子文g和子文g?/div><div>                            File f1[] = f.listFiles();</div><div>                            //循环处理每个对象</div><div>                            int len = f1.length;</div><div>                            for(int i = 0;i < len;i++){</div><div>                                     //递归调用Q处理每个文件对?/div><div>                                     deleteAll(f1[i]);</div><div>                            }</div><div>                            //删除当前文g?/div><div>                            f.delete();</div><div>                   }</div><div>         }</div><div>}</div><div>         关于Filecȝ使用׃l这么多Q其它的Ҏ和用时需要注意的问题q需要多q行l习和实际用?/div><div></div><div>11.3.1.3 d文g</div><div></div><div>         虽然前面介绍了流的概念,但是q个概念对于初学者来_q是比较抽象的,下面以实际的d文gZ子,介绍的概念Q以及输入流的基本用?/div><div></div><div>         按照前面介绍的知识,文件中的数据读入程序,是将E序外部的数据传入程序中Q应该用输入流——InputStream或Reader。而由于读取的是特定的数据?#8212;—文gQ则可以使用输入对应的子cFileInputStream或FileReader实现?/div><div></div><div>         在实际书写代码时Q需要首先熟悉读取文件在E序中实现的q程。在Java语言的IO~程中,d文g是分两个步骤Q?、将文g中的数据转换为流Q?、读取流内部的数据。其中第一个步骤由pȝ完成Q只需要创建对应的对象即可,对象创徏完成以后步骤1完成了Q第二个步骤使用输入对象中的readҎ卛_实现了?/div><div></div><div>         使用输入进行编E时Q代码一般分?个部分:1、创建流对象Q?、读取流对象内部的数据,3、关闭流对象。下面以d文g的代码示例:</div><div></div><div>                   import java.io.*;</div><div></div><div>/**</div><div> * 使用FileInputStreamd文g</div><div> */</div><div>public class ReadFile1 {</div><div>         public static void main(String[] args) {</div><div>                   //声明对?/div><div>                   FileInputStream fis = null;                 </div><div>                   try{</div><div>                            //创徏对?/div><div>                            fis = new FileInputStream("e:\\a.txt");</div><div>                            //d数据Qƈ读取到的数据存储到数组?/div><div>                            byte[] data = new byte[1024]; //数据存储的数l?/div><div>                            int i = 0; //当前下标</div><div>                            //d中的第一个字节数?/div><div>                            int n = fis.read();</div><div>                            //依次d后箋的数?/div><div>                            while(n != -1){ //未到达流的末?/div><div>                                     //有效数据存储到数组?/div><div>                                     data[i] = (byte)n;</div><div>                                     //下标增加</div><div>                                     i++;</div><div>                                     //d下一个字节的数据</div><div>                                      n = fis.read();</div><div>                            }</div><div>                           </div><div>                            //解析数据</div><div>                            String s = new String(data,0,i);</div><div>                            //输出字符?/div><div>                            System.out.println(s);</div><div>                   }catch(Exception e){</div><div>                            e.printStackTrace();</div><div>                   }finally{</div><div>                            try{</div><div>                                     //关闭,释放资源</div><div>                                     fis.close();</div><div>                            }catch(Exception e){}</div><div>                   }</div><div>         }</div><div>}</div><div>         在该CZ代码中,首先创徏一个FileInputStreamcd的对象fisQ?/div><div></div><div>                   fis = new FileInputStream("e:\\a.txt");</div><div></div><div>         q样建立了一个连接到数据源e:\a.txt的流Qƈ该数据源中的数据{换ؓ对象fisQ以后程序读取数据源中的数据Q只需要从对象fis中读取即可?/div><div></div><div>         dfis中的数据Q需要用readҎQ该Ҏ是从InputStreamcMl承q来的方法,该方法的作用是每ơ读取流中的一个字节,如果需要读取流中的所有数据,需要用@环读取,当到达流的末时QreadҎ的返回值是-1?/div><div></div><div>         在该CZ中,首先d中的第一个字节:</div><div></div><div>                   int n = fis.read();</div><div></div><div>         q将d的D值给int值nQ如果流fis为空Q则n的值是-1Q否则n中的最后一个字节包含的时流fis中的W一个字节,该字节被d以后Q将被从fis中删除?/div><div></div><div>         然后循环d中的其它数据,如果d到的数据不是-1Q则已l读取到的数据n强制转换为byteQ即取n中的有效数据——最后一个字节,q存储到数组data中,然后调用对象fis中的readҎl箋d中的下一个字节的数据。一直这样@环下去,直到d到的数据?1Q也是d到流的末ֈ循环l束?/div><div></div><div>         q里的数l长度是1024Q所以要求流中的数据长度不能过1024Q所以该CZ代码在这里具有一定的局限性。如果流的数据个数比较多Q则可以?024扩大到合适的个数卛_?/div><div></div><div>         l过上面的@环以后,可以将中的数据依ơ存储到data数组中,存储到data数组中有效数据的个数是i个,卛_@环次数?/div><div></div><div>         其实截至到这里,IO操作中的d数据已经完成Q然后再按照数据源中的数据格式,q里是文件的格式Q解析读取出的byte数组卛_?/div><div></div><div>         该示例代码中的解析,只是从对象中d到的有效的数据,也就是data数组中的前n个数据,转换为字W串Q然后进行输出?/div><div></div><div>         在该CZ代码中,只是在catch语句中输出异常的信息Q便于代码的调试Q在实际的程序中Q需要根据情况进行一定的逻辑处理Q例如给出提CZ息等?/div><div></div><div>         最后在finally语句块中Q关闭流对象fisQ释放流对象占用的资源,关闭数据源,实现操作的l束工作?/div><div></div><div>         上面详细介绍了读取文件的q程Q其实在实际d数据时Q还可以使用其它的readҎQ下面的CZ代码是用另外一个readҎ实现d的代码:</div><div></div><div>                   import java.io.FileInputStream;</div><div></div><div>/**</div><div> * 使用FileInputStreamd文g</div><div> */</div><div>public class ReadFile2 {</div><div>         public static void main(String[] args) {</div><div>                   //声明对?/div><div>                   FileInputStream fis = null;                 </div><div>                   try{</div><div>                            //创徏对?/div><div>                            fis = new FileInputStream("e:\\a.txt");</div><div>                            //d数据Qƈ读取到的数据存储到数组?/div><div>                            byte[] data = new byte[1024]; //数据存储的数l?/div><div>                            int i = fis.read(data);</div><div>                           </div><div>                            //解析数据</div><div>                            String s = new String(data,0,i);</div><div>                            //输出字符?/div><div>                            System.out.println(s);</div><div>                   }catch(Exception e){</div><div>                            e.printStackTrace();</div><div>                   }finally{</div><div>                            try{</div><div>                                     //关闭,释放资源</div><div>                                     fis.close();</div><div>                            }catch(Exception e){}</div><div>                   }</div><div>         }</div><div>}</div><div>         该示例代码中Q只使用一行代码:</div><div></div><div>                   int i = fis.read(data);</div><div></div><div>         实C流对象fis中的数据d到字节数ldata中。该行代码的作用是将fis中的数据读取出来,q依ơ存储到数组data中,q回gؓ实际d的有效数据的个数?/div><div></div><div>         使用该中方式在进行读取时Q可以简化读取的代码?/div><div></div><div>         当然Q在d文gӞ也可以用Readercȝ子类FileReaderq行实现Q在~写代码Ӟ只需要将上面CZ代码中的byte数组替换成char数组卛_?/div><div></div><div>使用FileReaderd文gӞ是按照char为单位进行读取的Q所以更适合于文本文件的dQ而对于二q制文g或自定义格式的文件来_q是使用FileInputStreamq行dQ方便对于读取到的数据进行解析和操作?/div><div></div><div>d其它数据源的操作和读取文件类|最大的区别在于建立对象时选择的类不同Q而流对象一旦徏立,则基本的dҎ是一P如果只用最基本的readҎq行dQ则使用基本上是一致的。这也是IOc设计的初衷Q得对于流对象的操作保持一_化IOcM用的隑ֺ?/div><div></div><div> E?/div><div></div><div>         基本的输出流包含OutputStream和Writer两个Q区别是OutputStream体系中的c?也就是OutputStream的子c?是按照字节写入的Q而Writer体系中的c?也就是Writer的子c?是按照字W写入的?/div><div></div><div>         使用输出进行编E的步骤是:</div><div></div><div>                   1、徏立输出流</div><div></div><div>                            建立对应的输出流对象Q也是完成由流对象到外部数据源之间的{换?/div><div></div><div>                   2、向中写入数据</div><div></div><div>                            需要输出的数据Q调用对应的writeҎ写入到流对象中?/div><div></div><div>                   3、关闭输出流</div><div></div><div>                            在写入完毕以后,调用对象的closeҎ关闭输出,释放资源?/div><div></div><div>         在用输出流向外部输出数据时Q程序员只需要将数据写入对象即可,底层的API实现流对象中的内容写入外部数据源,q个写入的过E对于程序员来说是透明的,不需要专门书写代码实现?/div><div></div><div>         在向文g中输出数据,也就是写文gӞ使用对应的文件输出流Q包括FileOutputStream和FileWriter两个c,下面以FileOutputStreamZ子说明输出流的用。示例代码如下:</div><div></div><div>                   import java.io.*;</div><div></div><div>/**</div><div> * 使用FileOutputStream写文件示?/div><div> */</div><div>public class WriteFile1 {</div><div>         public static void main(String[] args) {</div><div>                   String s = "Java语言";</div><div>                   int n = 100;</div><div>                   //声明对?/div><div>                   FileOutputStream fos = null;</div><div>                   try{</div><div>                            //创徏对?/div><div>                            fos = new FileOutputStream("e:\\out.txt");</div><div>                            //转换为byte数组</div><div>                            byte[] b1 = s.getBytes();</div><div>                            //换行W?/div><div>                            byte[] b2 = "\r\n".getBytes();</div><div>                            byte[] b3 = String.valueOf(n).getBytes();</div><div>                            //依次写入文g</div><div>                            fos.write(b1);</div><div>                            fos.write(b2);</div><div>                            fos.write(b3);</div><div>                   } catch (Exception e) {</div><div>                            e.printStackTrace();</div><div>                   }finally{</div><div>                            try{</div><div>                                     fos.close();</div><div>                            }catch(Exception e){}</div><div>                   }</div><div>         }</div><div>}</div><div>         该示例代码写入的文g使用C本打开以后Q内容ؓQ?/div><div></div><div>                   Java语言</div><div></div><div>100</div><div>         在该CZ代码中,演示了将一个字W串和一个intcd的gơ写入到同一个文件中。在写入文gӞ首先创徏了一个文件输出流对象fosQ?/div><div></div><div>                   fos = new FileOutputStream("e:\\out.txt");</div><div></div><div>         该对象创Z后,实C从流到外部数据源e:\out.txt的连接。说明:当外部文件不存在Ӟpȝ会自动创文gQ但是如果文件\径中包含未创建的目录时将出现异常。这里书写的文g路径可以是绝对\径也可以是相对\径?/div><div></div><div>         ?实际写入文gӞ有两U写入文件的方式Q覆盖和q加。其?#8220;覆盖”是指清除原文件的内容Q写入新的内容,默认采用该种形式写文Ӟ“q加”是指在已有文?的末ֆ入内容,保留原来的文件内容,例如写日志文件时Q一般采用追加。在实际使用时可以根据需要采用适合的Ş式,可以使用Q?/div><div></div><div>                   public FileOutputStream(String name, boolean append) throws FileNotFoundException</div><div></div><div>         只需要用该构造方法在构造FileOutputStream对象Ӟ第二个参数append的D|ؓtrue卛_?/div><div></div><div>         对象创建完成以后,可以用OutputStream中提供的wirteҎ向流中依ơ写入数据了。最基本的写入方法只支持byte数组格式的数据,所以如果需要将内容写入文gQ则需要把对应的内定w先{换ؓbyte数组?/div><div></div><div>         q里以如下格式写入数据:首先写入字符串sQ用StringcȝgetBytesҎ该字符串{换ؓbyte数组Q然后写入字W串“\r\n”Q{换方式同上,该字W串的作用是实现文本文g的换行显C,最后写入int数据nQ首先将n转换为字W串Q再转换为byte数组。这U写入数据的序以及转换为byte数组的方式就是流的数据格式,也就是该文g的格式。因里写的都是文本文Ӟ所以写入的内容以明文的形式昄出来Q也可以Ҏ自己需要存储的数据讑֮特定的文件格式?/div><div></div><div>         其实Q所有的数据文gQ包括图片文件、声x件等{,都是以一定的数据格式存储数据的,在保存该文gӞ需要保存的数据按照该文件的数据格式依次写入卛_Q而在打开该文件时Q将d到的数据按照该文件的格式解析成对应的逻辑卛_?/div><div></div><div>         最后,在数据写入到内部以后,如果需要立卛_写入内部的数据强制输出到外部的数据源,则可以用流对象的flushҎ实现。如果不需要强制输出,则只需要在写入l束以后Q关闭流对象卛_。在关闭对象时Q系l首先将中未输出到数据源中的数据强制输出,然后再释放该对象占用的内存I间?/div><div></div><div>         使用FileWriter写入文gӞ步骤和创建流对象的操作都和该CZ代码一_只是在{换数据时Q需要将写入的数据{换ؓchar数组Q对于字W串来说Q可以用String中的toCharArrayҎ实现转换Q然后按照文件格式写入数据即可?/div><div></div><div>         对于其它cd的字节输出流/字符输出来_只是在逻辑上连接不同的数据源,在创建对象的代码上会存在一定的不同Q但是一旦流对象创徏完成以后Q基本的写入Ҏ都是writeҎQ也需要首先将需要写入的数据按照一定的格式转换为对应的byte数组/char数组Q然后依ơ写入即可?/div><div></div><div>         所以IOcȝq种设计形式Q只需要熟悉该体系中的某一个类的用以后,可以触cL通的学会其它相同cd的类的用,从而简化程序员的学习,使得使用时保持统一?/div><p style="margin-top: 10px; margin-bottom: 10px; color: #4d4d4d; font-family: Tahoma, Geneva, Arial, Helvetica, sans-serif; font-size: 13px; line-height: 26px; background-color: #ffffff;"><span style="font-family: 宋体;"></span></p><img src ="http://www.tkk7.com/titanaly/aggbug/404191.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/titanaly/" target="_blank">不高?/a> 2013-09-17 17:51 <a href="http://www.tkk7.com/titanaly/archive/2013/09/17/404191.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Netty 3.1 中文用户手册http://www.tkk7.com/titanaly/archive/2013/06/07/400359.html不高?/dc:creator>不高?/author>Fri, 07 Jun 2013 08:11:00 GMThttp://www.tkk7.com/titanaly/archive/2013/06/07/400359.htmlhttp://www.tkk7.com/titanaly/comments/400359.htmlhttp://www.tkk7.com/titanaly/archive/2013/06/07/400359.html#Feedback4http://www.tkk7.com/titanaly/comments/commentRss/400359.htmlhttp://www.tkk7.com/titanaly/services/trackbacks/400359.html

序言


本指南对Netty q行了介lƈ指出其意义所在?br />

1. 问题


现在Q我们用适合一般用途的应用或组件来和彼此通信。例如,我们常常使用一个HTTP客户端从q程服务器获取信息或者通过web servicesq行q程Ҏ的调用?br />
然而,一个适合普通目的的协议或其实现q不具备其规模上的扩展性。例如,我们无法使用一个普通的HTTP服务器进行大型文Ӟ电邮信息的交互,或者处理金 融信息和多h游戏数据那种要求准实时消息传递的应用场景。因此,q些都要求用一个适用于特D目的ƈl过高度优化的协议实现。例如,你可能想要实C个对 ZAJAX的聊天应用,媒体或大文件传输进行过Ҏ优化的HTTP服务器。你甚至可能惛_设计和实C个全新的Q特定于你的需求的通信协议?br />
另一U无法避免的场景是你可能不得不用一U专有的协议和原有系l交互。在q种情况下,你需要考虑的是如何能够快速的开发出q个协议的实现ƈ且同时还没有牺牲最l应用的性能和稳定性?br />

2. Ҏ


Netty 是一个异步的Q事仉动的|络~程框架和工P使用Netty 可以快速开发出可维护的Q高性能、高扩展能力的协议服务及其客L应用?br />
也就是说QNetty 是一个基于NIO的客P服务器端~程框架Q用Netty 可以保你快速和单的开发出一个网l应用,例如实现了某U协议的客户Q服务端应用。Netty相当化和线化了|络应用的编E开发过E,例如QTCP和UDP的socket服务开发?br />
“快?#8221;?#8220;?#8221;q不意味着会让你的最l应用生维护性或性能上的问题。Netty 是一个吸收了多种协议的实现经验,q些协议包括FTP,SMPT,HTTPQ各U二q制Q文本协议,q经q相当精心设计的目Q最l,Netty 成功的找C一U方式,在保证易于开发的同时q保证了其应用的性能Q稳定性和伸羃性?br />
一些用户可能找C某些同样声称hq些Ҏ的~程框架Q因此你们可能想问Netty 又有什么不一L地方。这个问题的{案是Netty 目的设计哲学。从创立之初Q无论是在APIq是在其实现上Netty 都致力于Z提供最适的使用体验。虽然这q不是显而易见的Q但你终会认识到这U设计哲学将令你在阅L指南和用Netty 时变得更加得L和容易?br />
W一? 开?br />

q一章节围lNetty的核心结构展开Q同旉过一些简单的例子可以让你更快的了解Netty的用。当你读完本章,你将有能力用Netty完成客户端和服务端的开发?br />
如果你更喜欢自上而下式的学习方式Q你可以首先完成 W二章:架构总览 的学习,然后再回到这里?br />
1.1. 开始之?br />

q行本章CZE序的两个最低要求是Q最新版本的NettyE序以及JDK 1.5或更高版本。最新版本的NettyE序可在目下蝲?下蝲。下载正版本的JDKQ请C偏好的JDK站点下蝲?br />
q就已经_了吗Q实际上你会发现Q这两个条g已经_你完成Q何协议的开发了。如果不是这P误pNetty目C֌ Q让我们知道q缺了什么?br />
最l但不是臛_Q当你想了解本章所介绍的类的更多信息时请参考API手册。ؓ方便你的使用Q这文档中所有的cd均连接至在线API手册。此外,如果本篇文档中有M错误信息Q无论是语法错误Q还是打印排版错误或者你有更好的Q请不要虑Q立卌pNetty目C֌ ?br />
1.2. 抛弃协议服务


在这个世界上最化的协议不是“Hello,world!”而是抛弃协议 。这是一U丢弃接收到的Q何数据ƈ不做M回应的协议?br />
实现抛弃协议QDISCARD protocolQ,你仅需要忽略接受到的Q何数据即可。让我们直接从处理器QhandlerQ实现开始,q个处理器处理Netty的所有I/O事g?br />
Java代码
package org.jboss.netty.example.discard;

@ChannelPipelineCoverage("all")1
public class DiscardServerHandler extends SimpleChannelHandler {2

@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {3
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {4
e.getCause().printStackTrace();

Channel ch = e.getChannel();
ch.close();
}
}


代码说明

1)ChannelPipelineCoverage注解了一U处理器cdQ这个注解标CZ一个处理器?否可被多个Channel通道׃nQ同时关联着ChannelPipelineQ。DiscardServerHandler没有处理M有状态的信息Q?因此q里的注解是“all”?br />
2)DiscardServerHandlerl承了SimpleChannelHandlerQ这也是一个ChannelHandler 的实现。SimpleChannelHandler提供了多U你可以重写的事件处理方法。目前直接承SimpleChannelHandler已经_ 了,q不需要你完成一个自q处理器接口?br />
3)我们q里重写了messageReceived事g处理Ҏ。这个方法由一个接收了客户端传送数据的MessageEvent事g调用。在q个例子中,我们忽略接收到的M数据Qƈ以此来实C个抛弃协议(DISCARD protocolQ?br />
4)exceptionCaught 事g处理Ҏ׃个ExceptionEvent异常事g调用Q这个异怺件v因于Netty的I/O异常或一个处理器实现的内部异常。多数情况下Q捕?到的异常应当被记录下来,q在q个Ҏ中关闭这个channel通道。当然处理这U异常情늚Ҏ实现可能因你的实际需求而有所不同Q例如,在关闭这个连 接之前你可能会发送一个包含了错误码的响应消息?br />
目前q展不错Q我们已l完成了抛弃协议服务器的一半开发工作。下面要做的是完成一个可以启动这个包含DiscardServerHandler处理器服务的L法?br />


Java代码
package org.jboss.netty.example.discard;

import java.net.InetSocketAddress;
import java.util.concurrent.Executors;

public class DiscardServer {

public static void main(String[] args) throws Exception {
ChannelFactory factory =
new NioServerSocketChannelFactory (
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool());

ServerBootstrap bootstrap = new ServerBootstrap (factory);

DiscardServerHandler handler = new DiscardServerHandler();
ChannelPipeline pipeline = bootstrap.getPipeline();
pipeline.addLast("handler", handler);

bootstrap.setOption("child.tcpNoDelay", true);
bootstrap.setOption("child.keepAlive", true);

bootstrap.bind(new InetSocketAddress(8080));
}
}


代码说明


1)ChannelFactory 是一个创建和理Channel通道及其相关资源的工厂接口,它处理所有的I/Ohq生相应的I/O ChannelEvent通道事g。Netty 提供了多U?ChannelFactory 实现。这里我们需要实C个服务端的例子,因此我们使用NioServerSocketChannelFactory实现。另一仉要注意的事情是这个工 厂ƈ自己不负责创建I/OU程。你应当在其构造器中指定该工厂使用的线E池Q这样做的好处是你获得了更高的控制力来管理你的应用环境中使用的线E,例如一 个包含了安全理的应用服务?br />
2)ServerBootstrap 是一个设|服务的帮助cR你甚至可以在这个服务中直接讄一个Channel通道。然而请注意Q这是一个繁琐的q程Q大多数情况下ƈ不需要这样做?br />
3)q里Q我们将DiscardServerHandler处理器添加至默认的ChannelPipeline通道。Q何时候当服务器接收到一个新的连 接,一个新的ChannelPipeline道对象被创徏Qƈ且所有在q里d的ChannelHandler对象被d臌个新?ChannelPipeline道对象。这很像是一U浅拯操作Qa shallow-copy operationQ;所有的Channel通道以及其对应的ChannelPipeline实例分享相同的DiscardServerHandler 实例?br />
4)你也可以讄我们在这里指定的q个通道实现的配|参数。我们正在写的是一个TCP/IP服务Q因此我们运行设定一些socket选项Q例?tcpNoDelay和keepAlive。请注意我们在配|选项里添加的"child."前缀。这意味着q个配置仅适用于我们接收到的通道实例Q而不 是ServerSocketChannel实例。因此,你可以这L一个ServerSocketChannel讑֮参数Q?br />bootstrap.setOption("reuseAddress", true);

5)我们l箋。剩下要做的是绑定这个服务用的端口q且启动q个服务。这里,我们l定本机所有网卡(NICs,network interface cardsQ上?080端口。当Ӟ你现在也可以对应不同的绑定地址多次调用l定操作?br />
大功告成Q现在你已经完成你的W一个基于Netty的服务端E序?br />
1.3. 查看接收到的数据


现在你已l完成了你的W一个服务端E序Q我们需要测试它是否可以真正的工作。最单的Ҏ是用telnet 命o。例如,你可以在命o行中输入“telnet localhost 8080 ”或其他类型参数?br />
然而,我们可以认ؓ服务器在正常工作吗?׃q是一个丢球协议服务,所以实际上我们无法真正的知道。你最l将收不CQ何回应。ؓ了证明它在真正的工作Q让我们修改代码打印其接收到的数据?br />我们已经知道当完成数据的接收后将产生MessageEvent消息事gQƈ且也会触发messageReceived处理Ҏ。所以让我在DiscardServerHandler处理器的messageReceivedҎ内增加一些代码?br />
Java代码
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
ChannelBuffer buf = (ChannelBuffer) e.getMessage();
while(buf.readable()) {
System.out.println((char) buf.readByte());
}
}


代码说明

1) 基本上我们可以假定在socket的传输中消息cdLChannelBuffer。ChannelBuffer是Netty的一个基本数据结构,q个?据结构存储了一个字节序列。ChannelBuffercM于NIO的ByteBufferQ但是前者却更加的灵zd易于使用。例如,Netty允许你创 Z个由多个ChannelBuffer构徏的复合ChannelBuffercdQ这样就可以减少不必要的内存拯ơ数?br />
2) 虽然ChannelBuffer有些cM于NIO的ByteBufferQ但强烈你参考Netty的API手册。学会如何正的使用ChannelBuffer是无障碍使用Netty的关键一步?br />
如果你再ơ运行telnet命oQ你会看到你所接收到的数据?br />抛弃协议服务的所有源代码均存攑֜在分发版的org.jboss.netty.example.discard包下?br />

1.4. 响应协议服务


目前Q我们虽然用了数据Q但最l却未作M回应。然而一般情况下Q一个服务都需要回应一个请求。让我们实现ECHO协议 来学习如何完成一个客戯求的回应消息QECHO协议规定要返回Q何接收到的数据?br />
与我们上一节实现的抛弃协议服务唯一不同的地ҎQ这里需要返回所有的接收数据而不是仅仅打印在控制C上。因此我们再ơ修改messageReceivedҎp够了?br />
Java代码
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
Channel ch = e.getChannel();
ch.write(e.getMessage());
}
代码说明
1) 一个ChannelEvent通道事g对象自n存有一个和其关联的Channel对象引用。这个返回的Channel通道对象代表了这个接?MessageEvent消息事g的连接(connectionQ。因此,我们可以通过调用q个Channel通道对象的writeҎ向远E节点写入返 回数据?br />
现在如果你再ơ运行telnet命oQ你会看到服务器返回的你所发送的M数据?br />
相应服务的所有源代码存放在分发版的org.jboss.netty.example.echo包下?br />
1.5. 旉协议服务


q一节需要实现的协议是TIME协议 。这是一个与先前所介绍的不同的例子。这个例子里Q服务端q回一?2位的整数消息Q我们不接受h中包含的M数据q且当消息返回完毕后立即关闭q接。通过q个例子你将学会如何构徏和发送消息,以及当完成处理后如何d关闭q接?br />
因ؓ我们会忽略接收到的Q何数据而只是返回消息,q应当在建立q接后就立即开始。因此这ơ我们不再用messageReceivedҎQ取而代之的是用channelConnectedҎ。下面是具体的实玎ͼ



Java代码
package org.jboss.netty.example.time;

@ChannelPipelineCoverage("all")
public class TimeServerHandler extends SimpleChannelHandler {

@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) {
Channel ch = e.getChannel();

ChannelBuffer time = ChannelBuffers.buffer(4);
time.writeInt(System.currentTimeMillis() / 1000);

ChannelFuture f = ch.write(time);

f.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) {
Channel ch = future.getChannel();
ch.close();
}
});
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
e.getCause().printStackTrace();
e.getChannel().close();
}
}

代码说明
1) 正如我们解释q的QchannelConnectedҎ在一个连接徏立后立即触发。因此让我们在这个方法里完成一个代表当前时_U)?2位整数消息的构徏工作?br />
2) Z发送一个消息,我们需要分配一个包含了q个消息的buffer~冲。因为我们将要写入一?2位的整数Q因此我们需要一?字节?ChannelBuffer。ChannelBuffers是一个可以创建buffer~冲的帮助类。除了这个buffer?法,ChannelBuffersq提供了很多和ChannelBuffer相关的实用方法。更多信息请参考API手册?br />
另外Q一个很不错的方法是使用静态的导入方式Q?br />import static org.jboss.netty.buffer.ChannelBuffers.*;
...
ChannelBuffer dynamicBuf = dynamicBuffer(256);
ChannelBuffer ordinaryBuf = buffer(1024);

3) 像通常一P我们需要自己构造消息?br />
但是打住Qflip在哪Q过L们在使用NIO发送消息时不是常常需要调?ByteBuffer.flip()Ҏ吗?实际上ChannelBuffer之所以不需要这个方法是因ؓ ChannelBuffer有两个指针;一个对应读操作Q一个对应写操作。当你向一?ChannelBuffer写入数据的时候写指针的烦引g会增加,但与此同时读指针的烦引g会有M变化。读写指针的索引值分别代表了q个消息的开 始、结束位|?br />
与之相应的是QNIO的buffer~冲没有为我们提供如此简z的一U方法,除非你调用它的flipҎ。因此,当你忘记调用flipҎ而引起发送错?Ӟ你便会陷入困境。这L错误不会再Netty中发生,因ؓ我们对应不同的操作类型有不同的指针。你会发现就像你已习惯的q样q程变得更加Ҏ—一U没 有flippling的体验!

另一炚w要注意的是这个写Ҏq回了一个ChannelFuture对象。一个ChannelFuture 对象代表了一个尚未发生的I/O操作。这意味着QQ何已h的操作都可能是没有被立即执行的,因ؓ在Netty内部所有的操作都是异步的。例如,下面的代 码可能会关闭一 个连接,q个操作甚至会发生在消息发送之前:

Channel ch = ...;
ch.write(message);
ch.close();

因此Q你需要这个writeҎq回的ChannelFuture对象QcloseҎ需要等待写操作异步完成之后的ChannelFuture通知/监听触发。需要注意的是,关闭Ҏ仍旧不是立即关闭一个连接,它同样也是返回了一个ChannelFuture对象?br />
4) 在写操作完成之后我们又如何得到通知Q这个只需要简单的个返回的ChannelFuture对象增加一个ChannelFutureListener 卛_。在q里我们创徏了一个匿名ChannelFutureListener对象Q在q个ChannelFutureListener对象内部我们处理?异步操作完成之后的关闭操作?br />
另外Q你也可以通过使用一个预定义的监听类来简化代码?br />f.addListener(ChannelFutureListener.CLOSE);


1.6. 旉协议服务客户?br />

不同于DISCARD和ECHO协议服务Q我们需要一个时间协议服务的客户端,因ؓZ无法直接一?2位的二进制数据{换一个日历时间。在q一节我们将学习如何保服务器端工作正常Q以及如何用Netty完成客户端的开发?br />
使用Netty开发服务器端和客户端代码最大的不同是要求用不同的Bootstrap及ChannelFactory。请参照以下的代码:

Java代码
package org.jboss.netty.example.time;

import java.net.InetSocketAddress;
import java.util.concurrent.Executors;

public class TimeClient {

public static void main(String[] args) throws Exception {
String host = args[0];
int port = Integer.parseInt(args[1]);

ChannelFactory factory =
new NioClientSocketChannelFactory (
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool());

ClientBootstrap bootstrap = new ClientBootstrap (factory);

TimeClientHandler handler = new TimeClientHandler();
bootstrap.getPipeline().addLast("handler", handler);

bootstrap.setOption("tcpNoDelay" , true);
bootstrap.setOption("keepAlive", true);

bootstrap.connect (new InetSocketAddress(host, port));
}
}
代码说明
1) 使用NioClientSocketChannelFactory而不是NioServerSocketChannelFactory来创建客L的Channel通道对象?br />
2) 客户端的ClientBootstrap对应ServerBootstrap?br />
3) h意,q里不存在?#8220;child.”前缀的配|项Q客L的SocketChannel实例不存在父UChannel对象?br />
4) 我们应当调用connectq接ҎQ而不是之前的bindl定Ҏ?br />
正如你所看到的,q与服务端的启动q程是完全不一L。ChannelHandler又该如何实现呢?它应当负责接收一?2位的整数Q将其{换ؓ可读的格式后Q打印输出时_q关闭这个连接?br />


Java代码
package org.jboss.netty.example.time;

import java.util.Date;

@ChannelPipelineCoverage("all")
public class TimeClientHandler extends SimpleChannelHandler {

@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
ChannelBuffer buf = (ChannelBuffer) e.getMessage();
long currentTimeMillis = buf.readInt() * 1000L;
System.out.println(new Date(currentTimeMillis));
e.getChannel().close();
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
e.getCause().printStackTrace();
e.getChannel().close();
}
}

q看h很是单,与服务端的实Cq未有什么不同。然而,q个处理器却时常会因为抛出IndexOutOfBoundsException异常而拒l工作。我们将在下一节讨个问题生的原因?br />
1.7. 数据的传输处理



1.7.1. Socket Buffer的缺?br />

对于例如TCP/IPq种Z的传输协议实现Q接收到的数据会被存储在socket的接受缓冲区内。不q的是,q种Z的传输~冲区ƈ不是一个包?列,而是一个字节队列。这意味着Q即使你以两个数据包的Ş式发送了两条消息Q操作系l却不会把它们看成是两条消息Q而仅仅是一个批ơ的字节序列。因此,?q种情况下我们就无法保证收到的数据恰好就是远E节Ҏ发送的数据。例如,让我们假设一个操作系l的TCP/IP堆栈收到了三个数据包Q?br />


+-----+-----+-----+
| ABC | DEF | GHI |
+-----+-----+-----+

׃q种传输协议的普遍性质Q在你的应用中有较高的可能会把这些数据读取ؓ另外一UŞ式:



+----+-------+---+---+
| AB | CDEFG | H | I |
+----+-------+---+---+


因此对于数据的接收方Q不是服务端还是客LQ应当重构这些接收到的数据,让其变成一U可让你的应用逻辑易于理解的更有意义的数据l构。在上面所q的q个例子中,接收到的数据应当重构Z面的形式Q?br />


+-----+-----+-----+
| ABC | DEF | GHI |
+-----+-----+-----+

1.7.2. W一U方?br />

现在让我们回到时间协议服务客L的例子中。我们在q里遇到了同L问题。一?2位的整数是一个非常小的数据量Q因此它常常不会被切分在不同的数据段内。然而,问题是它实可以被切分在不同的数据段内,q且q种可能性随着量的增加而提高?br />
最单的Ҏ是在E序内部创徏一个可准确接收4字节数据的篏U性缓册Ӏ下面的代码是修复了q个问题后的TimeClientHandler实现?br />


Java代码
package org.jboss.netty.example.time;

import static org.jboss.netty.buffer.ChannelBuffers.*;

import java.util.Date;

@ChannelPipelineCoverage("one")
public class TimeClientHandler extends SimpleChannelHandler {

private final ChannelBuffer buf = dynamicBuffer();

@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
ChannelBuffer m = (ChannelBuffer) e.getMessage();
buf.writeBytes(m);

if (buf.readableBytes() >= 4) {
long currentTimeMillis = buf.readInt() * 1000L;
System.out.println(new Date(currentTimeMillis));
e.getChannel().close();
}
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
e.getCause().printStackTrace();
e.getChannel().close();
}
}


代码说明

1) q一ơ我们?#8220;one”做ؓChannelPipelineCoverage的注解倹{这是由于这个修改后的TimeClientHandler不在?在内部保持一个buffer~冲Q因此这个TimeClientHandler实例不可以再被多个Channel通道或ChannelPipeline?享。否则这个内部的buffer~冲无法缓冲正的数据内容?br />
2) 动态的buffer~冲也是ChannelBuffer的一U实玎ͼ其拥有动态增加缓冲容量的能力。当你无法预估消息的数据长度Ӟ动态的buffer~冲是一U很有用的缓冲结构?br />
3) 首先Q所有的数据会被篏U的~冲至buf容器?br />
4) 之后Q这个处理器会查是否收C_的数据然后再q行真实的业务逻辑处理Q在q个例子中需要接?字节数据。否则,Netty重复调用messageReceivedҎQ直?字节数据接收完成?br />
q里q有另一个地斚w要进行修攏V你是否q记得我们把TimeClientHandler实例dCq个ClientBootstrap实例的默 认ChannelPipeline道里?q意味着同一个TimeClientHandler实例被多个Channel通道׃nQ因此接受的数据也将?到破坏。ؓ了给每一个Channel通道创徏一个新的TimeClientHandler实例Q我们需要实C?ChannelPipelineFactory道工厂Q?br />
Java代码
package org.jboss.netty.example.time;

public class TimeClientPipelineFactory implements ChannelPipelineFactory {

public ChannelPipeline getPipeline() {
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("handler", new TimeClientHandler());
return pipeline;
}
}

现在Q我们需要把TimeClient下面的代码片D:

Java代码
TimeClientHandler handler = new TimeClientHandler();
bootstrap.getPipeline().addLast("handler", handler);

替换为:

Java代码
bootstrap.setPipelineFactory(new TimeClientPipelineFactory());

虽然q看上去有些复杂Qƈ且由于在TimeClient内部我们只创Z一个连接(connectionQ,因此我们在这里确实没必要引入TimeClientPipelineFactory实例?br />
然而,当你的应用变得越来越复杂Q你M需要实现自qChannelPipelineFactoryQ这个管道工厂将会o你的道配置变得更加h灉|性?br />
1.7.3. W二U方?br />


虽然W二U方案解决了旉协议客户端遇到的问题Q但是这个修改后的处理器实现看上d不再那么z。设想一U更为复杂的Q由多个可变长度字段l成的协议。你的ChannelHandler实现变得越来越难以l护?br />
正如你已注意到的Q你可以Z个ChannelPipelined多个ChannelHandlerQ因此,Z减小应用的复杂性,你可以把q个臃肿?ChannelHandler切分为多个独立的模块单元。例如,你可以把TimeClientHandler切分Z个独立的处理器:

TimeDecoderQ解x据分D늚问题?br /> TimeClientHandlerQ原始版本的实现?br />q运的是QNetty提供了一个可扩展的类Q这个类可以直接拿过来用帮你完成TimeDecoder的开发:

Java代码
package org.jboss.netty.example.time;


public class TimeDecoder extends FrameDecoder {

@Override
protected Object decode(
ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) {

if (buffer.readableBytes() < 4) {
return null;
}

return buffer.readBytes(4);
}
}


代码说明
1) q里不再需要用ChannelPipelineCoverage的注解,因ؓFrameDecoderL被注解ؓ“one”?br />
2) 当接收到新的数据后,FrameDecoder会调用decodeҎQ同时传入一个FrameDecoder内部持有的篏U型buffer~冲?br />
3) 如果decodeq回null|q意味着q没有接收到_的数据。当有够数量的数据后FrameDecoder会再ơ调用decodeҎ?br />
4) 如果decodeҎq回一个非I|q意味着decodeҎ已经成功完成一条信息的解码。FrameDecoder丢弃这个内部的累计型缓册Ӏ请?意你不需要对多条消息q行解码QFrameDecoder保持对decodeҎ的调用,直到decodeҎq回非空对象?br />
如果你是一个勇于尝试的人,你或许应当用ReplayingDecoderQReplayingDecoder更加化了解码的过E。ؓ此你需要查看API手册获得更多的帮助信息?br />
Java代码
package org.jboss.netty.example.time;

public class TimeDecoder extends ReplayingDecoder<VoidEnum> {

@Override
protected Object decode(
ChannelHandlerContext ctx, Channel channel,
ChannelBuffer buffer, VoidEnum state) {

return buffer.readBytes(4);
}
}

此外QNettyqؓ你提供了一些可以直接用的decoder实现Q这些decoder实现不仅可以让你非常Ҏ的实现大多数协议Qƈ且还会帮你避免某些臃ѝ难以维护的处理器实现。请参考下面的代码包获得更加详l的实例Q?br />
org.jboss.netty.example.factorial for a binary protocol, and
org.jboss.netty.example.telnet for a text line-based protocol
1.8. 使用POJO代替ChannelBuffer


目前为止所有的实例E序都是使用ChannelBuffer做ؓ协议消息的原始数据结构。在q一节,我们改q时间协议服务的客户/服务端实玎ͼ使用POJO 而不是ChannelBuffer做ؓ协议消息的原始数据结构?br />
在你的ChannelHandler实现中用POJO的优势是很明昄Q从你的ChannelHandler实现中分MChannelBuffer?取数据的代码Q将有助于提高你的ChannelHandler实现的可l护性和可重用性。在旉协议服务的客?服务端代码中Q直接?ChannelBufferd一?2位的整数q不是一个主要的问题。然而,你会发现Q当你试囑֮C个真实的协议的时候,q种代码上的分离是很有必?的?br />
首先Q让我们定义一个称之ؓUnixTime的新cd?br />
Java代码
package org.jboss.netty.example.time;

import java.util.Date;

public class UnixTime {
private final int value;

public UnixTime(int value) {
this.value = value;
}

public int getValue() {
return value;
}

@Override
public String toString() {
return new Date(value * 1000L).toString();
}
}

现在让我们重C改TimeDecoder实现Q让其返回一个UnixTimeQ而不是一个ChannelBuffer?br />
Java代码
@Override
protected Object decode(
ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) {
if (buffer.readableBytes() < 4) {
return null;
}

return new UnixTime(buffer.readInt());
}

FrameDecoder和ReplayingDecoder允许你返回一个Q何类型的对象。如果它们仅允许q回一个ChannelBufferc?型的对象Q我们将不得不插入另一个可以从ChannelBuffer对象转换 为UnixTime对象的ChannelHandler实现?br />

有了q个修改后的decoder实现Q这个TimeClientHandler便不会再依赖ChannelBuffer?br />
Java代码
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
UnixTime m = (UnixTime) e.getMessage();
System.out.println(m);
e.getChannel().close();
}

更加单优雅了Q不是吗Q同L技巧也可以应用在服务端Q让我们现在更新TimeServerHandler的实玎ͼ

Java代码
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) {
UnixTime time = new UnixTime(System.currentTimeMillis() / 1000);
ChannelFuture f = e.getChannel().write(time);
f.addListener(ChannelFutureListener.CLOSE);
}

现在剩下的唯一需要修改的部分是这个ChannelHandler实现Q这个ChannelHandler实现需要把一个UnixTime对象重新 转换Z个ChannelBuffer。但q却已是相当单了Q因为当你对消息q行~码的时候你不再需要处理数据包的拆分及l装?br />
Java代码
package org.jboss.netty.example.time;

import static org.jboss.netty.buffer.ChannelBuffers.*;

@ChannelPipelineCoverage("all")
public class TimeEncoder extends SimpleChannelHandler {

public void writeRequested(ChannelHandlerContext ctx, MessageEvent e) {
UnixTime time = (UnixTime) e.getMessage();

ChannelBuffer buf = buffer(4);
buf.writeInt(time.getValue());

Channels.write(ctx, e.getFuture(), buf);
}
}

代码说明

1) 因ؓq个encoder是无状态的Q所以其使用的ChannelPipelineCoverage注解值是“all”。实际上Q大多数encoder实现都是无状态的?br />
2) 一个encoder通过重写writeRequestedҎ来实现对写操作请求的拦截。不q请注意虽然q个writeRequestedҎ使用了和 messageReceivedҎ一LMessageEvent参数Q但是它们却分别对应了不同的解释。一个ChannelEvent事g可以既是一 个上升流事gQupstream eventQ也可以是一个下降流事gQdownstream eventQ,q取决于事g的方向。例如:一个MessageEvent消息事g可以作ؓ一个上升流事gQupstream eventQ被messageReceivedҎ调用Q也可以作ؓ一个下降流事gQdownstream eventQ被writeRequestedҎ调用。请参考API手册获得上升事Ӟupstream eventQ和下降事Ӟdownstream eventQ的更多信息?br />
3) 一旦完成了POJO和ChannelBuffer转换Q你应当保把这个新的buffer~冲转发臛_前的 ChannelDownstreamHandler处理Q这个下降通道的处理器由某个ChannelPipeline理。Channels提供了多个可 以创建和发送ChannelEvent事g的帮助方法。在q个例子中,Channels.write(...)Ҏ创徏了一个新?MessageEvent事gQƈ把这个事件发送给了先前的处于某个ChannelPipeline内的 ChannelDownstreamHandler处理器?br />
另外Q一个很不错的方法是使用静态的方式导入Channelsc:

import static org.jboss.netty.channel.Channels.*;
...
ChannelPipeline pipeline = pipeline();
write(ctx, e.getFuture(), buf);
fireChannelDisconnected(ctx);


最后的d是把q个TimeEncoder插入服务端的ChannelPipelineQ这是一个很单的步骤?br />
1.9. 关闭你的应用


如果你运行了TimeClientQ你肯定可以注意刎ͼq个应用q没有自动退只是在那里保持着无意义的q行。跟t堆栈记录你可以发现Q这里有一些运?状态的I/OU程。ؓ了关闭这些I/OU程q让应用优雅的退出,你需要释放这些由ChannelFactory分配的资源?br />
一个典型的|络应用的关闭过E由以下三步l成Q?br />
关闭负责接收所有请求的server socket?br />关闭所有客Lsocket或服务端为响应某个请求而创建的socket?br />释放ChannelFactory使用的所有资源?br />Z让TimeClient执行q三步,你需要在TimeClient.main()Ҏ内关闭唯一的客戯接以及ChannelFactory使用的所有资源,q样做便可以优雅的关闭这个应用?br />
Java代码
package org.jboss.netty.example.time;

public class TimeClient {
public static void main(String[] args) throws Exception {
...
ChannelFactory factory = ...;
ClientBootstrap bootstrap = ...;
...
ChannelFuture future = bootstrap.connect(...);
future.awaitUninterruptible();
if (!future.isSuccess()) {
future.getCause().printStackTrace();
}
future.getChannel().getCloseFuture().awaitUninterruptibly();
factory.releaseExternalResources();
}
}
代码说明
1) ClientBootstrap对象的connectҎq回一个ChannelFuture对象Q这个ChannelFuture对象告知这个连接操 作的成功或失败状态。同时这个ChannelFuture对象也保存了一个代表这个连接操作的Channel对象引用?br />
2) d式的{待Q直到ChannelFuture对象q回q个q接操作的成功或p|状态?br />
3) 如果q接p|Q我们将打印q接p|的原因。如果连接操作没有成功或者被取消QChannelFuture对象的getCause()Ҏ返回连接失败的原因?br />
4) 现在Q连接操作结束,我们需要等待ƈ且一直到q个Channel通道q回的closeFuture关闭q个q接。每一个Channel都可获得自己的closeFuture对象Q因此我们可以收到通知q在q个关闭旉Ҏ行某U操作?br />
q且即ɘq个q接操作p|Q这个closeFuture仍旧会收到通知Q因个代表连接的 Channel对象会在连接操作失败后自动关闭?br />
5) 在这个时间点Q所有的q接已被关闭。剩下的唯一工作是释放ChannelFactory通道工厂使用的资源。这一步仅需要调?releaseExternalResources()Ҏ卛_。包括NIO Secector和线E池在内的所有资源将被自动的关闭和终止?br />
关闭一个客L应用是很单的Q但又该如何关闭一个服务端应用呢?你需要释攑օl定的端口ƈ关闭所有接受和打开的连接。ؓ了做到这一点,你需要用一U数据结构记录所有的zdq接Q但q却q不是一件容易的事。幸q的是,q里有一U解x案,ChannelGroup?br />
ChannelGroup是Java 集合 API的一个特有扩展,ChannelGroup内部持有所有打开状态的Channel通道。如果一个Channel通道对象被加入到 ChannelGroupQ如果这个Channel通道被关闭,ChannelGroup自动移除这个关闭的Channel通道对象。此外,你还可以?一个ChannelGroup对象内部的所有Channel通道对象执行相同的操作。例如,当你关闭服务端应用时你可以关闭一个ChannelGroup 内部的所有Channel通道对象?br />
Z记录所有打开的socketQ你需要修改你的TimeServerHandler实现Q将一个打开的Channel通道加入全局的ChannelGroup对象QTimeServer.allChannels:

Java代码
@Override
public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) {
TimeServer.allChannels.add(e.getChannel());
}
代码说明
是的QChannelGroup是线E安全的?br />
现在Q所有活动的Channel通道被自动的维护,关闭一个服务端应用有如关闭一个客L应用一L单?br />
Java代码
package org.jboss.netty.example.time;

public class TimeServer {

static final ChannelGroup allChannels = new DefaultChannelGroup("time-server" );

public static void main(String[] args) throws Exception {
...
ChannelFactory factory = ...;
ServerBootstrap bootstrap = ...;
...
Channel channel = bootstrap.bind(...);
allChannels.add(channel);
waitForShutdownCommand();
ChannelGroupFuture future = allChannels.close();
future.awaitUninterruptibly();
factory.releaseExternalResources();
}
}
代码说明
1) DefaultChannelGroup需要一个组名作为其构造器参数。这个组名仅是区分每个ChannelGroup的一个标C?br />
2) ServerBootstrap对象的bindҎq回了一个绑定了本地地址的服务端Channel通道对象。调用这个Channel通道的close()Ҏ释放这个Channel通道l定的本地地址?br />
3) 不管q个Channel对象属于服务端,客户端,q是为响应某一个请求创建,M一U类型的Channel对象都会被加入ChannelGroup。因此,你尽可在关闭服务时关闭所有的Channel对象?br />
4) waitForShutdownCommand()是一个想象中{待关闭信号的方法。你可以在这里等待某个客L的关闭信h者JVM的关闭回调命令?br />
5) 你可以对ChannelGroup理的所有Channel对象执行相同的操作。在q个例子里,我们关闭所有的通道Q这意味着l定在服务端特定地址?Channel通道解除绑定,所有已建立的连接也异步关闭。ؓ了获得成功关闭所有连接的通知Qclose()Ҏ返回一?ChannelGroupFuture对象Q这是一个类似ChannelFuture的对象?br />
1.10. 总述


在这一章节Q我们快速浏览ƈC了如何用Netty开发网l应用。下一章节涉及更多的问题。同时请CQؓ了帮助你以及能够让NettyZ你的回馈得到持箋的改q和提高QNettyC֌ 永q欢q你的问题及?br />


W二? 架构总览

在这个章节,我们阐qNetty提供的核心功能以及在此基之上如何构徏一个完备的|络应用?br />
2.1. 丰富的缓冲实?br />

Netty使用自徏的buffer APIQ而不是用NIO的ByteBuffer来代表一个连l的字节序列。与ByteBuffer相比q种方式拥有明显的优ѝNetty使用新的 buffercdChannelBufferQChannelBuffer被设计ؓ一个可从底层解决ByteBuffer问题Qƈ可满x常网l应用开?需要的~冲cd。这些很LҎ包括:



如果需要,允许使用自定义的~冲cd?br />复合~冲cd中内|的透明的零拯实现?br />开即用的动态缓冲类型,h像StringBuffer一L动态缓冲能力?br />不再需要调用的flip()Ҏ?br />正常情况下具有比ByteBuffer更快的响应速度?br />更多信息请参考:org.jboss.netty.buffer package description

2.2. l一的异?I/O API


传统的Java I/O API在应对不同的传输协议旉要用不同的cd和方法。例如:java.net.Socket ?java.net.DatagramSocket它们q不h相同的超cdQ因此,q就需要用不同的调用方式执行socket操作?br />
q种模式上的不匹配得在更换一个网l应用的传输协议时变得繁杂和困难。由于(Java I/O APIQ缺乏协议间的移植性,当你试图在不修改|络传输层的前提下增加多U协议的支持Q这时便会生问题。ƈ且理ZԌ多种应用层协议可q行在多U传?层协议之上例如TCP/IP,UDP/IP,SCTP和串口通信?br />
让这U情况变得更p的是,Java新的I/OQNIOQAPI与原有的d式的I/OQOIOQAPIq不兼容QNIO.2(AIO)也是如此。由于所有的API无论是在其设计上q是性能上的Ҏ都与彼此不同,在进入开发阶D,你常怼被迫的选择一U你需要的API?br />
例如Q在用户数较的时候你可能会选择使用传统的OIO(Old I/O) APIQ毕竟与NIO相比使用OIO更加容易一些。然而,当你的业务呈指数增长q且服务器需要同时处理成千上万的客户q接时你便会遇到问题。这U情况下 你可能会试使用NIOQ但是复杂的NIO Selector~程接口又会耗费你大量时间ƈ最l会ȝ你的快速开发?br />
Netty有一个叫做Channel的统一的异步I/O~程接口Q这个编E接口抽象了所有点对点的通信操作。也是_如果你的应用是基于Netty的某 一U传输实玎ͼ那么同样的,你的应用也可以运行在Netty的另一U传输实C。Netty提供了几U拥有相同编E接口的基本传输实现Q?br />


NIO-based TCP/IP transport (See org.jboss.netty.channel.socket.nio),
OIO-based TCP/IP transport (See org.jboss.netty.channel.socket.oio),
OIO-based UDP/IP transport, and
Local transport (See org.jboss.netty.channel.local).
切换不同的传输实现通常只需对代码进行几行的修改调整Q例如选择一个不同的ChannelFactory实现?br />
此外Q你甚至可以利用新的传输实现没有写入的优势,只需替换一些构造器的调用方法即可,例如串口通信。而且׃核心APIh高度的可扩展性,你还可以完成自己的传输实现?br />
2.3. Z拦截链模式的事g模型


一个定义良好ƈh扩展能力的事件模型是事g驱动开发的必要条g。Nettyh定义良好的I/O事g模型。由于严格的层次l构区分了不同的事gcdQ因 此Netty也允怽在不破坏现有代码的情况下实现自己的事件类型。这是与其他框架相比另一个不同的地方。很多NIO框架没有或者仅有有限的事g模型?念;在你试图d一个新的事件类型的时候常帔R要修改已有的代码Q或者根本就不允怽q行q种扩展?br />
在一个ChannelPipeline内部一个ChannelEvent被一lChannelHandler处理。这个管道是拦截qo?模式的一U高UŞ式的实现Q因此对于一个事件如何被处理以及道内部处理器间的交互过E,你都拥有绝对的控制力。例如,你可以定义一个从socketd到数据后的操作:

Java代码
public class MyReadHandler implements SimpleChannelHandler {
public void messageReceived(ChannelHandlerContext ctx, MessageEvent evt) {
Object message = evt.getMessage();
// Do something with the received message.
...

// And forward the event to the next handler.
ctx.sendUpstream(evt);
}
}

同时你也可以定义一U操作响应其他处理器的写操作hQ?br />
Java代码
public class MyWriteHandler implements SimpleChannelHandler {
public void writeRequested(ChannelHandlerContext ctx, MessageEvent evt) {
Object message = evt.getMessage();
// Do something with the message to be written.
...

// And forward the event to the next handler.
ctx.sendDownstream(evt);
}
}

有关事g模型的更多信息,请参考API文档ChannelEvent和ChannelPipeline部分?br />
2.4. 适用快速开发的高lg


上述所提及的核心组件已l够实现各U类型的|络应用Q除此之外,Netty也提供了一pd的高U组件来加速你的开发过E?br />
2.4.1. Codec框架


像“1.8. 使用POJO代替ChannelBuffer”一节所展示的那P从业务逻辑代码中分d议处理部分L一个很不错的想法。然而如果一切从零开始便会遭?到实C的复杂性。你不得不处理分D늚消息。一些协议是多层的(例如构徏在其他低层协议之上的协议Q。一些协议过于复杂以致难以在一C机(single state machineQ上实现?br />
因此Q一个好的网l应用框架应该提供一U可扩展Q可重用Q可单元试q且是多层的codec框架Qؓ用户提供易维护的codec代码?br />
Netty提供了一l构建在其核心模块之上的codec实现Q这些简单的或者高U的codec实现帮你解决了大部分在你q行协议处理开发过E会遇到的问题,无论q些协议是简单的q是复杂的,二进制的或是单文本的?br />
2.4.2. SSL / TLS 支持


不同于传l阻塞式的I/O实现Q在NIO模式下支持SSL功能是一个艰隄工作。你不能只是单的包装一下流数据q进行加密或解密工作Q你不得不借助?javax.net.ssl.SSLEngineQSSLEngine是一个有状态的实现Q其复杂性不亚于SSL自n。你必须理所有可能的状态,例如?码套Ӟ密钥协商Q或重新协商Q,证书交换以及认证{。此外,与通常期望情况相反的是SSLEngine甚至不是一个绝对的U程安全实现?br />
在Netty内部QSslHandler装了所有艰隄l节以及使用SSLEngine可能带来的陷阱。你所做的仅是配置q将该SslHandler插入C的ChannelPipeline中。同样Netty也允怽实现像StartTlS 那样所拥有的高U特性,q很Ҏ?br />
2.4.3. HTTP实现


HTTP无疑是互联网上最受欢q的协议Qƈ且已l有了一些例如Servlet容器q样的HTTP实现。因此,Z么Nettyq要在其核心模块之上构徏一套HTTP实现Q?br />
与现有的HTTP实现相比Netty的HTTP实现是相当与众不同的。在HTTP消息的低层交互过E中你将拥有l对的控制力。这是因为Netty?HTTP实现只是一些HTTP codec和HTTP消息cȝ单组合,q里不存在Q何限?#8212;—例如那种被迫选择的线E模型。你可以随心所Ʋ的~写那种可以完全按照你期望的工作方式工作 的客L或服务器端代码。这包括U程模型Q连接生命期Q快~码Q以及所有HTTP协议允许你做的,所有的一切,你都拥有绝对的控制力?br />
׃q种高度可定制化的特性,你可以开发一个非帔R效的HTTP服务器,例如Q?br />
要求持久化链接以及服务器端推送技术的聊天服务Qe.g. Comet Q?br />需要保持链接直x个文件下载完成的媒体服务(e.g. 2时长的电媄Q?br />需要上传大文gq且没有内存压力的文件服务(e.g. 上传1GB文g的请求)
支持大规模mash-up应用以及C万计q接的第三方web services异步处理q_
2.4.4. Google Protocol Buffer 整合


Google Protocol Buffers 是快速实C个高效的二进制协议的理想Ҏ。通过使用ProtobufEncoder和ProtobufDecoderQ你可以把Google Protocol Buffers ~译?(protoc)生成的消息类攑օ到Netty的codec实现中。请参?#8220;LocalTime ”实例Q这个例子也同时昄出开发一个由单协议定?的客户及服务端是多么的容易?br />
2.5. 总述
在这一章节Q我们从功能Ҏ的角度回顾了Netty的整体架构。Netty有一个简单却不失强大的架构。这个架构由三部分组?#8212;—~冲QbufferQ, 通道QchannelQ,事g模型Qevent modelQ?#8212;—所有的高Ҏ都构徏在这三个核心lg之上。一旦你理解了它们之间的工作原理Q你便不隄解在本章要提及的更多高Ҏ?br />
你可能对Netty的整体架构以及每一部分的工作原理仍旧存有疑问。如果是q样Q最好的方式是告诉我?应该如何改进q䆾指南

]]>
parseInt?8,09bughttp://www.tkk7.com/titanaly/archive/2012/11/08/391014.html不高?/dc:creator>不高?/author>Thu, 08 Nov 2012 05:49:00 GMThttp://www.tkk7.com/titanaly/archive/2012/11/08/391014.htmlhttp://www.tkk7.com/titanaly/comments/391014.htmlhttp://www.tkk7.com/titanaly/archive/2012/11/08/391014.html#Feedback4http://www.tkk7.com/titanaly/comments/commentRss/391014.htmlhttp://www.tkk7.com/titanaly/services/trackbacks/391014.html     parseInt("08")=0;
    parseInt("09")=0;

q里先回一下parseInt的函数声?

/*
 * 字W串解析成数字时Q从左向右依此解析,解析到第一个非法字W即停止?br />  * 若指定radix?-36之间的数字,则按相应的进制进行解析;
 * 若radix指定?Q或大于36的数字,则直接返回NaN
 * 若指定radix?Q或未指定radixQ则Ҏ字符串开头字W确?
 *  ?1-9'开头的字符Ԍ?0q制解析Q?br />  *  ?0'开头的字符Ԍ?q制解析Q?br />  *  ?0x'?0X'开头的字符Ԍ?6q制解析?br />  * 
 * @param string 要被解析的字W串?br />  * @param radix  表示要解析的数字的基数。该g?nbsp;2 ~ 36 之间?br />  
*/
parseInt(string, radix)


 ‘01’?#8217;07’Q按8q制?0q制解析会得到相同的l果??#8217;08’Q?#8217;09’?q制解析会得?#8217;0’Q因?#8217;8’?#8217;9’?q制中是非法字符Q不会被解析。由此导致上q的bug?br />扑ֈ问题ҎQ修复就变得很简单了Q显C指定radix?0.
parseInt("08",10);


]]>
Eclipse调试Java?0个技??http://www.tkk7.com/titanaly/archive/2012/10/19/389881.html不高?/dc:creator>不高?/author>Fri, 19 Oct 2012 08:54:00 GMThttp://www.tkk7.com/titanaly/archive/2012/10/19/389881.htmlhttp://www.tkk7.com/titanaly/comments/389881.htmlhttp://www.tkk7.com/titanaly/archive/2012/10/19/389881.html#Feedback5http://www.tkk7.com/titanaly/comments/commentRss/389881.htmlhttp://www.tkk7.com/titanaly/services/trackbacks/389881.html在本教程中,我们看C用Eclipse调试Java应用E序。调试可以帮助我们识别和解决应用E序中的~陷。我们将重点攑֜q行旉的问题,而不是编译时错误。有提供像gdb的命令行调试器。在本教E中Q我们将集中在基于GUI的调试,我们把我们最喜爱的IDE Eclipse来运行,通过本教E。虽然我们说的EclipseQ点大多是通用的,适用于调试用的IDE像NetBeans?/p>

在看q篇文章前,我推荐你看一?/span>Eclipse 快捷键手?/span>Q你也可以到q儿Q?a style="color: #ff9900; text-decoration: none; ">下蝲PDF文g我的eclipse版本?.2 Juno?/span>

0.三点特别提醒Q?/h2>
  • 不要使用System.out.println作ؓ调试工具
  • 启用所有组件的详细的日志记录?/span>
  • 使用一个日志分析器来阅L?/span>

[

QSystem.out.println()对开发h员来_有时候也许可以是一U调试手D,但是目一旦完成他没有什么用途了Q就变成垃圾了,得必L释或删除掉,q样会比较麻烦。启用所有组件的详细日志记录U别Q运用日志分析器来记录详l系l运行状?q对后期|站的优化和l护会有很多作用。)q仅仅是个h理解Q仅供参考!

]


1.条g断点

惌一下我们^时如何添加断点,通常的做法是双击行号的左辏V在debug视图中,BreakPoint View所有断炚w列出来,但是我们可以d一个booleancd的条件来军_断点是否被蟩q。如果条件ؓ真,在断点处E序停止,否则断点被蟩q,E序l箋执行?/span>

2.异常断点

在断点view中有一个看h像JQ的按钮Q我们可以用它d一个基于异常的断点Q例如我们希望当NullPointerException抛出的时候程序暂停,我们可以q样:

3.观察?/h2>

q是一个很好的功能Q?span style="font-family: 微Y雅黑, Verdana, sans-serif, 宋体; line-height: 22px; font-size: 14px; ">他允许当一个选定的属性被讉K或者被更改的时候程序执行暂停,q进行debug。最单的办法是在cM声明成员变量的语句行号左边双击,可以加入一个观察点?/span>

4.查看变量

在选中的变量上使用Ctrl+Shift+d 或?Ctrl+Shift+i可以查看变量|另外我们q可以在Expressions View中添加监视?/span>

5.更改变量的?/h2>

我们可以在Debug的时候改变其中变量的倹{在Variables View中可以按下图所C操作?/span>

6.在主Ҏ停止

在Run/Debug讄中,我们可以按如下图所C的启用q个Ҏ。程序将会在mainҎ的第一行停?/span>

7.环境变量

我们可以很方便的在Edit Conriguration对话框中d环境变量

8.跛_函数到选定?/h2>

q个功能非常P是我W二个非常喜Ƣ的功能QDrop to frame是_可以重新跛_当前Ҏ的开始处重新执行Qƈ且所有上下文变量的g回到那个时候。不一定是当前ҎQ可以点d前调用栈中的M一个frame跛_那里Q除了最开始的那个frameQ。主要用途是所有变量状态快速恢复到Ҏ开始时候的样子重新执行一遍,卛_以一遍又一遍地在那个你x的上下文中进行多ơ调试(l合改变变量值等其它功能Q,而不用重来一遍调试到哪里了。当Ӟ原来执行q程中生的副作用是不可逆的Q比如你往数据库中插入了一条记录)?/span>

9.分步qo

当我们在调试的时候摁F5进入方法的内部Q但q有个缺Ҏ的时候可能会q入C些库的内部(例如JDKQ,可能q不是我们想要的Q我们可以在Preferences中添加一个过滤器Q排除指定的包?/span>

10.跛_Q蟩q和q回

其实q个技巧是debug最基本的知识?/span>
  • F5-Step IntoQ移动到下一步,如果当前的行是一个方法调用,进入这个方法的W一行。(可以通过W九条来排除Q?/span>
  • F6-Step OverQ移动到下一行。如果当前行有方法调用,q个Ҏ被执行完毕q回Q然后到下一行?/span>
  • F7-Step ReturnQl执行当前方法,当当前方法执行完毕的时候,控制{到当前方法被调用的行?/span>
  • F8-UdC一个断点处?/span>




]]>
iospȝ|页上的安装包直接安?/title><link>http://www.tkk7.com/titanaly/archive/2012/07/26/384024.html</link><dc:creator>不高?/dc:creator><author>不高?/author><pubDate>Thu, 26 Jul 2012 02:35:00 GMT</pubDate><guid>http://www.tkk7.com/titanaly/archive/2012/07/26/384024.html</guid><wfw:comment>http://www.tkk7.com/titanaly/comments/384024.html</wfw:comment><comments>http://www.tkk7.com/titanaly/archive/2012/07/26/384024.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.tkk7.com/titanaly/comments/commentRss/384024.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/titanaly/services/trackbacks/384024.html</trackback:ping><description><![CDATA[<span style="color: #881280; font-family: monospace; font-size: medium; line-height: normal; white-space: pre-wrap; "><a </span><span style="font-family: monospace; font-size: medium; line-height: normal; white-space: pre-wrap; ">href</span><span style="color: #881280; font-family: monospace; font-size: medium; line-height: normal; white-space: pre-wrap; ">="</span><a webkit-html-external-link"="" target="_blank" href="itms-services://?action=download-manifest&amp;url=http://test1.gc73.com.cn/hoho.plist" style="font-family: monospace; font-size: medium; line-height: normal; white-space: pre-wrap; ">itms-services://?action=download-manifest&amp;url=http://test1.gc73.com.cn/hoho.plist</a><span style="color: #881280; font-family: monospace; font-size: medium; line-height: normal; white-space: pre-wrap; ">"></span> <br /><br /><a webkit-html-external-link"="" target="_blank" href="itms-services://?action=download-manifest&amp;url=http://test1.gc73.com.cn/hoho.plist" style="font-family: monospace; font-size: medium; line-height: normal; white-space: pre-wrap; ">http://test1.gc73.com.cn/hoho.plist</a> 文g格式如下<br /><pre style="line-height: normal; word-wrap: break-word; white-space: pre-wrap; "><?xml version="1.0" encoding="gbk"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>items</key> <array> <dict> <key>assets</key> <array> <dict> <key>kind</key> <string>software-package</string> <key>url</key> <string>http://dlx1.gc73.com/pokerddzV2.1-7.20.iPad.ipa</string> </dict> <dict> <key>kind</key> <string>full-size-image</string> <key>needs-shine</key> <false/> <key>url</key> <string>http://test1.gc73.com.cn/ipad_tmp.png</string> </dict> <dict> <key>kind</key> <string>display-image</string> <key>needs-shine</key> <false/> <key>url</key> <string>http://test1.gc73.com.cn/ipad_tmp.png</string> </dict> </array> <key>metadata</key> <dict> <key>bundle-identifier</key> <string>com.pokercity.fightlordiPad</string> <key>kind</key> <string>software</string> <key>subtitle</key> <string>q试</string> <key>title</key> <string>q试(狱?</string> </dict> </dict> </array> </dict> </plist></pre><img src ="http://www.tkk7.com/titanaly/aggbug/384024.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/titanaly/" target="_blank">不高?/a> 2012-07-26 10:35 <a href="http://www.tkk7.com/titanaly/archive/2012/07/26/384024.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>hg clone abort: error:http://www.tkk7.com/titanaly/archive/2012/05/30/379595.html不高?/dc:creator>不高?/author>Wed, 30 May 2012 14:25:00 GMThttp://www.tkk7.com/titanaly/archive/2012/05/30/379595.htmlhttp://www.tkk7.com/titanaly/comments/379595.htmlhttp://www.tkk7.com/titanaly/archive/2012/05/30/379595.html#Feedback4http://www.tkk7.com/titanaly/comments/commentRss/379595.htmlhttp://www.tkk7.com/titanaly/services/trackbacks/379595.html用tortoisehg下蝲google code时报错abortQerror
解决Ҏ:https: 换成http 试一?/div>

]]>
visualvm监控jvm及远Ejvm监控Ҏhttp://www.tkk7.com/titanaly/archive/2012/03/20/372318.html不高?/dc:creator>不高?/author>Tue, 20 Mar 2012 13:59:00 GMThttp://www.tkk7.com/titanaly/archive/2012/03/20/372318.htmlhttp://www.tkk7.com/titanaly/comments/372318.htmlhttp://www.tkk7.com/titanaly/archive/2012/03/20/372318.html#Feedback2http://www.tkk7.com/titanaly/comments/commentRss/372318.htmlhttp://www.tkk7.com/titanaly/services/trackbacks/372318.html    VisualVM是Sun的一个OpenJDK目Q其目的在于为Java应用创徏一个整套的问题解决工具。它集成了多个JDK命o工具的一个可视化工具Q它主要用来监控JVM的运行情况,可以用它来查看和览Heap Dump、Thread Dump、内存对象实例情cGC执行情况、CPU消耗以及类的装载情c?Java开发h员可以?VisualVM创徏必要信息的日志,pȝ理人员可用来监控及控制Java应用E序在网l中的运行状c?/span> 
   下蝲面 : https://visualvm.dev.java.net/download.html

    文档地址 : https://visualvm.dev.java.net/docindex.html

    入门文档 : https://visualvm.dev.java.net/zh_CN/gettingstarted.html

    


    安装插g
    通过安装 VisualVM 更新中心提供的插Ӟ可以?VisualVM d功能?br />     1. 从主菜单中选择“工具”>“插g”?br />     2. ?#8220;可用插g”标签中,选中该插件的“安装”复选框。单?#8220;安装”?br />     3. 逐步完成插g安装E序?br />     


    功能    
    1. 概述
        查看jvm信息及系l配|?br />     

    2. 监视
        了解目q动的概?br />     

    3. visual gc
        可以看到内存gc的详l情?br />         


    q程监控    
    1. 通过jstatd启动RMI服务
        配置java安全讉KQ将如下的代码存为文?jstatd.all.policyQ放到JAVA_HOME/bin中,其内容如下,
        grant codebase "file:${java.home}/../lib/tools.jar" {

               permission java.security.AllPermission;

          };
            
          执行命ojstatd -J-Djava.security.policy=jstatd.all.policy -J-Djava.rmi.server.hostname=192.168.1.8 &(192.168.1.8  Z服务器的ip地址,&表示用守护线E的方式q行)
          jstatd命o详解 :http://hzl7652.iteye.com/blog/1183182 
         
          打开jvisualvm, 右键Remort,选择 "Add Remort Host..."Q在弹出框中输入你的q端IP,比如192.168.1.8. q接成功.
    
    2. 配置JMX理tomcat
        打开Tomcat的bin/catalina.batQ?span style="color: #ff0000; ">如果为linux或unixpȝQ则为catalina.sh文g ?nbsp;
           无限制访?nbsp;
           

  set JAVA_OPTS=-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9008 -Dcom.sun.management.jmxremote.authenticate=false -    Dcom.sun.management.jmxremote.ssl=false 

需要用户名和密码访?/div>
        JAVA_OPTS='-Xms128m -Xmx256m -XX:MaxPermSize=128m
        -Djava.rmi.server.hostname=192.168.1.8
        -Dcom.sun.management.jmxremote.port=8088
        -Dcom.sun.management.jmxremote.ssl=false
        -Dcom.sun.management.jmxremote.authenticate=true
        -Dcom.sun.management.jmxremote.password.file=/usr/java/default/jre/lib/management/jmxremote.password
        -Dcom.sun.management.jmxremote.access.file=/usr/java/default/jre/lib/management/jmxremote.access'
          (jmxremote.access 在JAVA_HOME\jre\lib\management下有模板)
         jmxremote.access 中显C?br />          monitorRole   readonly
         controlRole   readwrite
        
        jmxremote.password中显C?br />         monitorRole  QED            (QED为密?
        controlRole   R&D

        重新在visualvm中打开q程tomcat可以用JMX带来的功能了

 



]]>Eclipse快捷键大?转蝲)http://www.tkk7.com/titanaly/archive/2012/03/07/371407.html不高?/dc:creator>不高?/author>Wed, 07 Mar 2012 07:52:00 GMThttp://www.tkk7.com/titanaly/archive/2012/03/07/371407.htmlhttp://www.tkk7.com/titanaly/comments/371407.htmlhttp://www.tkk7.com/titanaly/archive/2012/03/07/371407.html#Feedback3http://www.tkk7.com/titanaly/comments/commentRss/371407.htmlhttp://www.tkk7.com/titanaly/services/trackbacks/371407.htmlEclipse快捷键大?转蝲)
Ctrl+1 快速修?最l典的快捷键,׃用多说了)
Ctrl+D: 删除当前?nbsp;
Ctrl+Alt+↓ 复制当前行到下一?复制增加)
Ctrl+Alt+↑ 复制当前行到上一?复制增加)
Alt+↓ 当前行和下面一行交互位|?特别实用,可以省去先剪?再粘贴了)
Alt+↑ 当前行和上面一行交互位|?同上)
Alt+← 前一个编辑的面
Alt+→ 下一个编辑的面(当然是针对上面那条来说了)
Alt+Enter 昄当前选择资源(工程,or 文g or文g)的属?br />Shift+Enter 在当前行的下一行插入空?q时鼠标可以在当前行的Q一位置,不一定是最?
Shift+Ctrl+Enter 在当前行插入I(原理同上?
Ctrl+Q 定位到最后编辑的地方
Ctrl+L 定位在某?nbsp;(对于E序过100的h有音?
Ctrl+M 最大化当前的Edit或View (再按则反?
Ctrl+/ 注释当前?再按则取消注?br />Ctrl+O 快速显C?nbsp;OutLine
Ctrl+T 快速显C当前类的承结?br />Ctrl+W 关闭当前Editer
Ctrl+K 参照选中的Word快速定位到下一?br />Ctrl+E 快速显C当前Editer的下拉列?如果当前面没有昄的用黑体表示)
Ctrl+/(键? 折叠当前cM的所有代?br />Ctrl+×(键? 展开当前cM的所有代?br />Ctrl+Space 代码助手完成一些代码的插入(但一般和输入法有冲突,可以修改输入法的热键,也可以暂用Alt+/来代?
Ctrl+Shift+E 昄理当前打开的所有的View的管理器(可以选择关闭,Ȁzȝ操作)
Ctrl+J 正向增量查找(按下Ctrl+J?你所输入的每个字母编辑器都提供快速匹配定位到某个单词,如果没有,则在stutes line中显C没有找C,查一个单词时,特别实用,q个功能Idea两年前就有了)
Ctrl+Shift+J 反向增量查找(和上条相?只不q是从后往前查)
Ctrl+Shift+F4 关闭所有打开的Editer
Ctrl+Shift+X 把当前选中的文本全部变呛_?br />Ctrl+Shift+Y 把当前选中的文本全部变为小?br />Ctrl+Shift+F 格式化当前代?br />Ctrl+Shift+P 定位到对于的匚wW?譬如{}) (从前面定位后面时,光标要在匚wW里?后面到前?则反?

下面的快捷键是重构里面常用的,本hp己喜Ƣ且常用的整理一??一般重构的快捷键都是Alt+Shift开头的?
Alt+Shift+R 重命?nbsp;(是我自己最q的一个了,其是变量和cȝRename,比手工方法能节省很多力_?
Alt+Shift+M 抽取Ҏ (q是重构里面最常用的方法之一?其是对一大堆泥团代码有用)
Alt+Shift+C 修改函数l构(比较实用,有N个函数调用了q个Ҏ,修改一ơ搞?
Alt+Shift+L 抽取本地变量( 可以直接把一些魔法数字和字符串抽取成一个变?其是多处调用的时?
Alt+Shift+F 把Class中的local变量变ؓfield变量 (比较实用的功?
Alt+Shift+I 合ƈ变量(可能q样说有点不妥Inline)
Alt+Shift+V Ud函数和变?不怎么常用)
Alt+Shift+Z 重构的后悔药(Undo)

~辑
作用?nbsp;功能 快捷?nbsp;
全局 查找q替?nbsp;Ctrl+F 
文本~辑?nbsp;查找上一?nbsp;Ctrl+Shift+K 
文本~辑?nbsp;查找下一?nbsp;Ctrl+K 
全局 撤销 Ctrl+Z 
全局 复制 Ctrl+C 
全局 恢复上一个选择 Alt+Shift+↓ 
全局 剪切 Ctrl+X 
全局 快速修?nbsp;Ctrl1+1 
全局 内容辅助 Alt+/ 
全局 全部选中 Ctrl+A 
全局 删除 Delete 
全局 上下文信?nbsp;Alt+Q?br />Alt+Shift+?
Ctrl+Shift+Space 
Java~辑?nbsp;昄工具提示描述 F2 
Java~辑?nbsp;选择装元素 Alt+Shift+↑ 
Java~辑?nbsp;选择上一个元?nbsp;Alt+Shift+← 
Java~辑?nbsp;选择下一个元?nbsp;Alt+Shift+→ 
文本~辑?nbsp;增量查找 Ctrl+J 
文本~辑?nbsp;增量逆向查找 Ctrl+Shift+J 
全局 _脓 Ctrl+V 
全局 重做 Ctrl+Y 

 
查看
作用?nbsp;功能 快捷?nbsp;
全局 攑֤ Ctrl+= 
全局 ~小 Ctrl+- 

 
H口
作用?nbsp;功能 快捷?nbsp;
全局 Ȁzȝ辑器 F12 
全局 切换~辑?nbsp;Ctrl+Shift+W 
全局 上一个编辑器 Ctrl+Shift+F6 
全局 上一个视?nbsp;Ctrl+Shift+F7 
全局 上一个透视?nbsp;Ctrl+Shift+F8 
全局 下一个编辑器 Ctrl+F6 
全局 下一个视?nbsp;Ctrl+F7 
全局 下一个透视?nbsp;Ctrl+F8 
文本~辑?nbsp;昄标尺上下文菜?nbsp;Ctrl+W 
全局 昄视图菜单 Ctrl+F10 
全局 昄pȝ菜单 Alt+- 

 
D
作用?nbsp;功能 快捷?nbsp;
Java~辑?nbsp;打开l构 Ctrl+F3 
全局 打开cd Ctrl+Shift+T 
全局 打开cd层次l构 F4 
全局 打开声明 F3 
全局 打开外部javadoc Shift+F2 
全局 打开资源 Ctrl+Shift+R 
全局 后退历史记录 Alt+← 
全局 前进历史记录 Alt+→ 
全局 上一?nbsp;Ctrl+, 
全局 下一?nbsp;Ctrl+. 
Java~辑?nbsp;昄大纲 Ctrl+O 
全局 在层ơ结构中打开cd Ctrl+Shift+H 
全局 转至匚w的括?nbsp;Ctrl+Shift+P 
全局 转至上一个编辑位|?nbsp;Ctrl+Q 
Java~辑?nbsp;转至上一个成?nbsp;Ctrl+Shift+↑ 
Java~辑?nbsp;转至下一个成?nbsp;Ctrl+Shift+↓ 
文本~辑?nbsp;转至?nbsp;Ctrl+L 

 
搜烦
作用?nbsp;功能 快捷?nbsp;
全局 出现在文件中 Ctrl+Shift+U 
全局 打开搜烦对话?nbsp;Ctrl+H 
全局 工作Z的声?nbsp;Ctrl+G 
全局 工作Z的引?nbsp;Ctrl+Shift+G 

 
文本~辑
作用?nbsp;功能 快捷?nbsp;
文本~辑?nbsp;改写切换 Insert 
文本~辑?nbsp;上滚?nbsp;Ctrl+↑ 
文本~辑?nbsp;下滚?nbsp;Ctrl+↓ 

 
文g
作用?nbsp;功能 快捷?nbsp;
全局 保存 Ctrl+X 
Ctrl+S 
全局 打印 Ctrl+P 
全局 关闭 Ctrl+F4 
全局 全部保存 Ctrl+Shift+S 
全局 全部关闭 Ctrl+Shift+F4 
全局 属?nbsp;Alt+Enter 
全局 新徏 Ctrl+N 

 

作用?nbsp;功能 快捷?nbsp;
全局 全部构徏 Ctrl+B 

 
源代?br />作用?nbsp;功能 快捷?nbsp;
Java~辑?nbsp;格式?nbsp;Ctrl+Shift+F 
Java~辑?nbsp;取消注释 Ctrl+\ 
Java~辑?nbsp;注释 Ctrl+/ 
Java~辑?nbsp;d导入 Ctrl+Shift+M 
Java~辑?nbsp;l织导入 Ctrl+Shift+O 
Java~辑?nbsp;使用try/catch块来包围 未设|,太常用了Q所以在q里列出,自己讄?br />也可以用Ctrl+1自动修正?nbsp;

 
q行
作用?nbsp;功能 快捷?nbsp;
全局 单步q回 F7 
全局 单步跌 F6 
全局 单步跛_ F5 
全局 单步跛_选择 Ctrl+F5 
全局 调试上次启动 F11 
全局 l箋 F8 
全局 使用qo器单步执?nbsp;Shift+F5 
全局 d/去除断点 Ctrl+Shift+B 
全局 昄 Ctrl+D 
全局 q行上次启动 Ctrl+F11 
全局 q行臌 Ctrl+R 
全局 执行 Ctrl+U 

 
重构
作用?nbsp;功能 快捷?nbsp;
全局 撤销重构 Alt+Shift+Z 
全局 抽取Ҏ Alt+Shift+M 
全局 抽取局部变?nbsp;Alt+Shift+L 
全局 内联 Alt+Shift+I 
全局 Ud Alt+Shift+V 
全局 重命?nbsp;Alt+Shift+R 
全局 重做 Alt+Shift+Y


]]>
css中className命名规则http://www.tkk7.com/titanaly/archive/2012/02/03/369335.html不高?/dc:creator>不高?/author>Fri, 03 Feb 2012 09:58:00 GMThttp://www.tkk7.com/titanaly/archive/2012/02/03/369335.htmlhttp://www.tkk7.com/titanaly/comments/369335.htmlhttp://www.tkk7.com/titanaly/archive/2012/02/03/369335.html#Feedback2http://www.tkk7.com/titanaly/comments/commentRss/369335.htmlhttp://www.tkk7.com/titanaly/services/trackbacks/369335.html    今天找一个css的加载问?class已经被浏览器加蝲?但是属性g直不昄出来,几经查找才发现className以数字开头命名的,D览器不认可,整一个悲?img src ="http://www.tkk7.com/titanaly/aggbug/369335.html" width = "1" height = "1" />

]]>
btrace使用实例加注?/title><link>http://www.tkk7.com/titanaly/archive/2012/02/02/369252.html</link><dc:creator>不高?/dc:creator><author>不高?/author><pubDate>Thu, 02 Feb 2012 09:38:00 GMT</pubDate><guid>http://www.tkk7.com/titanaly/archive/2012/02/02/369252.html</guid><wfw:comment>http://www.tkk7.com/titanaly/comments/369252.html</wfw:comment><comments>http://www.tkk7.com/titanaly/archive/2012/02/02/369252.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.tkk7.com/titanaly/comments/commentRss/369252.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/titanaly/services/trackbacks/369252.html</trackback:ping><description><![CDATA[<div><div>BTrace的技术分?本h暂没有这个技术能?大家可以?nbsp;<a >http://www.iteye.com/topic/1005918,<br /></a><a >http://mgoann.iteye.com/blog/1409667</a> <br />下面是个人写的一些简单实?不过我一直没办法通过BTrace拿到局部变量的?不知道哪位牛人帮帮解{下<br /><span style="font-family: Helvetica, Tahoma, Arial, sans-serif; line-height: 25px; text-align: left; background-color: #ffffff; ">Linux下:</span><br style="font-family: Helvetica, Tahoma, Arial, sans-serif; line-height: 25px; text-align: left; background-color: #ffffff; " /><span style="font-family: Helvetica, Tahoma, Arial, sans-serif; line-height: 25px; text-align: left; background-color: #ffffff; ">在http://kenai.com/projects/btrace下蝲btrace-bin.tar.gzQƈ解压Q在/etc/profile讄环境变量Q?/span> <br /><ol start="1" style="font-size: 12px; line-height: 1.4em; margin-top: 0px; margin-right: 0px; margin-bottom: 1px; margin-left: 0px; padding-top: 2px; padding-right: 0px; padding-bottom: 2px; padding-left: 0px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-style: solid; border-right-style: solid; border-bottom-style: solid; border-left-style: solid; border-top-color: #d1d7dc; border-right-color: #d1d7dc; border-bottom-color: #d1d7dc; border-left-color: #d1d7dc; border-image: initial; list-style-position: initial; list-style-image: initial; background-color: #ffffff; color: #2b91af; font-family: Monaco, 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', Consolas, 'Courier New', monospace; text-align: left; "><li style="font-size: 1em; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 38px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 10px; border-left-width: 1px; border-left-style: solid; border-left-color: #d1d7dc; background-color: #fafafa; line-height: 18px; "><span style="color: black; ">export BTRACE_HOME=/home/workspace/btrace/</span></li><li style="font-size: 1em; margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 38px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 10px; border-left-width: 1px; border-left-style: solid; border-left-color: #d1d7dc; background-color: #fafafa; line-height: 18px; "><span style="color: black; ">export PATH=$BTRACE_HOME/bin:$PATH  <br /></span></li></ol>讄完成?source /etc/profile<br />l执行文件赋权限 chmod +x btrace<br /><br />/* BTrace Script Template */</div><div>import com.sun.btrace.annotations.*;</div><div>import static com.sun.btrace.BTraceUtils.*;</div><div>import java.lang.reflect.Field;</div><div></div><div>@BTrace</div><div>public class TracingScript {</div><div><span style="white-space:pre"> </span>/* put your code here */</div><div><span style="white-space:pre"> </span>//打印实例属?/div><div><span style="white-space:pre"> </span>@OnMethod(clazz = "com.gameplus.action.siteLobby.LobbyAction", method = "/.*bankPage/", location = @Location(value = Kind.ENTRY))</div><div><span style="white-space:pre"> </span>//clazz = "com.gameplus.action.siteLobby.LobbyAction" 表示监控的类,method = "/.*bankPage/"表示监控的方?,q两个参数都可以用正则匹?/div><div><span style="white-space:pre"> </span>//如果是接口用+号clazz = "+com.gameplus.action.siteLobby.LobbyAction" </div><div><span style="white-space:pre"> </span>public static void bufferMonitor(@Self Object self ){<span style="white-space:pre"> </span>// @Self 表示监控点实?nbsp;</div><div><span style="white-space:pre"> </span>    print(strcat(strcat(name(probeClass()), "."), probeMethod()));<span style="white-space:pre"> </span>//probeClass()监控的类,probeMethod()监控的方?/div><div>        println(self);</div><div>        println(get(field(classOf(self), "money")));<span style="white-space:pre"> </span>//只能取值static变量</div><div><span style="white-space:pre"> </span>println(get(field(classOf(self), "money"),self));<span style="white-space:pre"> </span>//可以取值当前实例变?static也可以取?/div><div><span style="white-space:pre"> </span>Field moneyField = field("com.gameplus.action.siteLobby.LobbyAction", "money");<span style="white-space:pre"> </span>//知道class的名UC可以取?/div><div><span style="white-space:pre"> </span>get(moneyField,self);<span style="white-space:pre"> </span></div><div><span style="white-space:pre"> </span>get(field("com.gameplus.action.siteLobby.LobbyAction", "money"), self); </div><div></div><div>        Object montmp =get(field(getSuperclass(classOf(self)), "user"), self);<span style="white-space:pre"> </span>//获取父类变量的方?/div><div>        println(str(montmp));</div><div><span style="white-space:pre"> </span>long userId = (Long)get(field(classOf(montmp),"userId"),montmp);<span style="white-space:pre"> </span>//获取superClass.Object.变量?/div><div>        println(userId);</div><div></div><div>    }</div><div><span style="white-space:pre"> </span></div><div><span style="white-space:pre"> </span>//打印q行时lineNumber</div><div>    @OnMethod(clazz = "com.gameplus.action.siteLobby.LobbyAction", location=@Location(value=Kind.LINE, line=-1))</div><div>    public static void online(@ProbeClassName String pcn, @ProbeMethodName String pmn, int line) {</div><div><span style="white-space:pre"> </span>print(Strings.strcat(pcn, "."));<span style="white-space:pre"> </span>//className</div><div><span style="white-space:pre"> </span>print(Strings.strcat(pmn, ":"));<span style="white-space:pre"> </span>//methodName</div><div><span style="white-space:pre"> </span>println(line);<span style="white-space:pre"> </span>//lineNumber</div><div><span style="white-space:pre"> </span>//l果?com.gameplus.action.siteLobby.LobbyAction.bankPage:161</div><div><span style="white-space:pre"> </span>}</div><div></div><div><span style="white-space:pre"> </span>//打印传递的参数?/div><div><span style="white-space:pre"> </span>import com.sun.btrace.AnyType;</div><div><span style="white-space:pre"> </span>@OnMethod(clazz="com.gameplus.service.operateBankService.OperateBankService",method="/.*/")</div><div><span style="white-space:pre"> </span>public static void anyRead(@ProbeClassName String pcn, @ProbeMethodName String pmn, AnyType[] args) {</div><div><span style="white-space:pre"> </span>println(pcn);</div><div><span style="white-space:pre"> </span>println(pmn);</div><div><span style="white-space:pre"> </span>printArray(args);</div><div><span style="white-space:pre"> </span>}</div><div></div><div><span style="white-space:pre"> </span>//打印所有属?/div><div><span style="white-space:pre"> </span>@OnMethod(clazz = "com.gameplus.action.siteLobby.LobbyAction", method = "/.*bankPage/", location = @Location(value = Kind.RETURN))</div><div>    public static void bufferMonitor(@Self Object self,@Return Object command ,@Duration long time){ </div><div>        printFields(self);</div><div>        Object montmp =get(field(getSuperclass(classOf(self)), "user"), self);<span style="white-space:pre"> </span></div><div>        printFields(montmp);</div><div><span style="white-space:pre"> </span>//{password=null, newPassword=null, rePassword=, bankPassword=, newBankPassword=, reBankPassword=, operateType=0, operateBankType=0, money=111, integral=0, dateStartQuery=, dateEndQuery=, isDefaultPasswd=0, }</div><div><span style="white-space:pre"> </span>//{userId=10918, username=titanaly11, realname=Ҏ1, expTime=1146510, bankMoney=1317886229, bankPasswd=243b6503f2e3e83faccc89830aca1d91, ifAvailable=1, password=1bbd886460827015e5d605ed44252251, money=100000, }</div><div></div><div>    }</div><div></div><div><span style="white-space:pre"> </span>//初始化时的变量参?/div><div><span style="white-space:pre"> </span>//public User(long userId,String playServerId) {</div><div><span style="white-space:pre"> </span>//<span style="white-space:pre"> </span>this.userId = userId;</div><div><span style="white-space:pre"> </span>//<span style="white-space:pre"> </span>this.playServerId = playServerId;</div><div><span style="white-space:pre"> </span>//}</div><div><span style="white-space:pre"> </span>@OnMethod(clazz = "com.gameplus.core.model.oracle.user.User", method="<init>")</div><div>     public static void bufferMonitor(@Self Object self,long userId,String gameId){ </div><div>        printFields(self);</div><div>        println(userId);</div><div>        println(gameId);</div><div><span style="white-space:pre"> </span>//l果</div><div><span style="white-space:pre"> </span>//{userId=0, username=null, realname=null, expTime=0, bankMoney=0, bankPasswd=null, ifAvailable=0, password=null, money=0, nickName=null, iconNum=0, tim=null, infullCount=0, userToken=null, onlineTime=0,}</div><div><span style="white-space:pre"> </span>//10918</div><div><span style="white-space:pre"> </span>//222</div><div>    }</div><div></div><div><span style="white-space:pre"> </span>//打印pȝ参数</div><div><span style="white-space:pre"> </span>static {</div><div><span style="white-space:pre"> </span>println("System Properties:");</div><div><span style="white-space:pre"> </span>printProperties();</div><div><span style="white-space:pre"> </span>println("VM Flags:");</div><div><span style="white-space:pre"> </span>printVmArguments();</div><div><span style="white-space:pre"> </span>println("OS Enviroment:");</div><div><span style="white-space:pre"> </span>printEnv();</div><div><span style="white-space:pre"> </span>exit(0);</div><div><span style="white-space:pre"> </span>}</div><div></div><div><span style="white-space:pre"> </span>//打印E序执行关系</div><div><span style="white-space:pre"> </span>//LobbyAction中所有方法执行的执行序</div><div><span style="white-space:pre"> </span>@OnMethod(clazz="com.gameplus.action.siteLobby.LobbyAction", method="/.*/",</div><div>              location=@Location(value=Kind.CALL, clazz="/.*/", method="/.*/"))</div><div>    public static void n(@Self Object self, @ProbeClassName String pcm, @ProbeMethodName String pmn,</div><div>                         @TargetInstance Object instance, @TargetMethodOrField String method){ // all calls to the methods with signature "(String)"</div><div>        println(Strings.strcat("Context: ", Strings.strcat(pcm, Strings.strcat("#", pmn))));</div><div>        println(instance);<span style="white-space:pre"> </span>//被调用目标对?/div><div>        println(Strings.strcat("",method));<span style="white-space:pre"> </span>//被调用方?/div><div></div><div><span style="white-space:pre"> </span></div><div><span style="white-space:pre"> </span>//Context: com/gameplus/action/siteLobby/LobbyAction#bankPage</div><div><span style="white-space:pre"> </span>//$Proxy18@807c31</div><div><span style="white-space:pre"> </span>//getBankPage</div><div><span style="white-space:pre"> </span>//Context: com/gameplus/action/siteLobby/LobbyAction#bankPage</div><div><span style="white-space:pre"> </span>//{userEx=10, leverRestriction=6, isDefaultPasswd=0, bankPassEqGamePass=0, moneyTransferMsg=S, isSimplePasswd=0, mobile=null}</div><div><span style="white-space:pre"> </span>//get</div><div><span style="white-space:pre"> </span>//Context: com/gameplus/action/siteLobby/LobbyAction#bankPage</div><div><span style="white-space:pre"> </span>//6</div><div><span style="white-space:pre"> </span>//intValue</div><div><span style="white-space:pre"> </span>//Context: com/gameplus/action/siteLobby/LobbyAction#bankPage</div><div><span style="white-space:pre"> </span>//{userEx=10, leverRestriction=6, isDefaultPasswd=0, bankPassEqGamePass=0, moneyTransferMsg=S, isSimplePasswd=0, mobile=null}</div><div><span style="white-space:pre"> </span>//get</div><div>}</div></div><img src ="http://www.tkk7.com/titanaly/aggbug/369252.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/titanaly/" target="_blank">不高?/a> 2012-02-02 17:38 <a href="http://www.tkk7.com/titanaly/archive/2012/02/02/369252.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <p>лǵվܻԴȤ</p> <a href="http://www.tkk7.com/" title="亚洲av成人片在线观看">亚洲av成人片在线观看</a> <div class="friend-links"> </div> </div> </footer> վ֩ģ壺 <a href="http://864007.com" target="_blank">57paoƵ</a>| <a href="http://aidannis.com" target="_blank">ƹƵ߿</a>| <a href="http://22youjizz.com" target="_blank">һӰȷɫԴ</a>| <a href="http://hivzx.com" target="_blank">պƷһ</a>| <a href="http://www65axax.com" target="_blank">鶹һ</a>| <a href="http://zwdyw.com" target="_blank">avר߲</a>| <a href="http://53reniao.com" target="_blank">Ƶѹۿ</a>| <a href="http://am33318.com" target="_blank">ɫҹƵ</a>| <a href="http://mllm999.com" target="_blank">ĻþþƷVA</a>| <a href="http://xamxx.com" target="_blank">avŷղһ</a>| <a href="http://mmstom.com" target="_blank">黨ýmvѹۿ</a>| <a href="http://4eeyy.com" target="_blank">Ƶվѹۿ</a>| <a href="http://xx2015.com" target="_blank">޾ƷƵ</a>| <a href="http://bjbf99.com" target="_blank">޾ƷA߹ۿ</a>| <a href="http://qqcnm.com" target="_blank">ëƬƵ</a>| <a href="http://57fi.com" target="_blank">ѳߵӰ</a>| <a href="http://yidazn.com" target="_blank">վѹۿ</a>| <a href="http://5d8f.com" target="_blank">AVþþþվ</a>| <a href="http://ding001.com" target="_blank">ĻͼƬ</a>| <a href="http://wlzp88.com" target="_blank">պƵ</a>| <a href="http://zhaofeiz.com" target="_blank">þѸƵ</a>| <a href="http://2828228.com" target="_blank">˸徫Ʒѹۿ</a>| <a href="http://w6446.com" target="_blank">ŷ޹ۺAVþ</a>| <a href="http://mosason.com" target="_blank">ձĻ</a>| <a href="http://bjowj.com" target="_blank">ĻƷ</a>| <a href="http://zhhy68.com" target="_blank">պƷһ </a>| <a href="http://txtmp3.com" target="_blank">ձһ</a>| <a href="http://yutuzb.com" target="_blank">ӰƬһѹۿ</a>| <a href="http://quxx10.com" target="_blank">պǧ</a>| <a href="http://dxjz120.com" target="_blank">޾ƷӰԺþþþþ</a>| <a href="http://taoh2507.com" target="_blank">Ļһ </a>| <a href="http://maomi02.com" target="_blank">Ļ</a>| <a href="http://006dh.com" target="_blank">պѵӰ߹ۿ</a>| <a href="http://manghuo365.com" target="_blank">91Ƶ</a>| <a href="http://ti166.com" target="_blank">ɫҹƵ˵</a>| <a href="http://hbwhgd.com" target="_blank">ȫһһëƬѿ</a>| <a href="http://ulihix.com" target="_blank">ɫվWWWվ</a>| <a href="http://zhuguangbaoyu.com" target="_blank">޻ɫƵ</a>| <a href="http://hbqueena.com" target="_blank">޲͵V͵Vɫ </a>| <a href="http://caocl1024liu.com" target="_blank">޳AVַ</a>| <a href="http://kmc19.com" target="_blank">ѸӰƬһ</a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>