当前位置:网站首页 > Java基础 > 正文

java 在线教程(java教程视频免费)



原文:JavaTutorialNetwork

协议:CC BY-NC-SA 4.0

原文: https://javatutorial.net/jvm-explained

本文介绍了 Java 虚拟机(JVM)及其架构

JVM 代表 Java 虚拟机。 它为您提供了执行已编译程序的环境,称为字节码。 来自不同供应商的 JVM 有多种实现,可用于各种平台。 在本文中,我将解释 JVM 的主要组件,包括内存管理,类加载和垃圾收集器。

通常,我们不深入探讨 JVM 的内部机制。 如果我们的代码行得通,那么我们就不会在乎内部机制了……直到那天出了问题,我们需要调整 JVM 或修复内存泄漏。

Java 虚拟机问题在求职面试中非常受欢迎。 采访者喜欢提出有关 JVM 的各种问题,以证明您对 Java 平台的一般理解。

Java 被设计为可在各种平台上运行,其概念为“一次编写,随处运行”。 这是什么意思? 例如,与像 C++ 这样的编程语言不同,在 C++ 中,代码是针对特定平台进行编译并在其上本地运行的,而 Java 源代码则首先被编译为字节码文件。 编译后,类文件将由虚拟机(VM)解释。 看下图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在不同平台上运行 Java 字节码–“一次编写,随处运行”的概念

首先,我们将 Java 源代码(文件)编译为字节码(文件)。 字节码是 Java 和机器语言之间的中间语言。 您可以在任何 JVM 实现上执行相同的字节码,而无需调整一个或另一个 OS 或平台的代码。

Java 虚拟机包含三个主要区域:

  • 类加载器子系统
  • 运行时数据区
  • 执行引擎

我们将更详细地介绍每个

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Java 虚拟机架构图

类加载器子系统

我们已经在单独的教程中介绍了类加载器。 您可能需要查看 Java 类加载器,以了解更多详细信息。

载入

编译的类存储为文件。 当我们尝试使用类时,Java 类加载器将该类加载到内存中。 在已经运行的类中通过名称引用类时,这些类将引入 Java 环境。 一旦第一个类运行,以后将由类加载器完成加载类的尝试。 通常,通过声明并使用静态方法来完成第一类的运行。

有三种类型的类加载器:

  1. 自举类加载器 - 它加载 JDK 内部类,通常加载和其他核心类,例如包类
  2. 扩展类加载器 - 它从 JDK 扩展目录(通常是 JRE 的目录)加载类。
  3. 系统类加载器 - 从系统类路径加载类,可以在使用或命令行选项调用程序时进行设置。
链接

链接类或接口涉及验证和准备该类或接口,其直接超类,其直接超接口以及必要时其元素类型。

JVM 要求维护以下所有属性:

  • 类或接口在链接之前已完全加载。
  • 在初始化类或接口之前,必须对其进行完全验证和准备。
  • 链接期间检测到的错误会抛出到程序中某个位置,在该位置上,程序将采取某些操作,这些操作可能直接或间接地需要链接到错误所涉及的类或接口。
初始化

类或接口的初始化包括执行其类或接口的初始化方法或调用该类的构造函数。

因为 Java 虚拟机是多线程的,所以类或接口的初始化需要仔细的同步,因为某些其他线程可能试图同时初始化同一类或接口。

这是类加载的最后阶段,在这里所有静态变量都将被分配原始值,并且将执行静态块。

运行时数据区

运行时数据区域内有五个组件:

方法区

所有类级别的数据(包括静态变量)都将存储在此处。 每个 JVM 只有一个方法区域,它是共享资源。

堆区

所有对象及其对应的实例变量和数组都将存储在此处。 每个 JVM 还有一个堆区。 由于“方法”和“堆”区域共享多个线程的内存,因此存储的数据不是线程安全的。

栈区

对于每个线程,将创建一个单独的运行时栈。 对于每个方法调用,将在栈存储器中创建一个条目,称为栈帧。 所有局部变量都将在栈存储器中创建。 栈区域是线程安全的,因为它不是共享资源。 栈框架分为三个子实体:

  • 局部变量数组–与该方法有关,涉及多少局部变量,并且相应的值将存储在此处。
  • 操作数栈–如果需要执行任何中间操作,则操作数栈充当执行该操作的运行时工作区。
  • 帧数据–与该方法相对应的所有符号都存储在此处。 在任何例外情况下,捕获块信息都将保留在帧数据中。
PC 寄存器

每个线程将具有单独的 PC 寄存器,以保存当前执行指令的地址,一旦执行了该指令,PC 寄存器将被下一条指令更新。

本机方法栈

本机方法栈保存本机方法信息。 对于每个线程,将创建一个单独的本机方法栈。

执行引擎

分配给运行时数据区的字节码将由执行引擎执行。 执行引擎读取字节码并逐段执行。

解释器

解释器解释字节码的速度较快,但执行速度较慢。 解释器的缺点是,当多次调用一种方法时,每次都需要新的解释。

JIT 编译器

JIT 编译器消除了解释器的缺点。 执行引擎将使用解释器的帮助来转换字节码,但是当发现重复的代码时,它将使用 JIT 编译器,该编译器将编译整个字节码并将其更改为本地代码。 此本地代码将直接用于重复的方法调用,从而提高系统的性能。

垃圾收集器

垃圾收集器(GC)收集并删除未引用的对象。 可以通过调用来触发垃圾收集,但是不能保证执行。 JVM 的垃圾收集收集创建的对象。

Java 本机接口(JNI)

JNI 将与本机方法库进行交互,并提供执行引擎所需的本机库。

本机方法库

它是执行引擎所需的本机库的集合。

参考文献

Oracle 的 Java 虚拟机规范

原文: https://javatutorial.net/java-memory-examples

内存可以描述为字节数组,您可以在其中单独访问每个字节。 就像在 Java 中的数组中一样,在内存中的每个字节或更确切的位置上,都有可以访问的数据。 在 32 位架构中,每个内存“插槽”包含 32 位,也称为 1 个字,或仅 4 个字节。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

内存视觉展示

在上面的表示中,4000、4004、4008 等表示内存插槽的地址,或者在与数组,数据的索引或内存位置进行的比较中。 这些“随机值”中的每一个代表 32 位。 由于每个内存插槽占用 32 位或 4 个字节,因此内存地址每次增加 4。

通常,在 Java 和一般的编程中,有两种类型的可变范围 – 全局局部。 全局变量是可以从程序中的任何地方访问的变量,而局部变量是只能在给定函数中创建它们的地方访问的变量。 因此,这两种不同类型的变量作用域存储在不同的存储区域中 - 数据

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

 
  

在上面的简单 Java 示例中,存储在栈存储区域中。 这是因为是局部变量。

 
  

在上面的 Java 示例中,位于静态数据存储区中。 因为如您所见,即使未在其中创建方法,也可以通过该方法进行访问。 此外,可以在整个程序中对其进行访问。

 
  
 
  

在上面的 Java 示例中,我们创建了类的新实例并将其存储在中,实际上,我们使用堆区域为动态分配我们创建此类内存所需的内存。 使用关键字的实例。 换句话说,它不固定为一定大小。 无论实例有多少字节,如果有足够的内存(可能),将创建该实例,并且该实例将仅保留创建该实例所需的字节数。

Java 在动态内存分配方面为我们省去了很多麻烦,因为在某些其他语言(例如 C)中,您必须手动分配内存并在不再需要该内存时手动“释放”相同的内存, 而在 Java 中,一切都在幕后发生,只需调用关键字。

我将建议以下有关如何调整 JVM 以使用特定内存量的教程

Java 增加内存

原文: https://javatutorial.net/capture-java-heap-dump

在本文中,我将教您多种捕获 Java 堆转储的方法。 对于优化内存消耗至关重要,堆转储被描述为 Java 进程的内存打印。

Java 堆转储是诊断与内存相关的问题的重要对象,这些问题包括,垃圾收集问题和内存泄漏(缓慢),这些都是 Java Web 开发的一部分。

为了清楚起见,在进行快照的瞬间,堆转储包含诸如 Java 类和堆中的对象之类的信息。

可以帮助您分析堆转储的工具包括 Heap Hero 和 Eclipse MAT 。 但是,您仍然需要为工具提供在正确的时间和正确格式下捕获的堆转储。

值得注意的是,会将堆转储打印到特定的文件位置。工具通常打包在 JDK 中。 您可以在以下文件夹中找到它:。

要调用,请执行以下过程。

,其中是 Java 进程 ID,将为其捕获堆转储。此外,是其中打印堆转储的文件路径。

请注意,传递“实时”选择至关重要。 如果选项“通过”,则只会将活动对象写入堆转储文件。 但是,如果您无法通过该选项,则所有对象(包括未设置为垃圾收集的对象)都将被打印到堆转储中。 这样的错误会过多且不必要地增加其堆转储的大小。 通过将您的移动开发需求与 Java Development Company 签订合同,可以避免此类错误。

当应用程序遇到时,捕获瞬时堆转储至关重要。

这样的过程将有助于确定内存中占用的对象以及它们在发生时的位置所占用的空间量(百分比)。

但是,由于操作和技术很多,操作组可能无法捕获堆转储。 此外,团队可能还重新启动了该应用程序。 因此,堆转储捕获已成为系统应用程序的关键方面,尤其是在涉及内存问题时。

幸运的是,选项将在该过程中提供帮助。 您只需在应用程序启动时传递系统属性()。 然后, JVM 将通过在 JVM 面临的确切时间捕获堆转储来完成其余工作。

值得注意的是,在上述情况下将捕获的头转储将打印在名为的系统属性概述的位置中

工具用于发送命令请求以诊断 Java JVM 。 同样,工具包含在 JDK 软件包中。 您可以在名为的文件夹中获取它。

这是调用时需要使用的过程;

  1. 转到
  2. 其中:是一个 Java 进程 ID,将为其捕获堆转储
  3. 另外,是在其中打印堆转储的文件路径。

结论

在本文中,我讨论了可用于捕获 Java 堆转储的三个主要过程:(1)(2)和(3)。

原文: https://javatutorial.net/java-garbage-collection

本文讨论了有关 Java 垃圾收集(GC)的知识,该垃圾收集被视为 Java 编程语言中的复杂主题之一。

顾名思义,java 中的垃圾收集功能可以自动从内存中搜索,发现和删除垃圾,而无需用户明确执行此工作。 这里的垃圾是指未引用的对象。

有多种方法可以使对象成为未引用和无用的对象。 一些是:

  • 引用的清零
  • 将引用分配给另一个
  • 匿名对象等等

因此,垃圾收集功能会找出这些对象并自动将其从内存中删除并删除它们,从而实现高效的内存使用和管理。

如果要用 C 语言进行相同的垃圾收集和优化,则可以使用函数,而在 C++ 中,则可以使用方法。 因此,Java 中此过程实现了自动化,从而为用户减少了麻烦。

从技术上讲,Java 垃圾收集处理的是跟踪 JVM(Java 虚拟机)堆空间中的每个对象,并删除(删除/取消分配)未使用的对象。

垃圾收集(GC)实现有 4 种类型:

  1. 串行垃圾收集器
  2. 并行垃圾收集器
  3. CMS 垃圾收集器
  4. G1 垃圾收集器

GC 的此实现仅使用一个线程,并且在运行时冻结所有应用程序线程。 因此,它不是在多线程应用程序中使用的最佳选择。 所有垃圾收集事件都在一个线程中串行进行。

它用于对暂停时间要求不高且在客户端样式的计算机上运行的应用程序中。 它是为单线程系统设计的,是 JVM 中较小堆大小的首选。

此变体是 JVM 的默认垃圾收集器,也称为吞吐量收集器。 并行 GC 在管理堆时利用多个线程,而串行 GC 则不然。 但是,像串行 GC 一样,它在运行时也会冻结其余的应用程序线程。

可以指定最大垃圾收集线程以及暂停时间,吞吐量和占用空间(堆大小)。

CMS 代表并发标记扫描,并同时使用多个 GC 线程来扫描堆并标记未引用的对象,这些对象随后在扫描中被删除/取消分配。 对于那些需要短时间垃圾收集暂停并能够负担与 GC 共享资源的应用程序,它是首选。 使用该 GC 实现的应用程序比不使用 GC 的应用程序要慢,但是在执行垃圾收集时它们不会完全暂停整个应用程序。

在两种情况下,垃圾收集器的这种含义进入了“世界停止”(STW)模式:

  • 在初始化根的初始标记时
  • 当算法同时运行时,应用程序更改了堆的状态时; 强制其返回以确保标记了正确的对象。

当对象从年轻一代移动到老一代并且收集器没有足够的时间在老一代中腾出空间时,会遇到升级失败。

G1 代表垃圾优先 GC,适用于在具有大内存的多处理器计算机上运行的应用程序。 与 CMS 收集器相比,它具有更高的性能效率。

G1 GC 将堆划分为一组大小相等的堆区域,并标记并发的全局标记阶段,以确定是否正在使用堆中的对象。

标记完成后,GC 知道哪些区域大部分为空。 然后,GC 首先从这些区域收集垃圾(这称为清扫阶段),从而产生大量可用空间。 因此,该名称首先是垃圾。

  • 没有手动的内存分配/取消分配处理,因为 GC 会自动处理未使用的内存空间
  • 没有处理悬空指针的开销
  • 自动内存泄漏管理
  • 跟踪对象引用的创建/删除需要更多的 CPU 能力,并且可能会影响需要大内存的请求的性能
  • 程序员无法控制专用于释放不再需要的对象的 CPU 时间的调度
  • 使用某些 GC 实现可能会导致应用程序意外停止
  • 在某些情况下,自动内存管理可能不如适当的手动内存分配/取消分配那样有效。

一个简单的程序,显示 Java 垃圾收集的实现

 
  

输出:

 
  

原文: https://javatutorial.net/java-mutex-example

在更深入地了解互斥量之前,先给出一个示例:

想想一个队列。 不管长短,都没关系。 现在想想一辆正在出售游乐园门票的卡车。 一次一个人可以买票。 该人买了票后,就该排队了。

这个小故事与理解互斥量有什么关系? 让我解释。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

互斥量允许每个线程具有 1 个许可。换句话说,一次只能有 1 个线程可以访问资源。 在上面的类比中,两个人不能同时购买门票。互斥量也是如此。 它不是线程,而是人员,而是票证。 同样的事情或多或少…

互斥量与略有不同,因此允许多个线程访问资源。意思是,多个人可以同时购买门票。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第一个构造函数是我们实际上可以区分互斥量和的地方。 如果那里有 1 作为参数,则意味着将只允许 1 个线程获取锁。 请记住,由于它没有第二个参数,因此您正在使类以任何顺序访问任何线程。

第二个构造函数如果传递(公平),则确保按线程请求访问并在队列中等待的顺序给出访问。

 
  

输出

 
  

从输出中可以看到,当有人买票时,没有其他人可以买。这是显示以下内容的行之一:

Bob 仍在买票。 还有多少人可以和他一起买票: 0

但是,在他“购买”了机票之后,其他人立即购买了机票。

归根结底,它涉及和。是指该人开始“购买机票”的时间,是指该人“购买机票”的时间。

原文: https://javatutorial.net/java-semaphore-example

信号量可用于限制并发线程的数量,并且实质上,此类维护一组许可。

从信号量获取许可,然后将许可返回信号量。 如果没有许可证,则将阻塞直到可用

信号量用于控制对特定资源的访问。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

工作流程

信号量设置为一个计数值。 然后线程尝试获取许可,如果计数为 0 或小于 0,则线程将被阻塞,并且它将等待下一个许可(如果有)。 这将一直保持下去,直到数量大于 0。如果是,则信号量将提供对线程资源的访问。 然后线程将释放许可,计数将增加 1。

  1. :使用给定数量的许可和不公平的公平设置创建一个信号量
  2. :创建具有给定许可数量和给定公平性设置的信号量
  1. :从当前信号量获取许可,阻塞直到可用,否则线程被中断。
  2. :从当前信号量获取指定的许可数量,直到所有可用或线程中断为止一直阻塞。
  3. :返回当前信号量中可用的当前许可数量

要查看所有方法,请单击此处,您将被重定向到 Oracle 官方文档。

信号量可用于锁定对特定资源的访问。 每个线程都必须请求“权限”,因此需要在访问资源之前调用方法。 当线程不再需要资源时,它必须调用来释放锁。

 
  

输出

 
  

从上面的示例中可以看到,当您调用时,您正在向实例添加许可。 当您调用时,您正在删除。 如果没有许可证,而您打电话给获取,它将等待直到许可证被释放,因此在上例中将永远不会执行最后一个打印语句。

如果您有 1 个许可,然后又有一个调用,紧接着是一个调用,则称为锁。

 
  

输出

 
  

从输出中可以看到,线程 0 允许了一个获取,线程 1 开始等待为其本身获取一个许可,但是它(线程 1)仅在线程 0 释放其许可时才获得它。

原文: https://javatutorial.net/java-parallel-streams-example

通过流 API,开发人员可以通过创建并行流并提高程序执行操作的速度来利用多核架构并提高 Java 程序的性能。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

有两种创建并行流的方法:

  • 通过使用方法
  • 通过使用方法

当您使用并行流时,它们实际上会使用更多的 CPU 能力,这将使整个输出或处理(如果您愿意)的整体速度更快。 更快! 另外,如果您不使用并行流进行大型计算,则您的程序将仅使用 16 个内核中的 1 个内核。 多么低效!

并行流将问题分为多个小子集(或子问题),它们可以同时解决(不同于顺序排列的顺序,在这些子集中一个一个地执行每个小问题)。 在计算了子问题之后,将所有对它们的解决方案组合在一起。

Java 允许您通过使用诸如 map 之类的聚合操作来实现并行流。

 
  

在此示例中,我们有一个示例方法,该方法仅循环 10000 次并将加到。 最后,我们简单地返回。 在我们的主要函数中,我们创建一个包含整数的。 然后我们创建一个巨大的循环,循环 次,然后将每个增量加到中。 添加完之后,我们得到了这两个打印语句,它们指出这个大循环已经完成。

在这些打印语句下面是有趣的部分。 更具体地说,这是我们使用映射函数创建并行流的地方。 我们在上调用,对于中的每个,我们都在调用计算函数,并将的元素作为参数传递。 最后,我们将所有结果结合在一起。

  • 当有大量数据要处理时
  • 如果循序渐进的方法使您付出了代价
  • N(元素数量)Q(每元素成本)应大

原文: https://javatutorial.net/java-thread-synchronization

本文讨论了 Java 中线程同步的重要性以及如何在程序中实现线程同步。

当您的程序有多个线程时,当不同的线程尝试访问相同的资源或同时执行相同的操作时,可能会出现问题。 这种情况会导致错误和并发问题。 让我们以一个示例为例,您的程序中有两个线程试图读取/写入文本文件。 当两个线程都在写入文件时,一个线程的数据可能会被另一个线程覆盖,并且在读取文件时会发现相同的问题。

因此,线程同​​步是必需的,以便多个线程在任何给定时间点一次访问一个线程。

为什么要使用线程同步?

  • 防止线程干扰
  • 避免一致性问题和并发问题
  • 防止数据丢失

线程同步有互斥和线程间通信两种类型。

  • 互斥
  • 同步方法。
  • 同步块。
  • 静态同步。
  • 合作(Java 中的线程间通信)

同步块用于线程同步。

关键字用于标识 Java 中的同步块,并基于某些对象进行同步。 其背后的主要概念是,在同一对象上同步的所有同步块一次只能在其中一个线程内执行,这会阻止多个线程同时运行和执行。 尝试进入同步块的所有其他线程将被阻止,直到同步块内的线程退出该块为止。

共享资源保留在此同步块内,以便在给定时间点只有一个线程可以访问特定资源。

同步块的语法如下所示:

 
  

在尝试理解和实现 Java 同步时,监视器的概念很重要。

  • Java 中的每个对象都与一个监视器关联
  • 线程可以锁定或解锁监视器
  • 在给定时间,只有一个线程可以拥有监视器。
  • 一次只能有一个线程可以锁定监视器
 
  

输出:(每次运行都会产生不同的结果)

 
  

相同的示例,但是这次具有线程同步:

 
  

输出:(我们看到的输出是同步的,并且每次执行程序都是相同的)

 
  

原文: https://javatutorial.net/java-thread-pool-example

活动线程消耗系统资源,这可能导致 JVM 创建太多线程,这意味着系统将很快用尽内存。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这就是 Java 中的线程池有助于解决的问题。

线程池将先前创建的线程重用于当前任务。 这就解决了需要太多线程的问题,因此内存不足不是一个选择。 您甚至可以将线程池​​视为回收系统。 它不仅消除了用尽内存的选项,而且还使应用程序非常快速地响应,因为当请求到达时已经存在一个线程。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

上图的工作流不仅可以控制应用程序正在创建的线程数,还可以控制计划任务的执行并将传入的任务保持在队列中。

Java 提供了框架,这意味着您只需要实现对象并将其发送给执行器即可执行。

要使用线程池,首先我们需要创建一个对象并将任务传递给它。类设置核心和最大池大小。 然后,可运行对象将顺序执行。

 
  

接口包含许多方法,这些方法用于控制任务的进度并管理服务的终止。 您可以使用实例控制任务的执行。 有关如何使用的示例:

 
  

使您可以实现具有许多参数的可扩展线程池,这些参数包括,,,,,,。 但是,,和是主要变量,因为它们在每个构造函数中都使用。

是要保留在池中的线​​程数,即使它们处于空闲状态,除非设置了。

是池中允许的最大线程数。

是当线程数大于内核数时,这是多余的空闲线程将在终止之前等待新任务的最长时间。

有关其他参数的更多信息,请访问原始 Oracle 文档。

工作流程步骤:

  1. 创建要执行的任务
  2. 使用执行程序创建执行程序池
  3. 将任务传递给执行程序池
  4. 关闭执行程序池

 
  

 
  

输出

 
  

上面的代码实现的细分

表示任务类。 每个任务都有一个名称实例变量,并且每个任务都使用构造函数实例化。 此类有 1 个方法,称为。 在方法的主体内,有一个循环,该循环根据存在的任务数进行迭代。 在我们的例子中,有 5 个任务,这意味着它将运行 5 次。 第一次迭代,显示当前任务初始化的时间。 其他迭代,打印执行时间。 打印完成后,有一个方法调用,该方法用于以 1 秒的延迟显示每个迭代消息。 注意,像这样调用的方法名称非常重要,因为它是来自类正在实现的的抽象方法。

仅在池中的某个胎面变得空闲时才执行任务 4 和 5。 在此之前,额外的任务将放置在队列中。

执行完所有任务后,请关闭线程池。

组织服务器应用程序时。 如本文开头所述,在组织服务器应用程序时非常有用,因为使用线程池非常有效,就像有许多任务一样,它会自动将它们放入队列中。 不仅如此,它还可以防止内存不足,或者至少可以显着减慢这样做的速度。 使用使其更易于实现。

原文: https://javatutorial.net/threadlocal-java-example

是提供线程局部变量的类,用于实现线程安全。 存储的数据只能由特定线程访问。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

扩展了类,并提供了线程限制,它是局部变量的“一部分”。

 
  

上面代码中对象的实例化仅需要针对每个线程进行。

就像大多数类一样,一旦有了的实例,就可以在其上调用方法。 一些方法是:

  1. :返回此线程局部变量的当前线程副本中的值
  2. :返回当前线程局部变量的当前线程初始值
  3. :从当前线程中删除当前线程局部变量的值
  4. :将当前线程局部变量的当前线程副本设置为指定值

有关这些方法的更多详细信息,请访问原始 Oracle 文档。

实例是希望将状态与线程相关联的类中的私有静态字段(在大多数情况下)

 
  

输出

 
  

上面的代码显示了两种使它起作用的方法:一种是通过拥有对象并将其传递给实例,然后覆盖方法,或者您可以简单地创建一个实例并为其设置值,然后 可以获取或删除它。 从上面的示例中可以看到,即使它是相同的变量(局部变量),也可以包含不同的值。 在第一种情况下,它包含值。 在第二种情况下,它包含值。 对于其他实现,我只显示了一个线程。 但是,每个线程都是独立的–意味着,如果您要创建另一个实例(例如),并对其进行,它将独立运行,并且与其他实例变量无关。 要进行检查,可以在静态类中创建一个实例,然后在重写的方法中创建一个随机数,然后使用方法将其传递给当前线程。 您将看到,如果您在两个或多个不同的线程上调用它,则它们都将具有不同的值。

原文: https://javatutorial.net/livelock-and-deadlock-in-java

以下文章讨论了 Java 中的活锁和死锁状态,它们如何发生以及如何避免它们。

Java 中的活锁是一种递归条件,其中两个或多个线程不断重复一段特定的代码。

当一个线程不断响应另一个线程并且另一个线程也执行相同操作时,就会发生活锁。

要对其进行分解,我们可以总结以下几点:

  • 一个线程响应另一个线程的行为而运行,而另一个线程也响应先前的线程而运行,则可能发生活锁。
  • 活锁线程无法继续进行。
  • 线程没有被阻塞; 他们只是忙于互相回应。

活锁也被称为资源匮乏的特例

让我们通过将其与现实世界联系起来来理解该概念。 考虑两辆车在狭窄桥梁的相对两侧。 一次只能乘一辆车通过桥。 这两辆车的驾驶员都很有礼貌,正在等待对方先通过桥。 他们互相鸣叫,让他们知道他们想让对方先通过。 但是,两者都没有越过桥梁并互相鸣喇叭。 这种情况类似于活锁。

现在,通过一些编码来尝试这种实际情况:

第一辆等待过桥的汽车的等级:

 
  

等待通过桥的第二辆车的类别:

 
  

主要测试类别:

 
  

输出:

这导致了非终止循环。

死锁与活锁有些不同。 死锁是一种状态,其中每个成员都在等待其他成员释放锁。

可能存在以下情况:一个线程正在等待另一个线程获取的对象锁,而第二个线程正在等待第一个线程获取的对象锁。 由于两个线程都在互相等待释放锁,因此这种情况称为死锁。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图:死锁状态

让我们看一下发生死锁的情况:

 
  

考虑两个线程 T1 和 T2,T1 获取 A 并等待 B 完成其功能。 但是,T2 获取 B 并等待 A 完成其自身的功能。 在这里,T1 和 T2 正在等待被其他线程锁定的资源。 因此,这是一个僵局方案。

  • 第一个建议是避免同时使用多线程,但这在许多情况下可能不切实际。 因此,这种解决方案不是很明智。
  • 分析并确保在事先访问资源时没有锁。
  • 在上面的编码示例中,为避免死锁,只需管理访问资源的顺序(访问 A 和 B 的顺序)。
  • 避免一次持有多个锁,以防万一您必须始终以相同的顺序获得这些锁。
  • 避免在持有锁的同时执行外来代码。
  • 尝试使用可中断的锁,以便即使遇到死锁,也可以中断这些锁,并且可以毫无问题地执行该过程。

原文: https://javatutorial.net/java-future-example

异步计算的结果称为,更具体地说,它具有此名称,因为它(结果)将在将来的中稍后的时间点完成。 创建异步任务后,将创建将来的对象。 提供了许多方法来帮助检索结果(使用方法(这是唯一的检索方法)),使用方法进行取消以及检查是否检查结果的方法。 任务成功完成或被取消。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

的主要优点在于,它使我们能够同时执行其他进程,同时等待中嵌入的主要任务完成。 这意味着当我们面对大型计算时,使用是个好主意。

    1. 这与上面的 get 方法不同,因为在此方法中,指定了超时作为等待条件
 
  
 
  

在上调用方法,返回。 现在有了,我们可以调用上面的方法了。 但是,您应该特别注意的是,它在提交的任务正在“运行”时打印“正在完成任务…”。

如果任务没有在指定的时间内完成(如注释所示),将抛出。

原文: https://javatutorial.net/java-equals-method-example

Java 方法和运算符都用于比较对象是否相等。 但是,他们以非常不同的方式进行检查,从而产生不同的结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

它们之间的主要区别是检查两个对象是否都指向相同的内存位置,并且求和对象中包含的实际值的比较。

一个示例将为您提供更多线索:

 
  

 
  

您认为会打印出什么?

输出

 
  

即使两个对象都是同一个类的实例并包含相同的值,但它们并不引用相同的对象。 每当您键入的关键字时,它都会自动创建一个新的对象引用。 当我们使用关键字创建两个对象时,即使它们包含相同的值,它们也不相同。 它们指向不同的存储位置。

 
  

您认为将在屏幕上显示什么?

输出

 
  

如果您说“等于”,那么您将是正确的。 当字符串包含相同的内容

 
  

它们指向相同的存储位置。

现在让我们做与上面完全相同的示例,但改用关键字。

 
  

