This post will discuss content in Java IO streams. It is based Java SE 8.
Input Stream
Input Stream Methods
int available(): return available to read the remaining byte size.
void close(): close the input stream.
void mark(int readlimit): marks the current position in this input stream.
readlimit: the number of bytes that can be read from the stream after setting the mark before the mark becomes invalid. Note the parameter for some subclass of InputStream has no meaning.
boolean markSupported(): return if this input stream supports the mark and reset methods.
int read(): reads the next byte of data.
int read(byte[] b): reads some bytes from input stream and stores them into buffer array b.
int read(byte[] b, int off, int len): reads up to len bytes of data from input stream into array of bytes.
void reset(): repositions this stream to the position at mark (default 0)
long skip(long n): skips bytes of data.
What does Input Stream actually do?
InputStream only reads data from front to back. It can skip some data to read. It can back to specified position to read again.
InputStream can read bytes one by one or read some bytes into byte array once.
If there is no available data in InputStream, the calling one of the read methods will return -1.
int read(byte b[]) => int read(byte b[], int offset, int len), abstract int read()
read() methods can implement directly (e.g ByteArrayInputStream) or implement by calling the native method (e.g FileInputStream).
InputStream Types
ByteArrayInputStream: Byte array as data
ByteArrayInputStream(byte[] b)
FileInputStream: File as data
FileInputStream(File file)
ObjectInputStream: Deserializes primitive data and objects previously written using an ObjectOutputStream.
ObjectInputStream(InputStream in)
readObject()
PipedInputStream: A piped input stream should be connected to a piped output stream; the piped input stream then provides whatever data bytes are written to the piped output stream.
PipedInputStream()
connect(PipedOutputStream src)
StringBufferStream(Deprecated): String as data.
StringBufferInputStream(String s)
SequenceInputStream: Logical concatenation of other input streams.
long getFreeSpace(), long getTotalSpace(), long getUsableSpace(), long lastModified(), long length()
FileDescriptor
Whenever a file is opened, the operating system creates an entry to represent this file and stores its information. Each entry is represented by an integer value and this entry is termed as file descriptor. [2]
Basically, Java class FileDescriptor provides a handle to the underlying machine-specific structure representing an open file, an open socket, or another source or sink of bytes. [2]
The applications should not create FileDescriptor objects, they are mainly used in creation of FileInputStream or FileOutputStream objects to contain it. [2]
File Paths
Get file, absolute filepath and inputStream in classpath, e.g src/resources/test.txt
You can get InputStream by using ClassLoader.getResourceAsStream() method.
getClass().getClassLoader().getResource("test.txt") //relative path getClass().getResource("/test.txt")); //note the slash at the beginning
Get file, absolute file path and inputStream in JAR, e.g src/resources/test.txt
This is deliberate. The contents of the “file” may not be available as a file. Remember you are dealing with classes and resources that may be part of a JAR file or other kind of resource. The classloader does not have to provide a file handle to the resource, for example the jar file may not have been expanded into individual files in the file system. [3]
Anything you can do by getting a java.io.File could be done by copying the stream out into a temporary file and doing the same, if a java.io.File is absolutely necessary. [3]
Result: You can get input stream and File by ClassLoader, but you can’t get right usable absolute file path. If you want get a usable absolute path in JAR, you can copy resource file stream to create a new temporary file, then get absolute file path of the temporary file.
while ((read = input.read(bytes)) != -1) { out.write(bytes, 0, read); } out.close(); file.deleteOnExit(); } catch (IOException ex) { Exceptions.printStackTrace(ex); } } else { //this will probably work in your IDE, but not from a JAR file = newFile(res.getFile()); }
System.out.println(file.getAbsolutePath());
Summary
IO Streams Functionality
InputStream or Reader it read data from somewhere, e.g byte or char array, file, pipe. It can read single-unit data from the stream to return or read multiple units data from the stream to store into an array every time. read() methods can implement by java code or implements by calling native method. Filter streams use the Decorator Pattern to add additional functionality (e.g unread) that implements by internal buffer array to other stream objects.
OutputStream or Writer it write data to somewhere, e.g byte or char array, file, pipe.
IO Stream Types
Byte or Char Array
File
Object
Piped
Sequence:
Filter
Buffered: Operating with buffer array.
DataInput, DataOutput: Operating with primitive Java types
Pushback: Add unread functionality.
Print:
LineNumber
Others
class java.nio.CharBuffer using in method int read(CharBuffer target)
interface java.lang.CharSequence using in method Writer append(CharSequence csq)
Questions
InputStream vs Reader? What the real difference between them? Who has more efficient?
Stream is operating bytes, and reader or writer is operating characters. Writing streams of raw bytes such as image data using OutputStream. Writing streams of characters using Writer.
CharSequence vs String
A CharSequence is a readable sequence of char values. You can call chars() method get the InputStream.
来自 query string 和 post body 的数据是聚合在 request parameter set 中的。query string 数据表示在post body 数据之前。如:一个请求的 query string 是 http://xxx?a=hello,它的 post body 是 a=goodbye&a=world,参数集的结果将是 a=(hello, goodbye, world)。
Post 表单数据转换为 parameter set 的条件:
它是一个 HTTP or HTTPS 请求。
HTTP method 是 POST。
content type 是 application/x-www-form-urlencoded
Servlet 已对请求对象上的任何 getParameter 方法族进行了初始调用。
如果上面的条件没有全部满足,post 请求的 form data 不会包含在 parameter set中,但 post data 依然可以通过 request object 的 input stream 中获取。如果所有条件都满足,post form data 将不再能从 request object 的 input stream 中读取。parameter 只能表现为 form data 和 input stream 两种方式之一。
编写 bad code 可能会导致严重的后果。bad code 会使得团队的生产力持续降低。bad code 可能导致软件无法维护,以及存在大量 bug,最终导致软件无法正常运行。最后,不得不重新设计代码,建立一个新的团队重构旧的系统,以及跟进旧系统的改变。
产生 bad code 的原因
产生 bad code 的原因有很多,如:1)需求的改变。2)时间规划太紧,没有足够的时间去做得更好。3)对一个程序很疲倦,想要早点结束。4)手上堆积了很多其它的事,想要赶紧做完它,然后做其它事情。5)管理者的管理不当。…
作为程序开发者,我们不能一味地抱怨外部原因,我们更应该反思我们自己,我们能否做得更好。1)当软件的时间规划和安排不合理时,我们应该及时的反馈我们的想法。大部分管理者想要看到事实,以及想要 good code。2)在开发过程中,我们应该保持专业的态度,持续保证代码的整洁,就像医生做手术前要洗手一样,多花一点时间保持代码的整洁,让保证代码不会变成 bad code。唯一保证 deadline 的方法就是在任何时候尽可能地保持代码地整洁,为了加快速度编写混乱的代码最终会拖垮你的进度。
// Check to see if the employee is eligible for full benefits if ((employee.flog & HOURLY_FLAG) && (employee.age > 65))
代替为
if (employee.isEligibleForFullBenefits())
Good Comments
有些注释时有必要的或有益的。始终记住真正好的注释是找到一种方式不写注释。
常见的有必要的注释
Legal Comments。版权和作者声明是有必要的和合理的。如 // Copyright (C) 2003,2004,2005 by Object Mentor, Inc. All rights reserved.
Explanation of Intent。解释为什么这么做。如 // we are greater because we are the right type.
Warning of Consequence。警告注释。如 // Don't run unless you, have some time to kill
TODO Comments。TODO 一般用于此刻没时间做或者暂时做不了的事。它解释代码的问题和未来应该做什么。如 //TODO-MdM these are not needed // We expect this to go away when we do the checkout model
Amplification。详述注释。用于详述某些重要的事情,其他人可能认为不重要的事。// the trim is real important. It removes the starting spaces that could cause the item to be recognized as another list.
Formatting
Code formatting 是重要的,编码风格和可读性会持续影响软件的维护性和扩展性。
Vertical Formatting
行数。一个源码文件不应该超过 500 行,一般最好 200 行左右。小文件通常比大文件更容易理解。
自顶向下阅读。最上面的方法提供高层次的概念和算法。细节应该是从上到下递增的。
垂直的间隔。不同概念的一组代码应该用空行分隔。
垂直的密度。垂直密度暗示了紧密的关系,相关性高得代码应该紧密排布在一起。
垂直的距离。紧密关联的概念应该保持垂直方向接近的。1)变量声明语句应该尽可能接近它们使用的地方。2)实例变量应该声明在 class 的顶端。3)依赖的方法。一个方法调用另一个方法,它们应该在垂直方向相互接近的,调用者应该在被调者的上面。4)概念上类似。相似的内容应该在垂直方向相互接近的。如方法的重载,相同的方法名,不同的参数的方法,它们的功能是类似的。
publicclassClient{ pubilc staticvoidmain(String[] args){ Element[] elements = newElement[]{newConcreteElementA(1), newConcreteElementB(2)}; // operation 1 in elements object strucutre VisitorsumVisitor=newComputeSumConcreteVisitor1(); for (Element e : elements){ e.accept(sumVisitor); } intsum= sumVisitor.getSum(); // operation 2 in elements object strucutre VisitorproductVisitor=newComputeProductConcreteVisitor2(); for (Element e : elements){ e.accept(productVisitor); } intproduct= productVisitor.getProduct(); } }
What is plans, estimates, and schedules in project management?
Click to expand!
Plans: A project plan outlines the objectives, scope, deliverables, resources, and timelines of a project. It serves as a roadmap that guides the project team throughout the project lifecycle. A project plan includes various sections such as project scope, milestones, work breakdown structure (WBS), resource allocation, risk management, and communication plan.
Estimates: Project estimates involve estimating the time, effort, and cost required to complete project activities. These estimates are crucial for budgeting, resource allocation, and determining project feasibility. Estimation techniques such as expert judgment, analogous estimating, parametric estimating, and three-point estimating are commonly used to predict project durations, costs, and resource requirements.
Schedules: A project schedule is a timeline that details the start and end dates of project activities. It helps in visualizing the project’s progress and ensuring that tasks are completed in a logical sequence. Gantt charts, network diagrams (e.g., PERT/CPM), and critical path analysis are tools commonly used to create and manage project schedules. Schedules also consider dependencies between tasks, resource availability, and project constraints to ensure timely completion.
II. 代码实现阶段
这个阶段主要是根据软件需求文档、软件设计文档和开发流程规范文档进行项目代码开发。
主要的工作:
配置服务器。具体包括:购买服务器,搭建项目运行环境,部署项目。
配置域名。具体包括:购买域名,DNS 解析,Nginx 反向代理,HTTPS。
搭建持续集成(CI/CD)。具体包括:创建 Git 仓库,添加 gitignore,搭建或购买 CI 服务,配置 Docker,配置 Jenkins。
Union rule。 If α → β holds and α → γ holds, then α → βγ holds.
Decomposition rule。If α → βγ holds, then α → β holds and α → γ holds.
Pseudotransitivity rule。 If α → β holds and γβ → δ holds, then αγ → δ holds.
Formal Forms
常见的范式(Formal Forms)有:第一范式,第二范式,第三范式,BC 范式,和第四范式。
Atomic Domains and First Normal Form
为了减少单个属性的数据冗余,我们常用的方法是:对于组合属性,如 address 由 street,city,state 和 zip 等组成,我们创建一个单独的表来表示这些属性。对于多值属性,我们让每一个多值属性中的每一项作为一个单独的属性。
在关系模型中,我们通过形式化(Formalize)来实现一个属性没有任何子结构。如果一个 domain 是不可再分的单元称这个 domain is atomic。我们定义:如果一个 relation schema R 中的所有属性的 domain 是 atomic,则称这个 schema R 是在 First Normal Form (1NF,第一范式) 中的。
Boyce-Codd Normal Form
Boyce-Codd Normal Form (BCNF) 它消除了可以基于 functional dependencies 发现的所有数据冗余。但是可能存在一些其它类型的的冗余,需要用其它的方法来解决,如 multivalue dependencies。
如果对于来自 α → β, a ⊆ R, β ⊆ R 的 F+ 中的所有 functional dependencies 满足以下至少一项条件,则 relation schema R 的 functional dependencies F 在BCNF中: