6,内存操作流
之前讲解流 目标都是文件,现在如果有临时的信息要通过io操作的话如果这些临时的内容保存在文件之中就很不合理了
因为操作完还要将文件删除,所以此时在io中提供内存操作流,通过内存提供输出和输入的目标是内存。
使用ByteArrayOutputStream和ByteArrayInputStream完成内存的操作流
ByteArrayOutputStream :内存向程序输出
ByteArrayInputStream :程序向内存写入
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
public class ByteArraDemo {
public static void main(String[] args) {
String str = "helloworld"; // 定义字符串,全部由小写字母组成
ByteArrayOutputStream bos = null; // 内存输出流
ByteArrayInputStream bis = null; // 内存输入流
bis = new ByteArrayInputStream(str.getBytes()); // 将内容保存在内存之中
bos = new ByteArrayOutputStream(); // 内存输出流
int temp = 0;
while ((temp = bis.read()) != -1) {// 依次读取
char c = (char) temp; // 接收字符
bos.write(Character.toUpperCase(c)); // 输出
}
String newStr = bos.toString();// 取出内存输出的内容
System.out.println(newStr);
}
}
ByteArrayInputStream 构造方法:public ByteArrayInputStream (byte[nuf])
7管道流
管道流用于两线程之间相互通信。使用PipedOutputStream,PipedinputStream来完成 但是这两个类基本与OutptStream,InputStream 类似唯一的区别就在于连接管道的操作上。
public void connect(PipedInputStream snk)throws IOException
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
class Send implements Runnable {// 发送数据的线程
private PipedOutputStream output = null;
public Send() {
this.output = new PipedOutputStream();
}
public PipedOutputStream getPipedOutputStream() {
return this.output;
}
public void run() {
String str = "hello world!!!";// 要发送的数据
try {
this.output.write(str.getBytes());// 发送
} catch (IOException e) {
e.printStackTrace();
}
try {
this.output.close(); // 关闭
} catch (IOException e) {
e.printStackTrace();
}
}
}
class Receive implements Runnable {// 接收数据的线程
private PipedInputStream input = null;
public Receive() {
this.input = new PipedInputStream();
}
public PipedInputStream getPipedInputStream() {
return this.input;
}
public void run() {
byte b[] = new byte[1024]; // 接收内容
int len = 0;
try {
len = this.input.read(b);// 内容读取
} catch (IOException e) {
e.printStackTrace();
}
try {
this.input.close(); // 关闭
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(new String(b, 0, len));
}
}
public class ThreadConnectDemo {
public static void main(String[] args) throws IOException {
Send send = new Send();
Receive rec = new Receive();
send.getPipedOutputStream().connect(rec.getPipedInputStream()); // 进行管道连接
new Thread(send).start(); // 启动线程
new Thread(rec).start(); // 启动线程
}
}
8,打印流
使用OutputStream可以完成数据的输出,但是现在如果有一个float型数据好输出吗?
输出流的操作类支持的功能并不是很强大,所以,瑞国要进行更方便的输出,可以使用打印流
PrintStream,PrintWriter
PrintStream 是OutputStream的子类 构造方法:public PrintStream(OutputStream out)
此方法接收OutputStream子类的引用。实际上PrintStream 属于装饰。也就是说根据实例化PrintStream类对象的不同输出的位置也不同。
使用PrintStream 向文件输出
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintStream;
public class PrintStreamDemo01 {
public static void main(String[] args) throws Exception {
File file = new File("d:" + File.separator + "demo.txt");
PrintStream out = new PrintStream(new FileOutputStream(file));
out.print("hello");
out.println("world");
out.println(19);
out.print(20.3);
out.close() ;
}
}
使用PrintStream格式化输出
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintStream;
public class PrintStreamDemo02 {
public static void main(String[] args) throws Exception {
File file = new File("d:" + File.separator + "demo.txt");
PrintStream out = new PrintStream(new FileOutputStream(file));
String name = "李兴华";
int age = 3;
float score = 99.9f;
char sex = 'M';
out.printf("姓名:%s;年龄:%d;成绩:%5.2f;性别:%c。", name, age, score, sex);
out.close();
}
}
9,System类 对IO的支持
System类中存在三个常量:
public static PrintStream out 表示一个标准的输出,输出的位置是显示器
public static PrintStream err 表示错误,错误的输出
public static PrintStream in 表示键盘的输入,标准输入,
9.1 System.out
System.out是PrintStream的实例化,常用的方法就是向屏幕打印信息,当然如果使用System.out的话也可以直接为OutputStream实例化。
import java.io.OutputStream;
public class SystemOutDemo {
public static void main(String[] args) throws Exception {
OutputStream out = System.out; // 此时具备了向屏幕输出的能力
out.write("hello world".getBytes());// 输出内容
out.close();
}
}
9.2 System.err
表示错误输出
public class SystemErrDemo {
public static void main(String[] args) {
try {
Integer.parseInt("hello");
} catch (Exception e) {
System.out.println(e);
System.err.println(e);
}
}
}
从代码本身看不出任何问题只有在eclipse下才会显示红色字体。
实际上 对于以上两个常量想要区别只能从概念上。
System.out 是给用户看的
System.err 不愿意给用户看的
9.3 System.in
实际上就是一个键盘的输入,使用此操作可以完成键盘输入数据的功能。
import java.io.InputStream;
public class SystemInDemo01 {
public static void main(String[] args) throws Exception {
InputStream input = System.in; // 准备键盘输入数据
byte b[] = new byte[20]; // 开辟空间接收内容
System.out.print("请输入内容:");
int len = input.read(b); // 接收内容
System.out.println("输入的内容是:" + new String(b, 0, len));
}
}
此时已经实现了键盘输入功能但是回收长度限制,而且在输入中文时会出问题。
import java.io.InputStream;
public class SystemInDemo03 {
public static void main(String[] args) throws Exception {
InputStream input = System.in; // 准备键盘输入数据
System.out.print("请输入内容:");
int temp = 0; // 接收内容
StringBuffer buf = new StringBuffer();
while ((temp = input.read()) != -1) {
char c = (char) temp; // 转型
if (c == '\n') {// 判断是否是回车
break;// 退出循环
}
buf.append(c);
}
System.out.println("输入的内容是:" + buf);
}
}
此时长度没有限制了。但是在输入中文是就无法正确读取了 用之后讲解的BufferedReader完成
9.4输出输入重定向
System.out,System.in 都有固定的输出目标,都是屏幕
System.in 有固定的输入目标,都是键盘
但是在System类中提供了一系列的输入输出重定向的方法,可以改变System.out,System.err,System.in 的输入输出位置
System.out 重定向:public static void setOut(PrintStream out)
System.err 重定向:public static void setErr(PrintStream out)
System.in 重定向:public static void setIn(PrintStream out)
import java.io.File; import java.io.FileOutputStream; import java.io.PrintStream; public class RedirectSystemOutDemo { public static void main(String[] args) throws Exception { File file = new File("d:" + File.separator + "demo.txt"); System.setOut(new PrintStream(new FileOutputStream(file))); System.out.println("hello world") ; } }
import java.io.File; import java.io.FileOutputStream; import java.io.PrintStream; public class RedirectSystemErrDemo { public static void main(String[] args) throws Exception { File file = new File("d:" + File.separator + "demo.txt"); System.setErr(new PrintStream(new FileOutputStream(file))); System.err.println("hello world") ; } }
之前说过System.err是不希望用户看到的 所以一般不建议改变它的重定向而是改变System.out的重定向。
import java.io.File; import java.io.FileInputStream; import java.io.InputStream; public class RedirectSystemInDemo { public static void main(String[] args) throws Exception { File file = new File("d:" + File.separator + "demo.txt"); System.setIn(new FileInputStream(file)); InputStream input = System.in; byte b[] = new byte[20]; // 开辟空间接收内容 int len = input.read(b); // 接收内容 System.out.println("内容是:" + new String(b, 0, len)); } }
10,BufferReader
实际上表示缓冲区读取,可以一次性把内容全读取进来
public BufferedReader(Reader in)
那么,如果使用BufferReader接收键盘输入的内容的话,则此时就无法直接实例化了,那么System.in属于InputStream类型的。
java中提供了两个专门的类,字节-字符流的转化类:
InputStreamReader:表示两字节的输入变为字节流
OutputStreamWriter:表示将字符的输出变为字节流
直接使用以上的类就可以完成转换功能,使用以下的方法可以读取数据:
public String readLine() throws IOException
表示一次性读取一行数据,而且一定要记住的是,如果返回的内容是String 是为好操作的,
范例 使用BufferReader完成输入
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class BufferedReaderDemo {
public static void main(String[] args) throws Exception {
BufferedReader buf = null;
// 将字节输入流变为字符输入流放在字符流的缓冲区之中
buf = new BufferedReader(new InputStreamReader(System.in));
System.out.print("请输入内容:");
String str = buf.readLine();
System.out.println("输入的内容是:" + str);
}
}
如果想完成键盘的输入功能,以上的操作是最合适的,也是键盘输入数据的标准格式。
11,Scanner
Scaner 是一个新的操作类,是在java.util 包中提供的一个操作类,使用此类可以方便完成输入流的输入操作。
import java.util.Scanner;
public class ScannerDemo01 {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int i = 0;
if (scan.hasNextInt()) {
i = scan.nextInt();
}
System.out.println("i = " + i);
}
}
下面再看一个可以接受字符串的例子
import java.util.Scanner;
public class ScannerDemo02 {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
String str = null ;
if (scan.hasNext()) {
str = scan.next();
}
System.out.println("str = " + str);
}
}
此类也可以用于正则匹配
import java.util.Scanner;
public class ScannerDemo03 {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
String str = null ;
if (scan.hasNext("\\d{4}-\\d{2}-\\d{2}")) {
str = scan.next();
}
System.out.println("str = " + str);
}
}
因为此类接受的是输入流的操作类,所以也可以完成文件的读取
import java.io.File;
import java.io.FileInputStream;
import java.util.Scanner;
public class ScannerDemo04 {
public static void main(String[] args) throws Exception {
File file = new File("D:" + File.separator + "demo.txt");
Scanner scan = new Scanner(new FileInputStream(file));
StringBuffer buf = new StringBuffer();
scan.useDelimiter("\n") ;
while (scan.hasNext()) {
buf.append(scan.next()).append("\n");
}
System.out.println("str = " + buf);
}
}
12,字符编码
在程序中 如果编码没有处理完整,呢会造成乱码,常见的编码有以下几种:
UTF: 包含了以下的编码
ISO 8859-1 :包含全部的英文编码
GBK/GBK2312 :GBK包含全部简体和繁体,GB2312 只包含简体
观察本机系统编码:
public class GetAllSystemInfo {
public static void main(String[] args) {
System.getProperties().list(System.out) ;
}
}
所以在使用时默认的编码就是GBK ,如果在程序中使用的时候,则使用的编码应该也是GBK,如果此时使用了ISO 8859-1编码的话,则可能会出现乱码
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class EncodeDemo {
public static void main(String[] args) throws Exception {
OutputStream output = new FileOutputStream(new File("d:"
+ File.separator + "test.txt"));
String str = "中国,你好!";
output.write(str.getBytes("ISO8859-1"));
output.close() ;
}
}
乱码的根本原因在于程序与本机环境编码不统一。
13,对象的序列化
对象序列化:就是将一个对象转化为二进制的数据流,如果一个类的对象要实现序列化必须实现Serializable 接口。在此接口中没有任何方法,此接口制作为一个标识,
标识对象具备了序列化的能力而已。
如果想要实现对象的序列化,还要依靠ObjectOutputStream类和ObjectInputStream类,
前者属于序列化,后者属于反序列化操作。
import java.io.Serializable;
public class Person implements Serializable {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String toString() {
return "姓名:" + this.name + ",年龄:" + this.age;
}
}
使用ObjectOutputStream完成序列化操作
import java.io.File;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class ObjectOutputStreamDemo {
public static void main(String[] args) throws Exception {
File file = new File("d:" + File.separator + "person.ser");
ObjectOutputStream oos = null;
oos = new ObjectOutputStream(new FileOutputStream(file));
Person per = new Person("张三", 30);
oos.writeObject(per) ;
oos.close() ;
}
}
使用ObjectIntputStream完成反序列化操作
import java.io.File;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
public class ObjectInputStreamDemo {
public static void main(String[] args) throws Exception {
File file = new File("d:" + File.separator + "person.ser");
ObjectInputStream ois = null;
ois = new ObjectInputStream(new FileInputStream(file));
Object obj = ois.readObject();
Person per = (Person) obj;
System.out.println(per);
}
}
以上操作实际上是将整个对象进行了序列化操作,如果现在假设类中某个属性不希望被序列化的话,则使用transient关键字声明。
private transient
String name;
既然可以对一个对象进行序列化,可不可以对多个对象进行序列化呢?
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class ArraySerDemo {
public static void main(String[] args) throws Exception {
Person per[] = { new Person("张三", 30), new Person("李四", 31),
new Person("王五", 32) };
ser(per);
Person p[] = (Person[]) dser();
print(p) ;
}
public static void ser(Object obj) throws Exception {
File file = new File("d:" + File.separator + "person.ser");
ObjectOutputStream oos = null;
oos = new ObjectOutputStream(new FileOutputStream(file));
oos.writeObject(obj);
oos.close();
}
public static Object dser() throws Exception {
Object temp = null;
File file = new File("d:" + File.separator + "person.ser");
ObjectInputStream ois = null;
ois = new ObjectInputStream(new FileInputStream(file));
temp = ois.readObject();
return temp;
}
public static void print(Person per[]) {
for (Person p : per) {
System.out.println(p);
}
}
}