जावा भाषा में बाइटकोड सुविधाएँ उपलब्ध नहीं हैं


146

क्या वर्तमान में (जावा 6) चीजें आप जावा बाइटकोड में कर सकते हैं जो आप जावा भाषा के भीतर से नहीं कर सकते हैं?

मुझे पता है कि दोनों ट्यूरिंग पूरी कर रहे हैं, इसलिए पढ़ें "कर सकते हैं" के रूप में "काफी तेज / बेहतर कर सकते हैं, या बस एक अलग तरीके से"।

मैं अतिरिक्त बायोटेक की तरह सोच रहा हूं invokedynamic, जो कि जावा का उपयोग करके उत्पन्न नहीं किया जा सकता है, सिवाय इसके कि भविष्य के संस्करण के लिए यह विशिष्ट है।


3
"चीजों" को परिभाषित करें। अंत में, जावा भाषा और जावा बाइटकोड दोनों ट्यूरिंग पूरा कर रहे हैं ...
माइकल बोर्गवर्ड

2
असली सवाल है; क्या जावा के बजाय जैस्मीन का उपयोग करके बाइट कोड में कोई लाभ प्रोग्रामिंग है?
पीटर लॉरी

2
rolअसेंबलर की तरह , जिसे आप C ++ में नहीं लिख सकते।
मार्टिअन कोर्टको जुले

1
यह एक बहुत ही खराब अनुकूलन करने वाला कंपाइलर है जो (x<<n)|(x>>(32-n))किसी rolनिर्देश का संकलन नहीं कर सकता है ।
रैंडम 832

जवाबों:


62

जहाँ तक मुझे पता है कि Java 6 द्वारा समर्थित bytecodes में कोई प्रमुख विशेषताएं नहीं हैं जो कि जावा स्रोत कोड से भी सुलभ नहीं हैं। इसका मुख्य कारण जाहिर है कि जावा बाइटकोड को जावा भाषा को ध्यान में रखकर डिजाइन किया गया था।

कुछ विशेषताएं हैं जो आधुनिक जावा कंपाइलरों द्वारा निर्मित नहीं हैं, हालांकि:

  • ACC_SUPERझंडा :

    यह एक ध्वज है जिसे एक वर्ग पर सेट किया जा सकता है और निर्दिष्ट करता है कि invokespecialइस वर्ग के लिए बाइटकोड के एक विशिष्ट कोने का मामला कैसे संभाला जाता है। यह सभी आधुनिक जावा कंपाइलर (जहां "आधुनिक" है = जावा 1.1, अगर मुझे सही से याद है) द्वारा सेट किया गया है और केवल प्राचीन जावा कंपाइलरों ने वर्ग फ़ाइलों का उत्पादन किया जहां यह अन-सेट था। यह ध्वज केवल पश्चगामी-संगतता कारणों के लिए मौजूद है। ध्यान दें कि जावा 7u51 के साथ शुरू, ACC_SUPER सुरक्षा कारणों से पूरी तरह से नजरअंदाज कर दिया गया है।

  • jsr/ retBytecodes।

    इन बाइटकोड का उपयोग उप-रूटीन (ज्यादातर finallyब्लॉकों को लागू करने के लिए ) को लागू करने के लिए किया गया था । वे अब जावा 6 के बाद से निर्मित नहीं हैं । उनके पदावनत करने का कारण यह है कि वे बिना किसी महान लाभ के लिए स्थैतिक सत्यापन को बहुत जटिल कर देते हैं (अर्थात जो कोड का उपयोग करता है वह लगभग हमेशा बहुत कम ओवरहेड के साथ सामान्य कूद के साथ फिर से लागू किया जा सकता है)।

  • एक कक्षा में दो विधियाँ होना जो केवल रिटर्न प्रकार में भिन्न होती हैं।

    जावा भाषा विनिर्देश एक ही कक्षा में दो तरीकों की अनुमति नहीं देता है जब वे केवल उनके रिटर्न प्रकार (यानी एक ही नाम, एक ही तर्क सूची, ...) में भिन्न होते हैं । JVM विनिर्देश हालांकि, ऐसा कोई प्रतिबंध नहीं है, इसलिए एक क्लास फ़ाइल में दो ऐसे तरीके हो सकते हैं, सामान्य जावा कंपाइलर का उपयोग करके ऐसी क्लास फ़ाइल बनाने का कोई तरीका नहीं है। इस उत्तर में एक अच्छा उदाहरण / स्पष्टीकरण है ।


5
मैं एक और उत्तर जोड़ सकता हूं, लेकिन हम आपका विहित उत्तर दे सकते हैं। आप यह उल्लेख करना चाह सकते हैं कि बायटेकोड में एक विधि के हस्ताक्षर में रिटर्न प्रकार शामिल है । यही है, आपके पास एक ही पैरामीटर प्रकारों के साथ दो तरीके हो सकते हैं, लेकिन विभिन्न रिटर्न प्रकार। इस चर्चा को देखें: stackoverflow.com/questions/3110014/is-this-valid-java/…
एडम पन्न्टर

8
आपके पास किसी भी वर्ण के साथ वर्ग, विधि और फ़ील्ड नाम हो सकते हैं। मैंने एक प्रोजेक्ट पर काम किया, जहां "फ़ील्ड्स" के नाम में रिक्त स्थान और हाइफ़न थे। : पी
पीटर लॉरी

3
@Peter: फ़ाइल सिस्टम वर्णों की बात करें, तो मैं एक ऑब्सफ़ेक्टर में भाग गया, जिसने एक वर्ग का नाम बदलकर aऔर दूसरा AJAR फ़ाइल के अंदर रखा था । इससे पहले कि मुझे पता चलता है कि लापता कक्षाएं थीं, मुझे विंडोज मशीन पर लगभग आधे घंटे का समय लगा। :)
एडम पेन्नर

