可以将linkedlist<java将string转list>的对象赋给linkedlist<object>变量吗

&p&&b&JAVA应用开发:&/b&&/p&
&p&现在基本等同于Android开发,如果现在没在一个稳定的Android团队的话,在如今时间点(2017年)&b&不建议学习了&/b&。(尝试一下也是可以的,谢绝撕逼)&/p&
&p&&b&JAVA后台开发:&/b&&/p&
&p&1、算法:&/p&&p&各种排序 (&a href=&///?target=http%3A//blog.csdn.net/qy1387/article/details/7752973& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Java常用排序算法/程序员必须掌握的8大排序算法 - 續寫經典 - 博客频道 - CSDN.NET&i class=&icon-external&&&/i&&/a&)、查找(&a href=&///?target=http%3A//xiaojun-/blog/2291852& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&常用查找算法的Java实现 - 一步一步学Java - ITeye技术网站&i class=&icon-external&&&/i&&/a&)、bitmap(&a href=&///?target=http%3A//zengzhaozheng./4108& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&海量数据解决思路之BitMap - 蚂蚁 - 51CTO技术博客&i class=&icon-external&&&/i&&/a&)、字符串存储和处理(&a href=&///?target=http%3A///p/e2bd1ee482c3& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&KMP字符串模式匹配算法Java实现 - 简书&i class=&icon-external&&&/i&&/a&)等基础中的基础算法,这些面试时是一定要问的,所以一定要刷好,&a href=&///?target=https%3A///& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&LeetCode Online Judge&i class=&icon-external&&&/i&&/a&。&/p&
&p&2、JAVA se,JAVA 8特性,RxJAVA&/p&&p&JAVAse没什么好说的,找本书看看就ok。也可以先看看这个图感受一下:&/p&&p&&img src=&/v2-d50da6eb69fe3d20f2041_b.png& data-rawwidth=&572& data-rawheight=&458& class=&origin_image zh-lightbox-thumb& width=&572& data-original=&/v2-d50da6eb69fe3d20f2041_r.png&&(图片来自:&a href=&///?target=https%3A///What-are-the-concepts-every-Java-programmer-must-know& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&What are the concepts every Java programmer must know?&i class=&icon-external&&&/i&&/a&)&/p&&p&一张图看懂JAVA8要学什么:&/p&&img src=&/v2-1ff26665cded0b6a472103_b.png& data-rawwidth=&1142& data-rawheight=&486& class=&origin_image zh-lightbox-thumb& width=&1142& data-original=&/v2-1ff26665cded0b6a472103_r.png&&&br&&p&3、设计模式:《&a href=&///?target=https%3A///subject//& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Java设计模式 (豆瓣)&i class=&icon-external&&&/i&&/a&》,或者其他的设计模式书翻翻就ok了,学设计模式并不是让你套各种模式写代码,而是为了让你能看懂别人写代码的套路(毕竟很多JAVA程序员以会各种模式为荣)&/p&&img src=&/v2-8a542ecff4a3cb0d9b6d4c_b.jpg& data-rawwidth=&1042& data-rawheight=&700& class=&origin_image zh-lightbox-thumb& width=&1042& data-original=&/v2-8a542ecff4a3cb0d9b6d4c_r.jpg&&&p&4、AOP(&a href=&/question/& class=&internal&&什么是面向切面编程AOP? - Java - 知乎&/a&),动态代理(&a href=&///?target=https%3A///techyc/p/3455950.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Java的动态代理(dynamic proxy)&i class=&icon-external&&&/i&&/a&),注解(&a href=&///?target=https%3A///mandroid/archive//2109829.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Java基础之理解Annotation - 宁 静 致 远 - 博客园&i class=&icon-external&&&/i&&/a&):这部分一定要多找些资料看和多实践,JAVA比较死板,也只有这部分能玩一些HOOK类的黑魔法了。&/p&&img src=&/v2-3a90f50673eeedb2ac5c1_b.jpg& data-rawwidth=&728& data-rawheight=&546& class=&origin_image zh-lightbox-thumb& width=&728& data-original=&/v2-3a90f50673eeedb2ac5c1_r.jpg&&&p&5、内存模型(&a href=&///?target=http%3A//q.com/cn/articles/java-memory-model-1& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&深入理解Java内存模型(一)——基础 &i class=&icon-external&&&/i&&/a&)、GC算法、JVM调优(&a href=&///?target=https%3A///developerworks/cn/java/j-lo-jvm-optimize-experience/index.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&JVM 优化经验总结&i class=&icon-external&&&/i&&/a&):前两个找工作时候是一定要熟的,面试会盯着这一块问。最后一个实践中会用到,当然面试也会问。参考书籍,周志明老师的《&a href=&///?target=https%3A///subject//& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&深入理解Java虚拟机(第2版) (豆瓣)&i class=&icon-external&&&/i&&/a&》&/p&&p&&img src=&/v2-82d9be69b1e863b7e0476_b.png& data-rawwidth=&1160& data-rawheight=&482& class=&origin_image zh-lightbox-thumb& width=&1160& data-original=&/v2-82d9be69b1e863b7e0476_r.png&&(&a href=&///?target=http%3A///2856/java-jvm-memory-model-memory-management-in-java& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Java (JVM) Memory Model - Memory Management in Java - JournalDev&i class=&icon-external&&&/i&&/a&)&/p&&p&GC算法(&a href=&///?target=http%3A///16173.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&深入理解 Java 垃圾回收机制&i class=&icon-external&&&/i&&/a&):&/p&&ul&&ul&&li&
引用计数法&img src=&/v2-b448ba0ebe37b78a943e_b.jpg& data-rawwidth=&480& data-rawheight=&360& class=&origin_image zh-lightbox-thumb& width=&480& data-original=&/v2-b448ba0ebe37b78a943e_r.jpg&&&/li&&li&
根搜索算法&img src=&/v2-d4bf6d04dcad57e862ec828d75d05b09_b.png& data-rawwidth=&856& data-rawheight=&476& class=&origin_image zh-lightbox-thumb& width=&856& data-original=&/v2-d4bf6d04dcad57e862ec828d75d05b09_r.png&&&/li&&li&
现代虚拟机中的垃圾搜集算法:&/li&&/ul&&/ul&&ol&&ol&&ol&&li&标记-清除&img src=&/v2-fca1e6bb35c99d6f5221d76_b.png& data-rawwidth=&212& data-rawheight=&232& class=&content_image& width=&212&&&/li&&li&复制算法(新生代)&/li&&li&标记-压缩(老年代)&img src=&/v2-051d1ccb32a584e96a2e5fe524c39ed4_b.jpg& data-rawwidth=&638& data-rawheight=&359& class=&origin_image zh-lightbox-thumb& width=&638& data-original=&/v2-051d1ccb32a584e96a2e5fe524c39ed4_r.jpg&&&/li&&/ol&&/ol&&/ol&&ul&&ul&&li& 分代收集&/li&&/ul&&/ul&&p&6、Concurrent(&a href=&///?target=https%3A///javase/7/docs/api/java/util/concurrent/package-summary.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&java.util.concurrent (Java Platform SE 7 )&i class=&icon-external&&&/i&&/a&)和nio(&a href=&///?target=https%3A///javase/7/docs/api/java/nio/package-summary.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&java.nio (Java Platform SE 7 )&i class=&icon-external&&&/i&&/a&)这两个package里面的类要会熟练用&/p&
&p&7、要会用主流的日志系统,后台需要大量日志要追溯崩溃点,可以看看log4j(&a href=&///?target=https%3A//logging.apache.org/log4j/2.x/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Log4j - Log4j 2 Guide - Apache Log4j 2&i class=&icon-external&&&/i&&/a&)。&/p&
&p&8、对于网络编程就是多接触框架了,比如Protobuf协议(&a href=&///?target=https%3A///protocol-buffers/& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&/p&/span&&span class=&invisible&&rotocol-buffers/&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&)、dubbo RPC框架(&a href=&///?target=http%3A//dubbo.io/User%2BGuide-zh.htm& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&User Guide-zh - Dubbo - Alibaba Open Sesame&i class=&icon-external&&&/i&&/a&),Netty和Mina、thrift。可以一起学一下Redis,RabbitMQ这些常用的message broker。&/p&&p&9、如果对大数据感兴趣,就学一下Spark(&a href=&///?target=https%3A//spark.apache.org/& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://&/span&&span class=&visible&&spark.apache.org/&/span&&span class=&invisible&&&/span&&i class=&icon-external&&&/i&&/a&)&/p&
&p&上述只是保证你找到一个比较好工作的基本要求,学完这些后,你就可以按照自己兴趣点来点技能树了~&/p&&p&祝您早日找到心仪的工作~&/p&
JAVA应用开发:
现在基本等同于Android开发,如果现在没在一个稳定的Android团队的话,在如今时间点(2017年)不建议学习了。(尝试一下也是可以的,谢绝撕逼)
JAVA后台开发:
1、算法:各种排序 (
&img src=&/v2-17bb8e9cc_b.jpg& data-rawwidth=&580& data-rawheight=&322& class=&origin_image zh-lightbox-thumb& width=&580& data-original=&/v2-17bb8e9cc_r.jpg&&&h1&1.前言&/h1&&p&在众多流行的编程语言中,Java对IO的处理应该是最特殊的,Java打着“尽量减少IO类的设计理念”,搞出了目前应该是最复杂的一套IO相关类,并称之为Java流。 &br&对于新手来说,Java流包含的类众多,含义混杂,上手困难且其中暗藏的陷阱众多;但是对于熟悉了Java流的程序员来说,它的确称得上功能强大。 &br&本文总结了一些Java流的使用指南,给出了一些实例代码,主要内容包括: &br&- Java流中的字节与字符 &br&- 文件流 &br&- 字节数组流 &br&- 管道流 &br&- 缓冲流 &br&- 数据流 &br&- 压缩流 &br&- 摘要流 &br&- 加密流 &br&- 多重流使用范例 &br&本文的读者应该是Java程序员,最好具有基本的Java基础知识。本文给出的代码都已经在Jdk7上运行通过,如有错误,请及时反馈。&/p&&h1&2.Java中的字节与字符&/h1&&h2&2.1Java流概论&/h2&&p&Java流是Java语言用来处理IO的一套类,针对字节进行处理的类被命名为stream结尾,例如FileInputStream、FileOutputStream;针对字符进行处理的类被命名为Reader或Writer结尾,例如FileReader或者FileWriter。要弄清Java流,首先要明白Java中的字节与字符的区别。 &br&包含InputStream或Reader的类被称为输入流,此处的“输入”是指从外部(文件、网络、其他程序)将信息输入到Java程序内部;或者指从Java程序内部的某个变量输入到当前操作的代码块。 &br&包含OutputStream或Writer的类被称为输出流,此处的“输出”是指从Java程序内部将信息输出到外部(文件、网络、其他程序);或者指从当前操作的代码块将信息输出到其他变量。&/p&&h2&2.2 字节(byte)的取值&/h2&&p&一个字节包含一个8位的二进制数字,在Java中使用byte来表示。最大值为127,最小值为-128。下面的代码列出了所有byte的值:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&public static void listAllByteValue(){
for (int i = -128; i & 128; i++) {
byte b = (byte)
System.out.println(&byte:& + b + &,Binary:& + byteToBinaryString(b));
public static String byteToBinaryString(byte b) {
String s = Integer.toBinaryString(b);
if (b & 0) {
s = s.substring(24);
if (s.length() & 8) {
int len = s.length();
for (int i = 0; i & 8 - i++) {
&/code&&/pre&&/div&&p&大于等于0的byte值,使用其原码表示,即直接存储该byte的二进制值;小于0的byte值,使用其补码表示,即将该值的绝对值的原码按位取反再加1。例如42就存储为;而-42先求42的值,然后按位取反得到,再加1得到,此即为-42的二进制值。&/p&&h2&2.3 字节(byte)的赋值&/h2&&p&byte可以用多种方法来赋值,见下列代码:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&public static void byteGetValue(){
//二进制以0b开头
byte b1 = 0b;
System.out.println(&b1:&+b1+&,Binary:&+byteToBinaryString(b1));
//八进制以0开头
byte b2 = 052;
System.out.println(&b2:&+b2+&,Binary:&+byteToBinaryString(b2));
byte b3 = 42;
System.out.println(&b3:&+b3+&,Binary:&+byteToBinaryString(b3));
//十六进制
byte b4 = 0x2a;
System.out.println(&b4:&+b4+&,Binary:&+byteToBinaryString(b4));
//-42的赋值
//二进制,由于以原码来理解已经超过了127,因此必须使用byte进行强制类型转换
byte b5 = (byte) 0b;
System.out.println(&b5:&+b5+&,Binary:&+byteToBinaryString(b5));
//八进制以0开头
byte b6 = -052;
System.out.println(&b6:&+b6+&,Binary:&+byteToBinaryString(b6));
byte b7 = -42;
System.out.println(&b7:&+b7+&,Binary:&+byteToBinaryString(b7));
//十六进制,由于0xd6以原码来理解已经超过了127,因此必须使用byte进行强制类型转换
byte b8 = (byte) 0xd6;
System.out.println(&b8:&+b8+&,Binary:&+byteToBinaryString(b8));
//将两个int转为byte的示例,示例告诉我们int转byte,就是简单的截取最后8位
int i1 = 0b;
int i2 = 0b;
System.out.println(&i1 = &+i1+&, i2 = &+i2);
byte b9 = (byte) i1;
byte b10 = (byte) i2;
System.out.println(&b9 = &+b9+&, b10 = &+b10);
&/code&&/pre&&/div&&p&值得注意的是,当int转换为byte时,直接截取了int的后8位。 &br&运行结果:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&b1:42,Binary:
b2:42,Binary:
b3:42,Binary:
b4:42,Binary:
b5:-42,Binary:
b6:-42,Binary:
b7:-42,Binary:
b8:-42,Binary:
i1 = 726, i2 = 3286
b9 = -42, b10 = -42
&/code&&/pre&&/div&&h2&2.4 字符(char)的取值&/h2&&p&由上面的例子可知byte就是一个单纯的8位二进制数字,它可以有多种赋值方法,但是在内存中始终不变。 &br&字符在Java中使用char基本类型来存储,它是一个16位的unicode码,也可以理解为一个16位的二进制数字,其取值范围为0到6次方-1)。下面的例子给出了一段中文的char值:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&public static void listCharValue() {
System.out.println(&char max value is :&+(int)Character.MAX_VALUE+&, min value is :&+(int)Character.MIN_VALUE);
for (char c = 19968; c & 20271; c++) {
System.out.print(c);
&/code&&/pre&&/div&&p&运行结果:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&char max value is :65535, min value is :0
一丁丂七丄丅丆万丈三上下丌不与丏丐丑丒专且丕世丗丘丙业丛东丝丞丟丠両丢丣两严並丧丨丩个丫丬中丮丯丰丱串丳临丵丶丷丸丹为主丼丽举丿乀乁乂乃乄久乆乇么义乊之乌乍乎乏乐乑乒乓乔乕乖乗乘乙乚乛乜九乞也习乡乢乣乤乥书乧乨乩乪乫乬乭乮乯买乱乲乳乴乵乶乷乸乹乺乻乼乽乾乿亀亁亂亃亄亅了亇予争亊事二亍于亏亐云互亓五井亖亗亘亙亚些亜亝亞亟亠亡亢亣交亥亦产亨亩亪享京亭亮亯亰亱亲亳亴亵亶亷亸亹人亻亼亽亾亿什仁仂仃仄仅仆仇仈仉今介仌仍从仏仐仑仒仓仔仕他仗付仙仚仛仜仝仞仟仠仡仢代令以仦仧仨仩仪仫们仭仮仯仰仱仲仳仴仵件价仸仹仺任仼份仾仿伀企伂伃伄伅伆伇伈伉伊伋伌伍伎伏伐休伒伓伔伕伖众优伙会伛伜伝伞伟传伡伢伣伤伥伦伧伨伩伪伫伬伭伮
&/code&&/pre&&/div&&h2&2.5 字符(char)的赋值&/h2&&p&后面会提到,char可以表现为世界各国的各种字符,但是在内存中,它就是一个16位的二进制数字,因此其赋值方法也与byte一样多种多样。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&public static void charGetValue() {
//把'一'赋值给char
char c1 = '一';
System.out.println(c1);
char c2 = 0b000;
System.out.println(c2);
char c3 = 047000;
System.out.println(c3);
char c4 = 19968;
System.out.println(c4);
char c5 = 0x4e00;
System.out.println(c5);
char c6 = '\u4e00';
System.out.println(c6);
int i1 = (int) '一';
System.out.println(Integer.toHexString(i1));
&/code&&/pre&&/div&&p&运行结果:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&一
&/code&&/pre&&/div&&h2&2.6 char转换为byte&/h2&&p&char存储的是字符,在很多情况下它需要被转换为字节,例如存储到文件中时,或者在网络上进行传递时。当字符转换为byte时,需要用到编码格式,例如GBK或者unicode或者UTF-8等等,不同的编码格式转换得到的byte数组也不一样,如下图所示: &br&&img src=&/v2-d1fb88b501b6b6848acdc6e6888bbdf2_b.jpg& data-rawwidth=&490& data-rawheight=&387& class=&origin_image zh-lightbox-thumb& width=&490& data-original=&/v2-d1fb88b501b6b6848acdc6e6888bbdf2_r.jpg&&&br&关于编码格式的详细介绍,可以去搜索其他文章,简要说一下: &br&Java内部使用Unicode作为char的编码格式,它又分为UCS-2(每两个字节代表一个char)和UCS-4(每四个字节代表一个char),目前主流的都是UCS-2。当把字符或者字符串转换为unicode时,会在头部加入两个字节的标志位(FE FF)表示big Endian(字节序,内存低位地址存放最高有效字节)。 &br&UTF-8编码:一个英文字符占一个字节,一个汉字占三个字节; &br&GBK编码:一个英文字符占一个字节,一个汉字占两个字节。 &br&代码如下:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&public static void charToBytes() {
byte[] buf1 = &一&.getBytes(&unicode&);
System.out.println(&---------unicode---------&);
for (int i = 0; i & buf1. i++) {
System.out.println(Integer.toHexString(buf1[i]));
System.out.println(&---------UTF-8---------&);
byte[] buf2 = &一&.getBytes(&UTF-8&);
for (int i = 0; i & buf2. i++) {
System.out.println(Integer.toHexString(buf2[i]));
System.out.println(&---------UTF-16---------&);
byte[] buf3 = &一&.getBytes(&UTF-16&);
for (int i = 0; i & buf3. i++) {
System.out.println(Integer.toHexString(buf3[i]));
System.out.println(&---------gbk---------&);
byte[] buf4 = &一&.getBytes(&gbk&);
for (int i = 0; i & buf4. i++) {
System.out.println(Integer.toHexString(buf4[i]));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
public static String byteToHexString(byte b) {
String s = Integer.toHexString(b);
int len = s.length();
if (len &= 2) {
s = s.substring(len - 2);
s = &0&+s;
&/code&&/pre&&/div&&p&运行结果:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&---------unicode---------
---------UTF-8---------
---------UTF-16---------
---------gbk---------
&/code&&/pre&&/div&&h2&2.7 byte转换为char&/h2&&p&当Java程序从外部(文件、网络、其他应用程序)读入字节流时,若该字节流代表的是字符,则需要将字节转换为字符,此动作被称为转码。转码时需要注意其编码格式,例子如下:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&public static void byteToChar() {
byte[] unicode_b = new byte[4];
unicode_b[0] = (byte) 0XFE;
unicode_b[1] = (byte) 0XFF;
unicode_b[2] = (byte) 0X4E;
unicode_b[3] = (byte) 0X2D;
String unicode_
byte[] gbk_b = new byte[2];
gbk_b[0] = (byte) 0XD6;
gbk_b[1] = (byte) 0XD0;
String gbk_
byte[] utf_b = new byte[3];
utf_b[0] = (byte) 0XE4;
utf_b[1] = (byte) 0XB8;
utf_b[2] = (byte) 0XAD;
String utf_
unicode_str = new String(unicode_b, &unicode&);
System.out.println(&unicode string is:& + unicode_str);
gbk_str = new String(gbk_b, &gbk&);
System.out.println(&gbk string is:& + gbk_str);
utf_str = new String(utf_b, &utf-8&);
System.out.println(&utf-8 string is:& + utf_str);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
&/code&&/pre&&/div&&p&运行结果如下:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&unicode string is:中
gbk string is:中
utf-8 string is:中
&/code&&/pre&&/div&&h2&2.8 转码和乱码&/h2&&p&将一种编码格式转换为另一中编码格式称之为转码。Java内部使用unicode来进行字符编码,当将字符转换为byte时需要转码,转为GBK或者其他编码;当从外部读入一段byte数组时,也需将其他编码转换为unicode编码的字符。 &br&转码发生的地方只有两个:从char到byte,或者从byte到char。转码发生时必须指定编码格式,如果不指定编码格式,则会使用默认的编码格式(一是IDE的环境中指定的格式,二是操作系统默认的编码格式),你可以用如下代码获取默认的编码格式:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&private static void showNativeEncoding() {
String enc = System.getProperty(&file.encoding&);
System.out.println(enc);
&/code&&/pre&&/div&&p&如果在转码时使用了错误的编码格式,则会出现乱码。&/p&&h1&3. 文件流&/h1&&h2&3.1 如何选择文件流的类&/h2&&p&文件流应该是Java流中使用最普遍、最广泛的流了。文件流分为两组,一组是操作字节的FileInputStream和FileOutputStream,另一组是操作字符的FileReader和FileWriter,事实上,我们还经常用到FileReader和FileWriter的父类InputStreamReader和OutputStreamWriter,原因后面会阐述。 &br&让我们从最简单的开始,使用FileWriter将一个字符串写入某文件:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&private static void writeString2File(String info,String filename){
BufferedWriter bufferedWriter=new BufferedWriter(new FileWriter(filename));
bufferedWriter.write(info);
bufferedWriter.flush();
bufferedWriter.close();
} catch (IOException e) {
e.printStackTrace();
private static void exeWriteString2File() {
String info = &Today is a good day.\r\n今天天气很好。&;
String outputfile = new File(&.&).getCanonicalPath() + File.separator + &output1.txt&;
writeString2File(info,outputfile);
} catch (IOException e) {
e.printStackTrace();
&/code&&/pre&&/div&&p&调用exeWriteString2File方法将一个包含两行字符的字符串写入了文件output1.txt。 &br&接下来,使用FileReader从output1.txt中读取信息:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&private static String readFile(String filename) {
String str =
BufferedReader bufferedReader = new BufferedReader(new FileReader(filename));
StringBuilder sb = new StringBuilder();
while ((str = bufferedReader.readLine()) != null) {
sb.append(str + &\n&);
str = sb.toString();
bufferedReader.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
private static void exeReadFile() {
String outputfile = new File(&.&).getCanonicalPath() + File.separator + &output1.txt&;
String str = readFile(outputfile);
System.out.println(str);
} catch (IOException e) {
e.printStackTrace();
&/code&&/pre&&/div&&p&调用exeReadFile方法从文件output1.txt中读取了信息,一切都很简单。 &br&以上代码的简单性来自一个假设,那就是我们假设文件编码都是统一的,但是实际编程中你会经常碰到文件编码不一致的情景。例如中文编码最常用的有GBK(多个版本的windows经常将GBK设置为默认编码格式)、UTF-8(网络传输、XML文档中经常使用这个编码格式)和UTF-16。下面的代码将同一个字符串分别使用三种编码格式写入了三个文件:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&private static void writeString2FileWithEncoding(String info,String filename,String charset){
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filename));
byte[] bytes = info.getBytes(charset);
bos.write(bytes);
bos.flush();
bos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
private static void exeWriteString2FileWithEncoding() {
String info = &Today is a good day.\r\n今天天气很好。&;
String output_gbk = new File(&.&).getCanonicalPath() + File.separator + &output_gbk.txt&;
writeString2FileWithEncoding(info,output_gbk,&gbk&);
String output_utf8 = new File(&.&).getCanonicalPath() + File.separator + &output_utf-8.txt&;
writeString2FileWithEncoding(info,output_utf8,&utf-8&);
String output_utf16 = new File(&.&).getCanonicalPath() + File.separator + &output_utf-16.txt&;
writeString2FileWithEncoding(info,output_utf16,&utf-16&);
} catch (IOException e) {
e.printStackTrace();
&/code&&/pre&&/div&&p&调用exeWriteString2FileWithEncoding将会创建三个不同编码格式的文件。在操作系统(windows、linux、Mac)中使用恰当的工具都能正确的打开这些文件。 &br&但如果我们使用前面的FileReader类来直接打开这三个文件,就会出现乱码:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&Today is a good day.
??????????á?
Today is a good day.
今天天气很好。
?? T o d a y
N?Y)Y)l _?Y}0
&/code&&/pre&&/div&&p&因此我们知道,在对文件进行读取时,需要考虑编码的问题。因此FileInputStream/FileOutputStream,FileReader/FileWriter和InputStreamReader和OutputStreamWriter这三组类都有了用武之地,下面是我总结的文件流使用指南: &br&当对文件进行拷贝、加密、压缩、摘要等与编码不相关的操作时,尽量使用字节流FileInputStream/FileOutputStream,文件的加密、压缩、摘要等功能留待后续章节(加密流、压缩流和摘要流)介绍; &br&当需要对文件读取内容或者写入指定编码格式的内容时,使用InputStreamReader和OutputStreamWriter,因为它们可以指定编码;也可以使用FileInputStream/FileOutputStream进行字节的读写,然后利用String和byte数组的转换来得到指定编码的内容; &br&当程序员可以确认默认的编码一定能满足要求时,直接使用FileReader/FileWriter来进行文件的读写。&/p&&h2&3.2 FileInputStream和FileOutputStream&/h2&&p&FileInputStream和FileOutputStream都是处理字节的类,因此使用它们时需要把信息转换为字节数组,然后进行输入输出操作。 &br&将一个字符串以GBK编码格式转换为字节后写入当前目录下的output.txt文件中:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&private static void fileOutputStreamExam() {
//得到当前目录下名为output.txt文件的路径
String outputfile = new File(&.&).getCanonicalPath() + File.separator + &output.txt&;
System.out.println(outputfile);
FileOutputStream fos = new FileOutputStream(outputfile);
String str = &Today is a good day.\r\n今天天气很好。&;
byte[] buf = str.getBytes(&GBK&);
fos.write(buf, 0, buf.length);
fos.flush();
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
&/code&&/pre&&/div&&p&将当前目录下的output.txt文件以字节流方式读入,并将读入的字节数组从GBK编码格式转换为字符(即转换为java内部使用的unicode)串:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&private static void fileInputStreamExam() {
//得到当前目录下名为output.txt文件的路径
String outputfile = new File(&.&).getCanonicalPath() + File.separator + &output.txt&;
FileInputStream fis = new FileInputStream(outputfile);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int len = 0;
while ((len = fis.read(buf)) != -1) {
baos.write(buf, 0, len);
String s = baos.toString(&GBK&);
System.out.println(s);
fis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
&/code&&/pre&&/div&&p&注意上面这段代码中使用了后面要讲到的字节数组流ByteArrayOutputStream,这个流可以存储动态长度的字节数组,因此非常适合在这里作为信息暂存的对象。&/p&&h2&3.3 InputStreamReader和OutputStreamWriter&/h2&&p&FileReader和FileWriter只能使用默认的编码格式来输入输出字符,当需要使用其他编码格式时,可以使用更加通用的类InputStreamReader和OutputStreamWriter。 &br&将字符串以GBK编码格式输出到文件output3.txt中:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&private static void outputStreamWriterExam() {
String outputfile = new File(&.&).getCanonicalPath() + File.separator + &output3.txt&;
//此处可以指定编码
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(outputfile), &GBK&);
String str = &Today is a good day.\r\n今天天气很好。&;
outputStreamWriter.write(str);
outputStreamWriter.flush();
outputStreamWriter.close();
} catch (IOException e) {
e.printStackTrace();
&/code&&/pre&&/div&&p&将output3.txt文件中的字符串以GBK编码格式读入程序中:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&private static void inputStreamReaderExam() {
String outputfile = new File(&.&).getCanonicalPath() + File.separator + &output3.txt&;
InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(outputfile), &GBK&);
CharArrayWriter charArrayWriter = new CharArrayWriter();
char[] buf = new char[1024];
int len = 0;
while((len=inputStreamReader.read(buf))!=-1){
charArrayWriter.write(buf,0,len);
String s = charArrayWriter.toString();
System.out.println(s);
inputStreamReader.close();
} catch (IOException e) {
e.printStackTrace();
&/code&&/pre&&/div&&p&注意上面这段代码中使用了字符数组流CharArrayWriter,这个流可以存储动态长度的字符数组,因此非常适合在这里作为信息暂存的对象。&/p&&h2&3.4 FileReader和FileWriter&/h2&&p&这两个类的使用前面已经介绍过了,就不赘述了。&/p&&h2&3.5 文件编码和乱码&/h2&&p&对于Java编程来说,如果使用IDE开发,则在IDE中会指定具体项目的编码,例如UTF-8或者GBK,那么在运行代码时IDE会自动加上-Dfile.encoding=UTF-8等参数,使得当前的默认编码被设置为UTF-8。如果在java运行时没有指定编码,则会使用操作系统的默认编码格式,中文windows一般默认是GBK。 &br&一般来说,输出文件时不太可能产生乱码,因为无论你以何种格式编码将字符流转换为字节流并存储到文件中时,该编码一定能够被识别出来,只要你找到合适的文件浏览工具。 &br&但是当输入文件时,如果使用了错误的编码格式进行字节–字符转换,例如将GBK编码的文件以UTF-8格式读入,则会造成乱码。&/p&&h1&4. 字节数组流和字符数组流&/h1&&p&很多网上的教程在介绍FileInputStream的时候,经常写出这样的代码:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&
FileInputStream fis = new FileInputStream(outputfile);
byte[] buf = new byte[1024];
int len = fis.read(buf);
&/code&&/pre&&/div&&p&代码中使用1024字节的byte数组来存储从文件中读入的字节,但实际工作中文件不一定会小于1024字节,因此这里需要的是一个可变长的字节数组。但是java中并不支持ArrayList,如果自己编写动态可扩展的byte数组又比较浪费时间,因此这里最合适的选择便是ByteArrayOutputStream。 &br&ByteArrayInputStream和ByteArrayOutputStream是用来表示内存中的字节数组流。其中ByteArrayOutputStream可以用来写入变长的字节数组,这对于不知道输入内容的具体长度时非常有用,例如要将一个文件的内容或者网络上的内容读入一个字节数组时。 &br&例子代码:读入一个未知大小的文件到内存中(假设此文件使用默认编码)&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&public static void byteArrayOutputStreamExam() {
FileInputStream fis = new FileInputStream(&d:\\d.txt&);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int len = 0;
while((len = fis.read(buf))!=-1){
baos.write(buf,0,len);
baos.flush();
byte[] result = baos.toByteArray();
String s = new String(result);
System.out.println(s);
baos.close();
fis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
&/code&&/pre&&/div&&p&例子代码:将一个byte数组做为输入流ByteArrayInputStream的来源,输入到一个ByteArrayOutputStream中。有趣的是,这段byte数组实际上是一段unicode编码,代表一些中文字符,程序中最后将byte数组转换为unicode编码的字符,并打印了这些字符。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&public static void byteArrayInputStreamExam() {
byte[] buf = new byte[256];
//赋值一段汉字的unicode编码,从4e00至4eff
for (int i = 0x00; i & 0 i = i + 2) {
buf[i] = 0x4e;
buf[i + 1] = (byte)
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(buf);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] getbuf = new byte[128];
int len = 0;
while ((len = byteArrayInputStream.read(getbuf)) != -1) {
byteArrayOutputStream.write(getbuf, 0, len);
byte[] result = byteArrayOutputStream.toByteArray();
//将得到的字节转换为unicode编码的字符串
String s = new String(result, &unicode&);
System.out.println(s);
} catch (IOException e) {
e.printStackTrace();
&/code&&/pre&&/div&&p&运行输出为:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&一丂丄丆丈上丌与丐丒且世丘业东丞丠丢两並丨个丬丮丰串临丶丸为丼举乀乂乄乆么乊乌乎乐乒乔乖乘乚乜乞习乢乤书乨乪乬乮买乲乴乶乸乺乼乾亀亂亄了予亊二于亐互五亖亘亚亜亞亠亢交亦亨亪京亮亰亲亴亶亸人亼亾什仂仄仆仈今仌从仐仒仔他付仚仜仞仠仢令仦仨仪们仮仰仲仴件仸仺仼仾
&/code&&/pre&&/div&&p&ByteArrayInputStream和ByteArrayOutputStream是字节数组流,那么与之对应的字符数组流则是StringReader和StringWriter(早期java使用StringBufferInputStream和StringBufferOutputStream,这两个类在jdk1.1后被废弃),也给出一个例子:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&public static void stringReaderExam() {
String str = &This is a good day.今天是个好天气。&;
StringReader stringReader = new StringReader(str);
StringWriter stringWriter = new StringWriter();
char[] buf = new char[128];
int len = 0;
while ((len = stringReader.read(buf)) != -1) {
stringWriter.write(buf, 0, len);
stringWriter.flush();
String result = stringWriter.toString();
System.out.println(result);
stringWriter.close();
stringReader.close();
} catch (IOException e) {
e.printStackTrace();
&/code&&/pre&&/div&&p&与字节数组流相比,字符数组流反而用得更少,因为StringBuilder和StringBuffer也能方便的用来存储动态长度的字符,而且大家更熟悉这些类。&/p&&h1&5. 管道流&/h1&&p&管道流是用来在多个线程之间进行信息传递的Java流,被号称是最难使用的流,被使用的频率也非常低。但事实上,管道流是非常有用的,它提供了多线程间信息传输的一种有效手段。 &br&管道流包括两个类PipedOutputStream和PipedInputStream。其中PipedOutputStream是写入者/生产者/发送者;PipedInputStream是读取者/消费者/接收者。在使用管道流之前,需要注意以下要点: &br&第一,管道流仅用于多个线程之间传递信息,若用在同一个线程中可能会造成死锁; &br&第二,管道流的输入输出是成对的,一个输出流只能对应一个输入流,使用构造函数或者connect函数进行连接; &br&第三,一对管道流包含一个缓冲区,其默认值为1024个字节,若要改变缓冲区大小,可以使用带有参数的构造函数; &br&第四,管道的读写操作是互相阻塞的,当缓冲区为空时,读操作阻塞;当缓冲区满时,写操作阻塞; &br&第五,管道依附于线程,因此若线程结束,则虽然管道流对象还在,仍然会报错“read dead end”; &br&第六,管道流的读取方法与普通流不同,只有输出流正确close时,输出流才能读到-1值。&/p&&h2&5.1 在管道中写入读取一个字符串&/h2&&p&源代码一:在线程Sender中向管道流中写入一个字符串,写入后关闭该管道流;在线程Reciever中读取该字符串。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&import java.io.ByteArrayOutputS
import java.io.IOE
import java.io.PipedInputS
import java.io.PipedOutputS
import java.util.concurrent.ExecutorS
import java.util.concurrent.E
import java.util.concurrent.TimeU
public class PipedStreamExam1 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
//创建管道流
PipedOutputStream pos = new PipedOutputStream();
PipedInputStream pis = new PipedInputStream(pos);
//创建线程对象
Sender sender = new Sender(pos);
Reciever reciever = new Reciever(pis);
//运行子线程
executorService.execute(sender);
executorService.execute(reciever);
} catch (IOException e) {
e.printStackTrace();
//等待子线程结束
executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.DAYS);
} catch (InterruptedException e) {
e.printStackTrace();
static class Sender extends Thread {
private PipedOutputS
public Sender(PipedOutputStream pos) {
this.pos =
public void run() {
String s = &This is a good day. 今天是个好天气。&;
System.out.println(&Sender:& + s);
byte[] buf = s.getBytes();
pos.write(buf, 0, buf.length);
pos.close();
} catch (IOException e) {
e.printStackTrace();
static class Reciever extends Thread {
private PipedInputS
public Reciever(PipedInputStream pis) {
this.pis =
public void run() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int len = 0;
while ((len = pis.read(buf)) != -1) {
baos.write(buf, 0, len);
byte[] result = baos.toByteArray();
String s = new String(result, 0, result.length);
System.out.println(&Reciever:& + s);
} catch (IOException e) {
e.printStackTrace();
&/code&&/pre&&/div&&p&注意,若管道流没有关闭,则使用这种方法读取管道中的信息会报错:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&while ((len = pis.read(buf)) != -1) {
baos.write(buf, 0, len);
&/code&&/pre&&/div&&p&错误代码为java.io.IOException: Write end dead;发生这个错误的原因在于,由于管道未关闭,所以read语句不会读到-1,因此PipedInputStream会持续从管道中读取数据,但是因为Sender线程已经结束,所以会抛出“Write end dead”异常。&/p&&h2&5.2 在管道中多次写入读取字符串&/h2&&p&管道流之所以难用,是因为很多情况下写入管道的数据难以区分“长度”,它的设计理念是“通过管道,将源数据源源不绝的发送到目的地”。因此,如果应用场景为“通过管道,将一段一段的数据一次次的发送到目的地”,就会发现很难使用。为此,使用Java多线程中的信号量来进行同步可以很好的满足此需求。 &br&源代码二:在线程Sender中反复写入多个字符串,在Reciever中多次接收字符串;使用两个信号量Semaphore来控制写入和读取。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&import java.io.IOE
import java.io.PipedInputS
import java.io.PipedOutputS
import java.util.R
import java.util.concurrent.ExecutorS
import java.util.concurrent.E
import java.util.concurrent.S
public class PipedStreamExam2 {
//控制读取的信号量,初始状态为0
public static Semaphore readSignal = new Semaphore(0,true);
//控制写入的信号量,初始状态为1,表示允许一次写入
public static Semaphore writeSignal = new Semaphore(1,true);
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
PipedOutputStream pos = new PipedOutputStream();
PipedInputStream pis = new PipedInputStream(pos);
Sender sender = new Sender(pos);
Reciever reciever = new Reciever(pis);
executorService.execute(sender);
executorService.execute(reciever);
} catch (IOException e) {
e.printStackTrace();
executorService.shutdown();
static class Sender extends Thread {
private PipedOutputS
public Sender(PipedOutputStream pos) {
this.pos =
public void run() {
for (int i = 0; i & 10; i++) {
Thread.sleep(new Random().nextInt(1000));
//获取写入信号量
writeSignal.acquire();
String content = &today is a good day. 今天是个好天气:&+i;
System.out.println(&Sender:& + content);
pos.write(content.getBytes(&utf-8&));
//释放读取信号量
readSignal.release();
pos.close();
readSignal.release();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
static class Reciever extends Thread {
private PipedInputS
public Reciever(PipedInputStream pis) {
this.pis =
public void run() {
byte[] buf = new byte[1024];
int len = 0;
while(true) {
//获取读取信号量
readSignal.acquire();
len = pis.read(buf);
if(len == -1)
s = new String(buf, 0, len, &utf-8&);
System.out.println(&Reciever:& + s);
//释放写入信号量
writeSignal.release();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
&/code&&/pre&&/div&&h2&5.3 通过管道将信息流发送至另一个线程&/h2&&p&经过思考,我认为管道流应用的经典场景应该是将某个输入流从一个线程通过管道发送到另一个线程进行处理,从而提升程序效率。例如:线程A负责从网络上持续读取信息,线程B负责处理信息,那么线程A就会将读取的信息通过管道流发送至线程B,从而确保线程A的读取性能。 &br&下面的例子中,Sender线程从文件中读取未知长度的字节流,然后交给Reciever线程,Reciever线程将此字节流存入另一个文件:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&import java.io.*;
import java.util.concurrent.ExecutorS
import java.util.concurrent.E
import java.util.concurrent.TimeU
public class PipedStreamExam3 {
public static void main(String[] args) {
PipedInputStream pis = new PipedInputStream();
PipedOutputStream pos = new PipedOutputStream(pis);
Sender sender = new Sender(pos);
Reciever reciever = new Reciever(pis);
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(sender);
executorService.execute(reciever);
executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.DAYS);
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
static class Sender extends Thread {
private PipedOutputStream pos =
public Sender(PipedOutputStream pos) {
this.pos =
public void run() {
FileInputStream fis = new FileInputStream(&d:\\input.txt&);
byte[] buf = new byte[1024];
int len = 0;
while ((len = fis.read(buf)) != -1) {
pos.write(buf, 0, len);
pos.flush();
pos.close();
fis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
static class Reciever extends Thread {
private PipedInputStream pis =
public Reciever(PipedInputStream pis) {
this.pis =
public void run() {
FileOutputStream fos = new FileOutputStream(&d:\\output.txt&);
byte[] buf = new byte[1024];
int len = 0;
while ((len = pis.read(buf)) != -1) {
fos.write(buf, 0, len);
fos.flush();
fos.close();
pis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
&/code&&/pre&&/div&&h2&5.4 管道流小结&/h2&&p&比起文件流、字节数组流和对象流这样使用很普遍的流,管道流有很多不同之处。首先它必须依附线程对象,当线程对象已经失效而流未关闭时会出错;其次它往往读不到-1,因此在很多场景中需要程序员自己来保证同步;第三管道流能够保证良好的互斥,这往往是很有用的一点。多加练习,管道流大有用武之地。&/p&&h1&6. Java过滤器概论&/h1&&p&前面章节讲到的各种Java流,包括文件流、字节数组流、管道流等等被称之为原始流,它们提供了对某类数据的输入输出功能。为了在流处理的过程中简化和标准化某一类功能,例如缓冲、压缩、加密、摘要等,Java提供了一系列过滤器类,每组类提供了一种典型的信息处理功能。根据操作对象是字节还是字符,过滤器又分为过滤器流和阅读器/书写器两类,举例如下:&/p&&ul&&li&BufferedInputStream/BufferedOutputStream提供了字节流的缓冲功能;&/li&&li&BufferedReader/BufferedWriter提供了字符流的缓冲功能;&/li&&li&DataInputStream/DataOutputStream用来将java的简单数据类型和字符串保存为二进制格式,并从二进制格式读取;&/li&&li&ZipInputStream/ZipOutputStream提供了字节流的zip格式压缩功能;&/li&&li&GZIPInputStream/GZIPOutputStream提供了字节流的gzip格式压缩功能;&/li&&li&DigestInputStream/DigestOutputStream提供了字节流的信息摘要功能;&/li&&li&CipherInputStream/CipherOutputStream提供了字节流的加密解密功能;&/li&&/ul&&h1&6.1 过滤器使用规则&/h1&&p&过滤器可以和其他过滤器、原始流和阅读器/书写器链接使用,但要遵守以下规则:&/p&&ul&&li&过滤器流可以置于原始流或其他过滤器流之上;&/li&&li&阅读器/书写器可置于原始流、过滤器流或其他阅读器/书写器之上;&/li&&li&过滤器流不能放在阅读器/书写器之后。&/li&&/ul&&h1&6.2 最简单的过滤器-缓冲流&/h1&&p&在文件拷贝的过程中加入缓冲功能,提升性能:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&import java.io.*;
public class BufferedStreamExam {
public static void main(String[] args) {
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(&d:\\input.txt&));
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(&d:\\output.txt&));
byte[] buf = new byte[1024];
int len = 0;
while ((len = bufferedInputStream.read(buf)) != -1) {
bufferedOutputStream.write(buf, 0, len);
bufferedOutputStream.flush();
bufferedOutputStream.close();
bufferedInputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
&/code&&/pre&&/div&&p&观察代码可知,当多个流链接到一起时,关闭最上层的流即可。为了防止多次关闭一个链条中的流,在创建多个流时仅保留最上层流的对象引用,类似: &br&&br&BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(&d:\\input.txt&)); &br&&br&后面会讲到,为了创建一个带有缓冲、加密、压缩的文件输入流,我们会这样写代码:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&BufferedInputStream bis = new BufferedInputStream(
new CipherInputStream(
new GZIPInputStream(
new FileInputStream(desFile)),pbe));
&/code&&/pre&&/div&&h1&7. 数据流&/h1&&p&数据流DataOutputStream和DataInputStream用来将Java的简单数据类型和字符串保存为二进制格式,并从二进制格式读取。使用它们时需要注意以下几点: &br&第一,DataOutputStream输出的二进制流,必须使用DataInputStream读入,且各个变量的输出输出顺序必须相同; &br&第二,boolean,byte,short,char,int,long,float,double和String可以使用相应的write和read方法进行输出和输入,例如writeInt和readInt; &br&第三,输入输出字符串时使用readUTF和writeUTF,避免使用writeChars和wirteBytes等方法。因为writeUTF方法中将字符串长度一并保存,所以在readUTF中可以正确读取。 &br&例子如下:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&public static void dataOutputStreamExam() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
boolean b =
dos.writeBoolean(b);
showDataByte(baos);
byte by = 0x03;
dos.writeByte(by);
showDataByte(baos);
short sh = 42;
dos.writeShort(sh);
showDataByte(baos);
char c = '一';
dos.writeChar(c);
showDataByte(baos);
int i = 1234567;
dos.writeInt(i);
showDataByte(baos);
long l = L;
dos.writeLong(l);
showDataByte(baos);
float f = 3.1415926f;
dos.writeFloat(f);
showDataByte(baos);
double dou = 3.;
dos.writeDouble(dou);
showDataByte(baos);
String s = &This is a good day. 今天是个好天气.&;
dos.writeUTF(s);
showDataByte(baos);
int i2 =233;
dos.writeInt(i2);
showDataByte(baos);
dos.flush();
baos.flush();
//依次读取数据
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(baos.toByteArray()));
b = dis.readBoolean();
System.out.println(b);
by = dis.readByte();
System.out.println(by);
sh =dis.readShort();
System.out.println(sh);
c=dis.readChar();
System.out.println(c);
i=dis.readInt();
System.out.println(i);
l=dis.readLong();
System.out.println(l);
f=dis.readFloat();
System.out.println(f);
dou=dis.readDouble();
System.out.println(dou);
s=dis.readUTF();
System.out.println(s);
i2=dis.readInt();
System.out.println(i2);
dis.close();
dos.close();
} catch (IOException e) {
e.printStackTrace();
public static void showDataByte(ByteArrayOutputStream baos) {
byte[] buf = baos.toByteArray();
for (int j = 0; j & buf. j++) {
System.out.print( byteToHexString(buf[j])+&,&);
System.out.println(&------------------------------&);
public static String byteToHexString(byte b) {
String s = Integer.toHexString(b);
int len = s.length();
if (len &= 2) {
s = s.substring(len - 2);
s = &0&+s;
&/code&&/pre&&/div&&p&例子中使用了ByteArrayOutputStream和ByteArrayInputStream来作为输入输出流,这样可以显得程序易读。事实上更多情况下是使用文件流来进行输出输入。&/p&&h1&8. 压缩流&/h1&&p&压缩流可以将输入的数据变为压缩格式后进行输出,或者读取压缩格式的数据后,解压为正常数据。&/p&&h2&8.1 将一个文件压缩为一个压缩文件&/h2&&p&注意ZipEntry的使用,一个ZipEntry代表压缩文件中的一个文件入口。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&import java.io.*;
import java.util.zip.ZipE
import java.util.zip.ZipOutputS
* Created by test2 on .
public class ZipStreamExam1 {
public static void main(String[] args) {
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(&D:\\input.txt&));
ZipOutputStream zipOutputStream = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(&d:\\output.zip&)));
byte[] buf = new byte[1024];
int len = 0;
ZipEntry ze = new ZipEntry(&input.txt&);
zipOutputStream.putNextEntry(ze);
while ((len = bufferedInputStream.read(buf)) != -1) {
zipOutputStream.write(buf, 0, len);
zipOutputStream.flush();
zipOutputStream.close();
bufferedInputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
&/code&&/pre&&/div&&h2&8.2 将一个多重目录压缩为一个文件&/h2&&p&代码中定义使用了递归函数zipDir:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&import java.io.*;
import java.util.zip.ZipE
import java.util.zip.ZipOutputS
public class ZipStreamExam2 {
public static void main(String[] args) {
File file = new File(&d:\\zipmultidir&);
ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(&d:\\zipmultidir.zip&)));
zipDir(file, zos, file);
zos.flush();
zos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
//压缩一个目录至zip文件
private static void zipDir(File dir, ZipOutputStream zos, File rootDir) throws IOException {
if (!dir.isDirectory())
File[] files = dir.listFiles();
for (int i = 0; i & files. i++) {
if (files[i].isFile()) {
System.out.println(files[i].getAbsolutePath());
String now = files[i].getAbsolutePath();
String root = rootDir.getAbsolutePath();
String name = now.substring(root.length() + 1);
System.out.println(name);
FileInputStream fis = new FileInputStream(files[i]);
byte buf[] = new byte[1024];
int len = 0;
ZipEntry ze = new ZipEntry(name);
zos.putNextEntry(ze);
while ((len = fis.read(buf)) != -1) {
zos.write(buf, 0, len);
fis.close();
} else if (files[i].isDirectory()) {
zipDir(files[i], zos, rootDir);
&/code&&/pre&&/div&&h2&8.3 将一个压缩文件解压到当前目录&/h2&&p&该压缩文件中可能有多重目录结构。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&import java.io.*;
import java.util.zip.ZipE
import java.util.zip.ZipInputS
* Created by test2 on .
public class ZipStreamExam3 {
public static void main(String[] args) {
File srcFile = new File(&d:\\zipmultidir.zip&);
System.out.println(srcFile.getCanonicalPath());
String curDir = srcFile.getParent()+File.separator+&destDir&+File.
ZipInputStream zipInputStream = new ZipInputStream(new BufferedInputStream(new FileInputStream(srcFile)));
ZipEntry ze =
byte[] buf = new byte[1024];
int len = 0;
while ((ze = zipInputStream.getNextEntry()) != null) {
String filePath = curDir + ze.getName();
File destFile = new File(filePath);
File destDir = new File(destFile.getParent());
if(!destDir.exists()){
destDir.mkdirs();
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(destFile));
while ((len = zipInputStream.read(buf)) != -1) {
bufferedOutputStream.write(buf, 0, len);
bufferedOutputStream.flush();
bufferedOutputStream.close();
zipInputStream.close();
} catch (IOException e) {
e.printStackTrace();
&/code&&/pre&&/div&&h2&9. 摘要流&/h2&&p&摘要流在对一组信息进行输入输出操作的同时,将摘要信息记录下来,最后通过getMessageDigest().digest()方法得到摘要信息的byte数组。 &br&摘要流示例代码:对字符串进行摘要和对文件进行摘要。&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&import java.io.*;
import java.security.DigestOutputS
import java.security.MessageD
import java.security.NoSuchAlgorithmE
public class DigestStreamExam {
public static void main(String[] args) {
String str = &Today is a good day. 今天是个好天气。&;
digestString(str);
File file = new File(&d:\\a.txt&);
digestFile(file);
private static void digestFile(File file) {
MessageDigest sha = MessageDigest.getInstance(&SHA&);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
DigestOutputStream digestOutputStream = new DigestOutputStream(byteArrayOutputStream, sha);
FileInputStream fis = new FileInputStream(file);
byte[] buf = new byte[1024];
int len = 0;
while ((len = fis.read(buf)) != -1) {
digestOutputStream.write(buf, 0, len);
digestOutputStream.flush();
digestOutputStream.close();
byte[] result = digestOutputStream.getMessageDigest().digest();
for (int i = 0; i & result. i++) {
System.out.print(byteToHexString(result[i]));
System.out.println();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
public static void digestString(String str) {
MessageDigest sha = MessageDigest.getInstance(&SHA&);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
DigestOutputStream digestOutputStream = new DigestOutputStream(byteArrayOutputStream, sha);
byte[] bytes = str.getBytes();
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
byte[] buf = new byte[1024];
int len = 0;
while ((len = byteArrayInputStream.read(buf)) != -1) {
digestOutputStream.write(buf, 0, len);
digestOutputStream.flush();
digestOutputStream.close();
byte[] result = digestOutputStream.getMessageDigest().digest();
for (int i = 0; i & result. i++) {
System.out.print(byteToHexString(result[i]));
System.out.println();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
public static String byteToHexString(byte b) {
String s = Integer.toHexString(b);
int len = s.length();
if (len &= 2) {
s = s.substring(len - 2);
&/code&&/pre&&/div&&p&运行输出为:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&cbb7ade978aa332a94
788dc68d752ffb1a12c52c5436cbdc
&/code&&/pre&&/div&&p&注意,为了输出美观,我提供了一个byteToHexString方法,该方法可以将byte转换为一个两个字符的String,该String中包含了该byte的十六进制格式。 &br&摘要流被广泛用于各种信息的验证中,是一种很常用的手段。 &br&本例中的加密流使用了PBEWithMD5AndDES加密算法,该算法是对称加密算法,加密和解密使用的密钥是一样的。不同的是除了密码之外,还需要“盐”。代码如下:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&import javax.crypto.*;
import javax.crypto.spec.DESKeyS
import javax.crypto.spec.PBEKeyS
import javax.crypto.spec.PBEParameterS
import javax.crypto.spec.SecretKeyS
import java.io.FileInputS
import java.io.FileNotFoundE
import java.io.FileOutputS
import java.io.IOE
import java.security.*;
import java.security.spec.InvalidKeySpecE
public class CypherStreamExam {
public static void main(String[] args) {
String infile = &D:\\a.txt&;
String outfile = &D:\\a.des&;
String password = &This is my key&;
byte[] salt = initSalt();
encryptFile(infile, outfile, password, salt);
infile = &D:\\a.des&;
outfile = &D:\\a.out&;
decryptFile(infile, outfile, password, salt);
//产生加密所用的盐
public static byte[] initSalt() {
//实例化安全随机数
SecureRandom random = new SecureRandom();
return random.generateSeed(8);
private static void decryptFile(String infile, String outfile, String password, byte[] salt) {
byte[] desKeyData = password.getBytes();
FileInputStream fin = new FileInputStream(infile);
FileOutputStream fout = new FileOutputStream(outfile);
Provider sunJce = new com.sun.crypto.provider.SunJCE();
Security.addProvider(sunJce);
//创建密钥
char[] pbeKeyData = password.toCharArray();
PBEKeySpec pbeKeySpec = new PBEKeySpec(pbeKeyData);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(&PBEWithMD5AndDES&);
SecretKey pbeKey = keyFactory.generateSecret(pbeKeySpec);
PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 100);
Cipher pbe = Cipher.getInstance(&PBEWithMD5AndDES&);
pbe.init(Cipher.DECRYPT_MODE, pbeKey, paramSpec);
CipherOutputStream cout = new CipherOutputStream(fout, pbe);
byte[] input = new byte[64];
int len = 0;
while ((len = fin.read(input)) != -1) {
cout.write(input, 0, len);
cout.flush();
cout.close();
fin.close();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
private static void encryptFile(String filein, String fileout, String password, byte[] salt) {
byte[] desKeyData = password.getBytes();
FileInputStream fin = new FileInputStream(filein);
FileOutputStream fout = new FileOutputStream(fileout);
Provider sunJce = new com.sun.crypto.provider.SunJCE();
Security.addProvider(sunJce);
//创建密钥
char[] pbeKeyData = password.toCharArray();
PBEKeySpec pbeKeySpec = new PBEKeySpec(pbeKeyData);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(&PBEWithMD5AndDES&);
SecretKey pbeKey = keyFactory.generateSecret(pbeKeySpec);
PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 100);
Cipher pbe = Cipher.getInstance(&PBEWithMD5AndDES&);
pbe.init(Cipher.ENCRYPT_MODE, pbeKey, paramSpec);
CipherOutputStream cout = new CipherOutputStream(fout, pbe);
byte[] input = new byte[64];
int len = 0;
while ((len = fin.read(input)) != -1) {
cout.write(input, 0, len);
cout.flush();
cout.close();
fin.close();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
&/code&&/pre&&/div&&p&在真实的应用中,加密方和解密方都知道密码,这个密码一般来说是双方约定的,例如你银行账户密码。如果这个密码被泄露,也不用担心,因为解密还需要“盐”。盐一般来说是由一种随机生成算法生成的,而且生存期很短。例如网上银行使用的U Key就是一种盐生成器,特别是“中国银行”的U Key,在进行转账时直接要求用户输入6位数字的盐。由于网上银行的服务端和客户手中的U Key使用同一种盐生成算法(这个算法当然是保密的,也无从泄露),因此保证了盐不会泄露,从而保证用户进行操作时不会泄密。 &br&将一组信息缓冲、加密、压缩后保存到一个文件中,然后从此文件中读取该信息,经解密、解压缩后输出到屏幕。代码如下:&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTP
import javax.crypto.*;
import javax.crypto.spec.PBEKeyS
import javax.crypto.spec.PBEParameterS
import java.io.*;
import java.security.*;
import java.security.spec.InvalidKeySpecE
import java.util.zip.GZIPInputS
import java.util.zip.GZIPOutputS
* Created by test2 on .
* 真实世界的流使用范例
public class RealWorldStream {
public static void main(String[] args) {
String srcFile = &D:\\src.txt&;
String desFile = &D:\\src.des.gz&;
makeSourceFile(srcFile);
//密码和盐
String password = &This is my key&;
byte[] salt = initSalt();
//加密压缩保存
gzipCipherFile(srcFile, desFile, password, salt);
//解密解压,输出到屏幕
ungzipCipherFile(desFile, password, salt);
private static void ungzipCipherFile(String desFile, String password, byte[] salt) {
//创建密钥
byte[] desKeyData = password.getBytes();
Provider sunJce = new com.sun.crypto.provider.SunJCE();
Security.addProvider(sunJce);
char[] pbeKeyData = password.toCharArray();
PBEKeySpec pbeKeySpec = new PBEKeySpec(pbeKeyData);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(&PBEWithMD5AndDES&);
SecretKey pbeKey = keyFactory.generateSecret(pbeKeySpec);
PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 100);
Cipher pbe = Cipher.getInstance(&PBEWithMD5AndDES&);
pbe.init(Cipher.DECRYPT_MODE, pbeKey, paramSpec);
BufferedInputStream bis = new BufferedInputStream(
new CipherInputStream(
new GZIPInputStream(
new FileInputStream(desFile)),pbe));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buff =new byte[1024];
int len=0;
while((len=bis.read(buff))!=-1){
baos.write(buff,0,len);
baos.flush();
String str = baos.toString(&UTF-16&);
baos.close();
bis.close();
System.out.println(str);
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
//产生加密所用的盐
public static byte[] initSalt() {
//实例化安全随机数
SecureRandom random = new SecureRandom();
return random.generateSeed(8);
//将源文件进行加密压缩后保存到目标文件
private static void gzipCipherFile(String srcFile, String desFile, String password, byte[] salt) {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
//创建密钥
byte[] desKeyData = password.getBytes();
Provider sunJce = new com.sun.crypto.provider.SunJCE();
Security.addProvider(sunJce);
char[] pbeKeyData = password.toCharArray();
PBEKeySpec pbeKeySpec = new PBEKeySpec(pbeKeyData);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(&PBEWithMD5AndDES&);
SecretKey pbeKey = keyFactory.generateSecret(pbeKeySpec);
PBEParameterSpec paramSpec = new PBEParameterSpec(salt, 100);
Cipher pbe = Cipher.getInstance(&PBEWithMD5AndDES&);
pbe.init(Cipher.ENCRYPT_MODE, pbeKey, paramSpec);
BufferedOutputStream bos = new BufferedOutputStream(
new CipherOutputStream(
new GZIPOutputStream(
new FileOutputStream(desFile)), pbe));
byte[] buff = new byte[1024];
int len = 0;
while ((len = bis.read(buff)) != -1) {
bos.write(buff, 0, len);
bos.flush();
bos.close();
bis.close();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidKeySpecException e) {
e.printStackTrace();
//创建一个程序中用来执行的源文件
private static void makeSourceFile(String srcFile) {
File file = new File(srcFile);
String str = &Today is a good day. 今天天气很好。&;
//使用JAVA内部的编码标准UTF-16来进行char到byte的编码转换
byte[] bytes = str.getBytes(&UTF-16&);
//写入文件
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
FileOutputStream fos = new FileOutputStream(file);
byte[] buff = new byte[1024];
int len = 0;
while ((len = bais.read(buff)) != -1) {
fos.write(buff, 0, len);
fos.flush();
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
&/code&&/pre&&/div&&p&如果你已经阅读过本博客的其他文章,理解这一段代码也没有任何问题了。Java的流就是这样使用的,入手稍微有一点困难,但是熟悉之后很容易写出功能强大的代码。&/p&
1.前言在众多流行的编程语言中,Java对IO的处理应该是最特殊的,Java打着“尽量减少IO类的设计理念”,搞出了目前应该是最复杂的一套IO相关类,并称之为Java流。 对于新手来说,Java流包含的类众多,含义混杂,上手困难且其中暗藏的陷阱众多;但是对于熟悉…
&img src=&/v2-91efcae203cbdaf20b42_b.jpg& data-rawwidth=&1439& data-rawheight=&1080& class=&origin_image zh-lightbox-thumb& width=&1439& data-original=&/v2-91efcae203cbdaf20b42_r.jpg&&&p&目前是我作为中国科学技术大学本科生的第四个年头,还有七、八个月就要毕业了,趁记忆尚好,兴趣尚在,记录一些故事,希望对读者有用。&br&&/p&&p&首先是关于这篇文章的标题。『抽空把大学上了!』来自于题图中的户外广告,这是我在 2016 年 7 月中旬在上海火车站旁看到的,读到这句话的一瞬间联想到我的本科生涯,遂随手拍下:)我的这三年多大多数时间都花在了一些『奇奇怪怪』的东西上,和同届很多其他学生的『正常生活』相比,差了十万八千里。回首看去,首先想到的不是课堂,不是考试,不是谈恋爱,而是泡实验室、写代码、刷英文原版、到处旅chu行chai、(刷知乎).... 似乎一路下来,上大学反而变成了抽空做的事情(当然不&/p&&br&&p&下文将以时间顺序简单介绍我经历过的一些事情,包括科大和科大之外的世界。由于一些客观限制,我不会详细描述,不过希望了解详情或者有疑问的同学可以在评论中说明。&/p&&p&&b&WARNING:&/b&本文中所描述的行为可能对 GPA 有害,请谨慎模仿!&/p&&p&&b&NOTE: &/b&本文中的某些链接可能从墙内打不开....&/p&&h2&科大转专业&/h2&&p&如果说中科大的整个教育系统相比其他高校的不同点中让我只挑一个&b&亮点&/b&,我会挑转专业。我和大多数人一样,都是在大一结束这个节点转的,不过其中大概一半人是在大一下学期就通过『缓修+选修』的方式&b&事实上&/b&转了专业(我也是其中一员)。具体而言,我是从生命科学专业转到计算机科学与技术专业。&/p&&p&虽然我高中就开始编程,但是其实一开始面对被分配到生命科学专业这个事实我的态度还是很积极的(不然也不会去玩 iGEM ...)。但是一个学期对生物的学习和对专业出路的了解还是让我越来越相信自己并不适合也不喜欢做这样的工作(虽然出于猎奇心理还是上了第二学期的生物学实验。。。),同时继续折腾计算机相关的东西也让我坚定了自己搞 CS 一百年不动摇的信念。&/p&&p&虽然如此,我在科大的朋友、尤其是老一辈的(大雾),大多都是在生院或相关活动中认识的,其中有不少都是坚定搞生物一百年不动摇的。但是这些同学其实反而和我更像:曾涉猎多个领域,逐渐对自己的偏好和擅长形成清醒的认识,同时有长期的目标和完成该目标的强烈动机。(注:当然能不能实现是另一回事 = =)&/p&&p&总而言之,有一个明确的努力方向(注:不一定等于纸面上的专业)对于本科生来说还是很重要的。要基于&b&自己的能力和喜好&/b&多做调查、体验和思考,尽快确定方向,这样日后无论是自身的努力和还是历史的行程才有意义。&/p&&br&&h2&IGEM 遗传工程机械竞赛&/h2&&p&&a href=&/?target=http%3A//igem.org& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&igem.org&i class=&icon-external&&&/i&&/a&&br&&/p&&p&关于这个比赛,我曾在结束后写了一篇&a href=&/?target=http%3A//blog.zhenzhang.me//igem.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&博文&i class=&icon-external&&&/i&&/a&批判了一番。但是回想起来其实:&/p&&ol&&li&自己作为一个大一学生做的也并不够好&/li&&li&无论如何,自己还是学到了很多东西的&br&&/li&&/ol&&br&&p&不过这个比赛就评分标准而言确实更重视 &b&想法的『创造性』&/b&、&b&团队协作&/b&(因为需完成的内容多样所以一个队伍往往 10 人以上,科大这边基本上是 20 人以上,这已经比一个正常的研究组大了)、&b&演示能力&/b&(包括 wiki website(做网站), poster(海报), presentation(演讲), local events(组织本地活动)......),还有很重要的 -- &b&筹款和管钱能力&/b&(注册费 + 出差费 + 实验费:几十万上下...)。&/p&&p&加之科大这边指导老师不怎么管你的现象,基本上,如果你是队长的话受到的训练是如何成为一个 PI (&a href=&/?target=https%3A//en.wikipedia.org/wiki/Principal_investigator& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Principal investigator&i class=&icon-external&&&/i&&/a&,俗称『老板』),如果你是队员的是视角色而言可能是 post-doc(博后)、research engineer(研发工程师)、Ph.D.(『搬砖』的)、supporting staff(『数钱』的)....&/p&&p&这个竞赛推荐任何专业的低年级本科生参加,但是对于想做 CS(程序员工作 or 计算机科学研究)的高年级本科生并不推荐。&/p&&h2&ISC 学生超算竞赛&/h2&&p&&a href=&/?target=http%3A///events/2015/isc15-student-cluster-competition/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&ISC 2015 Student Cluster Competition&i class=&icon-external&&&/i&&/a&&br&&/p&&p&相比 iGEM,这个比赛规模要小很多,每年邀请的队伍在20支以内吧,不过相似之处是都很烧钱。下面是上面页面的介绍部分中的关键:&/p&&blockquote&&p&The competition features small teams that compete to demonstrate the incredible capabilities of state-of-the-art high-performance cluster hardware and software. In a real-time challenge, 11 teams of six undergraduate and/or high school students will build a small cluster of their own design on the ISC exhibit floor and race to demonstrate the greatest performance across a series of benchmarks and applications.&/p&&/blockquote&&p&(坑:有空我会在这里整段翻译一下....)&/p&&p&然后我个人觉得首先在电脑普及了的今天,大多数人、包括很多 CS 专业学生,可能对于『超算』有着不正确的认识,所以我学到的最重要的东西是对于这个概念有了更好的认识。其他更多细节可以见另一篇&a href=&/?target=http%3A//blog.zhenzhang.me//isc-scc.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&博文&i class=&icon-external&&&/i&&/a&。&/p&&p&这个竞赛推荐高年级的 CS 学生参加,最好是学习过计算机体系结构(I mean David Patterson's book: &i&Computer Architecture: A Quantitative Approach&/i&)的,但是 HPC(高性能计算)背景并不需要。&/p&&h2&GSoC 谷歌编程之夏&/h2&&p&&a href=&/?target=https%3A//en.wikipedia.org/wiki/Google_Summer_of_Code& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Google Summer of Code&i class=&icon-external&&&/i&&/a&&br&&/p&&p&参加之前,我原以为会有很多中国学生;参加之后,才发现这个是印度学生的舞台 = = 中国学生寥寥无几(2016年:1206名学生中&b&34名&/b&来自中国,&b&454名&/b&来自印度)。&/p&&p&我前几天和一个外国人讨论过中国有庞大的程序员群体,但从事基础设施开发的数量相对很少,从事著名开源项目贡献的更是不多。当然市场需求和工作文化是主要原因,但是他也指出说很多开源社区的开发者都是从学生时代开始参与的,也许中国的 CS 专业学生没能利用本科这段『没有经济压力、有着大量时间』的机会开始接触这些项目,等工作了也就更没有动机了。&/p&&p&我个人的看法是,虽然课余时间是否充足对于中国高校(尤其是&b&某些&/b&高校)的学生是一个尚待商讨的问题,但是克服一下困难的话,参与 GSoC 可以:&/p&&ol&&li&补充国内 CS 课程设计的不足之处&/li&&li&掌握核心科技(雾:并非所有 GSoC 项目都是搞编译器、运行时、浏览器之类的,但是开源的项目还是以基础设施居多)&/li&&li&从没有经济压力升级为经济独立(!)&br&&/li&&/ol&&br&&p&注:我同样有一篇之前的&a href=&/?target=http%3A//blog.zhenzhang.me//servo-file-api.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&博文&i class=&icon-external&&&/i&&/a&记述本人项目的相关细节。&/p&&h2&MPI-SWS 研究实习&/h2&&p&&a href=&/?target=https%3A//www.mpi-sws.org/research-careers/%23internships& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Max Planck Institute for Software Systems&i class=&icon-external&&&/i&&/a&&br&&/p&&p&MPI-SWS 是类似于中科院系统的德国连锁(雾)学术机构 MPG 中的一个分支

我要回帖

更多关于 java将string转list 的文章

 

随机推荐