跳转到内容

5:IO流与项目挑战

后端Banner

提交题目时 附上你的思考与心得可以加分哦~

万物皆字节

所有的数据在计算机内部都是以二进制的数字保存,因此字节流可以传输任意类型的文件数据

这张可爱的 doro 也是字节哦~

Java09-1.jpg

点击下载图片

请你将这张可爱的doro复制(右键另存为)到你的项目根目录下,命名为doro.jpg

Q1. 设计一个程序 实现功能: 将doro.jpg复制一份到项目根目录下 新图片命名为doro_copy.jpg

提示:

  1. 使用File类来管理你的目录和文件

  2. 使用FileInputStreamFileOutStream( 常见的字节流 )来对目标文件进行读写

  3. 读写时既可以一个一个字节的读写,也可以使用字节数组进行读写。建议使用后者效率更高哦 ( 字节数组的大小设置为1024 )

  4. 调用FileInputStream实体类的read方法,以读入文件数据。当读到文件末尾时返回-1,据此设计循环条件

  5. 完成后记得关闭你的流资源,注意思考输入输出流的开关顺序哦!


掌握了字节流的基本用法之后,你可能会有疑问:既然有了字节流,为什么还需要字符流呢?

尽管字符在计算机底层仍然是以字节形式储存,但是对于一份相同的字节数据,使用不同的编码格式,编码出的文本就会不同,从而导致乱码问题。使用字符流,可以自动处理编码格式问题。

因此,对于图片、音频等,不需要关心编码格式的数据,使用字节流即可;而对于文本数据,使用字符流更为便捷。

下面是一张姓名表,请你在你的项目根目录下创建txt文件,命名为name.txt,将表中的内容拷贝进去。

Q2. 设计一个程序: 读取name.txt 将表中的人名按照unicode自然排序 每个人名占据一行 在项目根目录下创建name_sorted.txt文件 将排序后的内容写入name_sorted.txt文件 要求去掉多余的首尾空格与空行

张三
李四
王五
赵六
孙七
周八
钱九
吴十
郑十一
王十二
冯十三
陈十四
褚十五
卫十六
蒋十七
沈十八
韩十九
杨二十

提示:

  1. 常用的字符流为FileReaderFileWriter 但其无法指定编码格式,因此处理字符更好的方式是先创建字节流,再创建转换流,将字节流转换为字符流,同时指定编码格式。本题请使用UTF-8编码。

  2. 在实际开发中,字符流通常不会直接拿来使用,而是再包裹一层缓冲流。它与字符流使用方法类似,并额外提供了一些更便利的字符操作。

  3. 创建字符输入流的示例如下:

    File source = new File("./name.txt");
    //创建字节流
    FileInputStream fileInputStream = new FileInputStream(source);
    //创建转换流
    InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, StandardCharsets.UTF_8);
    //创建缓冲流
    BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

    创建了三个流,在代码结束时,你就需要关闭三个流,较为繁琐。如果你使用下面这种方式创建,当最外层的流被关闭时,内部包裹的流就会依次自动关闭。

    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(source), StandardCharsets.UTF_8));
  4. 建议使用BufferedReader中的readLine()方法 可以依次读取一行数据,读到末尾时返回null

  5. 建议使用BufferedWriter中的newLine()方法进行换行( 为什么不建议手写换行符? )

  6. String的trim方法可以去除首位的空格/制表符/换行

  7. 同样要注意流的开关顺序哦~


这里有一个学生类

public class Student {
private Integer id;
private String name;
private Integer gender; //1表示男性 2表示女性
private String phone;
public Student(Integer id, String name, Integer gender, String phone) {
this.id = id;
this.name = name;
this.gender = gender;
this.phone = phone;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", gender=" + gender +
", phone='" + phone + '\'' +
'}';
}
}

在主类的main函数中,我们创建这样的一个学生对象

Student student = new Student(1, "doro", 2, "114514");

那么我们能不能用字节流,把这样的一个实体对象储存到一个文件中呢?

答案是肯定的,这就是对象的序列化,而从文件中把这个对象数据提取出来的过程 就是反序列化

Q3. 用上面的代码 创建一个student对象 使用序列化将该对象的信息储存在项目文件根目录下的student.dat文件中 再使用反序列化将数据赋值给新的Student对象doro 最后输出doro的信息 ( Student的toString方法已重写好了 )

提示:

  1. 了解一下Serializable接口,掌握它的功能,为Student类补充完整

  2. 创建序列化流的方法示例:

    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("student.dat"));
  3. 由于输入输出流都是操作同一个文件,所以注意流的开关时机


这个部分主要是自己了解和探索的过程,除了学习到的知识之外,你还在实操过程中遇到了什么问题和困难,又是如何解决的?记录一下你学习知识-遇到困难-解决问题的过程和其他感悟吧 ᶻz ₍^_ ̫ _^₎

  1. 请你自行上网查阅资料,了解一下什么是maven
  2. 什么是jar包?
  3. maven和jar包有关系吗?有什么关系?
  4. 我们为什么要用maven?
  • 你的java环境配置好了吗?
  • 别忘了配置环境变量
  • 看看你的maven是什么版本
  1. 怎么配置你自己的本地仓库位置?
  2. 需要的jar包本地仓库没有?让我们去看看远程仓库!
  3. 远程仓库不止一种!你知道中央仓库是什么吗?
  4. 私服又是什么?
  1. 你的IDEA关联maven了吗?
  2. 一个maven项目都需要配置什么参数?你能说说看吗?
  3. 你的项目结构是什么样的?你知道你的代码、配置文件、测试代码、测试配置文件、maven项目配置文件都在哪里吗?
  4. pom.xml文件?
    1. 这是什么?
    2. 它有什么用?
    3. 它的基本配置有哪些?
    4. 你能通过这个文件管理你的jar包版本、项目类型吗?
  5. 怎么导入你需要的jar包?去找到答案吧!
  1. maven的常用命令有哪些你知道吗?
  2. 图形化界面是非常方便的,去观察IDEA中的maven面板,看看可以快速执行哪些maven指令吧!

恭喜你~你已经对maven有初步的了解了!现在,向着项目挑战进发吧!

Section titled “恭喜你~你已经对maven有初步的了解了!现在,向着项目挑战进发吧!”

李华成功通过高考考进了UESTC,开学第一天他发现他的宿舍在校外,如果要拿快递的话要去到校内的菜鸟驿站,比较远。所以他决定手搓一个快递柜,请你帮他编写一个客户端和服务端的程序,实现通过快递单号查询取件码的功能。

maven

HTTP(HyperText Transfer Protocol):应用层协议,用于客户端(浏览器/APP)与服务端之间的数据传输。

特点:

  • 无状态(每个请求独立,默认不记录历史)
  • 基于请求-响应模型
  • 支持明文(HTTP)和加密(HTTPS)传输

请求报文样例:

POST /login HTTP/1.1
Host: api.example.com
Content-Type: application/json
User-Agent: Mozilla/5.0
Content-Length: 45
{"username": "admin", "password": "123456"}

响应报文样例:

HTTP/1.1 200 OK
Content-Type: application/json
Set-Cookie: sessionid=abc123; Path=/
Content-Length: 30
{"status": "success", "token": "xyz"}

需要了解常见的请求方式,以及常见的响应状态码(404 应该是最熟悉的状态码了)

JSON数据格式

json本质来说就是一个字符串,是一种轻量级的数据交换格式,易于人阅读和编写,也易于机器解析和生成。

类似Java中的HashMap,通过键值的匹配来读取数据

键名必须用双引号包裹,值的类型多种多样,还可以嵌套,例如:

{
"key1": "value1",
"key2": 123,
"key3": true,
"key4": null,
"key5": { "nestedKey": "nestedValue" },
"key6": [1, 2, 3]
}

如果遇到很复杂的json字符串,还可以用这个网站帮助分析结构

JSON Formatter & Validator (curiousconcept.com)

TCP/IP 通信


先要在pom.xml中导入解析json格式的依赖

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.51</version>
</dependency>

用fastjson构造json格式

//fastjson这个库需要定义一个类作为映射
public class User {
private String name;
private int age;
// 必须有无参构造函数
// 必须提供 getter/setter (Fastjson 依赖它们)
}
//然后通过给这个类实例化具体数据构建json
User user = new User("Alice", 25);
String json = JSON.toJSONString(user);
// 结果: {"age":25,"name":"Alice"}

解析json格式

//{"age":25,"name":"Alice"}
String json = "{\"name\":\"Alice\",\"age\":25}";
User user = JSON.parseObject(json, User.class);
//这时候user这个对象就保存了对应字段的内容

代码实现部分

import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
public class Client {
private static final String SERVER_URL = "http://localhost:8000/query";
public static void main(String[] args) {
while (true) {
//读取用户输入的快递单号和手机号
try {
/*
用fastjson这个依赖构造json格式,构造的样例为
{"trackingNumber":"SF123456789","phone":"19867653558"}
*/
// 将这个json格式的数据写入请求体并发送HTTP POST请求
// 读取响应,状态码是200才是响应成功
if (connection.getResponseCode() == 200) {
/*
解析服务端响应的json格式,拿到取件码,拿不到就显示msg的内容
样例:
例如:{"pick_code":4096,"msg":"success"}
{"pick_code":null,"msg":"手机号不正确"}
*/
}
} else {
System.out.println("查询失败,状态码: " + connection.getResponseCode());
}
} catch (IOException e) {
System.out.println("请求发生错误: " + e.getMessage());
}
}
scanner.close();
System.out.println("客户端已退出");
}
}
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class Server {
private static final int PORT = 8000;
private static final Map<String, String> expressMap = new HashMap<>();
public static void main(String[] args) throws IOException {
// 初始化一些测试数据
initializeExpressData();
// 创建HTTP服务器,监听指定端口
HttpServer server = HttpServer.create(new InetSocketAddress(PORT), 0);
// 设置路由和处理程序
server.createContext("/query", new QueryHandler());
// 启动服务器
server.start();
System.out.println("Server started on port " + PORT);
}
private static void initializeExpressData() {
// 添加一些测试数据
//键的构成是 快递单号_手机号
expressMap.put("SF123456789_13005433678", "1234");
expressMap.put("JD987654321_19805433168", "5678");
expressMap.put("YT456789123_13905479698", "9012");
expressMap.put("ZT789123456_18505433664", "3456");
}
static class QueryHandler implements HttpHandler {
@Override
public void handle(HttpExchange exchange) throws IOException {
if ("POST".equals(exchange.getRequestMethod())) {
// 读取请求体
try {
// 解析JSON请求(例如:{"trackingNumber":"SF123456789"})获得单号
// 在expressMap中查询取件码,
/*
构建响应的json
例如:{"pick_code":4096,"msg":"success"}
如果找不到快递则是{"pick_code":null,"msg":"根据各种情况写提示信息"}
*/
// 发送响应
} catch (Exception e) {
// 处理异常,返回400状态码(Bad Request)
}
} else {
// 非POST请求返回405 Method Not Allowed
exchange.sendResponseHeaders(405, -1);
}
}
}
}

提交点这里

出题人:出题人头像  達

QQ:3228523940

邮箱:3228523940@qq.com