欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入解析JVM之內(nèi)存結(jié)構(gòu)及字符串常量池(推薦)

 更新時(shí)間:2020年07月21日 10:35:23   作者:夜勿語(yǔ)  
Java作為一種平臺(tái)無(wú)關(guān)性的語(yǔ)言,其主要依靠于Java虛擬機(jī)——JVM,接下來(lái)通過(guò)本文給大家介紹JVM之內(nèi)存結(jié)構(gòu)及字符串常量池的相關(guān)知識(shí),需要的朋友可以參考下

前言

Java作為一種平臺(tái)無(wú)關(guān)性的語(yǔ)言,其主要依靠于Java虛擬機(jī)——JVM,我們寫好的代碼會(huì)被編譯成class文件,再由JVM進(jìn)行加載、解析、執(zhí)行,而JVM有統(tǒng)一的規(guī)范,所以我們不需要像C++那樣需要程序員自己關(guān)注平臺(tái),大大方便了我們的開發(fā)。另外,能夠運(yùn)行在JVM上的并只有Java,只要能夠編譯生成合乎規(guī)范的class文件的語(yǔ)言都是可以跑在JVM上的。而作為一名Java開發(fā),JVM是我們必須要學(xué)習(xí)了解的基礎(chǔ),也是通向高級(jí)及更高層次的必修課;但JVM的體系非常龐大,且術(shù)語(yǔ)非常多,所以初學(xué)者對(duì)此非常的頭疼。本系列文章就是筆者自己對(duì)于JVM的核心知識(shí)(內(nèi)存結(jié)構(gòu)、類加載、對(duì)象創(chuàng)建、垃圾回收等)以及性能調(diào)優(yōu)的學(xué)習(xí)總結(jié),另外未特別指出本系列文章都是基于HotSpot虛擬機(jī)進(jìn)行講解。

正文

JVM包含了非常多的知識(shí),比較核心的有內(nèi)存結(jié)構(gòu)、類加載、類文件結(jié)構(gòu)、垃圾回收、執(zhí)行 引擎、性能調(diào)優(yōu)、監(jiān)控等等這些知識(shí),但所有的功能都是圍繞著內(nèi)存結(jié)構(gòu)展開的,因?yàn)槲覀兙幾g后的代碼信息在運(yùn)行過(guò)程中都是存在于JVM自身的內(nèi)存區(qū)域中的,并且這塊區(qū)域相當(dāng)?shù)闹悄?,不需要C++那樣需要我們自己手動(dòng)釋放內(nèi)存,它實(shí)現(xiàn)了自動(dòng)垃圾回收機(jī)制,這也是Java廣受喜愛的原因之一。因此,學(xué)習(xí)JVM我們首先就得了解其內(nèi)存結(jié)構(gòu),熟悉包含的東西,才能更好的學(xué)習(xí)后面的知識(shí)。

內(nèi)存結(jié)構(gòu)

如上圖所示,JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)(即內(nèi)存結(jié)構(gòu))整體上劃分為線程私有和線程共享區(qū)域,線程私有的區(qū)域生命周期與線程相同,線程共享區(qū)域則存在于整個(gè)運(yùn)行期間 。而按照J(rèn)VM規(guī)范細(xì)分則分為程序計(jì)數(shù)器、虛擬機(jī)棧、本地方法棧、方法區(qū)和堆五大區(qū)域(直接內(nèi)存不屬于JVM)。注意這只是規(guī)范定義需要存在的區(qū)域,具體的實(shí)現(xiàn)則不在規(guī)范的定義中。

1. 程序計(jì)數(shù)器

如其名,這個(gè)部件就是用來(lái)記錄程序執(zhí)行的地址的,循環(huán)、跳轉(zhuǎn)、異常等等需要依靠它。為什么它是線程私有的呢?以單核CPU為例,多線程在執(zhí)行時(shí)是輪流執(zhí)行的,那么當(dāng)線程暫停后恢復(fù)就需要程序計(jì)數(shù)器恢復(fù)到暫停前的執(zhí)行位置繼續(xù)執(zhí)行,所以必然是每個(gè)線程對(duì)應(yīng)一個(gè)。由于它只需記錄一個(gè)執(zhí)行地址,所以它是五大區(qū)域中唯一一個(gè)不會(huì)出現(xiàn)OOM(內(nèi)存溢出)的區(qū)域。另外它是控制我們JAVA代碼的執(zhí)行的,在調(diào)用native方法時(shí)該計(jì)數(shù)器就沒有作用了,而是會(huì)由操作系統(tǒng)的計(jì)數(shù)器控制。

