مقدمة
لقد قمت بمراجعته في مارس 2016 وأعيد النظر في سبب حاجة إلى تحسين الكود بناءً على عملي الخاص وتجربتي في التعلم اليومية. قبل أن أعول ، كان بياني هكذا:
تمامًا مثل الحوت الذي يأكل الجمبري ، ربما يكون تناول واحد أو اثنين من الجمبري ليس فعالًا للغاية بالنسبة للحوت ، ولكن إذا كنت تأكل الكثير من الروبيان ، فستكون الحيتان ممتلئة بشكل طبيعي. تحسين الكود هو نفسه. ربما لا يكون أحد أو التحسينات ذات الأهمية الضئيلة لتحسين كفاءة التشغيل للرمز ، ولكن طالما يمكنك الانتباه إلى تحسين الكود في كل مكان ، فهو مفيد بشكل عام لتحسين كفاءة تشغيل الكود.
هذا العرض ، في العرض الحالي ، هو سبب لتحسين الكود ، لكنه غير صحيح تمامًا. اليوم ، مع تطوير التكنولوجيا الميكانيكية ، غالبًا ما تحتوي الخوادم على 8 نوى و 16 نوى ووحدات المعالجة المركزية 64 بت وكفاءة تنفيذ التعليمات البرمجية مرتفعة للغاية. يستبدل StringBuilder StringBuffer و ArrayList يحل محل المتجه ، مما يحسن كفاءة تشغيل التعليمات البرمجية بواسطة Mineccule. حتى لو لاحظت كل نقطة في المشروع ، لا يمكنك رؤية أي تغييرات واضحة في عملية الكود.
أعتقد أن الدور الأكثر أهمية لتحسين التعليمات البرمجية يجب أن يكون: لتجنب أخطاء غير معروفة. خلال الكود الذي يعمل عبر الإنترنت ، غالبًا ما تحدث العديد من الأخطاء غير المتوقعة ، لأن البيئة عبر الإنترنت وبيئة التطوير مختلفة تمامًا ، وغالبًا ما يكون تحديد مواقع الأخطاء سببًا صغيرًا جدًا. ومع ذلك ، من أجل حل هذا الخطأ ، نحتاج إلى تحديد ذاتيًا أولاً ، ثم قم بتعبئة ملف الفئة المراد استبداله ، وتعليق الأعمال وإعادة التشغيل. بالنسبة لمشروع ناضج ، فإن الأخير له بالفعل تأثير كبير للغاية ، مما يعني أنه لا يمكن للمستخدمين الوصول إلى التطبيق خلال هذه الفترة. لذلك ، عند كتابة التعليمات البرمجية ، فإن الانتباه إلى التفاصيل المختلفة من المصدر ، ووزن واستخدام أفضل الخيارات سيتجنب بشكل كبير أخطاء غير معروفة ويقلل إلى حد كبير من عبء العمل على المدى الطويل.
أهداف تحسين الكود هي:
1. تقليل حجم الكود
2. تحسين كفاءة تشغيل الرمز
تأتي بعض محتويات هذه المقالة من الإنترنت ، والبعض الآخر يأتي من العمل اليومي والدراسة. بالطبع ، هذا ليس مهمًا. المهم هو ما إذا كانت تفاصيل تحسين الكود هذه مفيدة حقًا. ثم سيتم تحديث هذه المقالة لفترة طويلة. طالما تواجه تفاصيل تحسين الرمز الذي تستحق المشاركة ، سيتم تحديث هذه المقالة من وقت لآخر.
تفاصيل تحسين الكود
(1) حاول تحديد المعدل النهائي للفصل والطريقة قدر الإمكان
الفصول ذات المعدلات النهائية ليست قابلة للاشتقاق. في Java Core API ، هناك العديد من الأمثلة على تطبيق النهائي ، مثل java.lang.string ، والفئة بأكملها نهائية. يمكن أن يؤدي تحديد المعدل النهائي للفئة إلى منع الفصل بين الفئة ، وتحديد المعدل النهائي للطريقة يمكن أن يمنع تجاوز الطريقة. إذا تم تحديد الفصل على أنه نهائي ، فإن جميع أساليب تلك الفئة نهائية. سيبحث برنامج التحويل البرمجي Java عن فرص لتضمين جميع الأساليب النهائية. المضمون له أهمية كبيرة لتحسين كفاءة تشغيل جافا. للحصول على التفاصيل ، راجع تحسين وقت تشغيل Java. هذه الخطوة يمكن أن تحسن الأداء بمتوسط 50 ٪.
(2) حاول إعادة استخدام الكائنات
خاصة بالنسبة لاستخدام كائنات السلسلة ، يجب استخدام StringBuilder/StringBuffer بدلاً من ذلك عند حدوث سلسلة من السلسلة. نظرًا لأن أجهزة Java Virtual لا تحتاج فقط إلى قضاء بعض الوقت في توليد الكائنات ، فقد تحتاج أيضًا إلى قضاء وقت في جمع هذه الكائنات ومعالجتها في المستقبل ، فإن توليد الكثير من الكائنات سيكون له تأثير كبير على أداء البرنامج.
(3) استخدام المتغيرات المحلية قدر الإمكان
يتم تمرير المعلمات التي تم تمريرها عند استدعاء الطريقة والمتغيرات المؤقتة التي تم إنشاؤها في المكالمة على المكدس ، وهو أسرع. يتم إنشاء متغيرات أخرى ، مثل المتغيرات الثابتة ، ومتغيرات المثيل ، وما إلى ذلك ، في الكومة ، والتي تكون أبطأ. بالإضافة إلى ذلك ، مع انتهاء المتغيرات التي تم إنشاؤها في المكدس ، يتم اختفاء هذه المحتويات ولا يلزم وجود مجموعة إضافية من القمامة.
(4) أغلق التدفق في الوقت المناسب
أثناء برمجة Java ، كن حذرًا عند إجراء اتصال قاعدة البيانات وعمليات دفق الإدخال/الإخراج. بعد الاستخدام ، أغلقه في الوقت المناسب لإصدار الموارد. نظرًا لأن تشغيل هذه الكائنات الكبيرة سيؤدي إلى إدراج نظام كبير ، وإذا لم تكن حذراً ، فسيؤدي ذلك إلى عواقب وخيمة.
(5) تقليل الحسابات المتكررة للمتغيرات
لتوضيح مفهوم ما ، حتى لو كانت هناك جملة واحدة فقط في هذه الطريقة ، فلا يزال مستهلكًا ، بما في ذلك إنشاء إطارات المكدس ، وحماية الموقع عند استدعاء الطريقة ، واستعادة الموقع عند استدعاء الطريقة. على سبيل المثال ، العملية التالية:
لـ (int i = 0 ؛ i <list.size () ؛ i ++) {...}يوصى باستبداله بـ:
لـ (int i = 0 ، length = list.size () ؛ i <length ؛ i ++) {...}وبهذه الطريقة ، عندما تكون list.size () كبيرة جدًا ، فإنها تقلل من الكثير من الاستهلاك
(6) حاول تبني استراتيجية تحميل كسول ، أي أنشئها عند الحاجة
على سبيل المثال:
String str = "aaa" ؛ if (i == 1) {list.add (str) ؛}يوصى باستبداله بـ:
if (i == 1) {String str = "aaa" ؛ list.add (str) ؛}(7) استخدام تشوهات بحذر
الشذوذ ليست جيدة للأداء. لرمي استثناء ، يجب أولاً إنشاء كائن جديد. يستدعي مُنشئ الواجهة القابلة للتسمية طريقة التزامن المحلي المسماة FillInstackTrace (). تقوم طريقة FillInstackTrace () بالتحقق من المكدس وتجمع معلومات تتبع المكالمات. طالما تم طرح استثناء ، يجب على جهاز Java Virtual ضبط مكدس الاتصال لأنه يتم إنشاء كائن جديد أثناء المعالجة. لا يمكن استخدام الاستثناءات إلا لمعالجة الأخطاء ويجب عدم استخدامها للتحكم في تدفق البرنامج.
(8) لا تستخدم المحاولة ... الصيد ... في حلقة ، يجب وضعها على الطبقة الخارجية
وفقًا للآراء التي طرحها مستخدمي الإنترنت ، أعتقد أن هذا يستحق المناقشة
(9) إذا كان يمكن تقدير طول المحتوى المراد إضافته ، حدد الطول الأولي لفئات التجميع والأدوات المنفذة في صفيف على الطبقة الأساسية.
على سبيل المثال ، ArrayList ، LinkedLlist ، StringBuilder ، StringBuffer ، HashMap ، Hashset ، إلخ. خذ StringBuilder كمثال:
stringbuilder () // افتراضي تخصيص 16 حرفًا من Space StringBuilder (حجم int)
يمكن تعيين قدرتها على التهيئة من خلال مُنشئ الفصل (هنا نشير ليس فقط إلى StringBuilder أعلاه) ، والتي يمكن أن تحسن الأداء بشكل كبير. على سبيل المثال ، StringBuilder ، يمثل الطول عدد الأحرف التي يمكن أن يحافظ عليها StringBuilder الحالية. لأنه عندما يصل StringBuilder إلى الحد الأقصى لسعةه ، فإنه سيزيد من قدرته إلى مرتين ويضيف 2. كلما وصلت StringBuilder إلى أقصى قدرتها ، فسيتعين عليها إنشاء صفيف أحرف جديد ونسخ محتوى صفيف الأحرف القديم إلى صفيف الأحرف الجديد - هذه عملية تستهلك الأداء للغاية. فقط تخيل ، إذا كان بإمكانك تقدير أن 5000 حرف يتم تخزينها في صفيف الأحرف دون تحديد الطول ، فإن قوة 2 الأقرب إلى 5000 هي 4096 ، والمضاف إلى كل توسع بغض النظر عن 2 ، ثم:
استنادًا إلى 4096 ، تقدم بطلب للحصول على صفيفات أحرف بحجم 8194 ، والتي تضيف ما يصل إلى 12290 صفائف أحرف بحجم 12290 مرة واحدة. إذا تمكنت من تحديد صفائف أحرف بحجم 5000 في البداية ، فستوفر أكثر من ضعف المساحة لنسخ أحرف 4096 الأصلية في صفيف الأحرف الجديد.
هذا لا يهدر مساحة الذاكرة فحسب ، بل يقلل أيضًا من كفاءة تشغيل الكود. لذلك ، ليس من الخطأ تحديد قدرة تهيئة معقولة لفئات التجميع والأدوات المنفذة في الصفيف الأساسي ، والتي ستحقق نتائج فورية. ومع ذلك ، لاحظ أن مجموعات مثل hashmap التي يتم تنفيذها في المصفوفات + القوائم المرتبطة يجب ألا تحدد الحجم الأولي بنفس الحجم المقدر الخاص بك ، لأن إمكانية كائن واحد فقط متصل بجدول ما هو ما يقرب من 0. يوصى بتعيين الحجم الأولي على طاقة n من 2.
(10) عند نسخ كمية كبيرة من البيانات ، استخدم الأمر system.arraycopy ()
(11) الضرب والقسمة استخدام عمليات التحول
على سبيل المثال:
لـ (val = 0 ؛ val <100000 ؛ val += 5) {a = val * 8 ؛ ب = فال / 2 ؛}يمكن أن يؤدي استخدام عمليات التحول إلى تحسين الأداء بشكل كبير ، لأنه في أسفل الكمبيوتر ، تكون عملية تحديد المواقع هي الأكثر ملاءمة وأسرع ، لذلك يوصى بتعديله إلى:
لـ (val = 0 ؛ val <100000 ؛ val += 5) {a = val << 3 ؛ ب = فال >> 1 ؛}على الرغم من أن عملية التحول سريعة ، إلا أنها قد تجعل من الصعب فهم الكود ، لذلك من الأفضل إضافة التعليقات المقابلة.
(12) لا تنشئ باستمرار مراجع كائن في الحلقة
على سبيل المثال:
لـ (int i = 1 ؛ i <= count ؛ i ++) {object obj = new Object () ؛ }سيؤدي هذا النهج إلى وجود مرجع كائن الكونت في الذاكرة. إذا كان العدد كبيرًا ، فسوف يستهلك الذاكرة. يوصى بتغييره إلى:
Object obj = null ؛ for (int i = 0 ؛ i <= count ؛ i ++) {obj = new Object () ؛}وبهذه الطريقة ، هناك مرجع كائن واحد فقط في الذاكرة. في كل مرة يتم فيها استخدام كائن جديد () ، يشير مرجع كائن الكائن إلى كائن مختلف ، ولكن لا يوجد سوى كائن واحد في الذاكرة ، والذي يحفظ مساحة الذاكرة بشكل كبير.
(13) بناءً على النظر في الكفاءة والتحقق من النوع ، يجب استخدام الصفيف قدر الإمكان. يجب استخدام ArrayList فقط إذا كان لا يمكن تحديد حجم الصفيف.
(14) حاول استخدام HashMap و ArrayList و StringBuilder. ما لم يكن ضروريًا لسلامة مؤشرات الترابط ، لا ينصح باستخدام علامة التجزئة ، المتجه ، و stringBuffer. الثلاثة الأخيرة لها النفقات العامة للأداء بسبب استخدام آليات التزامن.
(15) لا تعلن المصفوفات كنهائي ثابت عام
نظرًا لأن هذا لا معنى له ، فإنه يعرّف فقط المرجع على أنه نهائي ثابت ، ولا يزال من الممكن تغيير محتوى الصفيف حسب الرغبة. إن إعلان الصفيف باعتباره عامًا هو ثغرة أمنية ، مما يعني أنه يمكن تغيير الصفيف بواسطة فئات خارجية
(16) حاول استخدام حالات واحدة في مناسبات مناسبة
يمكن أن يؤدي استخدام المفردات إلى تقليل عبء الحمل ، وتقصير وقت التحميل ، وتحسين كفاءة التحميل ، ولكن ليست جميع الأماكن مناسبة للفرد. ببساطة ، يتم تطبيق المفردات بشكل أساسي على الجوانب الثلاثة التالية:
التحكم في استخدام الموارد ، والتحكم في توليد مثيلات التحكم في الوصول المتزامنة من خلال مزامنة الخيط ، وذلك لتحقيق الغرض من توفير الموارد للتحكم في مشاركة البيانات ، وتمكين التواصل بين عمليات أو مؤشرات ترابط غير ذات صلة متعددة دون إنشاء ارتباطات مباشرة.
(17) حاول تجنب استخدام المتغيرات الثابتة في الإرادة
يجب أن تعلم أنه عندما يتم الرجوع إلى كائن بواسطة متغير محدد على أنه ثابت ، فإن GC عادة لا يعيد تدوير ذاكرة الكومة التي يشغلها الكائن ، مثل:
الفئة العامة a {private static b b = new b () ؛ }في هذا الوقت ، فإن دورة حياة المتغير الثابت B هي نفس دورة الفئة A A. إذا لم يتم إلغاء تثبيت الفئة A ، فسيتم الإشارة إلى الكائن B بالمرجع B في الذاكرة حتى ينتهي البرنامج
(18) امسح الجلسات التي لم تعد مطلوبة في الوقت المناسب
من أجل مسح الجلسات التي لم تعد نشطة ، تحتوي العديد من خوادم التطبيق على مهلة جلسة افتراضية ، عادة 30 دقيقة. عندما يحتاج خادم التطبيق إلى حفظ المزيد من الجلسات ، إذا لم تكن هناك ذاكرة كافية ، فسيقوم نظام التشغيل بنقل جزء من البيانات إلى القرص. قد يقوم خادم التطبيق أيضًا بتفريغ بعض الجلسات غير النشطة على القرص وفقًا لخوارزمية MRU (في أغلب الأحيان المستخدمة مؤخرًا) ، وقد يرمي استثناءات ذاكرة غير كافية. إذا تم إلقاء الجلسة على القرص ، فيجب أن يتم تسلسلها أولاً. في مجموعات واسعة النطاق ، فإن الأشياء التسلسلية غالية الثمن. لذلك ، عندما لم تعد الجلسة مطلوبة ، يجب استدعاء طريقة perilidate () من HTTPSESSINT في الوقت المناسب لمسح الجلسة.
(19) يجب أن تستخدم المجموعة التي تنفذ واجهة RandomAccess ، مثل ArrayList
ينصح هذا من قبل JDK للمستخدمين. إن شرح JDK API لواجهة RandomAccess هو: تطبيق واجهة RandomAccess يستخدم للإشارة إلى أنه يدعم الوصول العشوائي السريع. الغرض الرئيسي من هذه الواجهة هو السماح للخوارزميات العامة بتغيير سلوكها ، بحيث يمكن أن توفر أداءً جيدًا عند تطبيقه على قوائم الوصول العشوائية أو المستمرة. توضح التجربة العملية أنه إذا تم الوصول إلى مثيلات الفصل التي تنفذ واجهة RandomAccess بشكل عشوائي ، فستكون كفاءة استخدام الحلقات العادية أعلى من استخدام حلقات Foreach ؛ على العكس ، إذا تم الوصول إليها بالتتابع ، فسيكون من أكثر كفاءة استخدام Iterator. يمكنك استخدام رموز مشابهة لما يلي لإصدار الأحكام:
if (list almateof randomaccess) {for (int i = 0 ؛ i <list.size () ؛ i ++) {}} آخر {iterator <؟> iterator = list.iterable () ؛ بينما (iterator.hasnext ()) {iterator.next ()}}مبدأ التنفيذ الأساسي في حلقة Foreach هو التكرار ، انظر Java Syntax Sugar 1: معلمات الطول المتغيرة ومبدأ حلقة Foreach. لذا فإن النصف الثاني من الجملة "على العكس ، إذا تم الوصول إليه بالتتابع ، فإن استخدام ITERATOR سيكون أكثر كفاءة" يعني أن مثيلات الفصل التي يتم الوصول إليها متتالية باستخدام حلقة foreach.
(20) استخدم كتل التعليمات البرمجية المتزامنة بدلاً من طريقة التزامن
تم ذكر هذا بالفعل بوضوح في كتلة طريقة القفل المتزامنة في الوحدة النمطية متعددة الخيوط. ما لم يكن من الممكن تحديد أن الطريقة بأكملها تحتاج إلى مزامنة ، فحاول استخدام كتل التعليمات البرمجية المتزامنة لتجنب مزامنة تلك الرموز التي لا تحتاج إلى مزامنة ، مما يؤثر على كفاءة تنفيذ الكود.
(21) إعلان الثابت باعتباره نهائيًا ثابتًا واسمه في رأس المال
وبهذه الطريقة ، يمكن وضع هذه المحتويات في التجمع الثابت أثناء التجميع ، وتجنب حساب القيم الثابتة التي تم إنشاؤها أثناء وقت التشغيل. بالإضافة إلى ذلك ، فإن تسمية اسم ثابت في رأس المال يمكن أن يسهل التمييز بين الثوابت والمتغيرات
(22) لا تنشئ بعض الكائنات غير المستخدمة ، لا تستورد بعض الفصول غير المستخدمة
هذا لا معنى له. إذا لم يتم استخدام "قيمة المتغير المحلي أنا غير مستخدم" و "الاستيراد java.util لا يتم استخدامه أبدًا"
(23) تجنب استخدام التفكير أثناء تشغيل البرنامج
لمزيد من المعلومات ، راجع التفكير. الانعكاس هو وظيفة قوية للغاية توفرها Java للمستخدمين. وظائف قوية غالبا ما تعني كفاءة منخفضة. لا ينصح باستخدام آلية الانعكاس بشكل متكرر أثناء تشغيل البرنامج ، وخاصة طريقة استدعاء الطريقة. إذا كان ذلك ضروريًا بالفعل ، فإن النهج الموحي هو إنشاء مثيل لكائن من خلال التفكير ووضعه في الذاكرة عند بدء تشغيل المشروع. يهتم المستخدم فقط بأسرع سرعة استجابة عند التفاعل مع النظير ، ولا يهتم بالمدة التي يستغرقها مشروع الأقران.
(24) استخدم تجمع اتصالات قاعدة البيانات ومجموعة مؤشرات الترابط
يتم استخدام كلا التجمعين لإعادة استخدام الكائنات ، وتجنب الأول فتح وإغلاق التوصيلات المتكررة ، ويتجنب الأخير خلق وتدمير الخيوط
(25) استخدم تدفقات الإدخال والإخراج المخزنة لعمليات IO
تدفقات الإدخال والإخراج المخزنة ، وهي BufferredReader ، و CupheredWriter ، و BufferedInputStream ، و BufferedOutputStream ، والتي يمكن أن تحسن بشكل كبير من كفاءة IO
(26) استخدم ArrayList للمشاهد مع المزيد من الإدراج المتسلسل والوصول العشوائي ، واستخدم LinkedList للمشاهد مع المزيد من حذف العناصر والإدراج الوسيط.
هذا معروف من خلال فهم مبادئ arraylist و LinkedList
(27) لا تدع الكثير من المعلمات الرسمية في الطريقة العامة
الطريقة العامة هي طريقة مقدمة للعالم الخارجي. إذا أعطيت هذه الطرق الكثير من المعلمات الرسمية ، فهناك عيوبان رئيسيتان:
انتهاك فكرة البرمجة الموجهة نحو الكائن. تؤكد جافا على أن كل شيء هو كائن. الكثير من المعلمات الرسمية ولا يتطابق مع فكرة البرمجة الموجهة للكائن. الكثير من المعلمات سوف تؤدي حتما إلى زيادة في احتمال الخطأ في مكالمات الطريقة.
أما بالنسبة لعدد "عدد كبير جدًا" يشير إلى ، 3 أو 4. على سبيل المثال ، نستخدم JDBC لكتابة طريقة insertStudentInfo. هناك 10 حقول معلومات للطلاب يتم إدراجها في طاولة الطلاب. يمكن تغليف هذه المعلمات العشرة في فئة الكيان كمعلمات رسمية لطريقة إدراج.
(28) عندما تتم كتابة متغيرات السلسلة وثوابت السلسلة المكتوبة أمام ثوابت السلسلة
هذه خدعة شائعة نسبيًا ، إذا كان هناك الرمز التالي:
String str = "123" ؛ if (str.equals ("123")) {...}يوصى بتعديله إلى:
String str = "123" ؛ if ("123" .equals (str)) {...}هذا يمكن أن يتجنب بشكل أساسي استثناءات المؤشر الفارغ
(29) يرجى العلم أنه في Java لا يوجد فرق بين إذا (i == 1) وإذا (1 == i) ، ولكن من منظور عادات القراءة ، يوصى باستخدام السابق
في بعض الأحيان يسأل الناس ما إذا كان هناك أي فرق بين "if (i == 1)" و "if (1 == i)"؟ هذا يبدأ بـ C/C ++.
في C/C ++ ، فإن شرط الحكم "if (i == 1)" صالح ، ويستند إلى 0 و 0. 0 يعني خطأ ، وغير 0 يعني صحيح. إذا كان هناك مثل هذا الرمز:
int i = 2 ؛ if (i == 1) {...} آخر {...}قضاة C/C ++ بأن "i == 1" غير صالح ، لذلك يتم تمثيله بواسطة 0 ، أي خطأ. ولكن إذا:
int i = 2 ؛ if (i = 1) {...} آخر {...}إذا كتب المبرمج بطريق الخطأ "إذا (i == 1)" كـ "if (i = 1)" ، فستكون هناك مشكلة. تعيين أنا إلى 1 في الداخل. إذا حددت أن المحتوى الموجود فيه ليس 0 ، فهو صحيح ، لكن من الواضح أنني 2 ، والقيمة المقارنة هي 1 ، الخاطئة التي يجب إرجاعها. من المحتمل جدًا أن يحدث هذا الموقف في تطوير C/C ++ وسيؤدي إلى بعض الأخطاء التي يصعب فهمها. لذلك ، من أجل تجنب عمليات التخصيص غير الصحيحة للمطورين في البيانات ، يوصى بكتابة البيان IF على النحو التالي:
int i = 2 ؛ if (1 == i) {...} آخر {...}وبهذه الطريقة ، حتى إذا كتب المطور بطريق الخطأ "1 = i" ، يمكن لمجمول C/C ++ التحقق من ذلك في أقرب وقت ممكن ، لأنه يمكننا تعيين قيمة متغيرة إلى 1 ، لكن لا يمكننا تعيين قيمة ثابتة 1 إلى i.
ومع ذلك ، في Java ، من المستحيل ظهور "if (i = 1)" "بناء جملة C/C ++ ، لأنه بمجرد كتابة هذا الجملة ، ستجمع Java والإبلاغ عن خطأ" غير متطابق: لا يمكن التحويل من int إلى Boolean ". ومع ذلك ، على الرغم من عدم وجود فرق دلالي بين "if (i == 1)" و "if (1 == i)" في Java ، سيكون من الأفضل التوصية باستخدام السابق من حيث عادات القراءة.
(30) لا تستخدم طريقة tostring () على الصفيف
دعونا نلقي نظرة على ما هو مطبوع باستخدام ToString () للمصفوفات:
int i = 2 ؛ if (1 == i) {...} آخر {...}تحول:
أنا@18a992f
القصد الأصلي هو طباعة محتويات الصفيف ، ولكن قد يتسبب في استثناء مؤشر فارغ لأن مرجع الصفيف فارغ. ومع ذلك ، على الرغم من أنه ليس من المنطقي أن تتم طباعة المحتويات في المجموعة لمجموعة TOSTRING () ، لأن الفئة الأم للمجموعة ، تتم إعادة كتابة طريقة TOSTRING () للكائن.
(31) لا تقوم بتحويلات القوة الهابطة على أنواع البيانات الأساسية خارج النطاق
هذا لن يحصل أبدًا على النتيجة المرجوة:
public static void main (string [] args) {long l = 12345678901234l ؛ int i = (int) l ؛ system.out.println (i) ؛}قد نتوقع الحصول على بعضهم ، لكن النتيجة هي:
1942892530
اشرح ذلك. طويل في Java هو 8 بايت مع 64 بت ، لذلك يجب أن يكون تمثيل 12345678901234 في الكمبيوتر:
0000 0000 0000 0000 0000 1011 0011 1010 0111 0011 1100 1110 0010 1111 1111 0010
بيانات النوع int هي 4 بايت و 32 بت. أول 32 بت من السلسلة أعلاه من البيانات الثنائية مأخوذة من بتات منخفضة:
0111 0011 1100 1110 0010 1111 1111 0010
يتم تمثيل هذه السلسلة من الثنائية على أنها عشرية 1942892530 ، لذلك فهي إخراج المحتوى على وحدة التحكم أعلاه. من هذا المثال ، يمكننا رسم استنتاجين:
1. نوع البيانات الافتراضية لنوع عدد صحيح هو int ، Long L = 12345678901234L. لقد تجاوز هذا الرقم نطاق int ، لذلك هناك L في النهاية ، مما يشير إلى أن هذا هو رقم نوع طويل. بالمناسبة ، يكون النوع الافتراضي لنوع النقطة العائمة مزدوجًا ، لذلك عند تحديد العائمة ، يجب كتابته باسم "Float f = 3.5f"
2. بعد ذلك ، اكتب جملة أخرى "int ii = l + i ؛" وسيقوم بالإبلاغ عن خطأ لأن Long + Int طويل ولا يمكن تعيينه إلى int
(32) يجب إزالة البيانات غير المستخدمة في فئة التجميع العام في الوقت المناسب
إذا كانت فئة التجميع عامة (أي ، فهي ليست خاصية في طريقة) ، فلن يتم إصدار العناصر في هذه المجموعة تلقائيًا لأن هناك دائمًا إشارات تشير إليها. لذلك ، إذا لم يتم استخدام بيانات معينة في المجموعة العامة ولم تتم إزالتها ، فسوف يتسبب ذلك في نمو المجموعة العامة ، مما يؤدي إلى أن يكون للنظام إمكانية تسرب الذاكرة.
(33) تحويل نوع البيانات الأساسية إلى سلسلة. نوع البيانات الأساسي. toString () هو أسرع طريقة ، تليها string.valueof (البيانات) ، والبيانات +"" هي أبطأ
هناك عمومًا ثلاث طرق لتحويل نوع البيانات الأساسي إلى. لدي بيانات نوع عدد صحيح ، أي tostring () ، string.valueof (i) ، و i+"" ثلاث طرق. ما مدى كفاءة الطرق الثلاث؟ انظر الاختبار:
الفراغ الثابت العام (سلسلة [] args) {int looptime = 50000 ؛ عدد صحيح i = 0 ؛ وقت بدء طويل = system.currentTimeMillis () ؛ لـ (int j = 0 ؛ j <looptime ؛ j ++) {string str = string.valueof (i) ؛ } system.out.println ("string.valueof ():" + (System.CurrentTimeMillis () - StartTime) + "MS") ؛ startTime = system.currentTimeMillis () ؛ لـ (int j = 0 ؛ j <looptime ؛ j ++) {string str = i.toString () ؛ } system.out.println ("Integer.ToString ():" + (System.CurrentTimeMillis () - StartTime) + "MS") ؛ startTime = system.currentTimeMillis () ؛ لـ (int j = 0 ؛ j <looptime ؛ j ++) {string str = i+"" ؛ } system.out.println ("i + /" /":النتيجة الجارية هي:
string.valueof (): 11msinteger.toString (): 5ms + "": 25ms
لذلك ، عندما تقوم بتحويل نوع بيانات أساسي إلى سلسلة في المستقبل ، يجب أن تعطي الأولوية لاستخدام طريقة ToString (). أما السبب ، الأمر بسيط للغاية:
تسمى طريقة String.valueOf() طريقة Integer.toString() ، ولكن سيكون من المستحسن الحكم على طريقة Integer.toString() قبل الاتصال. سأتصل بـ i + "" تستخدم الطبقة السفلية تطبيق StringBuilder. استخدم أولاً طريقة إلحاق الصقور ، ثم استخدم طريقة ToString () للحصول على السلسلة.
بالمقارنة مع الثلاثة ، من الواضح أنه الأسرع والأسرع والأبطأ
(34) استخدم الطريقة الأكثر فعالية لاجتياز الخريطة
هناك العديد من الطرق لاجتياز الخريطة. عادة ، ما نحتاج إلى اجتياز المفتاح والقيمة في الخريطة. الطريقة الموصى بها والأكثر كفاءة هي:
Main Static void Main (String [] args) {hashMap <string ، string> hm = new hashmap <string ، string> () ؛ HM.Put ("111" ، "222") ؛ SET <map.entry <string ، string >> intplesset = hm.entryset () ؛ iterator <map.entry <string ، string >> iter = entrySet.iterator () ؛ بينما (iter.hasnext ()) {map.entry <string ، string> entry = iter.next () ؛ system.out.println (entry.getKey () + "/t" + enter.getValue ()) ؛ }} إذا كنت ترغب فقط في التكرار على القيمة الرئيسية لهذه الخريطة ، فسيكون ذلك أكثر ملاءمة لاستخدام " Set<String> keySet = hm.keySet();"
(35) يوصى بالعمل بشكل منفصل للموارد () من الموارد
هذا يعني ، على سبيل المثال ، لدي قطعة من الكود مثل هذا:
حاول {xxx.close () ؛ yyy.close () ؛} catch (استثناء e) {...}يوصى بتعديله إلى:
جرب {xxx.close () ؛} catch (استثناء e) {...} جرب {yyy.close () ؛} catch (استثناء e) {...}على الرغم من أنها مزعجة بعض الشيء ، إلا أنها يمكن أن تتجنب تسرب الموارد. نعتقد أنه إذا لم يكن هناك رمز معدّل ، إذا كان xxx.close () يلقي استثناء ، فسيؤدي ذلك إلى إدخال كتلة الصيد. لن يتم تنفيذ yyy.close () ، ولن يتم إعادة تدوير مورد YYY وسيتم احتلاله طوال الوقت. مع المزيد من الرموز مثل هذا ، قد يتسبب ذلك في تسرب مقبض الموارد. بعد التغيير إلى طريقة الكتابة التالية ، يضمن إغلاق XXX و YYY بغض النظر عن السبب.
(36) بالنسبة إلى threadlocal ، يجب إزالتك قبل أو بعد الاستخدام
حاليًا ، تستخدم جميع المشاريع تقنية تجميع الخيوط ، وهي جيدة جدًا. يمكنك تكوين عدد مؤشرات الترابط وإعادة استخدام مؤشرات الترابط ديناميكيًا.
ومع ذلك ، إذا كنت تستخدم ThreadLocal في مشروعك ، تذكر إزالته قبل الاستخدام أو بعده. وذلك لأن تقنية مجموعة مؤشرات الترابط المذكورة أعلاه هي إعادة استخدام مؤشر ترابط ، مما يعني أنه أثناء تشغيل الكود ، لن يتم تدمير مؤشر الترابط وسوف ينتظر الاستخدام التالي. دعنا نلقي نظرة على المرجع في فئة الخيوط التي تحمل threadlocal.throadlocalmap:
/* قيم ThreadLocal المتعلقة بهذا الموضوع. يتم الحفاظ على هذه الخريطة * بواسطة فئة threadlocal. */threadlocal.throadlocalmap threadlocals = null ؛
لا يتم تدمير مؤشر الترابط يعني أن البيانات الموجودة في threadlocal.throadlocalmap لا تزال موجودة. عندما يعيد مؤشر الترابط التالي استخدام هذا الموضوع ، فمن المحتمل أن يكون ما تحصل عليه هو بيانات مجموعة مؤشرات الترابط السابقة بدلاً من المحتوى الذي تريده.
هذه المشكلة غامضة للغاية. بمجرد حدوث الأخطاء الناجمة عن هذا السبب ، من الصعب للغاية العثور على هذه المشكلة دون خبرة ذات صلة أو أساس متين. لذلك ، يجب عليك الانتباه إلى هذا عند كتابة الرمز ، مما سيقلل من عبء العمل اللاحق.
(37) تذكر أن تحل محل رقم الشيطان بتعريف ثابت. إن وجود عدد الشيطان سيقلل بشكل كبير من قابلية قراءة الكود. ما إذا كانت ثوابت السلسلة تستخدم تعريفات ثابتة يمكن أن تعتمد على الموقف.
(38) عندما يكون التخصيص الأولي لفترة طويلة أو طويلة ، استخدم Lastcase L بدلاً من Lugrase L ، لأن الحرف L سهل الخلط مع الرقم 1 ، هذه النقطة مفصلة للغاية وتستحق الإشارة
(39) يجب أن تحتفظ جميع أساليب إعادة الكتابة بشروح Override
هناك ثلاثة أسباب للقيام بذلك:
(1) من الواضح أن هذه الطريقة موروثة من فئة الأصل
(2) طرق getObject () و get0bject (). الحرف الرابع من السابق هو "O" ، والوالد الرابع وطفل الأخير هو "0". يمكن لإضافة تعليق توضيحي Override تحديد ما إذا كانت إعادة الكتابة ناجحة.
(3) تعديل توقيع الطريقة في الفئة التجريدية ، وسوف تقوم فئة التنفيذ بالإبلاغ على الفور عن خطأ في التجميع.
(40) يوصى باستخدام فئة أدوات الكائنات التي تم تقديمها حديثًا في JDK7 لمقارنة الكائنات المساواة ، مباشرة A.Aquals (B) ، وهناك خطر استثناءات مؤشر فارغة.
(41) لا تستخدم "+" لصق السلاسل في جسم الحلقة ، ولكن استخدم StringBuilder للتلف بشكل مستمر
اسمحوا لي أن أتحدث عن سبب عدم استخدام "+" لربط السلسلة. إذا كان لدي طريقة:
public string appendstr (سلسلة oristr ، string ... appendstrs) {if (appendstrs == null || appendststrs.length == 0) {return oristr ؛ } لـ (string appendstr: appendstrs) {oristr += appendstr ؛ } return oristr ؛}بعد تجميع هذا الرمز ، قم بإلغاء توحيد ملف .class باستخدام Javap -C لاعتراض الجزء الرئيسي:
وهذا يعني أنه في كل مرة يواجه فيها الجهاز الظاهري مشغل "+" لصق السلسلة ، فإنه سيحدد سلسلة stringBuilder ، ثم اتصل بأسلوب الإلحاح ، وأخيراً استدعاء طريقة ToString () لتحويل السلسلة إلى كائن Oristr. وهذا هو ، كم مرة سوف الحلقة جديدة stringbuilder () ، وهو مضيعة للذاكرة.
(42) لا تلتقط فئة استثناء وقت التشغيل المحددة في مكتبة فئة Java الموروثة من RunTimeException
كفاءة معالجة الاستثناءات منخفضة ، يمكن تجنب معظم فئات استثناءات وقت التشغيل من RunTimeException تمامًا من قبل المبرمجين ، مثل:
(43) تجنب استخدام مثيلات عشوائية بواسطة مؤشرات ترابط متعددة. على الرغم من أن مشاركة المثيل آمن في مؤشر الترابط ، إلا أنه سيؤدي إلى تدهور الأداء بسبب المنافسة على نفس البذور. بعد JDK7 ، يمكنك استخدام ThreadLocalRandom للحصول على أرقام عشوائية
اشرح السبب في أن المنافسة على نفس البذور تسبب تدهور الأداء. على سبيل المثال ، ألقِ نظرة على تنفيذ طريقة NextInt () للفئة العشوائية:
public int nextint () {return next (32) ؛ }تسمى الطريقة التالية (البتات الداخلية) ، وهي طريقة محمية:
محمي int next (int bits) {long oldseed ، nextSeed ؛ بذرة Atomiclong = this.seed ؛ do {oldseed = seed.get () ؛ NextSeed = (Oldseed * Multiplier + Addend) & Mask ؛ } بينما (! seed.compareanset (Oldseed ، nextSeed)) ؛ العودة (int) (NextSeed >>> (48 - بت)) ؛}والبذرة هنا متغير عالمي:
/*** الحالة الداخلية المرتبطة بمولد الأرقام الزائفة هذا. * (تصف المواصفات الخاصة بالطرق في هذه الفئة الحوسبة المستمرة لهذه القيمة.) */ بذور Atomiclong النهائية الخاصة ؛
عندما تحصل خيوط متعددة على أرقام عشوائية في نفس الوقت ، فإنها ستتنافس على نفس البذور ، مما يؤدي إلى انخفاض في الكفاءة.
(44) فصول ثابتة ، فصول مفردة ، ودروس المصنع تضع بنائهم على القطاع الخاص
هذا لأننا لسنا بحاجة إلى تحديدهم في الخارج. بعد تعيين المُنشئ إلى القطاع الخاص ، نتأكد من أن هذه الفئات لن تنتج كائنات مثيل.
PostScript
رمز ممتاز يأتي من كل التحسين الصغير. إن الانتباه إلى كل التفاصيل لا يمكن فقط تحسين كفاءة تشغيل البرنامج ، ولكن أيضًا تجنب العديد من المشكلات غير المعروفة.
ما سبق هو 44 اقتراحات تحسين رمز Java المقدمة من المحرر. آمل أن يكون ذلك مفيدًا لك. إذا كان لديك أي أسئلة ، فيرجى ترك رسالة لي وسوف يرد المحرر إليك في الوقت المناسب. شكرا جزيلا لدعمكم لموقع wulin.com!