java的I/O学习记录(以及一些其他知识点)

JAVA的I/O


1 I/O的基础知识
    1.1 流
    Java程序通过流执行I/O。
    流是一种抽象,要么产生信息,要么使用信息。
    流通过java的I/O系统链接到物理设备。
    所有流的行为方式都是相同的,尽管与他们连接的物理设备是不同的。
    因此,可以为任意类型的设备应用相同的I/O类和方法。
    这意味着可以将许多不同类型的输入——磁盘文件、键盘或网络socket抽象为输入流。
    与之对应,输出流可以引用控制台、磁盘文件或网络连接。
    流是处理输入/输出的一种清晰方式,例如,代码中的所有部分都不需要理解键盘和网络之间的区别。
    流是Java在由java.io包定义的类层次中实现的。
注意:除了在java.io包中定义的基于流的I/O外,Java还提供了基于缓冲和基于通道的I/O,
它们实在java.nio及其子包中定义的。


1.2 字节流和字符流
·字节流为处理字节的输入和输出提供了方法。例如,当读取和写入二进制数据时,使用的就
是字节流。
·字符流为处理字符的输入和输出提供了方便的方法。它们使用Unicode编码,所以可以被
国际化。
·在某些情况下,字符流比字节流高效。
·在最底层,所有I/O仍然时面向字节的。基于字符的流只是为处理字符提供了一种方便
和高效的方法。


1.2.1 字节流类
·字节流时通过两个类层次定义的。在顶级时两个抽象类:InputStream和OutputStream。
·每个抽象类都有几个处理各种不同设备的具体子类,例如,磁盘文件、网络连接甚至内存缓冲区
·抽象类InputStream和OuputStream定义了其他流类实现的一些关键方法。其中最重要的两个
方法是read()和write(),这两个方法分别读取和写入字节数据。
每个方法都有抽象形式,派生的流类必须重写这两个方法。


1.2.2 字符流类
·字符流是通过两个类层次定义的。在顶层是两个抽象类:Reader和Writer。这两个抽象类处理
Unicode字符流。Java为这两个类提供了几个具体的子类。
·抽象类Reader和Writer定义了其他几个流类实现的重要方法。最重要的两个方法是read()和
write(),这两个方法分别读取和写入字符数据。每个方法都有抽象形式,派生的流类必须实现
这两个方法。


1.3 预定义流
·所有Java程序都自动导入java.lang包。这个包定义了System类,该类封装了运行时环境的
某些方法。
·System还包含3个预定义的流变量:in、out、err。这些变量在System类中被声明为public、
static和final。这意味着,程序中的其他任何部分都可以使用他们,而不需要引用特定的
System对象。
····System.out引用标准的输出流,默认情况下是控制台。(PrintStream类型的对象)
····System.in引用标准的输入流,默认情况下是键盘。(InputStream类型的对象)
····System.err引用标准的错误流,默认情况下也是控制台。(PrintStream类型的对象)
但是这些流可以本重定向到任何兼容的I/O设备。
····这3个都是字节流,尽管它们通常用于从控制台读取字符以及向控制台写入字符。可以看出
,如果愿意的话,可以在基于字符的流中封装这些流。


2 读取控制台输入
 使用面向字符的流可以是程序更容易国际化和维护。
·BufferedReader支持缓冲的输入流。其构造如下所示:
 BufferedReader(Reader inputReader)
·其中InputReader是与即将创建的BufferedReader实例链接的流。
·Reader是抽象类,InputStreamReader是它的一个具体子类,该类将字节转换成字符。
·为了获取与System.in链接的InputStreamReader对象,使用下面的构造函数:
 BufferReader br=new BufferReader(new InputStreamReader(System.in));
 br就是通过System.in与控制台链接的基于字符的流。
 
2.1 读取字符 
为了从BufferReader对象读取字符,需要使用read()方法。
int read() throws IOException
每次调用read()方法都会从输入流读取一个字符,并将之作为整形值返回。


2.2 读取字符串
·为了从键盘读取字符串,可以使用BufferedReader类的readLine()方法,该方法的一般形式如下:
String readLine() throws IOException


3 向控制台写输出
·使用print()和println()是向控制台写输出的最容易方式,这两个方法是由PrintStream
类(也就是System.out引用的对象的类型)定义的。System.out尽管是字节流,但它对于简单的程序仍然可以使用。
·因为PrintStream是派生自OutputStream的输出流,所以还实现了低级的write()方法。
以此可以使用write()向控制台输出。PrintStream定义的最简单形式的write()方法如下所示:
void write(int byteval)
 该方法输出由byteval指定的字节。尽管byteval被声明为整数,但是只有低8位被输出。