3
@JoachimSauer: दूसरे शब्दों में बयान JVM कल्पना, पेज 75: वर्ग के नाम, विधियां, फ़ील्ड और स्थानीय चर शामिल कर सकते हैं किसी भी छोड़कर चरित्र '.', ';', '[', या '/'। विधि के नाम समान हैं, लेकिन वे भी शामिल नहीं कर सकते हैं '<'या '>'। (के उल्लेखनीय अपवाद के साथ <init>और <clinit>उदाहरण और स्थिर निर्माताओं के लिए।) मैं कहना चाहिए कि यदि आप विनिर्देश सख्ती से पालन कर रहे हैं, वर्ग के नाम वास्तव में और अधिक विवश हैं, लेकिन बाधाओं से लागू नहीं कर रहे हैं।
लेविथानबर्गर

3
@JoachimSauer: इसके अलावा, मेरा खुद का एक अविभाजित जोड़: जावा भाषा में "throws ex1, ex2, ..., exn"विधि हस्ताक्षरों के हिस्से के रूप में शामिल है ; आप ओवरराइड विधियों के लिए अपवाद फेंकने वाले अपवादों को नहीं जोड़ सकते। लेकिन, JVM कम देखभाल नहीं कर सकता। तो केवल finalतरीकों को वास्तव में जेवीएम द्वारा अपवाद-मुक्त होने की गारंटी दी जाती है - निश्चित रूप से RuntimeExceptionएस और Errorएस से अलग । जाँच किए गए अपवाद से निपटने के लिए बहुत कुछ: D
leviathanbadger

401

काफी समय से जावा बाइट कोड के साथ काम करने और इस मामले पर कुछ अतिरिक्त शोध करने के बाद, यहाँ मेरे निष्कर्षों का सारांश है:

सुपर कंस्ट्रक्टर या सहायक कंस्ट्रक्टर को कॉल करने से पहले एक कंस्ट्रक्टर में कोड निष्पादित करें

जावा प्रोग्रामिंग लैंग्वेज (JPL) में, एक कंस्ट्रक्टर का पहला स्टेटमेंट एक सुपर कंस्ट्रक्टर या उसी क्लास के दूसरे कंस्ट्रक्टर का इनवोकेशन होना चाहिए। यह जावा बाइट कोड (JBC) के लिए सही नहीं है। बाइट कोड के भीतर, किसी भी कोड को कंस्ट्रक्टर से पहले निष्पादित करना बिल्कुल वैध है, जब तक कि:

  • इस कोड ब्लॉक के कुछ समय बाद एक और संगत कंस्ट्रक्टर को बुलाया जाता है।
  • यह कॉल सशर्त विवरण के भीतर नहीं है।
  • इस निर्माता के कॉल से पहले, निर्मित इंस्टेंस का कोई भी क्षेत्र पढ़ा नहीं जाता है और इसके किसी भी तरीके को लागू नहीं किया जाता है। यह अगले आइटम का तात्पर्य है।

सुपर कंस्ट्रक्टर या सहायक कंस्ट्रक्टर को कॉल करने से पहले उदाहरण फ़ील्ड सेट करें

जैसा कि पहले उल्लेख किया गया है, किसी अन्य कंस्ट्रक्टर को कॉल करने से पहले एक इंस्टेंस का फ़ील्ड मान सेट करना पूरी तरह से कानूनी है। यहां तक ​​कि एक विरासत हैक भी मौजूद है, जो 6 से पहले जावा संस्करणों में इस "सुविधा" का फायदा उठाने में सक्षम बनाता है:

class Foo {
  public String s;
  public Foo() {
    System.out.println(s);
  }
}

class Bar extends Foo {
  public Bar() {
    this(s = "Hello World!");
  }
  private Bar(String helper) {
    super();
  }
}

इस तरह, सुपर कंस्ट्रक्टर को लागू करने से पहले एक क्षेत्र निर्धारित किया जा सकता है जो अब संभव नहीं है। जेबीसी में, यह व्यवहार अभी भी लागू किया जा सकता है।

सुपर कंस्ट्रक्टर कॉल को ब्रांच करें

जावा में, एक कंस्ट्रक्टर कॉल को परिभाषित करना संभव नहीं है

class Foo {
  Foo() { }
  Foo(Void v) { }
}

class Bar() {
  if(System.currentTimeMillis() % 2 == 0) {
    super();
  } else {
    super(null);
  }
}

जावा 7u23 तक, हॉटस्पॉट वीएम के सत्यापनकर्ता ने इस जांच को याद नहीं किया जो कि यह संभव था। इसे कई कोड जनरेशन टूल्स ने हैक की तरह इस्तेमाल किया था लेकिन इस तरह की क्लास को लागू करना अब कानूनी नहीं है।

उत्तरार्द्ध इस संकलक संस्करण में केवल एक बग था। नए संकलक संस्करणों में, यह फिर से संभव है।

किसी भी निर्माता के बिना एक वर्ग को परिभाषित करें

जावा कंपाइलर हमेशा किसी भी वर्ग के लिए कम से कम एक कंस्ट्रक्टर को लागू करेगा। जावा बाइट कोड में, यह आवश्यक नहीं है। यह उन कक्षाओं के निर्माण की अनुमति देता है जिनका निर्माण प्रतिबिंब का उपयोग करते समय भी नहीं किया जा सकता है। हालाँकि, sun.misc.Unsafeअभी भी इस तरह के उदाहरणों के निर्माण की अनुमति है।

समान हस्ताक्षर के साथ तरीकों को परिभाषित करें लेकिन विभिन्न प्रकार के रिटर्न के साथ

