File类
java.io.File 类是文件和目录路径名的抽象表示,主要用于文件和目录的创建、查找和删除等操作。
构造方法
一个File对象代表硬盘中实际存在的一个文件或者目录。
无论该路径下是否存在文件或者目录,都不影响File对象的创建。
// 通过将给定的路径名字符串转换为抽象路径名来创建新的File实例
File(String pathname)
// 从父路径名字符串和子路径名字符串创建新的 File实例
File(String parent, String child)
// 从父抽象路径名和子路径名字符串创建新的 File实例。
File(File parent, String child)
File file = new File("C:\\Users\\qazokmzjl\\Desktop\\testFile.txt");
File file1 = new File("C:\\Users\\qazokmzjl\\Desktop", "testFile.txt");
File file2 = new File("C:\\Users\\qazokmzjl\\Desktop");
File file3 = new File(file2, "testFile.txt");
常量
// windows是 ;,linux是 :,就想象配置全局变量path那儿,分隔多个路径的
// 与系统相关的路径分隔符字符,为方便起见,表示为字符串
static final String pathSeparator = "" + pathSeparatorChar
// 与系统相关的路径分隔符
static final char pathSeparatorChar = fs.getPathSeparator()
// windows是 \ ,linux是 / ,就是路径多层目录的分隔符
// 与系统相关的默认名称 - 分隔符字符,以方便的方式表示为字符串
static final String separator = "" + separatorChar
// 与系统相关的默认名称分隔符
static final char separatorChar = fs.getSeparator()
常用方法
绝对路径:从盘符开始的路径,这是一个完整的路径。
相对路径:相对于项目目录的路径,这是一个便捷的路径,开发中经常使用。
// 获取功能
// 返回此File的绝对路径名字符串
String getAbsolutePath()
// 将此File转换为路径名字符串
String getPath()
// 返回由此File表示的文件或目录的名称
String getName()
// 文件大小,单位字节
// 文件为空或者不存在,返回0,
// 只能获取单个文件的长度,不能获取文件夹的长度
long length()
// 判断功能
// 此File表示的文件或目录是否实际存在
boolean exists()
// 此File表示的是否为目录
boolean isDirectory()
// 此File表示的是否为文件
boolean isFile()
// 创建删除功能
// 当且仅当具有该名称的文件尚不存在时,创建一个新的空文件
boolean createNewFile()
// 删除由此File表示的文件或目录
public boolean delete()
// 创建由此File表示的目录
boolean mkdir()
// 创建由此File表示的目录,包括任何必需但不存在的父目录
boolean mkdirs()
// 目录的遍历
// 返回一个String数组,表示该File目录中的所有子文件或目录
String[] list()
// 返回一个File数组,表示该File目录中的所有的子文件或目录
File[] listFiles()
IO
Java中I/O操作主要是指使用 java.io 包下的内容,进行输入、输出操作。输入也叫做读取数据,输出也叫做作写 出数据。
顶级父类
| 输入流 | 输出流 | |
|---|---|---|
| 字节流 | InputStream | OutputStream |
| 字符流 | Reader | Writer |
字节流
一切皆为字节
一切文件数据(文本、图片、视频等)在存储时,都是以二进制数字的形式保存,都一个一个的字节,那么传输时一 样如此。所以,字节流可以传输任意文件数据。在操作流的时候,我们要时刻明确,无论使用什么样的流对象,底 层传输的始终为二进制数据。
字节输出流 OutputStream
java.io.OutputStream抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地。它定义了字节输出流的基本共性功能方法。
// 关闭此输出流并释放与此流相关联的任何系统资源
// 当完成流的操作时,必须调用此方法,释放系统资源
void close()
// 刷新此输出流并强制任何缓冲的输出字节被写出
// 刷新缓冲区,流对象可以继续使用
void flush()
// 将 b.length字节从指定的字节数组写入此输出流
void write(byte[] b)
// 从指定的字节数组写入 len字节,从偏移量 off开始输 出到此输出流
void write(byte[] b, int off, int len)
// 将指定的字节输出流
abstract void write(int b)
FileOutputStream类
java.io.FileOutputStream 类是文件输出流,用于将数据写出到文件。
构造方法
// 创建文件输出流以写入由指定的 File对象表示的文件
FileOutputStream(File file)
FileOutputStream(File file, boolean append)
// 建文件输出流以指定的名称写入文件
FileOutputStream(String name)
FileOutputStream(String name, boolean append)
常用方法
void write(int b)
void write(byte b[])
void write(byte[] b, int off, int len)
字节输入流 InputStream
java.io.InputStream 抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存中。它定义了字节输入 流的基本共性功能方法。
// 关闭此输入流并释放与此流相关联的任何系统资源
void close()
// 从输入流读取数据的下一个字节
abstract int read()
// 从输入流中读取一些字节数,并将它们存储到字节数组 b中
int read(byte[] b)
FileInputStream类
java.io.FileInputStream 类是文件输入流,从文件中读取字节。
构造方法
// 通过打开与实际文件的连接来创建一个 FileInputStream ,
// 该文件由文件系 统中的 File对象 file命名
FileInputStream(File file)
// 通过打开与实际文件的连接来创建一个 FileInputStream ,
// 该文件由文件 系统中的路径名 name命名
FileInputStream(String name)
常用方法
// 每次读取一个字节的数据,得到的char提升为int类型,
// 读取到文件末尾,返回-1
int read()
int read(byte[] b)
字符流
当使用字节流读取文本文件时,可能会有一个小问题。就是遇到中文字符时,可能不会显示完整的字符,那是因为 一个中文字符可能占用多个字节存储。所以Java提供一些字符流类,以字符为单位读写数据,专门用于处理文本文件。
字符输入流 Reader
java.io.Reader 抽象类是表示用于读取字符流的所有类的超类,可以读取字符信息到内存中。它定义了字符输入 流的基本共性功能方法。
// 关闭此流并释放与此流相关联的任何系统资源
void close()
// 从输入流读取一个字符
int read()
// 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中
int read(char[] cbuf)
FileReader类
java.io.FileReader类是读取字符文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。
字符编码:字节与字符的对应规则。Windows系统的中文编码默认是GBK编码表。 idea中UTF-8
字节缓冲区:一个字节数组,用来临时存储字节数据。
// 创建一个新的 FileReader ,给定要读取的File对象
FileReader(File file)
// 创建一个新的 FileReader ,给定要读取的文件的名称
FileReader(String fileName)
InputStreamReader类
转换流java.io.InputStreamReader ,是Reader的子类,是从字节流到字符流的桥梁。它读取字节,并使用指定 的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。
// 创建一个使用默认字符集的字符流
InputStreamReader(InputStream in)
// 创建一个指定字符集的字符流
InputStreamReader(InputStream in, String charsetName)
字符输出流 Writer
java.io.Writer 抽象类是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地。它定义了字节 输出流的基本共性功能方法。
// 关闭此输出流并释放与此流相关联的任何系统资源
abstract void close()
// 刷新此输出流并强制任何缓冲的输出字符被写出
abstract void flush()
// 写出一个字符
void write(int c)
// 写出一个字符
// 将 b.length字符从指定的字符数组写出此输出流
void write(char[] cbuf)
// 从指定的字符数组写出 len字符,从偏移量 off开始输出到此输出流
abstract void write(char[] b, int off, int len)
// 写出一个字符串
void write(String str)
FileWriter类
java.io.FileWriter 类是写出字符到文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。
构造方法
// 创建一个新的 FileWriter,给定要读取的File对象
FileWriter(File file)
// 创建一个新的 FileWriter,给定要读取的文件的名称
FileWriter(String fileName)
OutputStreamWriter类
转换流 java.io.OutputStreamWriter ,是Writer的子类,是从字符流到字节流的桥梁。使用指定的字符集将字符 编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。
// 创建一个使用默认字符集的字符流
OutputStreamWriter(OutputStream in)
// 创建一个指定字符集的字符流
OutputStreamWriter(OutputStream in, String charsetName)
转换流

