文章目录
IO 流的分类
按照流的方向进行分类(以内存作为参照物):
- 输入流
往内存中去,叫做输入(Input)。或者叫做读(Read).
- 输出流
从内存中出来,叫做输出(output)。或者叫做写(write).
按照读取数据方式不同进行分类:
- 字节流
按照字节的方式读取数据,一次读取 1 个字节byte,等同于一次读取 8 个二进制
这种流是万能的,什么类型的文件都可以读取。包括:文本文件,图片,声音文件,视频
- 字符流
按照字符的方式读取数据的,一次读取一个字符(编码不同,一个字符可能包含多个字符,如 utf-8)
这种流是为了方便读取普通文本文件而存在的,这种流不能读取图片、声音、视频等文件。只能读取纯文本文件,连word文件都无法读取。
注意:在 Java 中只要"类名"以 Stream 结尾的都是字节流。以 “Reader/Writer” 结尾的都是字符流
Java IO 流 “四大家族”
四大家族的首领:
java.io.Inputstream 字节输入流
java.io.Outputstream 字节输出流
java.io.Reader 字符输入流
java.io.Writer 字符输出流
四大家族的首领都是抽象类
- 所有的流都实现了 java.io.Closeable 接口,都是可关闭的,都有 close() 方法。
流是一个管道,这个是内存和硬盘之间的通道;
用完流之后一定要关闭不然会耗费(占用)很多资源。 - 所有的输出流都实现了 java.io.Flushable 接口,都是可刷新的,都有 flush() 方法。
养成一个好习惯,输出流在最终输出之后,一定要记得 flush() 刷新一下。
这个刷新表示将通道/管道当中剩余未输出的数据强行输出完(清空管道!),即 刷新的作用就是清空管道。
如果没有 flush() 可能会导致丢失数据。
Java 常用的 IO 流
文件专属:
java.io.FileInputStream
java.io.FileOutputStream
java.io.FileReader
java.io.FileWriter
转换流:
(将字节流转换成字符流)
java.io.InputStreamReader
java.io.OutputStreamWriter
缓冲流专属
java.io.BufferedReader
java.io.BufferedWriter
java.io.BufferedInputStream
java.io.BufferedOutputStream
数据流专属
java.io.DataInputStream
java.io.DataOutputStream
标准输出流
java.io.PrintWriter
java.io.PrintStream
对象专属流
java.io.ObjectInputStream
java.io.ObjectOutputStream
FileInputStream 读文件
import org.junit.Test;
import java.io.FileInputStream;
import java.io.IOException;
public class test {
@Test
public void demo(){
byte[] bytes = new byte[4]; // 一个最多读入 4 个字节
int readCount = 0;
FileInputStream fis=null;
try {
fis = new FileInputStream("src/main/java/org/example/text");
while ((readCount = fis.read(bytes)) != -1) { // 读到文件末尾时,返回 -1。结束循环
System.out.print(new String(bytes, 0, readCount)); // 数组,起始位置,长度
// 读入多少 byte,输出多少。
// 不要用 new String(bytes),这个会转换 bytes 全部内容
//readCount = fis.read();读取一个字节,readCount为字节的asc码
// 两个常用方法available(),skip(long n);
// available()返回文件还剩下几个字节;skip()跳过几个字节不读取
//int byte[]=new int[fis.available]; new String(byte);即可将字节全部读入,但不可太大
}
} catch (IOException e) {
e.printStackTrace();
}
finally {
if(fis!=null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
FileOutputtStream 写文件
- 以字节流形式写出,可以写出任意文件类型
import org.junit.Test;
import java.io.FileOutputStream;
import java.io.IOException;
public class test2 {
@Test
public void demo(){
FileOutputStream fos = null;
// 以这种构造器创建的 fos,每次写入时会擦去原文件中的内容(如果文件不存在,会自动创建)
// fos = new FileOutputStream("src/main/java/org/example/text");
try {
//以这种构造器创建的 fos,不会擦去原文件内容,而在原文件末尾追加
fos = new FileOutputStream("src/main/java/org/example/text", true); // append = true;
// 1、写入一个字节
fos.write(97);
// 2、写入一个 byte[] 数组全部内容
byte[] bytes = {97, 98, 99, 100};
fos.write(bytes);
// 写入一个 byte[] 数组部分内容
fos.write(bytes, 0, 3);
// 3、添加 String
String s = "我是一个中国人,我自豪!";
byte[] bs = s.getBytes();
fos.write(bs);
} catch (IOException e) {
e.printStackTrace();
}
finally {
try {
fos.flush(); // 输出流,用完一定要刷新
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
文件复制
import org.junit.Test;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class filecopy {
@Test
public void copy1(){
FileInputStream fis = null;
FileOutputStream fos =null;
try {
fis = new FileInputStream("D:\\IDEA\\J1\\J1\\1.txt");
fos = new FileOutputStream("src/main/java/IOTest/text");
byte a[] = new byte[4];//最大为1mb 即1024*1024
int r=0;
while((r=fis.read(a))!=-1){
fos.write(a);
}
fos.flush();
} catch (IOException e) {
e.printStackTrace();
}
finally {
if(fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fos!=null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
FileReader 读文件
以字符流形式读取,只能读取普通文本文件(能用 txt 打开的),不能读取图片、音频、视频等
具体用法:把 FileInputStream 中的 bytes[] 换成 char[],其他的和 FileInputStream 一样
package IOTest;
import org.junit.Test;
import java.io.FileReader;
import java.io.IOException;
// FileReader使用
public class test3 {
@Test
public void read1(){
FileReader fr = null;
try {
fr = new FileReader("src/main/java/IOTest/text");
char []a = new char[4];
int r;
while((r=fr.read(a))!=-1){
System.out.print(new String(a,0,r));
}
} catch (IOException e) {
e.printStackTrace();
}
finally {
if(fr!=null){
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
FileWriter 写文件
以字符流形式写出,只能写普通文本文件(能用 txt 打开的),不能操作图片、音频、视频等
具体用法:把 FileOutputStream中的 bytes[] 换成 char[],其他的和 FileOutputStream一样
package IOTest;
import org.junit.Test;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
//文本文件复制
//FileWriter的使用
public class test4 {
@Test
public void Write1(){
FileWriter fw = null;
FileReader fr = null;
char []a = new char[4];
int r=-1;
try {
fr = new FileReader("src/main/java/IOTest/text");
fw = new FileWriter("src/main/java/IOTest/text1");
while((r=fr.read(a))!=-1){
fw.write(a,0,r);
}
fw.flush();
} catch (IOException e) {
e.printStackTrace();
}
finally {
if(fw!=null){
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fr!=null){
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
BufferedReader 读文件
- 带有缓冲区的字符输入流,不需要自定义 char[] 数组,或者说不需要自定义 byte[] 数组。
- 当一个流的构造方法中需要一个流的时候,这个被传进来的流叫做:节点流。
- 外部负责包装的这个流,叫做包装流 (处理流)。
- 特点:1.自带缓冲区,使用更方便; 可以一次读取一行 readLine()(readLine() 不会读取行尾的换行符)
2.关闭流时,只用关闭外层的包装流,不需要手动关闭内层节点流(源码里面已经实现了对节点流的关闭)
/**
* 当一个流的构造方法中需要一个流的时候,这个被传进来的流叫做:节点流。
* 外部负责包装的这个流,叫做︰包装流,还有一个名字叫做:处理流。
* 像当前这个程序来说:FiLeReader 就是一个节点流。
* BufferedReader 就是包装流/处理流。
*/
FileReader reader = new FileReader(".\\src\\io\\out2.txt");
BufferedReader bfr = new BufferedReader(reader);
String line = null;
// 注意:bfr.readLine() 可以读一行文本行,但是不会读入 换行符!
while ((line = bfr.readLine()) != null) { // 读取完文本内容后,返回 null
// System.out.print(line); // 这样将不会换行
System.out.println(line);
}
bfr.close(); // 只用关闭外层的包装流,内层的节点流会自动关闭(详见源代码)
BufferedWriter
- 自带缓冲,不需要指定 byte[] 或 char[]
- 特点:
自带缓冲,使用更方便 关闭流时,只用关闭外层的包装流,不需要手动关闭内层节点流(源码里面已经实现了)
package IOTest;
import org.junit.Test;
//BufferedWriter使用,文件复制
import java.io.*;
//缓冲流文本复制
public class test5 {
@Test
public void Write1() {
BufferedReader br = null;
BufferedWriter bw = null;
String r;
try {
br = new BufferedReader(new FileReader("src/main/java/IOTest/text"));
bw = new BufferedWriter(new FileWriter("src/main/java/IOTest/text1"));
while ((r = br.readLine())!=null) {
bw.write(r+"\n");
}
bw.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(bw != null){
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(br != null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
InputStreamReader 转换流
- InputStreamReader:将输入字节流转换为输入字符流
- OutputStreamWriter:将输出字节流转换为输出字符流
FileInputStream reader = new FileInputStream(".\\src\\io\\out2.txt"); // 文件字节输入流
// 对于 FileInputStream 而言,InputStreamReader 是包装流,FileInputStream是节点流
InputStreamReader fsr = new InputStreamReader(reader); // 转换流:将字节流转换为字符流
// 对于 BufferedReader 而言,InputStreamReader 是节点流,BufferedReader是包装流
BufferedReader bfr = new BufferedReader(fsr); // 带有缓冲区的字符输入流
// 合并上面三行写法(但是这样可读性差)
// bfr = new BufferedReader(new InputStreamReader(new FileInputStream(".\\src\\io\\out2.txt")));
String line = null;
while ((line = bfr.readLine()) != null) { // 读取完文本内容后,返回 null
System.out.println(line);
}
bfr.close(); // 只用关闭外层的包装流,内层的节点流会自动关闭(详见源代码)
DataOutputStream
- 可以将数据连同数据类型一并写入文件
- 注意:这个文件不是普通文本文档。(这个文件用 记事本打不开,会乱码)
final String path = ".\\src\\io\\dos.txt";
DataOutputStream dos = new DataOutputStream(new FileOutputStream(path));
long l = 400L;
float f = 2.0f;
char c = 'a';
boolean flag = false;
// 数据连同数据类型一同写入文件
dos.writeLong(l);
dos.writeFloat(f);
dos.writeChar(c);
dos.writeBoolean(flag);
DataInputStream
-
java.io.DataInputStream:数据字节输入流
-
DataOutputStream 写的文件,只能用 DataInputStreamTest 去读:
-
并且读的时候你必须提前知道写入的顺序;
-
只有当读的顺序需要和写的顺序一致,才可以正常读取数据。
final String path = ".\\src\\io\\dos.txt";
DataInputStream dis = new DataInputStream(new FileInputStream(path));
// 读取顺序必须和上面的写入顺序保持一致
long l = dis.readLong();
float f = dis.readFloat();
char c = dis.readChar();
boolean flag = dis.readBoolean();
使用 DataOutputStream 写文件,有点像加密数据;使用 DataInputStream 读文件,有点像解密数据。
标准输出流
package IOTest;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.PrintStream;
import java.util.Scanner;
//文本复制,测试标准流
public class test6 {
public static void main(String []arg){
//标准输出流,默认输出到控制台。
// 联合起来写
System.out.println("hello world!"); // 输出到控制台
//分开写
PrintStream ps,ps2;
ps = System.out;
ps.println("hello world!");
try {
//更改标准输出流的输出方向,输出到text1
ps2 = new PrintStream("src/main/java/IOTest/text1");
System.setOut(ps2);
ps2.println("hello world!");
//从控制台读取数据
Scanner r = new Scanner(System.in);
String a = r.nextLine();
//分别在控制台和text1文件输出
ps.println(a);
ps2.println(a);
//改成从文件text读取数据
FileReader fileReader=new FileReader("src/main/java/IOTest/text");
Scanner x = new Scanner(fileReader);
while (x.hasNextLine()) {
//text文件内容分别在控制台和text1文件输出
a = x.nextLine();
ps.println(a);
ps2.println(a);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
对象流
- 对象流可以将 Java 对象转换成二进制写入磁盘,这个过程通常叫做序列化,并且还可以从磁盘读出完整的 Java 对象,而这个过程叫做反序列化。
- 对象流主要包括:ObjectInputStream 和 ObjectOutputStream
- 如果实现序列化该类必须实现序列化接口java.io.Serializable,该接口没有任何方法,该接口只是一种标记接口,标记这个类是可以序列化的
- 如果为实现这个接口。系统将报错(该类不支持序列化)
-
Serializable 接口的作用:起到标识的作用
-
Serializable这个标志接口是给 Java 虚拟机参考的;
-
Java虚拟机看到这个接口之后,会为该类自动生成一个序列化版本号。
建议:手动提供序列化版本号,而不是自动提供。(这样类改变时,也不会影响反序列化)
否则,当类升级(改变)后,根据之前的序列化文件进行反序列化时,会失败,抛出 java.io.InvalidClassException -
序列化版本号的作用:类的标识,用来区分类,保证类的唯一性。
-
Java 如何区分类:(2种方法)
(1)类名不同,则根据类名区分;
(2)类名相同,则根据序列化版本号区分。
序列化、反序列化
-
序列化:内存(对象) -> 硬盘(对象)
拆分对象,序列化,使用 ObjectOutputStream -
反序列化:硬盘(对象) -> 内存(对象)
组装对象,反序列化,使用 ObjectInputStream -
需要系列化的类必须实现 Serializable 接口
-
类中使用 transient 修饰的字段,表示该字段不参与序列化
-
序列化版本号的作用:用来区分类,保证类的唯一性。
-
手动提供序列化版本号,而不是自动提供。
手动提供序列化版本号时,如果类(升价)改变,不会影响反序列化;
否则(使用 JVM 自动提供的序列化版本号时),当类升级(改变)后,如果仍然根据之前的序列化文件进行反序列化,会失败、抛出 java.io.InvalidClassException -
反序列化 & 序列化多个对象时,可以使用集合(将多个对象放进 list 中,然后序列化 list)
手动提供序列化版本号时,可以使用 IDEA/eclipse 生成类的序列化版本号。
package IOTest;
import java.io.Serializable;
//必须实现Serializable接口,不然不可序列化
//该接口无代码,只是一个标志,用来给虚拟机看,虚拟机看到就会给一个序列化版本号
//Java虚拟机区分类,先比较类名。在比较序列化(1)类名不同,则根据类名区分;(2)类名相同,则根据序列化版本号区分。
public class dog implements Serializable {
//自己声明序列化版本后,因为后续如果修改代码编译后版本号会生成新的版本号,会被虚拟机认为是两个不同的类
//如果自己手动生成,那么序列化就会一样,虚拟机就不会认为是不同的类
private static final long serialVersionUID = 8683452581122892189L;
int id;
String name;
public dog(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "dog{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
package IOTest;
import java.io.*;
public class XLTest {
public static void main(String []args){
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("src/text"));
dog d = new dog(1,"张三");
oos.writeObject(d);
oos.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(oos!=null) {
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package IOTest;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class XLTest02 {
public static void main(String []args){
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("src/text"));
Object a = ois.readObject();
System.out.println(a);
} catch (Exception e) {
e.printStackTrace();
}finally {
if(ois!=null){
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
//如果未实现 Serializable将会报此错误
java.io.NotSerializableException: IOTest.dog
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at IOTest.XLTest.main(XLTest.java:11)
序列化多个对象
package IOTest;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
public class XLTest03 {
public static void main(String[]args){
ObjectOutputStream oos=null;
try {
oos = new ObjectOutputStream(new FileOutputStream("src/text1"));
} catch (IOException e) {
e.printStackTrace();
}
dog d1 = new dog(1,"张三");
dog d2 = new dog(2,"李四");
dog d3 = new dog(3,"王五");
dog d4 = new dog(4,"赵六");
dog d5 = new dog(5,"秦七");
//序列化多个对象,需要使用列表进行,否则序列化第二个时将会报错
List<dog> l = new ArrayList<>();
l.add(d1);
l.add(d2);
l.add(d3);
l.add(d4);
l.add(d5);
try {
oos.writeObject(l);
oos.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(oos!=null){
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package IOTest;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.List;
public class XLTest04 {
public static void main(String[]args){
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("src/text1"));
List<dog> l = (List<dog>) ois.readObject();
for(dog a:l){
System.out.println(a);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if(ois!=null){
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
//dog{id=1, name='张三'}
//dog{id=2, name='李四'}
//dog{id=3, name='王五'}
//dog{id=4, name='赵六'}
//dog{id=5, name='秦七'}
transient 关键字
在name前面加上 transient 其他不变
package IOTest;
import java.io.Serializable;
public class dog implements Serializable {
private static final long serialVersionUID = 8683452581122892189L;
int id;
transient String name;//表示游离的,不参加序列化
public dog(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "dog{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
package IOTest;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
public class XLTest03 {
public static void main(String[]args){
ObjectOutputStream oos=null;
try {
oos = new ObjectOutputStream(new FileOutputStream("src/text1"));
} catch (IOException e) {
e.printStackTrace();
}
dog d1 = new dog(1,"张三");
dog d2 = new dog(2,"李四");
dog d3 = new dog(3,"王五");
dog d4 = new dog(4,"赵六");
dog d5 = new dog(5,"秦七");
//序列化多个对象,需要使用列表进行,否则序列化第二个时将会报错
List<dog> l = new ArrayList<>();
l.add(d1);
l.add(d2);
l.add(d3);
l.add(d4);
l.add(d5);
try {
oos.writeObject(l);
oos.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(oos!=null){
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package IOTest;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
public class XLTest03 {
public static void main(String[]args){
ObjectOutputStream oos=null;
try {
oos = new ObjectOutputStream(new FileOutputStream("src/text1"));
} catch (IOException e) {
e.printStackTrace();
}
dog d1 = new dog(1,"张三");
dog d2 = new dog(2,"李四");
dog d3 = new dog(3,"王五");
dog d4 = new dog(4,"赵六");
dog d5 = new dog(5,"秦七");
//序列化多个对象,需要使用列表进行,否则序列化第二个时将会报错
List<dog> l = new ArrayList<>();
l.add(d1);
l.add(d2);
l.add(d3);
l.add(d4);
l.add(d5);
try {
oos.writeObject(l);
oos.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(oos!=null){
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/*dog{id=1, name='null'}
dog{id=2, name='null'}
dog{id=3, name='null'}
dog{id=4, name='null'}
dog{id=5, name='null'}*/
File类
- File 和 IO 流的“四大家族”没有关系,所以通过 File 不能完成文件读/写。
- 它继承自 Object 类
public class File extends Object - File 是文件或目录路径名的抽象表示。
- File 类的常用方法:
目录复制
package IOTest;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.FileOutputStream;
public class dirCopy {
static void copy(File fi,File fi2){
if(fi.isFile()){//若是文件则停止并且复制文件
if(!fi2.exists()) fi2.mkdirs();//创建目录
FileInputStream fis = null;
FileOutputStream fos =null;
try {
fis = new FileInputStream(fi.getPath());//getPath获取文件绝对路径
fos = new FileOutputStream(fi2.getPath()+"\\"+fi.getName());//getName获取文件名
byte a[] = new byte[1024];//最大为1mb 即1024*1024
int r=0;
while((r=fis.read(a))!=-1){
fos.write(a,0,r);
}
fos.flush();
} catch (IOException e) {
e.printStackTrace();
}
finally {
if(fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fos!=null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return;
}
//若不是文件,则继续深入
File fi4 = new File(fi2.getPath()+"\\"+fi.getName());
File []fi3 = fi.listFiles();
for(File f:fi3){
copy(f,fi4);
}
}
public static void main(String []args){
File fi = new File("D:\\HTML\\text");
File fi2 = new File("D:");
copy(fi,fi2);
}
}
IO + Properties 读取属性配置文件
使用 IO + Properties 读取属性配置文件。
通常,与后面的 JDBC 或者 反射结合使用。
属性配置文件中:
‘=’ 左边:key;
‘=’ 右边:value
// Properties 相当于 Map,但是 key 和 value 都是 String 类型Properties properties = new Properties();
// 调用Properties对象的Load方法将文件中的数据加载到Map集合中。properties.load(reader);
// 文件中的数据顺着管道加载到 Map 集合中,其中等号=左边为key,右边为value
// 取出属性配置文件中的内容String name = properties.getProperty("user");System.out.println(name);
// String passwd = properties.getProperty("password");System.out.println(passwd);
package IOTest;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;
public class propertiesTest {
public static void main(String []args){
FileReader fr = null;
try {
fr = new FileReader("src/po.properties");
Properties pro = new Properties();
pro.load(fr);
String n = pro.getProperty("name");
System.out.println(n);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
#属性文件以井号注释
#等号两边不要有空格
#最好不要使用key:value形式,最好写成key=value形式
name=zhangsan
sex=男
#如果key一致,将会覆盖之前值
sex=女
#属性配置文件最好以.properties为后缀,但不是必要的