檫除与转换
- 严格的泛型代码里,带泛型声明的类总是应该带着类型参数;
- 为了兼容旧的代码,也允许在使用泛型类时不指定类型实参;
- 若未指定类型实参,则它默认是定义泛型类时声明的类型形参的上限类型。
File
File是Java.io包下的类,代表与平台无关的文件与目录;
File能创建(createNewFile)、删除(delete)、重命名文件(renameTo)和目录,也能检测、访问文件和目录本身;
File不能访问文件的内容,如果要访问文件的内容,则需要使用输入、输出流;
路径分隔符
1.Windows系统采用反斜线(\)作为路径分隔符;
2.Java程序中可以使用两条反斜线作为路径分隔符;
3.Java也支持使用一条正斜线(/)作为路径分隔符:
过滤文件
1.File类的listFiles()方法可以接受一个参数,用于在列举文件时对其进行过滤;
2,File类会依次将文件传给过滤器,当过滤器返回true时,File类才会列举该文件。
public interface FileFilter{
boolean accpet (File pathname);
}
public interface FilenameFilter{
boolean accept(File dir, String name);
}
遍历文件
public static void printfile(String filepath,int depth) throws IllegalAccessException {
File file=new File(filepath);
if(!file.exists()){
throw new IllegalAccessException("文件不存在");
}
for(int i=0;i>depth;i++){
System.out.println(" ");
}
//打印名字
if(file.isFile()){
System.out.print("-");
}
System.out.println(file.getName());
//目录递归
if(file.isDirectory()){
File[] files=file.listFiles();
for(File f:files){
printfile(f.getPath(),depth+1);
}
}
}
IO流简介
IO(Input Output)用于实现对数据的输入与输出操作;Java把不同的输入/输出源(键盘、文件、网络等)抽象表述为流(stream);流是从起源到接收的有序数据,程序采用同一方式可以访问不同的输入、输出源。
流的分类
输入流与输出流(方向)
--输入流只能读数据,不能写入数据;--输出流只能写入数据,不能读取数据。
输入、输出的方向:
通常从程序运行所在内存的角度来区分;数据从硬盘流向内存,通常称为输入流;数据从内存流向硬盘,通常称为输出流;
字节流与字符流(数据)
--字节流操作的数据单元是8位的字节;--字符流操作的数据单元是16位的字符;
节点流和处理流(功能)
--节点流可以直接从/向一个特定的IO设备(磁盘、网络等)读写数据,也称为低级流;
--处理流是对节点流的连接或封装,用于简化数据读/写功能或提高效率,也称为高级流;
流的模型
抽象基类
输入流
输出流
注意事项:
- 上述四个类都是抽象类,不能直接实例化;
- 上述四个类都定义了close()方法,你需要在使用流之后调用此方法将其关闭;
- 无论是否发生异常,使用流后都要阐释关闭它,通常在finally中关闭流;
- 上述四个类都实现了Closeable接口,因此可以在try()中创建流,以便于自动关闭。
文件流
这四个类都是节点流,会直接和指定的文件关联,即实例化时传入文件路径!
应用举例--复制文件
public static void copyfile(String filepathsrc,String filepathdest){
try(
FileInputStream fis=new FileInputStream(filepathsrc);
FileOutputStream fos=new FileOutputStream(filepathdest);
){
byte[] bytes=new byte[128];
int len=0;//实际读取的字节数
while((len=fis.read(bytes,0,128))>0) {//读到末尾返回-1,结束循环
fos.write(bytes,0,len);
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void copytextfile(String filepath_org,String filepath_dest){
try(
FileReader fr=new FileReader(filepath_org);
FileWriter fw=new FileWriter(filepath_dest);
){
char[] chars=new char[128];
int len=0;
while((len=fr.read(chars,0,128))>0){
System.out.println(String.valueOf(chars,0,len));
fw.write(chars,0,len);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
throw new RuntimeException(e);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
缓冲流
举例:
public static void copyfile(String scrfilepath,String destfilepath){
try(
BufferedInputStream bis =new BufferedInputStream(new FileInputStream(scrfilepath));
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(destfilepath));
// FileInputStream fis=new FileInputStream(scrfilepath);
// FileOutputStream fos=new FileOutputStream(destfilepath);
){
byte[] bytes=new byte[128];
int len=0;
// while((len=fis.read(bytes,0,128))>0){
// fos.write(bytes,0,len);
// }
while((len=bis.read(bytes,0,128))>0){
bos.write(bytes,0,len);
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
转换流
public static void main(String[] args){
try(
InputStreamReader r=new InputStreamReader(System.in);
BufferedReader br=new BufferedReader(r);
)
{
String line=null;
while((line=br.readLine())!=null){
if(line.equalsIgnoreCase("exit")){
break;
}
System.out.println(line);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
打印流
重定向
在Java中:
- System.in代表标准输入,默认情况下代表键盘;
- System.out代表标准输出,默认情况下代表显示器;
实例:
RandomAccessFile
- RAF支持随机访问文件,即程序可以跳转到文件的任意位置来访问文件;
- RAF包含了丰富的功能,既支持读取文件内容,也支持文件输出数据;
- RAF允许自由定义文件指针,该指针既可以向前移动,也可以向后移动;
- RAF包含的输入和输出的方法,与InputStream和OutputStream类似;
long getFilePointer()--返回文件指针当前所指向的位置;
void seek(long pos)--将文件指针定位到指定位置(pos);
序列化
序列化机制
序列化机制可以将对象转换成字节序列,这些字节序列可以保存在磁盘上,也可以在网络中传输,并允许程序将这些字节序列再次恢复成原来的对象。
- 对象的序列化(Serialize),是指将一个java对象写入IO流中;
- 对象的反序列化(Deserialize),是指从IO流中恢复该Java对象;
支持序列化
- 若对象要支持序列化机制,则它的类需要实现Serializable接口;
- 该接口是一个标记接口,它没有提供任何方法,只是表明该类是可以序列化的;
- Java的很多类已经实现了Serializable接口,如包装类,String,Date等。
实现序列化
使用对象流ObjectInputStream和ObjectOutputStream(处理流)!
- 序列化
- 创建ObjectOutputStream对象;
- 调用ObjectOutputStream对象的writeObject()方法,以输出对象序列。
- 反序列化
- 创建ObjectInputStream对象;
- 调用ObjectInputStream对象的readObject()方法,将对象序列恢复成对象。
序列化的规则
序列化的目的是将对象中的数据(成员变量)转换为字节序列,和成员方法无关。为了正确的序列化某个对象,这个对象以及它所对应的类需要符合如下的规则:
- 该对象中引用类型的成员变量必须是可序列化的;
- 该类的直接或间接的父类,要么具有午餐构造器,要么也是可序列化的;
- 一个对象只会被序列化一次,再次序列化时仅仅会输出它的序列号而已。
每个被序列化的对象都有一个序列号;在序列化对象之前,程序会先检查它是否被序列化过;若对象没有被序列化过,程序会将其转为字节序列;若对象已经被序列化过,程序会直接输出它的序列号。
序列化的版本
一个常见的场景:
定义类的序列化版本,在反序列化时,只要对象中所存的版本和当前类的版本一致,就允许做恢复数据的操作,否则将会抛出序列化版本不一致的错误。
java允许以下形式来定义序列化版本:
prviate static final long serialVersionUID=...;(类中)
- 如果没有显示定义serialVersionUID,则JVM会根据类的信息自动计算出它的值;由于升级前后类的内容发生了变化,则该值的计算结果通常不同,这就会导致反序列化失败;
- 最好在序列化的类中显示的定义serialVersionUID,这样即便对象被序列化后,它所对应的类被修改了,由于版本号是一致的,所以该对象依然可以被正确的反序列化。
- 在反序列时,只有那些成功匹配上的成员变量会被恢复!
Transient关键字
transient关键字用于修饰成员变量(只能修饰成员变量,不能修饰类中其他内容),表示反序列时将会忽略它;
在某些场景中,不希望序列化某个成员变量,例如:
该成员变量是敏感信息,如用户密码、银行账号登;该成员变量是引用类型,但它没有实现序列化接口。
private transient String password;
自定义序列化
自定义序列化,可以让程序自主控制序列化以及反序列化成员变量的方式,可以通过在类中定义如下具有特殊签名的方法进行实现。
- 写入对象的成员变量
private void writeObject(ObjectOutputStream out) throws IOException;
- 恢复对象的成员变量
private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException;
NIO
- new IO(NIO),是Java从4开始陆续增加的IO处理的新功能,这些类都被放在java.nio包以及子包下,并且Java.io包中很多类也以NIO为基础进行了改写;
- NIO采用内存映射文件的方式来处理输入和输出,它将文件或文件的一段区域映射到内存中,这样就可以像访问内存一样来访问文件了,这比传统的输入和输出的方式要高效很多;
- Channel和Buffer是NIO的两个核心对象:
--Channel(通道)是对传统的输入/输出系统的模拟,所有数据都要通过通道传输;
--Buffer(缓冲),是一个容器(数组),是程序与Channel沟通的桥梁,程序向Channel写入的数据都要先放到Buffer中,程序从Channel中读取的数据也会被放到Buffer中。
Buffer
Buffer是抽象类,它有如下子类:
ByteBuffer 、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer;
- 只能通过静态方法实例化Buffer:
public static CharBuffer allocate(int capacity);
- Buffer的核心是4个成员变量:
- 容量(capacity):Buffer可以存储的最大数据量,该值不可改变;
- 界限(limit):Buffer中可以读、写数据的边界,limit之后的数据不能访问;
- 位置(Position):下一个可以被读写的数据的位置(索引);
- 标记(Mark):Buffer允许将位置直接定位到该标记处,这是一个可选的属性;
上述变量满足如下的关系:0<=mark<=position<=limit<=capacity
示例:
Channel
Channel的实现类:
FileChannel、SocketChannel、ServerSocketChannel、DatagramChannel、Pipe.SourceChannel、Pipe.SinkChannel;
Channel的实例化:
- 各个Channel类提供的open()方法;
- FileInputStream、FileOutputStream、RandomAcessFile类提供了getChannel()方法,可以直接返回FileChannel。
Channel方法:
- map()方法用于将Channel对应的数据映射成ByteBuffer;
- read()方法有一系列重载的形式,用于从Buffer中读取数据;
- write()方法有一系列重载的形式,用于向Buffer中写入数据;
Charset
- 编码(Encoder):将明文的字符序列转换成二进制序列;
- 解码(Decoder):将二进制序列转换成明文的字符序列;
- 字符集(Charset):字符序列与字节序列转换时所遵循的规则,若编码时采用的字符集与解码时采用的字符集不一致,就会产生乱码。
Paths、Files
- Paths
- Paths类提供了两个静态的get()方法,用于返回Path类型的对象;
- Path是一个接口,代表与平台无关的路径,提供了访问路径的方法;
- Files
- Files是一个操作文件的工具类,它提供了大量便捷的访问文件的方法。