您认为现在将打印什么?

输出

 
  

如我上面指出的,打印不等于的原因是因为,当您使用关键字创建对象时,会创建一个指向其自身存储位置的新指针。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这是一个直观的示例。 内存位置刚刚组成。 但是从示例中可以看到,当创建和时,它们指向不同的内存位置。 因此,当您使用运算符比较它们时,无论如何,您都会得到假。

假设您要在两个对象上调用,并且如果它们包含相同的名称和年龄,则应返回。

 
  

 
  

输出

 
  

我们在类中重写方法,因此它符合我们自己的条件。 如果我们不重写它,而只是对两个对象调用方法,它将不会返回。

 
  

输出

 
  

在字符串上调用时,它将检查每个字符是否在两个字符串中都相同。 意思是,比较字符串时应始终使用,而不是。

 
  

输出

 
  

如您所见,使用时两个对象(字符串)是否指向不同的存储位置并不重要。 如果两个字符串中的内容相同,则返回。

比较字符串时,应始终使用而不是。

原文: https://javatutorial.net/java-lambda-expressions-tutorial

Java 8 引入了 Lambda 表达式,它是引入的最大(甚至不是最大)功能之一,因为 Lambda 表达式使功能编程成为可能,并且使您的代码更整洁,并从整体上极大地简化了整个代码的实现。

例如,当您必须使用匿名类并且该类非常简单(例如仅包含几个(或更少)方法)时,您会遇到非常不清楚且难以阅读/维护的语法。 这是 Lambda 表达式发挥作用的时候。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

让我们比较一下使用 Lambda 表达式和不使用 Lambda 表达式

假设有一个类,该类具有默认情况下按升序对元素进行排序的方法(例如)。 此类使用来对元素进行排序。 让我们覆盖它。

 
  
 
  

我相信您会注意到两种方法之间的区别。 两者都导致同一件事,这颠倒了该方法对元素进行排序的顺序(从升序到降序,对您的比较器开玩笑!)。

因此,仅通过查看此示例,使用 lambda 表达式的一个关键优势就是可以减少键入的代码。 对于我们懒惰的程序员来说,这是一个很好的例子。 但是,人们可能会争辩说,由于这种简单性,实际上使记住语法变得更加困难。 没错,这将是一个缺点。 最初,语法可能有点难以记住,但是一旦您记住它,就可以编写简洁的代码,该代码也允许您实现接口。

让我们看看 Lambda 表达式的更多示例。

首先,如我之前所说,语法起初有点令人生畏。

如果您要“覆盖”的函数没有任何参数,则只需键入。 另一方面,如果只有一个参数,则键入。 对于两个或更多参数,您将具有。

 
  

注意,在循环的主体中,我省略了花括号。 这是因为主体仅包含一行。 如果我有两行或更多行,假设另一个打印语句,那么我将拥有大括号,如下所示:

 
  

您也可以像使用方法一样使用 lambda 表达式返回值。 这里要注意的一个关键是关键字是可选的。 您可以忽略它,编译器知道它的工作。

语法类似于我们之前使用的语法。

 
  

因此,如果我们再次查看 Lambda 表达式提供的内容,我们可以得出结论,它为我们提供了可选的类型声明(无需声明参数的类型),可选的花括号,可选的关键字(因为编译器自动返回值) (如果主体具有返回值的符号表达式)和参数周围的可选括号。

原文: https://javatutorial.net/java-optional-example

Java 8 引入了类,该类用于根据值是否存在来操纵数据。 您可以不使用此类而具有相同的功能,但是最终会产生混乱的代码。 换句话说,您的空检查将更少,而则不会。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

让我举一个例子,说明为什么不使用会导致问题。

 
  

您能发现哪里会出问题吗? 显然,当我们到达语句时,由于对象名称被故意设置为,它将抛出。

 
  
 
  
 
  
  • 我们正在创建一个非常简单的类,称为,我们有 2 个具有构造函数的 getter 方法
  • 在我们的主方法中,我们正在创建该类的实例,但是我们还将第一个参数(恰好是名称)分配给
  • 检查传递的字符串是否为
  • 之后,我们尝试打印值
  • 我们有这个方法调用,这基本上意味着:如果您要在其上调用该方法的字符串为 null,那么我将使用给出的参数进行打印
  • 如果不为空,那么我将打印原始字符串
  • 换句话说,如果字符串为,它将打印默认值

是不是很酷? 只要看看我们检查空值时代码的外观如何优雅即可。 而且安全!

为了进行比较,让我们尝试不使用类而获得相同的结果。

 
  
 
  

它有效…但是干净吗? 我不这么认为!

您也可以使用方法检查对象中是否存在值:

 
  
 
  

而已。 不是那么难,不是吗? 甚至它都不是很容易理解,但是它也大大简化了您的代码,并且也非常易于阅读和维护。 如果要浏览提供的所有方法,请查看官方 Oracle 文档。

原文: https://javatutorial.net/java-11-http-client-example

Java 11 引入了 HTTP 客户端,该客户端可用于通过网络发送请求并检索其响应。 HTTP 客户端取代了旧的类,并且不支持易用性。 HTTP 客户端 API 同时支持 HTTP/1.1 和 HTTP/2。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

也是不可变的,这意味着它可以用于发送多个请求。

每个必须提供一个,并且其()函数用于确定如何处理响应(如果有的话)。

可以同步或异步发送请求

  • 阻塞,直到发送了请求并接收到响应为止
  • 发送请求并同时接收响应(异步)。 此方法返回,仅当响应可用时才完成。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 允许在收到实际的响应主体之前检查响应代码

存在的唯一目的是处理响应主体类型。 一些例子:

  • 等等
  • 实际上是请求正文的订阅者和响应正文字节的发布者
  • 请求和响应主体作为响应流(具有无阻塞背压的数据流)公开

  • 将 Java 对象转换为适合作为请求正文发送的字节缓冲区

  • 等等

  • 使用响应主体字节并将其转换为 Java 类型