属性集
java.util.Properties 继承于 Hashtable ,来表示一个持久的属性集。它使用键值结构存储数据,每个键及其 对应值都是一个字符串。该类也被许多Java类使用,比如获取系统属性时, System.getProperties 方法就是返回 一个 Properties 对象。
Properties类
常用方法
// 创建一个空的属性列表
public Properties()
// 保存一对属性
Object setProperty(String key, String value)
// 用此属性列表中指定的键搜索属性值
String getProperty(String key)
// 所有键的名称的集合
Set<String> stringPropertyNames()
与流相关的方法
文本中的数据,必须是键值对形式,可以使用空格、等号、冒号等符号分隔。
// 从字节输入流中读取键值对
void load(InputStream inStream)
// read.txt内容是
// filename=a.txt
//length=209385038
//location=D:\a.txt
public static void main(String[] args) {
Properties properties = new Properties();
try(FileInputStream fileInputStream = new FileInputStream("read.txt")){
properties.load(fileInputStream);
}catch (IOException e){
e.printStackTrace();
}
Set<Map.Entry<Object, Object>> entries = properties.entrySet();
for (Map.Entry<Object, Object> entry : entries) {
System.out.println("key是:" + entry.getKey());
System.out.println("value是" + entry.getValue());
System.out.println("====================");
}
}
缓冲流
缓冲流,也叫高效流,是对4个基本的 FileXxx 流的增强,所以也是4个流,按照数据类型分类:
- 字节缓冲流: BufferedInputStream , BufferedOutputStream
- 字符缓冲流: BufferedReader , BufferedWriter
缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO 次数,从而提高读写的效率。
字节缓冲流
和字节输入/输出流用法一致
// 创建一个 新的缓冲输入流
BufferedInputStream(InputStream in)
BufferedOutputStream(OutputStream out) : 创建一个新的缓冲输出流。
字符缓冲流
// 创建一个 新的缓冲输入流
BufferedReader(Reader in)
// 创建一个新的缓冲输出流
BufferedWriter(Writer out)
特有方法
字符缓冲流的基本方法与普通字符流调用方式一致,不再阐述,我们来看它们具备的特有方法。
- BufferedReader: public String readLine() : 读一行文字。
- BufferedWriter: public void newLine() : 写一行,行分隔符,由系统属性定义符号。
序列化
序列化和反序列化
把类转换成二进制持久化到本地,反序列化就是把本地的二进制文件转换成类。
Java 提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该 对象的数据 、 对象的 类型 和 对象中存储的属性 等信息。
字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。 反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。
对象的数据 、 对象的类型 和 对象中 存储的数据 信息,都可以用来在内存中创建对象。看图理解序列化:

