File:
File类是io包中唯一代表磁盘文件本身的对象,File类定义了一些与平台无关的方法来操作文件,
可以通过调用File类中的方法,实现创建.删除.重命名文件等,File类是对象主要用来获取未文件本身
的一些信息,如文件所在的目录,文件的长度.文件的读写权限等.数据流可以将数据系写入到文件中,
而文件也是数据流最常用的数据媒体.
构造方法:
File(File parent, String child)// parent:子路径字符串 child:父路径字符串
根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。
File(String pathname) //pathname指包括文件路径(包文件名)
通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。
File(String parent, String child) //
根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。
File(URI uri)
通过将给定的 file: URI 转换为一个抽象路径名来创建一个新的 File 实例。
---------------------------------------------------------------------------------------------
File类常见方法:
1,创建。
boolean | createNewFile():在指定位置创建文件,如果该文件已经存在,则不创建,返回false。
和输出流不一样,输出流对象一建立创建文件。而且文件已经存在,会覆盖。
boolean | mkdir():创建文件夹。
boolean | mkdirs():创建多级文件夹。
演示:
File f=new File("file.txt");
sop("create:"+f.createNewFile());//创建文件
sop("mkdir:"+f.mkdir());//创建文件夹
sop("mkdir:"+f.mkdirs());//创建多级的文件夹
ps:sop代表打印语句.
------------------------------------------------
2,删除。
boolean | delete():删除失败返回false。如果文件正在被使用,则删除不了返回falsel。
void | deleteOnExit();在程序退出时删除指定文件。
演示:
File f=new File("file.txt");
f.deleteOnExit();//程序退出时删除指定文件
sop("create:"+f.createNewFile());//创建文件
sop("delete:"+f.delete());//删除文件
------------------------------------------------
3,判断。
boolean | exists():文件是否存在.
boolean | isFile():判断是否是文件
boolean | isDirectory():是否是一个目录
boolean | isHidden():判断是是否是隐藏的文件
boolean | isAbsolute()::测试此抽象路径名是否为绝对路径名。
演示:
File f = new File("d:\\java1223\\day20\\file2.txt");
sop("dir:"+f.isDirectory());
sop("file:"+f.isFile());
sop(f.isAbsolute());
//记住在判断文件对象是否是文件或者目的时,必须要先判断该文件对象封装的内容是否存在。
//通过exists判断。
-----------------------------------------------
4,获取信息。
String | getName():返回由此抽象路径名表示的文件或目录的名称。
String | getPath():将此抽象路径名转换为一个路径名字符串。
String | getParent():返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回 null。如果相对路径中有上一层目录那么该目录就是返回结果。
String | getAbsolutePath():返回此抽象路径名的绝对路径名字符串。
long | lastModified():返回此抽象路径名表示的文件最后一次被修改的时间。
long | length():返回由此抽象路径名表示的文件的长度.
演示:
File f = new File("file.txt");
sop("path:"+f.getPath());
sop("abspath:"+f.getAbsolutePath());
sop("parent:"+f.getParent());//该方法返回的是绝对路径中的父目录。如果获取的是相对路径,返回null。
//如果相对路径中有上一层目录那么该目录就是返回结果。
其他:
static File[] | listRoots():列出可用的文件系统根。
File[] | listFiles():返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件。
String[] | list():返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中的文件和目录
importjava.io.*;
class FileMethod
{
publicstatic void sop(Object obj)
{
System.out.println(obj);
}
publicstatic void main(String[] args) throws IOException
{
//methodCreate();
//methodDel();
methodList();
}
publicstatic void methodCreate()throws IOException
{
Filef = new File("file.txt");
booleanb = f.createNewFile();
sop("create:"+ b);
Filedir = new File("abc");
Filedir2 = new File("ab.txt");
b= dir2.mkdir();
sop("mkdir2:"+ b);
b= dir.mkdir();
sop("midir:"+ b);
Filedirs = new File("abc\\aa\\bb\\cc");
b= dirs.mkdirs();
sop("mkdirs:"+ b);
}
publicstatic void methodDel()throws IOException
{
Filef = new File("file.txt");
booleanb = f.delete();
sop("delete:"+ b);
Filef2 = new File("Demo.txt");
f2.createNewFile();
f2.deleteOnExit();//为避免由于在执行后面的程序发生异常,这里如果创建了一个临时文件,需要在程序结束时删除。
}
publicstatic void methodList()throws IOException
{
Filef = new File("D:\\File");
String[]arr = f.list(new FilenameFilter(){
publicboolean accept(File dir,String name)
{
returnname.endsWith(".bmp");
}
});
sop("len:"+ arr.length);
for(Stringname : arr)
{
sop("bmp文件为:" + name);
}
}
}
递归:
对于每次循环都是用同一个功能的函数,即函数自身调用自身,这种表现形式或手法,称为递归。
注意:
1、限定条件,是作为结束循环用的,否则是死循环
2、注意递归的次数,尽量避免内存溢出。因为每次调用自身的时候都会先执行下一次调用自己的发那个发,所以会不断在栈内存中开辟新空间,次数过多,会导致内存溢出。
列出指定目录下文件或文件夹,包含子目录,即列出指定目录下所有内容
分析,因为目录中还有目录,只有使用同一个列出目录功能的函数完成即可,在列出过程中出现的还是目录的话,还可以再调用本功能,这就是利用递归原理。
public class ListFileDemo {
publicstatic void main(String[] args) {
Filef = new File("d:"+File.separator+"java");
getFile(f,0);
}
publicstatic void getFile(File f,int level){
if(!f.exists())//先判断文件或目录是否存在
newIOException("找不到文件");
if(f.isDirectory()){//判断是不是目录
File[]files = f.listFiles();//是就获取目录下的文件
for(Filef1 : files){
if(f1.isDirectory()){//在循环目录的过程中判断是否还是目录,是 //的话递归调用本方法
level++;
getFile(f1,level);
}
System.out.println(getLevel(level)+f1);//不是目录就打印
}
}
System.out.println(getLevel(level)+f);//不是目录就打印
}
//获取层级目录,打印出层次效果
publicstatic String getLevel(int level){
StringBuildersb = new StringBuilder();
sb.append("|--");
for(int x =0; x<level; x++)
{
sb.insert(0,"| ");
}
return sb.toString();
}
}
用递归删除文件:删除一个带内容的目录。
分析,在window中,删除目录从里面往外删除的。既然是从里往外删除。就需要用到递归。
import java.io.*;
class FileBack
{
publicstatic void main(String[] args)
{
Filef = new File("D:\\File\\f");
removeDirs(f);
}
//删除文件夹所有内容
publicstatic void removeDirs(File f)
{
//将文件存入File数组
File[]files = f.listFiles();
for(int i=0;i<files.length;i++)
{
//如果是文件夹,调用自身
if(files[i].isDirectory())
removeDirs(files[i]);
else
System.out.println(files[i].getName()+ ":files:" + files[i].delete());
}
System.out.println(f.getName()+ ":-dir-:" + f.delete());
}
}
将一指定目录中的java文件的绝对路径,存到一个文本文件中,建立一个java文件列表文件
思路:
1、对指定目录进行递归
2、获取递归过程所有的java文件的路径
3、将这些路径存储到集合中
4、将集合中的数据写到一个文件中
import java.util.*;
import java.io.*;
class WriterToFiles
{
publicstatic void main(String[] args)
{
Filef = new File("D:\\JAVA");
List<File>list = new ArrayList<File>();
Filedir = new File(f,"javalist.txt");
filesToList(f,list);
writeToFile(list,dir.toString());
}
//对指定目录进行递归
publicstatic void filesToList(File f,List<File> list)
{
File[]files = f.listFiles();
for(int i=0;i<files.length;i++)
{
//如果是文件夹,再调用自身
if(files[i].isDirectory())
filesToList(files[i],list);
else
{
//获取递归过程所有的java文件的路径
if(files[i].getName().endsWith(".java"))
//将这些路径存储到集合中
list.add(files[i]);
}
}
}
//将集合中的数据写到一个文件中
publicstatic void writeToFile(List<File> list,String javaListFile)
{
BufferedWriterbufw = null;
try
{
bufw= new BufferedWriter(new FileWriter(javaListFile));
//遍历集合,获取文件路径,并写入文件中
for(Filef : list)
{
Stringpath = f.getAbsolutePath();
bufw.write(path);
bufw.newLine();
bufw.flush();
}
}
catch(IOException e)
{
thrownew RuntimeException("文件路径获取失败");
}
//最终关闭写入流资源
finally
{
try
{
if(bufw!=null)
bufw.close();
}
catch(IOException e)
{
thrownew RuntimeException("文件路径获取失败");
}
}
}
}
Properties
Properties是hashtable的子类。
也就是说它具备map集合的特点。而且它里面存储的键值对都是字符串。
是集合中和IO技术相结合的集合容器。
该对象的特点:可以用于键值对形式的配置文件。
那么在加载数据时,需要数据有固定格式:键=值。
常用方法:
Object setProperty(String key,Stringvalue):
调用Hashtable的put方法,设置键值对
String getProperty(String key):
Set<String> stringPropertyNames:
获取集合中所有的键
void load(InputStream in):
从输入流中读取属性列表(键和元素对)。
void load(Reader reader):
按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。
void list(PrintStream out)
将属性列表输出到指定的输出流。
voidlist(PrintWriter out)
将属性列表输出到指定的输出流。
Store(OutputStream out,String comments):
Store(Writer writer,String comments)
//load方法
publicstatic void loadMthod()throws IOException
{
Propertiespop =new Properties();
FileInputStreamfis = new FileInputStream("info.txt");
//将流中的数据加载进集合
pop.load(fis);
pop.setProperty("zz","25");
pop.setProperty("ww","24");
FileOutputStreamfos = new FileOutputStream("info.txt");
pop.store(fos,"hehe");
pop.list(System.out);
fis.close();
fos.close();
}
举例:
如何将六种的数据存储到集合中?
将文本文件中的键值数据存到集合中:
1、用一个流和文件关联
2、读取一行数据,将改行数据用“=”切割
3、将等号左边作为键,右边作为值,存入到Properties集合即可
//将流中的数据存储到集合中
publicstatic void method()throws IOException
{
BufferedReaderbufr = null;
try
{
Propertiespop = new Properties();
bufr= new BufferedReader(new FileReader("info.txt"));
Stringline = null;
while((line=bufr.readLine())!=null)
{
String[]arr = line.split("=");
pop.setProperty(arr[0],arr[1]);
}
System.out.println(pop);
}
catch(IOException e)
{
thrownew RuntimeException("文件操作失败");
}
finally
{
try
{
if(bufr!=null)
bufr.close();
}
catch(IOException e)
{
thrownew RuntimeException("关闭流资源操作失败");
}
}
}
用于记录应用程序运行次数。如果使用次数已到,那么给出注册提示。
import java.io.*;
import java.util.*;
class RunCountDemo
{
public static void main(String[] args)
{
count();//调用count方法
}
public static void count()
{
//给对象初始化属性类
Properties prop=null;
File file=null;
FileInputStream fis=null;
FileOutputStream fos=null;
try
{
prop=new Properties(); //创建Properties属性类对象
file=new File("count.ini");//创建File文件对象
if(!(file.exists()))//对file进行判断是否存在.如果不存在创建一个.
file.createNewFile();
fis=new FileInputStream(file); //建立字节输入流对象.和文件相关联
prop.load(fis);//将流转换成Properties
int count=0;//计数器
String value=prop.getProperty("timp"); //获取timp的值.
if(value!=null)//判断是否为空
{
count=Integer.parseInt(value); //如果不等于空的话将值转换成Integet对象进行操作
if(count>=5)//如果判断等于>=5程序跳出
{
System.out.println("使用次数已经到");
return ;
}
}
count++;//计数器加加
fos=new FileOutputStream(file);//创建字符输出流.和文件相关联
prop.setProperty("timp",count+"");//设置键值.
prop.store(fos,"haha");//将流流中的数据存储到指定的文件中
}
catch (IOException e)//异常处理
{
throw new RuntimeException("配置文件创建失败"+e.toString());
}
finally
{
try
{
if(fis!=null)
fis.close();
}
catch (IOException e)
{
throw new RuntimeException("关闭输入流失败");
}
try
{
if(fos!=null)
fos.close();
}
catch (IOException e)
{
throw new RuntimeException("关闭输出流失败");
}
}
}
}
打印流:
该流提供了打印方法,可以将各种数据类型的数据都原样打印。
字节打印流:
PrintStream
构造函数可以接收的参数类型:
1,file对象。File
2,字符串路径。String
3,字节输出流。OutputStream
4,PrintStream(OutputStream out,boolean autoFlush)
//autoFlush - boolean 变量;如果为 true,则 println、printf 或 format 方法将刷新输出缓冲区
字符打印流:
PrintWriter
构造函数可以接收的参数类型:
1,file对象。File
2,字符串路径。String
3,字节输出流。OutputStream
4,字符输出流,Writer。
5,PrintWriter(OutputStream out,boolean autoFlush)
//autoFlush - boolean 变量;如果为 true,则 println、printf 或 format 方法将刷新输出缓冲区
演示:
import java.io.*;
class PrintDemo
{
publicstatic void main(String[] args) throws IOException
{
//键盘录入,创建读取流对象
BufferedReaderbufr = new BufferedReader(new InputStreamReader(System.in));
//使用打印流,将文件输出
//输出到屏幕
PrintWriterout = new PrintWriter(new FileWriter("a.txt"),true);
Stringline = null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
out.println(line.toUpperCase());
//out.flush();
}
bufr.close();
out.close();
}
}
合并流
SequenceInputStream是能对多个流进行合并成一个读取流,它在构造时需要传入Enumeration,而这个只用Vector中有,所以这个多个读取流要加入Vector集合中。
注意:它只是对读取流进行合并。
它使用步骤:
1. 创建Vector<InputStream>
2. 将要合并的InputStream加入Vector
3. 通过Vector获取Enumeration
4. 创建SequenceInputStream,将Enumeration作为参数传入。
import java.io.*;
import java.util.*;
class SequenceDemo
{
public static void main(String[] args) throwsIOException
{
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();//使用枚举的elements,.返回Enumeration,SequenceInputStream就可以使用了
SequenceInputStream sis=new SequenceInputStream(en);
FileOutputStream fos=new FileOutputStream("d:\\4.txt");//建立字符输出流.和文件相关联
byte[] buf=new byte[1024];//建立字符数组还缓冲
intlen=0;
while((len=sis.read(buf))!=-1)
{
fos.write(buf,0,len);
}
fos.close();//关闭流
sis.close();
}
}
切割流资源:
1、先关联文件FileInputStream
2、定义写入流变量:FileOutputStream
3、创建数组,并定义切割所需的大小|
4、循环读写数据,并每次创建一个新写入流,创建完后并写入文件中
5、关闭流资源
/*
需求:切割一个MP4文件.,并合并
步骤..
切割:
1.建立字符输入流和文件相关联
2.建立输出流.输出的时候分三个文件存储.
合并:
1.使用枚举的elements.返回Enumeration,因为合并流需要
2.往枚举里添加字节输入流对象.
3.建立输出流.和文件路径文件名关联.
4.输出.
*/
import java.io.*;
import java.util.*;
class SplitFile
{
public static void main(String[] args)
{
//split()
merge();
}
public static void merge()//合并文件
{
FileOutputStream fos=null;//初始化字节输入流对象
SequenceInputStream sis=null;//初始化字节输入流对象合并流
try
{
Vector<FileInputStream> v=new Vector<FileInputStream>();//建立枚举
for(int x=0;x<3;x++)
{
v.add(new FileInputStream("d:\\"+x+".mp4"));//for循环添加遍历对象.
}
Enumeration<FileInputStream> en=v.elements();使用枚举的elements,.返回Enumeration,SequenceInputStream就可以使用了
sis=new SequenceInputStream(en);//建立合并流,往合并流中传入
fos=new FileOutputStream("d:\\4.mp4");//建立字符输出流对象.并指定文件
byte[] buf=new byte[1024*1024*10];//建立字符数组10M的
int len=0;
while((len=sis.read(buf))!=-1)
{
fos.write(buf,0,len);//往容器中存数据.
}
}
catch (IOException e)//异常
{
throw new RuntimeException("文件合并失败");
}
finally
{
try
{
if(fos!=null)
fos.close();
}
catch (IOException e)
{
System.out.println("关闭流资源失败");
}
try
{
if(sis!=null)
sis.close();
}
catch (IOException e)
{
System.out.println("关闭流资源失败");
}
}
//fos.close();
//sis.close();
}
public static void split()//切割
{
FileInputStream fis=null;//初始化对象
FileOutputStream fos=null;
try
{
fis=new FileInputStream("d:\\VID_20130414_181841.mp4");//字符输入流指定文件
byte[] buf=new byte[1024*1024*10];
int len=0;
int count=0;//定义变量来当名字
while((len=fis.read(buf))!=-1)
{
fos=new FileOutputStream("d:\\"+(count++)+".mp4");//指定路径和文件名字
fos.write(buf,0,len);//开始输出.
fos.close();
}
}
catch (IOException e)
{
throw new RuntimeException("文件切割失败");
}
finally
{
try
{
if(fis!=null)
fis.close();
}
catch (IOException e)
{
System.out.println("关闭流资源失败");
}
}
}
}
对象序列化
数据可以封装成对象,对象运行时是在堆内存中的,如果对象的数据需要存储在硬盘上,那么就要用到对象的序列化流。对象序列化(也叫对象的可串行性)其实就是对象持久化,把内存中的对象,变成硬盘上的文件内容。IO中供对象序列化的流对象为ObjectInputStream和ObjectOutputStream。
注意:
1. 用ObjectOutputStream写入的的文件,只能用ObjectInputStream来重构读取。
2. 被序列化的对象必须实现Serializable接口。
3. 对象的静态成员和被transient关键字修饰的成员不能被序列化。(当对象在堆内存的私有对象不希望被序列化时,可以使用transient关键字)。
此外,序列化的文件一般以.ojbect作为类型后缀名,一个文件中可以存放多个不同类型的序列化对象。
Serializable接口
在对对象进行序列化时,必须实行Serializable接口,否则使用ObjectOutputStream写入时,会出现NotSerializableException异常。
Serializable接口并没必须要实现的方法,类定义时仅标示一下实现即可。实现Serializable的类,都有serialVersionUID,如果你没有在类中显式定义一个serialVersionUID,那么编译器会根据该类中的成员生成一个具有唯一性的serialVersionUID。
显式定义serialVersionUID的好处:如果你在对类对象进行了序列化之后,又修改了这个类,那么再次读取修改前序列化的对象时,编译器可以识别;如果没有显式定义,你修改后的类经过编译器编译后会生成一个新的serialVersionUID,这个serialVersionUID跟修改前类的serialVersionUID不同,当你再次读取时,编译器会报出InvalidClassException异常。所以,如果类对象需要序列化,建议显式定义serialVersionUID。
import java.io.*;
//创建Person类,实现序列化
class Person implements Serializable{
//定义自身的序列化方式
publicstatic final long serialVersionUID = 42L;
//定义私有属性
privateString name;
privateint age;
transientString id;
staticString country = "cn";
//构造Person类
Person(Stringname,int age,String id,String country){
this.name= name;
this.age= age;
this.id= id;
this.country= country;
}
//覆写toString方法
publicString toString(){
returnname+ ":" + age + ":" + id + ":" + country;
}
}
//对象序列化测试
class ObjectStreamDemo{
publicstatic void main(String[] args){
//对象写入流
writeObj();
//对象读取流
readObj();
}
//定义对象读取流
publicstatic void readObj(){
ObjectInputStreamois = null;
try{
//创建对象读取流
ois= new ObjectInputStream(new FileInputStream("obj.txt"));
//通过读取文件数据,返回对象
Personp = (Person)ois.readObject();
System.out.println(p);
}catch(Exception e){
thrownew RuntimeException("写入文件失败");
}
//最终关闭流对象
finally{
try{
if(ois!=null)
ois.close();
}catch(IOException e){
thrownew RuntimeException("写入流关闭失败");
}
}
}
//定义对象写入流
publicstatic void writeObj(){
ObjectOutputStreamoos = null;
try{
//创建对象写入流
oos= new ObjectOutputStream(new FileOutputStream("obj.txt"));
//写入对象数据
oos.writeObject(newPerson("lisi",25,"01","cn"));
}catch(Exception e){
thrownew RuntimeException("写入文件失败");
}
//关闭流资源
finally{
try{
if(oos!=null)
oos.close();
}catch(IOException e){
thrownew RuntimeException("写入流关闭失败");
}
}
}
}
管道流
管道流分为字节管道流(PipedInputStream和PipedOutputStream)和字符管道流(PipedReader和PipedWriter):它是IO技术和多线程技术的结合。在一条线程上写入的数据可以在另外一条线程上读取,它们是一对对配合使用的。如果在一条线程上使用管道读取和写入流会发生死锁的情况。
其使用步骤:
1. 分别定义写入和读取的Runnable接口子类,把相应的管道流作为构造参数传入给定义的私有管道流成员。
2. 将配对的管道流通过connect()方法连接起来。
3. 启动线程
import java.io.*;
class Read implements Runnable//多线程
{
private PipedInputStream in;//将PipedInputStream私有
Read(PipedInputStream in)
{
this.in = in;//初始化
}
public void run()//覆盖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);//等待6秒
out.write("piped lai la".getBytes());//将字符串转成字节数组
out.close();
}
catch (Exception e)
{
throw new RuntimeException("管道输出流失败");
}
}
}
class PipedStreamDemo
{
public static void main(String[] args) throwsIOException
{
PipedInputStream in = new PipedInputStream();//管道流
PipedOutputStream out = new PipedOutputStream();
in.connect(out);//两个流连接上
Read r = new Read(in);//传入in
Write w = new Write(out);
newThread(r).start();//开启线程
newThread(w).start();
}
}
RandomAccessFile 类
一、概述:
1、RandomAccessFile称之为随机访问文件的类,自身具备读写方法。
2、该类不算是IO体系中的子类,而是直接继承Object,但是它是IO包成员,因为它具备读写功能,内部封装了一个数组,且通过指针对数组的元素进行操作,同时可通过seek改变指针的位置。
3、可以完成读写的原理:内部封装了字节输入流
4、构造函数:RandomAccessFile(File file,String mode),可已从它的构造函数中看出,该类只能操作文件(也有字符串),而且操作文件还有模式。
模式传入值:”r“:以只读方式打开;”rw“:打开以便读写
如果模式为只读,则不会创建文件,会去读一个已存在的文件,若文件不存在,则会出现异常,如果模式为rw,且该对象的构造函数要操作的文件不存在,会自动创建,如果存在,则不会覆盖,也可通过seek方法修改。
二、特有方法:
1、seek(int n):设置指针,可以将指针设置到前面或后面
2、skipBytes(int n):跳过指定字节数,不可往前跳
三、使用步骤:
1、创建RandomAccessFile对象
2、将数据写入到指定文件中
3、读取数据,读入到指定文件中
注意:尧都区后面的数据,需要调用数组指针,通过改变角标位置,取出相应的数据
a.调整对象的指针:seek()
b.跳过指定字节数
import java.io.*;
class RandomAccessFileDemo
{
public static void main(String[] args) throws IOException
{
//writeFile_2();
//writeFile();
//readFile();
RandomAccessFile raf = newRandomAccessFile("raf1.txt","rw");
raf.write("hha".getBytes());
}
//读取,模式设置为“r”
public static void readFile() throws IOException
{
RandomAccessFile raf = newRandomAccessFile("raf.txt","r");
//调整对象中的指针,seek前后都能设置,所以比skipBytes使用范围广。
//raf.seek(8*0);//里边存入的数据都是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_2() throws IOException
{
RandomAccessFile raf = newRandomAccessFile("raf.txt","rw");
raf.seek(8*0);//修改数据,网络分段下载原理,要重点掌握。
raf.write("周期".getBytes());
raf.writeInt(103);
raf.close();
}
//写入,模式设置为“rw”
public static void writeFile() throws IOException
{
RandomAccessFile raf = newRandomAccessFile("raf.txt","rw");
raf.write("李四".getBytes());
// raf.write(97);write(int x)方法只写入低8位。如果写入的数字在byte取值范围内,那么可以read()正常读取,如果超出,读取时就会出现数据错乱。
raf.writeInt(97);//要把四个字节都写入,所以用writeInt
raf.write("王五".getBytes());
raf.writeInt(99);
raf.close();
}
}
操作基本数据类型的流对象
一、概述:
1、操作基本数据类型的流对象:DataInputStream和DataOutputStream
2、这两个读写对象,可用于操作基本数据类型的流对象,包含读写各种基本数据类型的方法
二、特有方法:
写 读
int型 writeInt(intn) int readInt()
boolean型 writeBoolean(booleanb) boolean readBoolean()
double型 writeDouble(doubled) double readDouble()
操作数组和字符串
一、操作字节数组的对象:ByteArrayInputStream和ByteArrayOutputStream
1、这个对象并没有调用底层资源,所以不用关闭流资源
2、存入的是缓冲区,并未用到键盘和硬盘灯,所以不需要抛任何IO异常
3、对象中封装了数组
4、构造函数:
1)ByteArrayInputStream:在构造函数的时候,需要接受数据源,而且数据源是一个字节数据。
2)ByteArrayOutputStream:在构造函数的时候,不用定义数据目的,因为该对象中已经在内部封装了可变长度的字节数组,这就是数据的目的地
4、因为两个流对象都是操作的是数据,并没有使用系统资源,所以不用进行close关闭。
6、其实就是用流的思想操作数组
7、特有方法:writeTo(OutputStream out) 这个方法用到了字节输出流,有异常存在,需要抛IO异常
二、对应的字符数组和字符串:
字符数组流对象:CharArrayReader和CharArrayWriter
字符串流对象: StringReader和StringWriter
示例:
import java.io.*;
class ArrayStreamDemo
{
publicstatic void main(String[] args)
{
//数据源
ByteArrayInputStreambais = new ByteArrayInputStream("ABCDEFF".getBytes());
//数据目的
ByteArrayOutputStreambaos = new ByteArrayOutputStream();
intby = 0;
//读取和写入数据
while((by=bais.read())!=-1)
{
baos.write(by);
}
System.out.println(baos.size());
System.out.println(baos.toString());
try
{
//方法,此处抛异常,所以上面需要抛出去
baos.writeTo(newFileOutputStream("a.txt"));
}
catch(IOException e)
{
thrownew RuntimeException("写入文件失败");
}
}
}
编码的练习及总结:
一、概述:
1、字符流的出现为了方便操作字符,更重要的是加入了编码的转换,即转换流。
2、通过子类进行转换
3、在两个对象进行构造时,可加入编码表
4、可传入编码表的有:
1)转换流:InuputStreamReader和OutputStreamWriter
2)打印流:PrintStream和PrintWriter,只有输出流
5、常见的编码表:
1)ASCII:美国标准信息交换码表。用一个字节的7位表示
2)IOS8859-1:拉丁码表;欧洲码表。用一个字节的8位表示
3)GB2312:中国的中文编码表
4)GBK:中国的中文编码表升级,融合了更多的中文文字字符。打头的是两个高位为1的两个字节编码。为负数
5)Unicode:国际标准码,融合了多种文字
6)UTF-8:最多用三个字节表示一个字符的编码表,包括:一位、两位、三位表示的字符
UTF-8有自己的字节码:
一个字节:0开头
两个字节:字节一 ---> 110 位数:10 ~ 6
字节二 ---> 10 位数:5 ~ 0
三个字节:字节一 ---> 110 位数:15 ~ 12
字节二 ---> 10 位数:11 ~ 6
字节三 ---> 10 位数:5 ~ 0
二、编码和解码:
四、特别注意:
对于中文的”联通“,这两个字比较特别,它的二进制位正好是和在UTF-8中两个字节打头的相同,可以找到对应的符号,但不再是”联通“了。
“联通”编码问题的原因:
//boBinaryString(int)它接受的是int,byte类型的b参与运算时会类型提升为int,我们需要的是提升后的低8位,所以&255
对联通的结果进行GBK编码时,其二进制码为:
11000001
10101010
11001101
10101000
编码的结果符合UTF-8的格式,所以再次打开记事本时,它会把它按照UTF-8的格式进行解码,结果就是两个乱码。
import java.util.*;
class EncodeDemo
{
publicstatic void main(String[] args) throws Exception{
CodeDemo();
//编译成功,解码失败后的解决方式
CodeBack();
}
publicstatic void CodeDemo()throws Exception{
Strings = "你好";
byte[]b1 = s.getBytes();
Strings1 = new String(b1);
System.out.println(Arrays.toString(b1));
byte[]b2 = s.getBytes("GBK");//默认编码
Strings2 = new String(b2);
System.out.println("s1="+ s1 + ",s2=" + s2);
System.out.println(Arrays.toString(b2));
byte[]b3 = s.getBytes("UTF-8");//国际编码
Strings3 = new String(b3);
System.out.println("s3="+ s3);
System.out.println(Arrays.toString(b3));
byte[]b4 = s.getBytes("ISO8859-1");//欧洲编码
Strings4 = new String(b4);
System.out.println("s4="+ s4);
System.out.println(Arrays.toString(b4));
}
//编码与解码
publicstatic void CodeBack()throws Exception{
String s = "你好";
System.out.println("原数据:" + s);
byte[]b1 = s.getBytes("GBK");//默认编码
System.out.println(Arrays.toString(b1));
Strings1 = new String(b1,"ISO8859-1");
System.out.println("s1="+ s1);
System.out.println("----对s1进行ISO8859-1编码-----");
//对s1进行ISO8859-1编码
byte[]b2 = s1.getBytes("ISO8859-1");//欧洲编码
System.out.println(Arrays.toString(b2));
Strings2 = new String(b2,"GBK");
System.out.println("s2="+ s2);
}
}
/*
有5个学生,每个学生有三门课的成绩。
从键盘输入以上数据(包括姓名,三门课成绩);
输入的格式:如zhangsan,30,40,60计算出总成绩。
并把学生的信息和计算出的总分数,按由高到低顺序存在在磁盘文件stud.txt中。
1.描述学生对象。
2.定义一个学生对象的工具类
思想:
1.通过获取键盘录入的一行数据。并将该行数据中的信息取出,封装成学生对象。
2. 因为学生对象有很多,就需要存储使用的集合,因为要对学生的总分排序,
所以可以使用TreeSet。
3.将集合中的信息写入到一个文件中。
*/
import java.io.*;
import java.util.*;
//使用TreeSet需要将其中的元素实现Comparable接口
class Student implementsComparable<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;
}
//Comparable接口要实现compareTo方法。
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;
}
//学生类也有可能存入HashSet中,所以要复写hashCode和equals方法。
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;
}
//复写toString方法,提供学生类特有的字符串表现形式。
public String toString()
{
return "Student["+name+","+ma+","+cn+","+en+"]";
}
}
//定义学生信息录入和存储工具类
class StudentInfoTool
{
//函数重载,提供一个默认的方法,对学生对象按照定义的自然顺序进行排序
public static Set<Student> getStudents() throws IOException
{
return getStudents(null);
}
//定义录入工具,并将键盘录入的结果存入Set集合中,因为要排序,所以要用TreeSet。
//这里加入比较器作为参数,是为了让集合可以按照不同的要求进行排序,比如按照某一单科成绩或总分从高到底
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);
//也可以用三元运算符进行优化
// Set<Student>stus=(cmp==null)?(new TreeSet<Student>()):(newTreeSet<Student>(cmp));
// 循环读取录入的信息,注意定义结束标记。
while((line =bufr.readLine())!=null)
{
if("over".equals(line))
break;
//用","进行切割---其实这里可以将录入信息line用正则表达式过滤一下,对于非法的信息不写入,并进行提示,防止录入非法的数据。
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 write2File(Set<Student> stus) throwsIOException
{
BufferedWriter bufw = new BufferedWriter(newFileWriter("stuinfo.txt"));
for(Student stu: stus)
{
bufw.write(stu.toString()+"\t");
//这里写入的数据是int类型的值,并且write会截取其低8位,所以要把它转成字符串,否则会出现乱码
bufw.write(stu.getSum()+"");
bufw.newLine();//写入跨平台的换行符
bufw.flush();//字符缓冲区一定要记得刷新动作
}
//关闭资源。
bufw.close();
}
}
class StudentInfoTest
{
public static void main(String[] args) throws IOException
{
//通过Collections集合工具类的反转命名方法,获得一个逆序比较器
Comparator<Student> cmp = Collections.reverseOrder();
//如果没有传入逆序比较器,学生会按照自然顺,即总成绩从低到高的排序,这与我们的现实生活习惯不符合。
Set <Student> stus = StudentInfoTool.getStudents(cmp);
//将集合中的学生信息写入文件。
StudentInfoTool.write2File(stus);
}
}