2. 虛擬機(jī)棧

虛擬機(jī)棧是方法執(zhí)行的內(nèi)存區(qū)域,每調(diào)用一個(gè)方法都會(huì)生成一個(gè)棧幀壓入棧中,當(dāng)方法執(zhí)行完成才會(huì)彈出棧。棧幀中又包含了局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接、方法出口。其中局部變量表就是用來(lái)存儲(chǔ)局部變量的(基本類型值和對(duì)象的引用),每一個(gè)位置32位,而像long/double這樣的變量則需要占用兩個(gè)槽位;操作數(shù)棧則類似于緩存,用于存儲(chǔ)執(zhí)行引擎在計(jì)算時(shí)需要用到的局部變量;動(dòng)態(tài)鏈接這里暫時(shí)不講,后面的章節(jié)會(huì)詳細(xì)分析;方法出口則包含異常出口和正常出口以及返回地址。下面來(lái)看三個(gè)方法示例分別展示棧和棧幀的運(yùn)行原理。

入棧出棧過(guò)程

public class ClassDemo1 {
 public static void main(String[] args) {
  new ClassDemo1().a();
 }
 static void a() { new ClassDemo1().b(); }
 static void b() { new ClassDemo1().c(); }
 static void c() {}

}

如上所示的方法調(diào)用入棧出棧的過(guò)程如下:

棧幀執(zhí)行原理

public class ClassDemo2 {

 public int work() {
  int x = 3;
  int y = 5;
  int z = (x + y) * 10;
  return z;
 }

 public static void main(String[] args) {
  new ClassDemo2().work();
 }

}

上面只是一簡(jiǎn)單的計(jì)算程序,通過(guò)javap -c ClassDemo2.class命令反編譯后看看生成的字節(jié)碼:

public class cn.dark.ClassDemo {
 public cn.dark.ClassDemo();
 Code:
  0: aload_0
  1: invokespecial #1     // Method java/lang/Object."<init>":()V
  4: return

 public int work();
 Code:
  0: iconst_3
  1: istore_1
  2: iconst_5
  3: istore_2
  4: iload_1
  5: iload_2
  6: iadd
  7: bipush  10
  9: imul
  10: istore_3
  11: iload_3
  12: ireturn

 public static void main(java.lang.String[]);
 Code:
  0: new   #2     // class cn/dark/ClassDemo
  3: dup
  4: invokespecial #3     // Method "<init>":()V
  7: invokevirtual #4     // Method work:()I
  10: pop
  11: return
}

主要看到work方法中,挨個(gè)來(lái)解釋(字節(jié)碼指令釋義可以參照這篇文章):執(zhí)行引擎首先通過(guò)iconst_3將常量3存入到操作數(shù)棧中,然后通過(guò)istore_1將該值從操作數(shù)棧中取出并存入到局部變量表的1號(hào)位(注意局部變量表示從0號(hào)開始的,但0號(hào)位默認(rèn)存儲(chǔ)了this變量);接著常量5執(zhí)行同樣的操作,完成后局部變量表中就存了3個(gè)變量(this、3、5);之后通過(guò)iload指令將局表變量表對(duì)應(yīng)位置的變量加載到操作數(shù)棧中,因?yàn)檫@里有括號(hào),所以先加載兩個(gè)變量到操作數(shù)棧并執(zhí)行括號(hào)中的加法,即調(diào)用iadd加法指令(所有二元算數(shù)指令會(huì)從操作數(shù)棧中取出頂部的兩個(gè)變量進(jìn)行計(jì)算,計(jì)算結(jié)果自動(dòng)加入到棧中);接著又將常量10壓入到棧中,繼續(xù)調(diào)用imul乘法指令,完成后需要通過(guò)istore命令再將結(jié)果存入到局部變量表中,最后通過(guò)ireturn返回(不管我們方法是否定義了返回值都會(huì)調(diào)用該指令,只是當(dāng)我們定義了返回值時(shí),首先會(huì)通過(guò)iload指令加載局部變量表的值并返回給調(diào)用者)。以上就是棧幀的運(yùn)行原理。

