مدونة فؤاد المالكي https://fouad.io مدونة مبرمج Tue, 26 May 2020 11:05:23 +0000 ar hourly 1 https://wordpress.org/?v=6.9.4 جمل switch المحسنة في جافا 14 https://fouad.io/2020/05/enhanced_switch_in_java14/ https://fouad.io/2020/05/enhanced_switch_in_java14/#respond Mon, 25 May 2020 14:30:35 +0000 https://fouad.io/?p=4575 بعدما قامت أوراكل بتغيير جدولة الإصدارات الجديدة من لغة جافا (إصدار جديد كل 6 أشهر)، أصبحنا نرى العديد من التحسينات والمزايا الجديدة. من أهم هذه التحسينات كانت على جمل switch والتي هي من أساسيات لغة جافا وموجودة منذ أول إصدار. تم إضافة هذه التحسينات كميزة تجريبية preview feature في الإصدارين 12 و 13 ثم أصبحت ميزة أساسية ابتداءً من جافا 14 والتي أصدرت يوم 17 مارس 2020.

منذ الإصدار 11 من جافا، تم إضافة ما يسمى بـ (ميزة تجريبية preview feature)، وهي ميزة تكون غير مفعلة بشكل افتراضي ويجب استخدام command-line option لتفعيلها (enable-preview--). عادةً لا ينصح باستخدامها في كود production حيث أن هذه الميزة قد يتم التعديل عليها أو إزالتها بشكل كامل في إصدارات لاحقة.

 

مراجعة switch statement ما قبل جافا 14

جملة switch تستخدم للتحكم في مسار تنفيذ البرنامج بناءً على قيمة متغير (variable) أو عبارة معينة (expression). الكود الآتي يوضح استخدام بسيط لجملة switch:

public static void foo(int x)
{
	switch(x + 1)
	{
		case 1:
			System.out.println(1);
			break;
		case 3:
			System.out.println(3);
			System.out.println(3);
		case 2:
			System.out.println(2);
			break;
		case 4:
		case 5: 
		{
			System.out.println(5);
			break;
		}
		case 5: return;
		default:
			System.out.println(0);
	}
}

أولاً يتم تنفيذ العبارة الموجودة ما بين الأقواس () التي تلي الكلمة switch. بعدها يتم المرور على الحالات (case) واحدة تلو الأخرى. عند التطابق مع قيمة أحد الحالات، يتم تنفيذ الأسطر الخاصة بهذه الحالة، ثم يتم تنفيذ الحالات التالية لها بالتسلسل ما لم تتواجد الكلمة break والتي تخرج من جملة switch إلى الجملة التالية (أيضاً return و continue تتسبب في تغيير مسار تنفيذ البرنامج). في حال لم يتم التطابق مع جميع الحالات، يتم تنفيذ الحالة default. وإذا لم تتطابق جميع الحالات ولا توجد حالة default، سيتم الخروج من جملة switch.

هنا بعض النقاط التي توضح تفاصيل أخرى لجمل switch:

  • العبارة الموجودة ما بين الأقواس () التي تلي الكلمة switch يجب أن تكون ضمن أحد الأنواع التالية: [byte و Byte و short و Short و int و Integer و char و Character] وأيضاً enums. منذ جافا 7، تم إضافة String.
  • إذا كان نوع المتغير المستخدم enum، فيجب استخدام القيم بدون اسم الـ enum. فمثلاً DayOfWeek هو enum، ويجب أن تكون الحالات بهذا الشكل: case SUNDAY وليس case DayOfWeek.SUNDAY.
  • تمرير null لجملة switch سيتسبب بـ NullPointerException.
  • لا يشترط وجود أقواس {} للأسطر الخاصة بالحالات (كما هو الحال في case 3).
  • الحالة default هي اختيارية. أيضاً ليس شرطاً أن تكون آخر حالة، ويمكن وضعها كأول حالة مثلاً.
  • الكلمة break يمكن استخدامها في الحالة الأخيرة، ولكن لا فائدة من ذلك.

 

التحسينات على switch statement في جافا 14

  • أصبح بالإمكان إضافة أكثر من قيمة في نفس الحالة:

public static void foo(int x)
{
	switch(x)
	{
		case 1, 3, 5, 7, 9:
			System.out.println("odd");
			break;
		case 0, 2, 4, 6, 8:
			System.out.println("even");
	}
}

  • يمكن استبدال النقاط : بالأسهم <-، وهنا يجب أن تكون أسطر الحالات سطر واحد فقط كحد أقصى (يمكن أن يكون block محاط بالأقواس {}). أيضاً لن يتم تنفيذ الحالات بالتسلسل عند استخدام الأسهم، حيث سيتم تنفيذ السطر الخاص بالحالة المطابقة فقط (no fall-through):

public static void foo(int x)
{
	switch(x)
	{
		case 1, 3, 5, 7, 9 -> System.out.println("odd");
		case 0, 2, 4, 6, 8 -> System.out.println("even");
	}
}

لا يمكن استخدام النقاط : والأسهم <- معاً في جملة switch واحدة.

 

إضافة switch expression في جافا 14

بالإضافة لجملة switch، تم إضافة ميزة جديدة تسمى بـ switch expression (عبارة switch). والعبارة (expression) تعني بأنه يمكن تحويلها إلى قيمة تُسند إلى متغير. هنا مثال:

var dayOfWeek = LocalDate.now().getDayOfWeek();
		
boolean weekend = switch(dayOfWeek)
{
	case FRIDAY, SATURDAY -> true;
	case SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY -> false;
};

أيضاً تم إضافة keyword جديد في جافا 14 (yield) يستخدم لإرجاع قيمة من داخل أحد الحالات (عملها يشبه عمل return من داخل الدوال). يستخدم إذا كان السطر الخاص بالحالة عبارة عن block محاط بأقواس {} أو تم استخدام النقاط : بالأسهم <-:

var dayOfWeek = LocalDate.now().getDayOfWeek();

boolean vacation = switch(dayOfWeek)
{
	case FRIDAY, SATURDAY ->
	{
		if(workNotFinishedYet) yield false;
		else yield true;
	}
	case SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY -> false;
};

var dayOfWeek = LocalDate.now().getDayOfWeek();

boolean weekend = switch(dayOfWeek)
{
	case FRIDAY, SATURDAY: yield true;
	case SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY: yield false;
};

 

يمكن أيضاً إضافة throw exception داخل عبارة switch:

public static boolean isEven(int x)
{
	return switch(x)
	{
		case 1, 3, 5, 7, 9 -> false;
		case 0, 2, 4, 6, 8 -> true;
		default -> throw new IllegalArgumentException("value must be in between 0 and 9");
	};
}

لاحظ بأن الـ switch expression لابد أن تنتهي بفاصلة منقوطة ; بعكس الـ switch statement.

بعكس الـ switch statement أيضاً، يجب تغطية جميع الحالات (cases) عند استخدام الـ switch expression.

]]>
https://fouad.io/2020/05/enhanced_switch_in_java14/feed/ 0
بعض المهارات التقنية التي يجب على كل مبرمج معرفتها والإلمام بأساسياتها https://fouad.io/2019/02/essential_programmer_skills/ https://fouad.io/2019/02/essential_programmer_skills/#comments Sat, 09 Feb 2019 09:39:38 +0000 https://fouad.io/?p=4551 هذه تغريدات كتبتها بالأمس على موقع تويتر، وأحببت نشرها على المدونة ليسهل الوصول إليها.

 

1- أسفل هذه التغريدة سأقوم بسرد بعض المهارات التقنية التي يجب على كل مبرمج معرفتها والإلمام بأساسياتها. #حزب_المبرمجين

2- الخوارزميات وحل المشاكل برمجياً: بحيث يتمكن المبرمج من تحليل مشكلة معينة وتحويلها إلى أكواد باستخدام عناصر البرمجة الأساسية مثل الجمل الشرطية (مثل if) وجمل التكرار (مثل for و while) واستخدام المصفوفات (arrays) وغيرها.

3- البرمجة الكائنية Object-Oriented: بحيث يتمكن المبرمج من تمثيل الأشياء في الحياة الواقعية على هيئة Classes و Objects، واستخدام المفاهيم المساعدة مثل Inheritance و Abstraction و Encapsulation و Polymorphism.

4- تراكيب البيانات Data Structures: وهي حاويات لحفظ عناصر داخلها بترتيب معينة، بحيث يكون الوصول إلى عناصرها وإجراء بعض العمليات عليها (مثل search و sort) ذو كفاءة عالية. ومن أمثلتها: List, Queue, Set, Tree, Map.

5- أنماط التصميم البرمجية Design Patterns: وهي عبارة عن حلول ونماذج جاهزة تستخدم لتصميم هيكلة الـ Objects وطرق إنشائها وكيفية التواصل بين بعضها البعض. من أمثلتها: Singleton, Builder, Factory method, Adapter, Composite, Decorator, Facade, Command, Iterator, Strategy, Visitor.

6- البرمجة المتوازية Concurrent Programming: بحيث يتم تنفيذ البرنامج باستخدام أكثر من Thread. كل Thread يقوم بتنفيذ عدة أوامر متتالية، والـ Threads جميعها يتم تنفيذها بشكل متزامن أو في آن واحد. توجد مفاهيم ذات علاقة، مثل: synchronization, locks, mutex, semaphore, race condition.

7- التعامل مع الملفات: يجب على المبرمج أن يفرق بين الملفات النصية والملفات الغير نصية (binary) (مثل ملفات الصور والفيديو وغيرها) وكيفية قراءتها وتخزينها على المساحة التخزينية، ويتعرف على مفهوم الـ Encoding (مثل UTF-8) وهي الطريقة المستخدمة لتمثيل النصوص على هيئة binary.

8- الملفات النصية الشائعة: هناك عدة أشكال formats تستخدم لتخزين البيانات على هيئة نصوص. مثل: CSV, XML, JSON, YAML.

9- التعابير المنطقية Regular Expressions أو اختصاراً Regex: وهي سلسلة من الحروف تستخدم للبحث عن تطابق داخل نص معين، ويمكن استخدامها أيضاً لتبديل كلمات بأخرى داخل النص أو للتحقق من مدخلات نصية Text Validation. أنصح باستخدام هذا الموقع أثناء بناء regex معين: https://regex101.com

