最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中invokedynamic字節(jié)碼指令問題

 更新時(shí)間:2019年04月23日 09:57:26   作者:kelthuzadx  
這篇文章主要介紹了Java中invokedynamic字節(jié)碼指令問題,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

1. 方法引用和invokedynamic

invokedynamic是jvm指令集里面最復(fù)雜的一條。本文將從高觀點(diǎn)的角度下分析invokedynamic指令是如何實(shí)現(xiàn)方法引用(Method reference)的。

具體言之,有這樣一個(gè)方法引用:

interface Encode {
  void encode(Derive person);
}
class Base {
  public void encrypt() {
    System.out.println("Base::speak");
  }
}
class Derive extends Base {
  @Override
  public void encrypt() {
    System.out.println("Derive::speak");
  }
}
public class MethodReference {
  public static void main(String[] args) {
    Encode encode = Base::encrypt;
    System.out.println(encode);
  }
}

使用javap -verbose MethodReference.class查看對(duì)應(yīng)字節(jié)碼:

// 常量池
Constant pool:
  #1 = Methodref     #6.#22     // java/lang/Object."<init>":()V
  #2 = InvokeDynamic   #0:#27     // #0:encode:()LEncode;
  #3 = Fieldref      #28.#29    // java/lang/System.out:Ljava/io/PrintStream;
  #4 = Methodref     #30.#31    // java/io/PrintStream.println:(Ljava/lang/Object;)V
  #5 = Class       #32      // MethodReference
  #6 = Class       #33      // java/lang/Object
  #7 = Utf8        <init>
  #8 = Utf8        ()V
  #9 = Utf8        Code
 #10 = Utf8        LineNumberTable
 #11 = Utf8        LocalVariableTable
 #12 = Utf8        this
 #13 = Utf8        LMethodReference;
 #14 = Utf8        main
 #15 = Utf8        ([Ljava/lang/String;)V
 #16 = Utf8        args
 #17 = Utf8        [Ljava/lang/String;
 #18 = Utf8        encode
 #19 = Utf8        LEncode;
 #20 = Utf8        SourceFile
 #21 = Utf8        MethodReference.java
 #22 = NameAndType    #7:#8     // "<init>":()V
 #23 = Utf8        BootstrapMethods
 #24 = MethodHandle    #6:#34     // invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;L
java/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang
/invoke/CallSite;
 #25 = MethodType     #35      // (LDerive;)V
 #26 = MethodHandle    #5:#36     // invokevirtual Base.encrypt:()V
 #27 = NameAndType    #18:#37    // encode:()LEncode;
 #28 = Class       #38      // java/lang/System
 #29 = NameAndType    #39:#40    // out:Ljava/io/PrintStream;
 #30 = Class       #41      // java/io/PrintStream
 #31 = NameAndType    #42:#43    // println:(Ljava/lang/Object;)V
 #32 = Utf8        MethodReference
 #33 = Utf8        java/lang/Object
 #34 = Methodref     #44.#45    // java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/Str
ing;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallS
ite;
 #35 = Utf8        (LDerive;)V
 #36 = Methodref     #46.#47    // Base.encrypt:()V
 #37 = Utf8        ()LEncode;
 #38 = Utf8        java/lang/System
 #39 = Utf8        out
 #40 = Utf8        Ljava/io/PrintStream;
 #41 = Utf8        java/io/PrintStream
 #42 = Utf8        println
 #43 = Utf8        (Ljava/lang/Object;)V
 #44 = Class       #48      // java/lang/invoke/LambdaMetafactory
 #45 = NameAndType    #49:#53    // metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Lj
ava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
 #46 = Class       #54      // Base
 #47 = NameAndType    #55:#8     // encrypt:()V
 #48 = Utf8        java/lang/invoke/LambdaMetafactory
 #49 = Utf8        metafactory
// 字節(jié)碼指令
 public static void main(java.lang.String[]);
   0: invokedynamic #2, 0       // InvokeDynamic #0:encode:()LEncode;
   5: astore_1
   6: getstatic   #3         // Field java/lang/System.out:Ljava/io/PrintStream;
   9: aload_1
  10: invokevirtual #4         // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
  13: return
// 屬性
SourceFile: "MethodReference.java"
InnerClasses:
   public static final #51= #50 of #56; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
BootstrapMethods:
 0: #24 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/Method
Type;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  Method arguments:
   #25 (LDerive;)V
   #26 invokevirtual Base.encrypt:()V
   #25 (LDerive;)V