JPL में, एक विधि की पहचान उसके नाम और उसके कच्चे पैरामीटर प्रकारों के रूप में की जाती है। जेबीसी में, कच्चे रिटर्न प्रकार को अतिरिक्त रूप से माना जाता है।

उन क्षेत्रों को परिभाषित करें जो केवल नाम से नहीं बल्कि केवल प्रकार से भिन्न होते हैं

एक वर्ग फ़ाइल में एक ही नाम के कई फ़ील्ड शामिल हो सकते हैं जब तक कि वे एक अलग फ़ील्ड प्रकार की घोषणा नहीं करते हैं। जेवीएम हमेशा एक क्षेत्र को नाम और प्रकार के टपल के रूप में संदर्भित करता है।

उन्हें पकड़ने के बिना अघोषित जाँच अपवाद फेंको

जावा रनटाइम और जावा बाइट कोड चेक किए गए अपवादों की अवधारणा से अवगत नहीं हैं। यह केवल जावा कंपाइलर है जो सत्यापित करता है कि चेक किए गए अपवाद हमेशा पकड़े जाते हैं या घोषित किए जाते हैं यदि उन्हें फेंक दिया जाता है।

लैम्ब्डा एक्सप्रेशन के बाहर डायनामिक मेथड इनवोकेशन का उपयोग करें

तथाकथित गतिशील विधि मंगलाचरण का उपयोग किसी भी चीज के लिए किया जा सकता है, न केवल जावा के लैम्ब्डा अभिव्यक्तियों के लिए। इस सुविधा का उपयोग उदाहरण के लिए रनटाइम पर निष्पादन तर्क को स्विच करने की अनुमति देता है। कई गतिशील प्रोग्रामिंग भाषाएं जो जेबीसी के लिए उबालती हैं उन्होंने इस निर्देश का उपयोग करके अपने प्रदर्शन में सुधार किया। जावा बाइट कोड में, आप जावा 7 में लैम्ब्डा एक्सप्रेशंस का अनुकरण भी कर सकते हैं, जहां कंपाइलर ने अभी तक डायनेमिक विधि के किसी भी उपयोग के लिए अनुमति नहीं दी थी, जबकि जेवीएम ने निर्देश को समझा था।

ऐसे पहचानकर्ताओं का उपयोग करें जिन्हें आमतौर पर कानूनी नहीं माना जाता है

