Java应用基础微专业-设计篇
第1章--抽象与接口
1.1 抽象
An abstract class can be created without abstract methods, the purpose of doing this is to prevent instantiating the class.
A class with any abstract method must be abstract.
1.2 数据与表现分离
separation of the logical layer and the graphical interface.
责任驱动:whose responsibility?
1.3 接口
接口所有的方法都是没有方法体的,而且都是public abstract,即使你没有这样声明。
接口中的所有成员变量都是public static final的变量,并且必须有定义初始化,因为所有变量都必须在编译的时候有确定值。
Java不允许多继承(一个类有两个父类),但是允许一个类实现多个接口,也允许一个接口从多个接口得到继承,但是不允许接口从类继承。
接口--任何需要传入传出的一定是接口而不是具体的类
pro: 适合多人同时写一个大程序
con: 代码量膨胀起来很快
第2章--异常
2.1 异常的捕捉
// Question: // how is the ArrayIndexOutOfBoundsException thrown by f() ?public class ExceptionThrown {public static void f() {int[] a = new int[10];a[10] = 10;System.out.println("hello");}public static void g() {f();}public static void h() {int i = 10;if (i < 100) {g();}}public static void k() {try {g();} catch (NullPointerException e) {System.out.println("k()");}}public static void main (String[] args) {try {k();} catch (ArrayIndexOutOfBoundsException e) {System.out.println("Exception Caught"); System.out.println(e); e.printStackTrace();}} }
捕捉异常后 可以处理异常,也可再次抛出
catch(Exception e) {
...
throw e;
}
finally{} -- execute finally block after try/catch.
2.2 异常的抛出和声明
若函数可能抛出异常,就必须在函数头部声明 throws ...
若调用一个声明了会抛出异常的函数,这需要try/catch来解决异常或重新抛出异常。
所有异常都是Exception类的子类。
捕捉异常时可以catch到异常的子类。
多句catch语句时会按照先后顺序匹配,若匹配到则不会继续匹配。
若同时有两句catch捕捉子类和父类异常,
注意"Unreachable catch block for child Exception" if child exception is put after the catch parent exception.
当覆盖一个函数的时候,子类的函数声明抛出的异常不能比被覆盖的父类函数声明抛出的异常多。
因为有可能拿着子类的对象当成父类的对象来看待: upper cast
public class A {public void f() throws AException {} }public class A1 extends A {public void f() {} // works okpublic void f() throws AException {} // works okpublic void f() throws AException, BException {} // Error: Exception BException is not compatible with throws clause in A.f() }
在子类的构造函数中,必须声明父类可能抛出的全部异常。
因为子类的构造器会自动调用父类的构造器。
第3章--IO
3.1 流
处理输入输出的方法:stream -- unidirectional; one-dimensional.
java.io: Package
InputStream & OutputStream -- abstract class in java.io
InputStream:
read(); read(byte b[]); read(byte[], int off, int len); --stream of bytes
int available(); mark(); reset(); markSupported(); close();
public class InputStreamTest {public static void main(String[] args) {System.out.println("Ready to read in: ");byte[] buffer = new byte[1024];try {int len = System.in.read(buffer); // how many bytes read inString s = new String(buffer, 0 ,len); // start at 0, with length lenSystem.out.println("read in " + len + " bytes.");System.out.println(s);System.out.println("length of s: " + s.length());} catch (IOException e) {e.printStackTrace();}} }
when the input stream contains Chinese characters, the value of len would be different from the value of s.length()
OutputStream:
write(); write(int b); write(byte b[]); write(byte b[], int off, int len);
flush()--; close();
File stream 文件流:
FileInputStream & FileOutputStream -- 实际工程中较少使用:常用在内存数据或通信数据上建立的流;具体的文件读写有更专业的类如配置文件和日志文件.
public static void main(String[] args) {System.out.println("prompt");byte[] buffer = new byte[10];for(int i = 0; i < buffer.length; i++) {buffer[i] = (byte) i;}try {FileOutputStream out = new FileOutputStream("a.dat");out.write(buffer);// a file called a.dat will be created of size 10 bytes} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}
流过滤器:
以一个介质流对象为基础,层层构建过滤器流,最终形成的流对象能在数据的输入输出过程中,逐层使用过滤器流的方法来读写数据。
public static void main(String[] args) {System.out.println("prompt");// data stream: input/output the primitive types value DataOutputStream out;try {out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("a.dat")));// nested stream -- bytes from FileOutputStream to BufferedOutputStream, to DataOutputStream // int i = 0xcafebabe; // hexint i = 123456; // what is written in the file is binary out.writeInt(i);out.close();DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream("a.dat")));int j = in.readInt();in.close();System.out.println(j);} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}
3.2 文本流
The class we use previously (Data stream) is only available for dealing with data of primitive types (binary data).
What if we wanna handle text? -- Reader & Writer.
public static void main(String[] args) {System.out.println("prompt");try {PrintWriter out = new PrintWriter (new BufferedWriter(new OutputStreamWriter(new FileOutputStream("a.txt"))));// FileOutputStream: bytes// OutputStreamWriter: the bridge between stream and writerint i = 123456;out.println(i);out.close();// BufferedReaderBufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream("src/ReaderWriter.java")));String line;while ((line = in.readLine()) != null) {// read in line by line System.out.println(line);}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}
Other classes:
LineNumberReader -- is able to get the line number and switch between lines
FileReader -- is the child class of InputStreamReader.
FileReader(File file);/ FileReader(String fileName); -- create a reader on a file directly.
文件编码方式的影响:
国标GB码:2bytes一个汉字
Unicode编码:所有平台上通用。
UTF-8:如果全部是英文字符,一个byte就能表示一个英文字符;如果有汉字(对他们来说不常见),这需要(可能)3个字节表示
若:上述程序读入的是UTF-8编码的文件,则会输出乱码--FileInputStream处理二进制数据后交给InputStreamReader处理文本(二进制转换为文本:default方法:系统默认方式)
程序改进--在InputStreamReader中给定处理方式new InputStreamReader(new FileInputStream("utf8.txt"), "utf8"));
(创建使用给定字符集的InputStreamReader: InputStreamReader(InputStream in, Charset cs);
创建使用给定字符集解码器的InputStreamReader: InputStreamReader(InputStream in, CharsetDecoder dec);
创建使用指定字符集的InputStreamReader: InputStreamReader(InputStream in, String charsetName);)
面对繁多的输入输出类,如何选择呢?
是二进制数据?-- InputStream
否 -- 表达的是文本? -- Reader (readLine...)
表达的是基本数据类型? -- Scanner(nextInt\nextDouble...)
3.3 流的应用 (instances)
network socket
public static void main(String[] args) {try {// construct connectionSocket socket = new Socket(InetAddress.getByName("localhost"), 12345);PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())));out.println("Hello"); // write Helloout.flush(); // kinda like refresh// Read inBufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));String line;line = in.readLine();System.out.println(line);out.close();socket.close();} catch (IOException e) {e.printStackTrace();}}
在terminal中打开nc -l 12345端口后,运行程序。
read()函数是阻塞的,在读到所需的内容之前会停下来等(基于read()的函数也是如此,如nextInt(), readLine())
常用单独的线程来做socket读入的等待,或使用nio的channel选择机制
对于socket,可以设置SO时间:setSoTimeout(int timeOut);
对象串行化:将类整个写到流里去 (Interface Serialization)
ObjectInputStream & ObjectOutputStream
public static void main(String[] args) {Student s1 = new Student("John", 18, 5);System.out.println("s1" + s1);try {ObjectOutputStream out;out = new ObjectOutputStream (new FileOutputStream("obj.dat"));out.writeObject(s1);out.close();} catch (IOException e) {e.printStackTrace();}ObjectInputStream in;try {in = new ObjectInputStream(new FileInputStream("obj.dat"));Student s2 = (Student)in.readObject();System.out.println("s2" + s2);in.close();} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}}
第4章--设计原则
4.1 城堡游戏(例子)
package castle;public class Room {public String description;public Room northExit;public Room southExit;public Room eastExit;public Room westExit;public Room(String description) {this.description = description;}public void setExits(Room north, Room east, Room south, Room west) {if(north != null)northExit = north;if(east != null)eastExit = east;if(south != null)southExit = south;if(west != null)westExit = west;}@Overridepublic String toString(){return description;} }
package castle;import java.util.Scanner;public class Game {private Room currentRoom;public Game() {createRooms();}private void createRooms(){Room outside, lobby, pub, study, bedroom;// 制造房间outside = new Room("城堡外");lobby = new Room("大堂");pub = new Room("小酒吧");study = new Room("书房");bedroom = new Room("卧室");// 初始化房间的出口outside.setExits(null, lobby, study, pub);lobby.setExits(null, null, null, outside);pub.setExits(null, outside, null, null);study.setExits(outside, bedroom, null, null);bedroom.setExits(null, null, null, study);currentRoom = outside; // 从城堡门外开始 }private void printWelcome() {System.out.println();System.out.println("欢迎来到城堡!");System.out.println("这是一个超级无聊的游戏。");System.out.println("如果需要帮助,请输入 'help' 。");System.out.println();System.out.println("现在你在" + currentRoom);System.out.print("出口有:");if(currentRoom.northExit != null)System.out.print("north ");if(currentRoom.eastExit != null)System.out.print("east ");if(currentRoom.southExit != null)System.out.print("south ");if(currentRoom.westExit != null)System.out.print("west ");System.out.println();}// 以下为用户命令private void printHelp() {System.out.print("迷路了吗?你可以做的命令有:go bye help");System.out.println("如:\tgo east");}private void goRoom(String direction) {Room nextRoom = null;if(direction.equals("north")) {nextRoom = currentRoom.northExit;}if(direction.equals("east")) {nextRoom = currentRoom.eastExit;}if(direction.equals("south")) {nextRoom = currentRoom.southExit;}if(direction.equals("west")) {nextRoom = currentRoom.westExit;}if (nextRoom == null) {System.out.println("那里没有门!");}else {currentRoom = nextRoom;System.out.println("你在" + currentRoom);System.out.print("出口有: ");if(currentRoom.northExit != null)System.out.print("north ");if(currentRoom.eastExit != null)System.out.print("east ");if(currentRoom.southExit != null)System.out.print("south ");if(currentRoom.westExit != null)System.out.print("west ");System.out.println();}}public static void main(String[] args) {Scanner in = new Scanner(System.in);Game game = new Game();game.printWelcome();while ( true ) {String line = in.nextLine();String[] words = line.split(" ");if ( words[0].equals("help") ) {game.printHelp();} else if (words[0].equals("go") ) {game.goRoom(words[1]);} else if ( words[0].equals("bye") ) {break;}}System.out.println("感谢您的光临。再见!");in.close();}}
4.2 消除代码复制
duplication -- bad for maintenance.
solution--using methods; inheritance.
System.out.println("你在" + currentRoom); System.out.print("出口有: "); if(currentRoom.northExit != null)System.out.print("north "); if(currentRoom.eastExit != null)System.out.print("east "); if(currentRoom.southExit != null)System.out.print("south "); if(currentRoom.westExit != null)System.out.print("west "); System.out.println();
duplications in printWelcome() and goRoom();
4.3 封装
encapsulation.
good design: low coupling & high cohesion.
for code modification & code reuse.
Lower coupling by using encapsulation
bad design: variables in Room set as public
solution: private visualization should be used
but how to access these variables? add getters? Not an OOP solution.
-- coupling: what does these exits used for?
We can make Room class tell Game about its exits, rather than figure it out in Game class.
getExitDesc() in Room
public String getExitSDesc() {StringBuffer sb = new StringBuffer();if(northExit != null)sb.append("north ");if(eastExit != null)sb.append("east ");if(southExit != null)sb.append("south ");if(westExit != null)sb.append("west ");return sb.toString();}
the reason why not using String s = ""; s+="north ";... -- immutable -> new and replace -> to much cost.
an efficient way to do this: StringBuffer.
-- coupling: same for move operation in Game.goRoom()
public Room moveToNextRoom(String direction) {Room nextRoom = null;if(direction.equals("north")) {nextRoom = northExit;}if(direction.equals("east")) {nextRoom = eastExit;}if(direction.equals("south")) {nextRoom = southExit;}if(direction.equals("west")) {nextRoom = westExit;}return nextRoom; }
4.4 可扩展性
可扩展性的意思就是代码的某些部分不需要经过修改就能适应将来可能的变化。
add functions: go up/down:
用容器来实现灵活性--原本: 用成员变量来表示Room的方向,增减方向需要改变代码。
solution: 用hash table来表示方向,那么方向就不是“硬编码”的了。
four private Room variables should not be created in Room class directly -- changing to use HashMap<String, Room> instead.
adjust the method setExits();
public void setExit(String dir, Room room) {exits.put(dir, room); }
change the initialization of the exits to:
outside.setExit("east", lobby); outside.setExit("south", study); outside.setExit("west", pub); lobby.setExit("west", outside); pub.setExit("east", outside); study.setExit("north", outside); study.setExit("east", bedroom); bedroom.setExit("west", study);// extended exits lobby.setExit("up", pub); pub.setExit("down", lobby);
4.5 框架加数据
进一步加强可拓展性:从程序中识别出框架和数据,以代码实现框架,将部分功能以数据的方式加载。
上一小节中的exits改动就是框架的使用。
还有一个地方也可以使用到这个理论来改良代码质量:读取指令的部分
while ( true ) {String line = in.nextLine();String[] words = line.split(" ");if ( words[0].equals("help") ) {game.printHelp();} else if (words[0].equals("go") ) {game.goRoom(words[1]);} else if (words[0].equals("bye") ) {break;}readInCmd(); }
while loop中的String-method pair可不可以也用HashMap的框架表示呢?
借助子类class中的override method来实现。
public void play() {Scanner in = new Scanner(System.in);while ( true ) {String line = in.nextLine();String[] words = line.split(" ");CmdHandler handler = handlers.get(words[0]);if (handler != null) {handler.cmdHandling();if (handler instanceof ByeHandler) {break;} else if (handler instanceof GoHandler) {if (words.length == 2) {((GoHandler)handler).cmdHandling(words[1]);} else {System.out.println("Invalid Command (Usage: go dir)\n");}}} else {System.out.println("Invalid Command\n");}}in.close();}
public class CmdHandler {public void cmdHandling() {} }public class GoHandler extends CmdHandler{private Game game;public GoHandler(Game game) {this.game = game;}public void cmdHandling(String dir) {game.goRoom(dir);} }public class HelpHandler extends CmdHandler{public void cmdHandling() {System.out.println("迷路了吗?你可以做的命令有:go bye help");System.out.println("如:\tgo east");} }public class ByeHandler extends CmdHandler{public void cmdHandling() {System.out.println("感谢您的光临。再见!");} }
之后,若是想要加入新的cmd,添加Handler的子类即可。
第5章--设计模式
5.1 注入控制反转
GUI(Graphic User Interface):
部件:创建GUI的独立部分,比如按钮、菜单、菜单项、选择条等
布局:如何在屏幕上放置组件 -- 通过layout manager布局管理器实现
容器:可以在容器frame中放置部件(需要指定位置BorderLayout)(容器本身也是部件)
事件处理:用来响应用户的输入。用户激活一个部件,系统就会产生一个事件,应用程序就可以收到关于这个时间的通知(调用一个方法)。
BorderLayout:NORTH/ SOUTH/ EAST/ WEST/ CENTER. (default-CENTER) (overwrite if the position is the same)
Swing:event handling处理用户输入。
动作事件:点击按钮或选中菜单项
鼠标事件:点击或移动鼠标
窗口事件:框架被关闭或最小化
等等。
Q: How does a JButton object invoke the methods in my class?
A: One way can be done on our own: have a class MyButton extends JButton and override the doPressed() method to invoke myMethod.
A: What Swing does: 注入反转(反转控制)
JButton class; ActionListener interface;
JButton.addActionListener()/JButton.removeActionListener().
ActionListener.actionPerformed();
implement actionPerformed()
when click the JButton, check any actionListener related
JButton knows its registered ActionListener
invoke ActionListener.actionPerformed();
// add button JButton btnStep = new JButton("下一步"); frame.add(btnStep, BorderLayout.NORTH); // which component, and where btnStep.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {step();frame.repaint();} });
上例中:ActionListener作为一个内部类(一个类定义在另一个类的内部,从而成为外部类的一个成员,具有和成员变量成员方法相同的性质)
内部类的最重要的特点就是能够访问外部类的所有成员(包括私有成员);当外部是函数时,只能访问那个函数里的final变量(?)。
此外:ActionListener也是一个匿名类(在new对象的时候给出的类的定义),匿名类可以继承某类,也可以实现某接口
5.2 MVC设计模式
JTable: separating the data and representation.
public static void main(String[] args) {JFrame frame = new JFrame();JTable table = new JTable(new CurriculumData());// JTable cannot display all of its contentJScrollPane panel = new JScrollPane(table);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.add(panel);frame.setSize(400, 400);frame.setVisible(true);}
CurriculumData is a class that stores the data
import javax.swing.event.TableModelListener; import javax.swing.table.TableModel;public class CurriculumData implements TableModel {private String[] title = {"周一","周二","周三","周四","周五","周六","周日"};private String[][] curriculum = new String[8][7];@Overridepublic int getRowCount() {return 8;}@Overridepublic int getColumnCount() {return 7;}@Overridepublic String getColumnName(int columnIndex) {// the name of each columnreturn title[columnIndex];}@Overridepublic Class<?> getColumnClass(int columnIndex) {// the data type of each columnreturn String.class;}@Overridepublic boolean isCellEditable(int rowIndex, int columnIndex) {return true;}@Overridepublic Object getValueAt(int rowIndex, int columnIndex) {// get the data value at a particular cellreturn curriculum[rowIndex][columnIndex];}@Overridepublic void setValueAt(Object aValue, int rowIndex, int columnIndex) {// set the data value at a particular cellcurriculum[rowIndex][columnIndex] = (String) aValue;}@Overridepublic void addTableModelListener(TableModelListener l) {// TODO Auto-generated method stub}@Overridepublic void removeTableModelListener(TableModelListener l) {// TODO Auto-generated method stub} }
what happens inside is: There is a Table Model object between JTable and JTableData as the interface.
|
\|/
MVC (Model, View, Control)
模型:保存和维护数据,提供接口让外部(Control)修改数据,notify表现(View)刷新呈现的数据
表现:从模型获得数据,根据数据画出表现
控制:从用户得到输入,根据输入调用Model的接口来调整数据
NB: Control和View没有直接的联系
好处:每一个部分的实现都很单纯,比如View只需知道每次全部重画一遍,无需在意修改的细节
上例中:Model (TableModel); View&Control (JTable) -- 常见MVC实现方式,将View&Control合并(都是显示部分)
转载于:https://www.cnblogs.com/FudgeBear/p/7066969.html
Java应用基础微专业-设计篇相关推荐
- cmd 将文件夹下文件剪切到另外一个文件_总结java中文件拷贝剪切的5种方式-JAVA IO基础总结第五篇...
本文是Java IO总结系列篇的第5篇,前篇的访问地址如下: 总结java中创建并写文件的5种方式-JAVA IO基础总结第一篇 总结java从文件中读取数据的6种方法-JAVA IO基础总结第二篇 ...
- 计算机英语微课,【计算机英语论文】大学计算机基础微课设计研究
<[计算机英语论文]大学计算机基础微课设计研究>由会员分享,可在线阅读,更多相关<[计算机英语论文]大学计算机基础微课设计研究(5页珍藏版)>请在装配图网上搜索. 1.计算机英 ...
- 总结java创建文件夹的4种方法及其优缺点-JAVA IO基础总结第三篇
本文是Java IO总结系列篇的第3篇,前篇的访问地址如下: 总结java中创建并写文件的5种方式-JAVA IO基础总结第一篇 总结java从文件中读取数据的6种方法-JAVA IO基础总结第二篇 ...
- 中职微课计算机应用基础,高职计算机应用基础微课设计
[摘要] 微课以"短.精.活"受到了越来越多课程的追捧,最新的教学模式"翻转课堂"的实现离不开"微课"这一关键环节,正是通过微课的学习,改变 ...
- 邮件合并应用计算机教学,高职计算机应用基础微课设计 邮件合并
教学设计授课内容所属学科: 电子信息 授课内容所属专业: 计算机通识类 授课内容所属课程: 计算机应用基础 授课题目(作品)名称: 邮件合并--胸牌制作 授课内容适用对象: 高职生 微课比赛(作品)教 ...
- java 文件 剪切_总结java中文件拷贝剪切的5种方式-JAVA IO基础总结第五篇
本文是Java IO总结系列篇的第5篇,前篇的访问地址如下: 很多朋友在看我的<java IO总结系列>之前觉得创建文件.文件夹删除文件这些基础操作真的是太简单了.但看了我的文章之后,有小 ...
- java 中io的删除文件_总结删除文件或文件夹的7种方法-JAVA IO基础总结第4篇
本文是Java IO总结系列篇的第4篇,前篇的访问地址如下: 如果您阅读完成,觉得此文对您有帮助,请给我点个赞,您的支持是我不竭的创作动力. 为了方便大家理解,我特意制作了本文对应的视频:总结删除文件 ...
- Java实现剪切MP3格式的文件_总结java中文件拷贝剪切的5种方式-JAVA IO基础总结第五篇...
本文是Java IO总结系列篇的第5篇,前篇的访问地址如下: 很多朋友在看我的<java IO总结系列>之前觉得创建文件.文件夹删除文件这些基础操作真的是太简单了.但看了我的文章之后,有小 ...
- java中怎么剪切文件_总结java中文件拷贝剪切的5种方式-JAVA IO基础总结第五篇
本文是Java IO总结系列篇的第5篇,前篇的访问地址如下: 很多朋友在看我的<java IO总结系列>之前觉得创建文件.文件夹删除文件这些基础操作真的是太简单了.但看了我的文章之后,有小 ...
- java 自动删除文件夹权限_总结删除文件或文件夹的7种方法-JAVA IO基础总结第4篇...
本文是Java IO总结系列篇的第4篇,前篇的访问地址如下: 如果您阅读完成,觉得此文对您有帮助,请给我点个赞,您的支持是我不竭的创作动力.为了方便大家理解,我特意制作了本文对应的视频:总结删除文件或 ...
最新文章
- SpringBoot2.0.3之quartz集成,不是你想的那样哦!
- python填写excel-使用Python填写Excel文件的简便方法
- Python模拟浏览器实现网页访问
- python模拟postman发https请求_【接口测试】Postman入门01-特点和简介
- 又为写作思路熬到秃头?这16篇最新论文打包送你
- jquery append 动态添加的元素事件on 不起作用的解决方案
- php strlen ctf,CTF中的PHP反序列化漏洞简单分析
- MySQL入门之数据完整性约束与表维护
- Wannafly挑战赛23 A	字符串
- Visual Assist插件使用的一些相关文章
- ProGuard详解 - Java代码混淆
- Mysql之Specified key was too long; max key length is 767 bytes
- 守望先锋ptr服务器位置,守望先锋测试服怎么进 守望先锋ptr怎么进
- Opencv基础使用3——HDR 图像合成
- 关于Android开发的面试经验总结,妈妈再也不用担心我找工作了!
- php中文的正则表达式_php汉字正则表达式
- iOS如何避免图像解压缩的时间开销
- KLayout教程(一)画不同的形状
- WCF服务通过TCP实时监控客户端状态,并可以向客户端广播推送消息,实现双向通信
- freeswitch实战八(动态生成拨号计划)
热门文章
- C++ std::thread::hardware_concurrency() 获取CPU核心数
- C++ 函数重载 参数默认值可以不写
- BOOTSTRAP学习之路(1)
- ajax怎么在服务器返回值,node.js+Ajax实现获取HTTP服务器返回数据?
- MyBatis学习(三)MyBatis的动态SQL
- javascript 遍历数组的常用方法(迭代、for循环 、for… in、for…of、foreach、map、filter、every、some,findindex)
- 一、虚拟机,CentOS安装教程,Linux文件系统结构,Linux文件命名规则
- 3.26 Tensorflow 实验记录
- SpringBoot4.5.2 整合HikariCP 数据库连接池
- jira 6.x 日期设置