اذهب إلى المحتوى

البحث في الموقع

المحتوى عن 'الكائنات'.

  • ابحث بالكلمات المفتاحية

    أضف وسومًا وافصل بينها بفواصل ","
  • ابحث باسم الكاتب

نوع المحتوى


التصنيفات

  • الإدارة والقيادة
  • التخطيط وسير العمل
  • التمويل
  • فريق العمل
  • دراسة حالات
  • التعامل مع العملاء
  • التعهيد الخارجي
  • السلوك التنظيمي في المؤسسات
  • عالم الأعمال
  • التجارة والتجارة الإلكترونية
  • نصائح وإرشادات
  • مقالات ريادة أعمال عامة

التصنيفات

  • مقالات برمجة عامة
  • مقالات برمجة متقدمة
  • PHP
    • Laravel
    • ووردبريس
  • جافاسكربت
    • لغة TypeScript
    • Node.js
    • React
    • Vue.js
    • Angular
    • jQuery
    • Cordova
  • HTML
  • CSS
    • Sass
    • إطار عمل Bootstrap
  • SQL
  • لغة C#‎
    • ‎.NET
    • منصة Xamarin
  • لغة C++‎
  • لغة C
  • بايثون
    • Flask
    • Django
  • لغة روبي
    • إطار العمل Ruby on Rails
  • لغة Go
  • لغة جافا
  • لغة Kotlin
  • لغة Rust
  • برمجة أندرويد
  • لغة R
  • الذكاء الاصطناعي
  • صناعة الألعاب
  • سير العمل
    • Git
  • الأنظمة والأنظمة المدمجة

التصنيفات

  • تصميم تجربة المستخدم UX
  • تصميم واجهة المستخدم UI
  • الرسوميات
    • إنكسكيب
    • أدوبي إليستريتور
  • التصميم الجرافيكي
    • أدوبي فوتوشوب
    • أدوبي إن ديزاين
    • جيمب GIMP
    • كريتا Krita
  • التصميم ثلاثي الأبعاد
    • 3Ds Max
    • Blender
  • نصائح وإرشادات
  • مقالات تصميم عامة

التصنيفات

  • مقالات DevOps عامة
  • خوادم
    • الويب HTTP
    • البريد الإلكتروني
    • قواعد البيانات
    • DNS
    • Samba
  • الحوسبة السحابية
    • Docker
  • إدارة الإعدادات والنشر
    • Chef
    • Puppet
    • Ansible
  • لينكس
    • ريدهات (Red Hat)
  • خواديم ويندوز
  • FreeBSD
  • حماية
    • الجدران النارية
    • VPN
    • SSH
  • شبكات
    • سيسكو (Cisco)

التصنيفات

  • التسويق بالأداء
    • أدوات تحليل الزوار
  • تهيئة محركات البحث SEO
  • الشبكات الاجتماعية
  • التسويق بالبريد الالكتروني
  • التسويق الضمني
  • استسراع النمو
  • المبيعات
  • تجارب ونصائح
  • مبادئ علم التسويق

التصنيفات

  • مقالات عمل حر عامة
  • إدارة مالية
  • الإنتاجية
  • تجارب
  • مشاريع جانبية
  • التعامل مع العملاء
  • الحفاظ على الصحة
  • التسويق الذاتي
  • العمل الحر المهني
    • العمل بالترجمة
    • العمل كمساعد افتراضي
    • العمل بكتابة المحتوى

التصنيفات

  • الإنتاجية وسير العمل
    • مايكروسوفت أوفيس
    • ليبر أوفيس
    • جوجل درايف
    • شيربوينت
    • Evernote
    • Trello
  • تطبيقات الويب
    • ووردبريس
    • ماجنتو
    • بريستاشوب
    • أوبن كارت
    • دروبال
  • الترجمة بمساعدة الحاسوب
    • omegaT
    • memoQ
    • Trados
    • Memsource
  • برامج تخطيط موارد المؤسسات ERP
    • تطبيقات أودو odoo
  • أنظمة تشغيل الحواسيب والهواتف
    • ويندوز
    • لينكس
  • مقالات عامة

التصنيفات

  • آخر التحديثات

أسئلة وأجوبة

  • الأقسام
    • أسئلة البرمجة
    • أسئلة ريادة الأعمال
    • أسئلة العمل الحر
    • أسئلة التسويق والمبيعات
    • أسئلة التصميم
    • أسئلة DevOps
    • أسئلة البرامج والتطبيقات

التصنيفات

  • كتب ريادة الأعمال
  • كتب العمل الحر
  • كتب تسويق ومبيعات
  • كتب برمجة
  • كتب تصميم
  • كتب DevOps

ابحث في

ابحث عن


تاريخ الإنشاء

  • بداية

    نهاية


آخر تحديث

  • بداية

    نهاية


رشح النتائج حسب

تاريخ الانضمام

  • بداية

    نهاية


المجموعة


النبذة الشخصية

تم العثور على 4 نتائج

  1. هو كائن مدمَج في المتصفح، يسمح بإرسال طلبات HTTP في جافاسكريبت JavaScript، ويمكنه العمل مع أي نوع من البيانات، وليس فقط بيانات "XML" على الرغم من وجود الكلمة "XML" في تسميته. يساعدنا هذا الكائن في عمليات رفع وتنزيل الملفات وتتبع تقدمها وغير ذلك الكثير، ولا بدّ من الإشارة إلى وجود أسلوب أكثر حداثةً منه وهو استخدام fetch التي ألغت استخدامه بشكل أو بآخر. يُستخدم XMLHttpRequest في تقنيات تطوير الويب العصرية لثلاثة أسباب، هي الآتية: أسباب تاريخية: عند الحاجة إلى دعم سكربت موجود يستخدم XMLHttpRequest. الحاجة إلى دعم المتصفحات القديمة دون استخدام شيفرة "polyfill". إنجاز شيء لا يمكن تنفيذه باستخدام fetch مثل تتبع تقدم رفع ملف. إن كنت تحتاج ما ذكرناه تابع قراءة المقال وإلا فتوجه إلى فصل استخدام Fetch. الأساسيات يعمل الكائن XMLHttpRequest وفق النمطين المتزامن وغير المتزامن، سنتحدث بدايةً عن الوضع غير المتزامن، لأنه يستخدم في أغلب الحالات. يُنجز طلب HTTP وفق 4 خطوات: الخطوة الأولى، إنشاء الكائن XMLHttpRequest: let xhr = new XMLHttpRequest(); ليس للدالة البانية أي وسطاء. الخطوة الثانية، تهيئة الكائن، وتكون بعد إنشائه عادةً: xhr.open(method, URL, [async, user, password]) يؤمن التابع open المعاملات الرئيسية للطلب، وهي: method: تحدد نوع طلب HTTP، عادةً يكون GET أو POST. URL: عنوان الموقع الذي نرسل الطلب إليه، ويمكن استخدام الكائن URL. async: تكون العملية متزامنةً إذا أسندت لها القيمة false صراحةً، وسنغطي ذلك لاحقًا. user وpassword: اسم المستخدم وكلمة المرور للاستيثاق، عند الحاجة. لا يفتح التابع open الاتصال على الرغم من اسمه، بل يهيئ الطلب فقط، ولا يبدأ الاتصال عبر الشبكة إلا باستدعاء send. الخطوة الثالثة، إرسال الطلب: xhr.send([body]) يفتح هذا التابع الاتصال ويرسل الطلب إلى الخادم، حيث يحتوي المعامل body على جسم الطلب.ولا تمتلك الطلبات مثل GET جسماً، وتستخدم طلبات أخرى مثل POST الجسم لإرسال البيانات إلى الخادم، وسنرى أمثلةً عن ذلك لاحقًا. الخطوة الرابعة، الاستماع إلى أحداث الكائن xhr لتلقي الاستجابة، وإليك أكثر الأحداث استخدامًا: load: عندما يكتمل الطلب ونحصل على الاستجابة كاملةً، حتى لو أعاد الخادم رمز الحالة 500 أو 400 مثلًا. error: عندما يتعذر تنفيذ الطلب، كأن تكون الشبكة معطلةً أو العنوان خاطئًا. progress: ويقع دوريًا أثناء تنزيل الاستجابة، ويوضح الكمية التي نُزِّلت. xhr.onload = function() { alert(`Loaded: ${xhr.status} ${xhr.response}`); }; xhr.onerror = function() { // عندما لا تحدث الاستجابة إطلاقًا alert(`Network Error`); }; xhr.onprogress = function(event) { // يقع دوريًا // event.loaded - الحجم الذي نزل بالبايت // event.lengthComputable = true إن أرسل الخادم ترويسة طول المحتوى // event.total - العدد الكلي للبايتات alert(`Received ${event.loaded} of ${event.total}`); }; إليك مثالًا كاملًا، حيث تحمّل الشيفرة الموالية العنوان الآتي article/xmlhttprequest/example/load/ من الخادم، وتطبع تقدم العملية: // 1. إنشاء الكائن let xhr = new XMLHttpRequest(); // 2. URL /article/.../load تهيئته مع العنوان xhr.open('GET', '/article/xmlhttprequest/example/load'); // 3. إرسال الطلب عبر الشبكة xhr.send(); // 4. سيستدعى هذا الجزء عند تلقي الاستجابة xhr.onload = function() { if (xhr.status != 200) { // analyze HTTP status of the response alert(`Error ${xhr.status}: ${xhr.statusText}`); // e.g. 404: Not Found } else { // show the result alert(`Done, got ${xhr.response.length} bytes`); // response is the server response } }; xhr.onprogress = function(event) { if (event.lengthComputable) { alert(`Received ${event.loaded} of ${event.total} bytes`); } else { alert(`Received ${event.loaded} bytes`); // no Content-Length } }; xhr.onerror = function() { alert("Request failed"); }; نستقبل نتيجة الطلب باستخدام خصائص الكائن xhr التالية حالما يستجيب الخادم: status: رمز حالة HTTP، عدد، مثل 200 و404 و403 وهكذا، كما يمكن أن يكون 0 عند حدوث خطأ لا علاقة له بالنقل وفق بروتوكول HTTP. statusText: رسالة حالة HTTP، نص، مثل OK لرمز الحالة 200 وNot Found لرمز الحالة 404، وForbidden لرمز الحالة 403. response: قد تستخدم السكربتات القديمة responseText، وتمثل جسم الاستجابة التي يرسلها الخادم. يمكن أن نحدد أيضًا زمن انتهاء timeout مستخدمين الخاصية الموافقة: xhr.timeout = 10000; // زمن الانتهاء بالميلي ثانية إن لم ينجح الطلب خلال الفترة الزمنية المحددة فسيُلغى وسيقع الحدث timeout. إضافة معاملات بحث إلى العنوان URL let url = new URL('https://google.com/search'); url.searchParams.set('q', 'test me!'); // 'q' ترميز المعامل xhr.open('GET', url); // https://google.com/search?q=test+me%21 نوع الاستجابة من الممكن استخدام الخاصية ResponseType لضبط تنسيق الاستجابة: "": القيمة الافتراضية، الحصول على الاستجابة كنص. "text": الحصول على الاستجابة كنص. "arraybuffer": الحصول على الاستجابة على شكل كائن ArrayBuffer، للحصول على بيانات ثنائية. "blob": الحصول على الاستجابة على شكل كائن Blob، للحصول على بيانات ثنائية. "document": الحصول على مستند XML، باستخدام اللغة XPath أو غيرها. "json": الحصول على الاستجابة بصيغة JSON، تفسر الاستجابة تلقائيًا. لنستقبل الاستجابة بصيغة JSON مثلًا: let xhr = new XMLHttpRequest(); xhr.open('GET', '/article/xmlhttprequest/example/json'); xhr.responseType = 'json'; xhr.send(); // {"message": "Hello, world!"} الاستجابة هي xhr.onload = function() { let responseObj = xhr.response; alert(responseObj.message); // Hello, world! }; حالات الجاهزية Ready States يمر الكائن بعدة حالات عند تقدم تنفيذ الطلب، ويمكن الوصول إلى الحالة من خلال xhr.readyState، وإليك الحالات جميعها كما وردت في التوصيفات: UNSENT = 0; // الحالة الأساسية OPENED = 1; //open استدعاء التابع HEADERS_RECEIVED = 2; // استقبال ترويسة الاستجابة LOADING = 3; // تلقي جسم الاستجابة DONE = 4; // اكتمال الطلب ينتقل الكائن XMLHttpRequest بين الحالات السابقة بالترتيب 0 ثم 1 ثم 2 ثم 3 ثم 4، وتتكرر الحالة 3 في كل مرة تُستقبل فيها حزمة بيانات عبر الشبكة، ويمكننا تتبع هذه الحالات باستخدام الحدث readystatechange. xhr.onreadystatechange = function() { if (xhr.readyState == 3) { // تحميل } if (xhr.readyState == 4) { // انتهاء الطلب } }; قد تجد مستمعات للأحداث readystatechange حتى في الشيفرات القديمة، وذلك لأنّ أحداثًا مثل load وغيرها، لم تكن موجودةً في فترة ما، لكن معالجات الأحداث load/error/progress قد ألغت استخدامها. طلب الإلغاء Aborting request يمكن إلغاء الطلب في أي لحظة، وذلك باستدعاء التابع ()xhr.abort : xhr.abort(); // إلغاء الطلب يحرّض استدعاء التابع الحدث abort وتأخذ الخاصية xhr.status القيمة 0. الطلبات المتزامنة عند إسناد القيمة false إلى المعامل الثالث async للتابع open، فسيُنفَّذ الطلب بتزامن Synchronously. وبعبارة أخرى، ستتوقف JavaScript مؤقتًا عن التنفيذ عند إرسال الطلب ()send ثم تتابع عند تلقي الاستجابة، وهذا يشابه تعليمتي alert وprompt. إليك المثال السابق وقد أعيدت كتابته مع وجود المعامل الثالث للتابع open: let xhr = new XMLHttpRequest(); xhr.open('GET', '/article/xmlhttprequest/hello.txt', false); try { xhr.send(); if (xhr.status != 200) { alert(`Error ${xhr.status}: ${xhr.statusText}`); } else { alert(xhr.response); } } catch(err) { // onerror بدلًا من alert("Request failed"); } قد يبدو الأمر جيدًا لكنها طريقة يندر استخدامها، لأنها تعيق تنفيذ شيفرة JavaScript حتى يكتمل التحميل، وقد يصبح من المستحيل تمرير الصفحة لعرض بقية المحتويات أثناء تنفيذها في بعض المتصفحات، وقد يقترح المتصفح أيضًا إغلاق الصفحة العالقة إذا تأخر تنفيذ الطلب المتزامن كثيرًا. ولا تتاح العديد من الإمكانيات المتقدمة للكائن XMLHttpRequest، مثل الطلب من نطاق آخر أو تحديد زمن الانتهاء من الطلب، عند إرسال الطلبات بتزامن، ولا مؤشرات أيضًا على تقدم العملية، لذلك لا تُنفّذ الطلبات المتزامنة إلا نادرًا. ترويسات HTTP يتيح الكائن XMLHttpRequest إرسال ترويسات مخصصة وقراءة ترويسات الاستجابة، وهنالك ثلاثة توابع للتعامل مع الترويسات: (setRequestHeader(name, value: يعطي اسمًا name وقيمةً value لترويسة الطلب، وإليك مثالًا عن ذلك: xhr.setRequestHeader('Content-Type', 'application/json') فيما يلي مثال لتوضيح ذلك: xhr.setRequestHeader('X-Auth', '123'); xhr.setRequestHeader('X-Auth', '456'); // ستكون الترويسة: // X-Auth: 123, 456 (getResponseHeader(name: تعيد ترويسة الاستجابة ذات الاسم المحدد name، عدا Set-Cookie وSet-Cookie2، إليك مثالًا: xhr.getResponseHeader('Content-Type') ()getAllResponseHeaders: تعيد كل ترويسات الاستجابة، عدا Set-Cookie وSet-Cookie2، وتُعاد القيم ضمن سطر مفرد، حيث سيكون الفاصل هو "\r\n" (لا يتعلق بنظام تشغيل محدد) بين كل ترويستين، وبالتالي يمكن فصلها إلى ترويسات مفردة، وإليك مثالًا: Cache-Control: max-age=31536000 Content-Length: 4260 Content-Type: image/png Date: Sat, 08 Sep 2012 16:53:16 GMT تفصل بين اسم الترويسة وقيمتها النقطتان المتعامدتان، يليها فراغ " :"، وهذا أمر ثابت في التوصيفات، فلو أردنا مثلًا الحصول على كائن له اسم وقيمةP فلا بد من تنفيذ الأمر بالشكل التالي، مفترضين أنه عند وجود ترويستين بنفس الاسم، وعندها ستحل الأخيرة منهما مكان الأولى: let headers = xhr .getAllResponseHeaders() .split('\r\n') .reduce((result, current) => { let [name, value] = current.split(': '); result[name] = value; return result; }, {}); // headers['Content-Type'] = 'image/png' الطلب POST والكائن FormData يمكن استخدام الكائن FormData لإرسال طلب HTTP-POST: إليك الشيفرة اللازمة: let formData = new FormData([form]); //<form> إنشاء كائن يُملأ اختيارًا من formData.append(name, value); // ربط الحقل تنشئ الشيفرة السابقة الكائن FormData وتملؤه بقيم من نموذج form اختياريًا، وتضيف append حقولًا أخرى إن اقتضى الأمر، ومن ثم: نستخدم الأمر (...,'xhr.open('POST: لنؤسس الطلب POST. نستخدم الأمر (xhr.send(formData: لإرسال النموذج إلى الخادم. إليك المثال التالي: <form name="person"> <input name="name" value="John"> <input name="surname" value="Smith"> </form> <script> // form ملؤها من let formData = new FormData(document.forms.person); // إضافة حقل جديد formData.append("middle", "Lee"); // إرساله let xhr = new XMLHttpRequest(); xhr.open("POST", "/article/xmlhttprequest/post/user"); xhr.send(formData); xhr.onload = () => alert(xhr.response); </script> يُرسَل النموذج بترميز multipart/form-data، فإذا أردنا استخدام JSON فلا بدّ من تنفيذ الأمر JSON.stringify ثم إرساله في هيئة نص، إلى جانب ضبط الترويسة Content-Type على القيمة application/json، وتفكِّك العديد من إطارات العمل مع الخادم محتوى JSON تلقائيًا بهذه الطريقة. let xhr = new XMLHttpRequest(); let json = JSON.stringify({ name: "John", surname: "Smith" }); xhr.open("POST", '/submit') xhr.setRequestHeader('Content-type', 'application/json; charset=utf-8'); xhr.send(json); ويمكن للتابع أن يُرسل أي جسم للطلب بما في ذلك كائنات Blob وBufferSource. تقدم عمليات رفع البيانات يقع الحدث progress في مرحلة التنزيل فقط، فلو نشرنا شيئًا ما باستخدام الطلب POST، فسيرفع الكائن XMLHttpRequest البيانات -جسم الطلب- أولًا ومن ثم ينزل الاستجابة. وسيكون تتبع تقدم عملية رفع البيانات خاصةً إن كانت ضخمة؛ أمرًا هامًا، لكن لن نستفيد من الحدث xhr.onprogress في حالتنا، يوجد كائن آخر لا يمتلك توابعًا، وهو مخصص حصرًا لتتبع أحداث رفع البيانات وهو xhr.upload، الذي يولِّد أحداثًا كما يفعلxhr، لكنها تقع فقط عند رفع البيانات: loadstart: يقع عندما يبدأ رفع البيانات. progress: يقع دوريًا مع تقدم الرفع. abort: يقع عند إلغاء الرفع. error: يقع عند وقوع خطأ لا يتعلق بالبروتوكول HTTP. load: يقع عند نجاح عملية الرفع. timeout: يقع عند انتهاء الوقت المخصص لرفع البيانات، إذا ضُبطت الخاصية timeout. loadend: يقع عند انتهاء الرفع بنجاح أو بإخفاق. إليك أمثلةً عن معالجات هذه الأحداث: xhr.upload.onprogress = function(event) { alert(`Uploaded ${event.loaded} of ${event.total} bytes`); }; xhr.upload.onload = function() { alert(`Upload finished successfully.`); }; xhr.upload.onerror = function() { alert(`Error during the upload: ${xhr.status}`); }; إليك أيضًا مثالًا واقعيًا عن رفع ملف مع مؤشرات على تقدم العملية: <input type="file" onchange="upload(this.files[0])"> <script> function upload(file) { let xhr = new XMLHttpRequest(); // تعقب تقدم عملية الرفع xhr.upload.onprogress = function(event) { console.log(`Uploaded ${event.loaded} of ${event.total}`); }; // تعقب الانتهاء، بنجاح أو إخفاق xhr.onloadend = function() { if (xhr.status == 200) { console.log("success"); } else { console.log("error " + this.status); } }; xhr.open("POST", "/article/xmlhttprequest/post/upload"); xhr.send(file); } </script> الطلبات ذات الأصول المختلطة يمكن للكائن XMLHttpRequest تنفيذ طلبات الأصل المختلط مستخدمًا سياسة CORS، تمامًا كما يفعل fetch، ولن يُرسل ملفات تعريف الارتباط cookies أو معلومات استيثاق إلى مواقع ذات أصل مختلف افتراضيًا. ولتمكين ذلك لا بدّ من ضبط الخاصية xhr.withCredentials على القيمة true. let xhr = new XMLHttpRequest(); xhr.withCredentials = true; xhr.open('POST', 'http://anywhere.com/request'); ... اطلع على فصل "استخدام Fetch مع الطلبات ذات الأصل المختلط" لمعلومات أكثر. خلاصة تمثل الشيفرة التالية، الشيفرة النموذجية لطلب GET باستخدام XMLHttpRequest let xhr = new XMLHttpRequest(); xhr.open('GET', '/my/url'); xhr.send(); xhr.onload = function() { if (xhr.status != 200) { // HTTP error? // معالجة الخطأ alert( 'Error: ' + xhr.status); return; } // xhr.response الحصول على الاستجابة من }; xhr.onprogress = function(event) { // إعطاء تقرير عن التقدم alert(`Loaded ${event.loaded} of ${event.total}`); }; xhr.onerror = function() { // handle non-HTTP error (e.g. network down) }; هنالك أحداث أكثر في التوصيفات الحديثة، والتي وُضِعت في قائمة مرتبة وفق دورة حياة كل حدث: loadstart: عندما يبدأ الطلب. progress: عند وصول حزمة بيانات، وتكون الاستجابة الكاملة في هذه اللحظة ضمن الاستجابة response. ()abort: عند إلغاء الطلب باستدعاء التابع. error: عند وقوع خطأ في الاتصال، مثل اسم نطاق خاطئ ولم يحدث نتيجة خطأ HTTP مثل 404. load: عند انتهاء الطلب بنجاح. timeout: عند إلغاء الطلب نتيجة تجاوز الوقت المخصص، ويحدث عندما تُضبَط هذه الخاصية. loadend: ويقع بعد الأحداث load أو error أو timeout أو abort، وهذه الأحداث متنافية فيما بينها، أي لا يمكن وقوع سوى حدث واحد منها. إن أكثر الأحداث استخدامًا هما حدث إكمال التحميل load وحدث إخفاق التحميل error، كما يمكن استعمال معالج الحدث loadend والتحقق من خصائص الكائن xhr للتأكد من طبيعة الحدث الذي وقع. لقد تعرفنا في هذا المقال أيضًا على الحدث readystatechange، والذي ظهر منذ زمن بعيد قبل استقرار التوصيفات، ولا حاجة حاليًا لاستخدامه، حيث يُستبدل بالأحداث الأكثر عصريةً. لكن مع ذلك قد نصادفه في بعض السكربتات القديمة. إذا أردنا تعقب تقدم رفع البيانات، فلا بدّ من الاستماع إلى نفس أحداث التنزيل لكن باستخدام الكائن xhr.upload. ترجمة -وبتصرف- للفصل XMLHttpRequest من سلسلة The Modern JavaScript Tutorial. اقرأ أيضًا المقال السابق: كائنات URL في جافاسكريبت ترميز النصوص والتعامل مع كائنات الملفات في جافاسكريبت فحص الأصناف عبر instanceof في جافاسكربت هياكل البيانات: الكائنات والمصفوفات في جافاسكريبت الحياة السرية للكائنات في جافاسكريبت
  2. من السهل رفع ملف باستخدام fetch، لكن كيف سنستأنف عملية الرفع بعد فقدان الاتصال؟ لا توجد خيارات مدمجة لتنفيذ هذا الأمر، لكن لدينا كل الأجزاء التي تمكننا من تنفيذ ذلك. ينبغي أن تأتي عمليات الرفع القابلة للاستئناف مع مؤشرات على تقدم العملية، خاصةً عندما يكون الملف ضخمًا -إن أردنا استئناف الرفع-، وطالما لن تسمح fetch بتعقب عملية الرفع؛ فسنحتاج إلى الكائن XMLHttpRequest. حدث تتبع غير مفيد كفاية لا بدّ من معرفة الكمية التي جرى رفعها قبل انقطاع الاتصال لاستئناف رفع الملف، حيث يمتلك الكائن xhr الحدث xhr.upload.onprogress لتتبع تقدم عملية الرفع، لكنه لن يفيدنا في استئناف الرفع -لسوء الحظ-، لأنه سيقع عند انتهاء إرسال البيانات، لكن لا يمكن للمتصفح تحديد هل استقبلها الخادم فعلًا أم لا، فمن الممكن أن تكون هذه البيانات قد خُزّنت مؤقتًا من قبل شبكة محلية وكيلة، وربما تكون العملية انتهت على الخادم البعيد قبل أن يتمكن من معالجة البيانات، أو أنها ببساطة قد فُقدت في مرحلة ما ولم تصل إلى المستقبل النهائي، لذلك لن تتعدى فائدة هذا الحدث إظهار شريط تقدم جميل على الصفحة. إذًا لا بدّ من معرفة عدد البايتات التي استقبلها الخادم بدقة حتى نتمكن من استئناف الرفع، والخادم فقط هو من يمتلك هذه المعلومة، لذا فلا بدّ من إرسال طلب إضافي. الخوارزمية ننشئ في البداية معرّفًا مميزًا id للملف الذي سنرفعه، بالشكل التالي: let fileId = file.name + '-' + file.size + '-' + file.lastModified; سنحتاج إليه لاستئناف الرفع، إذ لا بدّ من إبلاغ الخادم عن الملف الذي سنستأنف رفعه، حيث إذا تغير اسم أو حجم أو تاريخ آخر تعديل للبيانات، فسيتغيّر معرّف الملف fileId. ثم نرسل إلى الخادم طلبًا نسأله عن حجم البيانات التي وصلت إليه (مقدرًا بالبايت)، بالشكل التالي: let response = await fetch('status', { headers: { 'X-File-Id': fileId } }); // تلقى الخادم هذا العدد من البايتات let startByte = +await response.text(); هذا يفترض قدرة الخادم على تتبع تقدم عملية رفع الملف باستخدام الترويسة X-File-Id، وينبغي إنجاز ذلك في الواجهة الخلفية (من جانب الخادم)، فإذا لم يكن الملف موجودًا بعد على الخادم، فيستجيب الخادم بالرمز 0. يمكننا استخدام التابع slice العائد لكائن البيانات الثنائية Blob لإرسال الملف ابتداءً من البايت الذي سنتابع بعده startByte، بالشكل التالي: xhr.open("POST", "upload", true); // معرّف الملف ليعلم الخادم اسم الملف الذي يُرفع xhr.setRequestHeader('X-File-Id', fileId); // البايت الذي سنستأنف منه الرفع xhr.setRequestHeader('X-Start-Byte', startByte); xhr.upload.onprogress = (e) => { console.log(`Uploaded ${startByte + e.loaded} of ${startByte + e.total}`); }; //أو أي مصدر input.files[0] قد يكون مصدر الملف من xhr.send(file.slice(startByte)); وهكذا سنرسل إلى الخادم معرّف الملف عبر الترويسة X-File-Id ليعرف الملف الذي نرفعه، كما سنرسل بايت البداية عبر الترويسة X-Start-Byte ليعرف الخادم بأننا لا نرفع الملف من البداية بل نستأنف عملية رفع سابقة، ولا بدّ أن يتحقق الخادم من سجلاته. فإذا جرت عملية رفع سابقة لهذا الملف وكان حجمه الحالي مساويًا تمامًا لقيمة X-Start-Byte؛ فسيُلحِق البيانات التي يستقبلها تاليًا به. إليك تطبيقًا نموذجيًا يضم شيفرتي الخادم والعميل وقد كتبت باستخدام Node.js، ويمكنك تنزيله وتشغيله محليًا لترى آلية عمله. شيفرة الخادم "server.js": let http = require('http'); let static = require('node-static'); let fileServer = new static.Server('.'); let path = require('path'); let fs = require('fs'); let debug = require('debug')('example:resume-upload'); let uploads = Object.create(null); function onUpload(req, res) { let fileId = req.headers['x-file-id']; let startByte = +req.headers['x-start-byte']; if (!fileId) { res.writeHead(400, "No file id"); res.end(); } // we'll files "nowhere" let filePath = '/dev/null'; // يمكن استخدام مسار حقيقي عوضًا عنه، مثل: // let filePath = path.join('/tmp', fileId); debug("onUpload fileId: ", fileId); // تهيئة عملية رفع جديدة if (!uploads[fileId]) uploads[fileId] = {}; let upload = uploads[fileId]; debug("bytesReceived:" + upload.bytesReceived + " startByte:" + startByte) let fileStream; // صفرًا أو غير مهيئ فسننشئ ملفًا جديدًا، وإلا فسيختبر الحجم ويضمه إلى الملف الموجود startByte إذا كان if (!startByte) { upload.bytesReceived = 0; fileStream = fs.createWriteStream(filePath, { flags: 'w' }); debug("New file created: " + filePath); } else { // we can check on-disk file size as well to be sure if (upload.bytesReceived != startByte) { res.writeHead(400, "Wrong start byte"); res.end(upload.bytesReceived); return; } // ضمه إلى الملف الموجود fileStream = fs.createWriteStream(filePath, { flags: 'a' }); debug("File reopened: " + filePath); } req.on('data', function(data) { debug("bytes received", upload.bytesReceived); upload.bytesReceived += data.length; }); // إرسال جسم الطلب إلى الملف req.pipe(fileStream); // عندما ينتهي الطلب وتكتَب المعلومات كلها fileStream.on('close', function() { if (upload.bytesReceived == req.headers['x-file-size']) { debug("Upload finished"); delete uploads[fileId]; // يمكننا فعل شيء آخر بالملف المرفوع res.end("Success " + upload.bytesReceived); } else { // فقد الاتصال، نترك الملف غير المكتمل debug("File unfinished, stopped at " + upload.bytesReceived); res.end(); } }); // I/O أنهِ الطلب في حال خطأ fileStream.on('error', function(err) { debug("fileStream error"); res.writeHead(500, "File error"); res.end(); }); } function onStatus(req, res) { let fileId = req.headers['x-file-id']; let upload = uploads[fileId]; debug("onStatus fileId:", fileId, " upload:", upload); if (!upload) { res.end("0") } else { res.end(String(upload.bytesReceived)); } } function accept(req, res) { if (req.url == '/status') { onStatus(req, res); } else if (req.url == '/upload' && req.method == 'POST') { onUpload(req, res); } else { fileServer.serve(req, res); } } // ----------------------------------- if (!module.parent) { http.createServer(accept).listen(8080); console.log('Server listening at port 8080'); } else { exports.accept = accept; } شيفرة العميل "uploader.js": class Uploader { constructor({file, onProgress}) { this.file = file; this.onProgress = onProgress; // أنشئ معرّف الملف // يمكننا إضافة معرّف لجلسة المستخدم (عند وجوده ) لجعله فريدًا أكثر this.fileId = file.name + '-' + file.size + '-' + file.lastModified; } async getUploadedBytes() { let response = await fetch('status', { headers: { 'X-File-Id': this.fileId } }); if (response.status != 200) { throw new Error("Can't get uploaded bytes: " + response.statusText); } let text = await response.text(); return +text; } async upload() { this.startByte = await this.getUploadedBytes(); let xhr = this.xhr = new XMLHttpRequest(); xhr.open("POST", "upload", true); // send file id, so that the server knows which file to resume xhr.setRequestHeader('X-File-Id', this.fileId); // send the byte we're resuming from, so the server knows we're resuming xhr.setRequestHeader('X-Start-Byte', this.startByte); xhr.upload.onprogress = (e) => { this.onProgress(this.startByte + e.loaded, this.startByte + e.total); }; console.log("send the file, starting from", this.startByte); xhr.send(this.file.slice(this.startByte)); // return // true if upload was successful, // false if aborted // throw in case of an error return await new Promise((resolve, reject) => { xhr.onload = xhr.onerror = () => { console.log("upload end status:" + xhr.status + " text:" + xhr.statusText); if (xhr.status == 200) { resolve(true); } else { reject(new Error("Upload failed: " + xhr.statusText)); } }; // xhr.abort() فقط عندما يُستدعى onabort يقع xhr.onabort = () => resolve(false); }); } stop() { if (this.xhr) { this.xhr.abort(); } } } الملف "index.js": <!DOCTYPE HTML> <script src="uploader.js"></script> <form name="upload" method="POST" enctype="multipart/form-data" action="/upload"> <input type="file" name="myfile"> <input type="submit" name="submit" value="Upload (Resumes automatically)"> </form> <button onclick="uploader.stop()">Stop upload</button> <div id="log">Progress indication</div> <script> function log(html) { document.getElementById('log').innerHTML = html; console.log(html); } function onProgress(loaded, total) { log("progress " + loaded + ' / ' + total); } let uploader; document.forms.upload.onsubmit = async function(e) { e.preventDefault(); let file = this.elements.myfile.files[0]; if (!file) return; uploader = new Uploader({file, onProgress}); try { let uploaded = await uploader.upload(); if (uploaded) { log('success'); } else { log('stopped'); } } catch(err) { console.error(err); log('error'); } }; </script> وستكون النتيجة بالشكل التالي: تُعَد الأساليب الجديدة لإرسال الطلبات عبر الشبكة أقرب في إمكانياتها إلى إدارة الملفات كما رأينا، مثل التحكم بالترويسات ومؤشرات تقدم العمليات وإرسال الملفات وغير ذلك، وهذا ما مكّننا من تنفيذ شيفرات لاستئناف رفع الملفات وغيرها الكثير. ترجمة -وبتصرف- للفصل Resumable file upload من سلسلة The Modern JavaScript Tutorial. اقرأ أيضًا هياكل البيانات: الكائنات والمصفوفات في جافاسكريبت الحياة السرية للكائنات في جافاسكريبت ترميز النصوص والتعامل مع كائنات الملفات في جافاسكريبت
  3. كوتلن (Kotlin) هي لغة برمجة مخصّصة لمنصّة جافا الافتراضية (Java Virtual Machine أو اختصارًا JVM)، الأندرويد، والمتّصفح. تنتمي للغات statically typed (التي تفحص الأنواع وقت الترجمة). وهي متوافقة مع جافا 100%. // (//)التعليقات على سطر واحد تبدأ بـ/* التعليقات المؤلفة من عدة أسطر تبدو كهذه */ تعمل الكلمة المفتاحية package بنفس طريقة جافا package com.learnxinyminutes.kotlin نقطة الإدخال لبرامج Kotlin هي دالة(تابع) تسمى main ،يمرر التّابع مصفوفة تحتوي على وسطاء arguments لسطر الأوامر. fun main(args: Array<String>) { التّصريح عن القيم يتم باستخدام إما var أو val ،تصريح val لا يمكن إعادة تعيينه، في حين يمكن ذلك في var val fooVal =10 لا يمكننا لاحقًا إعادة تعيين قيمة fooVal لقيمة أخرى. var fooVar =10 fooVar =20 من الممكن إعادة تعيين قيمة fooVar. في أغلب الحالات، يمكن لكوتلن أن تحدّد ما هو نوع المتغير، لذلك لا داعي لتحديده صراحة في كل مرّة. يمكننا أن نصرّح بنوع المتغير بوضوح كالتالي: val foo:Int=7 يمكن تمثيل السلاسل بطريقة مماثلة في java. يتم الهروب باستخدام backslash. val fooString ="My String Is Here!" val barString ="Printing on a new line?\nNo Problem!" val bazString ="Do you want to add a tab?\tNo Problem!" println(fooString) println(barString) println(bazString) تُحدد السلسلة الخام string raw باستخدام triple quote ("""). ويمكن أن تحتوي أسطر جديدة و أية محارف أخرى. val fooRawString =""" fun helloWorld(val name :String){ println("Hello, world!")}""" println(fooRawString) السلاسل ممكن أن تحتوي تعابير القالب template expressions. تبدأ تعابير القالب بالرمز $. val fooTemplateString ="$fooString has ${fooString.length} characters" println(fooTemplateString)// => My String Is Here! has 18 characters من أجل المتغيرات التي تحوي قيمة فارغة null يجب تحديد ذلك صراحة nullable. يمكن تحديد المتغير قابلاً للقيمة null بإلحاق? بنوعه. ويمكننا الوصول إلى المتحولات القابلة لـ null باستخدام مُعامل التشغيل? ،ويمكننا استخدام عامل التشغيل :? لتحديد قيمة بديلة للاستخدام إذا كان المتغير فارغ null. var fooNullable:String?="abc" println(fooNullable?.length)// => 3 println(fooNullable?.length ?:-1)// => 3 fooNullable = null println(fooNullable?.length)// => null println(fooNullable?.length ?:-1)// => -1 يمكن التصريح عن الدوال باستخدام الكلمة المفتاحية fun. وتحدد وسطاء الدالة بين قوسين بعد اسم الدالة. ويمكن لوسطاء الدالة اختياريًا الاحتواء على قيمة افتراضية. ويحدّد نوع إرجاع الدالة، إذا لزم الامر بعد المعطيات. fun hello(name:String="world"):String{return"Hello, $name!"} println(hello("foo"))// => Hello, foo! println(hello(name ="bar"))// => Hello, bar! println(hello())// => Hello, world! يمكن لبارامتر الدالة أن يوسم بالكلمة المفتاحية vararg للسماح بتمرير عدد متغير من المعطيات إلى الدالة. fun varargExample(vararg names:Int){ println("Argument has ${names.size} elements")} varargExample()// => لا يحوي الوسيط أية عناصر varargExample(1)// => يحوي الوسيط عنصر واحد varargExample(1,2,3)// => يحوي الوسيط 3 عناصر عندما تتكوّن الدّالة من تعبير واحد فقط، يمكن حذف الأقواس المنحنية { } ويتم تحديد الجسم(العملية) بعد رمز = fun odd(x:Int):Boolean= x %2==1 println(odd(6))// => false println(odd(7))// => true إذا كان نوع الاسترجاع يمكن استنتاجه فلسنا بحاجة لتحديده. fun even(x:Int)= x %2==0 println(even(6))// => true println(even(7))// => false الدوال يمكن أن تأخذ دوال كوسطاء وترجع دالة. fun not(f:(Int)->Boolean):(Int)->Boolean{return{n ->!f.invoke(n)}} يمكن للدوال المسمّاة أن تحدّد كوسائط باستخدام معامل التشغيل val notOdd = not(::odd) val notEven = not(::even) يمكن تحديد تعابير Lambda كوسائط val notZero = not {n -> n ==0} إذا احتوت lambda على بارامتر واحد فقط يمكن حذف التصريح عنه (جنبًا إلى جنب مع<- )وسيكون اسم البارامتر الوحيد it. val notPositive = not {it >0}for(i in 0..4){ println("${notOdd(i)} ${notEven(i)} ${notZero(i)} ${notPositive(i)}")} تستخدم الكلمة المفتاحية class للتّصريح عن الأصناف. class ExampleClass(val x:Int){ fun memberFunction(y:Int):Int{return x + y } infix fun infixMemberFunction(y:Int):Int{return x * y }} لإنشاء حالة instance جديدة نستدعي الباني. ولاحظ أن Kotlin لا تحوي الكلمة المفتاحية new. val fooExampleClass =ExampleClass(7) يمكن استدعاء دوال المستخدم باستخدام التنويت النقطي . println(fooExampleClass.memberFunction(4))// => 11 إذا وُسمت الدالة بالكلمة المفتاحية infix يمكن عندها أن يستدعى باستخدام التنويت الداخلي println(fooExampleClass infixMemberFunction 4)// => 28 أصناف البيانات Data classes هي طريقة مختصرة لإنشاء الأصناف التي تحتوي بيانات فقط وتنشئ دوال hashCode،equals و toString تلقائيًا. data classDataClassExample(val x:Int, val y:Int, val z:Int) val fooData =DataClassExample(1,2,4) println(fooData)// => DataClassExample(x=1, y=2, z=4) أصناف البيانات لديها دالة copy val fooCopy = fooData.copy(y =100) println(fooCopy)// => DataClassExample(x=1, y=100, z=4) يمكن أن تفكَك الكائنات Objects في متغيرات متعددة val (a, b, c)= fooCopy println("$a $b $c")// => 1 100 4 التفكيك باستخدام حلقة for for((a, b, c) in listOf(fooData)){ println("$a $b $c")// => 1 100 4} val mapData = mapOf("a" to 1,"b" to 2) Map.Entry قابل للتفكيك كذلك for((key, value) in mapData){ println("$key -> $value")} الدالة with مشابهة لعبارة with في جافا data classMutableDataClassExample(var x:Int, var y:Int, var z:Int) val fooMutableData =MutableDataClassExample(7,4,9) with (fooMutableData){ x -=2 y +=2 z--} println(fooMutableData)// => MutableDataClassExample(x=5, y=6, z=8) يمكننا إنشاء قائمة (لائحة) باستخدام الدالة listOf. ستكون القائمة غير قابلة للتغيير ولا يمكن إضافة عناصر أو إزالتها. val fooList = listOf("a","b","c") println(fooList.size)// => 3 println(fooList.first())// => a println(fooList.last())// => c// index يمكن الوصول إلى عناصر القائمة من خلال فهرسها println(fooList[1])// => b يمكن إنشاء قائمة قابلة للتعديل باستخدام الدالة mutableListOf val fooMutableList = mutableListOf("a","b","c") fooMutableList.add("d") println(fooMutableList.last())// => d println(fooMutableList.size)// => 4 يمكن إنشاء تعيين set باستخدام الدّالة setOf val fooSet = setOf("a","b","c") println(fooSet.contains("a"))// => true println(fooSet.contains("z"))// => false يمكننا إنشاء خريطة map باستخدام الدّالة mapOf val fooMap = mapOf("a" to 8,"b" to 7,"c" to 9) يمكن الوصول لقيم الـ Map من خلال مفاتيحها println(fooMap["a"])// => 8 تُمثل المتتالية Sequences مجموعات تقييمها مؤجل إلى حين الحاجة lazily-evaluated collections. ويمكننا إنشاء متتالية باستخدام الدّالة generateSequence. val fooSequence = generateSequence(1,{ it +1}) val x = fooSequence.take(10).toList() println(x)// => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] مثال لاستخدام متتالية لتوليد أرقام فيبانوتشي fibonacci fun fibonacciSequence():Sequence<Long>{ var a =0L var b =1L fun next():Long{ val result = a + b a = b b = result return a }return generateSequence(::next)} val y = fibonacciSequence().take(10).toList() println(y)// => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] تزوّد Kotlin دوال عالية الرتبة للعمل مع المجموعات collections val z =(1..9).map{it *3}.filter {it <20}.groupBy {it %2==0}.mapKeys {if(it.key)"even"else"odd"} println(z)// => {odd=[3, 9, 15], even=[6, 12, 18]} يمكن استخدام حلقة for مع أي شيء يؤمن التكرار. for(c in "hello"){ println(c)} تعمل حلقات while بشكل مشابه لعملها في اللغات الأخرى var ctr =0while(ctr <5){ println(ctr) ctr++}do{ println(ctr) ctr++}while(ctr <10) يمكن استخدام if كتعبير يرجع القيم. لهذا السبب ليس هناك حاجة لمعامل التشغيل :? في Kotlin. val num =5 val message =if(num %2==0)"even"else"odd" println("$num is $message")// => 5 is odd يمكن استخدام when كبديل لسلاسل if-else if val i =10 when { i <7-> println("first block") fooString.startsWith("hello")-> println("second block")else-> println("else block")} يمكن استخدام when مع المعطيات. when (i){0,21-> println("0 or 21") in 1..20-> println("in the range 1 to 20")else-> println("none of the above")} يمكن استخدام when كدالة ترجع القيم. var result = when (i){0,21->"0 or 21" in 1..20->"in the range 1 to 20"else->"none of the above"} println(result) يمكننا التحقق فيما إذا كان الكائن object من نوع محدّد باستخدام معامل التشغيل is. إذا مرر الكائن فحص النوع type check عندها يمكن استخدام هذا النوع دون الموائمة بشكل صريح. fun smartCastExample(x:Any):Boolean{if(x is Boolean){ X توائم أوتوماتيكيًا إلى Boolean return x }elseif(x is Int){ X توائم أوتوماتيكيًا إلى Int return x >0}elseif(x is String){ X توائم أوتوماتيكيًا إلى String return x.isNotEmpty()}else{returnfalse}} println(smartCastExample("Hello, world!"))// => true println(smartCastExample(""))// => false println(smartCastExample(5))// => true println(smartCastExample(0))// => false println(smartCastExample(true))// => true تعمل الموائمة الذكيّة smartcast أيضًا مع كتلة when fun smartCastWhenExample(x:Any)= when (x){ is Boolean-> x is Int-> x >0 is String-> x.isNotEmpty()else->false} الملحقات Extensions هي طريقة لإضافة وظائف جديدة للصنف class. وهي مشابهة لدوال ملحقات #C fun String.remove(c:Char):String{returnthis.filter {it != c}} println("Hello, world!".remove('l'))// => Hello, word! println(EnumExample.A)// => A println(ObjectExample.hello())// => hello أصناف Enum مشابهة لأنواع enum في جافا. enumclassEnumExample{ A, B, C } يمكن استخدام الكلمة المفتاحية object لإنشاء كائنات وحيدة. لا يمكننا تمثيلها ولكن يمكن أن نشير لحالتها الفريدة من خلال اسمها. وهذا مشابه لكائنات Scala singleton object ObjectExample{ fun hello():String{return"hello"}} fun useObject(){ObjectExample.hello() val someRef:Any=ObjectExample//كماهي تمامًا objects نستخدم أسماء الكائنات } ترجمة -وبتصرّف- للمقال Learn kotlin in Y Minutes
  4. جافا هي لغة برمجة حاسوبية للأغراض العامة، المتزامنة، المعتمدة على الصفوف، وغرضية التوجه // يبدأ التعليق المكتوب على خط واحد ب .// /* يبدو التعليق المكتوب على عدة سطور بهذا الشكل. */ /** *تبدو التعليقات في ملفات لغة البرمجة جافا بهذا الشكل *و تستخدم لوصف الصف أو الصفات المختلفة لكائن معين. *:الصفات الرئيسية * *@author- اسم مؤلف الكود. *ويحتوي على معلومات الاتصال كالبريد الالكتروني لمؤلف الكود أو للمؤلفين. *@version- النسخة الحالية من البرنامج. *@since -الوقت الذي تم فيه إضافة هذا الجزء من البرنامج. *@param -من أجل وصف البارامترات المختلفة للمنهج (method). *@return -لوصف القيمة التي يرجعها المنهج. *@deprecated -لإظهار انتهاء صلاحية الكود أو عدم وجوب استخدامه. *@see - روابط إلى جزء آخر من المستندات */ استورد الصف ArrayList بدلاً من استيراد الرزمة java.util كلّها import java.util.ArrayList; استورد جميع الصفوف الموجود داخل الرزمة import java.security.*; يحوي أي ملف جافا على صف عام، على المستوى الخارجي، له نفس اسم الملف public class LearnJava { ليعمل برنامج جافا يجب أن يحوي على تابع رئيسي بمثابة نقطة البدء public static void main(String[] args) { الدخل/الخرج دورة تطوير التطبيقات باستخدام لغة Python احترف تطوير التطبيقات مع أكاديمية حسوب والتحق بسوق العمل فور انتهائك من الدورة اشترك الآن الخرج استخدم ()System.out.println لطباعة السطور النصيّة System.out.println("Hello World!"); System.out.println( "Integer: " + 10 + " Double: " + 3.14 + " Boolean: " + true); استخدم ()System.out.print للطباعة بدون سطر جديد System.out.print("Hello "); System.out.print("World"); استخدم ()System.out.printf لتنسيق الطباعة بسهولة System.out.printf("pi = %.5f", Math.PI); // => pi = 3.14159 الدخل استخدم scanner لقراءة الدخل، يجب استيراد الصف ;java.util.Scanner Scanner scanner = new Scanner(System.in); لقراءة السلاسل المحرفية المُدخلة String name = scanner.next(); لقراءة البايتات المُدخلة byte numByte = scanner.nextByte(); لقراءة العدد الصحيح المُدخل int numInt = scanner.nextInt(); لقراءة العدد الحقيقي المٌدخل float numFloat = scanner.nextFloat(); لقراءة العدد الحقيقي مضاعف الدقة المُدخل double numDouble = scanner.nextDouble(); لقراءة القيمة المنطقية المُدخلة boolean bool = scanner.nextBoolean(); المتغيرات التصريح عن المتغيرات يتم التصريح عن متغير باستخدام // <type> <name> int fooInt; يتم التصريح عن مجموعة من المتغيرات من نفس النوع // <type> <name1>, <name2>, <name3> int fooInt1, fooInt2, fooInt3; تهيئة المتغير يتم تهيئة متغير باستخدام // <type> <name> = <val> int barInt = 1; يتم تهيئة مجموعة من المتغيرات من نفس النوع بنفس القيمة باستخدام // <type> <name1>, <name2>, <name3> // <name1> = <name2> = <name3> = <val> int barInt1, barInt2, barInt3; barInt1 = barInt2 = barInt3 = 1; أنواع المتغير البايت (Byte) : وهو 8bit ويستخدم لترميز الأعداد الصحيحة بين -128 و 127 byte fooByte = 100; إذ كنت ترغب بتفسير البايت كعدد صحيح موجب (بدون إشارة). فإن هذه العملية البسيطة من الممكن أن تساعد int unsignedIntLessThan256 = 0xff & fooByte; هذا يناقض عمل cast الذي من الممكن أن يعطي عدد سالب int signedInt = (int) fooByte; القصير (Short): وهو 16bit ويستخدم لترميز الأعداد الصحيحة بين -32,768 و 32,767 short fooShort = 10000; الصحيح (Integer): وهو 32bit ويستخدم لترميز الأعداد الصحيحة بين -2,147,483,648 و 2,147,483,647 int bazInt = 1; الطويل (Long): وهو 64bit ويستخدم لترميز الأعداد الصحيحة بين -9,223,372,036,854,775,808 و 9,223,372,036,854,775,807 long fooLong = 100000L; يستخدم المحرف L للدلالة على أن قيمة المتحول هي من النوع Long. و أي قيمة مُسندة للمتحول بدون استخدام L هي عبارة عن عدد صحيح int بشكل افتراضي. ملاحظة: إن الأنواع byte، short، int، long، هي أنواع ذات إشارة signed. أي من الممكن أن تحوي على قيم موجبة أو قيم سالبة. لا يوجد متغيرات بقيمة موجبة فحسب. ولكن المحارف تعتبر من نوعية unsigned ذات القيمة الموجبة فقط ذات 16bit. العائم (Float): وهو ذو دقة أحادية 32 bit IEEE 754 ويستخدم لترميز الأعداد الحقيقية ذات الفاصلة العائمة بين 149-^2 و 127^2 - (23-^2-2) float fooFloat = 234.5f; يستخدم المحرف f أو F للدلالة على أن قيمة المتحول هي من النوع Float. و إلا سيعتبر المتغير من النوع الحقيقي المضاعف. المضاعف (Double): وهو ذو دقة مضاعفة 64 bit IEEE 754 ويستخدم لترميز الأعداد الحقيقية ذات الفاصلة العائمة بين 1074-^2 و 1023^2 - (52-^2-2) double fooDouble = 123.4; القيم المنطقية (Boolean) ذات القيمة: true و false boolean fooBoolean = true; boolean barBoolean = false; Char هو محرف يونيكود 16بت أحادي char fooChar = 'A'; لا يمكن أعادة تهيئة المعطيات من النوع final final int HOURS_I_WORK_PER_WEEK = 9001; ولكن من الممكن تهيئتها بعد عملية التصريح عنها final double E; E = 2.71828; BigInteger -عبارة عن نوع الأعداد الصحيحة الثابتة بمستويات دقّة مختلفة. يستخدم نوع المعطيات BigInteger للسماح للمبرمج بالتعامل مع الأعداد الصحيحة التي هي أكبر من 64-بت. حيث تُخزّن الأعداد الصحيحة في مصفوفة من البايتات، التي يمكن التلاعب بها باستخدام التوابع المبنيّة في الصف BigInteger . يمكن تهيئة المتغير من النوع BigInteger بمصفوفة من البايتات أو بمصفوفة من السلاسل المحرفية. BigInteger fooBigInteger = new BigInteger(fooByteArray); BigDecimal عبارة عن نوع الأعداد الحقيقية الثابتة بمستويات دقة مختلفة. ويأخذ المتغير من النوع BigDecimal معاملين: عدد صحيح ذو حجم غير مقّيّد، يمثل مستوى الدقّة، وعدد صحيح أخر بحجم 32 بت. يسمح النوع BigDecimal بالتحكم بتقريب الأعداد الحقيقية. عندما يكون مطلوب مستوى دقة محدد للعدد الحقيقي يُنصح باستخدام BigDecimal . من الممكن تهيئة المتغير من النوع BigDecimal بأحد الأنواع التالية: int ،long ،double ،String ،BigInteger BigDecimal fooBigDecimal = new BigDecimal(fooBigInteger, fooInt); كُن حذرًا عند استخدامك للأنواع float ,double لأن عدم الدقّة في تحديد النوع سوف تُنسخ في الـ BigDecimal ويُفضل استخدام سلسلة محرفية ثابتة عندما تحتاج إلى قيمة دقيقة. BigDecimal tenCents = new BigDecimal("0.1"); String -السلاسل المحرفية String fooString = "My String Is Here!"; الحرف الخاص n\ يُعرف بحرف السطر الجديد الذي يحرك مؤشر الكتابة إلى بداية السطر التالي. String barString = "Printing on a new line?\nNo Problem!"; الحرف الخاص t\ يُعرف بالمسافة الأفقية، يقوم بتحريك مؤشر الكتابة مسافة معينة إلى النقطة التالية في السطر. String bazString = "Do you want to add a tab?\tNo Problem!"; System.out.println(fooString); System.out.println(barString); System.out.println(bazString); بناء السلاسل المحرفية: باستخدام عامل الجمع (+) تُعتبر الطريقة الأساسية (الأمثل) للقيام ببناء السلاسل المحرفية. String plusConcatenated = "Strings can " + "be concatenated " + “via + operator.”; System.out.println(plusConcatenated); // Output: Strings can be concatenated via + operator. باستخدام الصف StringBuilder لا تشكل هذه الطريقة أية سلاسل محرفية وسيطة، فقط تقوم بتخزين قطع السلاسل المحرفية و ربطها مع بعضها عندما يتم استدعاء التابع ()toString. تلميح: لا يعتبر الصف StringBuilder إجرائية آمنة. يوجد صف بديل آمن StringBuffer (مع تأثير بسيط على الأداء). StringBuilder builderConcatenated = new StringBuilder(); builderConcatenated.append("You "); builderConcatenated.append("can use "); builderConcatenated.append("the StringBuilder class."); System.out.println(builderConcatenated.toString()); // فقط الآن تمّ بناء السلسة المحرفية // Output: You can use the StringBuilder class. تكون StringBuilder فعاله عندما السلسلة المحرفية الكاملة المبنيّة مطلوبة في نهاية عملية ما. StringBuilder stringBuilder = new StringBuilder(); String inefficientString = ""; for (int i = 0 ; i < 10; i++) { stringBuilder.append(i).append(" "); inefficientString += i + " "; } System.out.println(inefficientString); System.out.println(stringBuilder.toString()); تتطلب inefficientString عمل أكثر، حيث أنها تنتج سلسلة محرفية عند كل دورة للحلقة. يتم ترجمة تجميع السلاسل المحرفية البسيطة بالعامل (+) إلى: toString() و StringBuilder تجنب استخدام تجميع السلاسل المحرفية داخل الحلقات. لاستخدام منسق السلاسل المحرفية طريقة أخرى بديلة لتوليد السلاسل المحرفية سريعة و قابلة للقراءة. String.format("%s may prefer %s.", “Or you”, “String.format()”); // Output: Or you may prefer String.format(). المصفوفات يجب تحديد حجم المصفوفة بشكل فوري بمجرد التصريح عنها. تستخدم الصيغة التالية للتصريح عن مصفوفة. // <datatype>[] <var name> = new <datatype>[<array size>]; // <datatype> <var name>[] = new <datatype>[<array size>]; int[] intArray = new int[10]; String[] stringArray = new String[1]; boolean boolArray[] = new boolean[100]; طريقة أخرى للتصريح عن مصفوفة و تهيئتها int[] y = {9000, 1000, 1337}; String names[] = {"Bob", "John", "Fred", "Juan Pedro"}; boolean bools[] = {true, false, false}; فهرسة المصفوفة - الوصول إلى عنصر فيها System.out.println("intArray @ 0: " + intArray[0]); تبدأ المصفوفات بالفهرس (0)، و هي قابله للتغيير . intArray[1] = 1; System.out.println("intArray @ 1: " + intArray[1]) ArrayLists; // => 1 مجموعة من أنواع المعطيات الأخرى، التي تستحق التدقيق فيها: ArrayLists : مشابهة للمصفوفات، إلا أنها تحوي على وظائف إضافية، وحجمها قابل للتعديل. LinkedLists : عبارة عن تنفيذ لقائمة مترابطة بشكل مضاعف، التي تنفذ جميع العمليات التي من المتوقع لقائمة مترابطة بشكل مضاعف أن تنفذها. Maps : عبارة عن وصل كائنات المفتاح إلى كائنات القيمة. وهي عبارة عن واجهة، و بالتالي لا يمكن أن يتم تشكيل كائنات منها. يجب أن يتم تحديد نوع المفاتيح و القيم بناءً على الكائن المُشكل من الصف. من الممكن ان يُربط كل مفتاح إلى قيمة واحدة فقط، ومن الممكن أن يظهر كل مفتاح مرة واحدة فقط. (لا يوجد نسخ) لتنفيذ الخريطة/الواجهة Map hashtable : يُستخدم هذا الصف الجدول ، HashMaps وينفذ الخريطة Map ، هذا يسمح بتثبيت زمن العمليات الأساسية، مثل الحصول على عنصر أو إدخال عنصر، حتى في المجموعات الكبيرة. TreeMap: عبارة عن Map ، مصنفة حسب مفاتيحها. يُحافظ كل تعديل على ترتيبه، إما باستخدام المقارن المزود عند عملية توليد الكائن، أو باستخدام المقارنات لكل كائن، اذا كان ينفذ واجهة قابلة للمقارنة. سوف يؤدي الفشل المدمج في تنفيذ واجهة قابلة للمقارنة، مع الفشل في تزويد مقارن إلى رمي ClassCastExceptions تأخذ عمليات إدخال و حذف عناصر زمن من الدرجة ((O(log(n ، لذلك تجنب استخدام بنى المعطيات هذه، إلا أذا كنت ترغب من الاستفادة من ميزة الترتيب. العمليات System.out.println("\n->Operators"); int i1 = 1, i2 = 2; // الطريقة المختصرة للتصريح عن عدة متغيرات في نفس الوقت العمليات الحسابية بسيطة System.out.println("1+2 = " + (i1 + i2)); // => 3 System.out.println("2-1 = " + (i2 - i1)); // => 1 System.out.println("2*1 = " + (i2 * i1)); // => 2 System.out.println("1/2 = " + (i1 / i2)); // => 0 (int يعيد int/int) System.out.println("1/2.0 = " + (i1 / (double)i2)); // => 0.5 باقي القسمة System.out.println("11%3 = "+(11 % 3)); // => 2 عمليات المقارنة System.out.println("3 == 2? " + (3 == 2)); // => false System.out.println("3 != 2? " + (3 != 2)); // => true System.out.println("3 > 2? " + (3 > 2)); // => true System.out.println("3 < 2? " + (3 < 2)); // => false System.out.println("2 <= 2? " + (2 <= 2)); // => true System.out.println("2 >= 2? " + (2 >= 2)); // => true العمليات المنطقية System.out.println("3 > 2 && 2 > 3? " + ((3 > 2) && (2 > 3))); // => false System.out.println("3 > 2 || 2 > 3? " + ((3 > 2) || (2 > 3))); // => true System.out.println("!(3 == 2)? " + (!(3 == 2))); // => true العمليات على مستوى البت ~ عامل إيجاد المتمم على مستولى البتات. << عامل الإزاحة الحسابية نحو اليسار (مع الأخذ بعين الاعتبار بت الإشارة). >> عامل الإزاحة الحسابية نحو اليمين (مع الأخذ بعين الاعتبار بت الإشارة). >>> عامل الإزاحة المنطقية نحو اليمين (بدون الأخذ بعين الاعتبار بت الإشارة). & على مستوى البتّات and العملية المنطقية ^ على مستوى البتّات xor العملية المنطقية | على مستوى البتّات or العملية المنطقية عمليات الزيادة int i = 0; System.out.println("\n->Inc/Dec-rementation"); العامل ++ يقوم بالزيادة بمقدار واحد. العامل – يقوم بالإنقاص بمقدار واحد. إذا تم وضع هذين العاملين قبل المتغير، فإنهن يقومان بالزيادة أو الإنقاص، ومن ثم يعيدان قيمة المتحول الجديدة. أما إذا تم وضع هذين العاملين بعد المتغير، فإنهما يقومان بإعادة قيمة المتحول، ومن ثم يقومان بالزيادة أو الإنقاص. System.out.println(i++); // i = 1, يطبع 0 (زياده لاحقة). System.out.println(++i); // i = 2, يطبع 2 (زيادة سابقة). System.out.println(i--); // i = 1, يطبع 2 (إنقاص لاحق) System.out.println(--i); // i = 0, يطبع 0 (إنقاص سابق) بنى التحكم System.out.println("\n->Control Structures"); عبارة if مشابهة لتلك الموجودة في لغة البرمجة C int j = 10; if (j == 10) { System.out.println("I get printed"); } else if (j > 10) { System.out.println("I don't"); } else { System.out.println("I also don't"); } حلقة While int fooWhile = 0; while(fooWhile < 100) { System.out.println(fooWhile); //زيادة العدّاد //(في المجال (0،1،..99 foowhile مكررة 100 مرة ، حيث أن قيم المتغير fooWhile++; } System.out.println("fooWhile Value: " + fooWhile); حلقة Do While int fooDoWhile = 0; do { System.out.println(fooDoWhile); //زيادة العدّاد //في المجال 0-99 foowhile مكررة 99 مرة ، حيث أن قيم المتغير fooDoWhile++; } while(fooDoWhile < 100); System.out.println("fooDoWhile Value: " + fooDoWhile); حلقة For : هيكلية الحلقة على الشكل التالي: for(<start_statement>; <conditional>; <step>) مثال for (int fooFor = 0; fooFor < 10; fooFor++) { System.out.println(fooFor); // fooFor 0->9 مكررة 10 مرات، حيث } System.out.println("fooFor Value: " + fooFor); //outerيتم الخروج من الحلقة الداخلية باستخدام جملة الهروب المعنونّة بـ outer: for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { if (i == 5 && j ==5) { break outer; //يخرج من الحلقة الخارجية أيضا، بدلاً من الخروج فقط من الحلقة الداخلية } } } حلقة For Each : قادرة على العمل مع المصفوفات و الكائنات for إن حلقة التي تنفذ الواجهات القابلة للتكرار. int[] fooList = {1, 2, 3, 4, 5, 6, 7, 8, 9}; هيكلية الحلقة على الشكل التالي: for (<object> : <iterable>) تقرأ هكذا: لكل عنصر في ال iterable ملاحظة: يجب أن يتطابق نوع الكائن مع نوع عناصر ال iterable for (int bar : fooList) { System.out.println(bar); //سوف تتكرر 9 مرات و يطبع 1-9 على سطور جديدة. } Switch تعمل Switch مع أنواع المعطيات: int، char، short ،byte وأيضًا من الممكن أن تعمل مع النوع Enum و String و مجموعة من الصفوف الخاصة التي تجمع الأنواع البدائية: Character, Byte, Short, Integer. بدءًا من جافا 7، من الممكن استخدام النوع String. ملاحظة: تذكر عدم استخدام التعليمة "break" في نهاية حالة جزئية إذا كان هناك يوجد حالة أخرى تستوفي الشرط أيضًا. int month = 3; String monthString; switch (month) { case 1: monthString = "January"; break; case 2: monthString = "February"; break; case 3: monthString = "March"; break; default: monthString = "Some other month"; break; } System.out.println("Switch Case Result: " + monthString); عبارة (Try-with-resources) في Java الإصدار +7 تُستخدم في جافا عبارات Try-catch-finally، و لكن في جافا +7 أيضا ً مُتاح استخدام try-with-resources إن عبارات try-with-resources تُبسط عمل عبارات try-catch-finally عن طريق إغلاق المصادر بشكل أتوماتيكي. لاستخدام try-with-resources ضمّن كائن من صف في تعليمة try. try (BufferedReader br = new BufferedReader(new FileReader("foo.txt"))) { // يمكنك أن تجرّب شيئًا يحتاج إلى استثناء System.out.println(br.readLine()); // في جافا 7 المصادر مغلقة دومًا حتى لو كان هناك استثناء } catch (Exception ex) { //catch سوف يتم إغلاق المصدر قبل أن تنفذ عبارة System.out.println("readLine() failed."); } في هذه الحالة، لا يوجد حاجة إلى عبارة finally حيث أن BufferedReader تم إغلاقه للتو. يتم استخدام هذا لتجنب الحالات الحرجة، عندما عبارة finally لا يمكن استدعاءها. الاختزال الشرطي يمكنك استخدام العامل "?" لسرعة الأسناد . تُقرأ كما يلي: "إذا كانت العبارة صحيحة ، استخدم <أول قيمة> و إلا استخدم <القيمة الثانية> int foo = 5; String bar = (foo < 10) ? "A" : "B"; System.out.println("bar : " + bar); // "bar : A"تطبع // صحيحة (foo < 10) لأن العبارة //أو بشكل مبسط: System.out.println("bar : " + (foo < 10 ? "A" : "B")); التحويل بين أنواع المعطيات تحويل المعطيات تحويل السلاسل المحرفية إلى عدد صحيح Integer.parseInt("123");//يعيد نسخة العدد الصحيح من السلسة المحرفية "123" تحويل العدد الصحيح إلى سلسلة محرفية Integer.toString(123);//يعيد النسخة المحرفية من العدد الصحيح 123 راجع تحويل المعطيات للأنواع التالية من الصفوف: Double Long String الصفوف و التوابع للتصريح عن كائن من الصف Bicycle استخدم الكلمة المفتاحية new Bicycle trek = new Bicycle(); استدعاء توابع الكائن trek.speedUp(3); // setter , getter يجب عليك استخدام توابع الواضع و الآخذ trek.setCadence(100); يُعيد التابع ()toString التمثيل النصي للكائن. System.out.println("trek info: " + trek.toString()); التهيئة مزدوجة الأقواس لا تملك جافا تراكيب لتشكيل مجموعات ساكنة من المعطيات بطريقة سهلة عادةً يتم استخدام الطريقة التالية: private static final Set<String> COUNTRIES = new HashSet<String>(); static { COUNTRIES.add("DENMARK"); COUNTRIES.add("SWEDEN"); COUNTRIES.add("FINLAND"); } ولكن يوجد طريقة أنيقة لإنجاز الشيء نفسه بشكل أسهل، تُدعى هذه الطريقة مزدوجة الأقواس private static final Set<String> COUNTRIES = new HashSet<String>() {{ add("DENMARK"); add("SWEDEN"); add("FINLAND"); }} يولد القوس الاول { } صف داخلي بدون اسم AnonymousInnerClass ويعرّف القوس الثاني{ } كتلة مهيئ الحالة instance. يتم استدعاء هذه الكتلة، عندما يتم توليد الصف الداخلي لا يعمل هذا فقط للمجموعات، إنما يعمل أيضًا لكل الصفوف غير النهائي non-final يمكنك تضمين صفوف أخرى خارجية غير عامة في ملف جافا ، ولكنها ليست بممارسة جيدة. بدلاً من ذلك يفضّل فصل الصفوف في ملفات جافا منفصلة صيغة التصريح عن صف: // <public/private/protected> class <class name> { // } حقول البيانات, أنماط الباني, الوظائف كلها في الداخل تستدعى الوظائف كالدوال في جافا class Bicycle { حقول و متغيرات الصف Bicycle Public: يمكن الوصول إليه من أي مكان public int cadence; Private: يمكن الوصول إليه فقط من داخل الصف private int speed; Protected: يمكن الوصول إليه فقط من داخل الصف و الصفوف الفرعية protected int gear; default: يمكن الوصول إليه فقط من داخل الرزمة. String name; Static متغير صف ساكن static String className; البلوك الساكن Static لا تملك جافا تنفيذ للبواني الساكنة، ولكن جافا تملك كتل برمجية لتعريف متغيرات الصف (المتغيرات لساكنة) سيتم استدعاء هذا البلوك عند تحميل الصف. static { className = "Bicycle"; } الباني هي الطريقة التي يتم فيها توليد الصفوف. هذا هو الباني public Bicycle() { //يمكنك أيضا استدعاء باني آخر: // this(1, 50, 5, "Bontrager"); gear = 1; cadence = 50; speed = 5; name = "Bontrager"; } هذا عبارة عن باني ذو معاملات public Bicycle(int startCadence, int startSpeed, int startGear, String name) { this.gear = startGear; this.cadence = startCadence; this.speed = startSpeed; this.name = name; } صيغة الدالة // <public/private/protected> <return type> <function name>(<args>) أصناف الجافا غالبًا ما تنفّذ getters وsetters في حقولها صيغة التصريح عن الدالة // <access modifier> <return type> <method name>(<args>) public int getCadence() { return cadence; } دوال void لا تحتاج تصريح عودة public void setCadence(int newValue) { cadence = newValue; } public void setGear(int newValue) { gear = newValue; } public void speedUp(int increment) { speed += increment; } public void slowDown(int decrement) { speed -= decrement; } public void setName(String newName) { name = newName; } public String getName() { return name; } دالة لتوليد قيم الصفات لهذا الكائن @Override // موروثة من الصف لهذا الكائن. public String toString() { return "gear: " + gear + " cadence: " + cadence + " speed: " + speed + " name: " + name; } الصف PennyFarthing عبارة عن صنف فرعي من Bicycle class PennyFarthing extends Bicycle { -Penny Farthings //هي عباره عن نوع من الدراجات بعجلات أمامية كبيرة. public PennyFarthing(int startCadence, int startSpeed) { // super استدعي الباني الأب باستخدام الكلمة المفتاحية super(startCadence, startSpeed, 0, "PennyFarthing"); } يجب عليك تعليم المنهج الذي تُعيد كتابته بـ annotation@. @Override public void setGear(int gear) { this.gear = 0; } Object casting بما أن الصف PennyFarthing يرث الصف Bicycle : فمن الممكن القول أن: Bicycle هو PennyFarthing، و يمكن أن نكتب: // Bicycle bicycle = new PennyFarthing(); هذا يُدعى"object casting" حيث يتم تشكيل كائن من كائن آخر. الواجهات صيغة التصريح عن واجهة // <access-level> interface <interface-name> extends <super-interfaces> { // // Constants // // Method declarations // } مثال - الطعام: public interface Edible { public void eat(); //يجب على أي صف ينفذ هذه الواجهة // ان ينفذ هذه الدالة } public interface Digestible { public void digest(); // منذ جافا 8، من الممكن أن تملك الواجهات دالة افتراضية. public default void defaultMethod() { System.out.println("Hi from default method ..."); } } الآن يمكنك تشكيل صف يُنفذ كلا من هاتين الواجهتين. public class Fruit implements Edible, Digestible { @Override public void eat() { // ... } @Override public void digest() { // ... } } في جافا، يمكنك أن تمدد(ترث) صف واحد فقط، ولكن يمكنك أن تنفذ عدّة واجهات . على سبيل المثال: public class ExampleClass extends ExampleClassParent implements InterfaceOne, InterfaceTwo { @Override public void InterfaceOneMethod() { } @Override public void InterfaceTwoMethod() { } } الصفوف التجريدية صيغة التصريح عن صف تجريدي: // <access-level> abstract class <abstract-class-name> extends // <super-abstract-classes> { // // Constants and variables // // Method declarations // } لا يمكن تشكيل كائنات من الصفوف المجردة (تمثيل الصفوف المجردة) يمكن للصفوف المجردة أن تعرف عن دوال مجرّدة. المناهج المجردة لا تحوي على جسم ويجب تعليمها بالكلمة المفتاحية abstract . يجب على الصفوف الأبناء غير المجردة أن تعيد كتابة Override@ جميع الدوال المجردة في صفوفها العليا. من الممكن أن تكون الصفوف المجرّدة مفيدة عند الجمع بين منطق التكرار و السلوك المخصص، ولكن بما أن الصف المجرد يتطلب الوراثة، فإن الصفوف المجردة تنتهك منطق "التركيب عبر الوراثة". لذلك خُذ بعين الاعتبار الطرق الأخرى التي تستخدم هذا التركيب public abstract class Animal { private int age; public abstract void makeSound(); من الممكن أن يحوي المنهج جسم public void eat() { System.out.println("I am an animal and I am Eating."); //(private)ملاحظة: من الممكن هنا الوصول للمتغيرات الخاصة age = 30; } public void printAge() { System.out.println(age); } يمكن أن تحوي الصفوف المجردة على دالة رئيسية public static void main(String[] args) { System.out.println("I am abstract"); } } class Dog extends Animal { لاحظ بأنه لا يزال هناك حاجة لإعادة كتابة الدوال التجريدية في الصف التجريدي // @Override public void makeSound() { System.out.println("Bark"); // age = 30; ==> ERROR! // Animal خاص بالنسبة للصف age خطأ- و ذلك لأن المتغير } ملاحظة: من الممكن أن تحصل على خطأ إذا قمت هنا باستخدام Override@ لأن جافا لا تسمح لك بإعادة كتابة الدوال الساكنة. ما يحدث هنا يُدعى إخفاء الدالة METHOD HIDING public static void main(String[] args) { Dog pluto = new Dog(); pluto.makeSound(); pluto.eat(); pluto.printAge(); } الصفوف النهائية final class صيغة التصريح عن الصفوف النهائية // <access-level> final <final-class-name> { // // Constants and variables // // Method declarations // } الصفوف النهائية هي عبارة عن الصفوف التي لا يمكن وراثتها، (أي هي الولد النهائي). بطريقة ما، تُعد الصفوف النهائية معاكس الصفوف المجردة. لأنه يجب توسيع (وراثة) الصفوف المجردة، أما الصفوف النهائية لا يمكن توسيعها. public final class SaberToothedCat extends Animal { لاحظ بأنه ما يزال يوجد حاجة لإعادة كتابة الدوال المجردة في الصف المجرد.// @Override public void makeSound() { System.out.println("Roar"); } } الدوال النهائية public abstract class Mammal() { // صيغة الدالة النهائية: // <access modifier> final <return type> <function name>(<args>) // لا يمكن إعادة كتابة الدوال النهائية بواسطة صف ابن، // وبالتالي الدالة النهائية هو أخر تنفيذ للدالة . public final boolean isWarmBlooded() { return true; } } النوع Enum еnum هي طريقة تنظيمية للثوابت (Constants) في الكود بحيث تجمع الثوابت التي لها علاقة ببعضها تحت فئة واحدة بطريقة تنظم الوصول إليها .بما أن هذه القيم هي عبارة عن ثوابت، لذلك فإن أسماء الحقول من النوع enum يجب أن تكتب بحروف كبيرة. في لغة جافا يتم تعريف هذا النوع باستخدام الكلمة المفتاحية enum. لتعريف متحول عن يوم من أيام الأسبوع، باستخدام النوع enum : public enum Day { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY } من الممكن استخدام متغيرنا Day من النوع enum كما يلي: public class EnumTest { //Enumالمتغير من النوع Day day; public EnumTest(Day day) { this.day = day; } public void tellItLikeItIs() { switch (day) { case MONDAY: System.out.println("Mondays are bad."); break; case FRIDAY: System.out.println("Fridays are better."); break; case SATURDAY: case SUNDAY: System.out.println("Weekends are best."); break; default: System.out.println("Midweek days are so-so."); break; } } public static void main(String[] args) { EnumTest firstDay = new EnumTest(Day.MONDAY); firstDay.tellItLikeItIs(); // => Mondays are bad. EnumTest thirdDay = new EnumTest(Day.WEDNESDAY); thirdDay.tellItLikeItIs(); // => Midweek days are so-so. } } الأنواع enum هي فعالة بشكل أكبر مما تم توضيحه فوق. من الممكن أن يحتوي جسم المتغير enum على دوال و حقول أخرى. ترجمة -وبتصرّف- للمقال Learn Java in Y Minutes
×
×
  • أضف...