Java输入输出流
问题及答案来源自《Java程序员面试笔试宝典》第四章 Java基础知识 4.7输入输出流
1、Java IO流的实现机制是什么?
流可以分为两大类:字节流和字符流。
- 字节流以字节为(8bt)单位,字符流以字符为(16bit)单位。
- 字节流包括两个抽象类:InputStream(输入流)和OutputStream(输出流)
- 字符流包括两个抽象类:Reader(输入流)和Writer(输出流)
字节流和字符流主要区别:
字节流在处理输入输出时不会用到缓存,而字符流用到了缓存
常见笔试题 - Java中有几种类型的流?
常见的流有两种:字节流和字符流,字节流继承于InputStream(输入流)和OutputStream(输出流),字符流
继承于Reader(输入流)和Writer(输出流),在java.io包中还有许多其他的流,流的主要作用是为了改善程序性能并且使用方便
2、管理文件和目录的类是什么?
Java中管理文件和文件夹的类是File类,File类常用方法:
- File(String pathname):根据指定路径创建File对象
- createNewFile():若目录或文件存在返回false,否则创建文件或文件夹
- delete():删除文件或文件夹
- isFile():判断这个对象表示的是否是文件
- isDirectory():判断这个对象表示的是否是文件夹
- listFiles():若对象表示目录,则返回目录中所有文件的File对象
- mkdir():根据当前对象指定的路径创建目录
- exists():判断对象对应的文件是否存在
常见笔试题 - 如何列出某个目录下的所有目录和文件?
详情见代码如下:
1 import java.io.File; 2 3 public class useListFiles { 4 public static void main(String[] args) { 5 File file = new File("D:\\test"); 6 // 判断目录是否存在 7 if(!file.exists()){ 8 System.out.println("directory is empty!"); 9 return; 10 } 11 File[] fileList = file.listFiles(); 12 for(int i=0; i<fileList.length; i++){ 13 // 判断是否为目录 14 if(fileList[i].isDirectory()){ 15 System.out.println("directory is: " + fileList[i].getName()); 16 } else{ 17 System.out.println("file is: " + fileList[i].getName()); 18 } 19 } 20 } 21 }
3、Java Socket是什么?
什么是Socket:
网络上的两个程序通过一个双向的通信连接实现数据的交换,这个双向链路的一端称为一个Socket
Socket也称为套接字,可以用来实现不同虚拟机或不同计算机之间的通信
Java中的Socket:
- 面向连接的Socket通信协议(TCP)
- 面向无连接的Socket通信协议(UDP)
- 任何一个Socket都是由IP地址和端口号唯一确定的
基于TCP的通信过程:
首先Server(服务器)Listen(监听)指定的某个端口(建议使用大于1024的端口)是否有连接请求;
然后Client(客户端)向Server端发出连接请求(Connect);最后Server端向Client端返回Accept消息(接受)
一个连接就建立起来了,会话立即产生,此时Server端和Client端都可以通过Send、Write等方法与对方通信
socket生命周期三个阶段:
- 打开Socket
- 使用Socket收发数据
- 关闭Socket
在Java中,可以使用ServerSocket来作为服务器端,Socket作为客户端来实现网络通信
常见笔试题 - 用Socket实现客户端和服务端的通信,要求客户发送数据后能返回相同的数据
首先创建一个名为Server.java的服务器端程序:
1 import java.io.*; 2 import java.net.*; 3 4 public class Server { 5 public static void main(String[] args) { 6 BufferedReader br = null; 7 PrintWriter pw = null; 8 try{ 9 ServerSocket server = new ServerSocket(8888); 10 Socket socket = server.accept(); 11 // 获取输入流 12 br = new BufferedReader(new InputStreamReader(socket.getInputStream())); 13 // 获取输出流 14 pw = new PrintWriter(socket.getOutputStream(), true); 15 String s = br.readLine(); // 获取接收的数据 16 pw.println(s); // 发送相同的数据给服务端 17 } catch(Exception e){ 18 e.printStackTrace(); 19 } finally{ 20 try{ 21 br.close(); 22 pw.close(); 23 } catch(Exception e){ 24 e.printStackTrace(); 25 } 26 } 27 } 28 }
然后创建一个名为Client.java的客户端程序:
1 import java.io.*; 2 import java.net.*; 3 4 public class Client { 5 6 public static void main(String[] args) { 7 BufferedReader br = null; 8 PrintWriter pw = null; 9 try{ 10 Socket socket = new Socket("localhost", 8888); 11 // 获取输入流与输出流 12 br = new BufferedReader(new InputStreamReader(socket.getInputStream())); 13 pw = new PrintWriter(socket.getOutputStream(), true); 14 // 向服务器发送数据 15 pw.println("Hello"); 16 String s = null; 17 while(true){ 18 s = br.readLine(); 19 if(s!=null){ 20 break; 21 } 22 } 23 System.out.println(s); 24 } catch (Exception e) { 25 e.printStackTrace(); 26 } finally{ 27 try{ 28 br.close(); 29 pw.close(); 30 } catch(Exception e){ 31 e.printStackTrace(); 32 } 33 } 34 } 35 }
最后启动服务器端程序,然后运行客户端程序,客户端会把从服务器转发过来的数据都打印出来
4、Java NIO是什么?
NIO与IO:
NIO是指Nonblocking IO,指的是非阻塞IO
阻塞是指暂停一个线程的执行以等待某个条件的发生,例如某资源就绪
当处理多个连接时用到多线程时,阻塞会导致大量线程进行上下文切换,使得程序运行效率底下
而NIO可以解决这个问题,NIO通过Selector、Channel和Buffer来实现非阻塞的IO操作
NIO原理:
NIO的实现主要采用了Reactor(反应器)模式,这个设计模式和Observer(观察者)模式类似,只不过
Observer模式只能处理一个事件源,而Reactor设计模式可以处理多个事件源
NIO与传统的Socket方式比,由于NIO采用了非阻塞的方式,在处理大量并发请求时使用NIO比使用
Socket的效率高的多!
5、什么是Java序列化?
Java提供了两种对象持久化的方法:序列化和外部序列化
(1)序列化
序列化:把对象的状态写在流里进行网络传输,或者保存到文件、数据库等系统里,在必要的时候可以
把流读取出来重新构造一个相同的对象
实现序列化:
- 所有实现序列化的类必须实现Serializable接口
- 使用输出流(FileOutputStream)来构造一个ObjectOutputStream对象
- 接着使用该对象的writeObject方法将obj对象写出(保存其状态)
序列化的两个特点:
- 一个类能被序列化,那么它的子类也能被序列化
- 由于static代表类的成员,transient代表对象的临时数据,这两种类型的数据成员是不能被序列化的
序列化实例:
1 import java.io.*; 2 3 public class People implements Serializable{ 4 public String name; 5 public int age; 6 7 public People(){ 8 this.name = "xxx"; 9 this.age = 18; 10 } 11 12 public static void main(String[] args) { 13 People p = new People(); 14 ObjectOutputStream oos = null; 15 ObjectInputStream ois = null; 16 try{ 17 // 序列化 18 FileOutputStream fos = new FileOutputStream("people.out"); 19 oos = new ObjectOutputStream(fos); 20 oos.writeObject(p); 21 oos.close(); 22 } catch (Exception e) { 23 e.printStackTrace(); 24 } 25 People p1; 26 try{ 27 // 反序列化 28 FileInputStream fis = new FileInputStream("people.out"); 29 ois = new ObjectInputStream(fis); 30 p1 = (People)ois.readObject(); 31 System.out.println(p1.name + " - " + p1.age); 32 } catch (Exception e) { 33 e.printStackTrace(); 34 } 35 } 36 }
(2)外部序列化
Java语言还提供了另外一种方式来实现对象持久化,即外部序列化
接口如下:
1 public interface Externalizable extends Serializable{ 2 void readExternal(ObjectInput in); 3 void writeExternal(ObjectOutput out); 4 }
外部序列化和序列化的主要区别:
序列化是内置的API,只要实现Serializable接口就可以使用,不需要编写具体代码就可以实现对象的序列化
而外部序列化中的读写接口必须由开发人员完成,难度更大,但更灵活,可以选择只持久化部分成员
引申 - 在用接口Serializable实现序列化时,类中的所有属性都会被序列化,那么怎么样才能只序列化部分属性?
- 用transient来控制序列化的属性,被transient修饰的属性是临时的,不会被序列化
- 实现Externalizable接口,开发人员根据实际需求实现readExternal和writeExternal方法来控制序列化的属性