في هذه المقالة ، سيقدم لك المؤلف ميزة مهمة ومثيرة للاهتمام للغاية في Java ، وهي الملاكمة التلقائية والملاكمة ، وتفسير مبادئ الملاكمة التلقائية والملاكمة من الكود المصدري. في الوقت نفسه ، تترك هذه الميزة أيضًا فخًا. إذا لم ينتبه المطورون ، فسوف يسقطون بسهولة في هذا الفخ.
autoboxing
تعريف
عند كتابة برامج Java ، غالبًا ما يحدد الأشخاص كائنًا صحيحًا بالطرق التالية:
عدد صحيح I = 100 ؛
من الكود أعلاه ، يمكنك أن تعرف أنني مرجع من عدد صحيح و 100 هو نوع البيانات الأساسي في Java (نوع البيانات البدائية). هذه الطريقة المتمثلة في تمرير نوع البيانات الأساسية مباشرة إلى فئة Wrapper المقابلة لها هي الملاكمة التلقائية.
في JDK 1.5 ، تم تقديم الملاكمة التلقائية لأول مرة. قبل JDK 1.5 ، إذا كنت ترغب في تحديد كائن عدد صحيح بقيمة 100 ، فأنت بحاجة إلى القيام بذلك:
عدد صحيح I = عدد صحيح جديد (100) ؛
مبدأ
دعنا نجعل نقطة توقف في الكود أعلاه "Integer I = 100 ؛" واتبعه.
بعد ذلك ، يمكننا أن نرى أن البرنامج يقفز إلى طريقة (int i) لفئة عدد صحيح.
/** * إرجاع مثيل <tt> integer </tt> يمثل القيمة المحددة * <tt> int </tt>. * إذا لم يكن هناك حاجة إلى مثيل جديد <tt> integer </tt> ، فيجب استخدام هذه الطريقة * بشكل عام في التفضيل إلى المُنشئ * {link #integer (int)} ، حيث من المحتمل أن تسفر هذه الطريقة * أداءً أفضل بكثير للوقت والوقت عن طريق التخزين المؤقت للقيم المطلوبة بشكل متكرر. * * @param i a <scode> int </code> قيمة. * Regurn A <tt> integer </tt> مثيل يمثل <tt> i </tt>. * since 1.5 */ public static integer valueof (int i) {if (i> = -128 && i <= integercache.high) إرجاع integercache.cache [i + 128] ؛ مرة أخرى إرجاع عدد صحيح جديد (ط) ؛ }بمعنى آخر ، التعبئة هي JDK التي تساعدك على إكمال المكالمة إلى integer.valueof (100).
unboxing
تعريف
integer integer100 = 100 ؛ int int100 = integer100 ؛
من الكود أعلاه ، يمكنك أن ترى أن Integer100 هو مرجع إلى عدد صحيح ، و int100 هو نوع بيانات بدائية من النوع int. ومع ذلك ، يمكننا تعيين كائن نوع عدد صحيح لمتغير من نوع البيانات الأصلي المقابل. هذا هو unboxing.
unboxing هو عكس التعبئة. الملاكمة هو متغير يعين نوع بيانات بدائية للفئة المغلفة المقابلة. تعني إلغاء اللاثنيع تعيين متغير لفئة مغلفة لمتغير من نوع البيانات الأصلي المقابل. أسماء التعبئة والملاحظة هي أيضا مناسبة تماما.
مبدأ
أعتقد أن الجميع قد خمنوا ما فعله JDK بالنسبة لنا أثناء عملية إلغاء التزام. دعنا نثبت تخميننا من خلال التجارب.
قم بتعيين نقطة توقف على السطر الثاني من الكود أعلاه ، أي ، قم بتعيين نقطة توقف على "int100 = Integer100 ؛" واتبعه.
يمكننا أن نرى أن البرنامج يقفز إلى طريقة Integer Intvalue ().
/** * إرجاع قيمة هذا <code> integer </code> كـ * <code> int </code>. */ public int intvalue () {return value ؛ }أي أن JDK يساعدنا في إكمال المكالمة على طريقة Intvalue (). بالنسبة للتجربة أعلاه ، فإنها هي استدعاء طريقة Intvalue () لـ Integer100 وتعيين قيمة الإرجاع إلى INT100.
ممتد
التجربة 1
integer integer400 = 400 ؛ int int400 = 400 ؛ system.out.println (integer400 == int400) ؛
في السطر الثالث من الكود أعلاه ، يقوم Integer400 و Int400 بتنفيذ == تشغيل. وهذان نوعان مختلفان من المتغيرات. هل integer400 unboxing أو int400 التعبئة؟ ما هي نتائج العملية؟
== العملية هي تحديد ما إذا كانت عناوين كائنين متساوية أو ما إذا كانت قيم النوعين من البيانات الأساسية متساوية. لذلك ، من السهل أن نستنتج أنه إذا كان Integer400 غير مرن ، فهذا يعني أن قيم النوعين الأساسيين تتم مقارنة الناتج الأساسيين ، ويجب أن تكون نتيجة التشغيل صحيحة في هذا الوقت ؛ إذا كانت INT400 معبأة ، فهذا يعني أن عناوين الكائنين متساوية ، ويجب أن تكون نتيجة التشغيل خاطئة في هذا الوقت. (أما السبب في أن المؤلف يعينهم إلى 400 ، فهو مرتبط بالفخاخ التي سيتم مناقشتها لاحقًا).
نتيجة الجري الفعلية لدينا صحيحة. لذلك كان integer400 unboxing. نتائج تتبع الكود تثبت هذا.
التجربة 2
integer integer100 = 100 ؛ int int100 = 100 ؛ system.out.println (integer100.equals (int100)) ؛
في السطر الثالث من الكود أعلاه ، فإن معلمة طريقة integer100 تساوي INT100. نحن نعلم أن معلمات طريقة متساوية هي كائن ، وليس نوع البيانات الأساسي ، لذلك يجب أن يكون INT100 هنا معبأة. نتائج تتبع الكود تثبت هذا.
في الواقع ، إذا كان نوع المعلمة في طريقة ما هو نوع البيانات الأصلي ونوع المعلمة الذي تم تمريره هو فئة التغليف الخاصة به ، فسيتم إلغاء تجميعه تلقائيًا ؛ وفقًا لذلك ، إذا كان نوع المعلمة في طريقة ما هو نوع التغليف ونوع المعلمة الذي تم تمريره هو نوع البيانات الأصلي ، فسيتم تعبئته تلقائيًا.
التجربة 3
integer integer100 = 100 ؛ int int100 = 100 ؛ long200 = 200L ؛ system.out.println (Integer100 + int100) ؛ system.out.println (long200 == (integer100 + int100)) ؛ system.out.println (long200.equals (integer100 + int100)) ؛
في التجربة الأولى ، تعلمنا أنه عندما يؤدي نوع البيانات الأساسية == عملية مع فئة التغليف ، فستكون فئة التغليف غير مربعة. ماذا لو +، -، *، /؟ يمكننا أن نعرف في هذه التجربة.
إذا كان + التشغيل ، سيتم محاذاة نوع البيانات الأساسية ، ثم:
• في السطر 4 ، ستحصل integer100+int100 على كائن O من عدد صحيح وقيمة 200 ، وتنفيذ طريقة TOSTRING () لهذا الكائن والإخراج "200" ؛
• في السطر 5 ، ستحصل integer100+int100 على كائن O من عدد صحيح وقيمة 200. العملية == تقارن هذا الكائن بكائن Long200. من الواضح أن كاذبة ستكون الإخراج ؛
• في السطر 6 ، ستحصل Integer100+Int100 على كائن O من عدد صحيح وقيمة 200. طريقة Long's Equals تقارن Long200 مع O ، لأن كلاهما فئات مغلفة من أنواع مختلفة ، وبالتالي فإن الإخراج خطأ ؛
إذا كان + التشغيل ، سيتم إلغاء علبة فئة التغليف ، ثم:
• في السطر 4 ، ستحصل Integer100+Int100 على نوع بيانات أساسي من النوع int و Value 200 ، ثم المربع B للحصول على O ، وتنفيذ طريقة ToString () لهذا الكائن ، والإخراج "200" ؛
• في السطر 5 ، ستحصل integer100+int100 على نوع البيانات الأساسي B1 من النوع int والقيمة 200. التشغيل == غير المربح Long200 للحصول على B2. من الواضح B1 == B2 ، ويخرج صحيح ؛
• في السطر 6 ، ستحصل Integer100+int100 على نوع بيانات أساسي من النوع الباحث والقيمة 200. مربعات طريقة Long's Equals B ، ولكن الملاكمة تؤدي إلى كائن O من عدد صحيح ، لأن O و Long200 كائنات من أنواع مختلفة ، وبالتالي فإن الإخراج خطأ ؛
نتيجة تشغيل البرنامج هو:
200
حقيقي
خطأ شنيع
لذلك ، فإن التكهنات الثانية صحيحة ، أي أن فئة التغليف سيتم إلغاء مراحلها أثناء عملية +.
فخ
فخ 1
integer integer100 = null ؛
int int100 = integer100 ؛
هذان السطران من الكود قانونيان تمامًا ويمكن تجميعهما تمامًا ، ولكن عند التشغيل ، سيتم إلقاء استثناء مؤشر فارغ. من بينها ، INTEGER100 هو كائن من النوع الصحيح ، والذي بالطبع يمكن أن يشير إلى فارغ. ولكن في السطر الثاني ، سيتم إلغاء صناديق Integer100 ، أي أن طريقة Intvalue () سيتم تنفيذها على كائن فارغ ، وبالطبع سيتم طرح استثناء مؤشر فارغ. لذلك ، عند إلغاء الاضطراب ، يجب أن تولي اهتمامًا خاصًا لما إذا كان كائن الفئة المغطاة فارغًا.
فخ 2
عدد صحيح I1 = 100 ؛
عدد صحيح I2 = 100 ؛
عدد صحيح I3 = 300 ؛
عدد صحيح I4 = 300 ؛
system.out.println (i1 == i2) ؛
System.out.println (i3 == i4) ؛
لأن I1 و I2 و I3 و I4 كلها أنواع عدد صحيح ، نعتقد أن نتائج الجري يجب أن تكون خاطئة. ومع ذلك ، فإن النتيجة الحقيقية للتشغيل هي "system.out.println (i1 == i2) ؛" وهذا صحيح ، ولكن "system.out.println (i3 == i4) ؛" وهو خطأ. هذا يعني أن مراجع نوعين عدد صحيح ، I1 و I2 ، تشير إلى نفس الكائن ، في حين أن i3 و i4 ، تشير إلى كائنات مختلفة. لماذا؟ ألا يطلق عليهم جميعًا طريقة integer.valueof (int i)؟
دعنا نلقي نظرة على طريقة integer.valueof (int i) مرة أخرى.
/** * إرجاع مثيل <tt> integer </tt> يمثل القيمة المحددة * <tt> int </tt>. * إذا لم يكن هناك حاجة إلى مثيل جديد <tt> integer </tt> ، فيجب استخدام هذه الطريقة * بشكل عام في التفضيل إلى المُنشئ * {link #integer (int)} ، حيث من المحتمل أن تسفر هذه الطريقة * أداءً أفضل بكثير للوقت والوقت عن طريق التخزين المؤقت للقيم المطلوبة بشكل متكرر. * * @param i a <scode> int </code> قيمة. * Regurn A <tt> integer </tt> مثيل يمثل <tt> i </tt>. * since 1.5 */ public static integer valueof (int i) {if (i> = -128 && i <= integercache.high) إرجاع integercache.cache [i + 128] ؛ مرة أخرى إرجاع عدد صحيح جديد (ط) ؛ }يمكننا أن نرى أنه عندما i> =-128 و i <= integercache.high ، يتم إرجاع integercache.cache [i + 128] مباشرة. من بينها ، integercache هي فئة ثابتة داخلية من عدد صحيح ، والرمز الأصلي الخاص به هو كما يلي:
integercache integercache {static نهائي ثابت ؛ ذاكرة التخزين المؤقت الثابتة النهائية [] ؛ ثابت {Final int low = -128 ؛ // قد يتم تكوين قيمة عالية بواسطة الخاصية int h = 127 ؛ if (integerCacheHighpropValue! = null) {// استخدم long.decode هنا لتجنب استدعاء الطرق التي تتطلب ذاكرة التخزين المؤقت التلقائية الخاصة بـ integer int i = long.decode (integerCacheHhighpropValue) .intvalue () ؛ i = math.max (i ، 127) ؛ // الحد الأقصى لحجم الصفيف هو integer.max_value h = math.min (i ، integer.max_value - -low) ؛ } عالية = h ؛ ذاكرة التخزين المؤقت = عدد صحيح جديد [(ارتفاع - منخفض) + 1] ؛ int j = low ؛ لـ (int k = 0 ؛ k <cache.length ؛ k ++) ذاكرة التخزين المؤقت [k] = عدد صحيح جديد (j ++) ؛ } integercache () {}}يمكننا أن نرى بوضوح أن integercache لديها ذاكرة التخزين المؤقت المتغير الأعضاء الثابت ، وهي صفيف مع 256 عنصرًا. تتم تهيئة ذاكرة التخزين المؤقت أيضًا في integercache ، أي أن العنصر i-th هو كائن عدد صحيح بقيمة I-128. -128 إلى 127 هي كائنات عدد صحيح شيوعًا ، وهذا النهج يحسن الأداء بشكل كبير. ولهذا السبب أيضًا أن "integeri1 = 100 ؛ integer i2 = 100 ؛" ، يحصل i1 و i2 على نفس الكائن.
بمقارنة التجربة الثانية في الامتداد ، تعلمنا أنه عندما يتم تشغيل فئة التغليف == بالنوع الأساسي ، فإن فئة التغليف سوف تلفت الانتباه ، وتتم مقارنة نتيجة إلغاء التفسير بالنوع الأساسي ؛ بينما عندما يتم تشغيل فئتي التغليف == مع الكائنات الأخرى ، فإنها تقارن عناوين الكائنين ، أي لتحديد ما إذا كانت المرجعتين تشير إلى نفس الكائن.
تناقش المقالة أعلاه لفترة وجيزة التعبئة التلقائية Java و Unboxing وفرخها هي كل المحتوى الذي أشاركه معك. آمل أن يعطيك مرجعًا وآمل أن تتمكن من دعم wulin.com أكثر.