------- android培训、java培训、期待与您交流! ----------
IO流(数据流)
|----IO可以处理设备上已有的数据,比如硬盘上的文件,内存中驻留,Socket套接字流的数据等
|----按照数据分:字节流和字符流(1byte = 8bit)
字节流是由字节组成的,字符流是由字符组成的. Java里字符由两个字节组成.字节流是最基本的,
所有的InputStream和OutputStream的子类都是字节流,主要用在处理二进制数据,它是按字节来处理的但实际中很多的数据是文本,
又提出了字符流的概念,它是按虚拟机的encode来处理,也就是要进行字符集的转化。在从字节流转化为字符流时,
实际上就是byte[]转化为String时,public String(byte bytes[], String charsetName)有一个关键的参数字符集编码,通常我们都省略了,那系统就用操作系统默认的lang
|----字节流**抽象**基类:InputStream,OutputStream
|----字符流**抽象**基类:Reader,Writer。只要是字符流就有会绑定字符集
|----这四个基类派生的子类都是以父类名作为子类名的后缀比如:FileInputStream
|----IO中的read方法在输入数据可用、检测到文件末尾或者抛出异常前,此方法一直阻塞。
|----FileWrite
|----FileWrite fw = new FileWrite( "Demo.txt" );该对象一初始化必须明确要处理的文件
//在创建对象时如果指定的绝对路径不存在就会抛出IOException比如k:\\demo.txt,机器没有k盘“\\”转义字符\\代表一个\
文件被创建到指定目录下,如果该目录有同名文件,会将同名文件覆盖
fw.write( "abc" );fw.flush();fw.close();先刷新然后关闭流,如果继续操作对象**引用**会抛出IOException异常
java读写文件调用了本地系统的方法,用完之后要释放资源。
在使用IO流对象的引用时要注意引用是否是打开的,类似于c++判断文件指针是否为空
**字符**流**缓冲区BufferedWriter
|----1. java.io.BufferedReader和java.io.BufferedWriter类各拥有8192字符的缓冲区。当BufferedReader在读取文本文件时,
会先尽量从文件中读入字符数据并置入缓冲区,而之后若使用read()方法,会先从缓冲区中进行读取。如果缓冲区数据不足,
才会再从文件中读取,使用BufferedWriter时,写入的数据并不会先输出到目的地,而是先存储至缓冲区中。如果缓冲区中的数据满了,
才会一次对目的地进行写出。
2. 从标准输入流System.in中直接读取使用者输入时,使用者每输入一个字符,System.in就读取一个字符。为了能一次读取一行使用者的输入,
使用了BufferedReader来对使用者输入的字符进行缓冲。readLine()方法会在读取到使用者的换行字符时,再一次将整行字符串传入。
3. System.in是一个位流,为了转换为字符流,可使用InputStreamReader为其进行字符转换,然后再使用BufferedReader为其增加缓冲功能。
BufferedReader( new InputStreamReader( System.in ) );
|----java在不定义缓冲区时虚拟机也会默认提供一个缓冲区,这个缓冲区和自定义的缓冲区有什么不一样呢?
猜想:是否默认的缓冲区不可以更改缓存数据的大小,而自定义的可以更改大小?
|----使用注意:
缓冲区是为了提高数据的操作效率出现的,因此要先有流对象才能创建缓冲区。
缓冲区只是提高效率,真正执行操作比如write,close都是流对象执行,虽然buffer.write(),前面用了缓冲对象的引用。
用到了缓冲区,就要记得刷新。buffer.flush();
缓冲区的本质就是封装的数组。
|----缓冲区中出现的新方法,
|----BufferedWriter:buffer.newLine(),跨平台换行linux,win都可以,根据操作系统不同,封装\r\n或者\n
|----BufferedReader:buffer.readLine()读一行。包含**该行**内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null
对于文本一行一行的读最方便。
****注意:readLine()不读取换行符
|----readLine的原理
readLine实际上运用了read方法一个一个将数据读入到缓冲区,直到读到\n则将这个一行数据变成字符串返回
之所以一个一个读可能是为了读到\n。
|----装饰设计模式
当想要对已有的对象进行功能增强时,可以定义**类**,将**已有对象**传入,**基于已有的**功能,并提供增强功能
那么自定义的类称为装饰类。装饰类与被装饰类属于同一个体系。通常继承于同一个父类。
装饰类通常会通过构造函数接收被装饰对象,并基于被装饰对象的功能,提供更强的功能。
装饰类因为增强已有对象,具备的功能和已有是相同的(比如读,写),只不过提供了更强的功能,所以装饰类和被装饰
类都是属于一个体系(读写体系)
|----好处:
装饰模式比继承更灵活,避免了继承体系的臃肿。而且降低了类与类之间的关系。
|----设计规则:
java.lang.Object
|----java.io.Writer
|----java.io.BufferedWriter
BufferedWriter(Writer out)
BufferedWriter和被装饰的对象继承同一个父类,接收一个父类对象。那么也可以接收BufferedWriter对象了。
|----LineNumberReader是BufferedReader的子类,也是一个装饰类
|----setLineNumber设置当前行号,设定完后后面的行号都按照设置的行号向下排列
|----getLineNumber得到行号
字节流
|----字节流在写入的时候不刷新而数据直接存入文件中。字符流之所以要刷新,是因为字符要先存储到字符缓冲区
然后查表找到对应的文字(比如中文是两个字节)。字符流的底层还是字节流。
字节流如果没有指定具体缓冲区的话,不需要缓冲,直接将数据写入文件中
|----write( int b ):将指定的字节写入此输出流。write 的常规协定是:向输出流写入一个字节。
****注意:要写入的字节是参数 b 的八个低位。b 的 24 个高位将被忽略。 单个字节读取时返回的是b&0xff
|----int read()从输入流中读取数据的下一个字节。返回 0 到 255 范围内的 int 字节值。如果因为已经到达流末尾而没有可用的字节,则返回值 -1。
在输入数据可用、检测到流末尾或者抛出异常前,此方法一直阻塞。
|----方法阻塞
//read会阻塞,write不会
//,比如fw.write;fw.flush();如果fw的write不阻塞,有可能还没输入完成就刷新了,导致后面的不刷新。
write(byte[] b) "123".getBytes()将字符串转化为字节数组
|----字节流读取方式:
|----1.和字符流相同的读取方式
byte[] ch = new byte[1024];
int len = 0;
while( ( len = r.read() ) != -1 )
{
System.out.println( new String( ch, 0, len ) );
}
|----2.int available() ,返回文件中包含的字节数,可以用这个字节数来建立精确的数组,而不需使用
循环的方式来确定读取的个数。但是如果文件很大这种就不合适,比如几个G的DVD,在内存中建立
一个数组,内存不一定有这么大。
byte[] b = new byte[ r.available() ];
r.read( ch );
|----在数据比较小的情况下用第二种方法,主要还是以第一种为主,建立1024的整数倍,使建立的数组在自己可以
控制的范围内,就算装不下也不会导致内存溢出
|----拷贝图片:用字符流拷贝图片有可能打不开,读取数据去查编码表,如果找不到对应的编码会用相似的编码代替。
FileInputStream fis = new FileInputStream( "内涵.jpg" );
FileOutputStream fos = new FileOutputStream( "内涵图2.jpg" );
byte[] b = new byte[1024];
int len = 0;
while( ( len = fis.read( b ) ) != -1 )
{
fos.write( b, 0, len );
}
***注意拷贝完成之后要看一下原图片与拷贝后的图片大小是否一致。
|----字节流缓冲区
|----int b = bis.read();//读取单个字节
|----****注意:字节流缓冲区在写入时不刷新或者关闭就不会存入
|----键盘录入
class KeyStream
{
public static void main(String[] args) throws IOException
{
InputStream in = System.in;//从键盘上读取(输入流),设备也是流,控制台(cmd)显示器为输出流
StringBuilder sb = new StringBuilder();
int b = 0;
while( true )
{
b = in.read();
if( b == '\r' )
continue;
if( b == '\n' )
{
String s = sb.toString();
if( "over".equals(s) )
break;
System.out.println( s.toUpperCase() );
sb.delete( 0, sb.length() );//打印结束后要记得清空
}
else//这里注意要加上else,要不然打印后会将/n添加进sb中
sb.append((char)b);
}
}
}
|----字节流转换为字符流
|----上述录入的代码和readLine的实现代码差不多,用readLine来读取更方便,但是readLine是字符流中的方法
|----字节流通向字符流的桥梁InputStreamReader。读取时将字节流按照字符集转换成字符。这个对象也有read等方法。FileReader是他的子类
FileReader中的编码表已经固定就是GBK,因此在需要用到别的编码表时需要调用父类方法。
BufferedReader buf = new BufferedReader( new InputStreamReader( System.in ) );
//构造对象时没有指定字符集,将按照默认字符集将读取的字节转换为字符
String line = null;
while( ( line = buf.readLine() ) != null )
{
if( line.equals( "over" ) )
break;
System.out.println( line );
}
buf.close();
|----字符流通向字节流的桥梁OutputStreamWriter。写入时按照字符集将字符转换成字节存入文件中。FileWriter是他的子类
BufferedReader buf = new BufferedReader( new InputStreamReader( System.in ) );
//构造对象时没有指定字符集将按照默认编码表,将字符转换成字节存入文件中
BufferedWriter bw = new BufferedWriter( new OutputStreamWriter( System.out ) );
String line = null;
while( ( line = buf.readLine() ) != null )
{
if( line.equals( "over" ) )
break;
bw.write( line );
bw.newLine();//不要忘记这两步
bw.flush();
}
buf.close();
bw.close();
|----通常涉及到字符编码转换时需要用到转换流
|----字符在存入读取时底层也是字节流的方法,只是字符流在存入时按照一定的规则存和读。比如汉字2需要两个字节才能构成一个汉字
一个字节的话就不行。
|----流操作的基本规律。通过两个明确来完成
|----1.明确源和目的。
|----源:Reader, InputStream.
|----目的:Writer, OutputWriter
|----2.操作的是否是纯文本
|----是,字符流体系。
|----不是,字节流体系。在上面的例子中,键盘源是字节流,但是为了操作方便而将他转换成字符流
|----3.明确体系后再确定具体使用哪个对象
通过设备来区分:
|----源设备:内存,硬盘(FileReader对象),键盘(System.in)
|----目的设备:内存,硬盘(FileWriter对象),控制台( System.out )
|----改变标准输入流和标准输出流
System.setIn( new FileInputStream( "1.txt" ) );
System.setOut( new PrintStream( "2.txt" ) );
|----异常日志信息
public static void main(String[] args)
{
try
{
int[] num = new int[2];
System.out.println( num[3] );
}
catch ( Exception e )
{
try
{
PrintStream ps = new PrintStream( "ExceptionLog.log" );//建立IO对象时有可能发生异常
Date d = new Date();
SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd KK:mm:ss" );//hh是12小时制KK是24小时制
String s = sdf.format(d);
ps.println( s );//打印流中的方法,和System.println一样
System.setOut( ps );
}
catch ( IOException ex )
{
throw new RuntimeException( "建立日志文件失败" );
}
e.printStackTrace( System.out );//重载函数,空参默认是输出到标准输出流
//将信息打印到指定输出流
}
}
File
|----概述:
将文件或者文件夹封装成对象,文件有很多信息比如大小,路径,是否可写,创建时间等等。
这些信息可以抽取出来建立一个类。文件夹也是。这个类的出现弥补了流对象的不足,流对象
只能操作数据,想要操作数据封装的文件信息就用File
|----File f = new File( "a.txt" );在创建对象时文件并没有在硬盘上创建,所以不需要抛出异常,
如果调用了createNewFile()则需要对异常进行处理。
|----file.getName();如果file是一个目录则得到最低一级目录的名称
|----file.createNewFile();创建的是一个文件,如果不带后缀则是一个什么都不带的文件(不是目录)
|----mkdirs既可以创建单级也可以创建多级
|----File封装的文件不存在,可以用输出但是不可以用输入流,输出流在对象不存在时也可以创建
|----格式:
public static void method_2() throws Exception
{
//可以将已有的或者未出现的文件文件夹封装成对象
File f1 = new File( "a.txt" );
File f2 = new File( "d:\\abc\\a.txt" );
//f3和f2比更灵活
File f3 = new File( "d:\\abc", "b.txt" );
File d = new File( "d:\\abc" );
File f4 = new File( d, "c.txt" );
sop( "f1:"+f1 );
sop( "f2:"+f2 );
sop( "f3:"+f3 );
sop( "f4:"+f4 );
//目录分隔符(字段,不是方法),跨平台\\不一定适合别的平台
//File f5 = new File( "d:\\aa\\b\\c\\a.txt" );
************为什么在流打开文件时FileInputStream时用的是单\而在建立File对象时用的是转义呢?
在win下目录分隔符是\而在java中用/可以表示目录分隔符,并且只是单/而如果用\表示需要\\转义
在获取文件时用相对路径,在创建时用绝对路径,File(String path)中path可以是相对也可以是绝对。
相对路径前不加/或者\\,在配置web.xml等文件时写虚拟目录时加/,不要混了
这句话有误,不管加不加/ (\\)只要是正确路径即可,在Servlet中用String path = getServletContext().getRealPath("/1.html");
得到绝对路径时,/代表D:\Program Files// (x86)\apache-tomcat-6.0.20\webapps\Day01\,加上/就将地址分成两段解析也会得到
绝对路径
InputStream in = new FileInputStream("WebRoot/1.html");
InputStream inStream = new FileInputStream("E:/java_project/Workspace/Day01/WebRoot/1.html");
用/代替
File f5 = new File( "d:"+File.separator+"aa"+File.separator+"b"+File.separator+"c"+File.separator+"a.txt" );
sop( f5 );
}
|----方法:
|----创建与删除:
public static void method_3() throws Exception
{
//创建文件,如果已经存在则不创建返回false
File f = new File( "d:"+File.separator+"a.txt" );
f.deleteOnExit();//在虚拟机退出时删除,如果程序运行时抛出异常没有执行到delete语句
//那么文件不会删除,如果用finally那么如果文件处于使用状态也不可以删除
这条语句是在虚拟机终止时,请求删除此抽象路径名表示的文件或目录。
sop( "boolean:"+f.createNewFile() );
sop( f.delete() );
//static File createTempFile(String prefix, String suffix) 创建临时文件,前缀和后缀,如果后缀为空使用.tmp
//static File createTempFile(String prefix,String suffix, File directory)两个方法都是静态的,在指定文件目录创建临时文件
sop( File.createTempFile( "mail", null) );
}
|----创建文件夹boolean mkdir 单级目录 mkdir
boolean mkdirs 多级mulu
|----判断
public static void method_4() throws Exception
{
File f = new File( "123.java" );
//f.createNewFile();//判断之前如果文件不存在则都是假,因此要先判断文件是否存在
//exists() 测试此抽象路径名表示的文件或目录是否存在
f.mkdir();//创建名字为123.java的文件夹,所以带'.'的不一定就是文件,还是要通过isDe
sop( "File boolean:"+f.isFile()+"....."+"Directory boolean:"+f.isDirectory() );
if( f.exists() )//这个方法比较常用,并且很重要,在判断其他条件时,必须先判断该文件对象封装的内容是否存在
{
sop( "boolean:"+f.canExecute() );
sop( "boolean:"+f.canWrite() );
sop( "boolean:"+f.canRead() );
}
//int compareTo(File pathname) 按字母顺序比较两个抽象路径名
// isHidden()是否隐藏,java访问不了一些系统文件,这些文件一般是隐藏的,因此不要访问一些被隐藏的文件
File f1 = new File( "abc" );
f1.mkdir();//单级
File f2 = new File( "abc\\vb\\vc\\java" );
f2.mkdirs();//多级
File f3 = new File( "d:\\123\\456\\678" );
//这个目录没有创建但是也可以判断,不论文件或者文件夹是否存在
sop( "Absolute:"+f3.isAbsolute() );//判断是否是绝对路径
}
|----获取
public static void method_5() throws Exception
{
File f = new File( "abcd.txt" );
//获取路径信息不依赖于文件是否存在
sop( "name:"+f.getName() );//得到文件的名字,带后缀,是字符串
sop( "path:"+f.getPath() );//getPath()方法,对象建立时,参数中怎么写就怎么获得
sop( "Absolute:"+f.getAbsolutePath() );//获得文件的绝对路径,他的返回值有两个String && File其中File就是将
//绝对路径封装成一个文件对象,两者可以互相转换File用toString()方法可以得到路径的字符串形式
sop( "parent:"+f.getParent() );//返回父目录,如果文件在封装时没有写父目录则返回null
File f1 = new File( "d:"+File.separator+"abc"+"a.txt" );
sop( "parent:"+f1.getParent() );
File f2 = new File( "abc\\c.txt" );
sop( "parent:"+f2.getParent() );//返回abc
f2.createNewFile();
FileWriter fw = new FileWriter( "abc\\c.txt" );
fw.write( "abcdefg" );
fw.close();
sop( "length:"+f2.length() );//比InputStream中的 int available() 读取的数据更大
//最后一次修改时间,记录我最后一次修改时间,下次打开时如果发现最后一次修改时间和我记录的不一致则说明有人动过
sop( f2.lastModified() );
//length()与lastModified()得到的都是long型
//将一个文件移动到另一个文件所在地并将名称改为目的文件的名称
//boolean renameTo(File dest) 注意目标是一个File对象,如果目标路径文件已经存在则移动失败
//将abc\\c.txt的文件移动到默认目录,并将名字改为f3封装的文件名称,f3文件如果已经存在则移动失败
//File f4 = new File( "abc\\d.txt" );
File f3 = new File( "ll.txt" );
sop( "boolean:"+f2.renameTo( f3 ) );//如果f2不存在也会移动失败
}
|----获取根目录,获取目录下文件列表
public static void method_6()
{
File[] files = File.listRoots();//返回当前机器的根目录C:\\ D:\\。。。
for( File f : files )
{
sop( f );
}
File f1 = new File( "c:\\" );
String[] names = f1.list();//得到当前目录下的所有文件,f1中封装的必须是文件目录并且该目录要存在
//如果封装的是一个文件则返回null
for( String name : names )
{
sop( name );
}
}
|----获取文件列表,返回是File[]将上面方法得到的文件名封装到File中,可以得到name,length,path等,实际开发用的比较多
public static void method_8()
{
File f = new File( "c:\\" );
File[] files = f.listFiles();
for( File f1 : files )
{
sop( "name:"+f1.getName()+"...length:"+f1.length() );
}
}
|----用递归方法获得目录中所有文件
public static void method_8( String dir )
{
File f = new File( dir );
File[] files = f.listFiles();
for( File f1 : files )
{
if( f1.isDirectory() && !f1.isHidden() )//注意要避免隐藏文件,删除时不需要
{
method_8( f1.getAbsolutePath() );
}
else
sop( "name:"+f1.getName()+"...length:"+f1.length() );
}
}
|----文件打印层级
public static void method_8( String dir, int num )
{
sop( getLevel( num ) + dir );
File f = new File( dir );
File[] files = f.listFiles();
for( File f1 : files )
{
if( f1.isDirectory() )
{
num++;
method_8( f1.getAbsolutePath(), num );
}
else
sop( " "+getLevel( num )+"name:"+f1.getName()+"...length:"+f1.length() );
}
}
public static String getLevel( int num )
{
StringBuilder sb = new StringBuilder();
sb.append( "|--" );
for( int i = 0; i < num; i++ )
{
sb.insert( 0, " " );//前插入四个空格与上一级的文件差2个空格
}
for( int x = 0; x < num; x++ )
{
sb.append("--");
}
return sb.toString();
}
|----文件过滤
public static void method_7()
{
//public String[] list(FilenameFilter filter)返回文件目录中满足过滤器的文件
//FilenameFilter 是一个接口,其中只有boolean accept(File dir, String name) 方法
File files = new File( "c:\\" );
String[] names = files.list( new FilenameFilter()
{
public boolean accept( File dir, String name )//dir就是对象中指定的目录,name就是目录下所有文件名
{
return name.endsWith( ".bmp" );
}
});
for( String s : names )
{
sop( s );
}
}
|----文件删除
public static void method_9( File files )
{
File[] names = files.listFiles();
for( File name : names )
{
if( name.isDirectory() )//
{
method_9( name );
}
//else//这里用else,删除当前目录在函数执行结束后删除
//如果不用else则要下面的函数
name.delete();
}
//files.delete();
}
public static void method_99( File files )
{
method_9( files );
files.delete();
}
|----创建java文件列表
class JavaList
{
public static void main(String[] args) throws IOException
{
File[] files = File.listRoots();
for( File dir : files )
{
if( dir.toString().equals( "C:\\" ) )//C盘禁止访问,返回空指针
continue;
List<File> l = new ArrayList<File> ();
fileToList( dir, l );//将符合条件的文件存入列表中
writeToFile( l, new File( dir, "当前盘符下的avi文件列表.txt" ) );//将列表写入文件
}
}
public static void fileToList( File dir, List<File> l )
{
File[] names = dir.listFiles();
for( File name : names )
{
if( name.isDirectory() && !name.isHidden() )
{
fileToList( name, l );
}
else
{
if( name.getName().endsWith( ".avi" ) )
{
l.add( name );
}
}
}
}
public static void writeToFile( List<File> l, File fileName ) throws IOException
{
BufferedWriter buf = null;
try
{
buf = new BufferedWriter( new FileWriter( fileName ) );
for( File f : l )
{
buf.write( f.getAbsolutePath() );
buf.newLine();
buf.flush();
}
}
catch ( IOException e )
{
throw e;
}
finally
{
try
{
if( buf != null )
buf.close();
}
catch ( IOException e )
{
throw new RuntimeException( "关闭文件失败" );
}
}
}
}
Properties
|----概述:
Properties 是HashTable的子类,也就是他具有map集合的特点,里面存储的键值对都是字符串
是集合与IO相结合的集合容器。
该对象的特点,可以用于键值对形式的配置文件(配置文件要放到硬盘上,而不能放到内存中)
|----应用:
public static void getProperties()
{
//打印属性列表的方法有三种
Properties prop = System.getProperties();
Set<String> names = prop.stringPropertyNames();//得到键的集合
for( String name : names )
{
System.out.println( name+":"+prop.getProperty( name ) );
}
}
public static void readProperties() throws IOException//load方法的原理
{
BufferedReader fr = new BufferedReader( new FileReader("info.txt") );
Properties prop = new Properties();
String line = null;
while( (line = fr.readLine()) != null )
{
String[] arr = line.split( "=" );//文件中的键值对以=或者:链接
prop.setProperty( arr[0], arr[1] );
}
prop.list( System.out );//list(PrintStream out) 将属性列表输出到指定的输出流。
}
public static void method_1() throws IOException
{
Properties prop = new Properties();
//load(Reader reader) 1.6新方法,之前都要字节流
FileReader fr = new FileReader( "info.txt" );
//prop.load( new FileReader( "info.txt" ) );//将文件中的键值链接改成:也能读取,在留作为参数时,建立匿名对象,流怎么关闭?
prop.load( fr );
prop.setProperty( "xiaoming", "55" );//只是在内存中改的,文件没变化,文件要用下面的语句进行存储
//store(Writer writer, String comments)
//prop.store( new FileWriter( "info.txt"), "xiaoming" );//第二个参数为注释信息以#或者!作为注释行的开头
FileWriter fw = new FileWriter( "info.txt" );
prop.store( fw, "hhh" );
prop.list( System.out );
fr.close();
fw.close();
}
|----练习,规定软件使用次数
软件使用次数不能保存在内存中,要保存到文件中。
class RuntimeCount
{
public static void main(String[] args) throws IOException
{
code();
}
public static void code() throws IOException
{
Properties prop = new Properties();
File f = new File( "count.ini" );//在操作文件时将文件封装成File对象,操作很方便。
//凡是操作File的流对象都很重要。
if( !f.exists() )
f.createNewFile();
FileInputStream fis = new FileInputStream( f );
int count = 0;
prop.load( fis );
String value = null;
if( ( value = prop.getProperty( "time" ) ) != null )
{
count = Integer.parseInt( value );
if( count >= 5 )
{
System.out.println( "使用次数已到" );
return;
}
}
count++;
prop.setProperty( "time", count+"" );//数字加上""变成字符串
FileOutputStream fos = new FileOutputStream( f );
prop.store( fos, "" );
fis.close();
fos.close();
}
}
------- android培训、java培训、期待与您交流! ---------- 详细请查看:http://edu.csdn.net/heima/