該區(qū)域同樣是線程私有,每個(gè)線程對(duì)應(yīng)會(huì)生成一個(gè)棧,并且每個(gè)棧默認(rèn)大小是1M,但也不是絕對(duì),根據(jù)操作系統(tǒng)不同會(huì)有所不

一樣,另外可以用-Xss控制大小,官方文檔對(duì)該該參數(shù)解釋如下:

既然可以控制大小,那么這塊區(qū)域自然就會(huì)存在內(nèi)存不足的情況,對(duì)于棧當(dāng)內(nèi)存不足時(shí)會(huì)出現(xiàn)下面兩種異常:

  • 棧溢出(StackOverflowError)
  • 內(nèi)存溢出(OutOfMemoryError)

為什么會(huì)有兩種異常呢?在周志明的《深入理解Java虛擬機(jī)》一書中講到,在單線程環(huán)境下只會(huì)出現(xiàn)StackOverflowError異常,即棧幀填滿了?;蚓植孔兞勘磉^(guò)大;而OutOfMemoryError只有當(dāng)多線程情況下,無(wú)節(jié)制的創(chuàng)建多個(gè)棧才會(huì)出現(xiàn),因?yàn)椴僮飨到y(tǒng)對(duì)于每個(gè)進(jìn)程是有內(nèi)存限制的,即超出了進(jìn)程可用的內(nèi)存,無(wú)法創(chuàng)建新的棧。

  • 棧幀共享機(jī)制

通過(guò)上文我們知道同一個(gè)線程內(nèi)每個(gè)方法的調(diào)用會(huì)對(duì)應(yīng)生成相應(yīng)的棧幀,而棧幀又包含了局部變量表和操作數(shù)棧等內(nèi)容,那么當(dāng)方法間傳遞參數(shù)時(shí)是否可以優(yōu)化,使得它們共享一部分內(nèi)存空間呢?答案是肯定的,像下面這段代碼:

public int work(int x) throws Exception{
  int z =(x+5)*10;// 參數(shù)會(huì)按照順序放到局部變量表
  Thread.sleep(Integer.MAX_VALUE);
  return z;
 }
 public static void main(String[] args)throws Exception {
  JVMStack jvmStack = new JVMStack();
  jvmStack.work(10);//10 放入操作數(shù)棧
 }

在main方法中首先會(huì)把10放入操作數(shù)棧然后傳遞給work方法,作為參數(shù),會(huì)按照順序放入到局部變量表中,所以x會(huì)放到局部變量表的1號(hào)位(0號(hào)位是this),而此時(shí)通過(guò)HSDB工具查看這時(shí)的棧調(diào)用信息會(huì)發(fā)現(xiàn)如下情況:

如上圖所示,中間一小塊用紅框圈起來(lái)的就是兩個(gè)棧幀共享的內(nèi)存區(qū)域,即work的局部變量表和main的操作數(shù)棧的一部分。

3. 本地方法棧

和虛擬機(jī)棧是一樣的,只不過(guò)該區(qū)域是用來(lái)執(zhí)行本地本地方法的,有些虛擬機(jī)甚至直接將其和虛擬機(jī)棧合二為一,如HotSpot。(通過(guò)上面的圖也可以看到,最上面顯示了Thread.sleep()的棧幀信息,并標(biāo)記了native)

4. 方法區(qū)

