1、File类
文件和文件夹的基本操作
public class TestFile {
public static void main(String[] args) throws IOException {
File file = new File("d:/a");//文件对象
//String[] list = file.list();
//File[] listFiles = file.listFiles();
String[] list = file.list(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.contains("a");
}
});
System.out.println(Arrays.toString(list));
//file.createNewFile();
//System.out.println(file.exists());//判断是否存在
//System.out.println(file.canRead());
//System.out.println(file.canWrite());
//System.out.println(file.delete());
//System.out.println(file.getAbsolutePath());
//System.out.println(file.getName());
//String fileName = file.getName();
//System.out.println(fileName.substring(fileName.lastIndexOf(".")+1));
// String parent = file.getParent();
// System.out.println(parent);
//long freeSpace = file.getFreeSpace();
//System.out.println(freeSpace);
//System.out.println(file.length());
//System.out.println(file.getPath());
//System.out.println(file.isFile());
//file.mkdirs();
//System.out.println(file.isDirectory());
}
}
2、流
java中有四大家族的流,他们都是抽象类:
java.io.InputStream 字节输入流
java.io.OutputStream 字节输出流
java.io.Reader 字符输入流
java.io.Writer 字符输出流
对于文件有这些流常用:
java.io.FileInputStream
java.io.FIleOUtStream
java.io.FileReader
java.io.FileWriter
对象有关的流:
java.io.ObjectInputStream
java.io.ObjectOutputStream
转换流:
OutputStreamWriter
InputStreamReader
2.1 字节流
文件的读和写:
public static void main(String[] args) throws Exception{
//创建流---指定文件位置
InputStream is = new FileInputStream("d:/a.txt");
OutputStream os = new FileOutputStream("d:/b.txt",true);//已经创建了b.txt
//调用流的方法去读和写
//一个字节一个字节读
// int data;
// while((data= is.read())!=-1) {
// System.out.println((char)data);
// }
//一次读多个字节
byte[] data = new byte[4];//缓存数组--8*1024
int length;
while((length = is.read(data))!=-1) {
//输出到控制台
System.out.println(new String(data,0,length));
os.write(data, 0, length);
}
os.write(10);//换行
//关闭资源
is.close();
os.close();
System.out.println("over");
}
//找一张图片或者音乐,用字节流操作来复制一下这个图片或音乐
public class TestCopyPic {
public static void main(String[] args) {
InputStream is= null;
OutputStream os = null;
try {
is = new FileInputStream("d:/th.jpg");
os = new FileOutputStream("d:/a/th.jpg",true);
byte[] buf = new byte[8*1024];
int length=-1;
while((length=is.read(buf))!=-1) {
os.write(buf, 0, length);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//先开的后关
try {
os.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
2.2、字符流
FileWriter和FileReader中的操作方式和FileInputStream以及FileOutStream相似
2.3、缓冲流
FileReader fileReader = new FileReader("G:/b/b.text");
BufferedReader bufferedReader = new BufferedReader(fileReader);
String s2 = null;
while((s2 = bufferedReader.readLine()) != null){
System.out.println(s2);
}
bufferedReader.close();
FileWriter fileWriter = new FileWriter("G:/b/b.text");
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
bufferedWriter.newLine();
bufferedWriter.write(123);
bufferedWriter.flush();
bufferedWriter.close();
2.4、序列化流(对象流)
//反序列化流--将序列化过程中所生产的二级制串转化成数据结构或对象
public class ObjectInputStream
extends InputStream implements ObjectInput, ObjectStreamConstants{}
//序列化流--将数据结构或对象转换成二进制串
public class ObjectOutputStream
extends OutputStream implements ObjectOutput, ObjectStreamConstants{}
//Serializable接口,当一个类的实例要被对象流读写时,要求必须实现serializable接口
3、socket
服务端:
ServerSocket server = new ServerSocket(6666);
//启动监听
Socket socket = server.accept(); //一直没有接收到客户端信号就一直阻塞
System.out.println(socket.getInetAddress().getHostAddress()+"连接了服务器");
客户端:
Socket socket = new Socket("10.7.163.102", 6666);
交互客户端:
package com.qf.cdjava2201.day24.chat;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class Server {
//接收消息,并且转发消息给所有的客户端--集合,也可以使用线程池
List<ThreadSocket> clients = new ArrayList<Server.ThreadSocket>();
public void start() {
try {
ServerSocket server = new ServerSocket(8888);
while(true) {
Socket socket = server.accept(); //接收客户端连接
System.out.println(socket.getInetAddress().getHostAddress()+"连接了服务器");
//保存这个客户端
ThreadSocket client = new ThreadSocket(socket);
clients.add(client);
new Thread(client).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new Server().start();
}
public class ThreadSocket implements Runnable{
Socket socket;
String name;
public ThreadSocket(Socket socket) {
this.socket = socket;
}
//处理每个客户端的聊天信息
public void run() {
//接收一个名字
try {
//从socket中获取输入流并建立缓冲区进行读取
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
name = br.readLine();
//把名字广播给所有的客户端
String msg = "欢迎"+name+"进入聊天室";
sendMsg(msg);
while(true) {
//接收消息并转发
BufferedReader br1 = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String info = br1.readLine();
sendMsg(name+"["+socket.getInetAddress().getHostAddress()+"]:"+info);
System.out.println(name+"["+socket.getInetAddress().getHostAddress()+"]:"+info);
if(info.equals("88")) {
System.out.println(name+"退出了聊天室");
socket.close();
clients.remove(this);
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
//广播所有信息的方法
private void sendMsg(String msg) {
for (ThreadSocket client : clients) {
try {
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(client.socket.getOutputStream()));
bw.write(msg);
bw.newLine();
bw.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
客户端
package com.qf.cdjava2201.day24.chat;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
public class Client {
static boolean flag = true;
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
//建立连接
try {
Socket socket = new Socket("127.0.0.1",8888);
//接收消息--启动一个线程来一直接收消息,客户端不关闭,便一直接收
new Thread(new Runnable() {
public void run() {
while(flag) {
BufferedReader br=null;
try {
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String info="";
info = br.readLine();
System.out.println(info);
} catch (IOException e1) {
//输入88这里有个异常
}
}
}
}).start();
//输入昵称发给服务器
System.out.println("请输入您的昵称:");
String name = input.next();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write(name);
bw.newLine();
bw.flush();
//发送消息
while(true) {
System.out.println("请发送消息:");
String msg = input.next();
BufferedWriter bw1 = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw1.write(msg);
bw1.newLine();
bw1.flush();
if(msg.equals("88")) {
//最后关闭流的方式还可以用socket.shutdownOutput()。相比于socket.close(),这个方法是在服务端收到消息后,还能再发消息给客户端,然后再关闭Socket。
socket.close();
flag = false;
break;
}
}
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//这里设计的通知服务端已发送完命令的方式是设定“88”,但在真实的使用中,肯定是麻烦的,比如不能单独输入88了。
//这里推荐使用“指定长度”的方法。
转载于---博客园,作者:已往之不谏。
如果你了解一点class文件的结构(后续会写,敬请期待),那么你就会佩服这么设计方式,也就是说我们可以在此找灵感,就是我们可以先指定后续命令的长度,然后读取指定长度的内容做为客户端发送的消息。
现在首要的问题就是用几个字节指定长度呢,我们可以算一算:
1个字节:最大256,表示256B
2个字节:最大65536,表示64K
3个字节:最大16777216,表示16M
4个字节:最大4294967296,表示4G
依次类推
这个时候是不是很纠结,最大的当然是最保险的,但是真的有必要选择最大的吗,其实如果你稍微了解一点UTF-8的编码方式(字符编码后续会写,敬请期待),那么你就应该能想到为什么一定要固定表示长度字节的长度呢,我们可以使用变长方式来表示长度的表示,比如:
第一个字节首位为0:即0XXXXXXX,表示长度就一个字节,最大128,表示128B
第一个字节首位为110,那么附带后面一个字节表示长度:即110XXXXX 10XXXXXX,最大2048,表示2K
第一个字节首位为1110,那么附带后面二个字节表示长度:即110XXXXX 10XXXXXX 10XXXXXX,最大131072,表示128K
依次类推
上面提到的这种用法适合高富帅的程序员使用,一般呢,如果用作命名发送,两个字节就够了,如果还不放心4个字节基本就能满足你的所有要求,下面的例子我们将采用2个字节表示长度,目的只是给你一种思路,让你知道有这种方式来获取消息的结尾:
服务端程序:
public class SocketServer {
public static void main(String[] args) throws Exception {
// 监听指定的端口
int port = 55533;
ServerSocket server = new ServerSocket(port);
// server将一直等待连接的到来
System.out.println("server将一直等待连接的到来");
Socket socket = server.accept();
// 建立好连接后,从socket中获取输入流,并建立缓冲区进行读取
InputStream inputStream = socket.getInputStream();
byte[] bytes;
// 因为可以复用Socket且能判断长度,所以可以一个Socket用到底
while (true) {
// 首先读取两个字节表示的长度
int first = inputStream.read();
//如果读取的值为-1 说明到了流的末尾,Socket已经被关闭了,此时将不能再去读取
if(first==-1){
break;
}
int second = inputStream.read();
int length = (first << 8) + second;
// 然后构造一个指定长的byte数组
bytes = new byte[length];
// 然后读取指定长度的消息即可
inputStream.read(bytes);
System.out.println("get message from client: " + new String(bytes, "UTF-8"));
}
inputStream.close();
socket.close();
server.close();
}
}
此处的读取步骤为,先读取两个字节的长度,然后读取消息,客户端为:
public class SocketClient {
public static void main(String args[]) throws Exception {
// 要连接的服务端IP地址和端口
String host = "127.0.0.1";
int port = 55533;
// 与服务端建立连接
Socket socket = new Socket(host, port);
// 建立连接后获得输出流
OutputStream outputStream = socket.getOutputStream();
String message = "你好 yiwangzhibujian";
//首先需要计算得知消息的长度
byte[] sendBytes = message.getBytes("UTF-8");
//然后将消息的长度优先发送出去
outputStream.write(sendBytes.length >>8);
outputStream.write(sendBytes.length);
//然后将消息再次发送出去
outputStream.write(sendBytes);
outputStream.flush();
//==========此处重复发送一次,实际项目中为多个命名,此处只为展示用法
message = "第二条消息";
sendBytes = message.getBytes("UTF-8");
outputStream.write(sendBytes.length >>8);
outputStream.write(sendBytes.length);
outputStream.write(sendBytes);
outputStream.flush();
//==========此处重复发送一次,实际项目中为多个命名,此处只为展示用法
message = "the third message!";
sendBytes = message.getBytes("UTF-8");
outputStream.write(sendBytes.length >>8);
outputStream.write(sendBytes.length);
outputStream.write(sendBytes);
outputStream.close();
socket.close();
}
}