2.1 输入 / 输出流

No relationship with java.util.stream.

抽象类 Readers/writes process characters, not bytes (P43). 

2.1.1-2.1.3 读写字节

Easiest to use static methods from the java.nio.file.Files class:

1 Path path = Paths.get(filenameString);
2 InputStream in = Files.newInputStream(path);
3 OutputStream out = Files.newOutputStream(path);

Get an input stream from any URL:

1 URL url = new URL("http://horstmann.com/index.html");
2 InputStream in = url.openStream();

Get an input stream from a byte[] array:

1 byte[] bytes = ...;
2 InputStream in = new ByteArrayInputStream(bytes);

Conversely, you can write to a ByteArrayOutputStream and then collect the bytes:

1 ByteArrayOutputStream out = new ByteArrayOutputStream();
2 Write to out
3 byte[] bytes = out.toByteArray();

The read method returns a single byte (as an int) or -1 at the end of input:

1 InputStream in = ...;
2 int b = in.read();
3 if (b != -1) { byte value = (byte) b; ...}

It is more common to read bytes in bulk:

1 byte[] bytes = ...;
2 int len = in.read(bytes);

No method for reading all bytes from a stream. Here is one solution:

1 ByteArrayOutputStream out = new ByteArrayOutputStream();
2 byte[] bytes = new byte[1024];
3 while ((len = in.read(bytes)) != -1) {out.write(bytes, 0 , len);} // -1: end of the input stream
4 bytes = out.toByteArray();

For files, just call:

1 byte[] bytes = Files.readAllBytes(path);

You can write one byte or bytes from an array:

1 OutputStream out = ...;
2 int b = ...;
3 out.write(b);
4 byte[] bytes = ...;
5 out.write(bytes);
6 out.write(bytes, start, length);

When writing to a stream, close it when you are done:

out.close();

Or better, use a try-with-resources block (resource will be automatically closed):

1 try (OutputStream out = ...) {
2 out.write(bytes);
3 }

To save an input stream to a file, call:

1 Files.copy(in, path, StandardCopyOption.REPLACE_EXISTING);

Java new feature:

  • 1. There is finally a method to read all bytes from an input stream: byte[] bytes = url.openStream().readAllBytes();
  • There is also readNBytes.
  • 2. InputStream.transferTo(OutputStream) transfer all bytes from an input stream to an output stream.
  • 3. Java 10: Reader.transferTo(Writer)
  • 4. Java 10: Character sets in PrintWriter, Scanner, etc. can be specified as Charset instead of String.  new Scanner(path, StandCharsets.UTF_8)
  • 5. Scanner.tokens gets a stream of tokens, similiar to Pattern.splitAsStream from Java 8: Stream<String> tokens = new Scanner(path).useDelimiter("\\s*,\\s*).tokens();

   

2.1.4 读写文本文件

1. Summary:

  • Input/Output streams process bytes.
  • Text files contain characters.
  • Java uses Unicode for characters.
  • Readers/writes convert between bytes and characters.
  • Always specify the character encoding. Use StandardCharsets.UTF_8 for Charset parameters, "UTF-8" for string parameters.

2. You can obtain a Reader for any input stream:

1 InputStream inStream = ...;
2 Reader in = new InputStreamReader(inStream, charset);

The read methods reads one char value, it's too low-level for most purposes.

1) You can read a short file into a string:

1 String content = new String(Files.readAllBytes(path), charset);

2) You can get all lines as a list or stream:

1 List<String> lines = Files.readAllLines(path, charset);
2
3 try (Stream<String> lines = Files.lines(path, charset)) {
4 ...
5 }

3) Use a Scanner to split input into numbers, words, and so on:

1 Scanner in = new Scanner(path, "UTF-8");
2 while (in.hasNextDouble()) {
3 double value = in.nextDouble();
4 ...
5 }

To read words, set the delimeter to any sequence of non-letters:

1 in.useDelimiter("\\PL+");
2 while (in.hasNext()) {
3 String word = in.next();
4 ...
5 }
6 Or
7 Stream<String> words = in.tokens();

