文件IO
java.io.File
是用於操作文件或目錄的類:
File file = new File("hello.txt");
實例化File時不關心路徑的目標並不會去讀取文件或目錄. File類提供了一些有用的方法:
-
isFile()
: 判斷路徑指向的是否為文件 -
createNewFile()
: 當路徑指向的文件不存在時創建一個空文件 -
exists()
: 判斷路徑指向的文件是否存在 -
delete()
: 刪除路徑指向的文件或目錄 -
length()
: 返回文件的長度 -
isDirectory()
: 判斷路徑指向的是否為目錄 -
mkdir()
: 根據路徑創建空目錄, 父目錄必須存在 -
mkdirs()
: 根據路徑創建空目錄, 會創建必要的父目錄 -
list()
: 以String[]
類型返回目錄中所有文件名 -
listFiles()
: 以File[]
類型返回目錄中所有文件
字符流
Java使用流來讀寫文件, 字符流用來讀寫文本文件. 所有的字符流類都在java.io
包中.
讀取文本文件:
File file = new File("a.txt");
FileReader fin = new FileReader(file);
BufferedReader reader = new BufferedReader(fin);
try {
String str = reader.readLine();
System.out.println(str);
}
catch (IOException e) {
e.printStackTrace();
}
finally {
reader.close();
fin.close();
}
FileReader也可以直接用文件名創建. new FileReader("a.txt")
.
寫入文本文件:
FileWriter fout = new FileWriter("a.txt", true);
BufferedWriter writer = new BufferedWriter(fout);
try {
String str = "Hello World";
writer.write(str);
}
catch (IOException e) {
e.printStackTrace();
}
finally {
writer.close();
fout.close();
}
FileWriter的第二個參數為append, true代表在文件尾追加, 默認false代表清空文件重寫.
字節流
字節流用於讀寫二進制文件, 其讀寫的數據以byte[]
類型存儲.
所有字節流類都在java.io
包中.
讀取文件:
File file = new File("a.in");
FileInputStream fin = new FileInputStream(file);
Byte[] buf = new Byte[512];
try {
fin.read(buf);
}
catch {IOException e} {
e.printStackTrace();
}
finally {
fin.close();
}
寫文件:
File file = new File("a.in");
FileOutputStream fout = new FileOutputStream(file);
Byte[] buf = new Byte[512];
try {
// write sth in buf
fin.write(buf);
}
catch {IOException e} {
e.printStackTrace();
}
finally {
fout.close();
}
標准輸入輸出
java.lang.System
對象中維護了3個標准流, 用於終端輸入輸出:
System.out
, 標准輸出流,PrintStream
對象System.err
: 標准錯誤流,PrintStream
對象System.in
: 標准輸入流,FileInputStream
對象
在需要的時候我們可以將它們重定向到文件.
重定向標准輸出:
File file = new File("a.out");
FileOutputStream fout = new FileOutputStream(file);
PrintStream pout = new PrintStream(fout);
PrintStream stdout = System.out; // save
System.setOut(pout);
System.setOut(stdout); // recover
因為標准輸入是字節流, 我們需要把它們轉換成需要的類型才能使用:
int n;
byte[] buf = new byte[1024];
n = System.in.read(buf);
String s = new String(buf, 0, n);
System.out.println(s);
java.util.Scanner
允許用迭代器的方式讀取輸入:
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
// scanner.next(); // return Object
scanner.nextInt(); // return int
}
scanner可以直接將輸入轉換為內置類型使用很方便.
網絡IO
TCP客戶端
java.net.Socket
是一個用作Tcp客戶端的Socket. 從Socket中獲得InputStream
和OutputStream
對象就可以與服務器通過Tcp連接通信了.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
public class TcpClient {
public static void main(String[] args) throws IOException {
Socket client = new Socket("127.0.0.1", 5000);
client.setSoTimeout(10000);
PrintStream out = new PrintStream(client.getOutputStream());
BufferedReader buf = new BufferedReader(new InputStreamReader(client.getInputStream()));
String[] msgs = {"你好", "世界"};
for (String msg : msgs) {
out.println(msg);
while (true) {
String echo = buf.readLine();
if (echo != null) {
System.out.println(echo);
break;
}
}
}
}
}
上述示例中發送完一條消息則進入輪詢, 查看是否有服務端消息.
java.net.ServerSocket
則是一個用作Tcp服務端的socket.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
public class TcpServer implements Runnable {
private Socket client = null;
private String address;
public TcpServer(Socket client) {
this.client = client;
}
@Override
public void run() {
try {
String host = client.getInetAddress().toString();
String port = Integer.toString(client.getPort());
PrintStream out = new PrintStream(client.getOutputStream());
System.out.println("Get Connection");
BufferedReader buf = new BufferedReader(new InputStreamReader(client.getInputStream()));
address = host + ":" + port;
System.out.println("get connection from" + address);
while (true) {
String str = buf.readLine();
if (str != null) {
out.println(str);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
ArrayList<Thread> list = new ArrayList<Thread>();
try {
ServerSocket socket = new ServerSocket(5000);
while (true) {
// accept a new connection
Socket client = socket.accept();
Thread thread = new Thread(new TcpServer(client));
list.add(thread);
thread.start();
}
}
catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
上述示例是一個多線程服務器, ServerSocket
監聽5000端口. 當有客戶端連接該端口時, socket.accept()
將返回一個java.net.Socket
對象.
創建一個線程持有Socket對象, 並與客戶端進行通信.
UDP客戶端
java.net.Datagram
可以用作UDP客戶端, 其接收到的數據報被封裝為java.net.DatagramPacket
.
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UdpClient {
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket();
String[] msgs = {"1", "2.3", "520"};
for (String msg : msgs) {
byte[] buf = msg.getBytes();
InetAddress addr = InetAddress.getByName("127.0.0.1");
DatagramPacket packet = new DatagramPacket(buf, buf.length, addr, 5000);
socket.send(packet);
while (true) {
byte[] recvBuf = new byte[256];
DatagramPacket recvPacket = new DatagramPacket(recvBuf, recvBuf.length);
socket.receive(recvPacket);
String echo = new String(recvPacket.getData(), 0, recvPacket.getLength());
if (echo != null) {
System.out.println(echo);
break;
}
}
}
}
}
DatagramSocket
的send方法用於發送數據報, receive
用於接收數據報.
UDP服務器
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;
public class UdpServer implements Runnable {
private DatagramSocket socket = null;
private DatagramPacket packet = null;
private String address;
public UdpServer(DatagramSocket socket, DatagramPacket packet) {
this.socket = socket;
this.packet = packet;
}
@Override
public void run() {
try {
String msg = new String(packet.getData());
UdpServer.sum += Double.parseDouble(msg);
UdpServer.count += 1;
int port = packet.getPort();
InetAddress address = packet.getAddress();
System.out.println("get msg from" + address);
String response = new String(recvPacket.getData(), 0, recvPacket.getLength());
DatagramPacket sendPacket = new DatagramPacket(response.getBytes(), response.getBytes().length, address, port);
socket.send(sendPacket);
}
catch (IOException ioe) {
ioe.printStackTrace();
}
}
public static void main(String[] args) {
AtomicInteger numThreads = new AtomicInteger(0);
ArrayList<Thread> list = new ArrayList<Thread>();
try {
DatagramSocket socket = new DatagramSocket(5000);
while (true) {
byte[] buf = new byte[100];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
Thread thread = new Thread(new UdpServer(socket, packet));
list.add(thread);
thread.start();
numThreads.incrementAndGet();
}
}
catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
因為UDP不需要維護連接, 服務端和客戶端的socket是同樣的.
上文是一個多線程UDP服務器, 不過所有線程持有同一個socket對象和要處理的數據報. 為了保證線程安全最好給socket加鎖.