使用invokedynamic指令生成encode對(duì)象,然后存入局部變量槽#1。接著獲取getstatic獲取java/lang/System類的out字段,最后局部變量槽#1作為參數(shù)壓棧,invokevirtual虛函數(shù)調(diào)用System.out的println方法。

那么invokedynamic到底是怎么生成encode對(duì)象的呢?

1.虛擬機(jī)解析

hotspot對(duì)invokedynamic指令的解釋如下:

CASE(_invokedynamic): {
    u4 index = Bytes::get_native_u4(pc+1);
    ConstantPoolCacheEntry* cache = cp->constant_pool()->invokedynamic_cp_cache_entry_at(index);
    // We are resolved if the resolved_references field contains a non-null object (CallSite, etc.)
    // This kind of CP cache entry does not need to match the flags byte, because
    // there is a 1-1 relation between bytecode type and CP entry type.
    if (! cache->is_resolved((Bytecodes::Code) opcode)) {
     CALL_VM(InterpreterRuntime::resolve_from_cache(THREAD, (Bytecodes::Code)opcode),
         handle_exception);
     cache = cp->constant_pool()->invokedynamic_cp_cache_entry_at(index);
    }
    Method* method = cache->f1_as_method();
    if (VerifyOops) method->verify();
    if (cache->has_appendix()) {
     ConstantPool* constants = METHOD->constants();
     SET_STACK_OBJECT(cache->appendix_if_resolved(constants), 0);
     MORE_STACK(1);
    }
    istate->set_msg(call_method);
    istate->set_callee(method);
    istate->set_callee_entry_point(method->from_interpreted_entry());
    istate->set_bcp_advance(5);
    // Invokedynamic has got a call counter, just like an invokestatic -> increment!
    BI_PROFILE_UPDATE_CALL();
    UPDATE_PC_AND_RETURN(0); // I'll be back...
   }

使用invokedynamic_cp_cache_entry_at獲取常量池對(duì)象,然后檢查是否已經(jīng)解析過,如果沒有就解析反之復(fù)用,然后設(shè)置方法字節(jié)碼,留待后面解釋執(zhí)行。那么,重點(diǎn)是這個(gè)解析。我們對(duì)照著jvm spec來(lái)看。

根據(jù)jvm文檔的描述,invokedynamic的操作數(shù)(operand)指向常量池一個(gè)動(dòng)態(tài)調(diào)用點(diǎn)描述符(dynamic call site specifier)。

 動(dòng)態(tài)調(diào)用點(diǎn)描述符是一個(gè)CONSTANT_InvokeDynamic_info結(jié)構(gòu)體:

CONSTANT_InvokeDynamic_info {
 u1 tag;
 u2 bootstrap_method_attr_index;
 u2 name_and_type_index;
}

•tag 表示這個(gè)結(jié)構(gòu)體的常量,不用管
•bootstrap_method_attr_index 啟動(dòng)方法數(shù)組
•name_and_type_index 一個(gè)名字+類型的描述字段,就像這樣Object p放到虛擬機(jī)里面表示是Ljava/lang/Object; p

然后啟動(dòng)方法數(shù)組結(jié)構(gòu)是這樣:

BootstrapMethods_attribute {
 ...
 u2 num_bootstrap_methods;
 { 
 u2 bootstrap_method_ref;
 u2 num_bootstrap_arguments;
 u2 bootstrap_arguments[num_boot]
 } bootstrap_methods[num_bootstrap_methods];
}

就是一個(gè)數(shù)組,每個(gè)元素是{指向MethodHandle的索引,啟動(dòng)方法參數(shù)個(gè)數(shù),啟動(dòng)方法參數(shù)}

MethodlHandle是個(gè)非常重要的結(jié)構(gòu),指導(dǎo)了虛擬機(jī)對(duì)于這個(gè)啟動(dòng)方法的解析,先關(guān)注一下這個(gè)結(jié)構(gòu):

CONSTANT_MethodHandle_info {
 u1 tag;//表示該結(jié)構(gòu)體的常量tag,可以忽略
 u1 reference_kind;
 u2 reference_index;
}

•reference_kind是[1,9]的數(shù),它表示這個(gè)method handle的類型,這個(gè)字段和字節(jié)碼的行為有關(guān)。
•reference_index 根據(jù)reference_kind會(huì)指向常量池的不同類型,具體來(lái)說 ◦reference_kind==1,3,4 指向CONSTANT_Fieldref_info結(jié)構(gòu),表示一個(gè)類的字段
◦reference_kind==5,8,指向CONSTANT_Methodref_info,表示一個(gè)類的方法
◦reference_kind==6,7, 同上,只是兼具接口的方法或者類的方法的可能。
◦reference_kind==9,指向CONSTATN_InterfaceMethodref_info,表示一個(gè)接口方法

