黑马程序员--IO(二)--File类、Properties类、打印流、序列流等
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
一、File类
File类用来将文件或文件夹封装成对象,方便对文件与文件夹的属性信息进行操作。File对象可以作为参数传递给流的构造函数。一旦创建,File对象表示的抽象路径名将永不改变。
//封装对象的方式:可以将已存在或不存在的文件或目录封装成file对象。
import java.io.File;
class FileDemo
{
public static void main(String[] args)
{
constructorDemo();
}
public static void constructorDemo()
{
//方式一:
File f1 = new File("d:\\demo\\a.txt");
//方式二:
File f2 = new File("d:\\demo","a.txt");
//方式三:
File f = new File("d:\\demo");
File f3 = new File(f,"a.txt");
//跨系统方式.File.separator是与系统有关的默认名称分隔符。UNIX系统上值为'/';
File f4 = new File("d:"+File.separator+"demo"+File.separator+"a.txt");
}
}
PS:流只能操作数据。
1、常见方法:
1.1 创建
boolean creatNewFile():在指定位置创建文件,如果该文件已经存在,则不创建,返回false。
和输出流不一样,输出流对象一建立创建文件。而且文件已经存在,会覆盖。
boolean mkdir():创建文件夹。只能创建一级文件夹。
boolean mkdirs():创建多级文件夹
1.2 删除
boolean delete():删除文件或目录。文件存在,返回true。文件不存在或正在被执行,返回false。
void deleteOnExit():在程序退出时删除指定文件。避免程序异常时,删除动作执行不到的问题。
1.3 判断
boolean canExecute():测试程序是否可以执行此路径名表示的文件。
boolean exists():判断文件是否存在。
boolean isDirectory():判断路径名是否是目录(文件夹)。
boolean isFile():判断路径名是否是文件。
boolean isHidden():判断路径名是否是一个隐藏文件。
boolean isAbsolute():判断路径名是否是绝对路径名。
PS:
判读文件对象是否是文件或目录时,必须要判断该文件对象封装的内容是否存在。
1.4 获取信息。
String getName():获取文件名。
String getPath():获取文件的相对路径。即创建的对象传入的参数是什么就获取到什么。
String getParent():返回文件绝对路径中的父目录。若获取相对路径,有明确父目录时,返回父目录,否则返回null。
String getAbsolutePath():获取文件的绝对路径。
long lastModified():返回文件最后一次被修改时间。判断文件是否被修改过。
long length():返回文件长度。
//File类常见方法:
import java.io.*;
class FileDemo1
{
public static void main(String[] args) throws IOException
{
method_create();
method_delete();
method_judge();
method_get();
method_list();
}
public static void method_create() throws IOException
{
File f = new File("file.txt");
//指定位置创建文件。因为它调用、启用资源有产生异常的可能。
sop("create:"+f.createNewFile());
File dir1 = new File("abc");
//创建一级文件夹
sop("mkdirs:"+dir1.mkdir());
File dir2 = new File("abc\\a\\aad\\e");
//创建多级文件夹
sop("mkdirs:"+dir2.mkdirs());
}
public static void method_delete() throws IOException
{
File f = new File("a.txt");
//删除文件或目录。文件存在,返回true。文件不存在或正在被执行,返回false。
sop("delete:"+f.delete());
//在程序退出时删除指定文件。
File f2 = new File("file.txt");
f2.deleteOnExit();
}
public static void method_judge() throws IOException
{
File f = new File("d:\\Demo\\IO3\\FileDemo\\file.txt");
sop("canExecute:"+f.canExecute());
//如果流存在才可以读写,所以exists方法可以直接判断流存在问题。
sop("exists:"+f.exists());
f.mkdir();
//当判断文件对象是否是文件或目录时,必须要先判断该文件对象封装的内存是否存在。
sop("dir:"+f.isDirectory());
sop("file:"+f.isFile());
sop("hidden:"+f.isHidden());
sop(f.isAbsolute());
}
public static void method_get() throws IOException
{
File f = new File("d:\\a.txt");
sop("path:"+f.getPath());
sop("abspath:"+f.getAbsolutePath());
sop("parent:"+f.getParent());
sop("length:"+f.length());
}
public static void method_list() throws IOException
{
File f1 = new File("d:\\helloworld.java");
File f2 = new File("d:\\a.java");
//列出可以文件系统根目录
sop("root"+f2.listRoots());
//列出当前目录下所有文件
sop("list"+f2.list());
sop("rename:"+f1.renameTo(f2));
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
1.5 列出文件及文件过滤static File[] listRoots():列出可用的文件系统根目录,即系统盘符。
String[] list():列出当前目录下所有文件,包括隐藏。调用list方法的file对象必须是封装了一个已存在的目录。
String[] list(FilenameFilter filter):返回一个字符串数组,获取目录中满足指定过滤器的文件和目录。
FilenameFilter:文件名过滤器,是一个接口,其中包含一个方法:accept(File dir,String name),返回的是boolean。
File[] listFiles():返回一个抽象路径名数组,获取当前文件夹下的所有文件和文件夹。
boolean renameTo(File dest):重命名。
list和listFiles区别:
list返回当前目录下的文件和名称;listFiles返回当前目录下的文件和文件对象。
即list只能获取名,listFiles可以通过方法获取更多属性。
//list方法
import java.io.*;
class FileDemo2
{
public static void main(String[] args)
{
listRootsDemo();
listDemo();
listDemo_2();
listfileDemo();
}
public static void listfileDemo()
{
File dir = new File("D:\\Demo\\IO3");
File[] files = dir.listFiles();
for(File f:files)
{
System.out.println(f.getName()+"::"+f.length());
}
}
public static void listDemo_2()
{
File dir = new File("D:\\Demo\\IO3");
String[] arr = dir.list(new FilenameFilter()
{
//list依据accept的返回值来判断数组内容。
public boolean accept(File dir,String name)
{
return name.endsWith(".txt");
}
});
System.out.println("len:"+arr.length);
for(String name:arr)
{
System.out.println(name);
}
}
public static void listDemo()
{
File f = new File("D:\\");
//调用list方法的file对象必须是封装了一个目录。该目录必须存在。
String[] names = f.list();
for(String name:names)
{
System.out.println(name);
}
}
//打印系统有效盘符。
public static void listRootsDemo()
{
File[] files = File.listRoots();
for(File f:files)
{
System.out.println(f);
}
}
}
2、递归
2.1 定义
递归:函数自身调用自身的表现形式或编程手法。因为目录中还有目录,只有使用同一个
列出目录功能的函数完成即可。在列出过程中出现的还是目录的话,还可以再次调用本功能。
2.2 注意事项:
a 限定次数。
b 注意递归次数,避免内存溢出。因为每次调用自己都会执行下一次调用自己的方法,所以会不断
在栈内存中开辟新空间,次数过多,会导致内存溢出。
/*
列出指定目录下文件或文件夹,包含子目录的内容。
因为目录中还有目录,只有使用同一个列出目录功能的函数完成即可。
在列出过程中出现的还是目录的话,还可以再次调用本功能。
也就是函数自身调用自身。
这种表现形式或编程手法,称为递归。
递归注意事项:
1,限定次数。
2,注意递归次数,避免内存溢出。
*/
import java.io.*;
class FileDemo3
{
public static void main(String[] args)
{
File dir = new File("D:\\Demo");
showDir(dir);
//int n = getSum(25000);
//System.out.println(n);
}
//递归会不断开辟栈对象次数过多,有内存溢出风险。
public static int getSum(int n)
{
if(n==1)
return 1;
return n+getSum(n-1);
}
//递归:自我调用。因为没有次数限制,会成为无限循环,失去价值。
public static void method()
{
method();
}
public static void showDir(File dir)
{
//打印目录
System.out.println(dir);
File[] files = dir.listFiles();
//递归
for(int x=0; x<files.length; x++)
{
//如果文件中有文件夹,再次调用自己。
if(files[x].isDirectory())
showDir(files[x]);
System.out.println(files[x]);
}
}
}
二、Properties类
Properties是hashtable的子类。所以它具备Map集合的特点。而且它存储的键值对都是字符串。无泛型定义。它是集合和IO技术集合的集合容器。
1、特点:
a 可以用于键值对形式的配置文件。
b 在加载数据时,需要数据有固定格式:键=值。
c 集合中的数据可以保存到流中或从流中获取。
2、特有方法:
2.1 设置
Object setProperty(String key,String value):设置键和值,调用Hashtable的方法put。
2.2 获取
String getProperty(String key):通过键获取值。
Set<String> stringPropertyNames():返回此属性列表中的键集。将map变成set,带泛型。
3.3 加载与存储
void load(InputStream ism):将字节流中的数据加载进集合。
void load(Reader reader):将字符流中的数据加载进集合。
void list(PrintStream out):将属性列表输出到指定的输出流中。
void store(OutputStream out,String comments):对应load(InputStream)将属性列表(键值对)写入输出流。comments注释信息。
void store(Writer writer,String comments):对应load(Reader)将属性列表(键值对)写入输出流。
/*
Properties类实例:
本文件解决问题:
1,设置和获取元素。
2,将流中的数据存储到集合中。
思路:
1,用一个流和info.txt文件关联。
2,读取一行数据,将该行数据用“=”进行切割。
3,等号左边作为键,右边作为值。存入到Properties集合中即可。
*/
import java.io.*;
import java.util.*;
class PropertiesDemo
{
public static void main(String[] args) throws IOException
{
setAndGet();
method_1();
loadDemo();
}
public static void loadDemo() throws IOException
{
Properties prop = new Properties();
FileInputStream fis = new FileInputStream("info.txt");
//将流中的数据加载进集合
prop.load(fis);
//修改数据信息,但是无法改源文件。
prop.setProperty("wangwu","39");
FileOutputStream fos = new FileOutputStream("info.txt");
//将属性列表(键值对)写入输出流。
prop.store(fos,"haha");
//System.out.println(prop);
//另一种输出方式。
prop.list(System.out);
fos.close();
fis.close();
}
//模拟load方法。将流中的数据存储到集合中。
public static void method_1() throws IOException
{
BufferedReader bufr = new BufferedReader(new FileReader("info.txt"));
String line = null;
Properties prop = new Properties();
while((line=bufr.readLine())!=null)
{
String[] arr = line.split("=");
//将键值对存入Properties中
prop.setProperty(arr[0],arr[1]);
}
bufr.close();
System.out.println(prop);
}
//设置和获取元素。
public static void setAndGet()
{
Properties prop = new Properties();
//设置
prop.setProperty("zhangsan","30");
prop.setProperty("lisi","34");
//System.out.println(prop);
//获取
String value = prop.getProperty("lisi");
//System.out.println(value);
//修改信息、
prop.setProperty("lisi",98+"");
//取出所有元素
Set<String> names = prop.stringPropertyNames();
for(String s : names)
{
System.out.println(s+":"+prop.getProperty(s));
}
}
}
应用:创建文件访问计数器。
/*
需求:记录应用程序运行次数。如果使用次数已到,给出注册提示。
思路:
1、在程序中定义计数器,随着程序的运行存在并自增。但是普通计数器会随着程序退出而清0;
2、建立一个配置文件,记录该软件的使用次数。
3、为便于阅读、操作数据,该配置文件使用键值对的形式。
4、键值对数据是map集合;数据是以文件形式存储,使用io技术;那么map+io -->properties。
配置文件可以实现应用程序数据的共享。
*/
import java.io.*;
import java.util.*;
class RunCount
{
public static void main(String[] args) throws IOException
{
Properties prop = new Properties();
//将文件独立封装可以使用判读方法,防止文件不存在时抛出异常。
File file = new File("count.ini");
if(!file.exists())
file.createNewFile();
FileInputStream fis = new FileInputStream(file);
prop.load(fis);
int count = 0;
String value = prop.getProperty("time");
if(value!=null)
{
//将字符串转成基本数据类型。静态装换方法
count = Integer.parseInt(value);
if(count>=5)
{
System.out.println("使用次数已到,请充值!");
return;
}
}
count++;
prop.setProperty("time",count+"");
FileOutputStream fos = new FileOutputStream(file);
prop.store(fos,"");
fos.close();
fis.close();
}
}
三、打印流
打印流:PrintStream、PrintWriter。可直接操作输入流和文件,将传入数据原样打印。1、PrintStream:字节打印流。为其他输出流添加打印各种数据值的功能。特点:不会抛出IOException。
PrintStream打印的所有字符都使用平台的默认字符编码转换为字节。当需要写入字符时应使用PrintWriter类。
构造函数可以接收的参数类型:
1.1 File对象。File
1.2 字符串路径。String
1.3 字节输出流。OutputStream
2、PrintWriter:字符打印流。
构造函数可以接收的参数类型:
2.1 File对象。File
2.2 字符串路径。String
2.3 字节输出流。OutputStream
2.4 字符输出流。Writer。
//打印流:提供打印方法,可以将各种数据类型的数据原样打印。
import java.io.*;
class PrintStreamDemo
{
public static void main(String[] args) throws IOException
{
PrintStream ps = new PrintStream("print.txt");
//write(int b):只写最低8位。
ps.write(97);//a
//print方法原数打印
ps.print(97);//97
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));
//PrintWriter构造函数的第二个参数设置为true,表示自动刷新。
PrintWriter pw = new PrintWriter(System.out,true);
String line = null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
pw.println(line.toUpperCase());//ln是刷新标记
//pw.flush();
}
ps.close();
pw.close();
bufr.close();
}
}
四、序列流
SequenceInputStream:序列流(合并流)。多源对应一个目的时,省去部分关联和续写步骤,简化代码。SequenceInputStream(InputStream s1, InputStreamS2 ): 依顺序读取s1,s2
SequenceInputStream(Enumeration<? extends InputStream> e):多个参数合并
/*
SequenceInputStream:序列流(合并流)。
需求:将1.txt、2.txt、3.txt合并为一个文件4.txt。
*/
import java.io.*;
import java.util.*;
class SequenceDemo
{
public static void main(String[] args) throws IOException
{
Vector<FileInputStream> v = new Vector<FileInputStream>();
v.add(new FileInputStream("D:\\1.txt"));
v.add(new FileInputStream("D:\\2.txt"));
v.add(new FileInputStream("D:\\3.txt"));
Enumeration<FileInputStream> en = v.elements();
//合并流,统一源。
SequenceInputStream sis = new SequenceInputStream(en);
//目的。
FileOutputStream fos = new FileOutputStream("D:\\4.txt");
byte[] buf = new byte[1024];
int len = 0;
while((len=sis.read(buf))!=-1)
{
fos.write(buf,0,len);
}
fos.close();
sis.close();
}
}
五、操作对象
操作对象:ObjectInputStream、ObjectOutputStream。ObjectOutputStream:将Java对象的基本数据类型和图形写入OutputStream。
被操作的对象需要实现Serializable接口才能使用序列化功能。
Serializable接口是标记接口,它用于给被序列化的类加入ID号,以判断类和对象是否是同一个版本。
静态不能被序列化,因为静态在方法区中,不在堆中。
serialVersionUID:给类定义固定标识,方便序列化。新类可以操作曾经被序列化的对象。
transient:是让非静态成员无法序列化的关键字。保证它的值在堆内存中存在,而不显示在文本文件中。
/*
操作对象:ObjectInputStream、ObjectOutputStream
NotSerializableException:某个要序列化的对象不能实现 java.io.Serializable 接口异常。
*/
import java.io.*;
class Person implements Serializable
{
//serialVersionUID:给类定义固定标识,使新类还可以用之前的序列化对象。
public static final long serialVersionUID = 42L;
private String name;
transient int age;//不想非静态成员序列化加关键字:transient
//静态不能被序列化,因为静态在方法区中,不在堆中。
static String country = "cn";
Person(String name,int age,String country)
{
this.name=name;
this.age=age;
this.country = country;
}
public String toString()
{
return name+":"+age+":"+country;
}
}
class ObjectStreamDemo
{
public static void main(String[] args) throws Exception
{
writeObj();
readObj();
}
public static void readObj() throws Exception
{
ObjectInputStream ois =
new ObjectInputStream(new FileInputStream("obj.txt"));
Person p = (Person)ois.readObject();
System.out.println(p);
ois.close();
}
public static void writeObj() throws IOException
{
//目的:将对象保存成文件
ObjectOutputStream oos =
new ObjectOutputStream(new FileOutputStream("obj.txt"));
oos.writeObject(new Person("lisi0",339,"USA"));
oos.close();
}
}
PS:1 对象的实体化存储:将对象存储在硬盘上。也可称为对象的序列化、对象的可串联性。
2 标记接口:没有方法的接口。只是为了标识其信息。
六、RandomAccessFile
RandomAccessFile:随机访问文件,自身具备读写方法。它直接继承自Object,不算是IO体系中子类。1、特点:
1.1 具备读和写功能;
1.2 该对象内部封装了一个byte数组,并通过指针可以操作数组中的元素;
1.3 可以通过getFilePointer方法获取指针位置,通过seek方法改变指针位置。
1.4 该对象只能操作文件,而且操作文件还有模式:只读:r,读写:rw。
PS:
1 读写原理:内部封装了字节输入流和输出流。
2 操作文件模式:
如果模式为只读 r:不创建文件。只读取一个已存在的文件,如果该文件不存在,则会出现异常。
如果模式为读写rw:操作的文件不存在,会自动创建。如果存在不会覆盖。
2、常用方法:
skipBytes()跳过指定的字节数;
seek()调整对象中指针;
//RandomAccessFile:随机访问文件。
import java.io.*;
class RandomAccessFileDemo
{
public static void main(String[] args) throws IOException
{
writeFile();
readFile();
writeFile_2();
}
public static void readFile() throws IOException
{
RandomAccessFile raf = new RandomAccessFile("ran.txt","r");//只读
//通过seek访问任意位置。
//raf.seek(8);
//跳过指定的字节数。不能回跳
raf.skipBytes(8);
byte[] buf = new byte[4];
raf.read(buf);
String name = new String(buf);
int age = raf.readInt();
System.out.println("name="+name);
System.out.println("age="+age);
raf.close();
}
public static void writeFile() throws IOException
{
RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
raf.write("Lisi".getBytes());
//使用write方法写入最后一个字节。只取8位,超过256时存在数据丢失。
raf.write(257);
//使用writeInt方法写入四个字节(int类型)
raf.writeInt(97);
raf.write("wagnwu".getBytes());
raf.writeInt(99);
raf.close();
}
public static void writeFile_2() throws IOException
{
RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
//往指定位置写入数据
raf.seek(2*8);
raf.write("周期".getBytes());
raf.writeInt(103);
raf.close();
}
}
七、管道流
管道流:PipedInputStream、PipedOutputStream。1、特点:
1.1 将输入流和输出流连接,省去了用数组存储数据的中转环节。
1.2 使用多线程解决管道执行顺序问题,防止死锁情况。
2、连接方式:
2.1 connect(PipedOutputStream src):将PipedInputStream连接到PipedOutputStream。
2.2 PipedInputStream():创建尚未连接的PipedInputStream。
2.3 PipedOutputStream():创建尚未连接的PipedOutputStream。
//管道流:PipedInputStream、PipedOutputStream
import java.io.*;
class Read implements Runnable
{
private PipedInputStream in;
Read(PipedInputStream in)
{
this.in = in;
}
public void run()
{
try
{
byte[] buf = new byte[1024];
System.out.println("读取前。。。未阻塞");
int len = in.read(buf);
System.out.println("读取后。。。阻塞结束");
String s = new String(buf,0,len);
System.out.println(s);
in.close();
}
catch (IOException e)
{
throw new RuntimeException("管道读取流失败");
}
}
}
class Write implements Runnable
{
private PipedOutputStream out;
Write(PipedOutputStream out)
{
this.out = out;
}
public void run()
{
try
{
System.out.println("6秒后写入数据");
//释放执行权
Thread.sleep(6000);
out.write("piped coming".getBytes());
out.close();
}
catch (Exception e)
{
throw new RuntimeException("管道输出流失败");
}
}
}
class PipedStreamDemo
{
public static void main(String[] args) throws IOException
{
PipedInputStream in = new PipedInputStream();
PipedOutputStream out = new PipedOutputStream();
//连接输入流和输出流
in.connect(out);
Read r = new Read(in);
Write w = new Write(out);
new Thread(r).start();
new Thread(w).start();
}
}
PS: 集合中涉及到IO流的是Properies;IO中涉及到多线程的是管道流。
八、其他
1、操作基本数据类型:DataInputStream、DataOutputStream:用于操作基本数据类型的流对象。相关方法:
writeUTF(String str):以与机器无关方式使用UTF-8修改版编码将一个字符串写入基础输出流。
意味着:只能通过对应的方法读取数据,用转换流读不出来。
readUTF(String str):专门用于读取UTF-8修改版编码。
PS:
UTF-8修改版占8个字节;UTF-8占6个字节;gbk占4个字节。假如用其他方法读取UTF-8修改版编码,
会出现EOFException异常,因为它需要读取8个字节才能结束,用其他方法读取会提前结束。
//操作基本数据类型:DataInputStream、DataOutputStream
import java.io.*;
class DataStreamDemo
{
public static void main(String[] args) throws IOException
{
writeData();
readData();
writeUTFDemo();
readUTFDemo();
}
public static void readUTFDemo() throws IOException
{
DataInputStream dis = new DataInputStream(new FileInputStream("utf.txt"));
String s = dis.readUTF();
System.out.println(s);
dis.close();
}
public static void writeUTFDemo() throws IOException
{
DataOutputStream dos = new DataOutputStream(new FileOutputStream("utf.txt"));
dos.writeUTF("你好");
dos.close();
}
public static void readData() throws IOException
{
DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
//读取顺序要与写入顺序相同
int num = dis.readInt();
boolean b = dis.readBoolean();
double d = dis.readDouble();
System.out.println("num="+num);
System.out.println("b="+b);
System.out.println("d="+d);
dis.close();
}
public static void writeData() throws IOException
{
DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
dos.writeInt(255);
dos.writeBoolean(true);
dos.writeDouble(9885.1516);
dos.close();
}
}
2、操作字节数组ByteArrayInputStream:在构造时,需要接收数据源,而且数据源是一个字节数组。
ByteArrayOutputStream:在构造时,不用定义数据目的。因为该对象中已经在内部封装了可变长度的字节数组,
也就是数据目的地。其缓冲区随着数据的不断写入而自动增长。
特点:这两个流对象都操作的是数组,没使用系统资源,不用进行close关闭。
优点:增加了对象的封装性;代码的复用性;用流的读写思想来操作数组,简化了书写。
流操作规律:
源设备:
键盘 System.in 硬盘 FileStream 内存 ArrayStream
目的设备:
控制台 System.out 硬盘 FileStream 内存 ArrayStream
//操作字节数组:ByteArrayInputStream、ByteArrayOutputStream
import java.io.*;
class ByteArrayStream
{
public static void main(String[] args)
{
//数据源
ByteArrayInputStream bis = new ByteArrayInputStream("ABCDEFG".getBytes());
//数据目的
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int by = 0;
while((by=bis.read())!=-1)
{
bos.write(by);
}
//获取输出数组大小
System.out.println(bos.size());
System.out.println(bos.toString());
//将此byte数组输出流的全部内容写入到指定的输出流参数中,会报出异常。
//bos.writeTo(new FileOutputStream("a.txt"));
}
}
3、操作字符数组:CharArrayReader、CharArrayWrite4、操作字符串:StringReader、StringWriter
九、字符编码
1、编码表由来:计算机只能识别二进制数据,早期由来是电信号。为了方便用于计算机,让它可以识别各个国家的文字。
就将各个国家的文字用数字来表示,并一一对应,形成一张表--->编码表。
2、分类
ASCII:美国标准信息交换码:用一个字节的7位表示;
ISO8859-1:拉丁码表(欧洲码表):用一个字节的8位表示;
GB2312:中国的中文编码表;两个字节表示一个字符,两个字节的高位都是1,兼容和避免了跟ASCII码重复;
GBK:升级版的中文编码表,融合了更多的中文文字符号;有两万左右字符;
Unicode:国际标准码,融合入了多种文字。所有文字都用两个字节来表示,java语言使用的就是Unicode;
UTF-8:按字符大小分配存储空间,最多三个字节表示一个字符,并在首字母加入标识符,便于区分。
3、字符编码
3.1字符流的出现为了方便操作字符,更重要的是加入了编码转换。
编码转换通过子类转换流来完成:InputStreamReader、OutputStreamWriter。
在两个对象进行构造的时候可以加入字符集。
//字符编码:编码转换:InputStreamReader、OutputStreamWriter
import java.io.*;
class EncodeStream
{
public static void main(String[] args) throws IOException
{
writeText();
readText();
}
public static void readText() throws IOException
{
InputStreamReader isr = new InputStreamReader(new FileInputStream("utf.txt"),"UTF-8");
char[] buf = new char[10];
int len =isr.read(buf);
String str = new String(buf,0,len);
System.out.println(str);
isr.close();
}
public static void writeText() throws IOException
{
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf.txt"),"UTF-8");
osw.write("你好");
osw.close();
}
}
3.2 编码:字符串变字节数组:String-->byte[];
byte[] getBytes(String charsetName):使用指定的字符集将此String解码为字节序列,并存储到数组中。
3.3 解码:
字节数组变字符串:byte[]-->String;
String(byte[],charsetName):将数组变成字符串。
/*
编码:字符串变字节数组;解码:字节数组变字符串。
注意utf-8和GBK都识别中文的情况,因为都是别中文,它们之间解码会造成源码改变的情形。
*/
import java.util.*;
class EncodeDemo
{
public static void main(String[] args) throws Exception
{
String s = "你好";
//编码
byte[] b1 = s.getBytes("gbk");
System.out.println(Arrays.toString(b1));
//错误解码 因为ISO8859-1是拉丁码表,不包含中文码表,必然造成乱码。
String s1 = new String(b1,"ISO8859-1");
System.out.println("s1="+s1);
//再编码 虽然s1是乱码,但也是一个字符对象,它的字节数组未变。
//所以我们依旧可以通过字节数组获取正确的解码值。
byte[] b2 = s1.getBytes("ISO8859-1");
System.out.println(Arrays.toString(b2));
//正确解码
String s2 = new String(b2,"gbk");
System.out.println("s2="+s2);
}
}
运行结果:
十、应用
/*
有五个学生,每个学生有3门课的成绩。
从键盘输入以上数据(包括姓名,三门课成绩),
输入的格式:如:zhangsan,30,40,60计算出总成绩。
并把学生的信息和总分数从高到低存放在磁盘文件"stud.txt"中。
步骤:
1,描述学生对象。
2,定义一个可操作学生对象的工具类。
思路:
1,通过获取键盘录入一行数据,并将该行中的信息取出封装成学生对象。
2,学生有很多,那么就需要存储,使用到集合。要对学生的总分排序。可以使用TreeSet。
3,将集合的信息写入到一个文件中。
*/
import java.io.*;
import java.util.*;
class Student implements Comparable<Student>
{
private String name;
private int ma,cn,en;
private int sum;
Student(String name,int ma,int cn,int en)
{
this.name = name;
this.ma = ma;
this.cn = cn;
this.en = en;
sum = ma + cn + en;
}
//自然排序:返回负整数、零或正整数。根据此对象是小于、等于还是大于指定对象。
public int compareTo(Student s)
{
int num = new Integer(this.sum).compareTo(new Integer(s.sum));
if(sum==0)
return this.name.compareTo(s.name);
return num;
}
public String getName()
{
return name;
}
public int getSum()
{
return sum;
}
public int hashCode()
{
return name.hashCode()+sum*78;//保证值的唯一性。
}
//判断是否是同一个人。
public boolean equals(Object obj)
{
if(!(obj instanceof Student))
throw new ClassCastException("类型不匹配");
Student s = (Student)obj;
return this.name.equals(s.name) && this.sum==s.sum;
}
public String toString()
{
return "student["+name+","+ma+","+cn+","+en+"]";
}
}
class StudentInfoTool
{
//默认比较方式
public static Set<Student> getStudents() throws IOException
{
return getStudents(null);
}
//比较器
public static Set<Student> getStudents(Comparator<Student> cmp) throws IOException
{
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));
String line = null;
Set<Student> stus = null;
if(cmp==null)
stus = new TreeSet<Student>();
else
stus = new TreeSet<Student>(cmp);
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
String[] info = line.split(",");
//解析一个字符串,并返回一个整数
Student stu = new Student(info[0],Integer.parseInt(info[1]),
Integer.parseInt(info[2]),
Integer.parseInt(info[3]));
stus.add(stu);
}
bufr.close();
return stus;
}
public static void writeFile(Set<Student> stus) throws IOException
{
BufferedWriter bufw = new BufferedWriter(new FileWriter("stuinfo.txt"));
for(Student stu : stus)
{
bufw.write(stu.toString() + "\t");
bufw.write(stu.getSum() + "");
bufw.newLine();
bufw.flush();
}
bufw.close();
}
}
class StudentInfoTest
{
public static void main(String[] args) throws IOException
{
//定义比较器,强行逆转
Comparator<Student> cmp = Collections.reverseOrder();
Set<Student> stus = StudentInfoTool.getStudents(cmp);
StudentInfoTool.writeFile(stus);
}
}