該區(qū)域是線程共享的區(qū)域,用來(lái)存儲(chǔ)已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)。該區(qū)域在JDK1.7以前是以永久代方式實(shí)現(xiàn)的,存在于堆中,可以通過(guò)-XX:PermSize(初始值)、-XX:MaxPermSize(最大值)參數(shù)設(shè)置大小;而1.8以后以元空間方式實(shí)現(xiàn),使用的是直接內(nèi)存(但運(yùn)行時(shí)常量池和靜態(tài)變量仍放在堆中),可以通過(guò)-XX:MetaspaceSize(初始值)、-XX:MaxMetaspaceSize(最大值)控制大小,如果不設(shè)置則只受限于本地內(nèi)存大小。為什么會(huì)這么改變呢?因?yàn)榉椒▍^(qū)和堆都會(huì)進(jìn)行垃圾回收,但是方法區(qū)中的信息相對(duì)比較靜態(tài),回收難以達(dá)到成效,同時(shí)需要占用的空間大小更多的取決于我們class的大小和數(shù)量,即對(duì)該區(qū)域難以設(shè)置一個(gè)合理的大小,所以將其直接放到本地內(nèi)存中是非常有用且合理的。

在方法區(qū)中還存在常量池(1.7后放入堆中),而常量池也分了幾種,常常讓初學(xué)者比較困惑,比如靜態(tài)常量池、運(yùn)行時(shí)常量池、字符串常量池。靜態(tài)常量池就是指存在于我們的class文件中的常量池,通過(guò)javap -v ClassDemo.class反編譯上面的代碼可以看到該常量池:

Constant pool:
 #1 = Methodref   #5.#26   // java/lang/Object."<init>":()V
 #2 = Class    #27   // cn/dark/ClassDemo
 #3 = Methodref   #2.#26   // cn/dark/ClassDemo."<init>":()V
 #4 = Methodref   #2.#28   // cn/dark/ClassDemo.work:()I
 #5 = Class    #29   // java/lang/Object
 #6 = Utf8    <init>
 #7 = Utf8    ()V
 #8 = Utf8    Code
 #9 = Utf8    LineNumberTable
 #10 = Utf8    LocalVariableTable
 #11 = Utf8    this
 #12 = Utf8    Lcn/dark/ClassDemo;
 #13 = Utf8    work
 #14 = Utf8    ()I
 #15 = Utf8    x
 #16 = Utf8    I
 #17 = Utf8    y
 #18 = Utf8    z
 #19 = Utf8    main
 #20 = Utf8    ([Ljava/lang/String;)V
 #21 = Utf8    args
 #22 = Utf8    [Ljava/lang/String;
 #23 = Utf8    MethodParameters
 #24 = Utf8    SourceFile
 #25 = Utf8    ClassDemo.java
 #26 = NameAndType  #6:#7   // "<init>":()V
 #27 = Utf8    cn/dark/ClassDemo
 #28 = NameAndType  #13:#14  // work:()I
 #29 = Utf8    java/lang/Object

靜態(tài)常量池中就是存儲(chǔ)了類和方法的信息、符號(hào)引用以及字面量等東西,當(dāng)類加載到內(nèi)存中后,JVM就會(huì)將這些內(nèi)容存放到運(yùn)行時(shí)常量池中,同時(shí)會(huì)將符號(hào)引用(可以理解為對(duì)象方法的定位描述符)會(huì)被解析為直接引用(即對(duì)象的內(nèi)存地址)存入到運(yùn)行時(shí)常量池中(因?yàn)樵陬惣虞d之前并不知道符號(hào)引用所對(duì)應(yīng)的對(duì)象內(nèi)存地址是多少,需要用符號(hào)替代)。而字符串常量池網(wǎng)上爭(zhēng)議比較多,我個(gè)人理解它也是運(yùn)行時(shí)常量池的一部分,專門用于存儲(chǔ)字符串常量,這里先簡(jiǎn)單提一下,稍后會(huì)詳細(xì)分析字符串常量池。

5. 堆

這個(gè)區(qū)域是垃圾回收的重點(diǎn)區(qū)域,對(duì)象都存在于堆中(但隨著JIT編譯器的發(fā)展和逃逸分析技術(shù)的成熟,對(duì)象也不一定都是存在于堆中),可以通過(guò)-Xms(最小值)、-Xmx(最大值)、-Xmn(新生代大小)、-XX:NewSize(新生代最小值)、-XX:MaxNewSize(新生代最大值)這些參數(shù)進(jìn)行控制。
在堆中又分為了新生代和老年代,新生代又分為Eden空間、From Survivor空間、To Survivor空間。詳細(xì)內(nèi)容后面文章會(huì)詳細(xì)講解,這里不過(guò)多闡述。

6. 直接內(nèi)存

