File类
提供一些方法(api)来操纵文件和获取文件的信息
File常用API
属性
获取系统分隔符
不同操作系统的分隔符
windows的目录分割符号是用向右的斜线,java中\ 表示转义字符,所以向右的斜线需要写两个 \;
linux目录分割符号是向左的斜线: /
private static final FileSystem fs = DefaultFileSystem.getFileSystem();
public static final char separatorChar = fs.getSeparator();
public static final String pathSeparator = "" + pathSeparatorChar;
程序案例:
System.out.println("File.pathSeparator:" + File.pathSeparator);
System.out.println("File.separatorChar:" + File.separatorChar);
程序运行结果:
File.pathSeparator:;
File.separatorChar:\
方法
获取文件/文件夹名称
程序案例:
File file = new File("D:\\java-workspace");
System.out.println("文件夹/文件名: " + file.getName());
System.out.println("绝对路径:" + file.getAbsolutePath());
System.out.println("路径: " + file.getPath());
程序运行结果:
文件夹/文件名: \java-workspace
绝对路径:D:\\java-workspace
路径: D:\\java-workspace
文件夹/文件
程序案例:
File file = new File("D:\\java-workspace");
System.out\.println("是否是文件: " + file.isDirectory());
System.out\.println("是否是文件: " + file.isFile());
程序运行结果:
是否是文件: true
是否是文件: false
文件操作
程序案例:
File file = new File("D:\\newFile.txt");if (file.exists()) {file.delete(); // 删除当前文件,如果是文件夹,要确保文件夹内容为空。
}
boolean f = file.createNewFile();if (f) {// 如果文件创建成功System.out\.println("是否是隐藏问: " + file.isHidden());System.out\.println("最后一次修改时间: " + new Date(file.lastModified()));System.out\.println("是否可读: " + file.canRead());System.out\.println("是否可写: " + file.canWrite());System.out\.println("是否可执行: " + file.canExecute());System.out\.println("磁盘总空间大小: "+ file.getTotalSpace()/1024/1024/1024+"G");System.out\.println("磁盘可用空间大小: "+file.getUsableSpace()/1024/1024/1024+"G");System.out\.println("磁盘剩余空间大小: "+ file.getFreeSpace()/1024/1024/1024+"G");
}
修改文件名称
file.renameTo(new File("D:\\modify.txt"));
System.out\.println("文件名称: "+ file.getName());
File nFile = new File("D:\\modify.txt");
System.out.println("修改文件名: "+ file.renameTo(nFile));
程序运行结果:
是否是隐藏问: false
最后一次修改时间: Tue Jan 25 14:28:29 CST 2022
是否可读: true
是否可写: true
是否可执行: true
磁盘总空间大小: 429G
磁盘可用空间大小: 424G
磁盘剩余空间大小: 424G
修改文件名: true
文件夹、文件访问
程序案例:
File file = new File("C:\\");
String[] fs = file.list();
File[] files = file.listFiles();for(File f: files) {System.out\.println(f.getName());
}
程序运行结果(部分):
eclipse
hiberfil.sys
Intel
pagefile.sys
PerfLogs
Program Files
流的概念
流是个抽象的概念,是\对输入输出设备的抽象\,Java程序中,对于数据的输入/输出操作都是以“流”的方式进行。设备可以是文件,网络,内存等。
1.2.1 输入、输出流
提输入、输出是相对于\内存\的操作而言。
字节流、字符流
字节流–传输过程中,传输数据的最基本单位是字节的流。
字符流–传输过程中,传输数据的最基本单位是字符的流。
1.2.3 流的层次结构
1.3 字节流
以字节为(8bits)为单位进行IO操作,一般用来处理二进制文件(例如:图片、声音、视频等)。
1.3.1 字节输入流
1.3.2 字节输出流
程序案例:
程序案例:使用字节流完成流的拷贝功能。
InputStream in = new FileInputStream("c:\\jdk-8u311-windows-x64.exe");OutputStream out = new FileOutputStream("d:\\jdk-8u311-windows-x64.exe");int n = 0;
long t1 = System.currentTimeMillis();while ((n = in.read()) != -1) {out.write(n);}
long t2 = System.currentTimeMillis();
out.close();
in.close();System.out\.println("字节拷贝,耗时: " + (t2 - t1)+"ms");程序运行结果: 字节拷贝,耗时: 439897ms
字节流的缓存
如果按照一个字节一个字节的进行IO操作,字节的入和出的速度不是对等的。比如我们在工地经常看到传递砖块的操作,一般不是一个砖接一个砖传递,而是几块砖头放在一起传递。在流操作中,经常用到的技术是定义一个缓存区域,积累到一定数量后,一次性发送给接收端。
程序案例: 使用缓存优化字节流完成流的拷贝功能
InputStream in = new FileInputStream("c:\\jdk-8u311-windows-x64.exe");OutputStream out = new FileOutputStream("d:\\jdk-8u311-windows-x64.exe");long t1 = System.currentTimeMillis();
byte[] buffer = new byte[1024];
while (in.read(buffer) != -1) {out.write(buffer);
}
long t2 = System.currentTimeMillis();
out.close();
in.close();
System.out\.println("字节缓存拷贝,耗时: " + (t2 - t1)+"ms");
程序运行结果:
字节缓存拷贝,耗时: 586ms
1.4 字符流
以字符为单位的IO操作,一般用来操作文本信息。
1.4.1 字符输入流
程序案例:
BufferedReader in =
new BufferedReader(
new FileReader("D:\\java-workspace\\javaio\\src\\javaio\\CopyDemo.java"));
String line = null;
int index = 0;while ((line = in.readLine()) != null) {System.out\.println((++index) +"\t"+ line);} in.close();
程序运行结果(部分):
1 package javaio;
2
3 import java.io.FileInputStream;
4 import java.io.FileOutputStream;
5 import java.io.IOException;
6 import java.io.InputStream;
7 import java.io.OutputStream;
1.4.2 字符输出流
程序案例:
PrintWriter pw = new PrintWriter(new FileWriter("D:\\pw.html"));
pw.println("<h1>你好,java IO 流</h1>");
pw.println("<h2>java爱好者</h2>");
pw.println("<h3>测试字符输出流的用法</h3>");
pw.flush();
pw.close();
程序运行结果:
流的异常捕获方案
try … catch … finally
一般流的异常都是可检查异常,所有在开发过程中需要强制使用异常捕获流程处理。一般打开流对象后,如果不关闭会一直占用一个通道(就像开辟了几条通道进行数据传输,其中一条已经完成,但是不释放则会一直占用此通道,造成资源浪费)。一般开发中我们会在finally语句块调用流的关闭语句,以此释放流资源。
程序案例:
InputStream in = null;
try{in = new FileInputStream(“D:\\in.dat”);
}catch(IOException e){//异常信息
}finally{
in.close(); //关闭流对象
}
try … with … resource
如果打开了外部资源(文件、数据库连接、网络连接等),我们必须在这些外部资源使用完毕后,手动关闭它们。因为外部资源不由JVM管理,无法享用JVM的垃圾回收机制,如果我们不在编程时确保在正确的时机关闭外部资源,就会导致外部资源泄露,紧接着就会出现文件被异常占用,数据库连接过多导致连接池溢出等诸多很严重的问题。
对于实现了Closeable接口的类,可以使用try…with…resource处理异常。
程序案例:
try(InputStream in = new FileInputStream(“D:/abc.txt”)){//异常信息
}catch(IOException e){//捕获异常
}
序列化
序列化:一个Java对象作一下“变换”,变成\字节序列\,这样一来方便持久化存储到磁盘,避免程序运行结束后对象就从内存里消失,另外变换成字节序列也更便于网络运输和传播
反序列化:把字节序列恢复为原先的Java对象。
实现序列化接口的对象,有一个特殊常量serialVersinUID。
提示: static修饰的不会被序列化,transient修饰的不会被序列化。
对象流
对象流:有的时候,我们可能需要将内存中的对象持久化到硬盘上,或者将硬盘中的对象信息读到内存中,这个时候我们需要使用对象输入输出流。
当使用对象流写入或者读取对象的时候,必须保证该对象是序列化的,这样是为了保证对象能够正确的写入文件,并能够把对象正确的读回程序。
(1) ObjectInputStream: 字节对象输入流
(2) ObjectOutputStream: 字节对象输出流
程序案例((ObjectInputStream):
public class Car implements Serializable{private static final long serialVersionUID\ = 1L;private String brand;private String color;private double price; public Car(String brand, String color, double price) {this.brand = brand;this.color = color;this.price = price;}//其它代码省略
}
.....ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("D:\\car.dat"));
Car car = new Car("BMW", "黑色", 1200000.00);
out.writeObject(car);
out.flush();
out.close();
程序运行结果:
程序案例(ObjectOutputStream):
ObjectInputStream in = new ObjectInputStream(new FileInputStream("D:\\car.dat"));
Car car = (Car) in.readObject();
System.out\.println(car);
in.close();
程序运行结果:
Car [brand=BMW, color=黑色, price=1200000.0]
序列化接口
Serializable是一个标记性接口,没有实现任何内容,只是告诉JVM当前对象需要序列化。
public interface Serializable {}
日志
日志概念
日志是一种记录、跟踪程序运行的技术。作用主要在以下三个方面:
调试
在Java项目调试时,查看栈信息可以方便地知道当前程序的运行状态,输出的日志便于记录程序在之前的运行结果。
错误定位
项目在运行一段时候后,可能由于数据问题,网络问题,内存问题等出现异常。这时日志可以帮助开发或者运维人员快速定位错误位置,提出解决方案。
数据分析
日志中蕴含了大量的用户数据,包括点击行为,兴趣偏好等,用户画像对于公布下一步的战略方向有一定指引作用。
日志级别
(1) ALL: 最低等级的,用于打开所有日志记录。
(2) TRACE: 很低的日志级别,一般不会使用。
(3) DEBUG: 指出细粒度信息事件对调试应用程序是非常有帮助的,主要用于打印一些调试信息。
(4) WARN: 表明会出现潜在错误的情形,有些信息不是错误信息,但是也要给程序员一些提示。
(5) INFO: 消息在粗粒度级别上突出强调应用程序的运行过程。打印一些你感兴趣的或者重要的信息。这个用于生产环境中输出程序运行的一些重要信息,但是不能滥用,避免打印过多的日志。
(6) ERROR: 指出虽然发生错误事件,但任然不影响系统的继续运行。打印错误和异常信息,如果不想输出太多的日志,可以使用这个级别。
(7) FATAL: 指出每个严重的错误事件将会导致应用程序的退出。这个级别比较高了。重大错误。
log4j
Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
日志配置
Loggers
Loggers组件在此系统中被分为五个级别:DEBUG、INFO、WARN、ERROR和FATAL。这五个级别是有顺序的,DEBUG < INFO < WARN < ERROR < FATAL,分别用来指定这条日志信息的重要程度Log4j有一个规则:只输出级别不低于设定级别的日志信息,假设Loggers级别设定为INFO,则INFO、WARN、ERROR和FATAL级别的日志信息都会输出,而级别比INFO低的DEBUG则不会输出。
Appenders
禁用和使用日志请求只是Log4j的基本功能,Log4j日志系统还提供许多强大的功能,比如允许把日志输出到不同的地方,如控制台(Console)、文件(Files)等,可根据天数或者文件大小产生新的文件,可以以流的形式发送到其它地方等等。
Layouts
Log4j可以在Appenders的后面附加Layouts来完成这个功能。Layouts提供四种日志输出样式,如根据HTML样式、自由指定样式、包含日志级别与信息的样式和包含日志时间、线程、类别等信息的样式。
PatternLayout格式参数如下表:
序号 | 格式 | 含义 |
---|---|---|
1 | %p | 输出日志信息的优先级:DEBUG,INFO,WARN,ERROR,FATAL。 |
2 | %d | 输出日志时间点的日期或时间。默认格式为ISO8601,也可以在其后指定格式。%d{yyyy/MM/dd HH:mm:ss SSS}。 |
3 | %r | 输出自应用程序启动到输出该log信息耗费的毫秒数。 |
4 | %t | 输出产生该日志事件的线程名。 |
5 | %l | 输出日志事件的发生位置,相当于%C%M(%F%L)的组合,包括类全名、方法、文件名以及在代码中的行数。例如:testLog4j.java: |
6 | %c | 输出日志信息所属的类目,通常就是所在类的全名。 |
7 | %M | 输出产生日志信息的方法名 |
8 | %F | 输出日志消息产生时所在的文件名称 |
9 | %L | 输出代码中的行号 |
10 | %m | 输出代码中指定的具体日志位置 |
11 | %n | 输出一个回车换行符,Windows平台为“\r\n”,Unix平台为“\n”。 |
日志使用
配置文件: 在src定义一个properties文件: log4j.properties。
配置文件案例
log4j.rootLogger = DEBUG,Console,infoFile
log4j.appender.Console = org.apache.log4j.ConsoleAppender
log4j.appender.Console.Target = System.out
log4j.appender.Console.Threshold = DEBUG
log4j.appender.Console.layout = org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=[%c] - %m%n
log4j.appender.infoFile = org.apache.log4j.FileAppender
log4j.appender.infoFile.File=D:/logs/test.log
log4j.appender.infoFile.Threshold = DEBUG
log4j.appender.infoFile.layout = org.apache.log4j.PatternLayout
log4j.appender.infoFile.layout.ConversionPattern=[%c] - %m%n
程序案例:
Logger.getLogger(Logger.class).debug("测试log4j日志...");
NIO编程
java.nio全称java non-blocking IO(实际上是 new io),是指JDK 1.4 及以上版本里提供的新api(new IO) ,为所有的原始类型(boolean类型除外)提供缓存支持的数据容器,使用它可以提供非阻塞式的高伸缩性网络。
NIO组件
缓冲区
(缓冲区(Buffer): 本质上是一个可以写入数据的内存块,然后可以再次读取,该对象提供了一组方法,可以更轻松地使用内存。
通道(Channel):
channel(通道)可以同时进行读写,而流只能读或者只能写。
通道可以实现异步读写数据。
通道可以从缓冲读数据,也可以写数据到缓冲。
channel提供了一个map()方法,可以直接将数据映射到内存中。
选择器(Selector)
可以检测多个NIO channel,看看读或者写事件是否就绪。多个\Channel\以事件的方式可以注册到同一个Selector,从而达到用一个线程处理多个请求成为可能。
NIO编程
程序案例:
String fileName = "D:\\java-workspace\\javaio\\src\\javaio\\ReaderDemo.java";RandomAccessFile raf = new RandomAccessFile(fileName, "rw");/** 创建Channel*/
FileChannel inChannel = raf.getChannel();/** 创建缓存区,缓存48个字节*/
ByteBuffer buffer = ByteBuffer.allocate(48);/*** 读取48个字符到buffer缓存中* 检查是否到达文件的末尾(-1表示末尾)*/
while((bytesRead = inChannel.read(buffer)) !=-1) {/*** 把buffer当前位置更改为buffer缓冲区的第一个位置*/buffer.flip(); while(buffer.hasRemaining()) {/*** 从buffer当前位置一个字节、一个字节的读取缓存区数据*/System.out\.print((char)buffer.get());}/*** 清空buffer,设置到buffer缓冲区的第一个位置*/buffer.clear();}
raf.close();
程序运行结果(部分):
package javaio;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;public class ReaderDemo { public static void main(String[] args) throws IOException {:::