顺便说一句,让我们看看一些实现。 首先,我们将发送 GET 请求:

 
  

现在,让我们发送一个 HTTP 请求。 我将向您展示同步和异步示例:

 
  
 
  
 
  

HTTP 客户端的角色是替换 API,并且在 Java 11 中作为 Java SE 平台的一部分进行了标准化,并且位于包中。 一个主要优点是它使用了现代 Java 语言以及 API 功能。

原文: https://javatutorial.net/java-class-loaders-explained

本文介绍了 Java 类加载器的关键组件。

Java 类加载器是 Java 虚拟机(JVM)的重要组成部分。 它们用于加载类和接口。 Althaout 类装入器是 JVM 不可或缺的一部分,它们对于 Java 的内部工作非常重要,因此您在作为 Java 开发人员的日常工作中不太可能需要创建自定义类装入器。 例如,如果要创建将在诸如 Tomcat 之类的容器上执行的应用程序,则可以使用自定义类加载器的实际应用程序。 Tomcat 要做的是为每个 Web 应用程序创建一个类加载器(以便它以后可以卸载 Web 应用程序并释放内存)。

本文旨在解释类加载器的工作方式,并列出 Java 类加载器的关键组件。 您下次 Java 面试时可能会遇到有关类加载器的问题。

我们知道 Java 程序在 Java 虚拟机(JVM)上运行。 当我们编译 Java 类时,它将转换为平台和机器无关的字节码。 编译的类存储为文件。 当我们尝试使用类时,Java 类加载器将该类加载到内存中。 在已经运行的类中通过名称引用类时,这些类将引入 Java 环境。 一旦第一个类运行,以后将由类加载器完成加载类的尝试。 通常,通过声明并使用静态方法来完成第一类的运行。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

类加载器的层次结构

  1. 自举类加载器 - 它加载 JDK 内部类,通常加载和其他核心类,例如包类
  2. 扩展类加载器 - 它从 JDK 扩展目录(通常是 JRE 的目录)加载类。
  3. 系统类加载器 - 从系统类路径加载类,可以在使用或命令行选项调用程序时进行设置。

何时加载类? 确实有两种情况:

  1. 当执行新的字节码时(例如,😉
  2. 当字节码静态引用一个类时(例如)。

类加载器是分层的。 第一个类是在类中声明的静态方法的帮助下专门加载的。 所有随后加载的类均由已加载并正在运行的类加载。

进一步的类加载器在加载类时遵循以下规则:

  1. 检查该类是否已经加载。
  2. 如果未加载,请要求父类加载器加载该类。
  3. 如果父类加载器无法加载类,请尝试在该类加载器中加载它。

使用 Java 的运算符静态加载类。 动态加载是一种使用在运行时以编程方式调用类加载器的功能的技术。

仅加载类,但不初始化对象,而在加载对象后初始化对象。 例如,如果您使用加载 JDBC 驱动程序,则该驱动程序将无法注册,并且您将无法使用 JDBC

方法返回与具有给定字符串名称的类或接口关联的对象。 如果找不到该类,则此方法引发

下面的示例演示的用法

 
  

加载类。 比我们打印类名称,包和该类所有可用方法的名称。 这是在 Java 8 中执行程序的结果:

 
  

原文: https://javatutorial.net/java-enum-example

枚举类型是一种特殊的数据类型,它具有不同的常量,例如,,。 约定是,它们应以大写字母命名,因为它们又是常量。 在 Java 中,您可以使用关键字定义枚举类型。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

 
  

如果您知道编译时程序的所有可能常量,则应以枚举类型表示它们。

Java 在 1.5 中引入了 enum 数据类型。

其他编程语言(例如 C++ 甚至 C)也具有枚举数据类型,但是在 Java 中,它更强大。 例如,在 C/C++ 中,枚举只是整数值的列表,而在 Java 中,它是扩展的类本身,并且通常对读取和写入都更好。 最重要的是,由于枚举是一个类的事实,它还提供了允许在枚举成员上进行迭代的不同方法。

在 Java 中,您还可以使用方法,该方法将为您提供枚举数据类型的成员值:

 
  

输出

 
  

Java 中的枚举还为我们提供了灵活性–可以在类内部或外部声明它们。

在类之外声明的枚举:

 
  

输出

 
  

在类内声明的枚举:

 
  

输出

 
  

每个枚举都是枚举类型的对象。 还记得我说的枚举赋予我们灵活性吗? 好吧,它也可以作为参数传递给语句。

使用语句的示例

 
  

输出

 
  

要记住的关键事项

  • 由于枚举是公共静态最终,这意味着可以使用枚举名称来访问它,而且由于它是最终的,因此我们无法创建该枚举的子代。

类和枚举之间到底有什么区别? 这是您可能会遇到的一个问题,它将是完全有效的!

类和枚举之间的主要区别

  • 枚举扩展了,并为它提供了人类可读的方法,和方法等。
  • 枚举可用于语句
  • 枚举构造函数为我们提供了一些额外的支持方法,例如,等,这些方法被证明确实有用。

原文: https://javatutorial.net/java-hashcode-method-example

Java 中的超类提供了两种比较对象的重要方法:和。当面对实现类之间的交互时,这些方法被广泛使用。 在本教程中,我们仅看一下。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

:默认情况下,此方法返回每次都是唯一的随机整数。 例如,如果第二次执行应用程序,则该值将不同。值主要用于哈希格式的集合中,例如 HashSet,等。请注意,在覆盖方法的每个类中都必须覆盖此方法。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

的简单说明

如果我们想执行方法,则需要确保这些对象具有相同的唯一哈希码 ID。当哈希码 ID 不同时,我们永远不要执行。

注意:当比较返回时,方法还必须返回。 如果哈希码不同,则对象不等

 
  

上面代码的简要分解

在前几行中,我们将创建两个对象,并传递一个和一个品牌名称。

 
  

然后,我们将一个布尔值存储在名为的变量中,该变量根据两个对象的 ID 是否相等而为或。

 
  

在那之后,我们有一个条件可以检查是还是。 如果为,则表示两个对象的 ID 相等。 如果不是,则意味着相反。 如果它们相等,我们只需打印“相等”。 如果不是,我们将打印一条有用的消息,该消息基本上会说,如果它们不相等,则无需使用相等进行检查,因为这两个对象不共享相同的 ID。

 
  

接下来是我们的静态类。

 
  
  • 在方法中返回一个常量值,而不是为每个对象返回唯一的值。
  • 使用之类的哈希集合时,不覆盖和。
  • 忘记使用方法或其他方法覆盖。
  1. 使用有效的算法,以便生成唯一的哈希码
  2. 覆盖方法时,请始终确保也覆盖了方法。
  3. 如果比较两个对象的哈希码的结果为,则方法也应为。 (请参见上面的代码示例)
  4. 如果在起诉哈希集合时没有覆盖和,则该集合将具有重复项。

原文: https://javatutorial.net/how-to-profile-standalone-java-applications

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

单元测试是一种软件测试方法,其中正在测试 Java 应用程序的小组件。 其目的是确认每个软件的行为均符合预期。 即使是这样,您也可以使用单元测试来确定另一种实现是否在内存和性能方面会带来更好的结果。 如果您对单元测试不是很熟悉,建议您参考本主题的本教程。

性能分析会检查应用程序,并尝试查找与 Java 应用程序相关的内存或性能问题。它允许您执行的操作是通过监视 JVM(Java)来获取有关性能,方法时序,对象分配等的数据。 虚拟机)。

