字节流
当不同的介质之间有数据交互的时候,JAVA就使用流来实现。
数据源可以是文件,还可以是数据库,网络甚至是其他的程序
比如读取文件的数据到程序中,站在程序的角度来看,就叫做输入流
输入流: InputStream
输出流:OutputStream
InputStream字节输入流
OutputStream字节输出流
用于以字节的形式读取和写入数据
所有的流,无论是输入流还是输出流,使用完毕之后,都应该关闭。 如果不关闭,会产生对资源占用的浪费。 当量比较大的时候,会影响到业务的正常开展。
关闭流的方法有以下:
-
在try中关闭
在try的作用域里关闭文件输入流有一个弊端; 如果文件不存在,或者读取的时候出现问题而抛出异常,那么就不会执行这一行关闭流的代码,存在巨大的资源占用隐患。
-
在finally中关闭
这是标准的关闭流的方式 1). 首先把流的引用声明在try的外面,如果声明在try里面,其作用域无法抵达finally. 2). 在finally关闭之前,要先判断该引用是否为空 3). 关闭的时候,需要再一次进行try catch处理
public static void fileSplit(File file, int everyLength) {
if (file.length() == 0) {
System.out.println("file length is 0");
}
byte[] all = new byte[(int) file.length()];
FileInputStream in = null;
try {
in = new FileInputStream(file);
in.read(all);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (in != null)
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
int fileNumber;
if (all.length / everyLength == 0) {
fileNumber = (int) (all.length / everyLength);
} else {
fileNumber = (int) (all.length / everyLength) + 1;
}
for (int i = 0; i < fileNumber; i++) {
String fileName = file.getName() + "-" + i;
File newFile = new File(file.getParent(), fileName);
FileOutputStream write = null;
try {
byte[] everyContent = new byte[everyLength];
if (i != fileNumber - 1) {
everyContent = Arrays.copyOfRange(all, i*everyLength, everyLength*(i+1));
} else {
everyContent = Arrays.copyOfRange(all, i*everyLength, all.length);
}
write = new FileOutputStream(newFile);
write.write(everyContent);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (write != null) {
try {
write.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}}
public static void fileMerge(String dir, String fileName) {
File destFile = new File(dir, "merge.exe");
FileOutputStream out = null;
FileInputStream in = null;
try {
out = new FileOutputStream(destFile);
int idx = 0;
while(true) {
File srcFile = new File(dir, fileName + "-" + idx++);
byte[] eachContent = new byte[(int) srcFile.length()];
if (! srcFile.exists())
break;
try {
in = new FileInputStream(srcFile);
in.read(eachContent);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (in != null)
in.close();
}
out.write(eachContent);
out.flush();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
这是标准的严谨的关闭流的方式,但是看上去很繁琐,所以写不重要的或者测试代码的时候,都会采用上面的有隐患try的方式,因为不麻烦~
-
使用try()的方式
把流定义在try()里,try,catch或者finally结束的时候,会自动关闭 这种编写代码的方式叫做 try-with-resources, 这是从JDK7开始支持的技术
所有的流,都实现了一个接口叫做 AutoCloseable,任何类实现了这个接口,都可以在try()中进行实例化。 并且在try, catch, finally结束的时候自动关闭,回收相关资源。
public static void testCloseTry(File file) {
//把流定义在try()里,try,catch或者finally结束的时候,会自动关闭
try (FileInputStream fis = new FileInputStream(file)) {
byte[] all = new byte[(int) file.length()];
fis.read(all);
for (byte b : all) {
System.out.println(b);
}
} catch (IOException e) {
e.printStackTrace();
}
}
字符流
Reader字符输入流
Writer字符输出流
专门用于字符的形式读取和写入数据
加解密
加密算法:
数字: 如果不是9的数字,在原来的基础上加1,比如5变成6, 3变成4 如果是9的数字,变成0
字母字符: 如果是非z字符,向右移动一个,比如d变成e, G变成H
如果是z,z->a, Z-A。 字符需要保留大小写 非字母字符 比如’,&^保留不变,中文也保留不变
解密算法:
数字:如果不是0的数字,在原来的基础上减1,比如6变成5, 4变成3
如果是0的数字,变成9
字母字符:如果是非a字符,向左移动一个,比如e变成d, H变成G
如果是a,a->z, A-Z。字符需要保留大小写
非字母字符:比如’,&^ 保留不变,中文也保留不变
package com.test.how2j;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class TestEncodeAndDescode {
public static void main(String[] args) {
File encodingFile = new File("D:/file/encoding.txt");
File encodedFile = new File("D:/file/encoded.txt");
File descodedFile = new File("D:/file/descoded.txt");
encodeFile(encodingFile, encodedFile);
decodeFile(encodedFile, descodedFile);
}
/*
* 在这个方法中把decodingFile的内容进行解密,然后保存到decodedFile文件中。 解密算法: 数字:
* 如果不是0的数字,在原来的基础上减1,比如6变成5, 4变成3 如果是0的数字,变成9 字母字符: 如果是非a字符,向左移动一个,比如e变成d,
* H变成G 如果是a,a->z, A-Z。 字符需要保留大小写 非字母字符: 比如',&^ 保留不变,中文也保留不变
*/
public static void encodeFile(File encodingFile, File encodedFile) {
FileReader in = null;
FileWriter out = null;
try {
in = new FileReader(encodingFile);
out = new FileWriter(encodedFile);
char[] inContent = new char[(int) encodingFile.length()];
in.read(inContent);
System.out.println("加密前的内容:");
System.out.println(new String(inContent));
encode(inContent);
System.out.println("加密后的内容:");
System.out.println(new String(inContent));
out.write(inContent);
out.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
if (in != null)
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
if (out != null)
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/*
* 在这个方法中把decodingFile的内容进行解密,然后保存到decodedFile文件中。 解密算法: 数字:
* 如果不是0的数字,在原来的基础上减1,比如6变成5, 4变成3 如果是0的数字,变成9 字母字符: 如果是非a字符,向左移动一个,比如e变成d,
* H变成G 如果是a,a->z, A-Z。 字符需要保留大小写 非字母字符: 比如',&^ 保留不变,中文也保留不变
*/
public static void decodeFile(File decodingFile, File decodedFile) {
FileReader in = null;
FileWriter out = null;
try {
in = new FileReader(decodingFile);
out = new FileWriter(decodedFile);
char[] inContent = new char[(int) decodingFile.length()];
in.read(inContent);
System.out.println("解密前的内容:");
System.out.println(new String(inContent));
descode(inContent);
System.out.println("解密后的内容:");
System.out.println(new String(inContent));
out.write(inContent);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
if (in != null)
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
if (out != null)
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void encode(char[] cbuf) {
for (int i = 0; i < cbuf.length; i++) {
char c = cbuf[i];
if (isLetterOrDigit(c)) {
switch (c) {
case '9':
c = '0';
break;
case 'z':
c = 'a';
break;
case 'Z':
c = 'A';
break;
default:
c++;
break;
}
cbuf[i] = c;
}
}
}
public static void descode(char[] cbuf) {
for (int i = 0; i < cbuf.length; i++) {
char c = cbuf[i];
if (isLetterOrDigit(c)) {
switch (c) {
case '0':
c = '9';
break;
case 'a':
c = 'z';
break;
case 'A':
c = 'Z';
break;
default:
c--;
break;
}
cbuf[i] = c;
}
}
}
public static boolean isLetterOrDigit(char c) {
// 不使用Character类的isLetterOrDigit方法是因为,中文也会被判断为字母
String letterOrDigital = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
return -1 == letterOrDigital.indexOf(c) ? false : true;
}
}
缓存流
以介质是硬盘为例,字节流和字符流的弊端: 在每一次读写的时候,都会访问硬盘。 如果读写的频率比较高的时候,其性能表现不佳。
为了解决以上弊端,采用缓存流。
缓存流在读取的时候,会一次性读较多的数据到缓存中,以后每一次的读取,都是在缓存中访问,直到缓存中的数据读取完毕,再到硬盘中区读取。
就好比吃饭,不用缓存就是每吃一口都到锅里去铲。用缓存就是先把饭盛到碗里,碗里的吃完了,再到锅里去铲
缓存流在写入数据的时候,会先把数据写入到缓存区,直到缓存区达到一定的量,才把这些数据,一起写入到硬盘中去。按照这种操作模式,就不会像字节流,字符流那样每写一个字节都访问硬盘,从而减少了IO操作。
1. 使用缓存流读取数据
缓存字符输入流 BufferedReader 可以一次读取一行数据
public static void testReader(File file) {
try (FileReader in = new FileReader(file); BufferedReader br = new BufferedReader(in);) {
while (true) {
String line = br.readLine();
if (line == null)
break;
System.out.println(line);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
2. 使用缓存流写出数据,并使用flush刷新文件内容
PrintWriter 缓存字符输出流, 可以一次写出一行数据
有的时候,需要立即把数据写入到硬盘,而不是等缓存满了才写出去。 这时候就需要用到flush
public static void testWrite(File file) {
FileWriter fw = null;
BufferedWriter bw = null;
PrintWriter pw = null;
try {
fw = new FileWriter(file);
bw = new BufferedWriter(fw);
pw = new PrintWriter(fw);
bw.write("hello\n");
bw.flush();
pw.println("this is print write test.");
pw.flush();
pw.println("this is print write test2.");
pw.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
if (fw != null) {
fw.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
if (bw != null) {
bw.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
数据流
DataInputStream 数据输入流
DataOutputStream 数据输出流
直接进行字符串的读写
使用数据流的writeUTF()和readUTF() 可以进行数据的格式化顺序读写
public static void write(File file) {
try (FileOutputStream out = new FileOutputStream(file); DataOutputStream dos = new DataOutputStream(out);) {
dos.writeBoolean(true);
dos.writeInt(10);
dos.writeUTF("test");
} catch (IOException e) {
e.printStackTrace();
}
}
public static void read(File file) {
try (FileInputStream in = new FileInputStream(file); DataInputStream dis = new DataInputStream(in);) {
System.out.println("读到布尔型: " + dis.readBoolean());
System.out.println("读到int类型: " + dis.readInt());
System.out.println("读到字符串类型:" + dis.readUTF());
} catch (IOException e) {
e.printStackTrace();
}
}
对象流
对象流指的是可以直接把一个对象以流的形式传输给其他的介质,比如硬盘
一个对象以流的形式进行传输,叫做序列化。 该对象所对应的类,必须是实现Serializable接口
序列化一个对象
public class Student implements Serializable {
private static final long serialVersionUID = -6955047867001941524L;
public int id;
public String name;
public String sex;
}
public static void objStream(File file, Student obj) {
try (FileOutputStream out = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(out);
FileInputStream in = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(in);) {
oos.writeObject(obj);
Student s = (Student) ois.readObject();
System.out.println(s.id);
System.out.println(s.name);
System.out.println(s.sex);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.in和System.out
System.out 是常用的在控制台输出数据的
System.in可以从控制台输入数据
System.in输入字符串Scanner读取字符串
package com.test.how2j;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;
public class TestScanner {
public static void main(String[] args) {
// sysIn();
// scanner();
classMaker();
// System.out.println(String.format("test %s", "123", "\n"));
}
public static void sysIn() {
try (InputStream is = System.in;) {
System.out.println(is.read());
} catch (IOException e) {
e.printStackTrace();
}
}
public static void scanner() {
Scanner s = new Scanner(System.in);
while(true) {
String str = s.nextLine();
System.out.println("所读数据是:" + str);
}
}
public static void classMaker() {
Scanner s = new Scanner(System.in);
System.out.println("请输入类名:");
String className = s.nextLine();
s.nextLine();
System.out.println("请输入属性类型:");
String type = s.nextLine();
s.nextLine();
System.out.println("请输入属性名称:");
String name = s.nextLine();
s.nextLine();
File file = new File("D:/file/" + className + ".java");
try (FileWriter fw = new FileWriter(file)) {
String str = "public class " + className + " {" + "\n";
str += " public " + type + " " + name + ";\n";
str += " public " + className + "()" + "{\n }" + "\n";
str += "\n";
str += " public void set" + nameMaker(name) + "(" + type + " " + name + ")" + "{\n";
str += " this." + name + " = " + name + ";\n";
str += " }";
str += "\n\n";
str += " public " + type + " get" + nameMaker(name) + "() {\n";
str += " return " + name + ";\n";
str += " }\n";
str += "}";
fw.write(str);
fw.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
public static String nameMaker(String str) {
char[] c = str.toCharArray();
StringBuffer sb = new StringBuffer();
sb.append(String.valueOf(String.valueOf(c[0]).charAt(0)).toUpperCase());
sb.append(str.substring(1));
return sb.toString();
}
}