序列化的前提
该类必须实现
java.io.Serializable接口, Serializable 是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出 NotSerializableException 。该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用
transient关键字修饰。被static修饰的也不能被序列化,因为不属于对象
ObjectOutputStream类
java.io.ObjectOutputStream 类,将Java对象的原始数据类型写出到文件,实现对象的持久存储。
// 创建一个指定OutputStream的ObjectOutputStream
public ObjectOutputStream(OutputStream out)
// 将指定的对象写出
final void writeObject (Object obj)
@Test
void contextLoads() {
Employee employee = new Employee("zs", "xz", 9);
try(ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("employee.txt"))) {
objectOutputStream.writeObject(employee);
} catch (IOException e) {
e.printStackTrace();
}
}
ObjectInputStream类
ObjectInputStream反序列化流,将之前使用ObjectOutputStream序列化的原始数据恢复为对象。
@Test
void contextLoads() {
try(ObjectInputStream objectOutputStream = new ObjectInputStream(new FileInputStream("employee.txt"))) {
Employee employee = (Employee) objectOutputStream.readObject();
System.out.println(employee);
} catch (IOException e) {
e.printStackTrace();
}catch (ClassNotFoundException e){
e.printStackTrace();
}
}
序列化id
JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操 作也会失败,抛出一个InvalidClassException异常。
发生这个异常的原因如下:
该类的序列版本号与从流中读取的类描述符的版本号不匹配
该类包含未知数据类型
该类没有可访问的无参数构造方法
Serializable 接口给需要序列化的类,提供了一个序列版本号。 serialVersionUID该版本号的目的在于验证序列化的对象和对应类是否版本匹配。
package com.example.demo;
import java.io.Serializable;
public class Employee implements Serializable {
// 加入序列版本号
private static final long serialVersionUID = 1L;
public String name;
public String address;
// 添加新的属性 ,重新编译, 可以反序列化,该属性赋为默认值.
public int eid;
public void addressCheck() {
System.out.println("Address check : " + name + " ‐‐ " + address);
}
}
当我们一个实体类中没有显示的定义一个名为“serialVersionUID”、类型为long的变量时,Java序列化机制会根据编译时的class自动生成一个serialVersionUID作为序列化版本比较,这种情况下,只有同一次编译生成的class才会生成相同的serialVersionUID。
譬如,当我们编写一个类时,随着时间的推移,我们因为需求改动,需要在本地类中添加其他的字段,这个时候再反序列化时便会出现serialVersionUID不一致,导致反序列化失败。
那么如何解决呢?便是在本地类中添加一个“serialVersionUID”变量,值保持不变,便可以进行序列化和反序列化。
序列化的作用
网络传输
序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化,可以对流化后的对象进行读写操作,也可以将流化后的对象传输与网络之间;为了解决对象流读写操作时可能引发的问题(如果不进行序列化,可能会存在数据乱序的问题)
序列化除了能够实现对象的
持久化之外,还能够用于对象的深度克隆
html图片上传
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itheima</groupId>
<artifactId>io-upload</artifactId>
<version>1.0</version>
<packaging>war</packaging>
<name>io-upload Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.26</version>
</dependency>
<!--c3p0-->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<!--dbutils-->
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.6</version>
</dependency>
<!--servlet的api-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<!-- beanUtils工具类 封装javabean -->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!--commons io 依赖-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.1</version>
</dependency>
</dependencies>
<build>
<finalName>yourarchetype</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-archetype-plugin</artifactId>
<version>3.0.0</version>
</plugin>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.0.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.20.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.0</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>商品上传</title>
</head>
<body>
<!--
页面上 想要实现文件上传
传统方式 pass 不行 不能将文件 提交 仅仅提交的文件名字
要想让表单中的附件可以传输过去 表单满足三个条件
1:提交方式变为 post
2: 表单项 必须有name属性
3:加一个新的属性在 form表单上 enctype ="multipart/form-data
-->
<form action="/io-upload/upload2" method="post" enctype="multipart/form-data">
商品名称:<input type="text" name="pname"><br>
商品简介:<input type="text" name="pdesc"><br>
商品图片:<input type="file" name="pimage"><br>
<input type="submit" value="提交表单">
</form>
</body>
</html>
package com.itheima.upload;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.IOUtils;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.ResourceBundle;
import java.util.UUID;
@WebServlet("/upload2")
public class Upload2Servlet extends HttpServlet {
/**
优化
解决 文件出现的重名问题
*
*/
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
try {
// 创建磁盘工厂类
DiskFileItemFactory itemFactory = new DiskFileItemFactory();
// 创建 在servlet上解析 请求的 对象
ServletFileUpload fileUpload = new ServletFileUpload(itemFactory);
// fileUpload 就是用来解析 requet对象的 它可以将请求对象中的表单数据 一项项的解析出来
List<FileItem> list = fileUpload.parseRequest(request);
// 文件项 也可以叫表单项 表单项可以是单选框 复选框 密码框 文本框 下拉框....
for (FileItem item : list) {
// 遍历 得到每一个表单项对象
// 表单项分为 判断一下 表单项是普通项还是 上传项
if(item.isFormField()){//判断是不是表单的普通项 true 是 false 不是 上传项
//参数名 和参数值
String name = item.getFieldName();//参数名
// 参数值
// item.getString()//用默认的编码解析
String value = item.getString("utf-8");//参数值
System.out.println(name+" "+value);
}else{//处理的是上传项
String name = item.getFieldName();//参数名
System.out.println("上传项的参数名:"+name);
//获取文件名
String filename = item.getName();
System.out.println("文件名:"+filename);// p004.jpg
// 变成 永不重复的这种名字
// 截取后缀名
String houzhui = filename.split("\\.")[1];
filename = UUIDUtils.getUUID()+"."+houzhui;
// 刚完成一半
// 从流中 将上传的数据 读取出来 写到 服务器的一个位置上!!
// 需要输入流
InputStream in = item.getInputStream(); //这个流关联着上传的数据
// 需要 输出流关联目的地
//目的地应该在那 ? 项目中存储图片的位置
// 目的地的根路径
// 项目存储图片路径+日期目录
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String date = sdf.format(new Date());
/*
将 根目录 配置到 一个配置文件中 需要读取再进行拼接
*/
ResourceBundle bundle = ResourceBundle
.getBundle("rootDir");
File rootDir = new File(bundle.getString("rootdir"),date);
// 第一次出现的时候要创建
if(!rootDir.exists()){
rootDir.mkdir();
}
// 创建一个目的地文件
File uploadFile = new File(rootDir,filename);
//创建字节输出流关联目的地文件
FileOutputStream fos = new FileOutputStream(uploadFile);
// 复制
IOUtils.copy(in,fos);
// 释放资源
IOUtils.closeQuietly(in);
IOUtils.closeQuietly(fos);
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
}
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
}
}