المحتوى التالي هو الأسئلة والأجوبة المسؤولة التي تم تقديمها بعد مراجعة مجموعة أسئلة مقابلة Java الأصلية والإجابات بالكامل. كانت هناك العديد من الأسئلة المكررة والأسئلة القيمة في الأسئلة الأصلية ، وكانت العديد من الإجابات المرجعية خاطئة أيضًا. تقوم أسئلة مقابلة Java المعدلة بتعيين المراجع أحدث إصدار من JDK ، وتزيل محتوى عديم الفائدة مثل EJB 2.x ، ويكمل بنية البيانات والأسئلة المتعلقة بالخوارزمية ، وأسئلة برمجة المقابلة الكلاسيكية ، والهندسة المعمارية الفنية للمواقع الكبيرة ، ونظام التشغيل ، وقاعدة البيانات ، واختبار البرمجيات ، وأنماط التصميم ، ومحتوى UML وغيرها من المحتوى. في الوقت نفسه ، تم تحليل العديد من نقاط المعرفة بعمق ، مثل تصميم طريقة HashCode ، وكومة وتوليد جمع القمامة ، والبرمجة الجديدة المتزامنة لـ Java ، و Nio.2 ، وما إلى ذلك. أعتقد أنه سيكون من المفيد لمبرمجي Java الذين يستعدون للانضمام.
مجموعة أسئلة مقابلة المبرمج Java (1-50)
1. جافا أساسيات
1. ما هي جوانب الخصائص الموجهة للكائن؟
الإجابة: الجوانب الرئيسية للميزات الموجهة للكائنات هي:
1) الملخص: التجريد هو عملية تلخيص الخصائص الشائعة لنوع الكائن في فئة من الكائنات ، بما في ذلك تجريد البيانات وتجريد السلوك. يركز التجريد فقط على السمات والسلوكيات التي يمتلكها الكائن ، ولا ينتبه إلى تفاصيل هذه السلوكيات.
2) الميراث: الميراث هو عملية الحصول على معلومات الميراث من الفئات الحالية وإنشاء فئات جديدة. يسمى الفصل الذي يوفر معلومات ورثية الفئة الأصل (الفئة الفائقة ، الفئة الأساسية) ؛ يسمى الفصل الذي يحصل على المعلومات الموروثة الفئة الفرعية (الفئة المشتقة). يمنح الميراث نظام البرمجيات المتغيرة درجة معينة من الاستمرارية ، والميراث هو أيضًا وسيلة مهمة لتغليف العوامل المتغيرة في البرنامج (إذا لم تتمكن من فهمه ، فيرجى قراءة الجزء حول وضع الجسر في "Java and Patterns" للدكتور يان هونغ أو "أنماط التصميم رائعة").
3) التغليف: يُعتقد عمومًا أن التغليف هو ربط البيانات بطريقة تشغيل بيانات ، ويمكن تحقيق الوصول إلى البيانات إلا من خلال الواجهة المحددة. جوهر الموجهة نحو الكائن هو تصوير العالم الحقيقي كسلسلة من الأشياء المستقلة والمغلقة تمامًا. الطريقة التي نكتبها في الفصل هي تغليف تفاصيل التنفيذ ؛ نكتب فئة هي تغليف البيانات وعمليات البيانات. يمكن القول أن التغليف هو إخفاء كل ما يمكن إخفاؤه ، ولا يوفر سوى أبسط واجهة برمجة للعالم الخارجي (يمكنك التفكير في الفرق بين غسالة غسالة عادية وغسالة أوتوماتيكية بالكامل. من الواضح أن الغسالة التلقائية بالكامل تم تعبئتها بشكل أفضل ، وبالتالي فإن الهاتف الذكي الذي نستخدمه الآن يتم تعبئته أيضًا بشكل جيد لأن بعض الأتباع يمكن أن يتعامل مع كل شيء).
4) تعدد الأشكال: يشير تعدد الأشكال إلى السماح لكائنات من أنواع فرعية مختلفة بالاستجابة بشكل مختلف لنفس الرسالة. ببساطة ، هو استدعاء نفس الطريقة مع مرجع الكائن نفسه ولكن القيام بأشياء مختلفة. تنقسم الأشكال المتعددة إلى تعدد الأشكال في وقت التجميع والتشكيلات المتعددة للوقت. إذا تم اعتبار طريقة الكائن خدمة مقدمة من الكائن إلى العالم الخارجي ، فيمكن تفسير تعدد الأشكال في وقت التشغيل على النحو التالي: عندما يقوم النظام A بالوصول إلى الخدمات التي يوفرها النظام B ، فإن النظام B لديه طرق متعددة لتوفير الخدمات ، ولكن كل شيء شفاف للنظام A (تمامًا هو أن نظام التشغيل الكهربائي هو نظام الطاقة A ، فإن نظام الطاقة B ، يمكن أن يكون النظام B ، نظامًا للبطائرات أو AC ، وقد يكون ذلك حتى يتم حلول الطاقة. الكائنات ، لكنها لا تعرف ما هو التنفيذ الأساسي لنظام إمداد الطاقة وكيف يحصل على الطاقة). الأسلوب الزائد للأسلوب ينفذ تعدد الأشكال في وقت التجميع (المعروف أيضًا باسم prebinding) ، في حين أن تجاوز الطريقة ينفذ تعدد الأشكال في وقت التشغيل (المعروف أيضًا باسم ما بعد الولادة). تعدد الأشكال في وقت التشغيل هو الشيء الأكثر أهمية في الموجهة نحو الكائن. لتنفيذ تعدد الأشكال ، يجب القيام بأمرين: 1. إعادة كتابة الطريقة (ترث الفئة الفرعية فئة الأصل وإعادة كتابة الأساليب الحالية أو المجردة في فئة الأصل) ؛ 2. نمذجة الكائن (في إشارة إلى كائن نوع الطفل مع مرجع نوع الأصل ، بحيث تُظهر المرجع نفسه نفس الطريقة سيظهر سلوكيات مختلفة وفقًا لكائنات الفئة الفرعية المختلفة).
2. ما هو الفرق بين الوصول إلى المعدلات العامة والخاصة والمحمية وعدم الكتابة (الافتراضي)؟
الإجابة: الاختلافات هي كما يلي:
النطاق هو نفس الفئة الفرعية كعكة.
العام
محمية √ √ √ ×
الافتراضي √ √ × ×
خاص √ × × ×
الافتراضي هو افتراضي عندما لا يكتب أعضاء الفصل تعديل الوصول. بشكل افتراضي ، فإنه يعادل الجمهور لفئات أخرى في نفس الحزمة ، وخاصة للفئات الأخرى التي ليست في نفس الحزمة. المحمية مكافئة للجمهور على الفئات الفرعية ، والخاصة إلى الفئات التي ليست في نفس الحزمة التي ليس لها علاقة بين الوالدين والطفل.
3. هل سلسلة البيانات الأساسية؟
الإجابة: لا ، لا يوجد سوى 8 أنواع بيانات أساسية في Java: Byte ، Short ، int ، Long ، Float ، Double ، Char ، and Boolean ؛ باستثناء النوع الأساسي (النوع البدائي) ونوع التعداد (نوع التعداد) ، يكون الباقي أنواعًا مرجعية (نوع مرجعي).
4. تعويم F = 3.4 ؛ هل هذا صحيح؟
الإجابة: غير صحيح. 3.4 هو رقم دقة مزدوجة. سيؤدي تعيين نوع النقطة المزدوجة إلى الطفو (الطفو) إلى فقدان الدقة (الصب الأسفل ، والمعروف أيضًا باسم التضيق) ، لذلك تحتاج إلى إلقاء Float F = (Float) 3.4 ؛ أو كتابة تعويم f = 3.4f ؛.
5. قصيرة S1 = 1 ؛ S1 = S1 + 1 ؛ هل هناك أي شيء خاطئ؟ قصيرة S1 = 1 ؛ S1 += 1 ؛ هل هناك شيء خاطئ؟
الإجابة: ل s1 s1 = 1 ؛ S1 = S1 + 1 ؛ نظرًا لأن 1 هو نوع int ، فإن نتيجة عملية S1+1 هي أيضًا نوع int ، ويجب أن يكون نوع المصبوب مطلوبًا لتعيين قيمة للنوع القصير. وقصير S1 = 1 ؛ S1 += 1 ؛ يمكن تجميعها بشكل صحيح لأن S1+= 1 ؛ يعادل S1 = (قصير) (S1 + 1) ؛ هناك قوالب ضمنية.
6. هل هناك غوتو في جافا؟
الإجابة: Goto هي كلمة محجوزة في Java ولا تستخدم في الإصدار الحالي من Java. (وترد قائمة بالكلمات الرئيسية Java في ملحق كتاب "لغة برمجة Java" التي كتبها جيمس جوسلينج (والد Java) ، والتي تشمل Goto و Const ، لكن هذين الكلمات الرئيسية غير قابلة للاستخدام حاليًا ، لذا فإن بعض الأماكن التي تسميها الكلمات المخصصة. تعتبر كلمات محفوظة)
7. ما هو الفرق بين int و integer؟
الإجابة: Java هي لغة برمجة موجهة نحو كائن تقريبًا ، ولكن من أجل راحة البرمجة ، لا تزال تقدم أنواع البيانات الأساسية التي ليست كائنات. ومع ذلك ، من أجل تشغيل أنواع البيانات الأساسية هذه ككائنات ، قدمت Java فئة التفاف المقابلة لكل نوع بيانات أساسي. فئة التعبئة والتغليف من int هي عدد صحيح. منذ JDK 1.5 ، تم تقديم آلية التعبئة/التعبئة التلقائية ، بحيث يمكن تحويل الاثنين إلى بعضهما البعض.
يوفر Java أنواع الغلاف لكل نوع بدائي:
الأنواع البدائية: منطقية ، شار ، بايت ، قصيرة ، int ، طويلة ، تعويم ، مزدوج
أنواع التغليف: منطقية ، شخصية ، بايت ، قصيرة ، عدد صحيح ، طويل ، تعويم ، مزدوج
حزمة com.lovo ؛ . عدد صحيح B = 3 ؛ // المربع التلقائي 3 في نوع عدد صحيح int c = 3 ؛ system.out.println (a == b) ؛ . // True A قم بإلغاء صناديق تلقائيًا في نوع int ثم يقارن بـ C}}
تمت إضافة: لقد واجهت مؤخرًا سؤال مقابلة ، يرتبط أيضًا بالتعبئة التلقائية واللصق ، الرمز كما يلي:
الفئة العامة test03 {public static void main (string [] args) {Integer f1 = 100 ، f2 = 100 ، f3 = 150 ، f4 = 150 ؛ system.out.println (f1 == f2) ؛ system.out.println (f3 == f4) ؛ }} // hovertree.comإذا كنت لا تفهم ذلك ، فمن السهل التفكير في أن كلا المخرجين صحيحان أو خطأين. بادئ ذي بدء ، من المهم أن نلاحظ أن المتغيرات الأربعة F1 و F2 و F3 و F4 كلها كائنات عدد صحيح ، وبالتالي فإن العملية التالية == تقارن القيم ولكن المراجع. ما هو جوهر التعبئة؟ عندما نقوم بتعيين قيمة int لكائن عدد صحيح ، سوف ندعو القيمة الثابتة لفئة عدد صحيح. إذا نظرنا إلى الكود المصدري لـ ValueOF ، فسوف نعرف ما يجري.
public static integer valueof (int i) {if (i> = integercache.low && i <= integercache.high) إرجاع integercache.cache [i + (-integerCache.low)] ؛ إرجاع عدد صحيح جديد (i) ؛ } // hovertree.comIntegerCache هي فئة داخلية من عدد صحيح ، ويبدو أن الكود الخاص به:
/** * ذاكرة التخزين المؤقت لدعم دلالات هوية الكائن من Autoboxing للقيم بين * -128 و 127 (شاملة) كما هو مطلوب من قبل JLS. * * تتم تهيئة ذاكرة التخزين المؤقت عند الاستخدام الأول. قد يتم التحكم في حجم ذاكرة التخزين المؤقت * بواسطة خيار {Code -xx: AutoboxCachemax = <Size>}. * أثناء تهيئة VM ، يمكن تعيين خاصية java.lang.integer.integercache.high * في خصائص النظام الخاص في فئة * sun.misc.vm. * hovertree.com */ private static class integercache {static final int low = -128 ؛ ثابت النهائي int عالية. ذاكرة التخزين المؤقت الثابتة النهائية [] ؛ يمكن تكوين القيمة العالية {// عالية القيمة بواسطة الخاصية int h = 127 ؛ سلسلة integercachehighpropvalue = sun.misc.vm.getSavedProperty ("java.lang.integer.integercache.high") ؛ if (integerCacheHighpropValue! = null) {try {int i = parseint (integerCacheHighpropValue) ؛ i = math.max (i ، 127) ؛ // الحد الأقصى لحجم الصفيف هو integer.max_value h = math.min (i ، integer.max_value -(-low) -1) ؛ } catch (numberformatexception nfe) {// إذا كان لا يمكن تحليل الخاصية في int ، فتجاهلها. }} عالية = h ؛ ذاكرة التخزين المؤقت = عدد صحيح جديد [(ارتفاع - منخفض) + 1] ؛ int j = low ؛ لـ (int k = 0 ؛ k <cache.length ؛ k ++) ذاكرة التخزين المؤقت [k] = عدد صحيح جديد (j ++) ؛ // Range [-128 ، 127] يجب أن يتم استيعابها (JLS7 5.1.7) تأكيد integercache.high> = 127 ؛ } integercache () {}}ببساطة ، إذا كانت القيمة الحرفية تتراوح بين -128 و 127 ، فلن يكون كائن عدد صحيح جديد جديدًا ، ولكن سيتم الرجوع مباشرة إلى كائن عدد صحيح في التجمع الثابت. لذلك ، فإن نتيجة F1 == F2 في سؤال المقابلة أعلاه صحيحة ، ونتيجة F3 == F4 خاطئة. كلما أبسط أسئلة المقابلة ، كلما زاد الغموض ، يحتاج القائم بإجراء المقابلة إلى مهارات كبيرة.
8. ما هو الفرق بين & و &&؟
الإجابة: هناك استخدامان لـ & dorpator: (1) bitwise و (2) منطقي و. المشغل && هو دائرة قصيرة وعملية. الفرق بين المنطق والدوائر القصيرة ضخمة للغاية ، على الرغم من أن كلاهما يتطلب أن تكون القيم المنطقية في النهايات اليمنى واليسرى للمشغل صحيحة لقيمة التعبير بأكمله. && تسمى عملية الدائرة القصيرة لأنه إذا كانت قيمة التعبير على يسار && كاذبة ، فسيتم تعبيره على اليمين بشكل قصير مباشرة ولن يتم تنفيذ العملية. في كثير من الأحيان قد نحتاج إلى استخدام && بدلاً من &. على سبيل المثال ، عند التحقق من أن اسم المستخدم ليس فارغًا وليس سلسلة فارغة ، يجب أن تتم كتابتها على النحو التالي: اسم المستخدم! = null &&! username.equals (""). لا يمكن تبادل ترتيب الاثنين ، ولا يمكن استخدام المشغل ، لأنه إذا لم يكن الشرط الأول صحيحًا ، فلا يمكن إجراء مقارنة متساوية في السلسلة على الإطلاق ، وإلا سيتم إنشاء nullpointerxception. ملاحظة: وينطبق الشيء نفسه على الفرق بين المنطق أو المشغل (|) ودورة القصر أو المشغل (||).
تمت إضافة: إذا كنت على دراية بـ JavaScript ، فقد تتمكن من الشعور بقدرة الحوسبة القصيرة للدائرة. إذا كنت تريد أن تصبح سيد JavaScript ، فابدأ بلعب الحوسبة القصيرة.
9. اشرح استخدام مناطق التخزين المكوسية والكومية في الذاكرة.
الإجابة: عادةً ما نحدد متغيرًا من نوع البيانات الأساسي ، مرجع كائن ، وتخزين وظائف في الموقع جميع مكالمات جميع استخدام مساحة المكدس في الذاكرة ؛ ويتم وضع الكائنات التي تم إنشاؤها من خلال الكلمة الرئيسية الجديدة والمشارك في مساحة الكومة ؛ يتم وضع الحرفية في البرنامج ، مثل 100 ، "Hello" والثوابت المكتوبة مباشرة في منطقة التخزين الثابت. تعمل مساحة المكدس الأسرع ولكنها صغيرة جدًا أيضًا. عادةً ما يتم وضع عدد كبير من الكائنات في مساحة الكومة ، ويمكن استخدام الذاكرة بأكملها ، بما في ذلك الذاكرة الافتراضية على القرص الثابت ، كمساحة كومة.
String str = سلسلة جديدة ("Hello") ؛
في العبارة أعلاه ، يتم وضع STR على المكدس ، يتم وضع كائن السلسلة الذي تم إنشاؤه باستخدام جديد على الكومة ، ويتم وضع "Hello" الحرفي على منطقة التخزين الثابتة.
الملحق: يستخدم إصدار أحدث من Java تقنية تسمى "تحليل الهروب" يمكن أن تضع بعض الكائنات المحلية على المكدس لتحسين الأداء التشغيلي للكائنات.
10. ما هو Math.round (11.5) يساوي؟ ما هو Math.round (-11.5) يساوي؟
الإجابة: قيمة إرجاع Math.Round (11.5) هي 12 ، وقيمة إرجاع Math.Round (-11.5) هي -11. مبدأ التقريب هو إضافة 0.5 إلى المعلمة ثم حولها لأسفل.
11. هل يمكن أن يتصرف Swtich على Byte ، عمل طويل على String؟
الإجابة: في أوائل JDK ، في Switch (Expr) ، يمكن أن يكون Expr بايت ، قصير ، char ، و int. بدءًا من الإصدار 1.5 ، قدمت Java أنواع التعداد (ENUMS) ، ويمكن أيضًا أن تكون Expr تعدادًا ، بدءًا من الإصدار 1.7 من JDK ، وكذلك سلسلة (سلسلة). النوع الطويل غير مسموح به.
12. استخدم الطريقة الأكثر كفاءة لحساب 2 مرات 8؟
الإجابة: 2 << 3 (نقل 3 بت على اليسار يعادل الضرب بمقدار 2 إلى قوة 3 ، والتحرك 3 بت على اليمين يعادل الانقسام على 2 إلى قوة 3).
الملحق: عندما نعيد كتابة طريقة Hashcode للفصل الذي كتبناه ، قد نرى الرمز الموضح أدناه. في الواقع ، نحن لا نفهم تمامًا سبب استخدامنا للضرب هذا لإنشاء رمز التجزئة (رمز التجزئة) ، ولماذا هذا الرقم هو رقم رئيسي ، ولماذا يتم تحديد الرقم 31 عادة؟ يمكنك Baidu على إجابات على أول سؤالين. اختر 31 لأنه يمكن استخدام عمليات التحول والطرح بدلاً من الضرب ، وبالتالي تحقيق أداء أفضل. عند الحديث عن هذا ، ربما تكون قد فكرت في: 31 * num <==> (num << 5) - Num ، فإن تحويل 5 بتات اليسرى يعادل الضرب بمقدار 2 إلى الطاقة الخامسة (32) وطرح نفسه يعادل الضرب بمقدار 31. يمكن لجميع VMs إكمال هذا التحسين تابعًا.
حزمة com.loonstudio ؛ فئة عامة phonenumber {private int areacode ؛ بادئة السلسلة الخاصة عدد الكتان الخاص بالسلسلة ؛ Override public int hashcode () {Final int prime = 31 ؛ int النتيجة = 1 ؛ النتيجة = prime * result + AreaCode ؛ النتيجة = prime * result + ((linenumber == null)؟ 0: linenumber.hashCode ()) ؛ النتيجة = prime * result + ((prefix == null)؟ 0: prefix.hashCode ()) ؛ نتيجة العودة } Override Public Boolean يساوي (كائن OBJ) {if (this == obj) إرجاع true ؛ إذا (OBJ == NULL) عودة خطأ ؛ if (getClass ()! = obj.getClass ()) return false ؛ الصخور الأخرى = (عابر) OBJ ؛ if (AreaCode! = other.areAcode) إرجاع خطأ ؛ if (linenumber == null) {if (other.linenumber! = null) return false ؛ } آخر إذا (! linenumber.equals (other.linenumber)) إرجاع خطأ ؛ if (prefix == null) {if (other.prefix! = null) return false ؛ } آخر إذا (! prefix.equals (other.prefix)) إرجاع خطأ ؛ العودة صحيح. }} // لماذا تسأل عن hovertree.com13. هل هناك طريقة الطول () للصفائف؟ هل هناك طريقة الطول () للسلسلة؟
الإجابة: لا يحتوي المصفوفة على طريقة الطول () ، ولكن لها سمة طول. السلسلة لها طريقة الطول (). في JavaScript ، يتم الحصول على طول السلسلة من خلال سمة الطول ، والتي يمكن الخلط بينها بسهولة مع Java.
14. في جافا ، كيف تخرج من الحلقات المتداخلة المتعددة الحالية؟
الإجابة: أضف علامة مثل A قبل الحلقة الخارجية ، ثم استخدم Break A ؛ يمكن كسر حلقات متعددة. (تدعم Java العبارات المميزة ومتابعة البيانات ، ووظائفها تشبه إلى حد ما عبارات GOTO في C و C ++ ، ولكن تمامًا مثل تجنب GOTO ، يجب أن تتجنب الفاصل الموسومة والمتابعة لأنه لن يجعل برنامجك أكثر أناقة ، وغالبًا ما يكون له التأثير المعاكس ، لذلك فإن هذا النزاهة هو في الواقع أفضل.)
15. هل يمكن تجاوز المنشئ؟
الإجابة: لا يمكن مورث المنشئ ، لذلك لا يمكن إعادة كتابته ، ولكن يمكن زيادة تحميله.
16. كائنين لهما نفس القيمة (x.equals (y) == صحيح) ، ولكن يمكن أن يكون لهما رموز تجزئة مختلفة. هل هذا صحيح؟
الإجابة: لا ، إذا كان الكائنان x و y يرضي X.equals (y) == صحيح ، فيجب أن يكون رمز التجزئة هو نفسه. تنص Java على طريقة eqauls وطريقة hashcode كما يلي: (1) إذا كان كائنين متماثلان (تساوي طريقة إرجاع صحيح) ، فيجب أن تكون قيم Hashcode هي نفسها ؛ (2) إذا كانت ترميزات الكائنات هي نفسها ، فهي ليست هي نفسها بالضرورة. بالطبع ، لا يتعين عليك القيام بذلك كما هو مطلوب ، ولكن إذا انتهكت المبادئ المذكورة أعلاه ، فستجد أنه عند استخدام الحاويات ، يمكن أن يظهر نفس الكائن في المجموعة المحددة ، وسوف تسبب كفاءة إضافة عناصر جديدة بشكل كبير (بالنسبة للأنظمة التي تستخدم تخزين التجزئة ، فإن النزاعات المتكررة في رموز التجزئة ستؤدي إلى إعادة توزيع حاد في أداء الوصول).
الملحق: تعرف العديد من برامج Java عن أساليب المساواة وأساليب الترميز ، ولكن الكثير من الناس يعرفون ذلك. في تحفة Joshua Bloch "Java الفعالة" (العديد من شركات البرمجيات ، "Java الفعالة" ، "أفكار برمجة Java" و "إعادة إعادة النشر: تحسين جودة الكود الحالي" ، لا بد من قراءة الكتب من قبل المبرمجين Java. صحيح) ، والتماثل (x.equals (y) يرجع صحيح ، يجب أن يكون y.equals (x) أيضًا أنه يجب أن يعود صحيحًا) ، والعبور (x.equals (y) و y.equals (z) يجب أيضًا أن يعودوا صحيحًا) والاتساق (عندما لا يتم الإشارة إلى معلومات الكائنات (التي يجب أن تُشار إليها x و y ، يجب أن تحصل على نسخ متعددة إلى x.equals (y). تشمل الحيل لتنفيذ طرق متساوية عالية الجودة: 1. استخدم المشغل == للتحقق من "ما إذا كانت المعلمة مرجعية لهذا الكائن" ؛ 2. استخدم عامل التشغيل من أجل التحقق من "ما إذا كانت المعلمة هي النوع الصحيح" ؛ 3. بالنسبة للسمات الرئيسية في الفصل ، تحقق مما إذا كانت السمات التي تم تمريرها إلى الكائن تطابقها ؛ 4. بعد كتابة طريقة متساوية ، اسأل نفسك ما إذا كان يفي بالتماثل ، والعبور ، والاتساق ؛ 5. إعادة كتابة Hashcode دائمًا عند إعادة الكتابة ؛ 6. لا تستبدل كائن الكائن في معلمات طريقة متساوٍ بأنواع أخرى ، ولا تنسى التعليق التوضيحي Override عند إعادة الكتابة.
17. هل يمكن أن تكون فئة السلسلة موروثة؟
الإجابة: فئة السلسلة هي فئة نهائية ولا يمكن مورراها.
الملحق: سلسلة الوراثة هي سلوك خاطئ في حد ذاته. أفضل طريقة لإعادة استخدام أنواع السلسلة هي الارتباط (HAS-A) بدلاً من الميراث (IS-A).
18. عندما يتم تمرير كائن كمعلمة إلى طريقة ما ، يمكن لهذه الطريقة تغيير خصائص الكائن وإرجاع النتيجة التي تم تغييرها. فهل هو تمريرة قيمة أم تمريرة مرجعية هنا؟
الإجابة: إنه نقل القيمة. لغة برمجة Java تمر فقط المعلمات مع القيم. عندما يتم تمرير مثيل كائن إلى طريقة كمعلمة ، تكون قيمة المعلمة مرجعًا للكائن. يمكن تغيير خصائص الكائن أثناء عملية المكالمات ، لكن الإشارة إلى الكائن لن تتغير أبدًا. في C ++ و C#، يمكن تغيير قيمة المعلمات التي تم تمريرها عن طريق تمرير المراجع أو نقل المعلمات.
الملحق: من غير المريح حقًا عدم تمرير المراجع في Java ، والتي لم يتم تحسينها في Java 8. وبهذه الطريقة بالتحديد تظهر عدد كبير من فئات التفاف في الكود المكتوب في Java (وضع المراجع التي يجب تعديلها من خلال مكالمات الطريقة في فئة التفاف ، ثم تمرير كائن التفاف إلى طريقة). سيجعل هذا النهج فقط الكود منتفخ ، خاصة للمطورين الذين يتحولون من C و C ++ إلى مبرمجي Java إلى لا يطاق.
19. ما هو الفرق بين السلسلة و stringBuilder و StringBuffer؟
الإجابة: توفر منصة Java نوعين من الأوتار: String و StringBuffer/StringBuilder ، والتي يمكنها تخزين السلاسل ومعالجتها. حيث تكون السلسلة عبارة عن سلسلة للقراءة فقط ، مما يعني أنه لا يمكن تغيير محتوى السلسلة المشار إليها بواسطة السلسلة. يمكن تعديل كائنات السلسلة التي تمثلها فئات StringBuffer و StringBuilder مباشرة. تم تقديم StringBuilder في JDK 1.5. إنه بالضبط نفس طريقة StringBuffer ، والفرق هو أنه يتم استخدامه في بيئة مترابطة واحدة لأن جميع جوانبها لا يتم تعديلها عن طريق المزامنة ، لذلك فهي أكثر فعالية قليلاً من StringBuffer.
الملحق 1: هناك سؤال مقابلة: هل هناك أي موقف يكون فيه استخدام + للقيام بتسلسل السلسلة أفضل من استدعاء طريقة إلحاق كائن StringBuffer/StringBuilder؟ إذا كانت السلسلة التي تم الحصول عليها بعد الاتصال موجودة بالفعل في منطقة التخزين الثابتة ، فإن استخدام + لسلسلة السلسلة أفضل من طريقة إلحاق StringBuffer/StringBuilder.
الملحق 2: ما يلي هو أيضًا سؤال مقابلة ، وطرح إخراج البرنامج لمعرفة ما إذا كان يمكنك تقديم الإجابة الصحيحة.
حزمة com.lovo ؛ . السلسلة B = سلسلة جديدة ("البرمجة") ؛ السلسلة C = "البرنامج" + "Ming" ؛ system.out.println (a == b) ؛ system.out.println (a == c) ؛ system.out.println (A.equals (b)) ؛ System.out.println (A.equals (C)) ؛ System.out.println (A.Intern () == B.Intern ()) ؛ }}20. الفرق بين الحمل الزائد والتجاوز. هل يمكن التمييز بين الطرق المحملة بناءً على نوع الإرجاع؟
الإجابة: كل من المحمل الزائد وإعادة الكتابة هي طرق لتنفيذ تعدد الأشكال. الفرق هو أن السابق ينفذ تعدد الأشكال في الوقت ، بينما ينفذ الأخير تعدد الأشكال في وقت التشغيل. الحمل الزائد يحدث في الفصل. إذا كانت هناك طريقة ذات نفس الاسم لها قائمة معلمات مختلفة (أنواع معلمات مختلفة ، أو عدد مختلف من المعلمات ، أو كليهما) ، فهي تعتبر حمولة زائدة ؛ تحدث إعادة الكتابة بين الفئة الفرعية والفئة الأم. تتطلب إعادة الكتابة أن تتمتع طريقة إعادة كتابة الفئة الفرعية والفئة الأصل بنفس نوع الإرجاع مثل طريقة إعادة كتابة الفئة الأصل ، والتي هي أفضل وصول من طريقة إعادة كتابة الفئة الأصل ، ولا يمكنها الإعلان عن استثناءات أكثر من طريقة إعادة كتابة الفئة الأصل (مبدأ استبدال Rischer). لا يحتوي التحميل الزائد على متطلبات خاصة لأنواع الإرجاع.
الملحق: طرحت Huawei ذات مرة سؤالًا في سؤال المقابلة: لماذا لا يمكنك التمييز بين الحمل الزائد بناءً على نوع الإرجاع وأخبر إجابتك!
21. صف مبدأ وآلية ملفات فئة تحميل JVM؟
الإجابة: يتم تنفيذ تحميل الفئات في JVM بواسطة محمل فئة (ClassLoader) والفئات الفرعية. يعد Loader Class في Java مكونًا مهمًا لنظام وقت تشغيل Java ، وهو مسؤول عن العثور على فئات وتحميلها في ملفات الفصل في وقت التشغيل.
التجديد:
1. نظرًا لطبيعة الجافا المتقاطعة لـ Java ، فإن برنامج مصدر Java المترجم ليس برنامجًا قابلًا للتنفيذ ، ولكنه ملف فئة واحد أو أكثر. عندما يحتاج برنامج Java إلى استخدام فئة ، يضمن JVM تحميل الفصل وتوصيله (تم التحقق منه وإعداده وتحليله) وتهيئته. يشير تحميل الفئة إلى قراءة البيانات من ملف Class .Class في الذاكرة. عادة ، إنشاء صفيف بايت لقراءته في ملف .class ، ثم إنشاء كائن فئة يتوافق مع الفئة المحملة. بعد اكتمال التحميل ، لا يزال كائن الفئة غير مكتمل ، وبالتالي فإن الفئة غير متوفرة في هذا الوقت. عندما يتم تحميل الفصل ، فإنه يدخل مرحلة الاتصال. تتضمن هذه المرحلة ثلاث خطوات: التحقق ، والتحضير (تخصيص الذاكرة للمتغيرات الثابتة وتحديد القيمة الأولية الافتراضية) والحلية (استبدل مراجع الرمز مع المراجع المباشرة). أخيرًا ، يقوم JVM بتهيئة الفئة ، بما في ذلك: 1. إذا كان للفئة فئة الوالدين المباشرة ولم يتم تهيئة الفئة ، فسيتم تهيئة الفئة الأصل أولاً ؛ 2. إذا كانت هناك بيانات تهيئة في الفصل ، فسيتم تنفيذ بيانات التهيئة هذه بدورها.
2. يتم تحميل الفئة بواسطة Loader الفئة ، والذي يتضمن: Root Loader (Bootstrap) ، Loader (Extension) ، محمل النظام (النظام) وعملية فئة محددة من قبل المستخدم (الفئة الفرعية لـ java.lang.classloader). بدءًا من JDK 1.2 ، تعتمد عملية التحميل الفئة آلية تفويض الأب (PDM). PDM أفضل يضمن أمان منصة Java. في هذه الآلية ، فإن bootstrap الخاص بـ JVM هو اللودر الجذر ، ولحمل آخر ولديه محمل فئة من الوالدين. يطلب تحميل الفئة أولاً تحميل الفئة الأصل ، وسيتم تحميل تحميل الفئة الأصل فقط بواسطة محمل الفئة الفرعية الخاصة به عندما يكون عاجزًا. لا يوفر JVM إشارات إلى Bootstrap لبرامج Java. فيما يلي بعض الإرشادات للعديد من اللوادر الفئة:
أ) bootstrap: يتم تنفيذها بشكل عام باستخدام التعليمات البرمجية المحلية وهي مسؤولة عن تحميل مكتبة الفئة الأساسية الأساسية لـ JVM (RT.JAR) ؛
ب) الامتداد: قم بتحميل مكتبة الفصل من الدليل المحدد بواسطة خاصية نظام java.ext.dirs ، وتوصيله الأم هو bootstrap ؛
ج) النظام: المعروف أيضًا باسم Loader فئة التطبيق ، فئة الأم هو امتداد. هذا هو اللودر الفئة الأكثر استخداما على نطاق واسع. يسجل فئات من الدليل المحدد من قبل ClassPath المتغير البيئة أو سمة النظام java.class.path ، وهو المحمل الأصل الافتراضي للوحد المعرفة من قبل المستخدم.
22. هل يمكن تخزين شخصية صينية في متغير نوع Char؟ لماذا؟
الإجابة: يمكن لنوع char تخزين حرف صيني ، لأن الترميز المستخدم في Java هو Unicode (لا يتم تحديد ترميز محدد ، ويتم استخدام الأحرف مباشرة في رقم مجموعة الأحرف ، وهي الطريقة الوحيدة الموحدة). يأخذ نوع char 2 بايت (16bit) ، لذلك لا توجد مشكلة في وضع صيني.
الملحق: يعني استخدام Unicode أن الأحرف لديها مظاهر مختلفة داخل وخارج JVM ، وكلاهما يونيكود داخل JVM. عندما يتم نقل هذا الحرف من JVM إلى الخارج (على سبيل المثال ، تخزينه في نظام الملفات) ، يلزم تحويل الترميز. لذلك ، تحتوي Java على تدفقات البايت وتيارات الأحرف ، بالإضافة إلى تدفقات التحويل التي تتحول بين تدفقات الأحرف وتيارات البايت ، مثل InputStreamReader و OutputStreamReader. هاتان الفئتان هما فئات محول بين تدفقات البايت وتدفقات الأحرف ، وتنفيذ مهمة تشفير التحويل ؛ بالنسبة لمبرمجي C ، لإكمال هذا التحويل الترميز ، ربما يعتمدون على خصائص الاتحاد (الاتحاد/المجتمع) في الذاكرة المشتركة لتحقيقها.
23. ما هي أوجه التشابه والاختلاف بين الطبقة التجريدية والواجهة؟
الإجابة: لا يمكن إنشاء إنشاء كل من الفصول والواجهات التجريدية ، ولكن يمكن تحديد الإشارات إلى الفئات التجريدية وأنواع الواجهة. إذا ورث الفئة فئة مجردة أو تنفذ واجهة ، فإنها تحتاج إلى تنفيذ جميع الأساليب المجردة فيه ، وإلا فإن الفصل لا يزال يتعين الإعلان عن فئة مجردة. الواجهات أكثر تجريدًا من الفئات المجردة ، لأنه يمكن تعريف المنشآت في الفئات التجريدية ، ويمكن أن تكون هناك طرق مجردة وطرق ملموسة ، بينما لا يمكن تعريف المنشآت في الواجهات ، وجميع الطرق فيها هي طرق مجردة. يمكن أن يكون الأعضاء في الفصول المجردة خاصة ، افتراضية ، محمية ، وعامة ، في حين أن الأعضاء في الواجهات جميعهم عامة. يمكن تعريف متغيرات الأعضاء في الفصول التجريدية ، في حين أن متغيرات الأعضاء المحددة في الواجهات هي في الواقع ثوابت. يجب إعلان الفصول ذات الأساليب المجردة كطبقات مجردة ، ولا تحتوي الفصول التجريدية بالضرورة على طرق مجردة.
24. ما هو الفرق بين الطبقات المتداخلة الثابتة والفصول الداخلية (الطبقة الداخلية)؟
الإجابة: الطبقة المتداخلة الثابتة هي فئة داخلية تم إعلانها على أنها ثابتة ، والتي يمكن إنشاء مثيل لها دون الاعتماد على مثيلات فئة خارجية. تحتاج الطبقات الداخلية المعتادة إلى إنشاء مثيل لها بعد إنشاء فئة خارجية ، ويبدو أن بناء الجملة غريبًا تمامًا ، كما هو موضح أدناه.
حزمة com.lovo ؛ / ** * فئة البوكر (مجموعة من البوكر) لماذا تسأل عن hovertree.com * */ public class poker {private static string [] {"spade" ، "Rose" ، "Grass Flower" ، "Cube"} ؛ int static int [] faces = {1 ، 2 ، 3 ، 4 ، 5 ، 6 ، 7 ، 8 ، 9 ، 10 ، 11 ، 12 ، 13} ؛ البطاقات الخاصة [] بطاقات ؛ / *** Constructor**/ Public Poker () {Cards = New Card [52] ؛ لـ (int i = 0 ؛ i <suites.length ؛ i ++) {for (int j = 0 ؛ j <faces.length ؛ j ++) {cards [i * 13+j] = new card (suites [i] ، faces [j]) ؛ }}} / ** * خلط ورق اللعب (عشوائي من الترتيب) * * / / public void Shuffle () {for (int i = 0 ، len = cards.length ؛ i <len ؛ i ++) {int index = (int) (math.random () * len) ؛ بطاقة temp = البطاقات [الفهرس] ؛ البطاقات [الفهرس] = البطاقات [i] ؛ البطاقات [i] = temp ؛ }} / *** صفقة بطاقة* param index موقف الصفقة** / صفقة البطاقة العامة (int index) {strense cards [index] ؛ } / ** * Card Class (قطعة من البوكر) * [الفئة الداخلية] * Author Luo Hao * * / Class Public Card {Private String Suite ؛ // دعوى الوجه الخاص ؛ // Points Public Card (String suite ، int face) {this.suite = suite ؛ this.face = الوجه ؛ } Override public string toString () {string facestr = "" ؛ التبديل (الوجه) {الحالة 1: facestr = "a" ؛ استراحة؛ الحالة 11: facestr = "j" ؛ استراحة؛ الحالة 12: facestr = "q" ؛ استراحة؛ الحالة 13: facestr = "k" ؛ استراحة؛ الافتراضي: facestr = string.valueof (الوجه) ؛ } مجموعة الإرجاع + facestr ؛ }}} فئة الاختبار:
حزمة com.lovo ؛ class pokertest {public static void main (string [] args) {poker poker = new poker () ؛ poker.shuffle () ؛ // Shuffle Poker.card C1 = poker.deal (0) ؛ // يخدم البطاقة الأولى // لبطاقات الفئة الداخلية غير الدماغية // يمكن إنشاء كائن البطاقة فقط من خلال بطاقة بوكر من الفئة الخارجية. // إنشاء بطاقة نفسك system.out.println (C1) ؛ // أول نظام. // Print: Red Heart A}} // لماذا اسأل عن Hovertree.com25. هل سيكون هناك تسرب ذاكرة في جافا؟ يرجى وصفه لفترة وجيزة.
الإجابة: من الناحية النظرية ، لن يكون لدى Java تسرب ذاكرة لأنها تحتوي على آلية لجمع القمامة (GC) (وهذا أيضًا سبب مهم لاستخدام Java على نطاق واسع في البرمجة من جانب الخادم) ؛ ومع ذلك ، في التطوير الفعلي ، قد يكون هناك كائنات عديمة الفائدة ولكن يمكن الوصول إليها ، ولا يمكن إعادة تدوير هذه الكائنات بواسطة GC وسوف تحدث تسرب الذاكرة. مثال على ذلك أن الكائنات في جلسة السبات (ذاكرة التخزين المؤقت من المستوى الأول) مستمرة ، ولن يقوم جامع القمامة بإعادة تدوير هذه الكائنات ، ولكن قد يكون هناك كائنات قمامة عديمة الفائدة في هذه الكائنات. يوضح المثال التالي أيضًا تسرب الذاكرة في Java:
حزمة com.lovo ؛ // لماذا تسأل عن hovertree.commport java.util.arrays ؛ استيراد java.util.emptystackexception ؛ الطبقة العامة Mystack <t> {private t [] عناصر ؛ حجم int الخاص = 0 ؛ استاتيكي خاص int init_capacity = 16 ؛ Public Mystack () {elements = (t []) كائن جديد [init_capacity] ؛ } public void push (t elem) {insureCapacity () ؛ elements[size++] = elem; } public T pop() { if(size == 0) throw new EmptyStackException(); return elements[--size]; } private void ensureCapacity() { if(elements.length == size) { elements = Arrays.copyOf(elements, 2 * size + 1); }}}上面的代码实现了一个栈(先进后出(FILO))结构,乍看之下似乎没有什么明显的问题,它甚至可以通过你编写的各种单元测试。然而其中的pop方法却存在内存泄露的问题,当我们用pop方法弹出栈中的对象时,该对象不会被当作垃圾回收,即使使用栈的程序不再引用这些对象,因为栈内部维护着对这些对象的过期引用(obsolete reference)。在支持垃圾回收的语言中,内存泄露是很隐蔽的,这种内存泄露其实就是无意识的对象保持。如果一个对象引用被无意识的保留起来了,那么垃圾回收器不会处理这个对象,也不会处理该对象引用的其他对象,即使这样的对象只有少数几个,也可能会导致很多的对象被排除在垃圾回收之外,从而对性能造成重大影响,极端情况下会引发Disk Paging(物理内存与硬盘的虚拟内存交换数据),甚至造成OutOfMemoryError。
26、抽象的(abstract)方法是否可同时是静态的(static),是否可同时是本地方法(native),是否可同时被synchronized修饰?
答:都不能。抽象方法需要子类重写,而静态的方法是无法被重写的,因此二者是矛盾的。本地方法是由本地代码(如C代码)实现的方法,而抽象方法是没有实现的,也是矛盾的。synchronized和方法的实现细节有关,抽象方法不涉及实现细节,因此也是相互矛盾的。
27、静态变量和实例变量的区别?
答:静态变量是被static修饰符修饰的变量,也称为类变量,它属于类,不属于类的任何一个对象,一个类不管创建多少个对象,静态变量在内存中有且仅有一个拷贝;实例变量必须依存于某一实例,需要先创建对象然后通过对象才能访问到它。静态变量可以实现让多个对象共享内存。在Java开发中,上下文类和工具类中通常会有大量的静态成员。
28、是否可以从一个静态(static)方法内部发出对非静态(non-static)方法的调用?
答:不可以,静态方法只能访问静态成员,因为非静态方法的调用要先创建对象,因此在调用静态方法时可能对象并没有被初始化。
29、如何实现对象克隆?
答:有两种方式:
1.实现Cloneable接口并重写Object类中的clone()方法;
2.实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆,代码如下。
package com.lovo; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class MyUtil { private MyUtil() { throw new AssertionError(); } public static <T> T clone(T obj) throws Exception { ByteArrayOutputStream bout = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bout); oos.writeObject(obj); ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bin); return (T) ois.readObject(); // 说明:调用ByteArrayInputStream或ByteArrayOutputStream对象的close方法没有任何意义// 这两个基于内存的流只要垃圾回收器清理对象就能够释放资源} } //何问起hovertree.com下面是测试代码:
package com.lovo; import java.io.Serializable; /** * Human* @author Luo Hao* */ class Person implements Serializable { private static final long serialVersionUID = -9102017020286042305L; private String name; // Name private int age; // Age private Car car; // Car public Person(String name, int age, Car car) { this.name = name; this.age = العمر ؛ this.car = car; } السلسلة العامة getName () {return name ؛ } public void setName(String name) { this.name = name; } public int getage () {return Age ؛ } public void setAge(int age) { this.age = age; } public Car getCar() { return car; } public void setCar(Car car) { this.car = car; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + ", car=" + car + "]"; } } /** * Car class* @author Luo Hao* */ class Car implements Serializable { private static final long serialVersionUID = -5713945027627603702L; private String brand; // Brand private int maxSpeed; // Top speed public Car(String brand, int maxSpeed) { this.brand = brand; this.maxSpeed = maxSpeed; } public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } public int getMaxSpeed() { return maxSpeed; } public void setMaxSpeed(int maxSpeed) { this.maxSpeed = maxSpeed; } @Override public String toString() { return "Car [brand=" + brand + ", maxSpeed=" + maxSpeed + "]"; } } //Why ask about hovertree.comclass CloneTest { public static void main(String[] args) { try { Person p1 = new Person("Hao LUO", 33, new Car("Benz", 300)); Person p2 = MyUtil.clone(p1); // Deep cloning p2.getCar().setBrand("BYD"); // Modify the brand attributes of the cloned Person object p2-associated car object// The original Person object p1-associated car will not be affected in any way// Because when cloning Person object, the car object associated with it is also cloned System.out.println(p1); } catch (Exception e) { e.printStackTrace(); }}}注意:基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型限定,可以检查出要克隆的对象是否支持序列化,这项检查是编译器完成的,不是在运行时抛出异常,这种是方案明显优于使用Object类的clone方法克隆对象。
30、GC 是什么?为什么要有GC?
答: GC是垃圾收集的意思,内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的显示操作方法。Java程序员不用担心内存管理,因为垃圾收集器会自动进行管理。要请求垃圾收集,可以调用下面的方法之一:System.gc() 或Runtime.getRuntime().gc() ,但JVM可以屏蔽掉显示的垃圾回收调用。
垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存。垃圾回收器通常是作为一个单独的低优先级的线程运行,不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收,程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。在Java诞生初期,垃圾回收是Java最大的亮点之一,因为服务器端的编程需要有效的防止内存泄露问题,然而时过境迁,如今Java的垃圾回收机制已经成为被诟病的东西。移动智能终端用户通常觉得iOS的系统比Android系统有更好的用户体验,其中一个深层次的原因就在于Android系统中垃圾回收的不可预知性。
补充:垃圾回收机制有很多种,包括:分代复制垃圾回收、标记垃圾回收、增量垃圾回收等方式。标准的Java进程既有栈又有堆。栈保存了原始型局部变量,堆保存了要创建的对象。Java平台对堆内存回收和再利用的基本算法被称为标记和清除,但是Java对其进行了改进,采用“分代式垃圾收集”。这种方法会跟Java对象的生命周期将堆内存划分为不同的区域,在垃圾收集过程中,可能会将对象移动到不同区域:
伊甸园(Eden):这是对象最初诞生的区域,并且对大多数对象来说,这里是它们唯一存在过的区域。
幸存者乐园(Survivor):从伊甸园幸存下来的对象会被挪到这里。
终身颐养园(Tenured):这是足够老的幸存对象的归宿。年轻代收集(Minor-GC)过程是不会触及这个地方的。当年轻代收集不能把对象放进终身颐养园时,就会触发一次完全收集(Major-GC),这里可能还会牵扯到压缩,以便为大对象腾出足够的空间。
与垃圾回收相关的JVM参数:
-Xms / -Xmx --- 堆的初始大小/ 堆的最大大小
-Xmn --- 堆中年轻代的大小
-XX:-DisableExplicitGC --- 让System.gc()不产生任何作用
-XX:+PrintGCDetail --- 打印GC的细节
-XX:+PrintGCDateStamps --- 打印GC操作的时间戳
31、String s=new String(“xyz”);创建了几个字符串对象?
答:两个对象,一个是静态存储区的"xyz",一个是用new创建在堆上的对象。
32、接口是否可继承(extends)接口? 抽象类是否可实现(implements)接口? 抽象类是否可继承具体类(concrete class)?
答:接口可以继承接口。抽象类可以实现(implements)接口,抽象类可继承具体类,但前提是具体类必须有明确的构造函数。
33、一个“.java”源文件中是否可以包含多个类(不是内部类)?有什么限制?
答:可以,但一个源文件中最多只能有一个公开类(public class)而且文件名必须和公开类的类名完全保持一致。
34、Anonymous Inner Class(匿名内部类)是否可以继承其它类?是否可以实现接口?
答:可以继承其他类或实现其他接口,在Swing编程中常用此方式来实现事件监听和回调。
35、内部类可以引用它的包含类(外部类)的成员吗?有没有什么限制?
答:一个内部类对象可以访问创建它的外部类对象的成员,包括私有成员。
36、Java 中的final关键字有哪些用法?
答: (1)修饰类:表示该类不能被继承;(2)修饰方法:表示方法不能被重写;(3)修饰变量:表示变量只能一次赋值以后值不能被修改(常量)。
37、指出下面程序的运行结果:
class A{ static{ System.out.print("1"); } public A(){ System.out.print("2"); } } class B extends A{ static{ System.out.print("a"); } public B(){ System.out.print("b"); } } //Why ask about hovertree.compublic class Hello{ public static void main(String[] args){ A ab = new B(); ab = new B(); }}答:执行结果:1a2b2b。创建对象时构造器的调用顺序是:先初始化静态成员,然后调用父类构造器,再初始化非静态成员,最后调用自身构造器。
38、数据类型之间的转换:
1)如何将字符串转换为基本数据类型?
2)如何将基本数据类型转换为字符串?
إجابة:
1)调用基本数据类型对应的包装类中的方法parseXXX(String)或valueOf(String)即可返回相应基本类型;
2)一种方法是将基本数据类型与空字符串(””)连接(+)即可获得其所对应的字符串;另一种方法是调用String 类中的valueOf(…)方法返回相应字符串
39、如何实现字符串的反转及替换?
答:方法很多,可以自己写实现也可以使用String或StringBuffer / StringBuilder中的方法。有一道很常见的面试题是用递归实现字符串反转,代码如下所示:
public static String reverse(String originStr) { if(originStr == null || originStr.length() <= 1) return originStr; return reverse(originStr.substring(1)) + originStr.charAt(0); } //何问起hovertree.com40、怎样将GB2312编码的字符串转换为ISO-8859-1编码的字符串?
答:代码如下所示:
String s1 = "你好";String s2 = newString(s1.getBytes("GB2312"), "ISO-8859-1");41、日期和时间:
1)如何取得年月日、小时分钟秒?
2)如何取得从1970年1月1日0时0分0秒到现在的毫秒数?
3)如何取得某月的最后一天?
4)如何格式化日期?
答:操作方法如下所示:
1)创建java.util.Calendar 实例,调用其get()方法传入不同的参数即可获得参数所对应的值
2)以下方法均可获得该毫秒数:
Calendar.getInstance().getTimeInMillis(); System.currentTimeMillis(); //何问起hovertree.com
3)示例代码如下:
Calendar time = Calendar.getInstance(); time.getActualMaximum(Calendar.DAY_OF_MONTH); //何问起hovertree.com
4)利用java.text.DataFormat 的子类(如SimpleDateFormat类)中的format(Date)方法可将日期格式化。
42、打印昨天的当前时刻。
إجابة:
public class YesterdayCurrent { public static void main(String[] args){ Calendar cal = Calendar.getInstance(); cal.add(Calendar.DATE, -1); System.out.println(cal.getTime()); } } //何问起hovertree.com43、比较一下Java 和JavaSciprt。
答: JavaScript 与Java是两个公司开发的不同的两个产品。Java 是原Sun 公司推出的面向对象的程序设计语言,特别适合于互联网应用程序开发;而JavaScript是Netscape公司的产品,为了扩展Netscape浏览器的功能而开发的一种可以嵌入Web页面中运行的基于对象和事件驱动的解释性语言,它的前身是LiveScript;而Java 的前身是Oak语言。
下面对两种语言间的异同作如下比较:
1)基于对象和面向对象:Java是一种真正的面向对象的语言,即使是开发简单的程序,必须设计对象;JavaScript是种脚本语言,它可以用来制作与网络无关的,与用户交互作用的复杂软件。它是一种基于对象(Object-Based)和事件驱动(Event-Driven)的编程语言。因而它本身提供了非常丰富的内部对象供设计人员使用;
2)解释和编译:Java 的源代码在执行之前,必须经过编译;JavaScript 是一种解释性编程语言,其源代码不需经过编译,由浏览器解释执行;
3)强类型变量和类型弱变量:Java采用强类型变量检查,即所有变量在编译之前必须作声明;JavaScript中变量声明,采用其弱类型。即变量在使用前不需作声明,而是解释器在运行时检查其数据类型;
4)代码格式不一样。
补充:上面列出的四点是原来所谓的标准答案中给出的。其实Java和JavaScript最重要的区别是一个是静态语言,一个是动态语言。目前的编程语言的发展趋势是函数式语言和动态语言。在Java中类(class)是一等公民,而JavaScript中函数(function)是一等公民。对于这种问题,在面试时还是用自己的语言回答会更加靠谱。
44、什么时候用assert?
答: assertion(断言)在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制。一般来说,assertion用于保证程序最基本、关键的正确性。assertion检查通常在开发和测试时开启。为了提高性能,在软件发布后, assertion检查通常是关闭的。在实现中,断言是一个包含布尔表达式的语句,在执行这个语句时假定该表达式为true;如果表达式计算为false,那么系统会报告一个AssertionError。
断言用于调试目的:
assert(a > 0); // throws an AssertionError if a <= 0
断言可以有两种形式:
assert Expression1;
assert Expression1 : Expression2 ;
Expression1 应该总是产生一个布尔值。
Expression2 可以是得出一个值的任意表达式;这个值用于生成显示更多调试信息的字符串消息。
断言在默认情况下是禁用的,要在编译时启用断言,需使用source 1.4 标记:
javac -source 1.4 Test.java
要在运行时启用断言,可使用-enableassertions 或者-ea 标记。
要在运行时选择禁用断言,可使用-da 或者-disableassertions 标记。
要在系统类中启用断言,可使用-esa 或者-dsa 标记。还可以在包的基础上启用或者禁用断言。可以在预计正常情况下不会到达的任何位置上放置断言。断言可以用于验证传递给私有方法的参数。不过,断言不应该用于验证传递给公有方法的参数,因为不管是否启用了断言,公有方法都必须检查其参数。不过,既可以在公有方法中,也可以在非公有方法中利用断言测试后置条件。另外,断言不应该以任何方式改变程序的状态。
45、Error 和Exception 有什么区别?
答: Error 表示系统级的错误和程序不必处理的异常,是恢复不是不可能但很困难的情况下的一种严重问题;比如内存溢出,不可能指望程序能处理这样的情况;Exception 表示需要捕捉或者需要程序进行处理的异常,是一种设计或实现问题;也就是说,它表示如果程序运行正常,从不会发生的情况。
Supplement: During the interview with Motorola in 2005, I asked a question like "If a process reports a stack overflow run-time error, what's the most possible cause?", giving four options a. lack of memory; b. write on an invalid memory space; c. recursive function calling; د. array index out of boundary. Java programs may also encounter StackOverflowError when running. This is an error that cannot be recovered, so I can only re-modify the code. The answer to this interview question is c. If you write recursion that cannot converge quickly, it is very likely to cause a stack overflow error, as shown below:
package com.lovo; public class StackOverflowErrorTest { public static void main(String[] args) { main(null); } } //何问起hovertree.com因此,用递归编写程序时一定要牢记两点:1. 递归公式;2. 收敛条件(什么时候就不再递归而是回溯了)。
46、try{}里有一个return语句,那么紧跟在这个try后的finally{}里的code会不会被执行,什么时候被执行,在return前还是后?
答:会执行,在方法返回调用者前执行。Java允许在finally中改变返回值的做法是不好的,因为如果存在finally代码块,try中的return语句不会立马返回调用者,而是记录下返回值待finally代码块执行完毕之后再向调用者返回其值,然后如果在finally中修改了返回值,这会对程序造成很大的困扰,C#中就从语法上规定不能做这样的事。
47、Java 语言如何进行异常处理,关键字:throws、throw、try、catch、finally分别如何使用?
答: Java 通过面向对象的方法进行异常处理,把各种不同的异常进行分类,并提供了良好的接口。在Java 中,每个异常都是一个对象,它是Throwable 类或其子类的实例。当一个方法出现异常后便抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法可以捕获到这个异常并进行处理。Java 的异常处理是通过5 个关键词来实现的:try、catch、throw、throws和finally。一般情况下是用try来执行一段程序,如果出现异常,系统会抛出(throw)一个异常,这时候你可以通过它的类型来捕捉(catch)它,或最后(finally)由缺省处理器来处理;try用来指定一块预防所有“异常”的程序;catch 子句紧跟在try块后面,用来指定你想要捕捉的“异常”的类型;throw 语句用来明确地抛出一个“异常”;throws用来标明一个成员函数可能抛出的各种“异常”;finally 为确保一段代码不管发生什么“异常”都被执行一段代码;可以在一个成员函数调用的外面写一个try语句,在这个成员函数内部写另一个try语句保护其他代码。每当遇到一个try 语句,“异常”的框架就放到栈上面,直到所有的try语句都完成。如果下一级的try语句没有对某种“异常”进行处理,栈就会展开,直到遇到有处理这种“异常”的try 语句。
48、运行时异常与受检异常有何异同?
答:异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误,只要程序设计得没有问题通常就不会发生。受检异常跟程序运行的上下文环境有关,即使程序设计无误,仍然可能因使用的问题而引发。Java编译器要求方法必须声明抛出可能发生的受检异常,但是并不要求必须声明抛出未被捕获的运行时异常。异常和继承一样,是面向对象程序设计中经常被滥用的东西,神作《Effective Java》中对异常的使用给出了以下指导原则:
不要将异常处理用于正常的控制流(设计良好的API不应该强迫它的调用者为了正常的控制流而使用异常)
对可以恢复的情况使用受检异常,对编程错误使用运行时异常避免不必要的使用受检异常(可以通过一些状态检测手段来避免异常的发生)
优先使用标准的异常每个方法抛出的异常都要有文档保持异常的原子性不要在catch中忽略掉捕获到的异常
49、列出一些你常见的运行时异常?
إجابة:
ArithmeticException(算术异常)
ClassCastException (类转换异常)
IllegalArgumentException (非法参数异常)
IndexOutOfBoundsException (下表越界异常)
NullPointerException (空指针异常)
SecurityException (安全异常)
50、final, finally, finalize 的区别?
答: final:修饰符(关键字)有三种用法:如果一个类被声明为final,意味着它不能再派生出新的子类,即不能被继承,因此它和abstract是反义词。将变量声明为final,可以保证它们在使用中不被改变,被声明为final 的变量必须在声明时给定初值,而在以后的引用中只能读取不可修改。被声明为final 的方法也同样只能使用,不能在子类中被重写。finally:通常放在try…catch的后面构造总是执行代码块,这就意味着程序无论正常执行还是发生异常,这里的代码只要JVM不关闭都能执行,可以将释放外部资源的代码写在finally块中。finalize:Object类中定义的方法,Java中允许使用finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在销毁对象时调用的,通过重写finalize() 方法可以整理系统资源或者执行其他清理工作。
ما سبق هو كل محتوى هذه المقالة. I hope it will be helpful to everyone in the Java interview, and I hope everyone will support Wulin.com more.