关于流操作一些容易被忽视的细节:
1. 高级流在关闭的时候,只需要关闭其封装的低级流即可,高级流自动关闭。
2.InputStreamReader 和 OutputStreamReader 可以将输入输出字节流转化为字符流,因为字符流操作更为简便。
3.推回输入流,是PushbackInputStream 和 PushbackReader ,可以将内容返回缓冲区,读取数据时,优先从缓冲区查找。
可以用来查找某一连续字符串,普通流会涉及从中间割断目标字符串的可能。举例如下:
import java.io.*;
public class PushbackTest
{
public static void main(String[] args)
{
try(
// 创建一个PushbackReader对象,指定推回缓冲区的长度为64
PushbackReader pr = new PushbackReader(new FileReader(
"PushbackTest.java") , 64))
{
char[] buf = new char[32];
// 用以保存上次读取的字符串内容
String lastContent = "";
int hasRead = 0;
// 循环读取文件内容
while ((hasRead = pr.read(buf)) > 0)
{
// 将读取的内容转换成字符串
String content = new String(buf , 0 , hasRead);
int targetIndex = 0;
// 将上次读取的字符串和本次读取的字符串拼起来,
// 查看是否包含目标字符串, 如果包含目标字符串
if ((targetIndex = (lastContent + content)
.indexOf("new PushbackReader")) > 0)
{
// 将本次内容和上次内容一起推回缓冲区
pr.unread((lastContent + content).toCharArray());
// 指定读取前面len个字符
int len = targetIndex > 32 ? 32 : targetIndex;
// 再次读取指定长度的内容(就是目标字符串之前的内容)
pr.read(buf , 0 , len);
// 打印读取的内容
System.out.print(new String(buf , 0 ,len));
System.exit(0);
}
else
{
// 打印上次读取的内容
System.out.print(lastContent);
// 将本次内容设为上次读取的内容
lastContent = content;
}
}
}
catch (IOException ioe)
{
ioe.printStackTrace();
}
}
}
4.java虚拟机读写其它进程的数据
import java.io.*;
import java.util.*;
/**
* Description:
* <br/>网站: <a href="http://www.crazyit.org">疯狂Java联盟</a>
* <br/>Copyright (C), 2001-2012, Yeeku.H.Lee
* <br/>This program is protected by copyright laws.
* <br/>Program Name:
* <br/>Date:
* @author Yeeku.H.Lee kongyeeku@163.com
* @version 1.0
*/
public class WriteToProcess
{
public static void main(String[] args)
throws IOException
{
// 运行java ReadStandard命令,返回运行该命令的子进程
Process p = Runtime.getRuntime().exec("java ReadStandard");
try(
// 以p进程的输出流创建PrintStream对象
// 这个输出流对本程序是输出流,对p进程则是输入流
PrintStream ps = new PrintStream(p.getOutputStream()))
{
// 向ReadStandard程序写入内容,这些内容将被ReadStandard读取
ps.println("普通字符串");
ps.println(new WriteToProcess());
}
}
}
// 定义一个ReadStandard类,该类可以接受标准输入,
// 并将标准输入写入out.txt文件。
class ReadStandard
{
public static void main(String[] args)
{
try(
// 使用System.in创建Scanner对象,用于获取标准输入
Scanner sc = new Scanner(System.in);
PrintStream ps = new PrintStream(
new FileOutputStream("out.txt")))
{
// 增加下面一行将只把回车作为分隔符
sc.useDelimiter("\n");
// 判断是否还有下一个输入项
while(sc.hasNext())
{
// 输出输入项
ps.println("键盘输入的内容是:" + sc.next());
}
}
catch(IOException ioe)
{
ioe.printStackTrace();
}
}
}
5.java任意访问文件夹AccessRandomFile
向一个文件夹增加内容AccessRandomFile,基本原理就是先截取加入点之后的文本,放入临时文件,然后向原来的文本加字符串,最后把临时文本的内容导入到原文本。
import java.io.*;
class RandomAccessTest
{
public static void insertFile(String fileName,int pos,String insertContext) throws Exception
{
File temp=File.createTempFile("tem",null);
temp.deleteOnExit();
try(
BufferedInputStream br=new BufferedInputStream(new FileInputStream(temp));
BufferedOutputStream bw=new BufferedOutputStream(new FileOutputStream(temp));
RandomAccessFile ra=new RandomAccessFile(fileName,"rw"))
{
ra.seek(pos);
int leng=0;
byte[] b=new byte[1024];
while((leng=ra.read(b)) >0)
{
bw.write(b,0,leng);
}
bw.close(); //这一句话比较关键,否则无法把文本内容加进原文件中。但若是把BufferedInputStream 等换为 FileInputStream后,这句可以省略
//一个是缓冲流,需要把内容从缓冲区,刷新到文件中,另一个则不必这样做。
ra.seek(pos);
ra.write(insertContext.getBytes());
while((leng=br.read(b)) >0)
{
ra.write(b,0,leng);
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
public static void main(String[]args) throws Exception
{
RandomAccessTest.insertFile("RandomAccessTest.java",10,"我是新来的,多多关照");
}
}
6.序列化机制可以用来“克隆”对象
序列化和反序列化后,反序列化得到的对象和被序列化的对象值相同,得到的地址却不相同,且反序列化的过程与对象的构造器无关。 Person p=new Person("孙行者",500);
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("temp.txt"));
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("temp.txt"));
oos.writeObject(p);
Person per=(Person)ois.readObject();
System.out.println(p==per); // 打印false
除了 transient field (瞬态的)和 static field (属于类的)不可序列化之外,其它部分都可序列化。
建议给每一个将要被序列化的对象都加上field:private static final long serialVersionUID = -3273783136645942309L; 后面的值通过命令行
C:\Users\Administrator\Desktop\javaPractice>serialver Person 得到,若是不指定serialVersionUID 则不利于不同程序在不同的 JVM 之间移植。
注意:若是修改类中非静态field 非瞬态field,则可能导致序列化版本不兼容,则需要重新获取serialVersionUID 。
7. nio 效率高很多
将本文件输出到a.txt 并且打印出来
File f =new File("FileChannelTest.java");
try(FileChannel inFc =new FileInputStream(f).getChannel();
FileChannel outFc =new FileOutputStream("a.txt").getChannel())
{
MappedByteBuffer buff =inFc.map(FileChannel.MapMode.READ_ONLY,0,f.length());
outFc.write(buff);
buff.flip();
Charset cs=Charset.forName("GBK");
CharBuffer cb = cs.decode(buff);
System.out.print(cb);
}
catch(Exception e)
{
e.printStackTrace();
}
将本文件复制一遍,这里得到的FileChannel可读可写,这取决于它的来源。
File f=new File("FileChannelTest.java");
try(RandomAccessFile raf =new RandomAccessFile("FileChannelTest.java","rw");
FileChannel fc =raf.getChannel())
{
MappedByteBuffer bb =fc.map(FileChannel.MapMode.READ_ONLY,0,f.length());
fc.position(f.length());
fc.write(bb);
}
catch(Exception e)
{
e.printStackTrace();
}
如果你处理很大的文件,你需要分次进行,有时候运气好会遇到很尴尬的问题,比方说:你的一次读取64个字节,正好第64个字节是半个汉字位,这就相当尴尬了。
报错:MalformedInputException: Input length = 1 ,如果允许的话,可以将源文件的那个部分稍微调整下,加个空格什么的,或者就是改变每次分割的大小,调整1个字节试试。
File f =new File("FileChannelTest.java");
try(FileChannel inFc =new FileInputStream(f).getChannel())
{
ByteBuffer buff = ByteBuffer.allocate(64);
while(inFc.read(buff)!=-1)
{
buff.flip();
Charset cs=Charset.forName("GBK"); //我试试能乱码不
CharsetDecoder deco=cs.newDecoder();
CharBuffer cb = deco.decode(buff);
System.out.print(cb);
buff.clear();
}
}
catch(Exception e)
{
e.printStackTrace();
}
8.文件锁
FileLock lock=fileChannel.tryLock();
fileChannel.lock();
9.NIO.2