10- التعامل مع قواعد البيانات Databases: بحيث يتمكن المبرمج من كتابة برنامج يتصل بقاعدة بيانات ويستخدم لغة SQL للبحث داخل الجداول أو لإضافة أو تعديل سجلات. معظم لغات البرمجة بها أدوات تساعد في هذه العملية. فمثلاً في لغة جافا، يمكن استخدام JDBC.

11- استخدام أدوات إدارة وحفظ الأكواد Version Control Systems: وهي أدوات تساعد في حفظ الأكواد مع جميع التغييرات التي طرأت عليها، مع إمكانية استرجاع نسخ سابقة من الكود. من أشهرها: git أيضاً GitHub.com يعتبر أشهر المواقع التي تستضيف برامج مفتوحة المصدر داخل مستودعات git.

12- استخدام بيئات التطوير IDEs: وهي برامج ومحررات نصوص مع العديد من الأداوت التي تسهل من عملية كتابة الأكواد. أنصح باستخدام الاختصارات shortcuts والتعود عليها بشكل دائم للقيام بالمهام والعلميات داخل الـ IDE والتي تزيد من الإنتاجية. أنصح الجميع بالـ IDEs الرائعة من تطوير @JetBrains

13- قراءة التوثيق الرسمي للغة البرمجة documentation: وهو مستند يحتوي على وصف لجميع الـ classes والدوال الموجودة في مكتبات اللغة الرسمية مع توضيح لطريقة عملها. مثلاً في لغة جافا، لا غنى عن JavaDoc.

https://docs.oracle.com/en/java/javase/11/docs/api/index.html

14- مهارة الـ debugging: بحيث يتمكن المبرمج من إيجاد الأخطاء البرمجية داخل الكود. ويمكن استخدام الـ debugger Tools التي تزودها اللغة أو تجدها داخل الـ IDE. ملاحظة: أحياناً طباعة جمل معينة على الـ console بداخلها قيم لمتغيرات داخل الكود تكون عملية كافية وسريعة لإيجاد الأخطاء.

15- استخدام الـ Build Tools: وهي أداوت تسهل عملية الترجمة compiling وإدارة الـ dependencies وبناء الحزم النهائية للبرنامج مع الأخذ بالاعتبار اختلاف البيئات. في لغة جافا مثلاً، يوجد maven و gradle.

16- اختبار البرمجيات: وهي القيام بكتابة أكواد جانبية تختبر جميع وظائف البرنامج الأساسية للتأكد من صحة عملها. بحيث لو تم تغيير جزء من البرنامج الأساسي، تكشف هذه الاختبارات عن أي خلل في التغيير. أشهر مثال على هذه الاختبارات: Unit Testing. وفي لغة جافا، أشهر مكتبة للقيام بذلك هي JUnit.

17- التعامل مع HTTP: وهو بروتوكول الإنترنت الأشهر، ويستخدم للتواصل بين الـ Client والـ Server لإرسال بيانات. يجب معرفة الفروقات بين الـ HTTP Methods، وأشهرها GET و POST. أيضاً يجب معرفة مكونات الـ HTTP Request والـ HTTP Response ومعرفة أشهر الـ HTTP Headers و HTTP Status Codes.

18- التعامل مع RESTful Web Services: الـ REST هو آلية لتنظيم الخدمات الموجودة على سيرفر على هيئة APIs، بحيث يمكن لأي Client استخدام هذه الخدمات بالاتصال عبر البروتوكول HTTP. أنصح أي مبرمج بالاطلاع على GitHub REST API كمثال وكتابة HTTP Client يستخدمها: https://developer.github.com/v3

 

]]>
https://fouad.io/2019/02/essential_programmer_skills/feed/ 2
الـ Lambda Expressions في لغة جافا https://fouad.io/2018/09/lambda-expressions-in-java/ https://fouad.io/2018/09/lambda-expressions-in-java/#comments Fri, 21 Sep 2018 21:00:06 +0000 https://fouad.io/?p=4461 ماهية الـ Lambda Expression:

هي syntax جديد أضيف على لغة جافا منذ الإصدار الثامن Java SE 8، تختصر عملية كتابة بعض الأكواد الطويلة إلى أكواد أقصر. تساعد الـ Lambda Expression في تمرير كود (أو functionality) إلى الدوال على هيئة parameter (أو بمعنى آخر code as data) على هيئة أكواد مختصرة.

في الإصدار الأقدم من جافا (7 وما قبل)، عندما تريد تمرير block of code ليتم تنفيذه داخل دالة أخرى، عادةً يتم تمرير عيّنة (instance) من كلاس أو interface بداخله دالة واحدة. على سبيل المثال java.lang.Runnable، حيث يحتوي على دالة واحدة اسمها run:

public interface Runnable
{
    void run();
}

بحيث يتم إنشاء عيّنة عن طريق إنشاء كلاس يطبق دوال الـ interface:

public class HelloWorldPrinter implements Runnable
{
    @Override
    public void run()
    {
        System.out.println("Hello World");
    }
}

وبعد ذلك يتم تمريرها إلى دالة بهذا الشكل:

public class Main
{
    private static void executeCode(Runnable runnable)
    {
        runnable.run();
    }
    
    public static void main(String[] args)
    {
        executeCode(new HelloWorldPrinter());
    }
}

ويمكن أيضاً إنشاء عيّنة بشكل مباشر دون استخدام كلاس خارجي باستخدام ما يسمى بـ Anonymous class:

public class Main
{
    private static void executeCode(Runnable runnable)
    {
        runnable.run();
    }
    
    public static void main(String[] args)
    {
        executeCode(new Runnable()
        {
            @Override
            public void run()
            {
                System.out.println("Hello World");
            }
        });
    }
}

يوجد أيضاً interface آخر شبيه بالـ Runnable ولكن يقوم بإرجاع قيمة، ألا وهو java.util.concurrent.Callable

ويمكن أيضاً تعريف interface جديد واستخدامه بنفس الطريقة:

public interface MathOperation
{
    int calculate(int a, int b);
}

public class Main
{
    private static int executeMathOperation(int a, int b, MathOperation mathOperation)
    {
        return mathOperation.calculate(a, b);
    }
    
    public static void main(String[] args)
    {
        int a = 2;
        int b = 1;
        
        int c = executeMathOperation(a, b, new MathOperation()
        {
            @Override
            public int calculate(int a, int b)
            {
                return a + b;
            }
        });
        System.out.println("a + b = " + c);
        
        c = executeMathOperation(a, b, new MathOperation()
        {
            @Override
            public int calculate(int a, int b)
            {
                return a - b;
            }
        });
        System.out.println("a - b = " + c);

        c = executeMathOperation(a, b, new MathOperation()
        {
            @Override
            public int calculate(int a, int b)
            {
                return a * b;
            }
        });
        System.out.println("a x b = " + c);

        c = executeMathOperation(a, b, new MathOperation()
        {
            @Override
            public int calculate(int a, int b)
            {
                return a / b;
            }
        });
        System.out.println("a ÷ b = " + c);
    }
}

أما بالنسبة للإصدارات الحديثة (8 أو أحدث)، فيمكن استخدام الـ Lambda Expression حيث تختصر الكود السابق إلى الشكل التالي:

public class Main
{
    private static int executeMathOperation(int a, int b, MathOperation mathOperation)
    {
        return mathOperation.calculate(a, b);
    }
    
    public static void main(String[] args)
    {
        int a = 2;
        int b = 1;
        
        int c = executeMathOperation(a, b, (int x, int y) -> {return x + y;});
        System.out.println("a + b = " + c);
        
        c = executeMathOperation(a, b, (int x, int y) -> {return x - y;});
        System.out.println("a - b = " + c);

        c = executeMathOperation(a, b, (int x, int y) -> {return x * y;});
        System.out.println("a x b = " + c);

        c = executeMathOperation(a, b, (int x, int y) -> {return x / y;});
        System.out.println("a ÷ b = " + c);
    }
}

لاحظ بأنه قمنا باستبدال أسماء المتغيرات الداخلية a و b بـ x و y حتى لا تتعارض مع أسماء المتغيرات الخارجية، حيث أصبح الـ scope للمتغيرات داخل الـ Lambda Expression هو نفسه للمتغيرات داخل الدالة main.

وبما أن الـ lambda expression body يتكون من سطر واحد فقط، فيمكن إزالة الأقواس المحيطة به، بالإضافة إلى إزالة كلمة return وأيضاً الفاصلة المنقوطة ;

أيضاً، يمكن إزالة النوع الموجود داخل الـ lambda expression parameters. سيصبح الكود بهذا الشكل:

public class Main
{
    private static int executeMathOperation(int a, int b, MathOperation mathOperation)
    {
        return mathOperation.calculate(a, b);
    }
    
    public static void main(String[] args)
    {
        int a = 2;
        int b = 1;
        
        int c = executeMathOperation(a, b, (x, y) -> x + y);
        System.out.println("a + b = " + c);
        
        c = executeMathOperation(a, b, (x, y) -> x - y);
        System.out.println("a - b = " + c);

        c = executeMathOperation(a, b, (x, y) -> x * y);
        System.out.println("a x b = " + c);

        c = executeMathOperation(a, b, (x, y) -> x / y);
        System.out.println("a ÷ b = " + c);
    }
}

لو أن الـ lambda expression paramters يتكون من متغير واحد فقط، لكان بالإمكان إزالة الأقواس المحيطة به أيضاً.

استخدام الـ lambda expression في لغة الجافا أضاف لها ما يسمى بالبرمجة الوظيفية Functional Programming، حيث يمكننا إعطاء تعريف آخر للـ lambda expression بأنها function لها مدخلات ومخرجات ووظيفة معينة. بمعنى آخر، تعطيها س من المدخلات ثم تقوم ببعض العمليات عليها وتنتج لنا ص من المخرجات، حيث أن س ≥ 0 و ص = 0 أو 1.

 

تركيبة الـ Lambda Expression:

ببساطة يتكون الـ Lambda Expression في الـ syntax الخاص بلغة الجافا من 3 عناصر أساسية:

  • Parameters: وتمثل المدخلات.
  • الرمز <-
  • Body: وهو block of code، والقيمة المرجعة منه هي المخرجات.

 