·通常不会使用write()执行控制台输出(尽管在某些情况下这么做是有用的),因为print()
和println()确实更容易使用。


4 PrintWriter类
尽管使用System.out向控制台输出是可以接受的,但是最好将其用于调试或用于示例程序。
对于实际的程序,使用Java向控制台输出的推荐方法是通过PrintWriter流。
··PrintWriter类定义了几个构造函数,其中之一如下所示:
  PrintWriter(OutputStream outputStream,boolean flushingOn)
  其中,outputStream是OutputStream类型的对象,flushingOn控制Java是否在每次调用
  println()方法时刷新输出流。如果flushingOn为true,就自动刷新;如果为false,那么
  不会自动刷新。
··PrintWriter支持print()和println()方法。因此,可以使用与System.out相同的方式使用
它们。如果参数不是简单类型,PrintWriter方法会调用对象的toString()方法,然后输出结果。
··为了使用PrintWriter向控制台输出,为输出流指定System.out,然后在每个新行之后刷新流。
例如:
PrintWriter pw =new PrintWriter(System.out,true);
上面的代码创建了一个链接到控制台输出的PrintWriter.
使用PrintWriter可以使实际的应用程序更容易国际化。


5 读/写文件
··对于读写文件,最常用的两个流类时FileInputStream和FileOutputStream,这两个类创建与
文件链接的字节流。
··为了打开文件,只需要简单的创建这些类中某个类的对象,指定文件名作为构造函数的参数
即可。构造函数如下所示(其中的一种):
FileInputStream(String fileName) throws FileNotFoundException
FileInputStream(String fileName) throws FileNotFoundException
·其中fileName是指定希望打开的文件的名称。
·当创建输入流时,如果文件不存在,就会抛出FileNotFoundException异常。
·对于输出流,如果不能打开文件或不能创建文件,也会抛出FileNotFoundException异常。
·FileNotFoundException是IOException的子类。当打开输出文件时,先前存在的同名文
件将被销毁。
注意:对于存在安全管理器的情况,如果在试图打开文件时发生安全性违规,那么文件类
FileInputStream和FIleOutputStream会抛出SecurityException异常。
默认情况下,通过java运行的应用程序不会使用安全管理器。


··文件使用完之后必须关闭。关闭文件时通过close()方法完成的,FileInputStream和FileOutputStream
都实现了该方法。该方法声明如下:
void close() throws IOException
··关闭文件会释放为文件分配的系统资源,从而允许其他文件使用这些资源。
关闭文件失败会导致“内存泄漏”,因为未使用的资源没有被释放。
注意:从jdk7开始,close()方法是由java.lang包中的AutoCloseable接口指定的。java.io包
中的Closeable接口继承了AutoCloseable接口。所有流类都实现了这两接口,包括FileInputStream
和FileOutputStream。


··为了读取文件,可以使用在FileInputStream中定义的read()版本。如下:
int read() throws IOException
··每次调用read()方法时,都会从文件读取一个字节,并作为整形值返回。当达到
文件末尾时,read()方法返回-1。该方法可能抛出IOException异常。
··为了向文件中写入内容,可以使用FileOutputStream定义的write()方法,该方法的最简单
形式如下:
void write(int byteval) throws IOException


6 自动关闭文件
自动资源管理基于try语句的扩展形式,它的一般形式如下所示:
try(resource-specification){
//use the resource
}
try语句中声明的资源被隐式声明为final。这意味着,在创建资源变量后,
不能将其他资源赋给该变量。资源的作用域局限于带资源的try语句。


其中,resource-specification是用来声明和初始化资源(例如文件流)的语句。
该语句包含一个变量声明,在该变量声明中使用将被管理的对象引用初始化变量。
当try代码块结束时,自动释放资源。对于文件,这意味着会自动关闭文件(
因此,不需要显示地调用close()方法)
···只有对于哪些实现了AutoCloseable接口地资源,才能使用带资源地try语句。
AutoCloseable接口由java.lang包定义,该接口定义了close()方法。java.io包中的Closeable
接口继承自AutoCloseable接口。所有流类都实现了这两个接口。因此,当使用流时——包括文件流
,可以使用带资源的try语句。


