Resterende operatør på int forårsager java.util.Objects.requireNonNull?
Hvorfor ikke?
Forudsat
class C {
private final int taxos = 4;
public int test() {
final int a = 7;
final int b = this.taxos;
return a % b;
}
}
et opkald som c.test()
hvor c
er erklæret som C
skal kast når c
er null
. Din metode svarer til
public int test() {
return 3; // `7 % 4`
}
da du kun arbejder med konstanter. Med test
da den ikke er statisk, skal kontrollen udføres. Normalt ville det blive gjort implicit, når et felt bliver tilgået, eller en ikke-statisk metode bliver kaldt, men du gør det ikke. Så en eksplicit kontrol er nødvendig. En mulighed er at ringe til Objects.requireNonNull
.
Bytekoden
Glem ikke, at bytekoden dybest set er irrelevant for ydeevnen. Opgaven for javac
er at producere nogle bytekode, hvis udførelse svarer til din kildekode. Det er ikke meningen, at det skal gøre noget optimeringer, da optimeret kode normalt er længere og sværere at analysere, mens bytekoden faktisk er kildekoden til den optimerende JIT-kompiler. Så javac
forventes at holde det enkelt....
Ydeevnen
I min profiler så jeg, at der er 1 % CPU-forbrug i java.util.Objects.requireNonNull
Jeg ville give profileren skylden først. Profilering af Java er ret svært, og du kan aldrig forvente perfekte resultater.
Du skal nok prøve at gøre metoden statisk. Du bør helt sikkert læse denne artikel om nul-tjek.
Det ser ud til, at mit spørgsmål var 'forkert', da det ikke har noget at gøre med operatøren, men snarere selve feltet. Ved stadig ikke hvorfor..
public int test() {
final int a = 7;
final int b = this.taxos;
return a % b;
}
Hvilket bliver til:
public test()I
L0
LINENUMBER 51 L0
BIPUSH 7
ISTORE 1
L1
LINENUMBER 52 L1
ALOAD 0
INVOKESTATIC java/util/Objects.requireNonNull (Ljava/lang/Object;)Ljava/lang/Object;
POP
ICONST_4
ISTORE 2
L2
LINENUMBER 53 L2
BIPUSH 7
ILOAD 2
IREM
IRETURN
For det første er her et minimalt reproducerbart eksempel på denne adfærd:
/**
* OS: Windows 10 64x
* javac version: 13.0.1
*/
public class Test {
private final int bar = 5;
/**
* public int foo();
* Code:
* 0: iconst_5
* 1: ireturn
*/
public int foo() {
return bar;
}
/**
* public int foo2();
* Code:
* 0: aload_0
* 1: invokestatic #13 // Method java/util/Objects.requireNonNull:(Ljava/lang/Object;)Ljava/lang/Object;
* 4: pop
* 5: iconst_5
* 6: ireturn
*/
public int foo2() {
return this.bar;
}
}
Adfærden skyldes, hvordan Java-kompileren optimerer kompileringstidskonstanter .
Bemærk, at i bytekoden foo()
ingen objektreference er tilgået for at få værdien bar
. Det er fordi det er en kompileringstidskonstant, og JVM'en kan derfor simpelthen udføre iconst_5
handling for at returnere denne værdi.
Når du ændrer bar
ind i en ikke-kompilere tidskonstant (enten ved at fjerne final
søgeord eller ikke initialisering i erklæringen, men inde i konstruktøren), ville du få:
/**
* OS: Windows 10 64x
* javac version: 13.0.1
*/
public class Test2 {
private int bar = 5;
/**
* public int foo();
* Code:
* 0: aload_0
* 1: getfield #7
* 4: ireturn
*/
public int foo() {
return bar;
}
/**
* public int foo2();
* Code:
* 0: aload_0
* 1: getfield #7
* 4: ireturn
*/
public int foo2() {
return this.bar;
}
}
hvor aload_0
skubber referencen af this
på operand-stakken for derefter at få bar
felt for dette objekt.
Her er compileren klog nok til at bemærke det aload_0
(den this
reference i tilfælde af medlemsfunktioner) kan logisk set ikke være null
.
Er din sag nu faktisk en manglende compileroptimering?
Se @maaartinus svar.