打印流
字节打印流:PrintStream;字符打印流:PrintWriter。
优势:支持向文件中输出任意的数据类型。自动刷新的功能,不需要调用flush()或close(),操作更加简洁。
方法:
-
void print(String str):输出任意类型的数据。
-
void println(String str):输出任意类型的数据,自动写入换行操作。
可以通过构造方法,完成文件数据的自动刷新功能。
构造方法:开启文件自动刷新写入功能
public PrintWriter(OutputStream out, boolean autoFlush)
public PrintWriter(Writer out, boolean autoFlush)
/*打印流:
* PrintWriter:是Writer一个子类,只能写入,不能读取
*
* PrintWriter(OutputStream out)
PrintWriter(Writer out)
* void print(boolean b)
* void print(int i)
* void println(int x)
void println(String x)
PrintWriter特点:
1.保证数据原样写入
2.PrintWriter底层也使用了缓冲区,利用PrintWriter写数据,其实也是
写入了PrintWriter底层的缓冲区,我们需要通过手动刷新或者close()
3.自动刷新
PrintWriter(OutputStream out, boolean autoFlush)
PrintWriter(Writer out, boolean autoFlush)
当autoFlush为true的时候会启动自动刷新,自动刷新只针对println有效
*/
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class Demo {
public static void main(String[] args) throws IOException {
//PrintWriter自带刷新,需要在构造方法中开启
PrintWriter pw = new PrintWriter(new FileWriter("print.txt"),true);//默认不自动刷新
pw.print(12);
pw.println(12.22);//自动刷新,println()
pw.print("aaaaaa");
pw.println();//flush()
}
}
重定向标准I/O
System.in和System.out。它们是Java提供的两个标准的输入/输出流,主要用于从键盘接收数据以及向屏幕输出数据。
System.in常见方法:
-
int read(),此方法从键盘接收一个字节的数据,返回值是该字符的ASCII码。
-
int read(byte []buf),此方法从键盘接收多个字节的数据,保存至buf中,返回值是接收字节数据的个数。
System.out常见方法:
-
print(),向屏幕输出数据,不换行,参数可以是java的任意数据类型。
-
println(),向屏幕输出数据,换行,参数可以是java的任意数据类型。
System类提供了3个重定向标准输入/输出的方法:
方法 | 说明 |
---|---|
static void setErr(PrintStream) | 重定向标准错误输出流 |
static void setIn(InputStream in) | 重定向标准输入流 |
static void setOut(PrintStream out) | 重定向标准输出流 |
程序向文件输出,System.out.println()直接输出,不要重复创建FileWriter。
import java.io.FileInputStream;
import java.util.Scanner;
public class Demo {
public static void main(String[] args) throws Exception {
//InputStream in = System.in;//Console控制台
//Scanner(InputStream)
//Scanner读取任意数据类型
//Scanner input = new Scanner(System.in);
//Scanner从文件中读取数据aa.txt
Scanner input = new Scanner(new FileInputStream("aa.txt"));
//Scanner具有从console获取用户输入数据的功能
String content = input.nextLine();
System.out.println(content);
//Scanner:控制台的数据-->java程序 Input
input.close();
}
}
public class Demo {
public static void main(String[] args) throws IOException {
//模仿Scanner从控制台获取用户输入的数据
//InputStream-->Reader 转换流
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr);
//接收用户输入
System.out.print("请输入您的姓名:");
String name = br.readLine();
System.out.println("您的姓名是:"+name);
}
}
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
public class Demo {
public static void main(String[] args) throws FileNotFoundException {
//重定向:输出或输入的地点修改为其它的位置
//System.in默认从控制台读取数据,改成G:\\1.txt
//修改System.in获取数据的位置
System.setIn(new FileInputStream("g:\\1.txt"));
//System.out.println("hello");
//重定向System.out
PrintStream out = new PrintStream(new FileOutputStream("system.txt"));
System.setOut(out);
System.out.print("hello");
System.out.println(12);
System.out.println(12.23);
}
}
字节数组流
ByteArrayInputStream和ByteArrayOutputStream经常用在需要流和数组之间转化的情况。
字节数组输入流
说白了,FileInputStream是把文件当做数据源。ByteArrayInputStream则是把内存中的“字节数组对象”当做数据源。
public static void main(String[] args) {
byte[] arr = "abcde".getBytes();
ByteArrayInputStream bais = null;
try {
bais = new ByteArrayInputStream(arr);
StringBuilder sb = new StringBuilder();
int temp = 0;
while ((temp = bais.read()) != -1) {
sb.append((char) temp);
}
System.out.println(sb.toString());
} finally {
try {
bais.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
字节数组输出流
ByteArrayOutputStream流对象是将流中的数据写入到字节数组中。
public static void main(String[] args) {
ByteArrayOutputStream baos = null;
StringBuilder sb = new StringBuilder();
try {
baos = new ByteArrayOutputStream();
baos.write('a');
baos.write('b');
baos.write('c');
byte[] bytes = baos.toByteArray();
for (byte b : bytes) {
sb.append((char)b);
}
System.out.println(sb);
} finally {
try {
if (baos != null) {
baos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
序列化流(对象流)
概述
序列化和反序列化:字节流。核心:对象。场景:图形的存储。
序列化:程序中的对象保存文件中。ObjectOutputStream:输出流
反序列化:将文件中保存的对象读取到程序中。ObjectInputStream:输入流。
当两个进程远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。比如,我们可以通过http协议发送字符串信息;我们也可以在网络上直接发送Java对象。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象才能正常读取。
把Java对象转换为字节序列的过程称为对象的序列化。把字节序列恢复为Java对象的过程称为对象的反序列化。
对象序列化的作用有如下两种:
-
持久化:把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中。
-
网络通信:在网络上传送对象的字节序列。比如:服务器之间的数据通信、对象传递。
使用序列化保存对象信息
在Java中,只有实现了java.io.Serializable接口的类的对象才能被序列化,Serializable表示可串行的、可序列化的,所以,对象序列化在某些文献中也叫串行化。JDK类库中有些类,如String类、包装类和Date类等都实现了Serializable接口。
序列化算法规则:
-
所有保存到磁盘中的对象都有一个序列号。
-
当程序试图序列化一个对象时,将会检查是否已经被序列化,只有序列化后的对象才能被转换成字符序列输出。
-
如果对象已经被序列化,则程序直接输出一个序列化编号,而不再重新序列化。
实现序列化步骤:
-
准备对象,可以任意对象。一般自定义对象。
-
创建对象ObjectOutputStream,指定保存对象位置。。
-
实现序列化:writeObject(对象)
-
释放资源:close()。
好处:序列化完毕之后,对象保存在文件中。程序停止运行,对象不会丢失。
实现反序列化步骤:
-
创建对象ObjectInputStream,指定读取保存对象位置。
-
实现反序列化:Object o = readObject()。
-
释放资源:close()。
序列化注意事项
1、加static修饰的成员变量不会被序列化,静态成员随着类的加载而加载,当没有对象的时候,静态成员已经存在静态成员不属于对象,而属于类。
2、如果非静态成员不想序列化:加一个transient,一旦使用该关键该成员变量就不会被序列化。transient关键字:如果用transient声明一个实例变量,当对象存储时,它的值不需要维持。(在序列化中被忽略掉)
import java.io.Serializable;
public class Person implements Serializable {
/*
* 版本编号,序列化为防止程序代码变化。给java类定义版本号。
* 文件中号码与java类号码一致。反序列化不会出错。
* */
private static final long serialVersionUID = 1L;
public int id;
private String name;
private int age;
//省略name和age的构造方法、getter()、setter()、toString()
import java.io.*;
public class Demo01 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//method01();
//method02();
}
private static void method01() throws IOException {
//序列化
//1、创建Person对象
Person p = new Person("张三",20);
//2、创建ObjectOutputStream对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));
//3、调用writeObject()
oos.writeObject(p);
//4、释放资源
oos.close();
}
private static void method02() throws IOException, ClassNotFoundException {
//反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));
Object o = ois.readObject();
//Person p =(Person)o;
System.out.println(o);
ois.close();
}
}
当第一次序列化对象存储到文件中,修改Person类中的代码,反序列化会出错。即序列化的版本不一致。
Exception in thread "main" java.io.InvalidClassException: com.coydone.demo02.Person; local class incompatible: stream classdesc serialVersionUID = -4254320201896902916, local class serialVersionUID = -2943045096116274627
序列化和反序列化实现注册和登录
import java.io.Serializable;
public class User implements Serializable {
private String userName;
private String userPwd;
//省略有参构造方法、getter()、setter()、toString()
}
import java.io.*;
import java.util.Scanner;
public class Demo {
public static void main(String[] args) {
regist();
login();
}
private static void login() {
Scanner input = new Scanner(System.in);
System.out.print("账号:");
String userName = input.nextLine();
System.out.print("密码:");
String userPwd = input.nextLine();
//判断用户名和密码与注册信息是否一致
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("userInfo.dat"));
User o = (User)ois.readObject();
if (userName.equals(o.getUserName())&&userPwd.equals(o.getUserPwd())){
System.out.println("登录成功!");
}else {
System.out.println("登录失败!");
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("请先注册,再登录!");
}finally {
if (ois!=null){
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private static void regist() {
//注册
Scanner input = new Scanner(System.in);
System.out.print("账号:");
String userName = input.nextLine();
System.out.print("密码:");
String userPwd = input.nextLine();
System.out.print("确认密码:");
String reUserPwd = input.nextLine();
//两次密码输入一致
if (!userPwd.equals(reUserPwd)){
System.out.println("两次密码输入不一致,注册失败!");
return;
}
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("userInfo.dat"));
oos.writeObject(new User(userName,userPwd));
System.out.println("注册成功!");
} catch (Exception e) {
System.out.println("注册失败!");
}finally {
if (oos!=null){
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
随机访问流
RandomAccessFile可以实现两个作用:
1、实现对一个文件做读和写的操作。
2、可以访问文件的任意位置。不像其他流只能按照先后顺序读取。
在开发某些客户端软件时,经常用到这个功能强大的可以“任意操作文件内容”的类。比如,软件的使用次数和使用日期,可以通过本类访问文件中保存次数和日期的地方进行比对和修改。Java很少开发客户端软件,所以在Java开发中这个类用的相对较少。
RandomAccessFile的绝大多数功能,已经被JDK1.4的nio的“内存映射文件(memory-mapped files)”给取代了。
三个核心方法:
1、RandomAccessFile(String name,String mode):name用来确定文件,mode取r(读)或rw(可读写),通过mode可以确定流对文件的访问权限。
2、seek(long a):用来定位流对象读写文件的位置,a确定读写位置距离文件开头的字节个数。
3、getFilePointer():获得流的当前读写位置。
public static void main(String[] args) {
RandomAccessFile raf = null;
try {
raf = new RandomAccessFile("d:/a.txt","rw");
//写入数据
int[] arr = {10, 20, 30, 40, 50, 60};
for (int i : arr) {
raf.writeInt(i);
}
raf.seek(4);
System.out.println(raf.readInt()); //20
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (raf != null) {
raf.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Properties集合
概述
Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。
特点:
1、Hashtable的子类,map集合中的方法都可以用。
2、该集合没有泛型。键值都是字符串。
3、它是一个可以持久化的属性集。键值可以存储到集合中,也可以存储到持久化的设备(硬盘、U盘、光盘)上。键值的来源也可以是持久化的设备。
4、有和流技术相结合的方法。
/*Properties方法使用
* Properties经常用来操作key和value都是String类型的数据
* Object setProperty(String key, String value):向Properties中存放key和value
String getProperty(String key):根据key获取value
Set<String> stringPropertyNames():将properties中所有的key放到一个set集合中,返回这个set集合
*/
读取文件中的数据保存到集合
void load(InputStream inStream)//从输入流中读取属性列表(键和元素对)。
void load(Reader reader)//按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)
将集合中内容存储到文件
//以适合使用load(InputStream)方法加载到Properties表中的格式,将此Properties表中的属性列表(键和元素对)写入输出流。
void store(OutputStream out,String comments)
//以适合使用load(Reader)方法的格式,将此Properties表中的属性列表(键和元素对)写入输出字符。
void store(Writer writer,String comments)
使用场景
Properties 一般与配置文件一起使用。
配置文件常见格式:.xml、.properties。
.properties文件:对应java程序:Properties集合。
集合类:Properties,继承(派生) Map<K,V>
。Properties不支持泛型。默认存储都是字符串。Properties<String,String>
案例:
MySQL:登录的账号,密码:root、root。
创建db.properties配置文件保存用户名和密码。
userName=root
password=root
Java代码从配置文件中获取用户名和密码
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;
public class Demo {
public static void main(String[] args) throws IOException {
//获取登录的账号、密码IO
//1、创建Properties集合
Properties prop = new Properties();
//2、加载配置文件信息到prop对象中
prop.load(new FileReader("db.properties"));
//3、查看结果
System.out.println("账号:"+prop.getProperty("userName"));
System.out.println("密码:"+prop.getProperty("password"));
//账号:root
//密码:root
}
}
文件的压缩与解压
压缩文件的本质是把存储文件的二进制,用一定的规则表示来减少原文件的二进制长度,从而减少文件的大小。而这个规则不同的压缩软件规则不同。
Java中实现zip的压缩与解压缩
zipOutputStream:实现文件的压缩
-
ZipOutputStream(OutputStream out):创建新的ZIP输出流。
-
void putNextEntry(ZipEntry e):开始写入新的ZIP文件条目并将流定位到条目数据的开始处。
-
ZipEntry(String name):使用指定名称创建新的ZIP条目。
ZiplnputStream:实现文件的解压
-
ZipInputStream(lnputStream in):创建新的ZIP输入流。
-
ZipEntry getNextEntry():读取下一个ZlP文件条目并将流定位到该条目数据的开始处。
代码实现
import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
public class Test {
public static void main(String[] args) {
compression("G:\\01.zip",new File("G:\\01"));
// decompression("D:\\test.zip","D:\\test\\");
}
//压缩
private static void compression(String zipFileName, File targetFile) {
try {
//要生成的压缩文件
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFileName));
BufferedOutputStream bos = new BufferedOutputStream(out);
zip(out,targetFile,targetFile.getName(),bos);
bos.close();
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void zip(ZipOutputStream zOut, File targetFile, String name, BufferedOutputStream bos) throws IOException {
if (targetFile.isDirectory()) { //处理目录
File[] files = targetFile.listFiles();
if (files.length == 0) { //空文件夹
zOut.putNextEntry(new ZipEntry(name + "/"));
}
for (File file : files) {
//递归处理
zip(zOut,file,name + "/" + file.getName(),bos);
}
} else {
zOut.putNextEntry(new ZipEntry(name));
InputStream in = new FileInputStream(targetFile);
BufferedInputStream bis = new BufferedInputStream(in);
byte[] bytes = new byte[1024];
int len = -1;
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes,0,len);
}
bis.close();
}
}
//解压
private static void decompression(String targetFileName,String parent) {
try {
//构建解压输入流
ZipInputStream zIn = new ZipInputStream(new FileInputStream(targetFileName));
ZipEntry entry = null;
File file = null;
while ((entry = zIn.getNextEntry()) != null && !entry.isDirectory()) {
file = new File(parent,entry.getName());
if (!file.exists()) {
new File(file.getParent()).mkdirs(); //创建此文件的上级目录
}
OutputStream os = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(os);
byte[] bytes = new byte[1024];
int len = -1;
while ((len = zIn.read(bytes)) != -1) {
bos.write(bytes,0,len);
}
bos.close();
System.out.println(file.getAbsolutePath() + "解压成功");
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
评论区