7 applet(不用看)
8 transient和volatile修饰符
··如果将实例变量声明 为transient,那么当存储对象时,实例变量的值将不需要永久保存。
clas T{
  transient int a;//will not persist
  int b;//will persist
}
··修饰符volatile告诉编译器,由volatile修饰的变量可以被程序的其他部分随意修改。(
多线程)


9 使用instanceof运算符
运算符instanceof的一般形式如下: objref instanceof type


10 strictfp
通过使用strictfp修饰类、方法或接口,可以确保采用与Java以前版本使用的相同方式
执行浮点计算(以及所有截断)。如果使用strictfp对类进行了修饰,那么类中的所有方法也将自动使用stritfp进行修饰。
strictfp class MyClass{//...
//坦白地说,大多数程序员永远不需要使用strictfp只影响很少地一类问题。


11 本地方法
native用于声明本地代码方法。
public native int meth();
将c代码集成到java程序中的机制称为Java本地接口(Java Native Interface,JNI)


javah -jni NativeDemo(这个命令将生成文件名为NativeDemo.h的头文件)


本地方法存在的问题
1、潜在的安全性风险 因为本地方法执行实际的机器代码,所以可以访问宿主系统的
所有部分。也就是说,本地代码可以离开Java执行环境。这有可能导致感染病毒。
2、可以执行丢失 本地代码因为包含在DLL中,所以必须存在于执行Java程序的机器上。
而且,因为使用本地方法依赖于CPU和操作系统,所以DLL本身时不可移植的。因此,使用本地
方法的Java程序只能运行于已经安装了兼容DLL的机器上。


13 使用assert
assert 是用在开发期间创建断言,断言是在程序执行期间应当为true的条件。
断言经常用于证实在测试期间实际遇到了某些期望的条件。在发布代码中通常不使用它们。
assert关键字有两种形式:
··第一种:assert condition;
其中,condition是一个求值结果必须为布尔型的表达式。如果结果为true,那么断言为真,
不会发生其他动作。如果条件为false,那么断言失败并抛出默认的AssertionError对象。
··第二种:assert condition:expr;
expr是传递给AssertionError构造函数的值。这个值被转换成相应的字符串格式,并且如果
断言失败,将会显示该字符串。典型地,将为expr指定一个字符串,不过也可以指定任何非
void表达式,只要定义了合理地字符串转换即可。


·启用和禁用断言
-ea 启用断言
-da 禁用所有断言。
通过在-ea或-da选项后面指定包的名称,并在后面跟上三个点,可以启用或禁用指定包(及其
所有子包)中地断言。例如:-ea:MyPack...(启用MyPack包中的断言)
 -da:MyPack...(禁用MyPack包中的断言)
还可以为-ea或-da选项具体指定某个类。例如:
-ea:AssertDemo(启用AssertDemo类中的断言)


13 静态导入
静态导入扩展了import关键字的功能。
通过在import后面添加static关键字,可以使用import语句导入类或接口的静态成员。
静态导入语句有两种形式。
第一种形式:引入单个名称:import static 完整包名.成员名称;
第二种形式:导入给定类或接口的所有静态成员,如下所示:
import static java.lang.Math.*;


14 通过this()调用重载的构造函数
this()最适合用于包含大量初始化代码的构造函数,而不适合用于那些只简单设置少量域
变量值的构造函数。
限制:1、在调用this()时不能使用当前类的任何实例变量。
      2、在同一个构造函数中不能同时使用super()和this(),因为它们都必须是构造
      函数中第一条语句。


15 紧凑API配置文件
jdk8新增了一项功能,将API库的子集组织成所谓的紧凑配置文件。它们分别叫做compact1、
compact2、compact3.每个配置文件包含的一个子集。此外,compact2包含整个compact1,
compact3包含整个compact2.
因此,每个配置文件以前一个配置文件为基础。紧凑配置文件的优势在于,用不到完整库的应用
不需要下载整个库。


编译程序时,使用-profile选项可以确定程序是否使用了紧凑配置文件中定义的API。
javac -profile profileName programName
其中,profileName指定了配置文件,必须是compact1、compact2或compact3
例如:
javac -profile compact2 Test.java
这里指定了compact2配置文件。如果Test.java包含不属于compact2的API,就会引发
编译错误。

猜你喜欢

转载自blog.csdn.net/qq_15720911/article/details/77995359
今日推荐