通過invokedynamic,我們可以得

1.名字+描述符的表示(由name_and_type_index給出)

2.一個(gè)啟動(dòng)方法數(shù)組(由bootstrap_method_attr_index給出)

2.手動(dòng)解析

可以手動(dòng)模擬一下解析,看看最后得到的數(shù)據(jù)是什么樣的。在這個(gè)例子中:

  0: invokedynamic #2,  0   //第二個(gè)operand總是0

查看常量池#2項(xiàng):

#2 = InvokeDynamic   #0:#27     // #0:encode:()LEncode;
#27 = NameAndType    #18:#37    // encode:()LEncode;

BootstrapMethods:
 0: #24 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/Method
Type;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  Method arguments:
   #25 (LDerive;)V
   #26 invokevirtual Base.encrypt:()V
   #25 (LDerive;)V

得到的名字+描述符是:Encode.encode(),啟動(dòng)方法數(shù)組有一個(gè)元素,回憶下之前說的,這個(gè)元素構(gòu)成如下:

{指向MethodHandle的索引,啟動(dòng)方法參數(shù)個(gè)數(shù),啟動(dòng)方法參數(shù)}

這里得到的MethodHandle表示的是LambdaMetafactory.metafactory:

#24 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/Method
Type;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;`

啟動(dòng)方法參數(shù)有:

•#25 (LDerive;)V
•#26 invokevirtual Base.encrypt:()V
•#25 (LDerive;)V

3. java.lang.invoke.LambdaMetafactory

先說說LambdaMetafactory有什么用。javadoc給出的解釋是:

Facilitates the creation of simple "function objects" that implement one or more interfaces by delegation to a provided MethodHandle, after appropriate type adaptation and partial evaluation of arguments. Typically used as a bootstrap method for invokedynamic call sites, to support the lambda expression and method reference expression features of the Java Programming Language.
 When the target of the CallSite returned from this method is invoked, the resulting function objects are instances of a class which implements the interface named by the return type of invokedType, declares a method with the name given by invokedName and the signature given by samMethodType. It may also override additional methods from Object.

LambdaMetafactory方便我們創(chuàng)建簡(jiǎn)單的"函數(shù)對(duì)象",這些函數(shù)對(duì)象通過代理MethodHandle實(shí)現(xiàn)了一些接口。

 當(dāng)這個(gè)函數(shù)返回的CallSite被調(diào)用的時(shí)候,會(huì)產(chǎn)生一個(gè)類的實(shí)例,該類還實(shí)現(xiàn)了一些方法,具體由參數(shù)給出

將上面得到的MethodHandle寫得更可讀就是調(diào)用的這個(gè)方法:

public static CallSite LambdaMetafactory.metafactory(MethodHandles.Lookup caller,
                    String invokedName,
                    MethodType invokedType,
                    MethodType samMethodType,
                    MethodHandle implMethod,
                    MethodType instantiatedMethodType);

六個(gè)參數(shù),慢慢來(lái)。

3.1 LambdaMetafactory.metafactory()調(diào)用前

要知道參數(shù)是什么意思,可以從它的調(diào)用者來(lái)管中窺豹:

 

static CallSite makeSite(MethodHandle bootstrapMethod,
               // Callee information:
               String name, MethodType type,
               // Extra arguments for BSM, if any:
               Object info,
               // Caller information:
               Class<?> callerClass) {
    MethodHandles.Lookup caller = IMPL_LOOKUP.in(callerClass);
    CallSite site;
    try {
      Object binding;
      info = maybeReBox(info);
      if (info == null) {
        binding = bootstrapMethod.invoke(caller, name, type);
      } else if (!info.getClass().isArray()) {
        binding = bootstrapMethod.invoke(caller, name, type, info);
      } else {
        Object[] argv = (Object[]) info;
        maybeReBoxElements(argv);
        switch (argv.length) {
        ...
        case 3:
          binding = bootstrapMethod.invoke(caller, name, type,
                           argv[0], argv[1], argv[2]);
          break;
        ...
        }
      }
      //System.out.println("BSM for "+name+type+" => "+binding);
      if (binding instanceof CallSite) {
        site = (CallSite) binding;
      } else {
        throw new ClassCastException("bootstrap method failed to produce a CallSite");
      }
      ...
    } catch (Throwable ex) {
      ...
    }
    return site;
  }

對(duì)java.lang.invoke.LambdaMetafactory的調(diào)用是通過MethodHandle引發(fā)的,所以可能還需要補(bǔ)一下MethodHandle的用法,百度一搜一大堆,javadoc也給出了使用示例:

String s;
MethodType mt; MethodHandle mh;
MethodHandles.Lookup lookup = MethodHandles.lookup();
// mt is (char,char)String
mt = MethodType.methodType(String.class, char.class, char.class);
mh = lookup.findVirtual(String.class, "replace", mt);
s = (String) mh.invoke("daddy",'d','n');
// invokeExact(Ljava/lang/String;CC)Ljava/lang/String;
assertEquals(s, "nanny");

回到源碼,關(guān)鍵是這句:

binding = bootstrapMethod.invoke(caller, name, type,
                argv[0], argv[1], argv[2]);

argv[0],argv[1],argv[2]分別表示之前啟動(dòng)方法的三個(gè)參數(shù),

caller即調(diào)用者,這里是MethodReference這個(gè)類,然后name和type參見下面的詳細(xì)解釋:

•MethodHandles.Lookup caller 表示哪個(gè)類引發(fā)了調(diào)動(dòng)
•String invokedName 表示生成的類的方法名,對(duì)應(yīng)例子的encode
•MethodType invokedType 表示CallSite的函數(shù)簽名,其中參數(shù)類型表示捕獲變量的類型,返回類型是類要實(shí)現(xiàn)的接口的名字,對(duì)應(yīng)例子的()Encode,即要生成一個(gè)類,這個(gè)類沒有捕獲自由變量(所以參數(shù)類為空),然后這個(gè)類要實(shí)現(xiàn)Encode接口(返回類型為生成的類要實(shí)現(xiàn)的接口)

接下來(lái)

•MethodType samMethodType 表示要實(shí)現(xiàn)的方法的函數(shù)簽名和返回值,對(duì)于例子的#25 (LDerive;)V,即實(shí)現(xiàn)方法帶有一個(gè)形參,返回void
•MethodHandle implMethod 表示實(shí)現(xiàn)的方法里面應(yīng)該調(diào)用的函數(shù),對(duì)于例子的#26 invokevirtual Base.encrypt:()V,表示調(diào)用Base的虛函數(shù)encrypt,返回void
•MethodType instantiatedMethodType 表示調(diào)用方法的運(yùn)行時(shí)描述符,如果不是泛型就和samMethodType一樣

3.2 LambdaMetafactory.metafactory()調(diào)用

源碼面前,不是了無(wú)秘密嗎hhh,點(diǎn)進(jìn)源碼看看這個(gè)LambdaMetafactory到底做了什么:

 */
  public static CallSite metafactory(MethodHandles.Lookup caller,
                    String invokedName,
                    MethodType invokedType,
                    MethodType samMethodType,
                    MethodHandle implMethod,
                    MethodType instantiatedMethodType)
      throws LambdaConversionException {
    AbstractValidatingLambdaMetafactory mf;
    mf = new InnerClassLambdaMetafactory(caller, invokedType,
                       invokedName, samMethodType,
                       implMethod, instantiatedMethodType,
                       false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY);
    mf.validateMetafactoryArgs();
    return mf.buildCallSite();
  }

它什么也沒做,做事的是InnerClassLambdaMetafactory.buildCallSite()創(chuàng)建的最后CallSite,那就進(jìn)一步看看InnerClassLambdaMetafactory.buildCallSite():

 @Override
  CallSite buildCallSite() throws LambdaConversionException {
    // 1. 創(chuàng)建生成的類對(duì)象
    final Class<?> innerClass = spinInnerClass();
    if (invokedType.parameterCount() == 0) {
      // 2. 用反射獲取構(gòu)造函數(shù)
      final Constructor<?>[] ctrs = AccessController.doPrivileged(
          new PrivilegedAction<Constructor<?>[]>() {
        @Override
        public Constructor<?>[] run() {
          Constructor<?>[] ctrs = innerClass.getDeclaredConstructors();
          if (ctrs.length == 1) {
            // The lambda implementing inner class constructor is private, set
            // it accessible (by us) before creating the constant sole instance
            ctrs[0].setAccessible(true);
          }
          return ctrs;
        }
          });
      if (ctrs.length != 1) {
        throw new LambdaConversionException("Expected one lambda constructor for "
            + innerClass.getCanonicalName() + ", got " + ctrs.length);
      }

      try {
        // 3. 創(chuàng)建實(shí)例 
        Object inst = ctrs[0].newInstance();
        // 4. 根據(jù)實(shí)例和samBase(接口類型)生成MethodHandle
        // 5. 生成ConstantCallSite
        return new ConstantCallSite(MethodHandles.constant(samBase, inst));
      }
      catch (ReflectiveOperationException e) {
        throw new LambdaConversionException("Exception instantiating lambda object", e);
      }
    } else {
      try {
        UNSAFE.ensureClassInitialized(innerClass);
        return new ConstantCallSite(
            MethodHandles.Lookup.IMPL_LOOKUP
               .findStatic(innerClass, NAME_FACTORY, invokedType));
      }
      catch (ReflectiveOperationException e) {
        throw new LambdaConversionException("Exception finding constructor", e);
      }
    }
  }

首先它生成一個(gè).class文件,虛擬機(jī)默認(rèn)不會(huì)輸出,需要下面設(shè)置VM option-Djdk.internal.lambda.dumpProxyClasses=.,Dump出虛擬機(jī)生成的類我得到的是:

import java.lang.invoke.LambdaForm.Hidden;
// $FF: synthetic class
final class MethodReference$$Lambda$1 implements Encode {
  private MethodReference$$Lambda$1() {
  }
  @Hidden
  public void encode(Derive var1) {
    ((Base)var1).encrypt();
  }
}

該類實(shí)現(xiàn)了傳來(lái)的接口函數(shù)(動(dòng)態(tài)類生成,熟悉spring的朋友應(yīng)該很熟悉)。

回到buildCallSite()源碼,它使用MethodHandles.constant(samBase, inst)創(chuàng)建MethdHandle,放到CallSite里面,完成整個(gè)LambdaMetafactory的工作。

MethodHandles.constant(samBase, inst)相當(dāng)于一個(gè)總是返回inst的方法。

總結(jié)

到這里就結(jié)束了整個(gè)流程,文章有點(diǎn)長(zhǎng),總結(jié)一下:

1.虛擬機(jī)遇到invokedynamic,開始解析操作數(shù)
2.根據(jù)invokedynamic #0:#27獲取到啟動(dòng)方法(#0)和一個(gè)名字+描述符(#27)

其中啟動(dòng)方法是

BootstrapMethods:
 0: #24 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/Method
Type;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  Method arguments:
   #25 (LDerive;)V
   #26 invokevirtual Base.encrypt:()V
   #25 (LDerive;)V

名字+描述符是

 #27 = NameAndType        #18:#37        // encode:()LEncode;

1.啟動(dòng)方法指向LambdaMetafactory.metafactory,但是不會(huì)直接調(diào)用而是通過MethdHandle間接調(diào)用。調(diào)用位置位于CallSite.makeCallSite()
2.LambdaMetafactory.metafactory()其實(shí)使用InnerClassLambdaMetafactory.buildCallSite()創(chuàng)建了最后的CallSite
3.buildCallSite()會(huì)創(chuàng)建一個(gè).class,
4.buildCallSite()會(huì)向最后的CallSite里面放入一個(gè)可調(diào)用的MethdHandle
5.這個(gè)MethodHandle指向的是一個(gè)總是返回剛剛創(chuàng)建的.class類的實(shí)例的方法,由MethodHandles.constant(samBase, inst)完成
6.最后,用invokevirtual調(diào)用CallSite里面的MethdHandle,返回.class類的示例,即inst,即new MethodReference$$Lambda$1

總結(jié)

以上所述是小編給大家介紹的Java中invokedynamic字節(jié)碼指令問題,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
如果你覺得本文對(duì)你有幫助,歡迎轉(zhuǎn)載,煩請(qǐng)注明出處,謝謝!

相關(guān)文章

  • Java8如何將Array轉(zhuǎn)換為Stream的實(shí)現(xiàn)代碼

    Java8如何將Array轉(zhuǎn)換為Stream的實(shí)現(xiàn)代碼

    這篇文章主要介紹了Java8如何將Array轉(zhuǎn)換為Stream的實(shí)現(xiàn)代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • Java如何獲取當(dāng)前時(shí)間的小時(shí)/分鐘(實(shí)現(xiàn)方法)

    Java如何獲取當(dāng)前時(shí)間的小時(shí)/分鐘(實(shí)現(xiàn)方法)

    文章介紹了Java獲取當(dāng)前時(shí)間的小時(shí)、分鐘等的方法,包括兩種常見方法,并鼓勵(lì)讀者繼續(xù)瀏覽相關(guān)文章,感興趣的朋友一起看看吧
    2025-02-02
  • 使用spring aop 統(tǒng)一捕獲異常和寫日志的示例demo

    使用spring aop 統(tǒng)一捕獲異常和寫日志的示例demo

    本文通過一個(gè)小demo給大家介紹spring AOP 實(shí)現(xiàn)的異常捕獲和日志的方法技巧,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2021-08-08
  • 深入理解Java基礎(chǔ)中的集合框架

    深入理解Java基礎(chǔ)中的集合框架

    Java集合框架(Java Collections Framework, JCF)也稱容器,這里可以類比 C++中的 STL,在這里主要對(duì)如下部分進(jìn)行源碼分析,及在面試中常見的問題,例如,在阿里面試常問到的 HashMap和ConcurrentHashMap原理等等,深入源碼分析是面試中必備的技能
    2023-08-08
  • Mac OS X 下 IntelliJ IDEA、jEdit 等 Java 程序中文標(biāo)點(diǎn)輸入無(wú)效的完美解決方法

    Mac OS X 下 IntelliJ IDEA、jEdit 等 Java 程序中文標(biāo)點(diǎn)輸入無(wú)效的完美解決方法

    Mac OS X 下基于 Java 的程序會(huì)出現(xiàn)中文標(biāo)點(diǎn)輸入無(wú)效的問題,在中文輸入法狀態(tài),可以輸入中文字,但輸入中文標(biāo)點(diǎn)最后上去的是英文標(biāo)點(diǎn).這篇文章主要介紹了Mac OS X 下 IntelliJ IDEA、jEdit 等 Java 程序中文標(biāo)點(diǎn)輸入無(wú)效的完美解決方法,需要的朋友可以參考下
    2016-10-10
  • springboot實(shí)現(xiàn)圖片上傳與下載功能

    springboot實(shí)現(xiàn)圖片上傳與下載功能

    這篇文章主要為大家詳細(xì)介紹了后端spring項(xiàng)目經(jīng)常要做的功能,實(shí)現(xiàn)圖片上傳和下載,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-12-12
  • JAVA后端實(shí)現(xiàn)JWT令牌的示例

    JAVA后端實(shí)現(xiàn)JWT令牌的示例

    本文主要介紹了JAVA后端實(shí)現(xiàn)JWT令牌的示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-12-12
  • Springboot之@ConfigurationProperties注解解讀

    Springboot之@ConfigurationProperties注解解讀

    在Spring Boot中,@EnableConfigurationProperties注解的主要作用是激活@ConfigurationProperties注解的配置屬性類,從而讓配置屬性類能被Spring容器管理,這樣的話,我們就可以在屬性類中輕松地使用@ConfigurationProperties來(lái)綁定配置文件中的屬性
    2024-10-10
  • Java?AQS中ReentrantLock條件鎖的使用

    Java?AQS中ReentrantLock條件鎖的使用

    ReentrantLock繼承了Lock接口,?lock方法實(shí)際上是調(diào)用了Sync的子類NonfairSync(非公平鎖)的lock方法。ReentrantLock的真正實(shí)現(xiàn)在他的兩個(gè)內(nèi)部類NonfairSync和FairSync中,默認(rèn)實(shí)現(xiàn)是非公平鎖
    2023-02-02
  • Spring Boot項(xiàng)目維護(hù)全局json數(shù)據(jù)代碼實(shí)例

    Spring Boot項(xiàng)目維護(hù)全局json數(shù)據(jù)代碼實(shí)例

    這篇文章主要介紹了Spring Boot項(xiàng)目維護(hù)全局json數(shù)據(jù)代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02

最新評(píng)論

连云港市| 碌曲县| 仪陇县| 南丰县| 正安县| 昌江| 密山市| 南通市| 来安县| 宝山区| 武义县| 锡林郭勒盟| 基隆市| 南溪县| 灌南县| 定边县| 长泰县| 山阴县| 体育| 金堂县| 南京市| 新竹市| 浠水县| 博客| 新乡市| 桓台县| 房产| 贵阳市| 平利县| 博野县| 苏州市| 南陵县| 张掖市| 翼城县| 肇州县| 吉首市| 碌曲县| 陇西县| 荣昌县| 云梦县| 康保县|