直接內(nèi)存也叫堆外內(nèi)存,不屬于JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)的一部分,主要通過(guò)DirectByteBuffer申請(qǐng)內(nèi)存,該對(duì)象存在于堆中,包含了對(duì)堆外內(nèi)存的引用;另外也可以通過(guò)Unsafe類或其它JNI手段直接申請(qǐng)內(nèi)存。它的大小受限于本地內(nèi)存的大小,也可以通過(guò)-XX:MaxDirectMemorySize設(shè)置,所以這一塊也會(huì)出現(xiàn)OOM異常且較難排查。

字符串常量池

這個(gè)區(qū)域不是虛擬機(jī)規(guī)范中的內(nèi)容,所有官方的正式文檔中也沒有明確指出有這一塊,所以這里只是根據(jù)現(xiàn)象推導(dǎo)出結(jié)論。什么現(xiàn)象呢?有一個(gè)關(guān)于字符串對(duì)象的高頻面試題:下面的代碼究竟會(huì)創(chuàng)建幾個(gè)對(duì)象?

String str = "abc";
String str1 = new string("cde");

我們先不管這個(gè)面試題,先來(lái)思考下面代碼的輸出結(jié)果是怎樣的(以下試驗(yàn)基于JDK8,更早的版本結(jié)果會(huì)有所不同):

tring s1 = "abc";
 String s2 = "ab" + "c";
 String s3 = new String("abc");
 String s4 = new StringBuilder("ab").append("c").toString();
 System.out.println("s1 == s2:" + (s1 == s2));
 System.out.println("s1 == s3:" + (s1 == s3));
 System.out.println("s1 == s4:" + (s1 == s4));
 System.out.println("s1 == s3.intern:" + (s1 == s3.intern()));
 System.out.println("s1 == s4.intern:" + (s1 == s4.intern()));

輸出結(jié)果如下:

s1 == s2:true
s1 == s3:false
s1 == s4:false
s1 == s3.intern:true
s1 == s4.intern:true

上面的輸出結(jié)果和你想象的是否一樣呢?為什么呢?一個(gè)個(gè)來(lái)分析。

  • s1 == s2:字面量“abc”會(huì)首先去字符串常量池找是否有"abc"這個(gè)字符串,如果有直接返回引用,如果沒有則創(chuàng)建一個(gè)新對(duì)象并返回引用;s2你可能會(huì)覺得會(huì)創(chuàng)建"ab"、"c"和“abc”三個(gè)對(duì)象,但實(shí)際上首先會(huì)被編譯器優(yōu)化為“abc”,所以等同于s1,即直接從字符串常量池返回s1的引用。
  • s1 == s3:s3是通過(guò)new創(chuàng)建的,所以這個(gè)String對(duì)象肯定是存在于堆的,但是其中的char[]數(shù)組是引用字符創(chuàng)常量池中的s1,如果在這之前沒有定義的話會(huì)先在常量池中創(chuàng)建“abc”對(duì)象。所以這里可能會(huì)創(chuàng)建一個(gè)或兩個(gè)對(duì)象。
  • s1 == s4:s4通過(guò)StringBuilder拼接字符串對(duì)象,所以看起來(lái)理所當(dāng)然的s1 != s4,但實(shí)際上也沒那么簡(jiǎn)單,反編譯上面的代碼會(huì)可以發(fā)現(xiàn)這里又會(huì)被編譯器優(yōu)化為s4 = "ab" + "c"。猜猜這下會(huì)創(chuàng)建幾個(gè)對(duì)象呢?拋開前面創(chuàng)建的對(duì)象的影響,這里會(huì)創(chuàng)建3個(gè)對(duì)象,因?yàn)榕cs2不同的是s4是編譯器優(yōu)化過(guò)后還存在“+”拼接,因此會(huì)在字符創(chuàng)常量池創(chuàng)建“ab”、"c"以及“abc”三個(gè)對(duì)象。前兩個(gè)可以反編譯看字節(jié)碼指令或是通過(guò)內(nèi)存搜索驗(yàn)證,而第三個(gè)的驗(yàn)證稍后進(jìn)行。
  • s1 == s3.intern/s4.intern:這兩個(gè)為什么是true呢?先來(lái)看看周志明在《深入理解Java虛擬機(jī)》書中說(shuō)的:

