سؤال

لقد رأيت رمزًا مشابهًا لهذا:

public class Scratch
{
    public static void main(String[] args)
    {
        Integer a = 1000, b = 1000;
        System.out.println(a == b);

        Integer c = 100, d = 100;
        System.out.println(c == d);
    }
}

عند الركض ، سيتم طباعة كتلة الكود هذه:

false
true

أنا أفهم لماذا الأول false: لأن الكائنين هما كائنات منفصلة ، لذا فإن == يقارن المراجع. لكن لا يمكنني معرفة ذلك ، لماذا يعود البيان الثاني true؟ هل هناك بعض القواعد التلقائية الغريبة التي تبدأ عندما تكون قيمة عدد صحيح في نطاق معين؟ ماذا يحدث هنا؟

هل كانت مفيدة؟

المحلول

ال true يتم ضمان السطر بالفعل من خلال مواصفات اللغة. من القسم 5.1.7:

إذا كانت القيمة p التي يتم محاصرتها صحيحة ، أو خطأ ، أو بايت ، أو char في النطاق u0000 إلى u007f ، أو int أو رقم قصير بين -128 و 127 ، فدع R1 و R2 هي نتائج أي تحويلات ملاكمة من ص. هو الحال دائما أن r1 == r2.

يستمر المناقشة ، مما يشير إلى أنه على الرغم من ضمان خط الإخراج الثاني الخاص بك ، فإن الأول ليس (انظر الفقرة الأخيرة المذكورة أدناه):

من الناحية المثالية ، فإن الملاكمة قيمة بدائية معينة P ، من شأنها أن تسفر دائمًا عن مرجع متطابق. في الممارسة العملية ، قد لا يكون هذا ممكنًا باستخدام تقنيات التنفيذ الحالية. القواعد أعلاه هي حل وسط براغماتية. يتطلب البند النهائي أعلاه أن تكون بعض القيم المشتركة دائمًا محشورة في كائنات لا يمكن تمييزها. قد يكون التنفيذ مخبأة هذه ، بتكاسل أو بشغف.

بالنسبة للقيم الأخرى ، لا تسمح هذه الصيغة بأي افتراضات حول هوية القيم المعبأة في جزء المبرمج. هذا من شأنه أن يسمح (ولكن لا يتطلب) تبادل بعض أو كل هذه المراجع.

هذا يضمن أنه في معظم الحالات شيوعًا ، سيكون السلوك هو السلوك المطلوب ، دون فرض عقوبة أداء لا مبرر لها ، وخاصة على الأجهزة الصغيرة. قد تكون التطبيقات الأقل محدودة للذاكرة ، على سبيل المثال ، تخزين جميع الأحرف والسراويل القصيرة ، وكذلك الأعداد الصحيحة والطاقة في حدود -32k - +32k.

نصائح أخرى

public class Scratch
{
   public static void main(String[] args)
    {
        Integer a = 1000, b = 1000;  //1
        System.out.println(a == b);

        Integer c = 100, d = 100;  //2
        System.out.println(c == d);
   }
}

انتاج:

false
true

نعم يتم إنتاج الإخراج الأول لمقارنة المرجع ؛ "A" و "B" - هذان مرجعان مختلفان. في النقطة 1 ، يتم إنشاء مرجعين في الواقع وهو ما يشبه -

Integer a = new Integer(1000);
Integer b = new Integer(1000);

يتم إنتاج الناتج الثاني لأن JVM يحاول حفظ الذاكرة ، عندما Integer يقع في نطاق (من -128 إلى 127). في النقطة 2 ، لا يتم إنشاء مرجع جديد من عدد صحيح من النوع "D". بدلاً من إنشاء كائن جديد لمتغير مرجع نوع عدد صحيح ، تم تعيينه فقط باستخدام كائن تم إنشاؤه مسبقًا المشار إليه بواسطة "C". كل هذه تتم بواسطة JVM.

قواعد حفظ الذاكرة هذه ليست فقط عدد صحيح. لغرض حفظ الذاكرة ، ستكون حالتان من كائنات الغلاف التالية (أثناء إنشاءها من خلال الملاكمة) ، دائمًا == حيث تكون قيمها البدائية متماثلة -

  • منطقية
  • بايت
  • شخصية من u0000 إلى \u007f (7F هو 127 في عشري)
  • قصير وأدلي من -128 إلى 127

كائنات عدد صحيح في بعض النطاق (أعتقد أنه ربما -128 إلى 127) يتم تخزينه مؤقتًا وإعادة استخدامه. الأعداد الصحيحة خارج هذا النطاق تحصل على كائن جديد في كل مرة.

نعم ، هناك قاعدة غريبة على Autoboxing تبدأ عندما تكون القيم في نطاق معين. عندما تقوم بتعيين ثابت لمتغير كائن ، لا شيء في تعريف اللغة يقول كائن جديد يجب سيتم إنشاء. قد يعيد استخدام كائن موجود من ذاكرة التخزين المؤقت.

في الواقع ، فإن JVM عادة ما تخزن ذاكرة التخزين المؤقت للأعداد الصحيحة الصغيرة لهذا الغرض ، وكذلك قيم مثل boolean.true و boolean.false.

وهذا هو نقطة مثيرة للاهتمام. في هذا الكتاب جافا فعالة يقترح دائمًا تجاوز المساواة في فصولك. أيضًا ، للتحقق من المساواة في حالتين كائنات من فئة Java ، استخدم دائمًا طريقة Equals.