您可以使用 IDE 来剖析这些类型的 Java 应用程序:

  1. Java EE / Web 应用程序
  2. Java 自由格式项目
  3. Java SE 项目
  4. NetBeans 模块和模块套件

JProfiler(Ej 技术)

对于分析工具,JProfiler 是许多开发人员的最佳选择。 它具有相对易于使用的界面,可帮助检查系统性能,内存使用情况,内存泄漏(如果有)和线程分析。

通过提供所有这些信息,我们可以诊断我们的应用程序的行为,并在需要时进行更改。 请注意,下图显示了正在运行的应用程序。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

JProfiler 界面概述

从上图可以看到,它显示了与性能,内存直接相关的不同组件,可以轻松地帮助您优化程序。

让我们尝试一个使用 JProfiler(或一般情况下进行概要分析)的实际用例 - 内存泄漏检测

什么是内存泄漏?

要了解内存泄漏,如果您具有有关内存的基本知识,那将是一个好主意。 如果您不希望阅读,请随时阅读本文和此文章。

如果您之前已经编写过 Java 应用程序,则可能会遇到这个普遍存在的问题。 真正的实质是尚未释放回池中的内存。

考虑以下示例:想象您必须创建所需的对象。 但是,当您不再需要它时,请继续前进,而不是从内存中“释放”它。 幕后发生的事情是,仍然引用该对象。 当应用程序开始消耗更多资源时,它很快就会耗尽资源,并导致。

这是垃圾收集无法从内存中删除这些未使用的对象的时候。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

上图给出的主要结论是 – 未使用的对象仍然占据运行中的内存,应用程序拥有的资源越多,它得到的性能越差,最终导致一个结果 – 。

既然您知道什么是内存泄漏,并且实际上在编写应用程序时要考虑到这一点,那么让我们看看性能分析如何帮助我们识别并消除内存泄漏。

要在 Java 应用程序中轻松跟踪内存泄漏,请导航至左侧空白处的堆查询器

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

堆沃克 Jprofiler

要启动示例项目,请单击左上角的“启动中心”:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

然后选择第一个选项并选择开始:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

您应该会看到以下窗口:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们感兴趣的是“内存泄露”。但是在单击它之前,请选择“标记堆”以表明我们对新分配的对象感兴趣。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在,这是应用程序开始创建未进行垃圾收集的对象的过程。

现在我们已经完成了,单击“Memory Leak”,然后等待一些时间来创建新对象。

现在,让我们进行堆快照:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在,当我们单击“Heap Walker”时,我们将看到自上次标记堆操作以来已创建了多少个实例:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

通过查看此窗口,我们不知道哪些对象正完全参与内存泄漏。 为此,我们需要单击“使用新的”。

然后我们将看到:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

选择最通用的类​​并选择传入的引用,然后单击“确定”:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从那里开始,查找内存泄漏的最佳方法是在“显示 GC 根目录的路径”上进行选择:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们设法找到实例的泄漏。

通常,测试是程序员必须能够使用的最重要的技术之一。 有许多工具可以帮助测试和调试。 有些比其他的简单,例如 Eclipse 中的 Debug 模式,有些则更复杂,例如 JProfiler。 但是,如果您关心性能,优化和无内存问题,有时您需要深入研究给定 Java 应用程序的内部工作。

到此这篇java 在线教程(java教程视频免费)的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!

版权声明


相关文章:

  • java入门网站(java基础教程网站)2025-06-24 12:27:05
  • Json字符串转map(Json字符串转义后 保存到mysql Java程序)2025-06-24 12:27:05
  • java爬虫入门教程(java爬虫代码示例)2025-06-24 12:27:05
  • java面试题八股文面试(程序员 面试 八股文)2025-06-24 12:27:05
  • java课程设计网站(java课程设计案例精编)2025-06-24 12:27:05
  • javaspring教程(java spring)2025-06-24 12:27:05
  • java内存模型和java内存结构(java内存模型八种操作)2025-06-24 12:27:05
  • console是什么意思csgo(console是什么意思Java)2025-06-24 12:27:05
  • java 在线教程(java 官方教程)2025-06-24 12:27:05
  • Java阻塞队列(java阻塞队列原理)2025-06-24 12:27:05
  • 全屏图片