कभी रिक्त स्थान और अपने तरीके के नाम में एक लाइन ब्रेक का उपयोग कर? कोड समीक्षा के लिए अपनी खुद की JBC और शुभकामनाएँ बनाएँ। पहचानकर्ता के लिए केवल अवैध वर्ण मौजूद हैं ., ;, [और /। साथ ही, तरीकों नामित कि नहीं कर रहे हैं <init>या <clinit>नहीं हो सकते <और >

पुन: असाइन किए गए finalपैरामीटर या thisसंदर्भ

finalजेबीसी में पैरामीटर मौजूद नहीं हैं और फलस्वरूप इसे फिर से असाइन किया जा सकता है। thisसंदर्भ सहित किसी भी पैरामीटर को केवल जेवीएम के भीतर एक सरल सरणी में संग्रहीत किया जाता है जो एक एकल विधि फ्रेम के भीतर thisसूचकांक में संदर्भ को फिर से असाइन करने की अनुमति देता है 0

पुन: असाइन किए गए finalफ़ील्ड

जब तक एक निर्माता के भीतर एक अंतिम क्षेत्र सौंपा जाता है, तब तक इस मूल्य को फिर से असाइन करना कानूनी है या यहां तक ​​कि एक मूल्य भी निर्दिष्ट नहीं करना है। इसलिए, निम्नलिखित दो निर्माता कानूनी हैं:

class Foo {
  final int bar;
  Foo() { } // bar == 0
  Foo(Void v) { // bar == 2
    bar = 1;
    bar = 2;
  }
}

के लिए static finalखेतों, तो यह और भी वर्ग प्रारंभकर्ता के बाहर खेतों पुन: असाइन करने की अनुमति है।

कंस्ट्रक्टर और क्लास इनिशियलाइज़र के साथ ऐसा व्यवहार करें जैसे कि वे विधियाँ हों

यह एक वैचारिक विशेषता है, लेकिन कंस्ट्रक्टरों को सामान्य तरीकों की तुलना में जेबीसी के भीतर किसी भी तरह का व्यवहार नहीं किया जाता है। यह केवल जेवीएम का सत्यापनकर्ता है जो विश्वास दिलाता है कि निर्माता दूसरे कानूनी निर्माता को बुलाते हैं। इसके अलावा, यह केवल एक जावा नामकरण सम्मेलन है जिसे कंस्ट्रक्टरों को बुलाया जाना चाहिए <init>और क्लास इनिशियलाइज़र कहा जाता है <clinit>। इस अंतर के अलावा, तरीकों और निर्माणकर्ताओं का प्रतिनिधित्व समान है। जैसा कि होल्गर ने एक टिप्पणी में कहा, आप कंस्ट्रक्टरों को अन्य प्रकार के रिटर्न के साथ voidया क्लास इनिशियलाइज़र के साथ तर्कों के साथ परिभाषित कर सकते हैं , भले ही इन तरीकों को कॉल करना संभव न हो।

असममित रिकॉर्ड बनाएं *

रिकॉर्ड बनाते समय

record Foo(Object bar) { }

javac एक एकल नाम के साथ एक वर्ग फ़ाइल bar, एक अभिगम विधि नाम bar()और एक एकल लेने वाला एक निर्माणकर्ता उत्पन्न करेगा Object। इसके अतिरिक्त, एक रिकॉर्ड विशेषता barजोड़ी जाती है। मैन्युअल रूप से एक रिकॉर्ड बनाने के द्वारा, एक अलग कंस्ट्रक्टर आकार बनाना, क्षेत्र को छोड़ना और एक्सेसर को अलग तरीके से लागू करना संभव है। इसी समय, प्रतिबिंब एपीआई को यह विश्वास करना अभी भी संभव है कि वर्ग वास्तविक रिकॉर्ड का प्रतिनिधित्व करता है।

किसी भी सुपर विधि को कॉल करें (जावा 1.1 तक)

हालाँकि, यह केवल जावा संस्करण 1 और 1.1 के लिए ही संभव है। जेबीसी में, विधियों को हमेशा स्पष्ट लक्ष्य प्रकार पर भेजा जाता है। इसका मतलब है कि के लिए

class Foo {
  void baz() { System.out.println("Foo"); }
}

class Bar extends Foo {
  @Override
  void baz() { System.out.println("Bar"); }
}

class Qux extends Bar {
  @Override
  void baz() { System.out.println("Qux"); }
}

ऊपर कूदते समय Qux#bazआह्वान करना संभव था । हालांकि प्रत्यक्ष सुपर क्लास की तुलना में एक अन्य सुपर मेथड इम्प्लीमेंटेशन को कॉल करने के लिए एक स्पष्ट आह्वान को परिभाषित करना अभी भी संभव है, यह 1.1 के बाद जावा संस्करणों में कोई प्रभाव नहीं डालता है। जावा 1.1 में, इस व्यवहार को ध्वज को सेट करके नियंत्रित किया गया था जो उसी व्यवहार को सक्षम करेगा जो केवल प्रत्यक्ष सुपर क्लास के कार्यान्वयन को कहता है।Foo#bazBar#bazACC_SUPER

उसी वर्ग में घोषित की गई विधि की एक गैर-आभासी कॉल को परिभाषित करें

जावा में, एक वर्ग को परिभाषित करना संभव नहीं है

class Foo {
  void foo() {
    bar();
  }
  void bar() { }
}

class Bar extends Foo {
  @Override void bar() {
    throw new RuntimeException();
  }
}

उपरोक्त कोड हमेशा एक परिणाम में होता है RuntimeExceptionजब fooइसका उदाहरण दिया जाता है Bar। यह परिभाषित करने के लिए संभव नहीं है Foo::fooको लागू करने की विधि का अपना bar तरीका है जिसमें परिभाषित किया गया है Foo। जैसा barकि एक गैर-निजी उदाहरण विधि है, कॉल हमेशा आभासी होता है। बाइट कोड के साथ, कोई भी ओपकोड का उपयोग करने के लिए मंगलाचरण को परिभाषित कर सकता है INVOKESPECIALजो सीधे barतरीके से कॉल Foo::fooको Foo's' वर्जन से जोड़ता है । यह ओपकोड आमतौर पर सुपर मेथड इनवोकेशन को लागू करने के लिए उपयोग किया जाता है लेकिन आप वर्णित व्यवहार को लागू करने के लिए ओपकोड का पुन: उपयोग कर सकते हैं।

बारीक-दाने के प्रकार एनोटेशन

जावा में, एनोटेशन को उनके अनुसार लागू किया जाता @Targetहै जो एनोटेशन घोषित करता है। बाइट कोड हेरफेर का उपयोग करना, इस नियंत्रण से स्वतंत्र रूप से एनोटेशन को परिभाषित करना संभव है। इसके अलावा, उदाहरण के लिए यह संभव है कि पैरामीटर को एनोटेट किए बिना एक पैरामीटर प्रकार का एनोटेट किया जाए, भले ही @Targetएनोटेशन दोनों तत्वों पर लागू हो।

एक प्रकार या उसके सदस्यों के लिए किसी भी विशेषता को परिभाषित करें

जावा भाषा के भीतर, केवल फ़ील्ड, विधियों या कक्षाओं के लिए एनोटेशन को परिभाषित करना संभव है। जेबीसी में, आप मूल रूप से जावा कक्षाओं में किसी भी जानकारी को एम्बेड कर सकते हैं। इस जानकारी का उपयोग करने के लिए, आप अब जावा क्लास लोडिंग तंत्र पर निर्भर नहीं रह सकते हैं, लेकिन आपको अपने द्वारा मेटा जानकारी निकालने की आवश्यकता है।

ओवरफ्लो और परोक्ष असाइन byte, short, charऔर booleanमूल्यों

बाद के आदिम प्रकार सामान्य रूप से जेबीसी में नहीं जाने जाते हैं, लेकिन केवल सरणी प्रकारों के लिए या क्षेत्र और विधि वर्णनकर्ताओं के लिए परिभाषित किए जाते हैं। बाइट कोड निर्देशों के भीतर, सभी प्रकार के नाम 32 बिट स्थान लेते हैं जो उन्हें प्रतिनिधित्व करने की अनुमति देता है int। आधिकारिक तौर पर, केवल int, float, longऔर doubleप्रकार बाइट कोड के भीतर मौजूद है जो सभी की जरूरत JVM के सत्यापनकर्ता के शासन के द्वारा स्पष्ट रूपांतरण।

मॉनिटर जारी नहीं

एक synchronizedब्लॉक वास्तव में दो बयानों से बना होता है, एक अधिग्रहित करने के लिए और एक मॉनिटर जारी करने के लिए। जेबीसी में, आप इसे जारी किए बिना एक प्राप्त कर सकते हैं।

नोट : हॉटस्पॉट के हालिया कार्यान्वयन में, इसके बजाय IllegalMonitorStateExceptionएक विधि के अंत में या एक अंतर्निहित रिलीज के लिए जाता है अगर विधि एक अपवाद द्वारा ही समाप्त हो जाती है।

returnएक टाइप इनिशियलाइज़र में एक से अधिक कथन जोड़ें

जावा में, यहां तक ​​कि एक तुच्छ प्रकार का इनिशियलाइज़र भी

class Foo {
  static {
    return;
  }
}

गैरकानूनी है। बाइट कोड में, टाइप इनिशियलाइज़र को किसी अन्य विधि के रूप में माना जाता है, अर्थात रिटर्न स्टेटमेंट को कहीं भी परिभाषित किया जा सकता है।

इर्रिडिएबल लूप बनाएं

जावा संकलक जावा बाइट कोड में गोटो बयानों के लिए छोरों को परिवर्तित करता है। इस तरह के बयानों का उपयोग इर्रिसेबल लूप बनाने के लिए किया जा सकता है, जो जावा कंपाइलर कभी नहीं करता है।

एक पुनरावर्ती कैच ब्लॉक को परिभाषित करें

जावा बाइट कोड में, आप एक ब्लॉक को परिभाषित कर सकते हैं:

try {
  throw new Exception();
} catch (Exception e) {
  <goto on exception>
  throw Exception();
}

synchronizedजावा में एक ब्लॉक का उपयोग करते समय एक समान बयान को स्पष्ट रूप से बनाया जाता है जहां मॉनिटर जारी करते समय कोई भी अपवाद इस मॉनिटर को जारी करने के निर्देश पर वापस लौटता है। आम तौर पर, इस तरह के निर्देश पर कोई अपवाद नहीं होना चाहिए, लेकिन अगर यह (जैसे पदावनत ThreadDeath) होगा, तो भी मॉनिटर जारी किया जाएगा।

किसी भी डिफ़ॉल्ट विधि को कॉल करें

जावा कंपाइलर को डिफ़ॉल्ट विधि के आह्वान की अनुमति देने के लिए कई शर्तों को पूरा करने की आवश्यकता होती है:

  1. यह विधि सबसे विशिष्ट होनी चाहिए (एक उप इंटरफ़ेस द्वारा अतिरंजित नहीं होनी चाहिए जो किसी भी प्रकार से लागू होती है , जिसमें सुपर प्रकार भी शामिल है)।
  2. डिफ़ॉल्ट विधि का इंटरफ़ेस प्रकार सीधे उस वर्ग द्वारा लागू किया जाना चाहिए जो डिफ़ॉल्ट विधि कह रहा है। हालाँकि, यदि इंटरफ़ेस इंटरफ़ेस का Bविस्तार करता है, Aलेकिन इसमें एक विधि को ओवरराइड नहीं करता है A, तो विधि अभी भी लागू की जा सकती है।

जावा बाइट कोड के लिए, केवल दूसरी स्थिति मायने रखती है। पहले वाला हालांकि अप्रासंगिक है।

एक उदाहरण पर एक सुपर विधि लागू करें जो नहीं है this

जावा संकलक केवल उदाहरणों पर एक सुपर (या इंटरफ़ेस डिफ़ॉल्ट) विधि को लागू करने की अनुमति देता है this। बाइट कोड में, हालांकि निम्न के समान एक ही प्रकार की आवृत्ति पर सुपर विधि को लागू करना भी संभव है:

class Foo {
  void m(Foo f) {
    f.super.toString(); // calls Object::toString
  }
  public String toString() {
    return "foo";
  }
}

सिंथेटिक सदस्यों तक पहुंचें

जावा बाइट कोड में, सिंथेटिक सदस्यों को सीधे एक्सेस करना संभव है। उदाहरण के लिए, निम्न उदाहरण पर विचार करें कि किसी अन्य उदाहरण के बाहरी उदाहरण Barतक कैसे पहुँचा जाता है:

class Foo {
  class Bar { 
    void bar(Bar bar) {
      Foo foo = bar.Foo.this;
    }
  }
}

यह किसी भी सिंथेटिक क्षेत्र, वर्ग या विधि के लिए आम तौर पर सच है।

आउट-की-सिंक जेनेरिक प्रकार की जानकारी को परिभाषित करें

जबकि जावा रनटाइम जेनेरिक प्रकारों की प्रक्रिया नहीं करता है (जावा कंपाइलर टाइप इरेज़र लागू होने के बाद), यह जानकारी अभी भी संकलित वर्ग के लिए मेटा जानकारी के रूप में संलग्न है और प्रतिबिंब एपीआई के माध्यम से सुलभ है।

सत्यापनकर्ता मेटा डेटा- Stringइनकोड किए गए मानों की निरंतरता की जांच नहीं करता है । इसलिए जेनेरिक प्रकारों के बारे में जानकारी को परिभाषित करना संभव है जो इरेज़र से मेल नहीं खाता है। एक अवधारणा के रूप में, निम्नलिखित दावे सच हो सकते हैं:

Method method = ...
assertTrue(method.getParameterTypes() != method.getGenericParameterTypes());

Field field = ...
assertTrue(field.getFieldType() == String.class);
assertTrue(field.getGenericFieldType() == Integer.class);

इसके अलावा, हस्ताक्षर को अमान्य के रूप में परिभाषित किया जा सकता है जैसे कि एक रनटाइम अपवाद को फेंक दिया जाता है। यह अपवाद तब फेंका जाता है जब जानकारी का पहली बार एक्सेस किया जाता है क्योंकि इसका मूल्यांकन आलसी तरीके से किया जाता है। (त्रुटि के साथ एनोटेशन मान के समान।)

केवल कुछ विधियों के लिए पैरामीटर मेटा जानकारी संलग्न करें

जावा कंपाइलर, parameterसक्षम ध्वज के साथ एक वर्ग को संकलित करते समय पैरामीटर नाम और संशोधक जानकारी एम्बेड करने की अनुमति देता है । जावा वर्ग फ़ाइल प्रारूप में, यह जानकारी हालांकि प्रति-विधि संग्रहीत की जाती है जो केवल कुछ विधियों के लिए ऐसी विधि जानकारी को एम्बेड करना संभव बनाती है।

मेस बातें और अपने जेवीएम को हार्ड-क्रैश करें

एक उदाहरण के रूप में, जावा बाइट कोड में, आप किसी भी प्रकार पर किसी भी विधि को लागू करने के लिए परिभाषित कर सकते हैं। आमतौर पर, सत्यापनकर्ता शिकायत करेगा यदि एक प्रकार ऐसी विधि के बारे में नहीं जानता है। हालाँकि, यदि आप किसी सरणी पर एक अज्ञात विधि का उपयोग करते हैं, तो मुझे कुछ JVM संस्करण में एक बग मिला, जहाँ सत्यापनकर्ता को यह याद होगा और निर्देश के लागू होने के बाद आपका JVM समाप्त हो जाएगा। यह शायद ही एक विशेषता यह है, लेकिन यह तकनीकी रूप से कुछ है कि के साथ संभव नहीं है javac संकलित जावा। जावा में किसी प्रकार का दोहरा सत्यापन है। पहली मान्यता जावा कंपाइलर द्वारा लागू की जाती है, दूसरी जेवीएम द्वारा जब एक कक्षा भरी जाती है। संकलक को छोड़ देने से, आपको सत्यापनकर्ता के सत्यापन में एक कमजोर स्थान मिल सकता है। यह एक सुविधा की बजाय एक सामान्य कथन है, हालाँकि।

बाहरी वर्ग न होने पर एक कंस्ट्रक्टर के रिसीवर के प्रकार को नोट करें

जावा 8 के बाद से, आंतरिक वर्गों के गैर-स्थिर तरीके और निर्माता एक रिसीवर प्रकार की घोषणा कर सकते हैं और इन प्रकारों को एनोटेट कर सकते हैं। शीर्ष-स्तरीय कक्षाओं के निर्माता अपने रिसीवर के प्रकार को एनोटेट नहीं कर सकते हैं क्योंकि वे सबसे अधिक एक घोषित नहीं करते हैं।

class Foo {
  class Bar {
    Bar(@TypeAnnotation Foo Foo.this) { }
  }
  Foo() { } // Must not declare a receiver type
}

चूँकि , प्रतिनिधित्व को Foo.class.getDeclaredConstructor().getAnnotatedReceiverType()वापस AnnotatedTypeकरता है Foo, इसलिए Fooक्लास फाइल में सीधे टाइपर्स एनोटेशन को शामिल करना संभव है जहाँ ये एनोटेशन बाद में प्रतिबिंब एपीआई द्वारा पढ़े जाते हैं।

अप्रयुक्त / विरासत बाइट कोड निर्देशों का उपयोग करें

चूंकि अन्य लोगों ने इसका नाम दिया है, मैं इसे भी शामिल करूंगा। जावा पूर्व में JSRऔर RETकथनों द्वारा सबरूटीन्स का उपयोग कर रहा था । JBC को इस उद्देश्य के लिए अपने स्वयं के वापसी पता भी पता था। हालाँकि, सबरूटीन्स के उपयोग ने स्थैतिक कोड विश्लेषण को ओवरप्ले किया था यही कारण है कि इन निर्देशों का उपयोग नहीं किया जाता है। इसके बजाय, जावा कंपाइलर कोड को डुप्लिकेट करेगा। हालांकि, यह मूल रूप से समान तर्क बनाता है यही कारण है कि मैं वास्तव में इसे कुछ अलग हासिल करने के लिए नहीं मानता हूं। इसी तरह, आप उदाहरण के लिए जोड़ सकते हैंNOOPबाइट कोड निर्देश जो कि जावा कंपाइलर द्वारा उपयोग नहीं किया जाता है लेकिन यह वास्तव में आपको कुछ नया हासिल करने की अनुमति नहीं देगा। जैसा कि संदर्भ में बताया गया है, इन उल्लिखित "फ़ीचर निर्देश" को अब कानूनी ऑपकोड के सेट से हटा दिया जाता है जो उन्हें एक फ़ीचर से कम भी प्रदान करता है।


3
विधि के नामों के बारे में, आपके पास <clinit>नाम के साथ तरीकों को परिभाषित करके, <clinit>लेकिन मापदंडों को स्वीकार करने या गैर- voidवापसी प्रकार होने से एक से अधिक विधि हो सकती है । लेकिन ये तरीके बहुत उपयोगी नहीं हैं, JVM उन्हें अनदेखा करेगा और बाइट कोड उन्हें लागू नहीं कर सकता है। पाठकों को भ्रमित करने के लिए एकमात्र उपयोग होगा।
होल्गर

2
मुझे अभी पता चला है, कि Oracle का JVM पद्धति से बाहर निकलने पर अप्रतिबंधित मॉनिटर का पता लगाता है और IllegalMonitorStateExceptionयदि आपने monitorexitनिर्देश को छोड़ दिया है तो वह फेंकता है । और एक असाधारण तरीके से बाहर निकलने की स्थिति में जो ए करने में विफल रहा monitorexit, यह चुपचाप मॉनिटर को रीसेट करता है।
होल्गर

1
@ होल्गर - यह नहीं जानता था कि, मुझे पता है कि कम से कम पहले के जेवीएम में यह संभव था, इस तरह के कार्यान्वयन के लिए JRockit का अपना हैंडलर भी है। मैं एंट्री को अपडेट करूंगा।
राफेल विंटरथेलर

1
खैर, जेवीएम विनिर्देश इस तरह के व्यवहार को अनिवार्य नहीं करता है। मैंने इसे सिर्फ इसलिए खोजा क्योंकि मैंने ऐसे गैर-मानक बाइट कोड का उपयोग करके एक झूलने वाले आंतरिक ताला बनाने की कोशिश की।
होल्गर

3
ठीक है, मुझे प्रासंगिक कल्पना मिली : “ संरचित लॉकिंग वह स्थिति है, जब किसी विधि मंगलाचरण के दौरान, दिए गए मॉनिटर पर प्रत्येक निकास उस मॉनिटर पर पूर्ववर्ती प्रविष्टि से मेल खाता है। चूँकि इस बात का कोई आश्वासन नहीं है कि जावा वर्चुअल मशीन के लिए प्रस्तुत सभी कोड संरचित लॉकिंग करेंगे, जावा वर्चुअल मशीन के कार्यान्वयन की अनुमति है लेकिन संरचित लॉकिंग की गारंटी देने वाले निम्नलिखित दो नियमों को लागू करने की आवश्यकता नहीं है। … ”
होल्गर

14

यहाँ कुछ विशेषताएं हैं जो जावा बाइटकोड में की जा सकती हैं लेकिन जावा स्रोत कोड में नहीं:

  • एक विधि से एक चेक किए गए अपवाद को फेंकने की घोषणा किए बिना कि विधि इसे फेंकता है। जाँच की गई और अनियंत्रित अपवाद एक ऐसी चीज़ है जो केवल जावा कंपाइलर द्वारा जाँची जाती है, जेवीएम से नहीं। इस कारण उदाहरण के लिए स्काला बिना घोषित किए तरीकों से जाँच किए गए अपवादों को फेंक सकता है। हालांकि जावा जेनरिक के साथ स्नीकी थ्रो नामक एक वर्कअराउंड है ।

  • एक कक्षा में दो विधियाँ जो केवल रिटर्न प्रकार में भिन्न होती हैं, जैसा कि पहले से ही जोआचिम के उत्तर में उल्लिखित है : जावा भाषा विनिर्देश एक ही कक्षा में दो विधियों की अनुमति नहीं देता है जब वे केवल उनके वापसी प्रकार (यानी एक ही नाम, एक ही तर्क सूची) में भिन्न होते हैं , ...)। JVM विनिर्देश हालांकि, ऐसा कोई प्रतिबंध नहीं है, इसलिए एक क्लास फ़ाइल में दो ऐसे तरीके हो सकते हैं, सामान्य जावा कंपाइलर का उपयोग करके ऐसी क्लास फ़ाइल बनाने का कोई तरीका नहीं है। इस उत्तर में एक अच्छा उदाहरण / स्पष्टीकरण है ।