使用String類的intern方法動(dòng)態(tài)添加字符串常量到運(yùn)行時(shí)常量池中(intern方法在1.6和1.7及以后的實(shí)現(xiàn)不相同,1.6字符串常量池放于永久代中,intern會(huì)把首次遇到的字符串實(shí)例復(fù)制永久代中并返回永久代中的引用,而1.7及以后常量池也放入到了堆中,intern也不會(huì)再?gòu)?fù)制實(shí)例,只是在常量池中記錄首次出現(xiàn)的實(shí)例引用)。

上面的意思很明確,1.7以后intern方法首先會(huì)去字符串常量池尋找對(duì)應(yīng)的字符串,如果找到了則返回對(duì)應(yīng)的引用,如果沒有找到則先會(huì)在字符串常量池中創(chuàng)建相應(yīng)的對(duì)象。因此,上面s4和s4調(diào)用intern方法時(shí)都是返回s1的引用。
看到這里,相信各位讀者基本上也都能理解了,對(duì)于開始的面試題應(yīng)該也是心中有數(shù)了,最后再來(lái)驗(yàn)證剛剛說(shuō)的“第三個(gè)對(duì)象”的問(wèn)題,先看下面代碼:

String s4 = new StringBuilder("ab").append("c").toString();
System.out.println(s4 == s4.intern());

這里結(jié)果是true。為什么呢?別急,再來(lái)看另外一段代碼:

String s3 = new String("abc");
String s4 = new StringBuilder("ab").append("c").toString();

System.out.println(s3 == s3.intern());
System.out.println(s4 == s4.intern());

這里結(jié)果是兩個(gè)false,和你心中的答案是一致的么?上文剛剛說(shuō)了intern會(huì)先去字符串常量池找,找到則返回引用,否則在字符創(chuàng)常量池創(chuàng)建一個(gè)對(duì)象,所以第一段代碼結(jié)果等于true正好說(shuō)明了通過(guò)StringBuilder拼接的字符串會(huì)存到字符串常量池中;而第二段代碼中,在StringBuilder拼接字符串之前已經(jīng)優(yōu)先使用new創(chuàng)建了字符串,也就會(huì)在字符串常量里創(chuàng)建“abc”對(duì)象,因此s4.intern返回的是該常量的引用,和s4不相等。你可能會(huì)說(shuō)是因?yàn)閮?yōu)先調(diào)用了s3.intern方法,但即使你去掉這一段,結(jié)果還是一樣的,也剛好驗(yàn)證了new String("abc")會(huì)創(chuàng)建兩個(gè)對(duì)象(在此之前沒有定義“abc”字面量,就會(huì)在字符串常量池創(chuàng)建對(duì)象,然后堆中創(chuàng)建String對(duì)象并引用該常量,否則只會(huì)創(chuàng)建堆中的String對(duì)象)。

總結(jié)

本文是JVM系列的開篇,主要分析JVM的運(yùn)行時(shí)數(shù)據(jù)區(qū)、簡(jiǎn)單參數(shù)設(shè)置和字節(jié)碼閱讀分析,這也是學(xué)習(xí)JVM及性能調(diào)優(yōu)的基礎(chǔ),讀者需要深刻理解這些內(nèi)容以及哪些區(qū)域會(huì)發(fā)生內(nèi)存溢出(只有程序計(jì)數(shù)器不會(huì)內(nèi)存溢出),另外關(guān)于運(yùn)行時(shí)常量池和字符串常量池的內(nèi)容也需要理解透徹。