public class Scratch
{
    public static void main(String[] args)
    {
        Integer a = 1000, b = 1000;
        System.out.println(a.equals(b));

        Integer c = 100, d = 100;
        System.out.println(c.equals(d));
    }
}

عائدات:

true
true

أظن أن Java تحتفظ بتخزين ذاكرة صحيحة من الأعداد الصحيحة الصغيرة "محاصر بالفعل" لأنها شائعة جدًا وتوفر مجموعة كبيرة من الوقت لإعادة استخدام كائن موجود بدلاً من إنشاء واحد جديد.

في Java يعمل الملاكمة في النطاق بين -128 و 127 للحصول على عدد صحيح. عندما تستخدم الأرقام في هذا النطاق ، يمكنك مقارنته مع المشغل ==. لكائنات عدد صحيح خارج النطاق الذي يجب أن تستخدمه متساويًا.

يعد التخصيص المباشر لـ int الحرفي إلى مرجع عدد صحيح مثالًا على الملعب التلقائي ، حيث يتم التعامل مع القيمة الحرفية إلى رمز تحويل الكائن بواسطة المترجم.

لذلك أثناء تحويل برنامج التحويل البرمجي للتجميع Integer a = 1000, b = 1000; إلى Integer a = Integer.valueOf(1000), b = Integer.valueOf(1000);.

اذا هي كذلك Integer.valueOf() الطريقة التي تعطينا في الواقع كائنات عدد صحيح ، وإذا نظرنا إلى الكود المصدري لـ Integer.valueOf() الطريقة يمكننا أن نرى بوضوح الأسلوب CACHES INTEGER كائنات في النطاق -128 إلى 127 (شامل).

/**
 *
 * This method will always cache values in the range -128 to 127,
 * inclusive, and may cache other values outside of this range.
 *
 * @param  i an {@code int} value.
 * @return an {@code Integer} instance representing {@code i}.
 * @since  1.5
 */
 public static Integer valueOf(int i) {
     if (i >= IntegerCache.low && i <= IntegerCache.high)
         return IntegerCache.cache[i + (-IntegerCache.low)];
     return new Integer(i);
 }

لذا بدلاً من إنشاء وإعادة كائنات عدد صحيح جديد ، Integer.valueOf() تقوم الطريقة بإرجاع كائنات عدد صحيح من الداخلية IntegerCache إذا كانت الحرفية التي تم تمريرها أكبر من -128 وأقل من 127.

تقوم Java بتخزين هذه الكائنات الصحيحة لأن هذا النطاق من الأعداد الصحيحة يستخدم كثيرًا في البرمجة اليومية التي توفر بعض الذاكرة بشكل غير مباشر.

تتم تهيئة ذاكرة التخزين المؤقت عند الاستخدام الأول عندما يتم تحميل الفصل في الذاكرة بسبب الكتلة الثابتة. يمكن التحكم في نطاق أقصى ذاكرة التخزين المؤقت بواسطة -XX:AutoBoxCacheMax خيار JVM.

لا ينطبق سلوك التخزين المؤقت هذا على كائنات عدد صحيح فقط ، على غرار integer.integerCache لدينا أيضًا ByteCache, ShortCache, LongCache, CharacterCache إلى عن على Byte, Short, Long, Character على التوالى.

يمكنك قراءة المزيد على مقالتي Java Integer Cache - لماذا صحيح..

في Java 5 ، تم تقديم ميزة جديدة لحفظ الذاكرة وتحسين الأداء لمواجهة كائنات نوع عدد صحيح. يتم تخزين كائنات عدد صحيح داخليًا وإعادة استخدامها عبر نفس الكائنات المشار إليها.

  1. هذا ينطبق على قيم عدد صحيح في النطاق بين -127 إلى +127 (أقصى قيمة عدد صحيح).

  2. هذا التخزين المؤقت عدد صحيح يعمل فقط على autoboxing. لن يتم تخزين كائنات عدد صحيح عندما يتم بناؤها باستخدام المُنشئ.

لمزيد من التفاصيل الثابتة والمتنقلة ، تمر أدناه رابط:

مخبأ عدد صحيح بالتفصيل

إذا تحققنا من الكود المصدري لـ Integer Obeject ، سنجد مصدر valueOf الطريقة مثل هذه:

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

التي يمكن أن تفسر لماذا Integer الكائنات ، والتي في النطاق من -128 (Integer.low) إلى 127 (Integer.high) ، هي نفس الكائنات المرجعية أثناء autoboxing. ويمكننا أن نرى أن هناك فصل IntegerCache يعتني بـ Integer صفيف ذاكرة التخزين المؤقت ، وهي فئة داخلية ثابتة Integer صف دراسي.

هناك مثال آخر مثير للاهتمام قد يساعدنا على فهم هذا الموقف الغريب:

public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {

      Class cache = Integer.class.getDeclaredClasses()[0]; 
      Field myCache = cache.getDeclaredField("cache"); 
      myCache.setAccessible(true);

      Integer[] newCache = (Integer[]) myCache.get(cache); 
      newCache[132] = newCache[133]; 

      Integer a = 2;
      Integer b = a + a;
      System.out.printf("%d + %d = %d", a, a, b); //The output is: 2 + 2 = 5    

}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top