io
in入 out出指的是后续我们对计算机资源读取和写出。
File
- 文件和目录路径名的抽象表示。
delete()删除此抽象路径名表示的文件或目录【前提:文件真的存在】
createNewFile()当且仅当具有此名称的文件尚不存在时,以原子方式创建由此抽象路径名命名的新空文件
deleteOnExit()请求在虚拟机终止时删除此抽象路径名表示的文件或目录
常用构造方法
public boolean createNewFile() throws IOException
当且仅当具有此名称的文件尚不存在时,以原子方式创建由此抽象路径名命名的新空文件。检查文件是否存在以及文件的创建(如果不存在)是针对可能影响文件的所有其他文件系统活动的原子操作。
注意:此方法不应用于文件锁定,因为无法使生成的协议可靠地工作。 应该使用FileLock
设施。
结果
true
如果指定的文件不存在且已成功创建; false
如果指定的文件已存在
常用方法
public String getAbsolutePath()
返回此抽象路径名的绝对路径名字符串。
如果此抽象路径名已经是绝对路径名,则只需返回路径名字符串,就好像通过getPath()
)方法一样。 如果此抽象路径名是空抽象路径名,则返回当前用户目录的路径名字符串,该字符串由系统属性user.dir
命名。 否则,此路径名将以与系统相关的方式解析。 在UNIX系统上,通过将相对路径名解析为当前用户目录,使其成为绝对路径名。 在Microsoft Windows系统上,通过将路径名解析为路径名所指定的驱动器的当前目录(如果有),使相对路径名成为绝对路径名。 如果没有,则针对当前用户目录解析。
结果
绝对路径名字符串,表示与此抽象路径名相同的文件或目录
异常
SecurityException
- 如果无法访问所需的系统属性值。
public String getName()
返回此抽象路径名表示的文件或目录的名称。 这只是路径名称序列中的姓氏。 如果路径名的名称序列为空,则返回空字符串。
结果
此抽象路径名表示的文件或目录的名称,如果此路径名的名称序列为空,则为空字符串
public String getParent()
返回此抽象路径名父项的路径名字符串,如果此路径名未指定父目录,则返回null
。
抽象路径名的父节点包含路径名的前缀(如果有),以及路径名的名称序列中的每个名称(除了最后一个)。 如果名称序列为空,则路径名不会命名父目录。
结果
此抽象路径名指定的父目录的路径名字符串,如果此路径名未指定父路径, null
public File getParentFile()
返回此抽象路径名父项的抽象路径名,如果此路径名未指定父目录,则返回null
。
抽象路径名的父节点包含路径名的前缀(如果有),以及路径名的名称序列中的每个名称(除了最后一个)。 如果名称序列为空,则路径名不会命名父目录。
结果
此抽象路径名指定的父目录的抽象路径名,如果此路径名未指定父路径, null
public long length()
返回此抽象路径名表示的文件的长度。如果此路径名表示目录,则返回值未指定。
如果需要区分I / O异常与返回0L
的情况,或者同时需要同一文件的多个属性,则可以使用Files.readAttributes
)方法。
结果
此抽象路径名表示的文件的长度(以字节为单位),如果该文件不存在, 0L
。 对于表示系统相关实体(如设备或管道)的路径名,某些操作系统可能会返回0L
。
异常
SecurityException
- 如果存在安全管理器且其 SecurityManager.checkRead(java.lang.String)
)方法拒绝对文件的读访问权
public String getPath()
将此抽象路径名转换为路径名字符串。 生成的字符串使用default name-separator character
分隔名称序列中的名称。
结果
此抽象路径名的字符串形式
用于判断的方法
区别:
boolean renameTo(File dest) 重命名此抽象路径名表示的文件。
可用于文件迁移
字段
不同操作系统下,路径分隔符与名称分隔符有所不同
文件遍历
查找
public static void main(String[] args) throws IOException {
File g = new File("G://");
File[] files = g.listFiles();//调用g盘下的所有文件
listFiles(files);
}
public static void listFiles(File[] files) {//传入文件数组
//判断文件非空
if(files != null && files.length>0) {
for(File file:files) {
if(file.isFile()) {
//文件
if(file.getName().endsWith(".mp4")) {
//找到一个MP4文件
if (file.length() > 100 * 1024 * 1024)
/*
* {//可定义文件大小
* file.delete();
* System.out.println(file.getAbsolutePath()+"已删除"); }
*/
System.out.println("找到了mp4文件:"+file.getAbsolutePath());
}
}else {
//文件夹
File[] files2 = file.listFiles();
listFiles(files2);//递归
}
}
}
}
删除
File file = new File("D://test.txt");
boolean flag = file.createNewFile();
System.out.println(flag?"创建成功":"创建失败");
File file = new File("D://test");
file.mkdir();//创建文件夹
File a = new File(file,"a.txt");
a.createNewFile();
File b = new File("D://test","b.txt");
b.createNewFile();
a.delete();
b.delete();
文件过滤器
这是一个功能接口,因此可以用作lambda表达式或方法引用的赋值目标。
public interface FileFilter
抽象路径名的过滤器。
可以将此接口的实例传递给File
类的listFiles(FileFilter)
方法。
相对与绝对路径
绝对路径:从盘符开始,是一个完整的路径。
相对路径:不包含盘符,在Java代码中是相对于项目目录路径,是一个不完整的便捷路径,在Java开发中很常用。
流概述
JAVA中的IO操作主要指的是java.io报下的一些常用类的使用,通过这些常用类对数据进行读取(输入Input)和写出(输出Output)。
顶级父类
特殊流:打印流、对象流
OutputStream
一切皆字节;计算机中的任何数据(文本、图片、视频、音乐等)都是以二进制的形式存储的。在数据传输时也都是以二进制的形式存储的。
- 此抽象类是表示输出字节流的所有类的超类。输出流接受输出字节并将它们发送到某个接收器。
需要定义OutputStream
的子类的应用程序必须始终至少提供一个写入一个输出字节的方法。
缓存:提高效率;缓存就是数据交换的缓冲区(又称作Cache),当某一硬件要读取数据时,会首先从缓存中查找需要的数据,找到了则直接执行,找不到的话则从内存中查找。由于缓存的运行速度比内存快得多,故缓存的作用就是帮助硬件更快地运行。
因为缓存往往使用的是RAM(断电即掉的非永久性储存),所以在用完后还是会把文件送到硬盘等存储器里永久存储。
write字段
1.public abstract void write(int b) throws IOException
将指定的字节写入此输出流。 write的一般合同是将一个字节写入输出流。 要写入的字节是参数b的八个低位。 b的24个高位被忽略。
OutputStream子类必须提供此方法的实现。
1111 1111 = 255;
2.public void write(byte[] b) throws IOException
将指定字节数组中的b.length个字节写入此输出流。 write(b)的总合同是它应该与呼叫write(b, 0, b.length)具有完全相同的效果。
3.public void write(byte[] b, int off, int len) throws IOException
将从偏移量off开始的指定字节数组中的len字节写入此输出流。 write(b, off, len)的一般合同是数组b中的一些字节按b写入输出流; element b[off]是写入的第一个字节, b[off+len-1]是此操作写入的最后一个字节。
该write的方法OutputStream调用写出在每个字节中的一个参数的写入方法。 鼓励子类重写此方法并提供更有效的实现。
如果b是null ,则抛出NullPointerException 。
如果off为负,或len为负,或off+len大于数组b的长度,则抛出IndexOutOfBoundsException 。
FileOutputStream
boolean append
作用:这个流建立以后写出的数据是接着源文件内容往后写还是把文件重新写,true代表接着往后写,而false或不传参数表示把原来文件内容清空,重新写数据。
此相当于追加输入
直到流关闭之前,所写的都是一个文件的内容。否则参照下图会报错。
只有一个流重新创建时才需要指定是否追加,若不追加会清空源文件内容,如在数组中重新编辑数据内容会自动覆盖掉源文件内容。
进行流的编写时,一定要尽可能早的关闭。最后一次写出时,最好紧跟其后进行close操作,而不是后续再操作。
FileInputStream
public abstract int read() throws IOException
从输入流中读取下一个数据字节。 值字节返回int ,范围为0至255 。 如果由于到达流末尾而没有可用字节,则返回值-1 。 此方法将阻塞,直到输入数据可用,检测到流的末尾或抛出异常。
子类必须提供此方法的实现。
结果
数据的下一个字节,如果到达流的末尾, -1 。
范围是0-255,-1代表读取完毕,读不到了。
public int read(byte[] b) throws IOException
【返回的int是实际读取的字节数组】
从输入流中读取一些字节数并将它们存储到缓冲区数组b 。 实际读取的字节数以整数形式返回。 此方法将阻塞,直到输入数据可用,检测到文件结尾或引发异常。
如果b的长度为零,则不读取任何字节,并返回0 ; 否则,尝试读取至少一个字节。 如果由于流位于文件末尾而没有可用字节,则返回值-1 ; 否则,至少读取一个字节并存储到b 。
读取的第一个字节存储在元素b[0] ,下一个字节存入b[1] ,依此类推。 读取的字节数最多等于b的长度。 设k为实际读取的字节数; 这些字节将存储在元素b[0]到b[ k -1] ,使元素b[ k ]到b[b.length-1]不受影响。
类InputStream的read(b)方法具有与以下相同的效果: read(b, 0, b.length)
参数
b - 读取数据的缓冲区。
结果
读入缓冲区的总字节数,如果由于已到达流末尾而没有更多数据, -1 。
异常
IOException - 如果由于文件末尾以外的任何原因无法读取第一个字节,如果输入流已关闭,或者发生某些其他I/O错误。
NullPointerException - 如果 b是 null 。
如果所读取的文件有10个字节,第一次读取3个,第二次读取3个,第三次读取3个,则第四次只能读一个。由于已经读完了,第五次读取时会返回-1,如果由于流位于文件末尾而没有可用字节,则返回值-1 。
FileInputStream是InputStream的常用子类
常用方法
public void close() throws IOException
关闭此文件输入流并释放与该流关联的所有系统资源。
如果此流具有关联的频道,则该频道也将关闭。
API Note:
仅当直接调用或通过try-with-resources调用时,覆盖close()
)才能执行清理操作是可靠的。 不要依赖于最终化来调用close
; 最终确定不可靠并且已被弃用。 如果需要清理本机资源,则应使用其他机制,如Cleaner 。
读取单个字节
上述操作可通过循环实现
读取文件时如果超出文件的总字节数会一直返回-1
有时我们不能确定文件的长度,要读取多少。【我们可以通过length获取长度】
假设不能获取length长度时可通过if语句定义跳出循环,则可正常关闭,否则无法正常关闭。
读取一组字节(比单个字节读取常用;读一个比较大的文件时比较省时间;数据量可能相同,但一组比单个的耗时要短。)
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("g://x1.txt");
byte[] bytes = new byte[10];
fis.read(bytes);
System.out.println(new String(bytes));
fis.read(bytes);
System.out.println(new String(bytes));
fis.read(bytes);
System.out.println(new String(bytes));
fis.close();
}
输入流操作时极其容易遇到的一个误区
实现结果:
文本实际输入:
原因:第二组未被清空,最后一组覆盖其前6位
正确操作
当写一个文件下载器下载不知大小的文件时可能通过-1操作确定文件是否读完。
文件加密和解密工具
核心:异或运算
public static void main(String[] args) throws Exception {
System.out.println("请输入文件存储的全路径:");
Scanner input = new Scanner(System.in);
String fileName = input.nextLine();
//源文件
File oldFile = new File(fileName);
//加密存储的新文件
File newFile = new File(oldFile.getParentFile(),"mi-"+oldFile.getName());
FileInputStream fis = new FileInputStream(oldFile);
FileOutputStream fos = new FileOutputStream(newFile);
while(true) {
int b = fis.read();
if(b == -1) {
break;//读取到尾部
}
//异或运算,任何数据^相同的数字两次 结果就是其本身
fos.write(b^10);
}
System.out.println("加密或解密完成");
}
字符编码
使用字节流操作中文字符时会发生乱码问题,所以我们使用字符流。
ASCII码
在计算机中,所有的数据在存储和运算时都要使用二进制数表示(因为计算机用高电平和低电平分别表示1和0),例如,像a、b、c、d这样的52个字母(包括大写)、以及0、1等数字还有一些常用的符号(例如*、#、@等)在计算机中存储时也要使用二进制数来表示,而具体用哪些二进制数字表示哪个符号,当然每个人都可以约定自己的一套(这就叫编码),而大家如果要想互相通信而不造成混乱,那么大家就必须使用相同的编码规则,于是美国有关的标准化组织就出台了ASCII编码,统一规定了上述常用符号用哪些二进制数来表示。
表述方式
ASCII 码使用指定的7 位或8 位二进制数组合来表示128 或256 种可能的字符。标准ASCII 码也叫基础ASCII码,使用7 位二进制数来表示所有的大写和小写字母,数字0 到9、标点符号, 以及在美式英语中使用的特殊控制字符。其中:
0~31及127(共33个)是控制字符或通信专用字符(其余为可显示字符),如控制符:LF(换行)、CR(回车)、FF(换页)、DEL(删除)、BS(退格)、BEL(响铃)等;通信专用字符:SOH(文头)、EOT(文尾)、ACK(确认)等;ASCII值为8、9、10 和13 分别转换为退格、制表、换行和回车字符。它们并没有特定的图形显示,但会依不同的应用程序,而对文本显示有不同的影响。
3257为0到9十个阿拉伯数字。
65122号为26个小写英文字母,其余为一些标点符号、运算符号等。
同时还要注意,在标准ASCII中,其最高位(b7)用作奇偶校验位。所谓奇偶校验,是指在代码传送过程中用来检验是否出现错误的一种方法,一般分奇校验和偶校验两种。奇校验规定:正确的代码一个字节中1的个数必须是奇数,若非奇数,则在最高位b7添1;偶校验规定:正确的代码一个字节中1的个数必须是偶数,若非偶数,则在最高位b7添1。
后128个称为扩展ASCII码。许多基于x86的系统都支持使用扩展(或”高”)ASCII。扩展ASCII 码允许将每个字符的第8 位用于确定附加的128 个特殊符号字符、外来语字母和图形符号。
每个国家都有自己的编码表,绝大多数的编码表都兼容ASCII码表。
UTF-8
是针对Unicode的一种可变长度字符编码。它可以用来表示Unicode标准中的任何字符,而且其编码中的第一个字节仍与ASCII相容,使得原来处理ASCII字符的软件无须或只进行少部份修改后,便可继续使用。因此,它逐渐成为电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。
UTF-8使用1~4字节为每个字符编码:
·一个US-ASCIl字符只需1字节编码(Unicode范围由U+0000~U+007F)。
·带有变音符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文等字母则需要2字节编码(Unicode范围由U+0080~U+07FF)。
·其他语言的字符(包括中日韩文字、东南亚文字、中东文字等)包含了大部分常用字,使用3字节编码。
·其他极少使用的语言字符使用4字节编码。
乱码问题:输入方与输出方编码不同
字节流读取文字
扩大长度后
字符流:以一个完整的文字为单位;解决字节流读取是读一半的问题.(注意编码方式是否相同)
字符输出
- 用于写入字符流的抽象类。 子类必须实现的唯一方法是write(char [],int,int),flush()和close()。 但是,大多数子类将覆盖此处定义的一些方法,以提供更高的效率,附加功能或两者兼而有之。
通过字节进行封装,即字符流写出数据时用的也是字节流,只不过写出的单位从字节变成了字符.
方法
FileWriter常用
public class FileWriter
extends OutputStreamWriter
使用默认缓冲区大小将文本写入字符文件。从字符到字节的编码使用指定的charset或平台的default charset) 。
文件是否可用或是否可以创建取决于底层平台。 特别是某些平台允许一次仅打开一个文件以供写入FileWriter
(或其他文件写入对象)。 在这种情况下,如果涉及的文件已经打开,则此类中的构造函数将失败。
FileWriter
用于编写字符流。 要写入原始字节流,请考虑使用FileOutputStream
。
追加,可连续调方法;
字符读取
常用操作
一次读一组字符一定比一次读一个字符效率要高
flush刷新管道
字符和字节流在输出时不同的点,需要注意的点;字符在输出时,会以字符为单位进行输出。但是计算机里面存储的都是字节。那我们之前也说过,字符流里面其实也是字节流,那你可以这样认为,如果一个文字能有三个字节。他在读取这个文字的时候,会读满这三个字节,然后再把这个文字输出去。根据编码的不同的啊,所占的字节数也不一样,它会组织成文字,以文字的单位去输出,那么这种你可以认为当它读了半个自己半个文字,字节的时候是不会输出的,会把它缓存起来。
从内存中到硬盘中,这个输出的过程,或者说从输出方给输入方发送的这个过程中,它是有一个缓存存在的。就一个字一个字的缓存。那如果你在进行文字输出的时候,特别是用字符字符输出的时候我们一般会加一个操作叫做flush刷新;它指的就是刷新那个缓存的空间,强制把缓存空间里的内容写出到文件中去。那这个缓存其实缓存的不只是一个字。每一个文字都被缓存起来了。你如果不加刷新,是不会写文字里的,那可能有同学会有疑问,我们在写的时候是写进去了,原因在于我们在进行关闭时,我们调用了close方法,在进行关闭时,它会主动将缓存的这个管道对缓存的这个区域清空掉,说白了也是这样的一次刷新。
不缓存,且不勾选追加的话。内容是0字节;如果不加关闭只刷新也可以显示文字,只要有刷新操作文字就会被显示。
字节转换字符流(转换流)
具体转换流的名称:
InputStreamReader(左为字节流最大父类名称InputStream,右为字符流最大父类名称Reader)
public class InputStreamReader extends Java
InputStreamReader是从字节流到字符流的桥接器:它使用指定的charset
读取字节并将其解码为字符。它使用的字符集可以通过名称指定,也可以明确指定,或者可以接受平台的默认字符集。
每次调用一个InputStreamReader的read()方法都可能导致从底层字节输入流中读取一个或多个字节。 为了实现字节到字符的有效转换,可以从基础流中提取比满足当前读取操作所需的更多字节。
为了获得最高效率,请考虑在BufferedReader中包装InputStreamReader。
FileOutputSteram 网络编程做服务器时要跟客户端发数据的一个流
文件输出流是用于将数据写入
File
或FileDescriptor
的输出流。文件是否可用或是否可以创建取决于底层平台。特别是某些平台允许一次仅打开一个文件以供写入FileOutputStream
(或其他文件写入对象)。在这种情况下,如果涉及的文件已经打开,则此类中的构造函数将失败。FileOutputStream
用于写入诸如图像数据的原始字节流。 要编写字符流,请考虑使用FileWriter
。
Print与BufferedReader
打印流
字符流在输出时与字节流有一个操作层面区别:是否需要刷新管道。
也可用于转换字节流
若有需要往某个位置去输出字符的话,拿到字节流以后更建议转成打印流。
缓存读取流
作用:将字符输入流转换为带有缓存可以一次读取一行的缓存字符读取流
在未接触到readLine方法时;使用read方法返回的是int值,如果读到的是一个数组,返回的是字节个数或字符个数;使用readLine方法的返回值是读取的内容,读到文件结尾会返回null。
收集异常日志
可以以天命名文件夹,以时间命名文件名。
public static void main(String[] args) throws FileNotFoundException {
try {
String s = null;
s.toString();
}catch(Exception e){
PrintWriter pw = new PrintWriter("g://bugCollection.txt");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
pw.println(sdf.format(new Date(0)));
e.printStackTrace(pw);
pw.close();
}
}
properties
Properties
类表示一组持久的属性。Properties
可以保存到流中或从流中加载。属性列表中的每个键及其对应的值都是一个字符串。
属性列表可以包含另一个属性列表作为其“默认值”; 如果在原始属性列表中找不到属性键,则搜索第二个属性列表。
因为Properties
继承自Hashtable
,所以put
和putAll
方法可以应用于Properties
对象。 强烈建议不要使用它们,因为它们允许调用者插入其键或值不是Strings
。 应该使用setProperty
方法。 如果在包含非String
键或值的“受损”Properties
对象上调用store
或save
方法,则调用将失败。 同样,如果在包含非String
密钥的“受损”Properties
对象上调用,则对propertyNames
或list
方法的调用将失败。
在返回的迭代器iterator
(也就是此类的“集合视图”法entrySet()
,keySet()
和values()
)可能不是快速失败的(不像Hashtable的实现)。 这些迭代器保证遍历元素,因为它们在构造时只存在一次,并且可能(但不保证)反映构造之后的任何修改。
load方法 把硬盘中保存的文件(键值对),读取到集合中使用
public void load (InputStream inStream) throws IOException
//从输入字节流中读取属性列表(键和元素对)。
从字节输入流中读取键值对。
参数中使用了字节输入流,通过流对象,可以关联到某文件上,这样就能够加载文本中的数据了。文本中的数据,必须是键值对形式,可以使用空格、等号、冒号等符号分隔。
该方法常用于读取配置文件。
save方法
public void save (OutputStream out, String comments)
不推荐使用。
如果在保存属性列表时发生I / O错误,则此方法不会引发IOException。 保存属性列表的首选方法是通过store(OutputStream out,String注释)方法或storeToXML(OutputStream os,String注释)方法。
调用 store(OutputStream out, String comments)
方法并禁止抛出的IOExceptions。
store方法 把集合中的临时数据,持久化写入到硬盘中存储
public void store (OutputStream out, String comments) throws IOException
将此Properties
表中的此属性列表(键和元素对)以适合使用load(InputStream)
)方法加载到Properties
表的格式写入输出流。
此方法不会写出此Properties
表(如果有)的默认表中的属性。
getProperty方法(与get原理一样)
方法大全
System.out.println(“java版本号:” + System.getProperty(“java.version”)); // java版本号
System.out.println(“Java提供商名称:” + System.getProperty(“java.vendor”)); // Java提供商名称
System.out.println(“Java提供商网站:” + System.getProperty(“java.vendor.url”)); // Java提供商网站
System.out.println(“jre目录:” + System.getProperty(“java.home”)); // Java,哦,应该是jre目录
System.out.println(“Java虚拟机规范版本号:” + System.getProperty(“java.vm.specification.version”)); // Java虚拟机规范版本号
System.out.println(“Java虚拟机规范提供商:” + System.getProperty(“java.vm.specification.vendor”)); // Java虚拟机规范提供商
System.out.println(“Java虚拟机规范名称:” + System.getProperty(“java.vm.specification.name”)); // Java虚拟机规范名称
System.out.println(“Java虚拟机版本号:” + System.getProperty(“java.vm.version”)); // Java虚拟机版本号
System.out.println(“Java虚拟机提供商:” + System.getProperty(“java.vm.vendor”)); // Java虚拟机提供商
System.out.println(“Java虚拟机名称:” + System.getProperty(“java.vm.name”)); // Java虚拟机名称
System.out.println(“Java规范版本号:” + System.getProperty(“java.specification.version”)); // Java规范版本号
System.out.println(“Java规范提供商:” + System.getProperty(“java.specification.vendor”)); // Java规范提供商
System.out.println(“Java规范名称:” + System.getProperty(“java.specification.name”)); // Java规范名称
System.out.println(“Java类版本号:” + System.getProperty(“java.class.version”)); // Java类版本号
System.out.println(“Java类路径:” + System.getProperty(“java.class.path”)); // Java类路径
System.out.println(“Java lib路径:” + System.getProperty(“java.library.path”)); // Java lib路径
System.out.println(“Java输入输出临时路径:” + System.getProperty(“java.io.tmpdir”)); // Java输入输出临时路径
System.out.println(“Java编译器:” + System.getProperty(“java.compiler”)); // Java编译器
System.out.println(“Java执行路径:” + System.getProperty(“java.ext.dirs”)); // Java执行路径
System.out.println(“操作系统名称:” + System.getProperty(“os.name”)); // 操作系统名称
System.out.println(“操作系统的架构:” + System.getProperty(“os.arch”)); // 操作系统的架构
System.out.println(“操作系统版本号:” + System.getProperty(“os.version”)); // 操作系统版本号
System.out.println(“文件分隔符:” + System.getProperty(“file.separator”)); // 文件分隔符
System.out.println(“路径分隔符:” + System.getProperty(“path.separator”)); // 路径分隔符
System.out.println(“直线分隔符:” + System.getProperty(“line.separator”)); // 直线分隔符
System.out.println(“操作系统用户名:” + System.getProperty(“user.name”)); // 用户名
System.out.println(“操作系统用户的主目录:” + System.getProperty(“user.home”)); // 用户的主目录
System.out.println(“当前程序所在目录:” + System.getProperty(“user.dir”)); // 当前程序所在目录
System.out.println(“平台默认的字符编码:” + System.getProperty(“file.encoding”)); // 平台默认的字符编码
序列化技术
序列化:将程序中的对象直接以文件的形式存储起来
Java序列化就是指把Java对象转换为字节序列的过程。
Java反序列化就是指把字节序列恢复为Java对象的过程。
序列化最重要的作用:在传递和保存对象时.保证对象的完整性和可传递性。对象转换为有序字节流,以便在网络上传输或者保存在本地文件中。
序列化优点:
①将对象转为字节流存储到硬盘上,当JVM停机的话,字节流还会在硬盘上默默等待,等待下一次JVM的启动,把序列化的对象,通过反序列化为原来的对象,并且序列化的二进制序列能够减少存储空间(永久性保存对象)。
②序列化成字节流形式的对象可以进行网络传输(二进制形式),方便了网络传输。
③通过序列化可以在进程间传递对象。
Java实现序列化和反序列化的过程
1、实现序列化的必备要求:
只有实现了Serializable或者Externalizable接口的类的对象才能被序列化为字节序列。(不是则会抛出异常)
2、JDK中序列化和反序列化的API:
①java.io.ObjectInputStream:对象输入流。
该类的readObject()方法从输入流中读取字节序列,然后将字节序列反序列化为一个对象并返回。
②java.io.ObjectOutputStream:对象输出流。
该类的writeObject(Object obj)方法将将传入的obj对象进行序列化,把得到的字节序列写入到目标输出流中进行输出。
3、实现序列化和反序列化的三种实现:
①若Student类仅仅实现了Serializable接口,则可以按照以下方式进行序列化和反序列化。
ObjectOutputStream采用默认的序列化方式,对Student对象的非transient的实例变量进行序列化。<br />
ObjcetInputStream采用默认的反序列化方式,对Student对象的非transient的实例变量进行反序列化。
②若Student类仅仅实现了Serializable接口,并且还定义了readObject(ObjectInputStream in)和writeObject(ObjectOutputSteam out),则采用以下方式进行序列化与反序列化。
ObjectOutputStream调用Student对象的writeObject(ObjectOutputStream out)的方法进行序列化。<br />
ObjectInputStream会调用Student对象的readObject(ObjectInputStream in)的方法进行反序列化。
③若Student类实现了Externalnalizable接口,且Student类必须实现readExternal(ObjectInput in)和writeExternal(ObjectOutput out)方法,则按照以下方式进行序列化与反序列化。
ObjectOutputStream调用Student对象的writeExternal(ObjectOutput out))的方法进行序列化。<br />
ObjectInputStream会调用Student对象的readExternal(ObjectInput in)的方法进行反序列化。
一个类型想被序列化,要标记接口Serializable。假如内含其它属性是使用了其他类型, 但没有实现标记接口的。那一个类型不可被序列化,故此类要被序列化,里面的属性也得被序列化。String类型也有实现此标记接口。Collection(List、Set)与Map也有实现了Serializable。
public static void main(String[] args) throws IOException, ClassNotFoundException {
//序列化与反序列化
/*
//序列化
Book b = new Book("三国演义","描述了三国时代的故事");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("g://book.txt"));
oos.writeObject(b);
oos.close();
*/ //此次输出会出bug,告知此类不允许被序列化
//反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("g://Book.txt"));
// Object o = ois.readObject();//可强转
Book o = (Book) ois.readObject();
System.out.println(o.getName());
//相同包相同类才可以正确接收
}
static class Book implements Serializable{//使用这个接口后不用加任何的抽象方法实现,此接口为标记接口,实现其表示Book是此类的子,并无提供任何抽象方法
private String name;
private String info;
public Book() {
}
public Book(String name, String info) {
this.name = name;
this.info = info;
}
@Override
public String toString() {
return "Book [name=" + name + ", info=" + info + "]";
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the info
*/
public String getInfo() {
return info;
}
/**
* @param info the info to set
*/
public void setInfo(String info) {
this.info = info;
}
}
try-with-resources
在JDK 1.7版本之前,Java官方说我们在操作需要关闭的资源(输入流、输出流)时, 经常需要释放资源。
使用try-with-resouces
为了能自动关闭资源,资源声明和初始化必须在try中。
try (PrintWriter writer = new PrintWriter(new File("test.txt"))) {
writer.println("Hello World");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
用 try-with-resources 替换 try–catch-finally
Scanner scanner = null;
try {
scanner = new Scanner(new File("test.txt"));
while (scanner.hasNext()) {
System.out.println(scanner.nextLine());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (scanner != null) {
scanner.close();
}
}
try (Scanner scanner = new Scanner(new File("text.txt"))) {
while (scanner.hasNext()) {
System.out.println(scanner.nextLine());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
一个传统典型的try-catch-finally的代码块要冗长的多,而try-with-resources更简洁
try-with-resources 应用于多个资源 Resource
将多个资源的声明语句在try()的代码块里即可。
try (Scanner scanner = new Scanner(new File("testRead.txt"));
PrintWriter writer = new PrintWriter(new File("testWrite.txt"))) {
while (scanner.hasNext()) {
writer.print(scanner.nextLine());
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}