到此這篇關(guān)于深入探究JVM之內(nèi)存結(jié)構(gòu)及字符串常量池的文章就介紹到這了,更多相關(guān)JVM內(nèi)存結(jié)構(gòu)字符串常量池內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解Java 反射和反射的應(yīng)用場(chǎng)景

    詳解Java 反射和反射的應(yīng)用場(chǎng)景

    這篇文章主要介紹了Java 反射和反射的應(yīng)用場(chǎng)景的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)Java反射的相關(guān)知識(shí),感興趣的朋友可以了解下
    2020-08-08
  • Java比較兩個(gè)對(duì)象大小的三種方法詳解

    Java比較兩個(gè)對(duì)象大小的三種方法詳解

    在優(yōu)先級(jí)隊(duì)列中插入的元素必須能比較大小,如果不能比較大小,如插入兩個(gè)學(xué)生類型的元素,會(huì)報(bào)ClassCastException異常。本文就為大家總結(jié)了Java比較兩個(gè)對(duì)象大小的三種方法,需要的可以參考一下
    2022-07-07
  • 基于RecyclerChart的KLine繪制詳解

    基于RecyclerChart的KLine繪制詳解

    這篇文章主要為大家詳細(xì)介紹了基于RecyclerChart實(shí)現(xiàn)KLine繪制的相關(guān)資料,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-03-03
  • SpringBoot項(xiàng)目整合MybatisPlus并使用SQLite作為數(shù)據(jù)庫(kù)的過(guò)程

    SpringBoot項(xiàng)目整合MybatisPlus并使用SQLite作為數(shù)據(jù)庫(kù)的過(guò)程

    SQLite是一個(gè)緊湊的庫(kù),啟用所有功能后,庫(kù)大小可以小于 750KiB, 具體取決于目標(biāo)平臺(tái)和編譯器優(yōu)化設(shè)置, 內(nèi)存使用量和速度之間需要權(quán)衡,這篇文章主要介紹了SpringBoot項(xiàng)目整合MybatisPlus并使用SQLite作為數(shù)據(jù)庫(kù),需要的朋友可以參考下
    2024-07-07
  • Java 數(shù)組獲取最大和最小值的實(shí)例實(shí)現(xiàn)

    Java 數(shù)組獲取最大和最小值的實(shí)例實(shí)現(xiàn)

    這篇文章主要介紹了Java 數(shù)組獲取最大和最小值的實(shí)例實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • 構(gòu)建Maven多模塊項(xiàng)目的方法

    構(gòu)建Maven多模塊項(xiàng)目的方法

    這篇文章主要介紹了構(gòu)建Maven多模塊項(xiàng)目的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-08-08
  • Java中的Timer與TimerTask源碼及使用解析

    Java中的Timer與TimerTask源碼及使用解析

    這篇文章主要介紹了Java中的Timer與TimerTask源碼及使用解析,在Java中,經(jīng)常使用Timer來(lái)定時(shí)調(diào)度任務(wù),Timer調(diào)度任務(wù)有一次性調(diào)度和循環(huán)調(diào)度,循環(huán)調(diào)度有分為固定速率調(diào)度(fixRate)和固定時(shí)延調(diào)度(fixDelay),需要的朋友可以參考下
    2023-10-10
  • mybatis參數(shù)String與Integer類型的判斷方式

    mybatis參數(shù)String與Integer類型的判斷方式

    這篇文章主要介紹了mybatis參數(shù)String與Integer類型的判斷方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Java依賴注入容器超詳細(xì)全面講解

    Java依賴注入容器超詳細(xì)全面講解

    依賴注入(Dependency Injection)和控制反轉(zhuǎn)(Inversion of Control)是同一個(gè)概念。具體含義是:當(dāng)某個(gè)角色(可能是一個(gè)Java實(shí)例,調(diào)用者)需要另一個(gè)角色(另一個(gè)Java實(shí)例,被調(diào)用者)的協(xié)助時(shí),在 傳統(tǒng)的程序設(shè)計(jì)過(guò)程中,通常由調(diào)用者來(lái)創(chuàng)建被調(diào)用者的實(shí)例
    2023-01-01
  • Java實(shí)戰(zhàn)之晚會(huì)抽獎(jiǎng)系統(tǒng)的實(shí)現(xiàn)

    Java實(shí)戰(zhàn)之晚會(huì)抽獎(jiǎng)系統(tǒng)的實(shí)現(xiàn)

    這篇文章主要介紹了如何利用Java語(yǔ)言編寫一個(gè)晚會(huì)抽獎(jiǎng)系統(tǒng),文中采用到的技術(shù)有Jdbc、Servlert、JavaScript、JQuery、Ajax等,感興趣的可以學(xué)習(xí)一下
    2022-03-03

最新評(píng)論