3. To write to a file, make one of these calls as following. Then call out.print, out.println, or out.printf to produce output.

1 PrintWriter out = new PrintWriter(Files.newBufferedWriter(path, charset));
2
3 PrintWriter out = new PrintWriter(filenameString, charsetString);

Remeber to close the file: try (PrintWriter out = ... ) {...}

If you already have the entire output in a string, or a collection of lines, call:

1 Files.write(path, contentString.getBytes(charset));
2 Files.write(path, lines, charset);

You can also append output to a file:

1 Files.write(path, lines, charset, StandardOpenOption.APPEND);

4. Sometimes, a library method wants a Writer object. Example:

1 Throwable.printStackTrace(PrintWriter out)

If you want to capture the output in a string, not a file, use a StringWriter:

1 StringWriter writer = new StringWriter();
2 throwable.printStackTrace(new PrintWriter(writer));

Now you can process the stack trace as string:

1 String stackTrace = writer.toString();

2.2/2.5 读写二进制数据

1. 处理二进制文件

DataInput / DataOutput interfaces have methods readInt / writeInt, readDouble / writeDouble, and so on.

Can wrap any stream into a DataInputStream / DataOutputStream:

1 DataInput in = new DataInputStream(new FileInputStream(path));
2 DataOutput out = new DataOutputStream(new FileOutputStream(path));

Reading / writing stream data is sequential.

2. 随机访问文件

2.1 方式一: RandomAccessFile (section 2.2.2)

"Random access file": You can jump to any file position and start reading/writing. Open with "r" for reading or "rw" for writing:

1 RandomAccessFile file = new RandomAccessFile(filenameString, "rw");

The getFilePointer method yields the current position (as a long).

The seek method moves to a new position.

Example: Increment an integer that you just read:

1 int value = file.readInt();
2 file.seek(file.getFilePointer() - 4); // 第1句读取一个整数,此时位置偏移。此时读取当前位置 - 4(整数长度),即回到了刚才的位置
3 file.writeInt(value + 1);

2.2 方式二:内存映射文件 Memory-Mapped Files(section 2.5)

A memory-mapped file provides very efficient random access for large files. (Uses operating system mechanism for virtal memory.)

step1: Get a channel for the file:

1 FileChannel channel = FileChannel.open(path, StandardOpenOption.READ, StandOpenOption.WRITE)

step2: Map an area of the file (or all of it) into memory:

1 ByteBuffer buff = channel.map(FileChannel.MapMode.READ_WRITE, 0, channel.size());

step3: You use methods get, getInt, getDouble, and so on to read, and the equivalent put methods to write:

1 int position = ...;
2 int value = buffer.getInt(position);
3 buffer.put(position, value + 1);

The file is updated at some point, and certainly when the channel is closed.


2.4 操作文件(创建、访问、删除文件和目录): Path, Files

1.  Working with Path

Path objects specify abstract path names (which may not currently exist on disk). Sequence of directory names, optionally followed by a file name.

Use Paths.get to create paths:

1 Path absolute = Paths.get("/", "home", "cay");
2 Path relative = Paths.get("myapp", "conf", "user.properties");

Path separator / or \ is suppiled for the default file system. If you know which platform your program is running, you can provide a string with separators:

1 Path homeDirectory = Paths.get("/home/cay");

1.1. The call p.resolve(q) computes "p then q". If q is absolute, that's just q, otherwise, first follow p, then follow q:

1 Path workPath = homeDirectory.resolve("myapp/work");

1.2. The oppostie of resolve is relativize, yielding "how to get from p to q". 

1 Paths.get("/home/cay").relativize(Paths.get("/home/fred/myapp"))
2 // yields "../fred/myapp"

1.3. normalize removes . or directory/../ and other redundancies.

1.4. toAbsolutePath makes a path absolute.

2. Taking Paths Apart

Utility methods to get at the most important parts:

