5:IO流与项目挑战
提交题目时 附上你的思考与心得可以加分哦~
Task1. 字节流和字符流
Section titled “Task1. 字节流和字符流”万物皆字节
所有的数据在计算机内部都是以二进制的数字保存,因此字节流可以传输任意类型的文件数据
这张可爱的 doro 也是字节哦~
请你将这张可爱的doro复制(右键另存为)到你的项目根目录下,命名为doro.jpg
Q1. 设计一个程序 实现功能: 将doro.jpg复制一份到项目根目录下 新图片命名为doro_copy.jpg
提示:
-
使用File类来管理你的目录和文件
-
使用FileInputStream和FileOutStream( 常见的字节流 )来对目标文件进行读写
-
读写时既可以一个一个字节的读写,也可以使用字节数组进行读写。建议使用后者效率更高哦 ( 字节数组的大小设置为1024 )
-
调用FileInputStream实体类的read方法,以读入文件数据。当读到文件末尾时返回-1,据此设计循环条件
-
完成后记得关闭你的流资源,注意思考输入输出流的开关顺序哦!
掌握了字节流的基本用法之后,你可能会有疑问:既然有了字节流,为什么还需要字符流呢?
尽管字符在计算机底层仍然是以字节形式储存,但是对于一份相同的字节数据,使用不同的编码格式,编码出的文本就会不同,从而导致乱码问题。使用字符流,可以自动处理编码格式问题。
因此,对于图片、音频等,不需要关心编码格式的数据,使用字节流即可;而对于文本数据,使用字符流更为便捷。
下面是一张姓名表,请你在你的项目根目录下创建txt文件,命名为name.txt,将表中的内容拷贝进去。
Q2. 设计一个程序: 读取name.txt 将表中的人名按照unicode自然排序 每个人名占据一行 在项目根目录下创建name_sorted.txt文件 将排序后的内容写入name_sorted.txt文件 要求去掉多余的首尾空格与空行
张三李四
王五 赵六 孙七周八 钱九吴十
郑十一 王十二冯十三 陈十四褚十五卫十六
蒋十七沈十八 韩十九杨二十提示:
-
常用的字符流为FileReader和FileWriter 但其无法指定编码格式,因此处理字符更好的方式是先创建字节流,再创建转换流,将字节流转换为字符流,同时指定编码格式。本题请使用UTF-8编码。
-
在实际开发中,字符流通常不会直接拿来使用,而是再包裹一层缓冲流。它与字符流使用方法类似,并额外提供了一些更便利的字符操作。
-
创建字符输入流的示例如下:
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)); -
建议使用BufferedReader中的readLine()方法 可以依次读取一行数据,读到末尾时返回null
-
建议使用BufferedWriter中的newLine()方法进行换行( 为什么不建议手写换行符? )
-
String的trim方法可以去除首位的空格/制表符/换行
-
同样要注意流的开关顺序哦~
Task2. 序列化和反序列化
Section titled “Task2. 序列化和反序列化”这里有一个学生类
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方法已重写好了 )
提示:
-
了解一下Serializable接口,掌握它的功能,为Student类补充完整
-
创建序列化流的方法示例:
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("student.dat")); -
由于输入输出流都是操作同一个文件,所以注意流的开关时机
Task3. maven
Section titled “Task3. maven”这个部分主要是自己了解和探索的过程,除了学习到的知识之外,你还在实操过程中遇到了什么问题和困难,又是如何解决的?记录一下你学习知识-遇到困难-解决问题的过程和其他感悟吧 ᶻz ₍^_ ̫ _^₎
什么是maven?
Section titled “什么是maven?”- 请你自行上网查阅资料,了解一下什么是maven
- 什么是jar包?
- maven和jar包有关系吗?有什么关系?
- 我们为什么要用maven?
试着下载maven吧!
Section titled “试着下载maven吧!”- 你的java环境配置好了吗?
- 别忘了配置环境变量
- 看看你的maven是什么版本
什么是maven仓库?
Section titled “什么是maven仓库?”- 怎么配置你自己的本地仓库位置?
- 需要的jar包本地仓库没有?让我们去看看远程仓库!
- 远程仓库不止一种!你知道中央仓库是什么吗?
- 私服又是什么?
创建你自己的maven项目!
Section titled “创建你自己的maven项目!”- 你的IDEA关联maven了吗?
- 一个maven项目都需要配置什么参数?你能说说看吗?
- 你的项目结构是什么样的?你知道你的代码、配置文件、测试代码、测试配置文件、maven项目配置文件都在哪里吗?
- pom.xml文件?
- 这是什么?
- 它有什么用?
- 它的基本配置有哪些?
- 你能通过这个文件管理你的jar包版本、项目类型吗?
- 怎么导入你需要的jar包?去找到答案吧!
启动你的maven!
Section titled “启动你的maven!”- maven的常用命令有哪些你知道吗?
- 图形化界面是非常方便的,去观察IDEA中的maven面板,看看可以快速执行哪些maven指令吧!
恭喜你~你已经对maven有初步的了解了!现在,向着项目挑战进发吧!
Section titled “恭喜你~你已经对maven有初步的了解了!现在,向着项目挑战进发吧!”Task4. 快递取件码查询系统
Section titled “Task4. 快递取件码查询系统”李华成功通过高考考进了UESTC,开学第一天他发现他的宿舍在校外,如果要拿快递的话要去到校内的菜鸟驿站,比较远。所以他决定手搓一个快递柜,请你帮他编写一个客户端和服务端的程序,实现通过快递单号查询取件码的功能。
maven
HTTP(HyperText Transfer Protocol):应用层协议,用于客户端(浏览器/APP)与服务端之间的数据传输。
特点:
- 无状态(每个请求独立,默认不记录历史)
- 基于请求-响应模型
- 支持明文(HTTP)和加密(HTTPS)传输
请求报文样例:
POST /login HTTP/1.1Host: api.example.comContent-Type: application/jsonUser-Agent: Mozilla/5.0Content-Length: 45
{"username": "admin", "password": "123456"}响应报文样例:
HTTP/1.1 200 OKContent-Type: application/jsonSet-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 依赖它们)}//然后通过给这个类实例化具体数据构建jsonUser 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); } } }}本题提交方式
Section titled “本题提交方式”出题人联系方式
Section titled “出题人联系方式”出题人:
達
QQ:3228523940
