提交 4d51a80d 编写于 作者: W wizardforcel

2020-05-20 17:51:14

上级 9e1a3cb4
......@@ -142,7 +142,7 @@ static Float PI = 3.14f;
## 4\. 最佳做法
1. 使用 Java 变量命名约定并遵循最佳实践。
2. 将原始类型用于范围内局部变量。 例如 内部方法,用于循环和中间结果的计数器。
2. 将原始类型用于范围内局部变量。 例如内部方法,用于循环和中间结果的计数器。
3. 在方法或类之间传输数据时,最好使用对象,因为只会复制它们的引用,而不会增加内存开销。
4. 在处理集合(需要对象)时,应使用对象。
5. 通过网络发送数据时,请使用对象并使它们成为**可序列化的**。 包装器类可自动序列化。
......
......@@ -61,7 +61,7 @@ public class HelloWorld
```
在上述`HelloWorld`的示例中,变量`format`被声明为`protected`,因此可以由存在`HelloWorld.java`的同一包中存在的所有类以及存在于其他软件包中的子类来访问它
在上述`HelloWorld`的示例中,变量`format`被声明为`protected`,因此可以由存在`HelloWorld.java`的同一包中存在的所有类以及存在于其他软件包中的子类来访问它。
#### 1.3 默认(私有包)
......
......@@ -6,7 +6,7 @@
例如,`HashMap`存储键值对。 它为您提供了两种方法`get()``put()`方法,用于从地图存储和检索键值对。 实际上,这是您想要在应用程序中使用地图时所需的唯一信息。 它是如何工作的,不需要使用它就知道。 这是 Java 中非常抽象的示例。
再看一个**现实生活中的抽象示例**,它可以是电视遥控器。 您知道,当您按遥控器上的任何按钮时,电视上都会应用某些功能,例如 更改频道,更改音量级别等。不需要了解远程内部的工作原理,即可正确使用它。 这是一个抽象的例子。
再看一个**现实生活中的抽象示例**,它可以是电视遥控器。 您知道,当您按遥控器上的任何按钮时,电视上都会应用某些功能,例如更改频道,更改音量级别等。不需要了解远程内部的工作原理,即可正确使用它。 这是一个抽象的例子。
```java
Table of Contents
......@@ -36,7 +36,7 @@ Table of Contents
数据抽象是创建复杂数据类型并仅公开有意义的操作以与数据类型进行交互的方式,而将所有实现细节都隐藏在外部工作中。
这种方法的好处在于可以随着时间的流逝改善实施的能力,例如 解决性能问题无所不包。 想法是这样的更改不应该对客户端代码产生任何影响,因为它们在抽象行为上没有任何区别。
这种方法的好处在于可以随着时间的流逝改善实施的能力,例如解决性能问题无所不包。 想法是这样的更改不应该对客户端代码产生任何影响,因为它们在抽象行为上没有任何区别。
2. #### 控制抽象
......
......@@ -48,7 +48,7 @@ public static long Math.max(long a, long b){..}
#### 参数计数
接受不同数量参数的函数。 例如 在员工管理应用程序中,工厂可以采用以下方法:
接受不同数量参数的函数。 例如在员工管理应用程序中,工厂可以采用以下方法:
```java
EmployeeFactory.create(String firstName, String lastName){...}
......@@ -61,7 +61,7 @@ EmployeeFactory.create(Integer id, String firstName, String lastName){...}
**运行时多态性基本上被称为*方法覆盖*。** 方法覆盖是在程序中实现继承时获得的功能。
一个简单的例子可以来自现实世界,例如 动物。 应用程序可以具有`Animal`类,以及其专门的子类,例如`Cat``Dog`。 这些子类将覆盖`Animal`类提供的默认行为及其某些特定行为。
一个简单的例子可以来自现实世界,例如动物。 应用程序可以具有`Animal`类,以及其专门的子类,例如`Cat``Dog`。 这些子类将覆盖`Animal`类提供的默认行为及其某些特定行为。
```java
public class Animal {
......
......@@ -66,7 +66,7 @@ num1 = num2;
```
**如果要将`long`变量的值分配给`int`变量,则必须在代码**中明确提及这一事实,以便 Java 确保您知道可能存在数据 溢出。 您可以使用 Java 中的“类型转换”来执行此操作,如下所示:
**如果要将`long`变量的值分配给`int`变量,则必须在代码**中明确提及这一事实,以便 Java 确保您知道可能存在数据溢出。 您可以使用 Java 中的“类型转换”来执行此操作,如下所示:
```java
long num1 = (int) num2; // Now it is fine because of the "(int)" cast
......
......@@ -131,7 +131,7 @@ JDK 设计人员通过制作`HttpServlet`抽象类来解决此问题。 它具
#### 4.3 例
我想以集合框架中的`Map`接口为例。 它仅提供规则,以及地图在实践中应如何表现。 例如 它应该存储键-值对,应该可以使用键等访问该值。这些规则在接口中采用抽象方法的形式。
我想以集合框架中的`Map`接口为例。 它仅提供规则,以及地图在实践中应如何表现。 例如它应该存储键-值对,应该可以使用键等访问该值。这些规则在接口中采用抽象方法的形式。
所有实现类(例如 [HashMap](//howtodoinjava.com/java/collections/how-hashmap-works-in-java/ "How hashmap works in java") ,HashTable,TreeMap 或 [WeakHashMap](https://docs.oracle.com/javase/7/docs/api/java/util/WeakHashMap.html "WeakHashMap") )都不同地实现了所有方法,因此与其他方法相比具有不同的功能。
......
......@@ -2,7 +2,7 @@
> 原文: [https://howtodoinjava.com/java-concurrency-tutorial/](https://howtodoinjava.com/java-concurrency-tutorial/)
简而言之, [**并发**](https://en.wikipedia.org/wiki/Concurrency_%28computer_science%29) 是并行运行多个程序或程序的多个部分的能力。 并发使程序可以通过利用底层操作系统和机器硬件的未开发功能来实现高性能和吞吐量。 例如 现代计算机在一个 CPU 中具有多个 CPU 或多个内核,程序可以将所有内核用于处理的某些部分; 因此,与顺序处理相比,可以更早地完成任务。
简而言之, [**并发**](https://en.wikipedia.org/wiki/Concurrency_%28computer_science%29) 是并行运行多个程序或程序的多个部分的能力。 并发使程序可以通过利用底层操作系统和机器硬件的未开发功能来实现高性能和吞吐量。 例如现代计算机在一个 CPU 中具有多个 CPU 或多个内核,程序可以将所有内核用于处理的某些部分; 因此,与顺序处理相比,可以更早地完成任务。
**Java 并发**的主干是线程。 线程是一个轻量级进程,它具有自己的调用堆栈,但可以访问同一进程中其他线程的共享数据。 Java 应用程序默认在一个进程中运行。 在 Java 应用程序中,您可以使用许多线程来实现并行处理或并发。
......
......@@ -2,7 +2,7 @@
> 原文: [https://howtodoinjava.com/java/multi-threading/concurrency-vs-parallelism/](https://howtodoinjava.com/java/multi-threading/concurrency-vs-parallelism/)
**并发**表示多个任务,这些任务在重叠的时间段内以无特定顺序启动,运行和完成。 **并行性**是指多个任务或唯一任务的多个部分在字面上同时运行的情况,例如 在多核处理器上。 请记住,并发和并行性不是一回事。
**并发**表示多个任务,这些任务在重叠的时间段内以无特定顺序启动,运行和完成。 **并行性**是指多个任务或唯一任务的多个部分在字面上同时运行的情况,例如在多核处理器上。 请记住,并发和并行性不是一回事。
让我们更详细地了解当我说**并发与并行**时的意思。
......@@ -10,7 +10,7 @@
当我们谈论至少两个或更多任务时,并发本质上是适用的。 当一个应用程序实际上可以同时执行两个任务时,我们将其称为并发应用程序。 尽管这里的任务看起来像是同时运行的,但实际上它们可能不一样。 它们利用操作系统的 **CPU 时间分段**功能,其中每个任务运行其任务的一部分,然后进入等待状态。 当第一个任务处于等待状态时,会将 CPU 分配给第二个任务以完成其一部分任务。
因此,操作系统根据任务的优先级分配 CPU 和其他计算资源,例如 记忆; 依次处理所有任务,并给他们完成任务的机会。 对于最终用户,似乎所有任务都是并行运行的。 这称为并发。
因此,操作系统根据任务的优先级分配 CPU 和其他计算资源,例如记忆; 依次处理所有任务,并给他们完成任务的机会。 对于最终用户,似乎所有任务都是并行运行的。 这称为并发。
## 并行性
......
......@@ -6,7 +6,7 @@ Java 5 中最好的补充之一是`AtomicInteger`,`AtomicLong`等类中支持
## 1\. 乐观锁和悲观锁
传统的锁定机制,例如 在 Java 中使用*同步的*关键字的**被称为锁定或多线程的悲观技术**。 它要求您首先保证在特定操作之间没有其他线程会干扰(即锁定对象),然后仅允许您访问任何实例/方法。
传统的锁定机制,例如在 Java 中使用*同步的*关键字的**被称为锁定或多线程的悲观技术**。 它要求您首先保证在特定操作之间没有其他线程会干扰(即锁定对象),然后仅允许您访问任何实例/方法。
> 这就像说“请先关上门; 否则,其他骗子会进来重新整理您的东西。”
......
......@@ -4,7 +4,7 @@
[**Java 并发**](https://howtodoinjava.com/java-concurrency-tutorial/ "multithreading in java") 是一个非常复杂的主题,在编写处理在任何给定时间访问一个/多个共享资源的多个线程的应用程序代码时,需要引起很多关注。 Java 5 引入了诸如 [**BlockingQueue**](https://howtodoinjava.com/java/multi-threading/how-to-use-blockingqueue-and-threadpoolexecutor-in-java/ "How to use BlockingQueue and ThreadPoolExecutor in java")**Executors** 之类的类,它们通过提供易于使用的 API 消除了一些复杂性。
使用并发类的程序员会比使用 **wait()****notify()****notifyAll()**方法调用直接处理同步内容的程序员更有信心。 我还将建议您自己通过同步使用这些较新的 API,但是由于种种原因,我们经常需要这样做,例如 维护旧版代码。 掌握这些方法的丰富知识将在您到达这种情况时为您提供帮助。
使用并发类的程序员会比使用 **wait()****notify()****notifyAll()**方法调用直接处理同步内容的程序员更有信心。 我还将建议您自己通过同步使用这些较新的 API,但是由于种种原因,我们经常需要这样做,例如维护旧版代码。 掌握这些方法的丰富知识将在您到达这种情况时为您提供帮助。
在本教程中,我将讨论 Java 中 wait()notify()notifyall()的**用途。 我们将了解 wait 和 notify** 之间的**区别。**
......
......@@ -18,7 +18,7 @@
**Monitor 是一个同步构造,它允许线程具有互斥(使用锁)和协作**,即使线程能够等待某些条件成立的能力(使用 **wait-set** ) 。
换句话说,每个 Java 对象与实现锁的数据在逻辑上均与实现`wait-set`的数据相关联。 锁可以帮助线程在共享数据上独立工作而不会互相干扰,而等待集可以帮助线程相互协作以共同努力实现一个共同的目标,例如 所有等待线程都将移至该等待集,一旦释放锁定,所有通知线程都将得到通知。 **此等待集通过锁定(mutex)的附加帮助来帮助构建监视器。**
换句话说,每个 Java 对象与实现锁的数据在逻辑上均与实现`wait-set`的数据相关联。 锁可以帮助线程在共享数据上独立工作而不会互相干扰,而等待集可以帮助线程相互协作以共同努力实现一个共同的目标,例如所有等待线程都将移至该等待集,一旦释放锁定,所有通知线程都将得到通知。 **此等待集通过锁定(mutex)的附加帮助来帮助构建监视器。**
#### 互斥
......
......@@ -2,7 +2,7 @@
> 原文: [https://howtodoinjava.com/java/multi-threading/inter-thread-communication-using-piped-streams-in-java/](https://howtodoinjava.com/java/multi-threading/inter-thread-communication-using-piped-streams-in-java/)
Java 线程间通信在很长一段时间以来一直是热门的[面试问题](https://howtodoinjava.com/java-interview-questions/)。 在 JDK 1.5 版本中, [ExecutorService](https://howtodoinjava.com/java/multi-threading/java-executor-framework-tutorial-and-best-practices/)[BlockingQueue](https://howtodoinjava.com/java/multi-threading/how-to-use-blockingqueue-and-threadpoolexecutor-in-java/)[带来了另一种更有效的实现方式](//howtodoinjava.com/java-5/how-to-use-blockingqueue-and-threadpoolexecutor-in-java/ "How to use BlockingQueue and ThreadPoolExecutor in java"),但管道流方法也值得了解,在某些情况下可能有用 场景
Java 线程间通信在很长一段时间以来一直是热门的[面试问题](https://howtodoinjava.com/java-interview-questions/)。 在 JDK 1.5 版本中, [ExecutorService](https://howtodoinjava.com/java/multi-threading/java-executor-framework-tutorial-and-best-practices/)[BlockingQueue](https://howtodoinjava.com/java/multi-threading/how-to-use-blockingqueue-and-threadpoolexecutor-in-java/)[带来了另一种更有效的实现方式](//howtodoinjava.com/java-5/how-to-use-blockingqueue-and-threadpoolexecutor-in-java/ "How to use BlockingQueue and ThreadPoolExecutor in java"),但管道流方法也值得了解,在某些情况下可能有用。
```java
Table of contents
......
......@@ -49,7 +49,7 @@ Selector selector = Selector.open();
##### 3)IO 与 NIO API
猜测使用 NIO 时 API 调用与使用 IO 时看起来不同的猜测是没有根据的。 在 NIO 中,而不是从例如 如果是 InputStream,则必须首先将数据读入缓冲区,然后再对其进行处理。
猜测使用 NIO 时 API 调用与使用 IO 时看起来不同的猜测是没有根据的。 在 NIO 中,而不是从例如如果是 InputStream,则必须首先将数据读入缓冲区,然后再对其进行处理。
使用标准 IO 的示例代码
......@@ -129,7 +129,7 @@ public class ReadFileWithFixedSizeBuffer
NIO 允许您仅使用一个(或更少)线程来管理多个通道,但是代价是解析数据可能比使用标准 IO 从阻塞流中读取数据时要复杂得多。
如果您需要同时管理数千个打开的连接(每个连接仅发送少量数据),例如聊天服务器,则在 NIO 中实现该服务器可能是一个优势。 同样,如果您需要保持与其他计算机的大量开放连接,例如 在 P2P 网络中,使用单个线程来管理所有出站连接可能是一个优势。
如果您需要同时管理数千个打开的连接(每个连接仅发送少量数据),例如聊天服务器,则在 NIO 中实现该服务器可能是一个优势。 同样,如果您需要保持与其他计算机的大量开放连接,例如在 P2P 网络中,使用单个线程来管理所有出站连接可能是一个优势。
如果您只有很少的连接且带宽很高,那么一次发送大量数据,则应该选择标准 IO 服务器实现。
......
......@@ -8,7 +8,7 @@
任何严肃的文件提供者都提供一种机制,以使可下载文件具有校验和。 校验和是一种机制的形式,可确保正确下载我们下载的文件。 校验和就像文件有效性的证明一样,因此**如果文件损坏,则该校验和将更改**,因此,我们可以得知该文件不是同一文件,或者由于任何原因在传输之间文件都已损坏。
您还可以创建文件的校验和,以检测第三方对文件的任何可能更改,例如 许可证文件。 您向客户端提供许可证,这些许可证可以上传到您的服务器。 您可以交叉验证文件的校验和以验证许可证文件在创建后未被修改。
您还可以创建文件的校验和,以检测第三方对文件的任何可能更改,例如许可证文件。 您向客户端提供许可证,这些许可证可以上传到您的服务器。 您可以交叉验证文件的校验和以验证许可证文件在创建后未被修改。
> **阅读更多: [MD5,SHA,PBKDF2,BCrypt 示例](https://howtodoinjava.com/security/how-to-generate-secure-password-hash-md5-sha-pbkdf2-bcrypt-examples/)**
......
......@@ -119,7 +119,7 @@ Locale.setDefault(Locale.FRANCE);
`Locale.Category`枚举表示两个区域设置类别:
1. `Locale.Category.DISPLAY` –适用于**应用程序的用户界面**,例如 资源束消息。
1. `Locale.Category.DISPLAY` –适用于**应用程序的用户界面**,例如资源束消息。
2. `Locale.Category.FORMAT` –用于日期和数字格式,具体取决于特定的区域信息
```java
......
......@@ -26,7 +26,7 @@ Table of Contents
## 1.1 枚举是保留关键字
Java 中的`enum`是保留关键字。 这意味着您不能定义名称为`enum`的变量。 例如 这将导致编译时错误`"invalid VariableDeclaratorId"`
Java 中的`enum`是保留关键字。 这意味着您不能定义名称为`enum`的变量。 例如这将导致编译时错误`"invalid VariableDeclaratorId"`
![enum is reserved keyword](img/9256ebf45958fca9f9e2e8062a4fbd5d.png)
......
......@@ -45,7 +45,7 @@ Table of Contents
Java 迫使您在应用程序代码中以某种方式处理这些错误情况。 一旦您开始编译程序,它们将立即出现在您的脸上。 您绝对可以忽略它们,而让它们传递给 JVM,但这是一个坏习惯。 理想情况下,您必须在应用程序内部的适当级别上处理这些异常,以便可以通知用户有关失败的信息并要求他重试/稍后再来。
通常,检查的异常表示程序无法直接控制的错误情况。 它们通常发生在与外部资源/网络资源例如 数据库问题,网络连接错误,文件丢失等
通常,检查的异常表示程序无法直接控制的错误情况。 它们通常发生在与外部资源/网络资源例如数据库问题,网络连接错误,文件丢失等
> 检查的异常是**异常**类的子类。
......@@ -121,7 +121,7 @@ public static void main(String[] args)
## 3\. Java 异常处理最佳实践
1. **当方法无法执行其名称所说明的功能时,可以使用检查的异常**。 例如 预先准备好配置文件并使用配置文件进行配置的名为 prepareSystem()的方法可以声明抛出`FileNotFoundException`,这意味着该方法使用了文件系统中的配置文件。
1. **当方法无法执行其名称所说明的功能时,可以使用检查的异常**。 例如预先准备好配置文件并使用配置文件进行配置的名为 prepareSystem()的方法可以声明抛出`FileNotFoundException`,这意味着该方法使用了文件系统中的配置文件。
2. 理想情况下,绝对不应将检查异常用于编程错误,而在此类情况下,绝对不应将**资源错误**用于流控制。
3. 仅抛出方法无法以任何方式处理的那些异常。 方法应首先尝试在遇到它时立即对其进行处理。 仅当无法处理内部方法时才引发异常。
4. A good way to define method signatures is to declare exceptions close to method name. If your method is named `openFile`, then it is expected to throw `FileNotFoundException`?. If your method is named `findProvider`, then it is expected to throw `NoSuchProviderException`.
......
......@@ -93,7 +93,7 @@ class DemoClass {
```
在这里,我们希望一旦使用某种类型初始化了该类,则该类应仅与该特定类型一起使用。 例如 如果我们要让一个类的实例保存类型为“ `String`”的值 t,那么程序员应该设置并获取唯一的`String`类型。 由于我们已将属性类型声明为`Object`,因此无法强制执行此限制。 程序员可以设置任何对象。 由于所有 Java 类型都是`Object`类的子类型,因此可以从 get 方法获得任何返回值类型。
在这里,我们希望一旦使用某种类型初始化了该类,则该类应仅与该特定类型一起使用。 例如如果我们要让一个类的实例保存类型为“ `String`”的值 t,那么程序员应该设置并获取唯一的`String`类型。 由于我们已将属性类型声明为`Object`,因此无法强制执行此限制。 程序员可以设置任何对象。 由于所有 Java 类型都是`Object`类的子类型,因此可以从 get 方法获得任何返回值类型。
要强制执行此类型限制,我们可以使用以下泛型:
......@@ -281,7 +281,7 @@ ArrayList<?> list = new ArrayList<Employee>();
##### 上限通配符
例如,假设您要编写一种适用于列表<字符串>,列表<整数>和列表<双>的方法,则可以使用上限通配符来实现 例如 您将指定列表<吗 扩展 Number >。 这里的 Integer,Double 是 Number 类的子类型。 用通俗易懂的话来说,如果您希望通用表达式接受特定类型的所有子类,则可以使用“ **extended** ”关键字来使用上限通配符。
例如,假设您要编写一种适用于列表<字符串>,列表<整数>和列表<双>的方法,则可以使用上限通配符来实现 例如您将指定列表<吗 扩展 Number >。 这里的 Integer,Double 是 Number 类的子类型。 用通俗易懂的话来说,如果您希望通用表达式接受特定类型的所有子类,则可以使用“ **extended** ”关键字来使用上限通配符。
```java
public class GenericsExample<T>
......
......@@ -133,7 +133,7 @@ public class GenericsExamples
我们可以在篮子内添加苹果,甚至是亚洲苹果,但不能在篮子中添加 Fruit(苹果的超级类型)。 为什么?
原因是购物篮是**对 Apple** 的超类商品列表的引用。 同样,**我们不知道它是**是哪个超类型,但是我们知道可以将 Apple 及其任何子类型(它们是 Fruit 的子类型)添加为没有问题(*,您可以随时添加一个子类型 在超型*的集合中)。 因此,现在我们可以在购物篮中添加任何类型的 Apple。
原因是购物篮是**对 Apple** 的超类商品列表的引用。 同样,**我们不知道它是**是哪个超类型,但是我们知道可以将 Apple 及其任何子类型(它们是 Fruit 的子类型)添加为没有问题(您可以随时在超类的集合中添加一个子类)。 因此,现在我们可以在购物篮中添加任何类型的 Apple。
如何从这种类型的数据中获取数据呢? 事实证明,您唯一可以使用的是`Object`实例:由于我们无法知道它是哪个超类型,因此编译器只能保证它将是对`Object`的引用,因为`Object`是任何 Java 类型的超类型。
......
......@@ -53,7 +53,7 @@ Summary
1. **标记活动对象** –找出所有仍然存在的对象。
2. **删除无法访问的对象** –摆脱所有其他东西–所谓的已死和未使用的对象。
首先,GC 将某些特定对象定义为**垃圾收集根**。 例如 当前执行方法的局部变量和输入参数,活动线程,已加载类的静态字段和 JNI 引用。 现在,GC 遍历了内存中的整个对象图,从这些根开始,然后是从根到其他对象的引用。 GC 访问的每个对象都被标记为活动对象。
首先,GC 将某些特定对象定义为**垃圾收集根**。 例如当前执行方法的局部变量和输入参数,活动线程,已加载类的静态字段和 JNI 引用。 现在,GC 遍历了内存中的整个对象图,从这些根开始,然后是从根到其他对象的引用。 GC 访问的每个对象都被标记为活动对象。
> 需要停止应用程序线程以进行标记,因为如果它不断变化,它将无法真正遍历图形。 它被称为 **Stop The World pause** 。
......@@ -153,7 +153,7 @@ G1(垃圾优先)垃圾收集器已在 Java 7 中提供,旨在长期替代
G1 跟踪每个区域包含的实时数据量。 此信息用于确定包含最多垃圾的区域。 因此它们是首先收集的。 这就是为什么将其命名为**垃圾优先**集合的原因。
与其他算法一样,不幸的是,压缩操作是使用 *Stop the World* 方法进行的。 但是,根据设计目标,您可以为其设置特定的性能目标。 您可以配置暂停持续时间,例如 在任何给定的秒内不超过 10 毫秒。 垃圾优先 GC 将尽最大可能(但不能确定,由于 OS 级线程管理,这很难实时实现)来尽力实现该目标。
与其他算法一样,不幸的是,压缩操作是使用 *Stop the World* 方法进行的。 但是,根据设计目标,您可以为其设置特定的性能目标。 您可以配置暂停持续时间,例如在任何给定的秒内不超过 10 毫秒。 垃圾优先 GC 将尽最大可能(但不能确定,由于 OS 级线程管理,这很难实时实现)来尽力实现该目标。
如果要在 Java 7 或 Java 8 计算机中使用,请使用 JVM 参数,如下所示:
......
......@@ -2,7 +2,7 @@
> 原文: [https://howtodoinjava.com/java/serialization/java-externalizable-example/](https://howtodoinjava.com/java/serialization/java-externalizable-example/)
默认的 Java 序列化效率不高。 如果您序列化了一个具有许多属性和属性的 object 肿对象,则您不希望出于任何原因进行序列化(例如,它们始终被分配默认值),您将需要处理繁重的对象并通过网络发送更多字节,这在某些情况下可能会非常昂贵 案件
默认的 Java 序列化效率不高。 如果您序列化了一个具有许多属性和属性的 object 肿对象,则您不希望出于任何原因进行序列化(例如,它们始终被分配默认值),您将需要处理繁重的对象并通过网络发送更多字节,这在某些情况下可能会非常昂贵。
要解决此问题,您可以通过实现[可外部化](https://docs.oracle.com/javase/7/docs/api/java/io/Externalizable.html)接口并覆盖其方法`writeExternal()``readExternal()`来编写自己的序列化逻辑。 通过实现这些方法,您将告诉 JVM 如何编码/解码对象。
......
......@@ -2,7 +2,7 @@
> 原文: [https://howtodoinjava.com/java/serialization/externalizable-vs-serializable/](https://howtodoinjava.com/java/serialization/externalizable-vs-serializable/)
知道 Externalizable 与 Serializable 之间的**差异在两个方面都很重要,一个是(如果可以作为面试问题询问,另外两个则是)您可以利用该知识做出更明智的决策,以将序列化应用到您的性能中 应用。**
知道 Externalizable 与 Serializable 之间的差异在两个方面都很重要,一个是可以作为面试问题询问,另外一个是您可以利用该知识做出更明智的决策,将序列化应用到您的应用中来追求性能提升。
## 1\. Externalizable 与 Serializable 之间的区别
......
......@@ -2,7 +2,7 @@
> 原文: [https://howtodoinjava.com/java-regular-expression-tutorials/](https://howtodoinjava.com/java-regular-expression-tutorials/)
**正则表达式**用作字符串的**搜索模式**。 使用正则表达式,我们也可以找到一个或多个匹配项。 我们可以在字符串中查找匹配的任何国王,例如 简单字符,固定字符串或任何复杂的字符模式,例如电子邮件,SSN 或域名。
**正则表达式**用作字符串的**搜索模式**。 使用正则表达式,我们也可以找到一个或多个匹配项。 我们可以在字符串中查找匹配的任何国王,例如简单字符,固定字符串或任何复杂的字符模式,例如电子邮件,SSN 或域名。
## 1\. 正则表达式
......@@ -58,9 +58,9 @@ Start index: 20 End index: 25 - Brian
起点和终点分别用`'^'`(脱字符)和`'$'`(美元)符号表示。 插入号和美元的特殊之处在于它们**与行**中的位置匹配,而不是与任何实际的文本字符本身匹配。
例如,正则表达式“ cat”在字符串中的任何位置都可以找到“ cat”,但是仅当“ cat”位于行首时,“ ^ cat”才匹配。 例如 诸如“类别”或“目录”之类的词。
例如,正则表达式“ cat”在字符串中的任何位置都可以找到“ cat”,但是仅当“ cat”位于行首时,“ ^ cat”才匹配。 例如诸如“类别”或“目录”之类的词。
同样,“ cat $”仅在“ cat”位于行尾时匹配。 例如 像“粪便”之类的词。
同样,“ cat $”仅在“ cat”位于行尾时匹配。 例如像“粪便”之类的词。
#### 2.2 角色类
......
......@@ -23,7 +23,7 @@ Below given regex assumes that before performing the check for a valid number, w
如果不需要确定卡的类型,则可以删除围绕每种卡类型的图案的六个捕获组,因为它们没有任何其他用途。
如果您仅接受某些品牌的信用卡,则可以从正则表达式中删除不接受的信用卡。 例如,删除 JCB 时,请确保删除最后剩余的“ |” 在正则表达式中也是如此。 如果您以“ |”结尾 在您的正则表达式中,它也将接受空字符串作为有效的卡号。
如果您仅接受某些品牌的信用卡,则可以从正则表达式中删除不接受的信用卡。 例如,删除 JCB 时,请确保删除最后剩余的“ |” 在正则表达式中也是如此。 如果您的正则表达式以“ |”结尾,它也将接受空字符串作为有效的卡号。
```java
Regex : ^(?:(?<visa>4[0-9]{12}(?:[0-9]{3})?)|
......
......@@ -2,7 +2,7 @@
> 原文: [https://howtodoinjava.com/regex/java-regex-match-any-currency-symbol/](https://howtodoinjava.com/regex/java-regex-match-any-currency-symbol/)
在本教程中,我们将学习匹配所有可用的货币符号,例如 美元,欧元,日元,日元。
在本教程中,我们将学习匹配所有可用的货币符号,例如美元,欧元,日元,日元。
> **解决方案正则表达式:\\ p {Sc}**
......
......@@ -8,7 +8,7 @@
我们不能告诉正则表达式“匹配 1 到 31 之间的数字”。 而是正则表达式逐个字符地工作。
我们使用`'3[01]|[12][0-9]|0?[1-9]'`来匹配 3,后跟 0 或 1,或者匹配 1 或 2,后跟任意数字,或者匹配一个可选的 0,后跟 1 到 9。因此,您必须选择简单或准确的程度 您希望您的正则表达式是
我们使用`'3[01]|[12][0-9]|0?[1-9]'`来匹配 3,后跟 0 或 1,或者匹配 1 或 2,后跟任意数字,或者匹配一个可选的 0,后跟 1 到 9。因此,您必须选择您希望您的正则表达式的简单或准确的程度
## 1\. Java 日期验证正则表达式–允许省略前导零
......
......@@ -6,7 +6,7 @@
用于匹配行数的正则表达式将取决于用作行分隔符的确切字符或字符序列。 实际上,行分隔符可能会根据操作系统的约定,应用程序或用户首选项等而有所不同。 因此,编写理想的解决方案取决于应支持哪些约定来指示新行的开始。
本教程中讨论的以下解决方案支持标准 MS-DOS / Windows(“ \ r \ n”),旧版 Mac OS(“ \ r”)和 Unix / Linux / BSD / OS X(“ \ n”)行 违反约定。
本教程中讨论的以下解决方案支持标准 MS-DOS / Windows(“ \ r \ n”),旧版 Mac OS(“ \ r”)和 Unix / Linux / BSD / OS X(“ \ n”)行中断约定。
> **正则表达式:\\ A(?> [^ \ r \ n] *(?> \ r \ n?| \ n)){0,3} [^ \ r \ n] * \ \ z**
......
......@@ -27,9 +27,9 @@ What's maximum amount of RAM that will be allocated to java on a 32-bit machine
因此,理论上,在 32 位系统中,每个进程最多可以分配 4GB 的内存。 在 Windows 上打破这一点的是如何处理进程地址空间。 **Windows 将进程地址空间减少一半**。 其中一半保留给操作系统(用户进程无法使用),另一半保留给用户。 盒中有多少 RAM 无关紧要,一个 32 位进程只能使用 2GB RAM。 更糟糕的是– **地址空间必须是连续的**,因此实际上,**在 Windows 计算机**上通常只剩下 1.5-1.8GB 的堆空间。
精通技术的读者可能知道,现代芯片支持 [**PAE**](https://en.wikipedia.org/wiki/Physical_Address_Extension "Physical Address Extension") ,这是一种处理器技术,它允许操作系统使用更多的内存(最大为 64 GB),但它也需要特殊的应用程序 支持大多数应用程序没有或不一定需要的应用程序
精通技术的读者可能知道,现代芯片支持 [**PAE**](https://en.wikipedia.org/wiki/Physical_Address_Extension "Physical Address Extension") ,这是一种处理器技术,它允许操作系统使用更多的内存(最大为 64 GB),但它也需要特殊的应用程序支持,大多数应用程序没有或不一定需要它
Windows 的 4 GB 限制至少也是许可的因素。 32 位 Windows 的家庭版本在技术上能够支持 PAE,但出于许可和驱动程序兼容性方面的考虑,硬限制为 4 GB。 我要指出“ *驱动程序兼容性原因*”,因为某些使用本地文件(例如防病毒软件)的特定应用程序是专门为 32 位/ 64 位计算机构建的,而**本机文件不与其他机器兼容**
Windows 的 4 GB 限制至少也是许可的因素。 32 位 Windows 的家庭版本在技术上能够支持 PAE,但出于许可和驱动程序兼容性方面的考虑,硬限制为 4 GB。 我要指出“ *驱动程序兼容性原因*”,因为某些使用本地文件(例如防病毒软件)的特定应用程序是专门为 32 位/ 64 位计算机构建的,而**本机文件不与其他机器兼容**
要记住的另一件事是,您的 BIOS 和主板中的其他设备芯片(例如视频卡)也占用相同的 4 GB 空间中的一些内存,因此可供您的应用程序使用的实际内存进一步减少到大约 1.5 GB。
......@@ -39,7 +39,7 @@ Windows 的 4 GB 限制至少也是许可的因素。 32 位 Windows 的家庭
> Windows 64 位家庭版仍然限制为 16 GB RAM(全部是出于许可原因),但是由于各种兼容性问题,专业版和旗舰版目前最多可以使用 192 GB RAM。
RAM 的每个进程限制也大大提高了-在 64 位 Windows 上,**而不是 2 GB 限制,每个应用程序可以访问高达 8 TB 的虚拟内存,而无需任何特殊配置**(必须 存在于您的系统中)。 当考虑可能需要使用大量 RAM 的视频编辑或虚拟机等应用程序时,这是选择下一台计算机的重要因素。
RAM 的每个进程限制也大大提高了-在 64 位 Windows 上,**而不是 2 GB 限制,每个应用程序可以访问高达 8 TB 的虚拟内存,而无需任何特殊配置**(必须存在于您的系统中)。 当考虑可能需要使用大量 RAM 的视频编辑或虚拟机等应用程序时,这是选择下一台计算机的重要因素。
因此,现在我们对 **32 位计算机和 64 位计算机**有了很好的了解。 让我们关注与 Java 主要相关的内容。
......@@ -57,7 +57,7 @@ RAM 的每个进程限制也大大提高了-在 64 位 Windows 上,**而不是
**绝对是。 Java 字节码独立于 32 位或 64 位系统。** 这就是为什么编译的 Java 代码应在“ ***任何*** ”系统上可执行的原因。 请记住,因为虚拟机是打包在捆绑包中的一些本机文件,所以它只是为特殊的系统架构而编译的,而本机文件从不独立于平台。
***如果是,那么 32 位应用程序如何在 64 位系统上运行?*** 答案是 64 位系统包含一个称为 [**WoW64**](https://en.wikipedia.org/wiki/WoW64 "WoW64") 的兼容层,实际上**可以在 32 位和 64 位之间来回切换处理器 位模式**,取决于需要执行的线程; 使 32 位软件即使在 64 位环境中也能平稳运行。
***如果是,那么 32 位应用程序如何在 64 位系统上运行?*** 答案是 64 位系统包含一个称为 [**WoW64**](https://en.wikipedia.org/wiki/WoW64 "WoW64") 的兼容层,实际上**可以在 32 位和 64 位之间来回切换处理器位模式**,取决于需要执行的线程; 使 32 位软件即使在 64 位环境中也能平稳运行。
## 在 32 位计算机和 64 位计算机上,可分配给 Java 的最大 RAM 数量是多少?
......
......@@ -354,7 +354,7 @@ while (buffer.hasRemaining()) {
```
`put()`的批量版本的行为类似,但是将数据从数组移到缓冲区的方向相反。 关于转移量,它们具有相似的语义。 因此,如果缓冲区有足够的空间接受数组中的数据( *buffer.remaining()> = myArray.length* ),则数据将从当前位置开始复制到缓冲区中,并且 缓冲区的位置将增加所添加数据元素的数量。 如果缓冲区中没有足够的空间,则不会传输任何数据,并且会抛出`BufferOverflowException`
`put()`的批量版本的行为类似,但是将数据从数组移到缓冲区的方向相反。 关于转移量,它们具有相似的语义。 因此,如果缓冲区有足够的空间接受数组中的数据( *buffer.remaining()> = myArray.length* ),则数据将从当前位置开始复制到缓冲区中,并且缓冲区的位置将增加所添加数据元素的数量。 如果缓冲区中没有足够的空间,则不会传输任何数据,并且会抛出`BufferOverflowException`
通过以缓冲区引用作为参数调用`put()`,也可以将数据从一个缓冲区批量转移到另一个缓冲区:
......
......@@ -22,7 +22,7 @@ Java 是一种安全的编程语言,可以防止程序员犯很多愚蠢的错
```
注意:您的 IDE,例如 eclipse 可能显示与访问限制有关的错误。 不用担心 继续运行程序。 它将运行。
注意:您的 IDE,例如 eclipse 可能显示与访问限制有关的错误。 不用担心继续运行程序。 它将运行。
现在到主要部分。 使用此对象,您可以执行“有趣的”任务。
......
......@@ -6,9 +6,9 @@
## 1\. 什么是 UUID?
**UUID***通用唯一标识符*),也称为 **GUID***全局唯一标识符*)是`128 bits`长标识符,相对于所有其他 UUID 的空间而言。 它不需要中央注册过程。 结果,按需生成可以完全自动化,并用于多种目的。
**UUID***通用唯一标识符*),也称为 **GUID***全局唯一标识符*)是`128 bits`长标识符,相对于所有其他 UUID 的空间而言。 它不需要中央注册过程。 结果,按需生成可以完全自动化,并用于多种目的。
要了解 **UUID** 的独特性,您应该知道 UUID 生成算法支持非常高的分配速率,每台机器每秒可支持高达 *1000 万*的速度,因此甚至可以 用作交易 ID。
要了解 **UUID** 的独特性,您应该知道 UUID 生成算法支持非常高的分配速率,每台机器每秒可支持高达 *1000 万*的速度,因此甚至可以用作事务 ID。
我们可以应用排序,排序并将它们存储在数据库中。 通常,它在编程中很有用。
......
......@@ -2,7 +2,7 @@
> 原文: [https://howtodoinjava.com/java12/compact-number-format/](https://howtodoinjava.com/java12/compact-number-format/)
了解如何将语言环境敏感的紧凑/短号格式应用于通用编号,例如 小数,货币和百分比。 它是在 Java 12 中的 **CompactNumberFormat** 类中添加的。
了解如何将语言环境敏感的紧凑/短号格式应用于通用编号,例如小数,货币和百分比。 它是在 Java 12 中的 **CompactNumberFormat** 类中添加的。
例如,可以将数字(例如 1000)格式化为“ 1K”(短样式)或“ 1000”(长样式)。
......
......@@ -70,7 +70,7 @@ public Report getReportByName(String name) {}
## 5\. 变量命名约定
所有实例,静态和方法参数变量名称均应使用驼峰表示法。 它们应该简短,足以描述其目的。 临时变量可以是单个字符,例如 循环中的计数器。
所有实例,静态和方法参数变量名称均应使用驼峰表示法。 它们应该简短,足以描述其目的。 临时变量可以是单个字符,例如循环中的计数器。
```java
public Long id;
......
......@@ -66,7 +66,7 @@ version.patch();
它主要针对企业客户。 LTS 版本的产品将提供 Oracle 的首要和持续的支持,目标是每三年一次。 此外,这些版本的更新将至少提供三年。
这将导致“ LTS”在 java –versions 的输出中突出显示。 例如 例如`11.0.2+13-LTS`
这将导致“ LTS”在 java –versions 的输出中突出显示。 例如`11.0.2+13-LTS`
学习愉快!
......
......@@ -66,7 +66,7 @@ $ export CLASSPATH=/dependency\framework.jar:/location/*.jar
#### 2.3 将类添加到类路径
很多时候,您可能还需要在`classpath`中添加单个类。 为此,只需**添加存在类文件的文件夹**。 例如 假设`location`文件夹中存在五个`.class`文件,您希望将它们包括在类路径中。
很多时候,您可能还需要在`classpath`中添加单个类。 为此,只需**添加存在类文件的文件夹**。 例如假设`location`文件夹中存在五个`.class`文件,您希望将它们包括在类路径中。
```java
//WINDOWS
......
......@@ -29,7 +29,7 @@ Table of Contents
在基本级别上,集合和流之间的差异与计算事物时有关。 **集合是内存中的数据结构**,它保存该数据结构当前具有的所有值-集合中的每个元素必须先计算,然后才能添加到集合中。 **流是概念上固定的数据结构,其中元素按需计算**。 这带来了显着的编程优势。 想法是,用户将仅从 Stream 中提取他们需要的值,并且仅在需要时才对用户无形地生成这些元素。 这是生产者-消费者关系的一种形式。
在 java 中,java.util.Stream 表示可以在其上执行一个或多个操作的流。 流**操作是中间或终端**。 尽管**终端操作返回某个类型**的结果,但是**中间操作返回流本身**,因此您可以连续链接多个方法调用。 流是在源上创建的,例如 类似于列表或集合的 java.util.Collection(不支持映射)。 流操作可以顺序执行,也可以并行执行。
在 java 中,java.util.Stream 表示可以在其上执行一个或多个操作的流。 流**操作是中间或终端**。 尽管**终端操作返回某个类型**的结果,但是**中间操作返回流本身**,因此您可以连续链接多个方法调用。 流是在源上创建的,例如类似于列表或集合的 java.util.Collection(不支持映射)。 流操作可以顺序执行,也可以并行执行。
基于以上几点,如果我们列出 Stream 的各种特征,它们将如下所示:
......
......@@ -34,7 +34,7 @@ Lambda 表达式的最重要特征是**它们在其外观**的上下文中执行
答案在于,与面向对象编程(OOP)相比,功能编程所带来的好处。 大多数 OOP 语言围绕对象和实例发展,并且仅将它们作为头等公民对待。 另一个重要的实体,即职能倒退。 在 Java 中尤其如此,在 Java 中,函数不能存在于对象外部。 函数本身在 Java 中没有任何意义,直到它与某个对象或实例相关为止。
但是在函数式编程中,您可以定义函数,为它们提供引用变量并将其作为方法参数传递等等。 JavaScript 是一个很好的例子,您可以在其中传递回调方法,例如 到 Ajax 调用。 这是非常有用的功能,并且从一开始就在 Java 中缺少。 现在使用 Java 8,我们也可以使用这些 lambda 表达式。
但是在函数式编程中,您可以定义函数,为它们提供引用变量并将其作为方法参数传递等等。 JavaScript 是一个很好的例子,您可以在其中传递回调方法,例如到 Ajax 调用。 这是非常有用的功能,并且从一开始就在 Java 中缺少。 现在使用 Java 8,我们也可以使用这些 lambda 表达式。
#### 1.1 Lambda 语法
......
......@@ -85,7 +85,7 @@ multiple non-overriding abstract methods found in interface MyFirstFunctionalInt
* If an interface declares an ***abstract method overriding one of the public methods of `java.lang.Object`, that also does not count toward the interface’s abstract method count*** since any implementation of the interface will have an implementation from java.lang.Object or elsewhere. e.g. [**Comparator**](//howtodoinjava.com/search-sort/when-to-use-comparable-and-comparator-interfaces-in-java/ "When to use comparable and comparator interfaces in java") is a functional interface even though it declared two abstract methods. Why? Because one of these abstract methods “`equals()`” which has signature equal to `public` method in `Object` class.
例如 界面下方是有效的功能界面。
例如界面下方是有效的功能界面。
```java
@FunctionalInterface
......
......@@ -133,7 +133,7 @@ Company company = companyOptional.orElseThrow(IllegalStateException::new);
## d)使用过滤方法拒绝某些值
通常,您需要在对象上调用方法并检查某些属性。 例如 在下面的示例代码中,检查公司是否设有“财务”部门; 如果有,请打印出来。
通常,您需要在对象上调用方法并检查某些属性。 例如在下面的示例代码中,检查公司是否设有“财务”部门; 如果有,请打印出来。
```java
Optional<Company> companyOptional = Optional.empty();
......@@ -201,7 +201,7 @@ public T get() {
## 6)Optional 不尝试解决的问题是什么?
可选的是**并不是要避免所有类型的空指针**的机制。 例如 方法和构造器的强制输入参数仍然必须进行测试。
可选的是**并不是要避免所有类型的空指针**的机制。 例如方法和构造器的强制输入参数仍然必须进行测试。
就像使用 null 时一样,**可选**不能帮助传达缺失值的含义。 因此,该方法的调用者仍然必须检查 API 的 javadoc,以了解缺少**可选**的含义,以便对其进行正确处理。
......
......@@ -238,21 +238,21 @@ Java 具有明确定义的规则,用于指定当表达式具有多个运算符
| 优先顺序 | 运算符 | 类型 | 关联性 |
| --- | --- | --- | --- |
| 15 | `() [] .` | 括号 数组下标 成员选择 | 左到右 |
| 14 | `++ --` | 一元后自增 一元后自减 | 右到左 |
| 13 | `++ -- + – ! ~ (类型)` | 一元前自增 一元前自减 一元加 一元减 一元逻辑否定 一元按位补码 一元类型转换 | 右到左 |
| 12 | `* / % `| 乘法 除法 模数 | 左到右 |
| 11 | `+ –` | 加法 减法 | 左到右 |
| 10 | `<< >> >>>` | 按位左移 带符号扩展的按位右移 带零扩展的按位右移 | 左到右 |
| 9 | `< <= > >= instanceof` | 关系小于 关系小于或等于 关系大于 关系大于或等于 类型比较(仅对象) | 左到右 |
| 8 | `== !=` | 关系不等于 关系不等于 | 左到右 |
| 15 | `() [] .` | 括号、数组下标、成员选择 | 左到右 |
| 14 | `++ --` | 一元后自增一元后自减 | 右到左 |
| 13 | `++ -- + – ! ~ (类型)` | 一元前自增、一元前自减、一元加、一元减、一元逻辑否定、一元按位补码、一元类型转换 | 右到左 |
| 12 | `* / % `| 乘法、除法、模数 | 左到右 |
| 11 | `+ –` | 加法减法 | 左到右 |
| 10 | `<< >> >>>` | 按位左移、带符号扩展的按位右移、带零扩展的按位右移 | 左到右 |
| 9 | `< <= > >= instanceof` | 关系小于、关系小于或等于、关系大、关系大于或等于、类型比较(仅对象) | 左到右 |
| 8 | `== !=` | 关系不等于关系不等于 | 左到右 |
| 7 | `&` | 按位与 | 左到右 |
| 6 | `^` | 按位异或 | 左到右 |
| 5 | <code>&#124;</code> | 按位或 | 左到右 |
| 4 | `&&` | Logical AND | 左到右 |
| 3 | <code>&#124;&#124;</code> | 逻辑或 | 左到右 |
| 2 | `?:` | 三元条件 | 右到左 |
| 1 | `= += -= *= /= %=` | 赋值 加法赋值 减法赋值 乘法赋值 除法赋值 模赋值 | 右到左 |
| 1 | `= += -= *= /= %=` | 赋值、加法赋值、减法赋值、乘法赋值、除法赋值、模赋值 | 右到左 |
Java 中的**运算符就这些了。**
......
......@@ -22,7 +22,7 @@ Sections in this post:
Math 类提供了这些新方法,每次操作结果溢出到最大限制时,这些方法都会引发`"java.lang.ArithmeticException"`异常。 以上所有方法都将参数作为 int 或 long 原始类型。
例如 考虑乘以 100000 *100000。通常,这样做会“无声地”给您带来错误的回报,并且在应用程序运行时,您有责任每次检查最大限制以避免错误的计算。
例如考虑乘以 100000 *100000。通常,这样做会“无声地”给您带来错误的回报,并且在应用程序运行时,您有责任每次检查最大限制以避免错误的计算。
在 Java 8 中,`multiPlyExact`方法将为您完成此工作,并且如果结果超出最大限制,则将引发异常。
......@@ -43,7 +43,7 @@ Exception in thread "main" java.lang.ArithmeticException: integer overflow
```
其他操作也会发生相同的情况。 例如 添加操作与`addExact`
其他操作也会发生相同的情况。 例如添加操作与`addExact`
```java
......
......@@ -17,7 +17,7 @@ Final notes
## 资源清除的旧方法(在 Java 7 之前)
我们长期以来一直在这样做。 例如 从文件系统读取文件。 代码可能看起来有所不同,但流程如下例所示:
我们长期以来一直在这样做。 例如从文件系统读取文件。 代码可能看起来有所不同,但流程如下例所示:
```java
public class ResourceManagementBeforeJava7
......
......@@ -32,7 +32,7 @@ Throwable.[addSupressed](https://docs.oracle.com/javase/7/docs/api/java/lang/Thr
例如,在写入输出流时,可以从`try`块引发一个异常,当 try-with-resources 语句尝试关闭流时,可以从该异常中引发最多两个异常。
如果从 try 块引发一个异常,并且从 try-with-resources 语句引发一个或多个异常,则将从 try-with-resources 语句引发的那些异常抑制,并且由该块引发的异常是一个 `closeStream()`方法抛出。
如果从 try 块引发一个异常,并且从 try-with-resources 语句引发一个或多个异常,则将从 try-with-resources 语句引发的那些异常抑制,并且由该块引发的异常由`closeStream()`方法抛出。
您可以通过从 try 块引发的异常中调用 **Throwable.getSuppressed()**方法来检索这些受抑制的异常。
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册