1 Path p = Paths.get("/home", "cay", "myapp.properties");
2 Path parent = p.getParent(); // The path /home/cay
3 Path file = p.getFileName(); // The last element, myapp.properties
4 Path root = p.getRoot(); // The initial segment / (null for a relative path)
5 Path first = p.getName(0); // The first element, home
6 Path dir = p.subpath(1, p.getNameCount()); // All but the first element, cay/myapp.properties

You can iterate over the components:

1 for (Path component : path) {
2 ...
3 }

To interoperate with legacy File class, use:

1 File file = path.toFile();
2 Path path = file.toPath();

3. Files

2.4.3 To create a new directory, call:

1 Files.createDirectory(path);    // All but the last component must exist
2 Files.createDirectories(path); // Missing components are created. 创建路径中的中间目录

You can create an empty file, If the file exists, an exception occurs. Check and creation are atomic.

1 Files.createFile(path);

Convencience methods for creating temporary files:

1 Path tempFile = Files.createTempFile(dir, prefix, suffix);
2 Path tempFile = Files.createTempFile(prefix, suffix);
3
4 Path tempDir = Files.createTempDirectory(dir, prefix);
5 Path tempDir = Files.createTempDirectory(prefix);

Files.createTempFile(null, ".txt") might return a path such as /tmp/1234405522364837194.txt.

Files.exists(path) checks whether a path currently exists.

Use Files.isDirectory(path), Files.isRegularFile(path), Files.isSymbolicLink(path) to find out whether the path is directory, file, or symlink. More infor: isHidden, isExecutable, isReadable, isWritable of the Files class.

Files.size(path) reports the file size as a long value.

2.4.4 Use the copy or move method:

1 Files.copy(fromPath, toPath);
2 Files.move(fromPath, toPath);

Can define behavior with copy options:

1 Files.copy(fromPath, toPath, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
2 Files.move(fromPath, toPath, StandardCopyOption.ATOMIC_MOVE);

Delete a file like this:

1 Files.delete(path);    // throws exception if path doesn't exist
2 boolen deleted = Files.deleteIfExists(path);

2.4.6 Files.list(dirpath) yields a Stream<Path> of the directory entries. The directory is read lazily -- efficient for huge directories. Be sure to close the stream. (Files.list 不会进入子目录)

1 try (Stream<Path> entries = Files.list(pathToDirectory)) {...}

Call Files.walk(dirpath) to visit all descendants of subdirectories as well. Descendants are visited in depth-first order.

1 try (Stream<Path> entries = Files.walk(pathToRoot)) {...}

 If you filter results by file attributes (size, creation time, and so on), use find instead of walk for greater efficiency:

1 Files.find(path, maxDepth, (path, attr) -> attr.size() > 10000)

Use Files.walk to copy a directory tree:  // JDK 目前没有提供方法来实现复制目录

Files.walk(source).forEach(p -> {
try {
Path q = target.resolve(source.relativize(p));
if (Files.isDirectory(p)) {
Files.createDirectory(q);
}
else {
Files.copy(p, q);
}
catch (IOException ex) {
throw new UncheckedIOException(ex);
}
});

Unfortunately, this approach doesn't work for deleting a directory tree. Need to vist children before deleting the parent. => Use FileVisitor instead:

 // Delete the directory tree starting at root
1 Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
2 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
3 Files.delete(file);
4 return FileVisitResult.CONTINUE;
5 }
6 public FileVisitResult postVisitDirectory(Path dir, IOException ex) throws IOException {
7 if (ex != null) { throw ex; }
8 Files.delete(dir);
9 return FileVisitResult.CONTINUE;
10 }
11 });

Paths class looks up paths in the default file system.

4. ZIP file sytem

Can have file system for the files in a ZIP archive:

1 FileSystem zipfs = FileSystems.newFileSystem(Paths.get(zipname), null);

Copy out a file if you know its name:

1 Files.copy(zipfs.getPath(sourceName), targetPath);

To list all files in an archive, walk the file tree:

1 Files.walk(zipfs.getPath("/", forEach(p -> { Process p });

Here is the magic incantation for creating a zip file:

1 Path zipPath = Paths.get("myfile.zip);
2 URI uri = new URI("jar", zipPath.toUri().toString(), null);
3 // Constructs the URI jar:file://myfile.zip
4 try (FileSystem zipfs = FileSystems.newFileSystem(uri, Collections.singletonMap("create", "true"))) {
5 // To add files, copy them into the ZIP file system
6 Files.copy(sourcePath, zipfs.getPath("/").resolve(targetPath));
7 }

5. Java 新特性

  • String.lines yields a stream of all lines in a string.
  • String.strip trims Unicode whitespace.
  • Path.of does the same as Paths.get -- more consistent and shorter.
  • Files.readString reads a file into a string.
  • OutputStream nullOutputStream() provides a null stream.
  • Analogous methods for InputStream, Reader, Writer.

2.x 处理 互联网上的数据

You can read data from a given URL. That gets you the contents of the URL(from the GET request).

1 URL url = new URL("http://hostmann.com/index.html");
2 InputStream in = url.openStream();

You need to use the URLConnection class for more complex cases:

  • Making a POST request
  • Setting request headers
  • Reading response headers
1. Get an URLConnection object:
URLConnection connection = url.openConnection(); 2. Set request properties:
connection.setRequestProperty("Accept-Charset", "UTF-8, ISO-8859-1"); 3. Send data to the server:
connection.setDoOutput(true);
try (OutputStream out = connection.getOutputStream()) { Write to out } 4. Read the response headers:
connection.connect(); // If you skipped step 3
Map<String, List<String>> headers = connection.getHeaderFields(); 5. Read the response:
try (InputStream in = connection.getInputStream()) { Read from in }

When writing to a HttpURLConnection, the default encoding is application/x-www-form-urlencoded. But you still need to encode the name/value pairs.

Suppose POST data are given in a map:

URLConnection connection = url.openConnection();
connection.setDoOutput(true);
try (Writer out = new OutputStream(connection.getOutputStream(), StandardCharsets.UTF_8)) {
boolean first = true;
for (Map.Entry<String, String> entry : postData.entrySet()) {
if (first) { first = false; }
else { out.write("&"); }
out.write(URLEncoder.encode(entry.getKey(), "UTF-8");
out.write("=");
out.write(URLEncoder.encode(entry.getValue(), "UTF-8");
}
}

Java 9 HttpClient:

// Build a client:
HttpClient client = HttpClient.newBuilder()
.fllowRedirects(HttpClient.Redirect.ALWAYS)
.build(); // Build a request:
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://horstmann.com"))
.GET()
.build(); // Get and handle response:
HttpResponse<String> reponse = client.send(request, HttpResponse.BodyHandlers.ofString()); // Asynchronous processing:
Client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.completeOnTimeout("<html></html>", 10, TimeUnit.SECONDS)
.thenAccept(response -> Process response.body());

2.7 正则表达式

2.7.1 基本语法

Regualr expressions (regex) specify string patterns.

  • The regex [Jj]e?a.+ matches Java and jealous but not Jim or ja
  • Special characters . * + ? { | ( ) [ \ ^ $
  • . matches any character, * is 0 or more, + 1 or more, ? 0 or 1 repetition
  • Use braces for other multiplicities such as {2, 4}
  • | denotes alternatives: (Java|Scala)
  • () are used for grouping
  • [...] delimit character classes, such as [A-Za-z]
  • Useful predefined character classes such as \s (space), \pL (Unicode letters), completements(补集,即与前面相反) \S, \PL
  • ^ and $ match the beginning and end of input
  • Escape special character with \ to match them literally
  • Caution: Must double-escape \ in Java strings

Two principal ways to use a regex:

  • 应用一:Find all matches within a string;
  • 应用二:Find whether the entire string matches

应用一:This loop iterates over all matches of a regex in a string:

1 Pattern pattern = Pattern.compile(regexString);
2 Matcher matcher = pattern.matcher(input);
3 while (matcher.find()) {
4 String match = matcher.group();
5 ...
6 }

    Use matcher.start(), matcher.end() to get the position of the current match in the string.

应用二:Use the matches method to check wheter a string matches a regex:

1 String regex = "[12]?[0-9]:[0-5][0-9][ap]m";
2 if (Pattern.matches(regex, input)) { ... }

Compile the regex if you need it repeatedly:

1 Pattern pattern = Pattern.compile(regex);
2 Matcher matcher = patter.matcher(input);
3 if (matcher.matches()) ...

Can turn the pattern into a predicate:

1 Stream<String> result = streamOfStrings.filter(pattern.asPredicate());

Use groups to match subexpressions. Group index values start with 1.

// Example: Match records such as: Blackwell Toaster USD29.95

// 1. Regex with groups:
// step1: notes: \p{Alnum} 是预定义字符类,等同于 [A-Za-z0-9]
(\p{Alnum}+(\s+\p{Alnum}+)*)\s+([A-Z]{3})([0-9.]*) // step2: Use the group method to get at each group"
Matcher matcher = pattern.matcher(input);
if (matcher.matches()) {
item = matcher.group(1); // Blackwell Toaster
currency = matcher.group(3);  // USD
price = matcher.group(4);    // 29.95
} // 2. Clearer with named groups:
(?<item>\p{Alnum}+(\s+\p{Alnum}+)*)\s+(?<currency>[A-Z]{3})(?<price>[0-9.]*)
// then you retrive items by name:
item = matcher.group("item");

2.7.4 分隔符分割

1 // Specify the delimiter as a regex:
2 Pattern commas = Pattern.compile("\\s*,\\s*");
3 String[] tokens = commas.split(input); // String "1, 2, 3" truns into array ["1", "2", "3"]
4
5 // Fetch result lazily for large inputs:
6 Stream<String> tokens = commas.splitAsStream(input);
7
8 // If you don't care about efficiency, just use the String.split method:
9 String[] tokens = input.split("\\s*,\\s*");

2.7.5 替换匹配

// To replace all matches, can replaceAll on the matcher
Matcher matcher = commas.matcher(input);
String result = matcher.replaceAll(", "); // If you don't care about efficiency, just use the String.replaceAll method:
String result = input.replaceAll("\s*,\s*", ", "); // Group numbers $n or names $name are replaced with the captured group:
String result = "3:45".replaceAll(
"(\\d{1,2}):(?<minutes>\\d{2})",
"$1 hours and ${minutes} minutes");

Java 9/10 关于 正则表达式的改进:

1) Matcher.stream and Scanner.findAll gets a stream of match results:

1 Pattern pattern = Pattern.compile("[^,]");
2 Stream<String> matches = pattern.match(str).results().map(MatchResult::group);
3
4 matches = new Scanner(path).findAll(pattern).map(MatchResult::group);

2) Matcher.replaceFirst / replaceAll now have a version with a replacement function:

1 String result = Pattern.compile("\\pL{4,}")
2 .matcher("Mary had a little lamb)
3 .replaceAll(m -> m.group().toUpperCase());
4 // yields "MARY had a LITTLE LAMB"

2.3 序列化

Serialization turns an object into a sequence of bytes. Deserailization is the opposite: turning a sequence of bytes into an object.

Useful for sending objects to a different computer and short-term storage (e.g. cache). Not intended for long-term storage.

Participating classes implement the serializable marker interface:

1 public class Employee implements Serializable { ... }
// 1. 输出流
// 1.1 Construct an ObjectOutputStream object:
ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(path)); // 1.2 Call the writeObject method:
Employee peter = new Employee("Peter", 90000);
Employee paul = new Manager("Paul", 180000);
out.writeObject(peter);
out.writeObject(paul); // 2. 输入流
// 2.1 Construct an ObjectInputStream object:
ObjectInputStream in = new ObjectInputStream(Files.newInputStream(path)); // 对于 Employee 类,其包含字符串和浮点数,这些都是可串行化的 // 2.2 Retrieve the objects in the same order as they were saved:
Employee e1 = (Employee) in.readObject();
Employee e2 = (Employee) in.readObject();

使用 writeObject 方法写这些对象,要想正常工作,需要满足两个条件:

  • 1. 这个类需要实现 Serializable 接口;
  • 2. 这个类的所有实例变量也必须是可串行化的;
// Consider this network of objects, 一个对象被多个对象共享时: 需要保存这样的对象网络
Employee peter = new Employee("Peter", 40000);
Manager paul = new Manager("Paul", 105000);
Manager mary = new Manager("Mary", 180000);
paul.setAdmin(peter);
mary.setAdmin(peter);
ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(path));
out.writeObject(peter);
out.writeObject(paul);
out.writeObject(mary); // When reading the managers back in, both of them need to have some admin.
// Each object gets a serial number.
// Each object reference is saved with that serial number.
// When reloading object references, they are matched up with the deserialized object with that serial number.

上面备注的英文的完整的中文解释是:

对象序列化的算法是:

1)保存时:

  • 对遇到的每一个对象都关联一个序列化(serial number);
  • 对于每一个对象,当第一次遇到时,保存其对象数据到输出流中;
  • 如某个对象之前被保存过,只写出“与之前保存过的序列号为 x 的对象相同”

2)读出时:

  • 对于对象输入流中的对象,在第一次遇到其序列号时,构建它,并使用流中数据来初始化它,然后记录这个顺序号和新对象之间的关联;
  • 当遇到“与之前保存过的序列号为 x 的对象相同”这一标记,获取与这个序列号相关联的对象引用;

Declare fields that shouldn't be serialized with the transient modifier.

You can take over serialization of fields by implementing the readObject / writeObject methods. (Useful for saving instances of non-serializable classes.)

You can delegate serialization and deserialization to a proxy by implementing the readResolve/writeReplace methods. (Useful in rare cases when object identity needs to be preserved.)

You can declare multiple versions of serializations.

  • Default serialVersionUID is obtained by hashing fields names and types.
  • If the serialVersionUID changes, readObject throws an exception.
  • You can declare your own version ID and implement deserialization to conside multiple versions.  private static final long serialVersionUID = 2L;    // Version 2
  • Complex and raraly useful.

序列化的其他参考: https://www.cnblogs.com/bruce-he/p/17098132.html

读后笔记 -- Java核心技术(第11版 卷 II) Chapter2 输入与输出的更多相关文章

  1. Java I/O系统学习系列二:输入和输出

    编程语言的I/O类库中常使用流这个抽象概念,它代表任何有能力产出数据的数据源对象或者是有能力接收数据的接收端对象.“流”屏蔽了实际的I/O设备中处理数据的细节. 在这个系列的第一篇文章:<< ...

  2. java核心技术(第十版卷一)笔记(纯干货!)

    这是我读过的第三本关于java基础的书.第一本<<java从入门到精通>>这本书让我灵识初开.第二本<<java敏捷开发>>这本书则是有一次被一位师傅批 ...

  3. java核心技术第十版 笔记

    1.java区分大小写 2.类名是以大写字母开头 (驼峰) 3.http://docs.oracle.com/javase/specs  java语言规范 4. /* */ 注释不能嵌套 5. Jav ...

  4. Python核心技术与实战——四|Python黑箱:输入与输出

    抽象的看,Python程序可以被看成一个黑箱:通过输入流将数据送达,经过处理后在输入,也就是说具备了一个图灵机运作的必要条件. 输入输出基础 最简单的输入是来自键盘的操作 name = input(' ...

  5. Java初学者作业——声明变量对个人信息进行输入和输出

    返回本章节 返回作业目录 需求说明: 声明变量存储个人信息(姓名.年龄.性别.地址以及余额),通过键盘输入个人信息并存储在相应的变量中, 最后将个人信息输出. 实现思路: 声明存储姓名.年龄.性别.地 ...

  6. 《Java核心技术 卷II 高级特性(原书第9版)》

    <Java核心技术 卷II 高级特性(原书第9版)> 基本信息 原书名:Core Java Volume II—Advanced Features(Ninth Edition) 作者: ( ...

  7. Java核心技术·卷 II(原书第10版)分享下载

    Java核心技术·卷 II 内容介绍 Java领域最有影响力和价值的著作之一,由拥有20多年教学与研究经验的资深Java技术专家撰写(获Jolt大奖),与<Java编程思想>齐名,10余年 ...

  8. java系列:《java核心技术 卷1》学习笔记,chapter 11 调试技巧

    11. 6 调试技巧 1)一个不太为人所知却非常有效的技巧是在每个类中放一个main方法,这样就可以对每个类进行单元测试.这个方法可以保留,因为在java虚拟机只调用启动类的main方法. 2)   ...

  9. 《Java核心技术卷I》观赏指南

    Tomxin7 如果你有想看书的计划,但是还在纠结哪些书值得看,可以简单看看"观赏指南"系列,本文会简单列出书中内容,给还没有买书的朋友提供一个参考. 前言 秋招过去很久了,虽然在 ...

  10. Java核心技术卷阅读随笔--第4章【对象与类】

    对 象 与 类 4.1 面向对象程序设计概述 面向对象程序设计(简称 OOP) 是当今主流的程序设计范型, 它已经取代了 20 世纪 70 年代的" 结构化" 过程化程序设计开发技 ...

随机推荐

  1. Cesium鼠标移动到模型上,给模型添加高亮轮廓(四)

    2023-01-09 Cesium虽然也支持两种方式(Entity和Primitive)加载3D Tiles数据, 但因为多数情况下3D Tiles数据都是成片区的数据,数据量比较大,所以为了保证性能 ...

  2. [NOIP2018 提高组] 保卫王国

    题解 两只 \(\log\) 的动态 \(dp\) ! 相比标算倍增 动态 \(dp\) 既实用又好理解 \(Code\) #include<cstdio> #include<ios ...

  3. DESIR队列:早期axSpA的脊柱放射学进展

    DESIR队列:早期axSpA的脊柱放射学进展 EULAR2015; PresentID: FRI0234 SPINAL RADIOGRAPHIC PROGRESSION IN EARLY AXIAL ...

  4. 【PyCharm】配置 Git

    一.前提条件 本地先安装好:PyCharm 和 Git 二.操作步骤 1.打开 File -> Settings -> Version Control -> Git,在 Path t ...

  5. java struts2框架漏洞合集

    目录 struts2 s2-016 payload 数据包 返回结果 struts2 s2-016 参考:https://github.com/vulhub/vulhub/blob/master/st ...

  6. vue中如何在子组件添加类似于watch属性监听父组件数据,数据变化时子组件做出相应的动作

    首先:我们需要在父组件中标签中定义一个 ref="parentObjVue" 其次:我们在子组件中,通过  var tmp=this.$refs.parentObjVue找到父组件 ...

  7. Django框架大全

    Django框架大全 django数据模型models中on_delete, db_constraint的使用 第一篇:Django简介 第二篇:Django之路由层 第三篇:Django之视图层 第 ...

  8. python中周日历与时间的相互转换

    python中周日历与时间的相互转换 周日历(ISO国际标准)介绍 在线周日历(2022年) 基本介绍 在开发过程中,有些汇总咨询需要以周为单位统计,所以介绍下如何进行相互转换. 使用datetime ...

  9. 配置python库源为清华源

    目录 Windows Ubuntu pip较低版本 pip较高版本 Windows %HOMEPATH%/pip/pip.ini [global] index-url = https://pypi.t ...

  10. 靶机练习5: Sar

    靶机地址 https://www.vulnhub.com/entry/sar-1,425/ 信息收集阶段 进行全端口扫描,枚举目标的端口和服务 nmap -n -v -sS --max-retries ...