أمثلة لـ Lambda Expression صالحة:

() -> {}                     // No parameters; result is void

() -> 42                     // No parameters, expression body

() -> null                   // No parameters, expression body

() -> { return 42; }         // No parameters, block body with return

() -> { System.gc(); }       // No parameters, void block body

() ->                        // Complex block body with returns
{
    if(true) return 12;
    else
    {
        int result = 15;
        for(int i = 1; i < 10; i++) result *= i;
        return result;
    }
}

(int x) -> x+1               // Single declared-type parameter

(int x) -> { return x+1; }   // Single declared-type parameter

(x) -> x+1                   // Single inferred-type parameter

x -> x+1                     // Parentheses optional for single inferred-type parameter

(String s) -> s.length()     // Single declared-type parameter

(Thread t) -> { t.start(); } // Single declared-type parameter

s -> s.length()              // Single inferred-type parameter

t -> { t.start(); }          // Single inferred-type parameter

(int x, int y) -> x+y        // Multiple declared-type parameters

(x, y) -> x+y                // Multiple inferred-type parameter

 

أمثلة لـ Lambda Expression غير صالحة:

(x, int y) -> x+y   // Illegal: can't mix inferred and declared types

(x, final y) -> x+y // Illegal: no modifiers with inferred types

 

مصطلح الـ Functional Interface:

ابتداءً من جافا 8، أصبح هناك مصطلح جديد يسمى بالـ functional interface، وهو أي interface يتكون من دالة واحدة1. وقد تم إنشاء annotation جديد java.lang.FunctionalInterface يضاف على هذا النوع من الـ interface يستخدم فقط من قِبل الـ compiler لكي يتم التأكد بأن هذا الـ interface هو فعلاً functional interface وإلا سيحدث compilation error. بالإضافة لذلك، يظهر تعريف في صفحة الـ javadoc بأن هذا الـ interface هو functional interface.

1: لا يتم احتساب الدوال المطابقة في الـ signature لإحدى دوال الكلاس Object. مثال لهذه الدالة هي الدالة equals الموجودة في الكلاس java.util.Comparator. أيضاً لا يتم احتساب static methods ولا default methods واللتان أضيفتا في جافا 8.

مثال لاستخدام FunctionalInterface@:

@FunctionalInterface
public interface MathOperation
{
    int calculate(int a, int b);
}

وهنا يمكننا الاستنتاج بأنه يمكن إنشاء عيّنة من أي functional interface باستخدام الـ Lambda Expression حسب الصورة التالية:

مع ملاحظة بأنه يمكن إزالة ParamterType من الـ lambda expression وأيضاً يمكن إزالة الأقواس إذا كان هناك parameter واحد فقط. بالنسبة للـ lambda expression body، يمكن إزالة الأقواس وكلمة return والفاصلة المنقوطة ; في حالة كان الكود سطر واحد فقط.

ملاحظة أخيرة: يمكن إسناد lambda expression إلى متغير من نوع functional interface بالشكل التالي:

Runnable runnable = () -> System.out.println("Hello World");
runnable.run();

 

ما هو الـ Method Reference؟

من الإضافات الجميلة التي أضيفت إلى جافا 8 هي method reference، وهو نوع آخر من العبارات (بجانب الـ lambda expression) التي تقبل أن يتم إسندها إلى متغير من نوع functional interface. يأتي الـ method reference في 4 أشكال:

  1. SomeClass::staticMethod وهو مرجع لدالة static من كلاس معين.
  2. object::instanceMethod وهو مرجع لدالة instance لعيّنة من كلاس معين.
  3. SomeClass::instanceMethod وهو مرجع لدالة instance من كلاس معين.
  4. SomeClass::new وهو مرجع لدالة البناء (Constructor).

مثال على الشكل الأول:

Math::pow // same as (x, y) -> Math.pow(x, y)

 

مثال على الشكل الثاني:

System.out::println // same as "x -> System.out.println(x)"
this::someMethod    // same as "() -> this.someMethod()" or "x -> this.someMethod(x)" or ...
super::superMethod   // same as "() -> super.superMethod()" or "x -> super.superMethod(x)" or ...

 

مثال على الشكل الثالث:

String::equalsIgnoreCase // same as (x, y) -> x.equalsIgnoreCase(y)
SomeClass::instanceMethodWith2Params // same as (x, y, z) -> x.instanceMethodWith2Params(y, z)

 

مثال على الشكل الرابع:

SomeClass::new // same as () -> new SomeClass()

 

توجد حالة خاصة من الشكل الرابع وهي لإنشاء مصفوفة. مثال:

SomeClass[]::new // same as i -> new SomeClass[i]

]]>
https://fouad.io/2018/09/lambda-expressions-in-java/feed/ 3
شهادات الـ SSL وطريقة عمل البروتوكول الآمن HTTPS https://fouad.io/2017/08/ssl_certificates_and_how_https_works/ https://fouad.io/2017/08/ssl_certificates_and_how_https_works/#comments Sun, 06 Aug 2017 21:04:54 +0000 https://35.196.42.156/?p=4252 في هذه المقالة سأقوم بشرح ماهية شهادات الـ SSL وطريقة عمل البروتوكول الآمن HTTPS وعلاقته بتلك الشهادات . قبل أن أبدأ، دعونا نراجع سوياً أساسيات أمن المعلومات التالية اللازمة لفهم الموضوع:

  1. التشفير وأنواعه.
  2. عملية الـ hashing.
  3. التوقيع الرقمي.

 

  • ما هو التشفير (Encryption)؟

هو عملية تحويل نص أو أي نوع آخر من البيانات (binary مثلاً) إلى شكل آخر غير مفهوم مختلف كلياً عن شكله الأصلي وذلك لغرض إخفائه، ويتم ذلك بواسطة عمليات وخوارزميات رياضية تتضمن وجود مفتاح سري (secret key) يستخدم بشكل أو بآخر أثناء العملية العكسية (عملية فك التشفير Decryption).

 

قيمة المفتاح السري قد تكون رقم أو نص أو عدد من الأحرف العشوائية.

 

  • أنواع التشفير:

يوجد نوعين من عمليات التشفير:

  1. تشفير متماثل Symmetric: وفيه يكون المفتاح السري متماثل، أي هو نفسه المستخدم في التشفير وفك التشفير. من أشهر خوارزميات التشفير من هذا النوع هي AES.
  2. تشفير غير متماثل Asymmetric: وفيه يتم استخدام مفتاحين بدلاً من مفتاح واحد، وبينهما علاقة رياضية بحيث أحدهما يستخدم للتشفير والآخر يستخدم لفك التشفير. بمعنى آخر، أي رسالة تم تشفيرها بالمفتاح الأول لن يتمكن أحد من فك تشفير الرسالة إلا إذا كان لديه المفتاح الثاني، وأي رسالة تم تشفيرها بالمفتاح الثاني لن يتمكن أحد من فك تشفير الرسالة إلا إذا كان لديه المفتاح الأول. يطلق على أحد هذه المفاتيح مفتاح عام public key وعلى الآخر مفتاح خاص private key. يقصد بالمفتاح العام إمكانية مشاركته مع الجميع بينما يجب الاحتفاظ بالمفتاح الخاص بمكان خاص وعدم مشاركته مع أحد. فمثلاً، أقوم بتشفير رسالة بمفتاحي الخاص وأنشرها على الإنترنت. في هذه الحالة، أي شخص لديه المفتاح العام الخاص بي يمكنه فك تشفير رسالتي مع ضمان أن هذه الرسالة أنا من كتبها وليس شخصاً آخر. في الجهة المقابلة، أي شخص يشفر رسالة بمفتاحي العام لن يستطيع أحد قراءة هذه الرسالة إلا أنا حامل المفتاح الخاص. من أشهر خوارزميات التشفير من هذا النوع هي RSA.

 

  • ما هو الـ Hashing؟

هي عملية تحويل نص أو أي نوع آخر من البيانات (binary مثلاً) إلى شكل آخر غير مفهوم مختلف كلياً عن شكله الأصلي، ويتم ذلك بواسطة عمليات وخوارزميات رياضية دون استخدام أي مفاتيح خارجية، ولا يمكن إعادة البيانات إلى شكلها الأصلي ولذلك تسمى one way function بعكس التشفير two way function. الناتج من عملية الـ hashing يسمى تقنياً digest. عملية الـ hashing على نفس المدخل ينتج عنه نفس الـ digest في كل مرة. تستخدم عملية الـ hashing لعدة أمور من أهمها اختبار التكامل وعدم حدوث تغيير في البيانات (integrity)، بمعنى آخر لو تم تعديل ملف يمكن معرفة ما إذا تغير أم لا بمعرفة قيمة الـ hash الحالية والسابقة للملف. من أشهر خوارزميات الـ hashing هي MD5 وSHA بمختلف أنواعها (من SHA-0 إلى SHA-3).

 

الخوارزميات MD5 و SHA-0 و SHA-1 لا ينصح باستخدامها لوجود ثغرات معروفة فيها.

بعض المواقع تضع قيمة الـ hash بجانب روابط الملفات التي يتم تنزيلها حتى يمكننا التأكد من سلامة الملف بعمل hashing عليه بعد اكتمال عملية التنزيل ومطابقة القيمتين.

معلومة إضافية: الطريقة الصحيحة لتخزين كلمات المرور في قواعد البيانات هي بتطبيق عملية hashing على كلمة المرور وتخزينها بقاعدة البيانات، وأثناء عملية تسجيل الدخول يتم التحقق من كلمة المرور المدخلة بعمل hashing لها ومقارنتها بما هو موجود بقاعدة البيانات.

 

  • ما هو التوقيع الرقمي Digital Signature؟

التوقيع الرقمي على رسالة معينة هي آلية تضمن أن هذه الرسالة هي مرسلة من شخص محدد وليس من شخص آخر وكذلك تضمن بأن هذه الرسالة لم يتم التعديل عليها منذ إنشائها. أولى خطوات إنشاء التوقيع الرقمي هي إنشاء مفتاحين عام وخاص باستخدام أحد خوارزميات التشفير غير المتماثلة. بعد ذلك يتم عمل hashing للرسالة المراد إرسالها. بعد ذلك يتم تشفير قيمة الـ hash باستخدام المفتاح الخاص. وأخيراً يتم إرسال الرسالة كما هي غير مشفرة مع إرفاق قيمة الـ hash المشفرة سابقاً.

