------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
一、概念:IO(Input Output):用来处理设备之间的数据传输。
二、分类:按流向分:输入流和输出流。输入流是读,输出流是写。(相对io流对象分析)。
按数据类型分类:字节流和字符流,字节流操作文本以及其他数据(比如图片、音频);字符流专门用于操作文本数据。
三、在io流体系中:
字节流基类:InputStream 和OutputStream
字符流基类:Reader和Writer
这四个类派生出来的子类都是以父类名作为后缀。
四、编写方式:
1.导入io包
2.对编写的代码进行异常处理
3.关闭流方法要放在finally中
问题1:java中有垃圾回收机制为什么还要调用close()方法进行关闭流
io流涉及到操作系统的资源,不但在内存中分配空间,同时在操作系统中占用资源,
java回收机制只能回收内存中资源,所以一定要调用close方法进行关闭。
问题2:为什么一定要处理io异常
五、java中一些常用对象:System Runtime Data Calendar Math Random
System类中的方法和属性都是静态的
out:标准的输入,默认是控制台
in:标准的输出,默认是键盘
类中一些常用方法
exit(0)结束程序
Properties getProperties():获取当前的系统属性。
String getProperty(String key):根据key来获取属性值
setIn(InputStream in):改变输入流的源。
setOut(PrintStream out):改变输出流的目的。
setProperty(String key, String value):设置指定的key和value进去当前系统属性
Runtime类中没有构造方法,不能通过new来建立对象,通过getRuntime获取实例(单例设计模式)
exec方法,在单独的进程中运行一个程序
Data类通常用于获取当前时间
class DateDemo
{
public static void main(String[] args)
{
Date d = new Date();
System.out.println(d);//Fri Jul 31 07:58:13 CST 2015
//SimpleDateFormat用于格式化时间,指定格式将Date获取的时间转换
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日E hh:mm:ss");
String time = sdf.format(d);
System.out.println(time);//2015年07月31日星期五 07:58:13
}
}
Calendar类可以按照特定的格式获取日历的各个字段
代码示例:
class CalendarDemo
{
public static void main(String[] args)
{
Calendar c = Calendar.getInstance();
c.add(Calendar.DAY_OF_MONTH,10);//在当前月的基础上,向后推10天
printCalendar(c);
}
public static void printCalendar(Calendar c)
{
String[] month = {"一月","二月","三月","四月"
,"五月","六月","七月","八月"
,"九月","十月","十一月","十二月"};
String[] week = {"","星期日","星期一","星期二","星期三","星期四","星期五","星期六"};
sop(c.get(Calendar.YEAR)+"年");//获取当前年
sop(month[c.get(Calendar.MONTH)]);//获取当前月,通过查表法显示
sop(c.get(Calendar.DAY_OF_MONTH)+"日");//获取当前日
sop(week[c.get(Calendar.DAY_OF_WEEK)]);//获取当前周,通过查表法显示
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
Math类包含用于执行基本数学运算的方法
Random类用于生成伪随机数
代码示例:
class MathDemo
{
public static void main(String[] args)
{
double d = Math.ceil(12.33);//返回大于指定数据的最小整数
System.out.println(d);
double d2 = Math.floor(12.33);//返回小于指定数据的最大整数
System.out.println(d2);
long l = Math.round(12.33);//四舍五入
System.out.println(l);
double d3 = Math.pow(2,3);//2的3次幂
System.out.println(d3);
System.out.println("---------------");
//从1~10获取是个随机数,用Math.random方法
for (int x=0; x<10; x++)
{
int d4 = (int)(Math.random()*10+1);
System.out.println(d4);
}
System.out.println("---------------");
//从1~10获取是个随机数,直接实例化Random对象
Random r = new Random();
for (int x=0; x<10; x++)
{
int d5 = r.nextInt(10)+1;
System.out.println(d5);
}
}
}
六、字符流:
Reader
|--BufferedReader:字符流缓冲区,用于提高效率
|--LineNumberReader:带有行号的缓冲输入流
|--CharArrayReader:字符数组流,用于操作char类型数组
|--FilterReader:过滤流(抽象类)
|--InputStreamReader:转换流,将字节流转换为字符流,可自定义编码
|--FileReader:读取文本文件,常用
|--PipedReader:管道流
|--StringReader:字符串流,便于操作字符串
Writer
|--BufferedWriter:字符流缓冲区,用于提高效率
|--CharArrayWriter:字符数组流,用于操作char类型数组
|--FilterWriter:过滤流(抽象类)
|--OutputStreamWriter:转换流,将字节流转换为字符流,可自定义编码
|--FileWriter:写入文本文件,常用
|--PipedWriter:管道流
|--PrintWriter:打印流,只能操作文本文件
|--StringWriter:字符串流,便于操作字符串
代码示例:复制文本文件:用字符输入流读取文件,再用字符输出流将读取到的内容写到新文件中
public static void copy_1()
{
FileReader fr = null;
FileWriter fw = null;
try
{
fr = new FileReader("CopyTextFile.java");//创建读取字符流对象,并指定名称
fw = new FileWriter("d:\\CopyTextFile.java");//创建写入字符流对象,并指定名称
char[] buf = new char[1024];//创建字符数组作为缓冲区
int len = 0;//定义标记,用于记录读取返回值
while ((len=fr.read(buf))!=-1)//当返回值不为-1,则表示有内容,继续读取,并存储在字符数组中
{
fw.write(buf,0,len);//将字符数组的数据从0到读取到的长度写入文件
}
}
catch (IOException e)
{
throw new RuntimeException("读写失败");
}
finally
{
if (fr!=null)//在关闭之前进行判断,是否为空,不判断容易出现空指针异常
{
try
{
fr.close();//关闭读取流
}
catch (IOException e)
{
}
}
if (fw!=null)
{
try
{
fw.close();//关闭写入流
}
catch (IOException e)
{
}
}
}
}
代码示例2:对于复制文件,需要用到字符缓冲流。在上面的程序中的try部分代码可以修改为如下
/*
如果使用字符缓冲流,在读取数据时,不用定义数组,直接使用readLine方法
*/
BufferedReader bfr = null;
BufferedWriter bfw = null;
try
{
bfr = new BufferedReader(new FileReader("CopyTextFile.java"));
bfw = new BufferedWriter(new FileWriter("d:\\CopyTextFile.java"));
String line = null;
while ((line=bfr.readLine())!=null)//readLine方法,返回字符串
{
bfw.write(line);
bfw.newLine();
bfw.flush();
//close方法执行时会进行刷新数据,但是在这里定义flush方法
//是因为在写入过程中,如果发生异常,也可以将一部分数据写入文件
}
}
对于BufferedReader类,其实是内部将缓冲数组封装起来,提供readLine方法,这时我们可以自定义字符缓冲输入流
代码示例:
class MyBufferedReader extends Reader
{
private Reader r;
MyBufferedReader(FileReader r)//在初始化字符输入缓冲流时,需要有字符输入流对象
{
this.r = r;
}
//自定义readLine方法,抛出异常,因为这是功能,要让调用者进行处理
public String myReadLine() throws IOException
{
//定义字符缓冲区存储读取的字符,其实在BufferedReader内部是字符数组
StringBuilder sb = new StringBuilder();
int len = 0;
while ((len=r.read())!=-1)//调用Reader中的read方法
{
if (len=='\r')//因为windows操作系统中换行标记是\r\n,所以读到\r要继续读
continue;
if (len=='\n')
return sb.toString();//读到\n就将一行数据返回
else
sb.append((char)len);
}
//如果文件中最后一行没有换行,就读不到\n,也就是最后一行数据
//没有返回,所以要多一次判断字符缓冲区的长度
if ((sb.length())>0)
return sb.toString();
return null;
}
//自定义关闭方法,调用Reader中的close方法
public void close() throws IOException
{
r.close();
}
//覆盖Reader中的两个抽象方法,不是必要的
public int read(char[] cbuf, int off, int len) throws IOException
{
return r.read(cbuf,off,len);
}
public void myClose() throws IOException
{
r.close();
}
}
LineNumberReader是BufferedReader的子类,除了有父类的方法外,特有setLineNumber和getLineNumber方法,
分别设置行号和获取行号
定义一个LineNumberReader类:
class MyLineNumberReader extends MyBufferedReader
{
/*
在自定义LineNumberReader类时,可以直接继承自定义BufferedReader类中加入计数器,并
且提供设置行号和获取行号方法。
*/
private int lineNumber;//定义计数器,也就是行号
MyLineNumberReader(FileReader r)
{
super(r);
}
public String myReadLine() throws IOException
{
lineNumber ++;//当读取一行时,行号进行加1操作
return super.myReadLine();//父类中已经定义过读取一行的方法,可以直接使用
}
//设置行号,让行号从用户设置的数值开始计数
public void setLineNumber(int lineNumber)
{
this.lineNumber = lineNumber;
}
//获取行号
public int getLineNumber()
{
return lineNumber;
}
}
七、字节流
InputStream
|--ByteArrayInputStream:字节数组输入流
|--FileInputStream:用于读取文件,常用
|--FilterInputStream:过滤输入流的所有类的超类
|--BufferedInputStream:字节缓冲输入流
|--DataInputStream:基本数据类型输入流
|--LineNumberInputStream:带有行号的字节输入流
|--ObjectInputStream:可操作自定义的
序
列化的
对象和基本数据类型
|--PipedInputStream:管道字节输入流
|--SequenceInputStream:合并流
OutputStream
|--ByteArrayOutputStream:字节数组输出流
|--FileOutputStream:文件输出流,常用
|--FilterOutputStream:过滤输出流的所有类的超类
|--BufferedOutputStream:字节缓冲的输出流
|--DataOutputStream:基本数据类型输出流
|--PrintStream:打印流,如果是文本文件,要使用PrintWriter
|--ObjectOutputStream:可操作自定义的序列化的对象和基本数据类型
|--PipedOutputStream:管道字节输出流
代码示例:复制一张图片,实际就用字节输入流读取图片,再用字节输出流写入一张新图片
class CopyPic
{
public static void main(String[] args)
{
FileInputStream fis = null;
FileOutputStream fos = null;
try
{
fis = new FileInputStream("1.jpg");//字节输入流对象,用于读取图片
fos = new FileOutputStream("2.jpg");//字节输出流对象,用于写入图片
byte[] buf = new byte[fis.available()];//自定义字节数组,用于缓冲读取到的数据
int len = 0;
while ((len=fis.read(buf))!=-1)//read当返回-1时,表示文件读取完毕
{
fos.write(buf,0,len);//写入数据
}
}
catch (IOException e)
{
throw new RuntimeException("读取失败");
}
finally
{
if (fis!=null)
{
try
{
fis.close();
}
catch (IOException e)
{
throw new RuntimeException("关闭读取流失败");
}
}
if (fos!=null)
{
try
{
fos.close();
}
catch (IOException e)
{
throw new RuntimeException("关闭写入流失败");
}
}
}
}
}
代码示例2:复制音频文件,并加入缓冲流技术
public static void copy_1()
{
BufferedInputStream bfis = null;
BufferedOutputStream bfos = null;
try
{
bfis = new BufferedInputStream(new FileInputStream("1.mp3"));//字节缓冲输入流
bfos = new BufferedOutputStream(new FileOutputStream("2.mp3"));//字节缓冲输出流
int by = 0;
while ((by=bfis.read())!=-1)
{
bfos.write(by);
bfos.flush();//写入数据后进行刷新
}
}
catch (IOException e)
{
throw new RuntimeException("读取失败"+e.toString());
}
finally
{
if (bfis!=null)
{
try
{
bfis.close();//关闭输入流
}
catch (IOException e)
{
throw new RuntimeException("读取流关闭失败");
}
}
if (bfos!=null)
{
try
{
bfos.close();//关闭输出流
}
catch (IOException e)
{
throw new RuntimeException("写入流关闭失败");
}
}
}
}
代码示例3:自定义字节缓冲流
class MyBufferedInputStream
{
private InputStream in;
private byte[] buf = new byte[1024*4];//定义字节数组,用于存储数据
private int pos = 0;//定义指针,用于标记数组脚标
private int count = 0;//定义计数器,用于记录数组的长度
MyBufferedInputStream(InputStream in)//初始化字节流输入缓冲区,就要有字节输入流对象
{
this.in = in;
}
public int myRead() throws IOException
{
if (count==0)//判断如果count为0,代表数组中内容为空
{
count = in.read(buf);//就进行读操作
if (count<0)//读完数据后,如果文件已经读完,count就是-1
return -1;//在自定义的读取方法中就返回-1,标志数据已经读完
pos = 0;//每次读到一个新的数组就将指针归零
byte b = buf[pos];//定义byte变量记录数组中的数据
pos ++;//读到数组中一个byte,指针就进行加1操作,继续往下读
count --;//count就减1操作,因为已经读了一个字节,数组的长度就减少1
return b&255;
//&255的原因是有可能读到连续8个1的情况,8个1就是-1,就会造成数据已经读取完毕的标志
//read方法将读到的byte提升为int并返回,在提升为int类型后还是-1
//但是&225后,可以保留最低8位,也就是数据不改变,并且提升后不再是-1,是255
}
else if (count>0)//当count大于0,继续读取
{
byte b = buf[pos];
pos ++;
count --;
return b&255;
}
return -1;
}
public void myClose() throws IOException
{
in.close();
}
}
八、装饰类设计模式:
当一个类中的功能需要进行增强时,可以定义一个新的类继承本类,再复写要增强的功能,但是这样会造成继承体系臃肿,
这时可以定义一个类,将本类对象作为成员定义在增强类中,再进行功能增强,这个类称为装饰类,一般是与被装饰类是
统一体系中。装饰类比继承灵活,并且降低类与类之间的关系。
例如:字符缓冲流和字节缓冲流就是装饰类,对读取操作进行功能增加,提高效率。
九、转化流:控制台输出和键盘输入都是属于字节流,当要将键盘录入的信息存储到文本文件中,字节流可以操作,但是
转换为字符流再操作,更加方便,并且可以用字符缓冲流,提高效率,并且转换流可以指定编码。而要将一
个文本文件输出到控制台中也是同样道理
十、改变标准输入输出设备:System.setIn:改变标准输入设备,System.setOut改变标准输出设备。
十一、读取键盘录入:
代码示例:
public static void method_1() throws IOException
{
InputStream in = System.in;//System.in返回的是字节流对象
StringBuilder sb = new StringBuilder();//新建字符缓冲区,用于记录一行数据
while (true)//循环录入
{
int by = in.read();//读取一个字节
if(by=='\r')//如果读到\r就继续读
continue;
if(by=='\n')//如果读到\n就代表一行数据读取完毕
{
String s = sb.toString();//将字符缓冲区变成字符串
if ("over".equals(s))//如果是over,就停止程序
break;
else
System.out.println(s.toUpperCase());//返回大写形式
sb.delete(0,sb.length());//没返回一次就清空字符缓冲区
}
else
sb.append((char)by);//没有读到\r和\n 就将读到的字节强转成字符,并存储进字符缓冲区
}
}
对于上面的程序,读取一行后再返回大写形式,跟字符流缓冲区的readLine方法相识,这时就涉及到转换流,将字节输入流转换成字符输入流
上面的程序就可以改为如下形式:这样可以简化书写和提高效率
public static void method_2() throws IOException
{
//读取键盘的标准书写形式
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
//控制台输出的便准书写形式
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
String line = null;
while ((line=br.readLine())!=null)//一次读取一行
{
if("over".equals(line))
break;
bw.write(line.toUpperCase());//将数据输出到控制台
bw.newLine();//进行换行一次,不然数据会一直连续输出
bw.flush();
}
br.close();
bw.close();
}
十二、操作规律:在使用io流时,需要
明确源和目的
源:输入流 InputStream Reader
目的:输出流 OutputStream Writer
明确是否操作文本数据
是:字符流 常用:FileReader FileWriter
否:字节流 常用:FileInputStram FileOutputStream
明确具体对象
源:内存 硬盘 键盘
目的:内存 硬盘 控制台
练习:
class TransStreamTest
{
public static void main(String[] args)
{
//copyPic();
showText();
}
/*
将一个图片文件中数据存储到另一个文件中
1,要读取一个图片文件并写入到另一个文件,所以同时要用到输入流和输出流
2,图片是以2进制存储,要用到字节流。
3,源和目的都是硬盘:明确用FileInputStream和FileOutputStream
是否要提高效率?是,所以要用到BufferedInputStream和BufferedOutputStream
*/
public static void copyPic()
{
BufferedInputStream bfis = null;
BufferedOutputStream bfos = null;
try
{
bfis = new BufferedInputStream(new FileInputStream("1.jpg"));
bfos = new BufferedOutputStream(new FileOutputStream("3.jpg"));
int by = 0;
while ((by=bfis.read())!=-1)
{
bfos.write(by);
}
}
catch (IOException e)
{
throw new RuntimeException("复制失败");
}
finally
{
try
{
if(bfis!=null)
bfis.close();
}
catch (IOException e)
{
throw new RuntimeException("关闭读取流失败");
}
try
{
if(bfos!=null)
bfos.close();
}
catch (IOException e)
{
throw new RuntimeException("关闭写入流失败");
}
}
}
/*
将一个文本数据打印在控制台上
1,源为文本,目的为控制台
2,文本为字符数据,用到字符流,Reader
目的为控制台对应的对象是System.out,源是字符数据,目的是字节数据,所以要用到转换流
要用到OutputStreamWriter
3,源为硬盘,明确用FileReader,要提高效率,用到BufferReader
*/
public static void showText()
{
BufferedReader bfr = null;
BufferedWriter bfw= null;
try
{
bfr = new BufferedReader(new FileReader("trans.txt"));
bfw = new BufferedWriter(new OutputStreamWriter(System.out));
String line = null;
while ((line=bfr.readLine())!=null)
{
bfw.write(line);
bfw.newLine();
}
}
catch (IOException e)
{
throw new RuntimeException("读取失败");
}
finally
{
try
{
if(bfr!=null)
bfr.close();
}
catch (IOException e)
{
throw new RuntimeException("读取流关闭失败");
}
try
{
if(bfw!=null)
bfw.close();
}
catch (IOException e)
{
throw new RuntimeException("写入流关闭失败");
}
}
}
}
练习:保存异常信息
class ExceptionInfoDemo
{
//保存异常信息
public static void main(String[] args)
{
try
{
int[] arr = new int[1];
System.out.println(arr[2]);//定义一个异常
}
catch (ArrayIndexOutOfBoundsException e)
{
//保存异常信息时,记录时间
Date d = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String s = sdf.format(d);
try
{
//定义一个字节打印流
PrintStream ps = new PrintStream("Exception.log");
System.setOut(ps);//从控制台中写入异常信息
ps.println(s);
}
catch (IOException io)
{
throw new RuntimeException("保存异常信息失败");
}
}
}
}
File类:文件和目录路径名的抽象表示形式
一、常用方法
创建:
boolean createNewFile() :创建文件,如果文件存在,则不创建,返回false
boolean mkdir() :创建文件夹
boolean mkdirs() :创建多级文件夹
删除:
boolean delete():删除指定文件
void deleteOnExit():在程序退出时删除指定文件
判断:
boolean exists():判断是否存在
boolean isAbsolute():是否为绝对路径名。
boolean isDirectory():是否是一个目录。
boolean isFile():是否是一个标准文件。
boolean isHidden():是否是一个隐藏文件。
获取:
String getName():获取文件或者文件夹名称
String getParent():获取父目录,如果没有则返回null
String getPath():获取路径
String getAbsolutePath():获取绝对路径
long lastModified():获取文件最后修改的时间
long length():获取文件长度
String[] list():返回指定目录下所有文件和文件夹的名称
String[] list(FilenameFilter filter):返回指定目录下满足指定过滤器的文件和文件夹的名称
File[] listFiles():返回指定目录下所有文件
File[] listFiles(FileFilter filter):返回指定目录下符合指定过滤器的文件
File[] listFiles(FilenameFilter filter):返回指定目录下符合指定过滤器的文件
代码示例:
public static void method_1()throws IOException
{
File f = new File("file.txt");
System.out.println("创建文件"+f.createNewFile());
File dir = new File("files");
System.out.println("创建文件夹"+dir.mkdir());
File dirs = new File("files\\aa\\bb\\cc\\dd\\kk");
System.out.println("创建多级文件夹"+dirs.mkdirs());
System.out.println("文件是否存在"+f.exists());
System.out.println("是否为文件"+f.isFile());
System.out.println("是否为文件夹"+dir.isDirectory());
System.out.println("父目录名称为"+f.getParent());
System.out.println("path:"+f.getPath());
System.out.println("AbsolutePath:"+f.getAbsolutePath());
System.out.println("toString:"+f.toString());
}
代码示例:
//获取指定文件夹下的所有文件,listFiles方法返回的是文件
public static void showListFiles()throws IOException
{
File dir = new File("d:\\java\\day18");
File[] files = dir.listFiles();
for(File f:files)
{
System.out.println(f.getName()+"----"+f.length());
}
}
//自定义过滤器,列出指定文件
public static void showListFiles_1()throws IOException
{
File dir = new File("d:\\java\\day18");
File[] files = dir.listFiles(new FilenameFilter()//匿名内部类,实现FilenameFilter接口
{
public boolean accept(File dir,String name)//覆盖accept方法,自定义过滤方法
{
if (name.endsWith(".mp3"))
return true;
return false;
}
});
for(File f:files)
{
System.out.println(f.getName()+"----"+f.length());
}
}
//列出指定目录下的所有文件的文件名称,list方法返回的是文件的名称
public static void showListFiles_2()throws IOException
{
File dir = new File("d:\\java\\day18");
String[] name = dir.list();
for(String n:name)
{
System.out.println(n);
}
}
练习1:
/*
列出目录下的所有文件,如果有子文件夹,也要列出子文件夹的文件
*/
class FileDemo3
{
public static void main(String[] args)
{
File dir = new File("d:\\java");//指定文件夹
showDir(dir);
}
public static void showDir(File dir)
{
System.out.println(dir);
File[] files = dir.listFiles();//调用listFiles方法返回文件夹下的文件和文件夹
for(File f:files)
{
if(f.isDirectory())//进行判断,如果是文件夹,再次调用该方法,递归
showDir(f);
else
System.out.println(f);
}
}
}
练习2:
/*
删除指定文件夹下的所有文件以及文件夹,
删除文件是从里到外的,这里也用到递归的思想
*/
class RemoveDir
{
public static void main(String[] args)
{
File dir = new File("e:\\java_1");//指定要删除的文件夹
removeDir(dir);
}
public static void removeDir(File dir)
{
File[] files = dir.listFiles();//调用listFiles方法,列出文件夹下的所有文件以及文件夹
for(File f:files)
{
if(f.isDirectory())//如果是文件夹,再次调用该方法
removeDir(f);
else
System.out.println(f.getName()+"-----"+f.delete());//否则就调用delete方法将文件删除
}
System.out.println(dir.getName()+"----"+dir.delete());
}
}
练习3:
/*
需求:将一个文件夹下的所有java文件的绝对路径写到一个文本文件中
*/
import java.io.*;
import java.util.*;
class ListJavaFiles
{
public static void main(String[] args)
{
File dir = new File("d:\\java");
ArrayList list = new ArrayList();
filesToList(dir,list);
File f = new File("javalist.txt");
writeToFile(list,f.toString());
}
//获取文件列表的方法,并存储进一个File类型的集合
public static void filesToList(File dir,List list)
{
File[] files = dir.listFiles();//获取指定目录所有文件
for(File f:files)
{
if(f.isDirectory())//判断如果是文件夹,再次调用该方法
filesToList(f,list);
else
//在添加文件进集合的时候对文件名称进行判断,如果是以.java结尾就添加进集合
if((f.getName()).endsWith(".java"))
list.add(f);
}
}
//将集合中的数据写入文本文件
public static void writeToFile(List list,String fileName)
{
BufferedWriter bw = null;//字符缓冲流
try
{
bw = new BufferedWriter(new FileWriter(fileName));
for(File f:list)//增加for循环
{
bw.write(f.getAbsolutePath());//获取文件的绝对路径
bw.newLine();
bw.flush();
}
}
catch (IOException e)
{
throw new RuntimeException("保存文件列表失败");
}
finally
{
try
{
if(bw!=null)
bw.close();
}
catch (IOException e)
{
throw new RuntimeException("关闭流失败");
}
}
}
}
二、properties:属性文件,是Hashtable的子类,具有map集合的特点,里面的键值对都是String类型
练习1:
/*
需求:模拟使用软件,如果软件使用次数已经达到5次,就不能使用软件
要用定义计数器,但是程序每运行一次,计数器就在内存中就会消失,
所有要将计数器的值写在properties文件中
*/
import java.io.*;
import java.util.*;
class Count
{
public static void main(String[] args) throws IOException
{
Properties prpo = new Properties();//新建Properties对象
File file = new File("count.ini");//定义一个文件记录次数
if(!(file.exists()))//判断文件是否存在,不存在就新建文件
file.createNewFile();
FileInputStream fos = new FileInputStream(file);//定义字节输入流
prpo.load(fos);//从输入流中读取属性列表(键和元素对)
int count = 0;//定义计数器
String value = prpo.getProperty("time");//获取文件中key文time的值
if (value!=null)
{
count = Integer.parseInt(value);//将值转成Integer类型,再赋值给计数器
if (count>=5)//判断次数是否超过5次
{
System.out.println("使用次数已到,继续使用请购买!");
return ;
}
}
count ++;//计数器加1
prpo.setProperty("time",count+"");//将加1后的值写入文件中
FileOutputStream fis = new FileOutputStream(file);//定义字节输出流
prpo.store(fis,"");//将字节输出流对象传给store方法,将流中的数据写入文件
fos.close();
fis.close();
}
}
练习2:
/*
将一个文本文件中的信息存到properties中
文本中的信息是key=value的形式
读取文本文件,以“=”作为分隔符,用String类中的split方法取出key和value
*/
class PropertiesDemo
{
public static void main(String[] args) throws IOException
{
method();
}
public static void method()throws IOException
{
//字符缓冲输入流
BufferedReader bfr = new BufferedReader(new FileReader("prop.txt"));
//properties对象
Properties p = new Properties();
String line = null;
while ((line=bfr.readLine())!=null)
{
String[] arr = line.split("=");//读取一行后以“=”切割字符串
p.setProperty(arr[0],arr[1]);//setPropertry方法设置key和value
}
bfr.close();//关闭流
System.out.println(p);
}
}
三、序列流和切割文件
(序列流)SequenceInputStream
表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止
代码示例:
import java.io.*;
import java.util.*;
class SplitFile
{
public static void main(String[] args) throws IOException
{
//splitFile();
mergeFile();
}
//合并文件
public static void mergeFile()throws IOException
{
//定义vactor集合,用于存储字节输入流对象
Vector v = new Vector();
v.add(new FileInputStream("files\\1.part"));
v.add(new FileInputStream("files\\2.part"));
v.add(new FileInputStream("files\\3.part"));
//获取枚举
Enumeration en = v.elements();
//将枚举作为参数传递给合并流对象
SequenceInputStream sis = new SequenceInputStream(en);
//定义字节输出流,将多个文件写入到一个文件
FileOutputStream fos = new FileOutputStream("files\\4.mp3");
byte[] buf = new byte[1024*1024*8];
int len = 0;
while ((len=sis.read(buf))!=-1)
{
fos.write(buf);
fos.flush();
}
sis.close();
fos.close();
}
//切割文件
public static void splitFile()
{
FileInputStream fis = null;
FileOutputStream fos = null;
try
{
//定义缓冲区,文件以800kb大小分割
byte[] buf = new byte[1024*800];
int len = 0;
int count =1;//定义计数器,用于给文件命名
fis = new FileInputStream("1.mp3");
while ((len=fis.read(buf))!=-1)
{
//没填满缓冲区一次,就新建一个字节输出流,写入一个文件
fos = new FileOutputStream("files\\"+(count++)+".part");
fos.write(buf,0,len);
}
}
catch (IOException e)
{
throw new RuntimeException("切割文件失败");
}
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("关闭写入流失败");
}
}
}
}
四、对象的序列化:ObjectInputStream和ObjectOutputStream,能够读取和写入自定义对象。
自定义的对象要实现Serializable接口,该接口的作用就是给对象序列化,给对象进行唯一标识。
代码示例:将自定义对象写入到文本文件中并进行读取
自定义Proson对象
import java.io.*;
class Person implements Serializable
{
//该句代码的作用是给对象进行固定的标识,修改成员权限后,依然可以读取文件
public static final long serialVersionUID = 42L;
private String name;
private int age;
Person(String name,int age)
{
this.name = name;
this.age = age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
}
写入和读取的类
import java.io.*;
class ObjectStreamDemo
{
public static void main(String[] args) throws Exception
{
//write();
read();
}
public static void write()throws IOException
{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));
oos.writeObject(new Person("zhangsan",21));
oos.close();
}
public static void read()throws Exception
{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));
Person p = (Person)ois.readObject();
System.out.println(p.getName()+"-----"+p.getAge());
}
}
五、管道流:
PipedInputStream和PipedOutputStream:输入流和输出流进行连接,通常结合多线程进行使用
在读取过程中,如果没有数据写入,则会等待。这种现象称为阻塞
六、RandomAccessFile:可以随机对文件进行读写操作,类中封装了输入流和输出流,并有一个文件指针,实现随机写入和读取
通过getFilePointer获取指针位置,通过seek方法设置指针位置。
七、io一些其他类
DataInputStream和DataOutputStream:用于操作基本数据类型
ByteArrayInputStream和ByteArrayOutputStream:用于操作字节数组
CharArrayReader和CharArrayWriter:用于操作字符数组
StringReader 和StringWriter:用于操作字符串
八、字符编码:使用转换流时,可以指定字符编码
常见字符编码表
ASCII:美国标准信息交换码。
用一个字节的7位可以表示。
ISO8859-1:拉丁码表。欧洲码表
用一个字节的8位表示。
GB2312:中国的中文编码表。
GBK:中国的中文编码表升级,融合了更多的中文文字符号。
Unicode:国际标准码,融合了多种文字。
所有文字都用两个字节来表示,Java语言使用的就是unicode
UTF-8:最多用三个字节来表示一个字符。
编码解码过程
编码就是按照指定的编码表,将字符串转换成字节数组
解码就是按照指定的编码表,将字节数组转换成字符串
练习:
/*
有五个学生,每个学生有3门课的成绩,
从键盘输入以上数据(包括姓名,三门课成绩),
输入的格式:如:zhagnsan,30,40,60计算出总成绩,
并把学生的信息和计算出的总分数高低顺序存放在磁盘文件"stud.txt"中。
分析:定义学生对象,定义学生对象工具类
从键盘录入数据,再存储到文件中,要用到转换流
要存储多个对象,要用到集合,并且要按照总分排序,明确用TreeSet
*/
import java.io.*;
import java.util.*;
//定义学生对象,并实现Comparable接口,让学生具有比较性
class Student implements Comparable
{
private String name;
private int cn,ma,en;
private int sum;
Student(String name,int cn,int ma,int en)
{
this.name = name;
this.cn = cn;
this.ma = ma;
this.en = en;
this.sum = cn+ma+en;
}
public String getName()
{
return name;
}
public int getCn()
{
return cn;
}
public int getMa()
{
return ma;
}
public int getEn()
{
return en;
}
public int getSum()
{
return sum;
}
//复写hashCode方法,返回自定义姓名加年龄的哈希值
public int hashCode()
{
return this.name.hashCode()+sum*39;
}
//复写equals方法,姓名和年龄一致时,就视为同一个对象
public boolean equals(Object obj)
{
if(!(obj instanceof Student))
throw new ClassCastException("类型不兼容");
Student s = (Student)obj;
return this.name.equals(s.getName()) && this.sum==s.getSum();
}
//复写compareTo方法,按照总分排序
public int compareTo(Student s)
{
int a = new Integer(this.sum).compareTo(new Integer(s.getSum()));
if(a==0)
return this.name.compareTo(s.getName());
return a;
}
}
//定义学生工具类
class StuTool
{
//定义一个空比较器,从键盘获取学生信息的方法
public static Set getStudents()throws IOException
{
//直接调用定义比较器的方法,然后比较器参数传入null,提高代码复用
return getStudents(null);
}
//定义一个有比较器参数的方法
public static Set getStudents(Comparator comp)throws IOException
{
//定义字节流缓冲区对象,从键盘录入信息
BufferedReader bfr = new BufferedReader(new InputStreamReader(System.in));
Set stus;
//进行判断,如果比较器为null,就新建一个null比较器的对象
//如果比较器不为空,就新建一个有比较器参数的对象
if(comp==null)
stus = new TreeSet();
else
stus = new TreeSet(comp);
String line =null;
while ((line=bfr.readLine())!=null)
{
if("ok".equals(line))
break;
//将键盘录入的内容,用“,”进行切割,并存储集合
String[] arr = line.split(",");
stus.add(new Student(arr[0],Integer.parseInt(arr[1]),
Integer.parseInt(arr[2]),
Integer.parseInt(arr[3])));
}
bfr.close();
return stus;
}
//将学生信息写入文件的方法
public static void writeToFile(Set stus)throws IOException
{
//定义字符缓冲流兑现
BufferedWriter bfw = new BufferedWriter(new FileWriter("student.txt"));
for(Student s:stus)//用增强for循环将集合中的学生信息取出并写入文件
{
bfw.write("姓名:"+s.getName());
bfw.write("语文:"+s.getCn());
bfw.write("数学:"+s.getMa());
bfw.write("英语:"+s.getEn());
bfw.write("总分:"+s.getSum());
bfw.newLine();
}
bfw.close();
}
}
class StudentInfo
{
public static void main(String[] args) throws IOException
{
Comparator comp = Collections.reverseOrder();
Set stus = StuTool.getStudents(comp);
StuTool.writeToFile(stus);
}
}