في برمجة Java ، لا يمكن تعلم بعض المعرفة فقط من خلال مواصفات اللغة أو وثائق API القياسية. في هذه المقالة ، سأحاول جمع بعض التعابير الأكثر استخدامًا ، وخاصة تلك التي يصعب تخمينها.
أضع كل المدونة في هذه المقالة في الأماكن العامة. يمكنك نسخ وتعديل أي مقتطف رمز وفقًا لتفضيلاتك ، دون أي بيانات اعتماد.
تنفيذ متساوي ()
فئة شخص {اسم السلسلة ؛ int عيد ميلاد بايت [] الخام ؛ منطقية عامة تساوي (كائن obj) {if (! obj مثيل الشخص) إرجاع خطأ ؛ شخص آخر = (شخص) OBJ ؛ return name.equals (other.name) && pirthyyear == Other.Birthyear && arrays.equals (Raw ، other.raw) ؛ } public int hashcode () {...}} يجب أن تكون المعلمة من نوع كائن ، وليس من فئة المحيط.
يجب أن يعود foo.equals (NULL) خطأ ولا يمكن أن يرمي nullpointerxception. (لاحظ أن null extryof من أي فئة تُرجع دائمًا false ، بحيث يمكن تشغيل الرمز أعلاه.)
يتم استخدام مقارنة مجالات النوع الأساسية (على سبيل المثال ، int) == ، ويتم استخدام مقارنة مجالات صفيف النوع الأساسية بواسطة المصفوفات.
عندما تساوي الكتابة فوق () ، تذكر أن تكتب Hashcode () وفقًا لذلك ، لتكون متسقة مع متساوين ().
المرجع: java.lang.object.equals (كائن).
تنفيذ hashcode ()
الفئة شخص {string a ؛ الكائن ب ؛ بايت ج ؛ int [] d ؛ public int hashcode () {return A.HashCode () + B.HashCode () + c + arrays.hashCode (d) ؛ } منطقية عامة متساوية (كائن O) {...}} عندما يكون للكائنات x و y x.equals (y) == true ، يجب عليك التأكد من x.hashCode () == y.hashcode ().
وفقًا للاقتراح العكسي ، إذا كان X.HashCode ()! = Y.HashCode () ، ثم x.equals (y) == يجب أن يكون خطأ.
لا تحتاج إلى ضمان أن X.HashCode ()! = Y.HashCode () عندما X.equals (y) == false. ومع ذلك ، فإن هذا يحسن أداء جدول التجزئة إذا كنت تستطيع أن تجعله أطول فترة ممكنة.
أبسط تطبيق قانوني لـ HashCode () هو ببساطة إرجاع 0 ؛ على الرغم من أن هذا التنفيذ صحيح ، فإن هذا سيؤدي إلى تشغيل هياكل البيانات مثل HashMap ببطء شديد.
تنفيذ المقارنة ()
شخص الفئة ينفذ <profern> {String FirstName ؛ سلسلة العائلة ؛ عيد ميلاد // قارن بواسطة FirstName ، Break Ries by LastName ، أخيراً ، كسر العلاقات من قبل PrinctDate Public int compareto (الشخص الآخر) {if (firstName.compareto (other.firstName)! = 0) return firstname.compareto (other.firstName) ؛ آخر if (lastName.compareto (other.lastname)! = 0) return lastName.comPareto (other.lastname) ؛ وإلا وإلا عودة أخرى 0 ؛ }} قم دائمًا بتنفيذ الإصدار العام مقارنةً بدلا من النوع البدائي. لأن هذا يمكن أن يحفظ حجم الرمز ويقلل من المتاعب غير الضرورية.
ما عليك سوى الاهتمام بالعلامات (السلبية/الصفر/الإيجابية) التي تُرجع النتيجة ، لا يهم حجمها.
تشبه تنفيذ Comparator.compare () هذا واحد.
تنفيذ clone ()
قيم الفئة تنفذ استنساخ {string abc ؛ مزدوج فو int [] أشرطة ؛ تاريخ التعاقد ؛ القيم العامة clone () {try {dorder result = (values) super.clone () ؛ result.bars = result.bars.clone () ؛ result.hired = result.hired.clone () ؛ نتيجة العودة } catch (clonenotsupportedException e) {// مستحيل رمي تأكيد جديد (e) ؛ }}} استخدم super.clone () لجعل فئة الكائن مسؤولة عن إنشاء كائنات جديدة.
تم نسخ مجالات النوع الأساسية بشكل صحيح. مرة أخرى ، لا نحتاج إلى استنساخ أنواع غير قابلة للتغيير مثل String و BigInteger.
أداء يدويًا نسخًا عميقًا لجميع حقول الأنواع غير المناسبة (الكائنات والمصفوفات).
يتم تنفيذ الفئة المستنسخة ، ويجب ألا ترمي طريقة clone () أبدًا clonenotsupportedException. لذلك ، تحتاج إلى التقاط هذا الاستثناء وتجاهله ، أو لفه باستثناء غير محدد.
لا بأس وقانوني في تنفيذ طريقة clone () يدويًا دون استخدام طريقة الكائن.
استخدم StringBuilder أو StringBuffer
// join (["A" ، "B" ، "C"]) -> "A و B و C" السلسلة Join (List <String> strs) {StringBuilder sb = new StringBuilder () ؛ منطقية أولاً = صواب ؛ لـ (String s: strs) {if (first) first = false ؛ آخر sb.append ("و") ؛ sb.append (s) ؛ } إرجاع sb.tostring () ؛} لا تستخدم تسلسل السلسلة المكررة مثل هذا: S += العنصر ، لأن كفاءة الوقت هي O (N^2).
عند استخدام StringBuilder أو StringBuffer ، يمكنك استخدام طريقة إلحاق () لإضافة نص واستخدام طريقة ToString () للحصول على النص بالكامل المتصل.
يتم إعطاء الأولوية إلى StringBuilder لأنها أسرع. تتم مزامنة جميع طرق StringBuffer ، وعادة ما لا تحتاج إلى طريقة متزامنة.
توليد أعداد صحيحة عشوائية في نطاق
Random Rand = New Random () ؛ // بين 1 و 6 ، بما في ذلك Diceroll () {retroom Rand.NextInt (6) + 1 ؛} استخدم دائمًا طريقة Java API لإنشاء رقم عشوائي في نطاق الأعداد الصحيحة.
لا تحاول استخدام Math.ABS (rand.nextint ()) ٪ N لهذه الاستخدامات غير المؤكدة ، لأن نتائجها منحازة. بالإضافة إلى ذلك ، قد تكون قيمة النتيجة سلبية ، مثل Rand.NextInt () == integer.min_value.
استخدم iterator.remove ()
void filter (list <string> list) {for (iterator <string> iter = list.iterator () ؛ iter.hasnext () ؛) {string item = iter.next () ؛ if (...) iter.remove () ؛ }}تعمل طريقة إزالة () على الإدخال الذي تم إرجاعه مؤخرًا للطريقة التالية (). يمكن لكل إدخال استخدام طريقة إزالة () مرة واحدة فقط.
سلسلة العودة
سلسلة عكسية (سلسلة S) {إرجاع جديد stringbuilder (s) .reverse (). toString () ؛}من المحتمل أن تتم إضافة هذه الطريقة إلى مكتبة Java Standard.
بدء موضوع
الأمثلة الثلاثة التالية تفعل الشيء نفسه بطرق مختلفة.
كيفية تنفيذ Runnnable:
void startathread0 () {new thread (new myrunnable ()). start () ؛} class myrunnable armements {public void run () {...}}كيف ترث الموضوع:
void startathread1 () {new MyThread (). start () ؛} class myThread يمتد Thread {public void run () {...}}كيفية ورث الخيط بشكل مجهول:
void startathread2 () {new thread () {public void run () {...}} .start () ؛}لا تستدعي طريقة التشغيل () مباشرة. تسمى طريقة thread.start () دائمًا ، والتي تنشئ مؤشر ترابط جديد ويسبب مؤشر الترابط الذي تم إنشاؤه حديثًا للاتصال ().
استخدم المحاولة
مثال على دفق I/O:
void writestuff () يلقي ioException {outputStream out = new FileOutputStream (...) ؛ حاول {out.write (...) ؛ } أخيرًا {out.close () ؛ }}مثال قفل:
void dowithlock (lock lock) {lock.acquire () ؛ حاول {...} أخيرًا {lock.release () ؛ }} إذا فشلت البيان قبل أن يتم تشغيل المحاولة وتم طرح استثناء ، فلن يتم تنفيذ كتلة البيان أخيرًا. ولكن بغض النظر عن ما ، في هذا المثال ، لا داعي للقلق بشأن إطلاق الموارد.
إذا قامت البيان الوارد في كتلة TRAW بإلقاء استثناء ، فسيقفز تشغيل البرنامج إلى كتلة البيان أخيرًا لتنفيذ أكبر عدد ممكن من العبارات ، ثم القفز من هذه الطريقة (ما لم يكن لهذه الطريقة كتلة أخرى محيطية أخيرًا).
اقرأ بيانات البايت من دفق الإدخال
inputStream in = (...) ؛ حاول {بينما (صحيح) {int b = in.read () ؛ إذا (ب == -1) استراحة ؛ (... العملية ب ...)}} أخيرًا {in.close () ؛}تقوم طريقة القراءة () إما بإرجاع العدد التالي من البايتات القراءة من الدفق (من 0 إلى 255 ، بما في ذلك 0 و 255) ، أو إرجاع -1 عند الوصول إلى نهاية الدفق.
اقرأ بيانات الكتلة من دفق الإدخال
inputStream in = (...) ؛ حاول {byte [] buf = new byte [100] ؛ بينما (صحيح) {int n = in.read (buf) ؛ إذا (n == -1) استراحة ؛ (... معالجة buf مع الإزاحة = 0 والطول = n ...)}} أخيرًا {in.close () ؛}تذكر أن طريقة القراءة () لا تملأ بالضرورة BUF بأكملها ، لذلك عليك أن تفكر في طول العائد في منطق المعالجة.
اقرأ النص من ملف
BufferedReader in = new BufferEdReader (new inputStreamReader (new FileInputStream (...) ، "UTF-8")) ؛ حاول {بينما (صحيح) {string line = in.readline () ؛ إذا (السطر == فارغ) استراحة ؛ (... خط العملية ...)}} أخيرًا {in.close () ؛} يبدو أن إنشاء كائن BufferredReader يكون مطولاً للغاية. وذلك لأن Java يعامل البايتات والأحرف كمفهمين مختلفين (هذا يختلف عن C).
يمكنك استخدام أي نوع من InputStream بدلاً من FileInputStream ، مثل المقبس.
BufferedReader.ReadLine () إرجاع فارغة عند الوصول إلى نهاية الدفق.
لقراءة حرف واحد في وقت واحد ، استخدم طريقة reader.read ().
يمكنك استخدام ترميزات الأحرف الأخرى بدون UTF-8 ، ولكن من الأفضل عدم القيام بذلك.
اكتب نصًا إلى ملف
printWriter out = new PrintWriter (New OutputStreamWriter (FileOutputStream (...) ، "UTF-8")) ؛ جرب {out.print ("Hello") ؛ Out.print (42) ؛ Out.println ("World!") ؛} أخيرًا {Out.Close () ؛} يبدو أن إنشاء كائنات printwriter مطوّلة للغاية. وذلك لأن Java يعامل البايتات والأحرف كمفهمين مختلفين (هذا يختلف عن C).
تمامًا مثل System.out ، يمكنك استخدام Print () و println () لطباعة أنواع متعددة من القيم.
يمكنك استخدام ترميزات الأحرف الأخرى بدون UTF-8 ، ولكن من الأفضل عدم القيام بذلك.
قيمة التحقق الدفاعية
int factorial (int n) {if (n <0) رمي غير alfictalargumentException جديد ("غير محدد") ؛ وإلا آخر إذا (n == 0) العودة 1 ؛ عودة أخرى n * factorial (n - 1) ؛} لا تعتقد أن قيم الإدخال موجبة ، صغيرة بما يكفي ، وما إلى ذلك للكشف عن هذه الشروط بشكل صريح.
يجب أن تكون الدالة المصممة جيدًا قادرة على التنفيذ بشكل صحيح لجميع قيم الإدخال الممكنة. تأكد من أخذ جميع المواقف في الاعتبار وأنه لا يوجد ناتج خاطئ (مثل الفائض).
كائنات الاختبار الوقائي
int findIndEx (list <string> list ، string target) {if (list == null || target == null) رمي nullpointerxception () ؛ ...}لا أعتقد أن معلمات الكائن لن تكون فارغة. لاكتشاف هذا الشرط صراحة.
مؤشر صفيف الكشف الوقائي
void frob (byte [] b ، int index) {if (b == null) رمي nullpointerxception () ؛ if (index <0 || index> = B.Length) رمي indexoutofboundsexception () ؛ ...}لا تعتقد أن مؤشر الصفيف المعطى لن يعبر الحدود. للكشف عنها بشكل صريح.
فاصل الصفيف الوقائي
void frob (byte [] b ، int of ، int len) {if (b == null) رمي nullpointerxception () ؛ if (OFF <0 || OFF> B.Length || len <0 || ...}لا تعتقد أن الفاصل الزمني للمصفوفة المعطى (على سبيل المثال ، بدءًا من إيقاف عناصر LEN) لن يتجاوز الحدود. للكشف عنها بشكل صريح.
ملء عناصر صفيف
باستخدام الحلقات:
// املأ كل عنصر من عناصر الصفيف 'a' مع 123byte [] a = (...) ؛ لـ (int i = 0 ؛ i <a.length ؛ i ++) a [i] = 123 ؛
(تفضيلية) طرق لاستخدام المكتبة القياسية:
Arrays.fill (A ، (Byte) 123) ؛
انسخ عنصر صفيف في نطاق
باستخدام الحلقات:
// نسخ 8 عناصر من Array 'A' ابتداء من الإزاحة 3 // إلى صفيف 'B' بدءًا من الإزاحة 6 ، // على افتراض "A" و "B" صفان متميزان [] A = (...) ؛ Byte [] B = (...) ؛ لـ (int i = 0 ؛ i <8 ؛ i ++) b [6 + i] = a [3 + i] ؛
(تفضيلية) طرق لاستخدام المكتبة القياسية:
System.ArrayCopy (A ، 3 ، B ، 6 ، 8) ؛
تغيير حجم الصفيف
استخدم الحلقات (التحجيم):
// اجعل الصفيف 'a' أكبر إلى newlenbyte [] a = (...) ؛ byte [] b = new byte [newlen] ؛ for (int i = 0 ؛ i <a.length ؛ i ++) // يسير إلى طول b [i] = a [i] ؛ a = b ؛
استخدم الحلقات (تقليل الحجم):
// اصنع صفيف "A 'أصغر إلى newlenbyte [] a = (...) ؛ byte [] b = new byte [newlen] ؛ for (int i = 0 ؛ i <b.length ؛ i ++) // يسير إلى طول b b [i] = a [i] ؛ a = b ؛
(تفضيلية) طرق لاستخدام المكتبة القياسية:
a = arrays.copyof (a ، newlen) ؛
تعبئة 4 بايت في int
int packbigendian (byte [] b) {return (b [0] & 0xff) << 24 | (B [1] & 0xff) << 16 | (B [2] & 0xff) << 8 | (B [3] & 0xff) << 0 ؛} int packlittleendian (byte [] b) {return (b [0] & 0xff) << 0 | (B [1] & 0xff) << 8 | (B [2] & 0xff) << 16 | (B [3] & 0xff) << 24 ؛}تحلل int إلى 4 بايت
byte [] unpackbigendian (int x) {return new byte [] {(byte) (x >>> 24) ، (byte) (x >>> 16) ، (byte) (x >>> 8) ، (byte) (x >>> 0)} ؛} byte [] (بايت) (x >>> 8) ، (بايت) (x >>> 16) ، (بايت) (x >>> 24)} ؛}استخدم دائمًا مشغل التحول الأيمن غير الموقّع (>>>) للف البتات ، لا تستخدم مشغل التحول الأيمن (>>).