للتحقق من التوقيع الرقمي، يقوم المستقبل بعمل hashing للرسالة الأصلية ثم يقوم بفك تشفير قيمة الـ hash المشفرة التي تم استقبالها وذلك بواسطة المفتاح العام، وأخيراً يتم مقارنة القيمتين.

 


 

  • ما هي الشهادة الرقمية Digital Certificate؟

يشار إليها أحياناً بـ public key certificate وأحياناً SSL certificate، وهي مستند رقمي (ببساطة ملف) يستخدم لإثبات ملكية مفتاح عام public key. الشهادة تحتوي بشكل أساسي على المعلومات التالية:

  1. المفتاح العام public key.
  2. معلومات عن مالك الشهادة. تقنياً يطلق عليها اسم Subject.
  3. توقيع رقمي من جهة معتمدة لتوقيع الشهادات تسمى Certificate Authority أو اختصاراً CA، وذلك لغرض تأكيد صحة معلومات الشهادة. تقنياً يطلق عليها اسم Issuer.

المعيار X.509 هو معيار يستخدم لوصف مكونات الشهادة الرقمية، وهو المستخدم في شهادات الـ SSL المستخدمة في الاتصال المشفر HTTPS أثناء التصفح، بالإضافة إلى استخدامات أخرى سنستعرضها لاحقاً. النسخة الثالثة من المعيار X.509 هي المستخدمة حالياً.

 

  • مكونات شهادة X.509 (النسخة الثالثة):