4
ध्यान दें कि जावा में पहला काम करने का एक तरीका है। इसे कभी-कभी एक डरपोक कहा जाता है ।
जोकिम सॉयर

अब यह डरपोक है! : डी साझा करने के लिए धन्यवाद।
एस्को लुओंटोला

मुझे लगता है कि आप Thread.stop(Throwable)एक डरपोक फेंक के लिए भी उपयोग कर सकते हैं । मुझे लगता है कि पहले से ही जुड़ा हुआ है हालांकि तेज है।
बार्ट वैन ह्युकेलम

2
आप जावा बाइटकोड में एक निर्माता को बुलाए बिना एक उदाहरण नहीं बना सकते। सत्यापनकर्ता किसी भी कोड को अस्वीकार कर देगा जो एक असंवैधानिक उदाहरण का उपयोग करने की कोशिश करता है। ऑब्जेक्ट डिसेरिएलाइज़ेशन कार्यान्वयन, बिना कंस्ट्रक्टर कॉलिंग के उदाहरण बनाने के लिए मूल कोड हेल्पर्स का उपयोग करता है।
होल्गर

एक क्लास फू फैली हुई वस्तु के लिए, आप एक कन्स्ट्रक्टर जिसे ऑब्जेक्ट में घोषित किया जाता है, कॉल करके फू को तुरंत नहीं कर सकते। सत्यापनकर्ता इसे मना कर देगा। आप जावा के परावर्तन क्षेत्र का उपयोग करके इस तरह का एक निर्माणकर्ता बना सकते हैं, लेकिन यह शायद ही बाइट कोड की विशेषता है लेकिन जानी द्वारा महसूस की गई है। आपका उत्तर गलत है और होल्गर सही है।
राफेल विंटरथेलर

8
  • GOTOअपने स्वयं के नियंत्रण संरचनाओं ( for whileआदि के अलावा) बनाने के लिए लेबल के साथ इस्तेमाल किया जा सकता है
  • आप thisकिसी विधि के अंदर स्थानीय चर को ओवरराइड कर सकते हैं
  • इन दोनों को मिला कर आप टेल कॉल को अनुकूलित बायटेकोड बना सकते हैं (मैं JCompilo में ऐसा करता हूं )

एक संबंधित बिंदु के रूप में आप विधियों के लिए पैरामीटर नाम प्राप्त कर सकते हैं यदि डिबग के साथ संकलित किया गया है ( Paranamer यह bytecode पढ़कर करता है


आप overrideइस स्थानीय चर को कैसे करते हैं ?
माइकल

2
@ मिचेल ओवरराइडिंग एक बहुत मजबूत शब्द है। बाइटकोड के स्तर पर, सभी स्थानीय चर एक संख्यात्मक सूचकांक द्वारा एक्सेस किए जाते हैं और मौजूदा चर के लिए लिखने या एक नए चर को शुरू करने के बीच कोई अंतर नहीं है (एक अव्यवस्थित गुंजाइश के साथ), किसी भी मामले में, यह सिर्फ एक स्थानीय चर के लिए एक लेखन है। thisचर सूचकांक शून्य है, लेकिन साथ होने के अलावा पूर्व प्रारंभ thisजब एक उदाहरण विधि में प्रवेश संदर्भ, यह सिर्फ एक स्थानीय चर है। इसलिए आप इसके लिए एक अलग मान लिख सकते हैं, जो this'स्कोप' को खत्म करने या thisइसके इस्तेमाल के आधार पर वैरिएबल को बदलने जैसे कार्य कर सकता है।
होल्गर

समझा! तो क्या वास्तव में ऐसा thisहो सकता है? मुझे लगता है कि यह सिर्फ ओवरराइड शब्द था जो मुझे आश्चर्यचकित कर रहा था कि इसका वास्तव में क्या मतलब है।
माइकल

5

हो सकता है कि इस दस्तावेज़ में धारा 7 ए रूचि की हो, हालाँकि यह बाइटकोड विशेषताओं के बजाय बायटेकोड के नुकसान के बारे में है


दिलचस्प पढ़ा, लेकिन ऐसा नहीं लगता कि कोई भी उन चीजों का उपयोग करना चाहता है।
बार्ट वैन ह्युकेलम

4

जावा भाषा में एक कंस्ट्रक्टर में पहला स्टेटमेंट सुपर क्लास कंस्ट्रक्टर के लिए कॉल होना चाहिए। बाइटकोड के पास यह सीमा नहीं है, इसके बजाय नियम यह है कि सदस्यों को एक्सेस करने से पहले सुपर क्लास कंस्ट्रक्टर या उसी क्लास में एक अन्य कंस्ट्रक्टर को ऑब्जेक्ट के लिए बुलाया जाना चाहिए। इसे और अधिक स्वतंत्रता की अनुमति देनी चाहिए जैसे:

  • किसी अन्य ऑब्जेक्ट का एक उदाहरण बनाएं, इसे एक स्थानीय चर (या स्टैक) में संग्रहीत करें और इसे सुपर क्लास कंस्ट्रक्टर के पैरामीटर के रूप में पास करें जबकि अन्य उपयोग के लिए उस चर में संदर्भ रखते हुए।
  • एक शर्त के आधार पर विभिन्न अन्य कंस्ट्रक्टरों को कॉल करें। यह संभव होना चाहिए: जावा में सशर्त रूप से एक अलग निर्माता को कैसे कॉल किया जाए?

मैंने इनका परीक्षण नहीं किया है, इसलिए यदि मैं गलत हूं तो कृपया मुझे सुधार दें।


आप अपने सुपरक्लास कंस्ट्रक्टर को कॉल करने से पहले एक उदाहरण के सदस्यों को भी सेट कर सकते हैं। फ़ील्ड या कॉलिंग मेथड पढ़ना हालांकि इससे पहले संभव नहीं है।
राफेल विंटरथेलर

3

सादे जावा कोड के बजाय आप बाइट कोड के साथ कुछ कर सकते हैं, कोड उत्पन्न होता है जो एक संकलक के बिना लोड और चल सकता है। कई प्रणालियों में JDK की बजाय JRE होता है और यदि आप डायनामिक रूप से कोड जेनरेट करना चाहते हैं तो बेहतर हो सकता है, यदि आसान न हो, तो जावा कोड के बजाय बाइट कोड उत्पन्न करना होगा, जिसका उपयोग करने से पहले उसे संकलित किया जाना चाहिए।


6
लेकिन फिर आप सिर्फ कंपाइलर को छोड़ रहे हैं, कुछ ऐसा उत्पादन नहीं कर रहे हैं जो कंपाइलर का उपयोग करके उत्पादित नहीं किया जा सकता है (यदि यह उपलब्ध था)।
बार्ट वैन ह्युकेलम

2

जब मैं आई-प्ले था, तो मैंने एक बाईटेकोड ऑप्टिमाइज़र लिखा था (यह J2ME अनुप्रयोगों के लिए कोड आकार को कम करने के लिए डिज़ाइन किया गया था)। मैंने जो एक सुविधा जोड़ी थी वह इनलाइन बायटेकोड (सीलाइन में इनलाइन असेंबली भाषा के समान) का उपयोग करने की क्षमता थी। मैं एक फ़ंक्शन के आकार को कम करने में कामयाब रहा जो डीयूपी अनुदेश का उपयोग करके एक पुस्तकालय पद्धति का हिस्सा था, क्योंकि मुझे दो बार मूल्य की आवश्यकता है। मेरे पास शून्य बाइट निर्देश भी थे (यदि आप एक विधि को बुला रहे हैं जो एक चार्ट लेता है और आप एक इंट पास करना चाहते हैं, तो आपको पता है कि मुझे कास्ट करने की आवश्यकता नहीं है मैंने char2 (var) को बदलने के लिए int2char (var) जोड़ा है और यह हटा देगा कोड के आकार को कम करने के लिए i2c निर्देश। मैंने यह भी बनाया है कि फ्लोट a = 2.3; फ्लोट b = 3.4; फ्लोट c = a + b; और वह निश्चित बिंदु में परिवर्तित हो जाएगा (तेजी से, और कुछ J2ME ने भी नहीं किया समर्थन अस्थायी बिंदु)।


2

जावा में, यदि आप किसी संरक्षित विधि (या एक्सेस में कोई अन्य कमी) के साथ किसी सार्वजनिक विधि को ओवरराइड करने का प्रयास करते हैं, तो आपको एक त्रुटि मिलती है: "कमजोर एक्सेस विशेषाधिकार निर्दिष्ट करने का प्रयास"। यदि आप इसे JVM bytecode के साथ करते हैं, तो सत्यापनकर्ता इसके साथ ठीक है, और आप इन विधियों को मूल श्रेणी के माध्यम से कॉल कर सकते हैं जैसे कि वे सार्वजनिक थे।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.