1、JDK7新增的一个对I/O和NIOAPI有重大影响的特性是java.lang.AutoCloseable接口。目前大多数java.io类都以实现这个接口来支持try-with-resource。Java.nio.file.Files类可以使用Files类创建和删除文件(Files.createFile\delete)与目录(createDirectory\deleteIfexists)、检查文件是否存在(exists)、读取(readAllBytes\readAlllines)和写入文件(write)。但Files读取和写入文件的支持只适用于小文件,对于更大的文件和新增的功能需要使用流。流有四种基本的抽象类:InputStream(字节流)、OutputStream(字节流)、Reader(字符流)和Writer(字符流)。并且由于InputStream和OutputStream没有缓存且读取和写入都是以字节为单位,可以BufferInputStream bis=new BufferInputStream(Files.newInputStream(path,option))来包装。
byte[]readData=new byte[1024];
InputStream inputStream=Files.newInputStream(path1,StandardOpenOption.READ);
OutputStream outputStream=Files.newOutputStream(path2,StandardOpenOption.WRITE);
int i=inputStream.read(readData);
while(i!=-1){
outputStream.Write(readData,0,i);
i=inputStream.read(readData);
}
2、Writer:writer处理的是字符,除了有write(char[])字符外还有字符串参数的Writer(String text 。。。。。)。其子类OutputStreamWriter是字符流与字节流之间的桥梁,可以指定字符集将字符转为字节流。
public class OutputStreamWriterTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
char[] chars=new char[2];
chars[0]='\u4F60';
chars[1]='\u597D';
Path path=Paths.get("D:\\File\\ow.txt");
Charset simpleChineseCharset=Charset.forName("GB2312");
try (OutputStream outPutStream=Files.newOutputStream(path, StandardOpenOption.CREATE);
OutputStreamWriter writer=new OutputStreamWriter(outPutStream, simpleChineseCharset)){
writer.write(chars);
} catch (Exception e) {
// TODO: handle exception
}
}
}
3、Reader:处理的是字符流。其子类InputStreamReader用指定的字符集将字节转为字符。
4、BufferedReader:
优点:1、封装另一个Reader,并提供一个可以提高性能的缓存。
2、提供一个readLine来读取文本行,返回字符串,如到达末尾返回null。
5、随机访问文件:对于文件的随机访问,Java提供了几个类,第一个是java.io.RandomAccessFile类,它易于使用但现在过时了。第二个是java.nio.channels.SeekableByteChannel接口,它用于新开发的应用程序中。SeekableByteChannel既可以执行读出操作,也可以执行写入操作。使用Files类的newByteChannel方法可以得到SeekableByteChannel的实现:public static java.nio.channels.SeekableByteChannel newByteChannel(Path path,Opention… options),其方法需要传递ByteBuffer,int write(java.nio.ByteBuffer buffer)和int read(java.nio.ByteBuffer buffer)
public class SeekableByteChannelTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
ByteBuffer buffer=ByteBuffer.allocate(12);
System.out.println(buffer.position());
buffer.putInt(10);
System.out.println(buffer.position());
buffer.putLong(1234567890L);
System.out.println(buffer.position());
buffer.rewind();
System.out.println(buffer.getInt());
System.out.println(buffer.getLong());
buffer.rewind();
System.out.println(buffer.position());
Path path=Paths.get("C:/temp/channel");
System.out.println("--------------------");
try (SeekableByteChannel byteChannel=Files.newByteChannel(path,StandardOpenOption.CREATE,StandardOpenOption.READ,StandardOpenOption.WRITE);
){
System.out.println(byteChannel.position());
byteChannel.write(buffer);
System.out.println(byteChannel.position());
ByteBuffer byteBuffer3=ByteBuffer.allocate(40);
byteChannel.position(0);
byteChannel.read(byteBuffer3);
byteBuffer3.rewind();
System.out.println("int:"+byteBuffer3.getInt());
System.out.println("long:"+byteBuffer3.getLong());
System.out.println(byteBuffer3.getChar());
} catch (Exception e) {
// TODO: handle exception
}
}
}
6、对象序列化:即对象持久化,使用两个流:ObjectOutputStream是OutputStream的一个子类,而ObjectInputStream是InputStream的一个子类。为了使对象可以序列化,它们的类必须实现java.io.Serializable接口。这个标记会告诉JVM实现一个类的实例属于某一个类型。如果序列化的对象包含其他对象,那么被包含对象的类也必须要实现Serializable来使被包含的对象可以序列化。对于一些不想被序列化的域可以:transient public int age;来实现。
public class ObjectSeriallizationTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Path path=Paths.get("C:/temp/objectOutput");
Customer customer=new Customer(1,"jack","12 West Cost","basketball","red");
try (OutputStream outputStream=Files.newOutputStream(path, StandardOpenOption.CREATE);
ObjectOutputStream oos=new ObjectOutputStream(outputStream)){
oos.writeObject(customer);
oos.writeObject("Customer Info");
} catch (IOException e) {
// TODO: handle exception
e.printStackTrace();
}
try (InputStream inputStream=Files.newInputStream(path, StandardOpenOption.READ);
ObjectInputStream ois= new ObjectInputStream(inputStream)){
Customer customer1=(Customer)ois.readObject();
System.out.println("First Object");
System.out.println(customer1.id);
System.out.println(customer1.name);
System.out.println(customer1.address);
System.out.println(customer1.habit);
System.out.println(customer1.likeColor);
System.out.println();
System.out.println("Second object");
String info=(String)ois.readObject();
System.out.println(info);
} catch(ClassNotFoundException ex){
System.out.println("ClassNotFound"+ex.getMessage());
}
catch (IOException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
对于自定义序列化可以使用继承Externalizable接口来实现
对象序列化写的比较好的对象序列化
7、嵌套类和内部类:嵌套类是一个声明在另一个类或接口内部的类,它有两种类型:静态内部类和非静态嵌套类,非静态嵌套类也称为内部类。内部类主要有:成员内部类、局部内部类、匿名内部类;
相对于顶层类而言,嵌套类就像特殊的类成员,比如方法和域,例如可以有public、protected、default、private修饰符区别于顶层类。由于静态嵌套类区别于内部类:
》静态嵌套类可以有静态成员,内部类不可以有。
》内部类可以访问外层的静态和非静态成员,包括它的private成员,而静态嵌套类只能访问外层类的静态成员。
》无需先实例化静态嵌套类的外层类就可以实例化静态嵌套类,而实例化内部类则必须先实例化外层类。
内部类的优点:
》内部类可以使用外层类的所有成员包括private(并非直接访问 而是通过内部类的方法返回取得)
》内部类可以帮你完全隐藏类的实现。
- 成员内部类:它直接定义在另一个类或接口声明中,只有在引用它的外层实例时,才可以创建成员内部类的实例。在外层类创建内部类时可以直接像普通类创建一样(调用内部类的构造器),但从外层类的外部创建内部类实例需要先实例外层类:OuterClassName1.InnerClassName inner=OuterClassName1.new InnerClassName();
- 局部内部类:它是一个内部类,按照定义来说它不是外层类的成员类(因为它的声明不是直接在外层类的声明中),局部类可以生命在任何代码块中,它的作用域是这个块内。可以是方法、if、while块中声明局部类。
public Logger getLogger(){
class LoggerImpl implements Logger{
@Override
public void log(String message) {
// TODO Auto-generated method stub
System.out.println(appStartTime+":"+message);
}
}
return new LoggerImpl();//在方法内声明 且需要创建并返回
}
局部类不仅可以访问外层类的成员,还可以访问局部变量但局部变量必须是final(jdk1.8之前局部变量必须要finnal修饰,但jdk1.8不显式写也可以 效果等同final 主要是局部变量会随着局部方法结束而结束)。
package com.lr.internalclass;
interface PrefixLogger{
public void log(String message);
}
public class LocalClassTest2 {
public PrefixLogger getLogger( final String prefix){
class LoggerImpl implements PrefixLogger{
public void log(String message){
System.out.println(prefix+":"+message);
}
}
return new LoggerImpl();
}
public static void main(String[] args) {
LocalClassTest2 test=new LocalClassTest2();
PrefixLogger logger=test.getLogger("DEBUG");
logger.log("local class example");
}
}
- 匿名内部类:可以用来编写接口实现,还可以扩展抽象类或具体类来创建匿名内部类。
重点:JVM并不知道嵌套类的概念,是编译器在努力将内部类编译成顶层类,把外层类和内部类名称结合在一起,两者之间用美元符号隔开,形成名称:
public class Outer{
class Inner{
}
}
会被编译成两个类:Outer.class和Outer Inner.class而匿名内部类则编译器会用随机数字给它们取一个名称:Outer I n n e r . c l a s s 而 匿 名 内 部 类 则 编 译 器 会 用 随 机 数 字 给 它 们 取 一 个 名 称 : O u t e r 1.class,Outer$2.class. 嵌套类被实例化,这个实例就是堆中的一个独立的对象,实际上它并不在外层类对象内部。但对内部类而言,他们有对外层类对象的自动引用。这个引用在静态嵌套类实例中并不存在,因为静态嵌套类不能访问外层类的实例成员。
内部类访问外层类实例成员是因为编译器修改了内部类的构造器:
public Inner(int value);
变为
public Inner(Outer outer,int value);
所以随之而来的实例化时参数也是默认被传进去的。