1- tbsCertificate: تحتوي على المعلومات الأساسية للشهادة:

  • version: رقم نسخة المعيار X.509. حيث يبدأ العد من القيمة 0 (v3 = 2).
  • serialNumber: قيمة يتم إسنادها إلى الشهادة من قِبل الـ CA.
  • signature: الخوارزمية المستخدمة في توقيع الشهادة. مثال: “SHA-256 with RSA Encryption” (الخوارزمية SHA-256 هي خوارزمية hashing من نوع SHA-2 حيث آن طول القيمة الناتجة digest تكون بطول 256 بت).
  • issuer: معلومات الجهة (CA) التي أصدرت الشهادة وقامت بتوقيعها، وتصاغ المعلومات على هيئة (Distingushed Name (DN على حسب المعيار X.500 (الشائع استخدامه في الـ LDAP لتعريف العناصر). يتكون الـ DN عادةً من سلسلة من (relative distinguished names (RDN مفصولة فيما بينها بفاصلة “,”. أهم الـ RDNs المستخدمة هنا هي:
    • (commonName (CN: اسم الشهادة.
    • (organizationName (O: اسم الشركة مثلاً.
    • (organizationUnit (OU: اسم القسم داخل الشركة مثلاً.
    • (country (C: اسم الدولة مثلاً. ويتكون من حرفين حسب صيغة المعيار ISO 3166.
    • (stateName (S: اسم الولاية مثلاً.
    • (localityName (L: اسم المدينة مثلاً.
  • validity: تاريخ صلاحية الشهادة، ويتكون من حقلين:
    • notBefore: تاريخ بداية صلاحية الشهادة.
    • notAfter: تاريخ نهاية صلاحية الشهادة.
  • subject: معلومات الجهة مالكة الشهادة. وتصاغ المعلومات على هيئة (Distingushed Name (DN على حسب المعيار X.500، مطابق لما تم شرحه سابقاً في الحقل issuer. في حالة شهادات الـ SSL، يسند إلى الـ commonName رابط الموقع المراد تطبيق البروتوكول HTTPS عليه. هذه الطريقة أصبحت غير مدعومة في أغلب المتصفحات الجديدة، والبديل لها هو استخدام الحقل الإضافي “Subject Alternative Name” والذي سنتطرق للحديث عنه لاحقاً.
  • subjectPublicKeyInfo: يحتوي على حقلين:
    • algorithm: الخوارزمية المستخدمة في إنشاء المفتاح العام.
    • subjectPublicKey: قيمة المفتاح العام.
  • issuerUniqueID: قيمة يتم إسنادها إلى الشهادة من قِبل الـ CA.
  • subjectUniqueID: قيمة يتم إسنادها إلى الشهادة من قِبل الـ CA.
  • extensions: حقول إضافية تم إضافتها في النسخة الثالثة من المعيار X.509 لإمكانيات أكبر للشهادة. مع كل حقل إضافي يتم تحديد ما إذا كان الحقل critical أو non-critical. الأنظمة التي تستخدم الشهادات ترفض الشهادة إن كان هناك حقل critical ولا تدعمه، بينما تُهمل الحقول الـ non-critical التي لا تدعمها. من أهم هذه الحقول الإضافية:
    • Subject Alternative Name: يستخدم لإضافة أسماء إضافية للجهة المالكة للشهادة. أحد استخداماته هي تحديد أكثر من dns name للموقع في حالة شهادات الـ SSL. مثال: “DNS:fouad.io, DNS:subdomain.fouad.io” حيث ستكون الشهادة صالحة للموقع fouad.io و subdomain.fouad.io. يمكن تحديد IP بدلاً من DNS name. مثال: “ip:35.196.42.156″، ويمكن الجمع بينهما.
    • Basic Constraints: يستخدم في حالة كانت الشهادة هي شهادة CA.
    • Key Usage: يستخدم لتحديد استخدامات المفتاح العام المرفق مع الشهادة. يمكن إسناد أحد القيم التالية أو مجموعة منها: digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement, keyCertSign, cRLSign, encipherOnly, decipherOnly.
    • Extended Key Usage: يستخدم لتحديد استخدامات إضافية للمفتاح العام المرفق مع الشهادة. يمكن إسناد أحد القيم التالية أو مجموعة منها: serverAuth, clientAuth, codeSigning, emailProtection, timeStamping, OCSPSigning.
    • وغيرها الكثير.
  • signatureAlgorithm: يحمل نفس قيمة tbsCertificate.signature.
  • signature: قيمة التوقيع الرقمي، حيث تستخدم قيمة tbsCertificate كمدخل لخوارزمية التوقيع الرقمي.

 

  • ما هي الجهات المعتمدة لإصدار الشهادات وتوقيعها وما هو دورها؟

الـ Certificate Authority أو اختصاراً CA هي جهة معتمدة تقوم بإصدار الشهادات وتوقيعها. بعض هذه الجهات تقدم هذه الخدمة بشكل مجاني وبعضها تكون بمقابل مادي، وعادةً التي بمقابل مادي يتم تجديدها بشكل سنوي. وتشترط هذه الجهات للتوقيع بعض المستندات من مالك الشهادة للتأكد من المعلومات المدونة بالشهادة، وقد تستخدم طرق أخرى للتأكد مثل إضافة DNS record على عنوان الموقع أو استضافة رابط معين على السيرفر الذي يشير إليه عنوان الموقع. ولذلك فهي تضمن لأي طرف ثالث بأن تلك الشهادة هي ملك لصاحبها وجميع المعلومات التي عليها صحيحة.

تبدأ عملية إنشاء الشهادة بقيام مالك الشهادة بإنشاء زوج من المفاتيح مفتاح عام ومفتاح خاص باستخدام أحد خوارزميات التشفير الغير متماثل. يحتفظ مالك الشهادة بالمفتاح الخاص في مكان آمن. بعد ذلك يقوم مالك الشهادة بإنشاء طلب توقيع شهادة (certificate signing request (CSR (حسب المعيار PKCS#10) يتضمن المفتاح العام بالإضافة إلى معلومات مالك الشهادة بصيغة DN وهي المعلومات ذاتها التي ستضاف لاحقاً في الشهادة. بعد ذلك يقوم مالك الشهادة بالتوقيع الرقمي على الطلب باستخدام المفتاح الخاص، وهذا يضمن عدم التلاعب بمحتوى الطلب. وأخيراً يقوم مالك الشهادة بإرسال الطلب CSR إلى أحد الجهات المعتمدة CA من أجل إنشاء الشهادة وتوقيعها رقمياً. قد تطلب الـ CA من مالك الشهادة إرسال مستندات داعمة تثبت صحة معلومات الطلب أو قد تستخدم طرق أخرى كما تم ذكره سابقاً.

 

هنا قائمة بأشهر الجهات المعتمدة لإصدار الشهادات وتوقيعها:

  • Comodo.
  • Symantec.
  • GoDaddy.
  • GlobalSign.
  • IdenTrust.
  • DigiCert.
  • StartCom.
  • Entrust.
  • Trustwave.
  • Verizon.
  • Secom.
  • Unizeto.
  • Buypass.
  • QuoVadis.
  • Deutsche Telekom.
  • Network Solutions.
  • TWCA.
  • Let’s Encrypt.

 

  • ما هي الشهادة الموقعة ذاتياً self-signed certificate؟

هي الشهادة التي ينشئها مالكها ويوقعها بنفسه. بمعنى آخر، الشهادة تحمل نفس المعلومات في الحقلين subject وissuer.

 

  • ما هي سلسلة الشهادات certificate chain؟

هي سلسلة من شهادتين أو أكثر بحيث كل شهادة تكون هي المصدِّرة والموقِّعة للشهادة التي تسبقها في السلسلة، والشهادة الموجودة بآخر السلسلة هي شهادة موقعة ذاتياً self-signed certificate. الشهادة الموجودة بآخر السلسلة تسمى root certificate، والشهادة الموجودة بالمنتصف تسمى intermediate certificate، والشهادة الموجودة بأول السلسلة تسمى end-entity certificate. الـ root certificate والـ intermediate certificate هي شهادات تابعة لجهات معتمدة CA، بينما الشهادات التي لدى المستخدمين وأصحاب المواقع هي شهادات من نوع end-entity certificate.

 

  • الشهادات الموثوقة Trusted Certificate:

جميع الأنظمة التي تستخدم الشهادات الرقمية لديها قائمة افتراضية تحتوي على شهادات خاصة بالجهات المعتمدة CA تسمى بالشهادات الموثوقة Trusted Root Certificates، حيث أن أي شهادة تكون موقعة بواسطة CA ضمن هذه القائمة أو CA ضمن سلسلة شهادات وآخر السلسلة هي شهادة CA ضمن هذه القائمة فهذا يعني أن تلك الأنظمة تثق بهذه الشهادة.

أنظمة التشغيل لديها قائمة الشهادات الموثوقة بشكل افتراضية تستخدمها للوثوق بالشهادات الرقمية الخارجية. فمثلاً المتصفح Internet Explorer يستخدم قائمة الشهادات الموثوقة الخاصة بنظام التشغيل Windows للوثوق بالمواقع التي تستخدم البروتوكول HTTPS. المتصفح Chrome يستخدم قائمة الشهادات الموثوقة الخاصة بنظام التشغيل المستضيف، بينما المتصفح Firefox لديه قائمة شهادات موثوقة خاصة به. منصة Java لديها قائمة شهادات موثوقة خاصة بها أيضاً (القائمة موجودة داخل الـ keystore على المسار java.home/lib/security/cacerts)، وتستخدم للوثوق بالمواقع عند الاتصال بها بواسطة البروتوكول HTTPS وتستخدم أيضاً لتوقيع ملفات الـ jar لضمان سلامة الملفات من التعديل عليها (توقيع ملفات الـ jar أصبح شرط أساسي عند استخدام تقنية Java Web-Start).

عند تثبيت برنامج على نظام التشغيل Windows مثلاً، سيظهر لك اسم الناشر publisher في حالة تم توقيع ملف التثبيت بواسطة شهادة موثوقة كما هو موضوح في الصورة التالية:

يُشترط نوع خاص من الشهادات لتوقيع ملفات التثبيت وملفات الـ jar، وتحمل هذه الشهادات القيمة codeSigning ضمن الحقل الإضافي Extended Key Usage. غالباً هذا النوع من الشهادات غير مجاني، وسوف يشترط الـ CA من مقدم الطلب بعض المستندات للتحقق من صحة المعلومات المرفقة مع الـ CSR، وذلك لأن طرق التحقق الأخرى غير مناسبة هنا.

الشهادات الموقعة ذاتياً self-signed certificate والتي لا تتبع CA تكون غير موثوقة على جميع الأنظمة، والطريقة الوحيدة لجعلها موثوقة هي بإضافتها بشكل يدوي إلى قائمة الشهادات الموثوقة.

إذا كان السيرفر المستضيف لموقع معين يستخدم شهادة SSL موثوقة للمتصفح (بالاعتماد على قائمة الشهادات الموثوقة)، والشهادة تستخدم خوارزميات قوية ومدعومة من جهة المتصفح، وجميع الروابط الموجودة بداخل صفحة الـ HTML جميعها تبدأ بـ HTTPS وليس HTTP، فإن المتصفح سيظهر شعار القفل الأخضر وهذا يعني بأن الاتصال بينك وبين السيرفر مشفر وأن السيرفر الذي تتصل به هو فعلاً المكتوب بشريط العنوان الموجود بأعلى المتصفح. يرجى ملاحظة بأن القفل الأخضر لا يعني بالضرورة أن الموقع آمن!

يمكن التلاعب بقائمة الشهادات الموثوقة لمن لديه صلاحية وصول. فمثلاً، يمكن التلاعب بالشهادات الموثوقة في نظام التشغيل Windows للأجهزة الموجودة في بيئة عمل بواسطة قسم الـ IT، واستبدال شهادة Google مثلاً بشهادة أخرى وهمية ثم تحويل جميع الـ traffic من تلك الأجهزة إلى سيرفر خاص، وبذلك يمكن مراقبة جميع الزيارات إلى مواقع Google (والتعديل عليه إن أرادوا) وسيظهر القفل الأخضر الآمن لدى مستخدمي تلك الأجهزة.

بعض الـ CA يقدم نوع خاص من شهادات الـ SSL يسمى Extended Validation Certification أو اختصاراً EV Certificate، حيث يتميز الموقع المستخدم لهذه الشهادة بظهور اسم الجهة المالكة للشهادة في شريط أخضر يزيد من مصداقية الموقع. تقنياً يتم استخدام الحقل الإضافي Certificate Policy ضمن الشهادة.

بعض الـ CA يقدم شهادات تسمى Wildcard Certificate وتعني ببساطة أنها تعمل على مدى كامل من الـ subdomains. فمثلاً، fouad.io.* تعمل على a.fouad.io و b.fouad.io وما يشابه.

 

  • ما هو البرتوكول الآمن HTTPS؟

هو بروتوكول يستخدم للتخاطب بين الـ client والـ server عبر الإنترنت. يستخدم نفس لغة التخاطب المستخدمة في البروتوكول HTTP ولكن مدعوم بطبقة البروتوكول TLS (أو SSL سابقاً) التي تقدم ميزة تشفير البيانات والتأكد من سلامة البيانات المرسلة من التلاعب بالإضافة إلى الموثوقية عن طريق تأكيد هوية الموقع الذي يتم زيارته بواسطة الشهادات الرقمية.

يشار عادةً إلى البروتوكول TLS (أو الذي يسبقه SSL) بالاسم SSL.

البروتوكول SSL v3 غير آمن الآن لوجود ثغرات أمنية معروفة به، ويستحسن استخدام البروتوكول TLS v1.2.

ترسل جميع حقول الـ HTTP سواء في الـ HTTP Request أة الـ HTTP Response مشفرة ولا يمكن لأي طرف ثالث (شركات الاتصالات مثلاً) مشاهدة محتواها إذا كان الاتصال آمن بشكل سليم. يستطيع الطرف الثالث فقط معرفة IP السيرفر المراد زيارته، ولا يستطيع معرفة الـ path. فمثلاً عند زيارة الصفحة https://fouad.io/2012/11/thread_definition، الطرف الثالث سيعلم بأنك زرت الموقع fouad.io ولكن لن يستطيع معرفة أي صفحة تمت زيارتها.

 

  • طريقة عمل البروتوكول TLS v1.2:

عند زيارة موقع عبر البروتوكول الآمن HTTPS فإن أول خطوة يقوم بها المتصفح والسيرفر هي التصافح Handshake. تتكون عملية التصافح من 3 خطوات رئيسية:

  1. رسائل التحية Hello Messages: تستخدم هذه الرسائل لتبادل معلومات المتصفح والسيرفر وإمكانيات كلٍ منهما من ناحية دعمهما لمختلف نسخ بروتوكولات الأمان (TLSv1.2 مثلاً) وخوارزميات التشفير المدعومة (Cipher Suite) وغيرها. المتصفح يبدأ أولاً بإرسال ClientHello محملة بتلك المعلومات وبعدها يرد السيرفر بإرسال ServerHello مبيناً فيها التأكيد أو الرفض على بدء عملية الاتصال.
  2. تبادل الشهادة: يقوم السيرفر بإرسال الشهادة الرقمية الخاصة به إلى المتصفح. بعد ذلك يتأكد المتصفح من صلاحية الشهادة وما إذا كانت موثوقة بالنسبة له أم لا بالاعتماد على قائمة الشهادات الموثوقة كما تم شرحه سابقاً.
  3. تبادل المفتاح السري: يقوم المتصفح بإنشاء مفتاح سري باستخدام أسلوب التشفير المتماثل Symmetric Encryption (التشفير وفك التشفير يتم بنفس المفتاح، كما تم شرحه سابقاً). يقوم المتصفح بتشفير المفتاح السري بواسطة المفتاح العام الخاص بالسيرفر ثم يقوم بإرساله إلى السيرفر، وبذلك يضمن بأن السيرفر هو الوحيد القادر على فك التشفير بواسطة المفتاح الخاص.

في هذه المرحلة، يمتلك كلاً من المتصفح والسيرفر المفتاح السري الذي قام بإنشائه المتصفح. تنتهي هنا عملية المصافحة، ويبدأ المتصفح بإرسال أول HTTP Request إلى السيرفر بعد تشفيره بالمفتاح السري. عندما تصل الرسالة إلى السيرفر، يقوم بفك تشفيرها بواسطة المفتاح السري ذاته. بعد ذلك يقوم بإرسال HTTP Respone إلى المتصفح بعد تشفيره بالمفتاح السري. عندما تصل الرسالة إلى المتصفح، يقوم بفك تشفيرها بواسطة المفتاح السري. يرجى ملاحظة بأن المتصفح هنا هو مثال على الـ client، والعملية تكون مشابهة تماماً عندما يكون الـ client ليس المتصفح (يتم الاتصال من كود جافا مثلاً).

 

]]>
https://fouad.io/2017/08/ssl_certificates_and_how_https_works/feed/ 15
نبذة عن الجافا https://fouad.io/2017/01/java_introduction/ https://fouad.io/2017/01/java_introduction/#comments Sat, 14 Jan 2017 13:31:01 +0000 https://35.196.42.156/?p=3725 ما هي جافا؟

جافا أو Java هي لغة برمجة عالية المستوى وكائنية التوجه ومتعددة الاستعمالات. لغة برمجة عالية المستوى high-level حيث تتميز بسهولتها واستخدامها لمصطلحات إنجليزية مفهومة وتخفي الكثير من التفاصيل للتعامل مع عتاد الحاسب بعكس اللغات منخفضة المستوى. لغة برمجة كائنية التوجه (object-oriented (OO حيث أنه يتم تمثيل كل شيء على هئية كائن، وكل كائن له نوع معين وصفات وأفعال تميزه عن غيره. لغة برمجة متعددة الاستعمالات general-purpose حيث أنها تستخدم لصناعة برمجيات في شتى المجالات، ومنها على سبيل المثال لا الحصر: تطبيقات سطح المكتب، تطبيقات الويب، تطبيقات الهواتف المحمولة، تطبيقات الأجهزة محدودة الموارد embedded systems، وغيرها.

جافا هي أيضاً منصة برمجية software platform تحتوي على آلة جافا افتراضية (Java Virtual Machine (JVM تقوم بتشغيل برامج الجافا عليها بغض النظر عن نظام التشغيل (operating system (OS أو نوع ومعمارية المعالج CPU architecture التي تشغل هذه الآلة. تتألف برامج الجافا التي تقوم بتشغيلها آلة الجافا الافتراضية من لغة وسيطة intermediate language تسمى جافا بايت كود Java bytecode. يتم إنتاج الجافا بايت كود عادةً بواسطة مترجم الجافا Java compiler وهو الذي يقوم بترجمة لغة الجافا إلى لغة الجافا بايت كود التي تفهمها آلة الجافا الافتراضية. يوجد أيضاً العديد من لغات البرمجة التي يمكن ترجمتها إلى جافا بايت كود، وتسمى هذه اللغات بلغات آلة الجافا الافتراضية JVM languages، ومن أشهرها لغة سكالا Scala.

بسبب طبيعة آلة جافا الافتراضية من حيث أنها تقوم بتشغيل نفس الجافا بايت كود على أي بيئة تشغيلية، تتميز لغة الجافا بمبدأ “أكتب مرة، شغل بأي مكان” (Write Once, Run Anywhere” (WORA”. بمعنى آخر، أكتب برنامج جافا مرة واحدة فقط، وسوف يعمل على جميع الأنظمة التشغيلية، بعكس اللغات الأخرى مثل ++C\C حيث يتم كتابة برنامج مختلف لكل نظام تشغيل.

 

تاريخ الجافا

بدأ مشروع تطوير لغة جافا عام 1991 م بواسطة جَيْمس قُوْزلِن James Gosling في شركة سن مايكروسيستم Sun Microsystems. تم اختيار اسم اللغة اشتقاقاً من قهوة الجافا والتي يتم إنتاجها في جزيرة في إندونيسيا تسمى جافا. لذلك نلاحظ دائماً اقتران لغة الجافا بكوب القهوة.


تم إصدار أول نسخة من لغة جافا عام 1996 م. قامت سن مايكروسيستم بتحويل جافا إلى لغة مفتوحة المصدر open source عام 2007 م. في بداية عام 2010، قامت شركة أوراكل Oracle بالاستحواذ على شركة سن مايكروسيستم، ومازالت حتى الآن هي المالكة والمطورة للغة ومنصة الجافا.

 

منصات الجافا

يوجد 3 إصدارات رئيسية من منصة الجافا:

  • الإصدار القياسي (standard edition (Java SE: هذا الإصدار يحتوي على المكتبات libraries وإطارات العمل frameworks المستخدمة لبناء الوظائف الأساسية في أي برنامج جافا، بالإضافة إلى بناء البرامج المتقدمة مثل برامج الواجهات الرسومية (Graphical User Interface (GUI، والبرامج التي تعتمد على الاتصال بالإنترنت وبالشبكات الداخلية، والبرامج التي تتعامل مع قواعد البيانات، وغيرها الكثير. لاحظ بأنه عندما يتم ذكر جافا بشكل عام، فالمقصود بها هذا الإصدار من الجافا.
  • إصدار المؤسسات وقطاع الأعمال (enterprise edition (Java EE: هذا الإصدار مبني على الإصدار القياسي. بالإضافة لذلك، فهو يحتوي على تقنيات تساعد في بناء أنظمة ذات كفاءة عالية تعمل على خوادم servers ويتم الوصول إليها غالباً عبر الإنترنت.
  • إصدار الأجهزة الصغيرة وذات موارد محدودة (micro edition (Java ME: هذا الإصدار يحتوي على جزء من مكتبات الإصدار القياسي بالإضافة إلى مكتبات خاصة للتعامل مع الأجهزة الصغيرة ذات الموارد المحدودة (مثلاً ذاكرة صغيرة، معالج بسيط، إلخ).

يوجد أيضاً منصة خاصة بالطاقات الذكية تسمى جافا البطاقات Java Card، وتعتبر أصغر منصة من منصات الجافا. من أمثلة البطافات الذكية؛ شرائح الاتصال SIM card، بطاقات الصرّاف الآلي ATM card، وغيرها الكثير.

 

إصدارات الجافا

لغة ومنصة الجافا خضعت للعديد من التغييرات والتطوير منذ أول إصدار بتاريخ 23 يناير 1996 م. منذ الإصدار الرابع من المنصة القياسية J2SE 1.4، يتم تطوير الجافا عبر برنامج يطلق عليه (Java Community Process (JCP. يمكّن هذا البرنامج المؤسسات والأفراد من المشاركة في تطوير الجافا عبر آلية منظمة. البرنامج يتضمن استخدام (Java Specification Requests (JSRs وهي عبارة عن مستندات تشرح مواصفات التقنية المقترحة لإضافتها للجافا.

الجدول التالي يوضح إصدارات الجافا Java SE:

لكل إصدار من إصدارات الجافا الحديثة يتم إصدار تحديثات بشكل دوري، كل تحديث له ترقيم خاص. مثلاً التحديث الأربعون من النسخة الثامنة يحمل رقم النسخة 8u40.

الجدول التالي يوضح إصدارات الجافا Java EE:

الإصدار الحالي لمنصة Java ME هو 8.2.

 

مواصفات منصات الجافا وتطبيقاتها

كل منصة من منصات الجافا تكون عبارة عن مواصفات مكتوبة specifications، ويوجد منصات تطبيقية موافقة لهذه المواصفات implementations. بالنسبة للإصدار القياسي Java SE، فتنقسم فيه المواصفات إلى قسمين:

وأشهر المنصات التي تطبق هذه المواصفات هي:

  • Oracle: التطبيق الرسمي لمنصة Java SE، ويأتي بتوزعتين (Java Development Kit (JDK و (Java Runtime Environment (JRE. الـ JRE يحتوي على الأدوات اللازمة لتشغيل برامج الجافا، وهو موجه للمستخدم النهائي end user. الـ JDK يحتوي على JRE بداخله، بالإضافة إلى أدوات التطوير اللازمة لبناء برامج جافا، وهو موجه للمبرمجين. آلة جافا الافتراضية الموجودة بداخل الـ JRE تسمى HotSpot.
  • OpenJDK: التطبيق المرجعي (Reference Implementation (RI لمنصة Java SE. التطبيق المرجعي يعني أن أي محاولة لبناء منصة جافا قياسية جديدة يجب أن تكون متوافقة مع هذا التطبيق ليتم اعتباراها منصة جافا قياسية.
  • وغيرها الكثير.

بالنسبة لبقية منصات الجافا، فكل منصة تتألف من العديد من التقنيات، كل تقنية منها تكون مواصفاتها مكتوبة داخل JSR، وكل تقنية لها العديد من التطبيقات الموافقة لتلك المواصفات.

]]>
https://fouad.io/2017/01/java_introduction/feed/ 6
الطريق إلى شهادة الجافا OCA 1Z0-803 https://fouad.io/2013/11/oca-1z0-803_certificate_roadmap/ https://fouad.io/2013/11/oca-1z0-803_certificate_roadmap/#comments Sat, 30 Nov 2013 12:48:20 +0000 https://35.196.42.156/?p=3710 بدايةً، أشكر الله عز وجل الذي وفقني باجتياز الامتحان وبالتالي الحصول على شهادة الجافا OCA 1Z0-803. سأكتب هنا تقريراً موجزاً عن تجربتي في التحضير لهذا الامتحان وما هي المصادر التي اعتمدتُ عليها، وكيفية التسجيل لهذا الامتحان وما مدى صعوبته.

– ما هي شهادة OCA 1Z0-803؟

تقدم شركة Oracle العديد من الشهادات في تقنياتها، ومن ضمن هذه الشهادات شهادة الجافا OCA 1Z0-803، و OCA هي اختصارٌ لـ Oracle Certified Associate. تعتبر هذه الشهادة المستوى الأول من ضمن 3 شهادات لمنصة Java SE. يوجد أيضاً شهادات لتقنيات منصة Java EE ولكن للحصول عليها يتطلب الحصول على المستوى الثاني من الشهادات وهي OCP 1Z0-804 و OCP اختصارٌ لـ Oracle Certified Professional. وهذه لن يمكنك الحصول عليها إلى بعد الحصول على شهادة OCA. الشهادة الأخيرة في منصة Java SE هي OCM وتعتبر متقدمة جداً، و OCM اختصارٌ لـ Oracle Certified Master. وللحصول على شهادة الماستر، يجب الحصول على شهادتي OCA و OCP أولاً، وكلٍ منهما يتطلب اجتياز امتحان على حدة. للحصول على OCM تحتاج أيضاً أن تتقدم لأخذ دورة من أوراكل، بالإضافة إلى إنجاز مشروع كـ Assignment في مدة أقل من 6 أشهر، وأخيراً اختبار مقالي Essay مدته ساعتين (كل هذا؟ :O).

للمزيد من التفاصيل:

Oracle Certified Associate, Java SE 7 Programmer

Oracle Certified Professional, Java SE 7 Programmer

Oracle Certified Master, Java SE 6 Developer

توجد أيضاً شهادات Upgrade، مثلاً لديك شهادة SJCP القديمة وتريد الحصول على شهادة OCP، يوجد اختبار مناسب لك دون الحاجة للحصول على شهادة OCA:

Oracle Certified Professional, Java SE 7 Programmer

– التسجيل لهذا الامتحان.

قمتُ بالتسجيل للامتحان عن طريق الموقع http://www.pearsonvue.com/oracle، ومن هناك يتم تحديد موعد الامتحان والمركز أو المعهد الذي سيتم أداء الامتحان فيه. سجلتُ للامتحان قبل 3 أشهر تقريباً، واخترت TOPAZ Systems بشارع التحلية بالرياض. اختياري لهذا المكان ليس بسبب معرفته ولكن لأن مواعيده ناسبتني أكثر، حيث أن يوم السبت 30 نوفمبر 2013 هو يوم عطلة ولا يوجد لدي أعمال. قيمة الامتحان هي 245 دولار أمريكي، وسددت المبلغ عن طريق الفيزا (لا أدري إن كان هنالك حلول أخرى للتسديد).

– مصادر للتحضير من أجل الامتحان.

خبرتي بالجافا هي 3 سنوات، وأستطيع أن أقول بأن هذا الشيء ساعدني كثيراً لاجتياز الامتحان. أيضاً مشاركتي بموقع stackoverflow لمدة 3 سنوات تقريباً ساعدتني أيضاً في معرفة خفايا لغة الجافا وخصوصاً على مستوى الـ syntax.

بالنسبة للمصادر، اعتمدتُ بشكل كلي على الكتاب Sun Certified Programmer for Java 6 Study Guide وخصوصاً من الفصل الأول إلى الفصل السادس. صحيح أن الكتاب يعتبر قديماً وهو موجه للشهادة SCJP للجافا الإصدار السادس وشهادة OCA هي للجافا الإصدار السابع، ولكن أساس اللغة واحد. الكتاب جميل جداً وفي نظري أفضل كتاب لتعلم أساسيات الجافا بشكل دقيق. قرأتُ الستة فصول الأولى من الكتاب خلال شهر واحد تقريباً (> 400 صفحة).

بالإضافة لمنهج الكتاب توجد الميزات الجديدة التي أضيفت في جافا 7، وهي:

  • استخدام String داخل switch.
  • الـ diamond-operator، اعرف فقط ماذا تعني.
  • الـ try-with-resource.
  • الـ binary representation للأعداد؛ مثل: 0b1001011.
  • خاصية الـ underscore في الأعداد؛ مثل: 000_000_1.

أيضاً اشتريتُ OCAJP Oracle Certified Associate Java SE 7 Programmer Practice Exams وهو عبارة عن بنك أسئلة مشابهة تماماً لما في الامتحان (بعض الأسئلة من هذا الكتاب وجدتها بالامتحان).

هنا قائمة بالمواضيع المدرجة في الامتحان: Programmer Level I Exam.

– الامتحان ومدى صعوبته.

إجمالاً، الامتحان بسيط وبإمكان أي مطور جافا يفهم أساسيات اللغة أن يجيب عن الأسئلة بكل سهولة. ولكن، توجد أسئلة مخادعة (tricks) وهي كثيرة، وتوجد أيضاً أسئلة سخيفة لدرجة أنك تشك إن كان بها خدعة ما، وهذا ما يزيد من صعوبة الاختبار. هذا الامتحان يختبر مستوى فهمك للـ syntax وأساسيات الـ object-oriented مثل الـ inheritance وغيرها. بعض الأسئلة تختبرك وكأنك compiler لتكتشف خطأ ما بـ syntax وبالتالي compilation error، أو خطأ أثناء التشغيل وبالتالي exception at runtime، ولذلك هذا الامتحان يحتاج إلى تركيز عالي وقراءة السؤال أكثر من مرة حتى يُفهم بشكل تام.

بالنسبة للوقت، أخبروني برسالة إيميل بأن الوقت سيكون 150 دقيقة، ولكنني تفاجأت داخل قاعة الامتحان بأن الوقت هو 140 دقيقة فقط! لا تخف، الوقت كافي جداً وسيتبقى الكثير من الوقت وأنت قد انتهيت من حل جميع الأسئلة. أنهيت الامتحان مع المراجعة في 90 دقيقة فقط.

بالنسبة للأسئلة، فقد كانت 90 سؤالاً، بعضها يطلب منك أن تختار إجابة واحدة، وبعضها إجابتين، وبعضها ثلاثة. لا تقلق، سيخبرك بعدد الإجابات التي من المفترض أن يتم اختيارها. بعض الأسئلة يوجد بها tab إضافي ويسمى Exhibition ويحتوي على source code يشار إليه ضمن السؤال. مع كل سؤال، يوجد checkbox يسمى review وهو لوضع علامة على السؤال لمراجعته لاحقاً. بعد الانتهاء كلياً، تستطيع أيضاً مراجعة جميع الأسئلة من جديد بالضغط على الزر Review All.

بالنسبة للأوراق المطلوب إحضارها وقت الامتحان، أنا أحضرت بطاقة الأحوال المدنية ورأيتُ شخصاً آخر أحضر جواز سفره. أيضاً، أحضرتُ بطاقة بنكية ووقعت في خلف البطاقة في المكان المخصص (لأول مرة أستخدمه D:) ثم أخذوا صورة من بطاقة الأحوال ومن التوقيع. أيضاً التقطت لي صورة في نفس مركز الامتحان. درجة النجاح في الامتحان هي 77%، والحمد لله اجتزتُ الامتحان بدرجة 96%.

– أمثلة لبعض الأسئلة.

 للأسف، عندما تعطيك أوراكل النتيجة، لا تخبرك بالأسئلة التي أخطأت فيها. على أية حال، يوجد سؤال كنت قد أجبت عليه بإجابة صحيحة ثم عدلت الإجابة مع الـ review:

<List<String> list = new ArrayList<String>();
list.add("EE");
list.add("SE");
list.add("ME");
list.add("SE");
list.add("EE");

list.remove("SE");
System.out.println(list);

في البداية أجبت إجابة صحيحة واخترت أن remove تحذف أول occurrence فقط، ولكن مع المراجعة اخترت أن remove تحذف جميع الـ occurrences. الحمد لله على كل حال.

من الأسئلة المخادعة أيضاً استخدام String قيمته null داخل switch (الحمد لله أنني قد قرأت عن هذه الحالة من قبل D:):

String s = null;

switch(s)
{
    case "null" : System.out.println("1"); break;
    case null : System.out.println("2"); break;
    default: System.out.println("3"); break;
}

الجواب هو NullPointerException will be thrown at runtime.

سؤال مخادع آخر (جزء من الـ source code):

String[] array = {"Item1", "Item2", "Item3", "Item4", "Item5"};
int i = 0;
while(i++ < array.length)
{
    System.out.println(i);
    break;
}

كم سطراً سيتم طباعته؟ مع الارتباك ربما لن تنتبه للـ break وتعتقد أن الـ while سيكمل جميع دوراته.

سؤال آخر:

do
{
    int x = 0;
    x++;
}
while(x > 0);

لا أذكر السؤال جيداً، ولكن ما أذكره هو أن الجواب كان compilation error بسبب أن x لا يمكن استخدامها داخل while وذلك لأنها في scope آخر.

– ما هي الخطوة القادمة؟

بعد الانتهاء من الامتحان، ستظهر لك رسالة مفادها أنه سيصلك إيميل من أوراكل بالنتيجة خلال مدة أقصاها نصف ساعة. وصلتني الرسالة في أقل من 5 دقائق. بالنسبة للشهادة، سترسلها لك أوراكل للبريد المسجل في موقع pearsonvue.com خلال مدة أقصاها 8 أسابيع بحسب ما قرأت.

تحديث:

ستصلك الشهادة كنسخة إلكترونية عبر إيميل من أوراكل خلال 48 ساعة من إكمال متطلبات الشهادة، وسيخبروك بطريقة طلب النسخة الورقية والتي من المفترض أن تصلك خلال 4-8 أسابيع.

  

]]>
https://fouad.io/2013/11/oca-1z0-803_certificate_roadmap/feed/ 1
ما هو الـ stacktrace؟ https://fouad.io/2013/05/java_stacktrace/ https://fouad.io/2013/05/java_stacktrace/#respond Thu, 16 May 2013 13:05:12 +0000 https://35.196.42.156/?p=3720 في لغة الجافا، تنقسم ذاكرة الـ JVM إلى عدة أقسام، ومنها ما يسمى بالـ stack. عند استدعاء دالة س (method) فإنه يُنشأ frameبداخله معلومات الدالة س ويوضع هذا الـ frame أعلى الـ stack. وعند الانتهاء من تنفيذ الدالة س، يتم إخراج الـ stack frame الخاص بالدالة س من الـ stack ويرجع مسار التنفيذ (flow of control) إلى الدالة السابقة والتي استدعت الدالة س.

public class Main
{
    public static void main(String[] args)
    {
        x();
    }
    
    public static void x()
    {
        y();
    }
     
    public static void y()
    {
        // ...
    }
}

شكل الـ stack سيكون كالتالي:

بدايةً يقوم الـ JVM بإنشاء الـ main thread ثم يقوم باستدعاء دالة ()main ويضع frame خاص بهذه الدالة في الـ stack. خلال تنفيذ دالة الـ ()main سيقوم باستدعاء الدالة ()x وتباعاً يضع frame خاص بها في الـ stack. وخلال تنفيذ الدالة ()x يستدعي الدالة ()yويضع frame في الـ stack. عند الانتهاء من تنفيذ الدالة ()y يقوم الـ JVM بإزالة الـ frame الموجود بأعلى الـ stack وهو الـ frameالخاص بالدالة ()y. بعد ذلك يعود لاستكمال تنفيذ الدالة ()x ويزيل الـ frame الخاص بها عند الانتهاء من تنفيذها. وأخيراً يعود للدالة()main ويكمل تنفيذها، وعند الانتهاء منها يزيل آخر frame موجود في الـ stack ويقوم بإنهاء الـ main tread (ليسس شرطاً أن ينتهي البرنامج بانتهاء الـ main thread).

ما هو الـ stacktrace؟

الـ stacktrace باختصار هو سلسلة من الاستدعاءات للدوال method invocations مطابقة للـ stack frames. يستخدم الـ stacktrace عادةً في معالجة الأخطاء واكتشاف مصدرها. لنأخذ مثال بسيط لتتضح الصورة:

import java.io.IOException;

public class Main
{
    public static void main(String[] args) throws Exception
    {
        x();
    }
    
    public static void x() throws Exception
    {
        y();
    }
    
    public static void y() throws IOException
    {
        throw new IOException("IOException in y()");
    }
}

يتم طباعة الـ stacktrace على الشاشة في حالتين:

– إما أن نمسك الـ exception في try and catch وبداخل الـ catch نطبع الـ stacktrace عن طريق الدالة ()e.printStackTraceحيث أن e هو مرجع للخطأ reference for the exception.
– أو نقوم برمي الـ exception وتحويله (exception propagation) إلى الدالة التي استدعت الدالة الحالية، حتى يصل الـ exception إلى آخر frame في الـ stack كما فعلنا في المثال الأخير.

برنامج الجافا في المثال الأخير سيطبع الـ stacktrace التالي:

Exception in thread "main" java.io.IOException: IOException in y()
    at Main.y(Main.java:17)
    at Main.x(Main.java:12)
    at Main.main(Main.java:7)

وكقاعدة عامة في أي stacktrace:

– السطر الأول يتكون من ٣‎ معلومات مهمة: اسم الـ thread، ونوع الـ exception، ورسالة الخطأ (إن وجدت). في المثال السابق اسم الـthread كان main، ونوع الـ exception كان java.io.IOException، ورسالة الخطأ كانت ()IOExcpetion in y.
– السطر الثاني يكون دائماً مصدر الـ exception، وفي الكود يكون المصدر هو الـ constructor الخاص بالـ exception. في المثال السابق كان مصدر الخطأ في السطر ١٧ وهو ;(“()throw new IOException(“IOException in y.
– السطر الأخير يحتوي على اسم الـ method التي تكون مدخل الـ thread وبدايته. في المثال السابق، مدخل الـ thread هو الدالة main.

الـ stacktrace مع Caused by

أحياناً يكون خطأ ما سببه خطأ آخر، ولذلك يمكن في لغة الجافا ربط exception معين على أنه سبب لـ exception آخر عن طريق تمريره كـ parameter في الـ constructor أو عن طريق استخدام الدالة ()initCause على مرجع الـ exception الجديد. وربما الخطأ س سببه الخطأ ص، والخطأ ص سببه الخطأ ع .. وهكذا. الـ stacktrace الناتج سيحتوي على معلومات الـ exception الجديد وجميع الـexceptions المتسببة فيه مفصولةة فيما بينهم بالجملة Caused by. المثال التالي سيوضح ذلك:

import java.io.IOException;

public class Main
{
    public static void main(String[] args) throws Exception
    {
        x();
    }
     
    public static void x() throws Exception
    {
        try
        {
            y();
        }
        catch(IOException e)
        {
            // ex is caused by e
            Exception ex = new Exception("Exception in x()");
            ex.initCause(e);
            throw ex;
            
            // or
            // throw new Exception("Exception in x()", e)
        }
    }
    
    public static void y() throws IOException
    {
        throw new IOException("IOException in y()");
    }
}

والـ stacktrace الناتج يكون كالتالي:

Exception in thread "main" java.lang.Exception: Exception in x()
    at Main.x(Main.java:19)
    at Main.main(Main.java:7)
Caused by: java.io.IOException: IOException in y()
    at Main.y(Main.java:30)
    at Main.x(Main.java:14)
    ... 1 more

نفس القاعدة السابقة تنطبق على الـ stacktrace الحالي باستثناء أنه لا يكتب اسم الـ thread في الـ exception الموجود بعد Caused by. أيضاً يوجد اختلاف طفيف هنا وهو السطر الأخير. السطر المفقود هذا يمكنك الحصول عليه من الـ exception السابق له. المثال الآتي سيوضح كيف يمكنك الحصول على السطور المفقودة من الـ stacktrace:

لنفرض أنه لدينا الـ stacktrace التالي:

Exception in thread "main" ExceptionA
    at A5
    at A4
    at A3
    at A2
    at A1
Caused by: ExceptionB
    at B6
    at B5
    at B4
    ... 3 more
Caused by: ExceptionC
    at C8
    at C7
    at C6
    ... 5 more

كقاعدة عامة، إذا كان المفقود س من السطور في exception معين، نأخذ آخر س من السطور من الـ exception السابق له. وبالتالي الـstacktrace الكامل سيكون بالشكل التالي:

Exception in thread "main" ExceptionA
    at A5
    at A4
    at A3
    at A2
    at A1
Caused by: ExceptionB
    at B6
    at B5
    at B4
    at A3
    at A2
    at A1
Caused by: ExceptionC
    at C8
    at C7
    at C6
    at B5
    at B4
    at A3
    at A2
    at A1

الـ stacktrace مع javac options -g & -g:none

عند عمل compile بالأمر javac -g فإن الـ stacktrace يكون محتوياً على أرقام الأسطر كما في الأمثلة السابقة، بينما لو تم عملcompile بالأمر javac -g:none فإن الـ stacktrace للمثال الأخير سيكون كالتالي:

Exception in thread "main" java.lang.Exception: Exception in x()
    at Main.x(Unknown Source)
    at Main.main(Unknown Source)
Caused by: java.io.IOException: IOException in y()
    at Main.y(Unknown Source)
    ... 2 more

لاحظ الاختلاف!

الـ stacktrace بدون exception

يمكن في لغة الجافا الحصول على stacktrace من أي مكان في الكود وطباعته مباشرة، دون الحاجة إلى exception. لاحظ المثال التالي:

public class Main
{
    public static void main(String[] args)
    {
        x();
    }
    
    public static void x()
    {
        y();
    }
    
    public static void y()
    {
        Thread currentThread = Thread.currentThread();
        StackTraceElement[] elements = currentThread.getStackTrace();
        for(StackTraceElement element : elements)
        {
            System.out.println(element);
        }
    }
}

والناتج سيكون كالتالي:

java.lang.Thread.getStackTrace(Unknown Source)
Main.y(Main.java:16)
Main.x(Main.java:10)
Main.main(Main.java:5)

]]>
https://fouad.io/2013/05/java_stacktrace/feed/ 0
تعريف الـ Thread https://fouad.io/2012/11/thread_definition/ https://fouad.io/2012/11/thread_definition/#comments Thu, 29 Nov 2012 18:58:05 +0000 https://35.196.42.156/?p=3705 – الثريد “Thread” بإختصار هو “خيط” أو سلسلة من الأوامر البرمجية. سنعتبر مجازاً بأن الأمر البرمجي يمثل سطر واحد من كود لبرنامج معين.

–∎–∎–∎–∎–∎–∎–∎–∎–

– المسؤول عن تشغيل هذا الخيط من الأوامر البرمجية هو الـ CPU “وحدة المعالجة المركزية”.

– يحتوي أي برنامج على ثريد واحد على الأقل، ويسمى الثريد الرئيسي main thread. في لغة الجافا مثلاً، دالة main تحتوي على مجموعة من الأوامر البرمجية اللتي ينفذها الـ CPU على الـ main thread عند تشغيل البرنامج.

– عند إحتواء البرنامج على أكثر من ثريد، فإن نظام التشغيل OS يقوم بجدولة الـ CPU للعمل على تنفيذ الأوامر الموجودة في “الثريدات”. وتختلف الطريقة بحسب مواصفات جهاز الحاسب المشغل ونظام التشغيل.

– لو كان الجهاز يحتوي على CPU واحد فقط، فإن نظام التشغيل يجبر هذا الـ CPU بالعمل على تنفيذ الأوامر الموجودة بالثريدات بشكل متزامن “concurrently”. مثال: لدينا 3 ثريدات، كيف يقوم الـ CPU بتنفيذها؟ في الحقيقة، تختلف الطريقة المتبعة من نظام تشغيل إلى آخر ولكن أغلب الأنظمة الحديثة تتبع طريقة round-robin algorithm، وتعتمد هذه الطريقة على المساواة في الأوقات المعطاة لكل ثريد time slices. الشكل التالي يوضح الطريقة (بإفتراض كل ثريد يحتوي على 4 أوامر برمجية):

∎–∎-………………-∎–∎
………∎–∎-………………-∎–∎
………………∎–∎-………………-∎–∎

كما يُلاحظ في الشكل السابق، قام نظام التشغيل بجدولة الـ CPU على جميع الثريدات لتشغيلها جميعاً بشكل متزامن. قام الـ CPU بتنفيذ أمرين من الثريد الأولى ثم قام بالانتقال “context-switching” إلى الثريد الثانية وقام بتنفيذ أمرين، ثم انتقل إلى الثريد الثالثة وقام بتنفيذ أمرين أيضاً، ثم عاد إلى الثريد الأولى ونفذ أمرين … وهكذا. في الحقيقة، ليس هناك طريقة ثابتة متبعة من قبل أنظمة التشغيل لتنفيذ الثريدات. في المثال السابق مثلاً، ربما سيقوم نظام التشغيل بتنفيذ الثريد الأول كاملاً ثم ينتقل إلى البقية. ربما أيضاً يقوم بتنفيذ الثريد الثاني أو الثالث قبل البقية. حتى أن الترتيب أيضاً سيختلف مع كل مرة تقوم فيها بتشغيل البرنامج. الترتيب غير مهم في أغلب الحالات، ولكن إن لزم ترتيبها، توجد عدة طرق للتحكم بالترتيب، ومنها جعل ثريد معين بأولوية أعلى للتنفيذ، أو تشغيل ثريد معين عند الانتهاء من تنفيذ ثريد آخر.

– عندما يحتوي جهاز الحاسب على أكثر من CPU واحد، أو يحتوي الـ CPU الواحد على عدة أنوية “cores”، يتم التنفيذ بشكل متوازي “in parallel”. لنطبق المثال السابق ولكن هذه المرة مع وجود CPU ثنائي النواة:

∎–∎–∎–∎………
∎–∎-………-∎–∎
………∎–∎–∎–∎

مع وجود CPU ثنائي النواة، زادت سرعة التنفيذ إلى الضعف (قل زمن التنفيذ إلى النصف)! أيضاً، الترتيب هنا مجهول ومتغير ويختلف في كل مرة يتم فيها تشغيل البرنامج.

– لنأخذ مثال بسيط لتتضح الصورة بشكل أكبر:

أ/ مطعم يقدم وجبات سريعة. يوجد به عامل واحد فقط يقوم بتقديم الطعام إلى الزبائن. الزبائن عادةً يقفون في صف واحد وكل واحد ينتظر دوره. هذا مثال لجهاز حاسب (مطعم) به CPU واحد (العامل) وثريد وحدة من الأوامر البرمجية (صف واحد من الزبائن).

ب/ مطعم يوجد به عامل واحد مشابه للمطعم السابق، ولكن هذه المرة الزبائن يقفون على 3 صفوف والعامل يقوم بخدمة الصفوف الثلاثة بأسلوب الـ round-robin. هذا مثال لجهاز حاسب (مطعم) به CPU واحد (العامل) و 3 ثريدات من الأوامر البرمجية (3 صفوف من الزبائن).

ج/ مطعم يوجد به عاملان، والزبائن تقف في 3 صفوف. هذا مثال لجهاز حاسب (مطعم) به 2 من الـ CPU أو CPU ثنائي النواة (العاملان) و3 ثريدات من الأوامر البرمجية (3 صفوف من الزبائن). هنا الإنتاجية ستزيد.

– ملحوظة أخيرة وهي بأن نظام التشغيل يحتوي على الآلاف من الثريدات ونظام التشغيل يقوم بجدولة الـ CPUs المتوفرة لتنفيذها، وليس برنامجك وحده من يتم خدمته من قبل الـ CPUs.

]]>
https://fouad.io/2012/11/thread_definition/feed/ 7