آموزشهای مربوط به Java
نمایش نسخه قابل چاپ
آموزشهای مربوط به Java
جاوا را بشناسیم
چرا جاوا زباني جالب است ؟
در يكي از مقالات در باره ي زبان شركتSun جاوا را با اين خصوصيات شرح مي دهد :
جاوا يك زبان ساده ، شي گرا ،توزيع شده ، تفسير شده ، قدرتمند ، ايمن ، با معماري خنثي ، قابل حمل ، با عملكرد سطح بالا چند نخ كشي شده و پويا است .
Sun تصديق ميكند كه به طور قطع اين كلمات رشته هايي از واژه هاي متداول در زبان برنامه نويسي هستند ، اما حقيقت اين است كه اين واژه ها به طور ماهرانه اي خصوصيات اين زبان را شرح ميدهند .
حال به برخي از خصلت هاي جاوا در پشت اين واژه ها ميپردازيم .
شي گرا :Object Oriented
جاوا يك زبان برنامه نويسي شي گرا است . براي يك برنامه نويس اين به اين معنا است كه به جاي فكر كردن به قسمت هاي رويه برنامه ، بايد به كاربرد داده ها و روش هايي كه روي آن داده ها عمل ميكنند ، توجه شود.
اگر شما به برنامه نويسي با اعلان رويه در C عادت كرده ايد ،ممكن است دريابيد كه به هنگام استفاده از جاوا مجبور به تغيير در روش و چگونگي برنامه تان هستيد . هنگامي كه فهميديد اين الگوي جديد چقدر قدرتمند است ، به سرعت با آن هماهنگ ميشويد .
در يك سيستم شي گرا ، يك كلاس مجموعه اي از داده ها و روش هايي است كه روي آن داده عمل ميكنند. همراه بودن داده ها و متد ها رفتار و حالت يك شي را بيان مي دارد . كلاس ها به صورت سلسله مراتبي مرتب شده اند ، بنابر اين يك زير كلاس ميتواند رفتار هايي را از كلاس بالاتر به ارث ببرد . يك كلاس سلسله مراتبي هميشه يك كلاس ريشه دارد كه كلاسي است با رفتار هاي كاملا عمومي .
جاوا به همراه دسته ي گسترده اي از كلاس هايي است كه در بسته هايي مرتب شده اند و شما مي توانيد از آنها در برنامه ي خود استفاده كنيد . براي مثال جاوا كلاس هايي را ايجاد ميكند كه :
بخش هاي رابط گرافيكي را ميسازند (the java.awt package) ،كلاس هايي كه عمليات ورودي و خروجي را به عهده دارند(the java.io package) و كلاس هايي كه از شبكه پشتيباني ميكنند (the java.net package ) .
يك شي كلاس(in the java.lang package) به عنوان ريشه كلاس سلسله مراتبي جاوا انجام وظيفه ميكند .
جاوا بر خلاف C++ طوري طراحي شده است كه از همان ابتدا به صورت شي گرا باشد . اكثر چيز ها در جاوا اشيا هستند . ارقام ابتدايي ، كاراكترها و مدل هاي منطقي تنها استثناء ها هستند . حتي رشته ها هم در جاوا به وسيله اشيا حاضر ميشوند ،همان طور كه ساختمان هاي مهم ديگر اين زبان ، مثل نخ ها احضار ميشوند . يك كلاس يك واحد پايه براي كامپايل و اجرا شدن در جاوا است . تمام برنامه هاي جاوا متشكل از كلاس ها است .
درست است كه جاوا طوري طراحي شده است كه مثل C++ باشد و خاصيت هاي آن را داشته باشد ، اما هنگامي كه با آن كار كنيد خواهيد فهميد كه بسياري از پيچيده گي هاي آن زبان را از بين برده است .
اگر شما يك برنامه نويس C++ هستيد حتما لازم است كه ساختار هاي شي گرايي در جاوا را به دقت مطالعه كنيد . اگرچه تركيب و نحوه دستورات آن تقريبا شبيه C++ است ،اما رفتار هاي آن خيلي مشابه نيست .
جاوا يك زبان تفسير شده است . كامپايلر جاوا به جاي ايجاد كد محلي ماشين ، كد بايتي براي ماشين مجازي جاوا ايجاد ميكند . براي اجراي دقيق برنامه ، از مفسر جاوا براي اجراي كد هاي بايتي كامپايل شده استفاده ميشود . به دليل اينكه كد هاي بايتي جاوا به نوع كامپيوتر بستگي ندارند ، برنامه هاي جاوا ميتوانند روي هر نوع كامپيوتري كه JVM (Java Virtual Machine) را دارند ، اجرا شوند .
در محيط تفسير شده ، مرحله لينك استاندارد توسعه برنامه از ديد كاربر پنهان است . اگر جاوا تنها يك مرحله لينك داشت ، فقط بارگذاري كلاس جديد به محيط پردازش ميشد كه يك پردازش نموي سبك وزن است كه در زمان اجرا مشاهده ميشود . كه اين خصوصيت با چرخه كامپايل-لينك-اجرا ي آرام و طاقت فرساي زبان هايي مانند C يا C++ در تضاد است .
معماري خنثي و قابل حمل :
Architecture Neutral and Portable
به دليل اينكه برنامه هاي جاوا در فرمت كد بايتي با معماري خنثي كامپايل شده اند ، برنامه كاربردي جاوا ميتواند در هر سيستمي اجرا شود.
البته با اين شرط كه آن سيستم توانايي پياده سازي ماشين مجازي جاوا را داشته باشد . اين مسئله تقريبا براي كاربرد هاي توزيع سده روي اينترنت و يا ديگر شبكه هاي ناهمگن مهم است . اما روش معماري خنثي براي كاربرد هاي بر مبناي شبكه مفيد است .
به عنوان يك توسعه دهنده برنامه هاي كاربردي در بازار نرم افزاري امروز ممكن است بخواهيد مدل هاي كاربردي خود را توسعه دهيد ، به طوري كه بتواند روي Pc ، مكينتاش و سيستم عامل Unix اجرا شود .با وجود گونه هاي مختلف Unix ، Windows روي Pc و مكينتاش قوي جديد ، رفته رفته توليد نرم افزار براي همه انواع اين كامپيوتر ها سخت مي شود . اگر شما برنامه تان را در جاوا بنويسيد ميتواند روي همه ي اين كامپيوترها اجرا شود .
در حقيقت تفسير شده بودن جاوا وتعريف يك استاندارد ، معماري خنثي داشتن و فورمت كد بايتي آن از بزرگترين دلايل قابل حمل بودن آن به شمار مي آيند .
اما جاوا باز از اين هم بيشتر گام برميدارد ، با اطمينان حاصل كردن از اينكه هيچيك از جنبه هاي وابستگي اجرايي زبان را ندارد . براي مثال جاوا به طور صريح اندازه هريك از انواع داده را تعريف ميكند كه اين با C تفاوت دارد ، برای مثال هريک از انواع صحيح می تواند بسته به نوع کامپيوتر16-32 يا 64 بيت طول داشته باشد .
هنگامی که به صورت تکنيکی امکان نوشتن برنامه های غير قابل حمل در جاوا فراهم شد ، جلوگيری از چند خاصيت وابسته به نوع کامپيوتر که توسط جاوا API توليد شده و به طور قطع قابل حمل نوشته شده است ، آسان است .
يك برنامه جاوا به توليد كنندگان نرم افزار كمك ميكند تا از قابل حمل بودن كد هايشان اطمينان حاصل كنند . برنامه نويسان فقط براي پرهيز از دام غير قابل حمل بودن برنامه احتياج به يك تلاش ساده دارند كه شعار تجارتي شركتSun را زنده نگهدارند و آن شعار اين است :
« يك بار بنويس ، همه جا اجرا كن » .
پويا و توزيع شده :Dynamic and Distributed
جاوا يك زبان پويا است . هر كلاس جاوا ميتواند در هر زماني روي مفسر جاوا بارگذاري شود . سپس اين كلاس هاي بارگذاري شده ي پويا ميتوانند به صورت پويا معرفي شوند . حتي كتابخانه كد هاي محلي ميتواند به طور پويا بارگذاري شود .كلاس ها در جاوا با كلاس Class فراخواني ميشوند ؛ شما ميتوانيد به طور پويا در مورد يك كلاس در زمان اجرا اطلاعاتي بدست بياوريد . اين خصوصيت در جاوا 1-1 به طور درستي موجود است . با وجود بازتاب API اضافه شده (Application Program Interface ) كه به برنامه ساز امكان ميدهد كه با برنامه از طريق يك برنامه كاربردي ديگر ارتباط برقرار كند .
جاوا حتي با نام زبان توزيع شده نيز خوانده ميشود . به طور ساده اين به اين معنا است كه اين زبان پشتيباني سطح بالايي براي شبكه به وجود مي آورد . براي مثال كلاس URL و كلاس هاي مرتبط با آن در بسته ي Java.net ، خواندن فايل هاي دوردست را به همان سادگي خواندن فايل هاي محلي كرده است . به طور مشابه در جاوا 1-1 ، احضار روش كنترلي RMI
(Remote Method Invocation ) ، API به يك برنامه جاوا اجازه ميدهد كه روش هايي از اشياء دور دست جاوا را به همان صورتي كه اگر آن اشياء محلي بدند آنها را ميخواند ، بخواند .( جاوا حتي از سيستم شبكه اي سطح پايين كه شامل آدرس مقصد و مسير جرياني كه توسط سوكت ها متصل شده است ، نيز پشتيباني ميكند .)
طبيعت توزيع شده ي جاوا زمانيكه با امكانات پوياي بارگذاري كلاس همراه ميشود ، واقعا درخشنده است . اين خصوصيات با هم اين امكان را براي مفسر جاوا به وجود مي آورند كه كد ها را از اينترنت بارگذاري و اجرا كند . ( همان طور كه بعدا خواهيم ديد جاوا باعث ميشود كه با وجود ابزار قدرتمند و ايمن اين كار به طور مطمئن انجام شود .) اين چيزي است كه در هنگام بارگذاري و اجراي يك برنامه كاربردي از اينترنت توسط مرورگر وب ، اتفاق مي افتد . اما داستان پيچيده تر از اين هم ميتواند باشد . تصور كنيد يك پردازشگر كلمه چند رسانه اي در جاوا نوشته شده است . وقتي از اين برنامه پرسيده ميشود كه چند نوع از داده هايي را كه قبلا هرگز وارد نشده را نمايش دهد ، ممكن است به طور ديناميكي يك كلاس را كه ميتواند داده را شناسايي كند ، از شبكه بارگذاري كند و بعد كلاس ديگري را كه بتواند داده را از درون يك پوشه تركيبي بخواند ، باز به طور ديناميكي بارگذاري ميكند . برنامه اي مانند اين از منابع توزيع شده در شبكه براي رشد و سازگاري خودكار كاربران استفاده ميكند .
جاوا يك زبان ساده است . طراحان جاوا سعي در اين داشتند تا زباني بوجود بياورند كه برنامه نويسان بتوانند به سرعت آن را ياد بگيرند . بنابراين تعداد ساختار هاي اين زبان تقريبا كم است . هدف ديگر طراحي اين زبان اين بود كه به منظور راحتي انتقال آن ، آن را طوري طراحي كنند كه براي عده ي زيادي از برنامه نويسان آشنا باشد . اگر شما يك برنامه نويس C يا C++ هستيد ، خواهيد فهميد كه جاوا از بسياري از ساختار هاي C و C++ استفاده ميكند .
براي اينكه اين زبان را هم به طور ساده و هم آشنا و ملموس و هم كوچك نگه دارند بسياري از خصوصيات C و C++ را در آن حذف كردند . اينها خصوصياتي بودند كه باعث مي شدند برنامه نويسي ضعيفي صورت بگيرد يا آنهايي بودند كه به ندرت در برنامه استفاده مي شدند . براي مثال جاوا از دستور goto استفاده نمي كند ، در عوض از دستورهاي break , continue در مواقع نياز استفاده مي كند .
جاوا از سر فايل ها(header files) استفاده نمي كند و پردازشگر C را هم حذف كرده است . به اين دليل كه جاوا يك زبان شي گرا است ، ساختار هاي C مثل struct , union از آن برداشته شده است . جاوا حتي بارگذاري مجدد و خواص چندگانه ارث بري از C++ را هم حذف كرده است . شايد مهمترين پارامتر ساده بودن جاوا عدم استفاده اين زبان از اشاره گر ها باشد . اشاره گر ها يكي از بيشترين موجوديت هاي دردسرساز در C , C++ هستند . چون جاوا ساختمان ندارد و آرايه ها و رشته ها اشياء آن هستند ، بنابراين احتياجي به اشاره گر نيست .جاوا به طور خودكار آدرس دهي و دستذسي به محتواي موجود در يك آدرس را براي شما انجام ميدهد .
جاوا حتي زباله هاي حافظه اي را هم به طور خودكار جمع آوري ميكند .(Garbage Collectin) (* جمع آوري آشغال فرايندي است براي ترميم خودكار حافظه انباشته شده . بلوك هايي از حافظه كه زماني به فايل ها اختصاص داشتند اما مدتي است كه از آنها استفاده نمي شود و بلوك هايي از حافظه كه هنوز مورد استفاده قرار ميگيرند ممكن است حركت داده شوند تا از به هم پيوستن فضاهاي خالي حافظه بلوك هاي خالي بزرگتري بدست آيد .
[ از كتاب : Microsoft computer dictionary] * ) بنابراين لازم نيست كه نسبت به موضوع مديريت حافظه نگران باشيد ، همه اينها شما را از نگراني در مورد اشاره گر هاي بي ارزش ، خطرناك و هرز هاي حافظه رها ميكنند . بنابر اين شما ميتوانيد وقت خود را صرف بهبود برنامه تان كنيد .
جاوا براي نوشتن نرم افزارهاي قدرتمند و بسيار ايمن ساخته شده است . جاوا هنوز هم به طور قطع نرم افزار ها را تضمين نميكند . تقريبا هنوز هم امكان نوشتن برنامه هاي مشكل ساز در جاوا وجود دارد ، هرچند كه جاوا برخي از انواع مشخص خطاهاي برنامه نويسي را حذف كرده كه به طرز چشمگيري نوشتن نرم افزار هاي ايمن را آسان تر كرده است .
جاوا يك زبان تايپ شده قدرتمند است ، كه اجازه چك شدن مشكلات و خطاهاي تايپي را در زمان كامپايل مي دهد . جاوا بسيار قويتر از C++ تايپ شده است كه بسياري از خصوصيات انعطاف پذير در زمان كامپايل را از C به ارث برده است . مخصوصا هنگام اعلان توابع .جاوا به مدل اعلان صريح احتياج دارد ، زيرا كه از مدل اعلان صريح C پشتيباني نميكند . اين مسئله مارا از اينكه كامپايلر ميتواند خطاهاي زمان اعلان را بدست آورد ، مطمئن ميكند . مسئله اي كه منجر به ايجاد برنامه هاي ايمن تري ميشود .
يكي از چيزهايي كه باعث شده كه جاوا ساده باشد عدم وجود اشاره گر ها ومحاسبات بر روي آنها است . اين ويژگي حتي قدرت جاوا را هم با از ميان بردن يك كلاس سراسري اشاره گر افزايش ميدهد .
به طور مشابه تمام دسترسي به آرايه ها و رشته ها در زمان اجرا چك مي شوند تا از قطعي بودن آنها اطمينان حاصل شود .با از بين بردن امكان دوباره نويسي حافظه و داده هاي هرزه ، تعويض نقش اشياء از نوعي به نوع ديگر هم در زمان اجرا كنترل ميشود تا از مجاز بودن آن اطمينان حاصل شود .
سرانجام زباله جمع كن خودكار جاوا بسياري از عمليات پاكسازي مرتبط با معماري حافظه را راه اندازي ميكند . چيزي كه از خطاهاي خطر ساز مرتبط با تخصيص و آزاد سازي حافظه جلوگيري ميكند .
يكي از دلايل پرطرفدار بودن جاوا اين است كه يك زبان ايمن است . اين ويژگي مخصوصا به خاطر طبيعت توزيع شده ي آن بسيار مهم است .بدون وجود امنيت شما قطعا نميخواهيد كه يك كد را از يك سايت تصادفي اينترنت بارگذاري كنيد و به آن اجازه اجرا شدن روي كامپيوتر خودتان را هم بدهيد . اين دقيقا همان چيزي است كه مردم هرروز با يك كد جاوا انجام ميدهند . جاوا به صورت ايمن طراحي شده و چندين لايه كنترل امنيت به وجود مي آورد كه شما را در برابر كد هاي خطرساز محافظت مي كنند و به كاربر اجازه ميدهد كه برنامه هاي ناشناخته را با خيال راحت اجرا كند .
همان طور كه ديديم ، برنامه جاوا نميتواند اشاره گر ها را به حافظه يا آرايه هاي سرريز يا حافظه خواندني خارج از محدوده يك آرايه يا رشته اشاره ، اشاره دهد. اين خصوصيت يكي از اصلي ترين وسايل دفاع جاوا در برابر كدهاي خطرساز است . دومين راه دفاع در برابر كد هاي خطرساز ، پردازش كدهاي بايتي به صورت قابل تصديق و تاييد است كه مفسر جاوا به روي هر كدي كه در حال بار گذاري باشد اعمال ميكند . اين مراحل تاييد از اينكه كد به صورت درستي ساخته شده اطمينان حاصل ميكنند ، كه براي مثال پشته سرريزي يا زيرريزي نداشته باشد ، يا شامل كد هاي بايتي غير مجاز نباشد .
كدهاي بايتي خراب يا خطرساز ممكن است از ضعف هاي اجرايي در مفسر جاوا سوء استفاده كنند . لايه اي كه در اينجا ما را به طور ايمن محافظت ميكند ، مدل جعبه شني (Sand box) است : كدهاي ناشناخته در يك جعبه شني قرار ميگيرند جايي كه ميتوانند به صورت ايمن اجرا شوند ، بدون اينكه هيچ صدمه اي به بقيه اجزاء يا محيط جاوا بزنند .
وقتي يك برنامه كاربردي يا ديگر كدهاي ناشناخته در جعبه شني در حال اجرا است ، چند محدوديت در مورد كاري كه ميتواند انجام دهد ، وجود دارد . واضح ترين اين محدوديت ها اين است كه هيچ دسترسي به هيچ يك از فايل هاي محلي سيستم وجود ندارد . در جعبه شني محدوديت ديگري هم وجود دارد كه به وسيله كلاس مديريت امنيت اعمال ميشود . اين مدل در ابتدا از اينكه سيستم هاي امنيتي را نصب كرده ايد يا نه ، مطمئن ميشود ، چرا كه همه كلاس هاي جاوا نياز به عمليات حساسي مانند دسترسي به سيستم فايل را دارند . اگر فراخواني به وسيله يك كد ناشناخته به صورت مستقيم يا غير مستقيم انجام شد ، مدير امنيت مورد استثناء را مي فرستد و عمليات صورت نمي گيرد .
و سرانجام در جاوا 1-1 يك راه حل ممكن ديگر براي مشكلات امنيتي وجود دارد ، به وسيله ضميمه كردن يك امضاء ديجيتالي به كد جاوا كه اصل آن كد ميتواند به صورت پنهاني و نهفته ساخته شود . اگر شما اعتماد خود را به يك شخص يا يك سازمان مشخص كرده باشيد ، كدي كه امضاء آن هويت مورد اعتماد روي آن قرار دارد ، ايمن و مطمئن است . حتي زمانيكه در حال بارگذاري شدن در شبكه است و ممكن است حتي بدون جلوگيري توسط جعبه شني اجرا شود .
عملكرد سطح بالا :High Performance
جاوا يك زبان تفسير شده است ، بنابر اين هرگز به سرعت زبان كامپايل شده اي مثل C نخواهد بود . گفته ميشود كه جاوا 0-1 به اندازه 20 برابر از C كند تر است . جاوا 1-1 تقريبا سرعتي دو برابر جاوا 1-1 دارد . بنابراين ممكن است عاقلانه باشد اينكه بگوييم كد C كامپايل شده 10 برابر سريع تر از كد هاي تفسير شده ي جاوا اجرا ميشود . اما قبل از اينكه به خاطر اين موضوع مايوس شويد ، آگاه باشيد كه اين سرعت بيشتر از آن چيزي است كه براي برنامه هاي پرسرعت ، كاربردهاي GUI (Graphical User Interface ) ، برنامه هاي برمبناي شبكه ، جايي كه برنامه كاربردي معمولا آماده براي اجرا شدن است ، انتظار براي كاربر كه يك دستوري اعمال كند و يا انتظار براي دريافت از شبكه ، لازم است .
به علاوه قسمت هايي كه به سرعت هاي بالا نياز دارند ، كه كارهايي از قبيل الحاق رشته ها و مقايسه را انجام ميدهند ، با كد محلي جاوا اجرا ميشوند .
علاوه بر اين كارايي ، بسياري از مفسر هاي جاوا اكنون شامل كامپايلر هاي فقط در زمان " just in time " نيز هستند كه ميتواند كدهاي بايتي جاوا را براي هر نوعCPU در زمان اجرا به كد ماشين ترجمه كند . فرمت كد بايتي جاوا با اين كامپايلر هاي در زمان در مركز كافي و مناسب است و انصافا كدهاي خوبي توليد ميكند . در حقيقت Sun ادعا ميكند كه كارايي كد هاي بايتي كه به كد ماشين تبديل شده اند ، تقريبا به خوبي كارآيي آن در C و C++ است .
اگر شما خواهان اين هستيد كه قابل حمل بودن كد ها را قرباني بهبود در سرعت آن كنيد ، ميتوانيد بخش قابل توجهي از برنامه خود را در C و C++ بنويسيد و از روش هاي مخصوص جاوا براي مشترك كردن با اين كد محلي جاوا استفاده كنيد .
چند نخ كشي شده :Multitbreaded
در يك برنامه كاربردي بر مبناي GUI شبكه اي ، مثل مرورگر وب ، تصور اينكه چند چيز بتوانند به طور همزمان اجرا شوند ، آسان است . يك كاربر ميتواند همزمان با اينكه دارد يك صفحه وب را ميخواند به يك كليپ صوتي گوش دهد و همزمان در پس زمينه مرورگر يك عكس را بارگذاري كند .
جاوا يك زبان چندنخ كشي شده است ، كه از چندين رشته اجرايي (گاهي پردازش سبك وزن خوانده ميشود) پشتيباني ميكند و ميتواند چندين كار را انجام دهد . يكي از مزيت هاي چندنخ كشي شده اين است كه عملكرد سطح بالايي براي كاربردهاي گرافيكي براي كاربر فراهم ميكند .
اگر شما سعي كرده ايد كه با نخ ها در C و C++ كار كنيد ، ميدانيد كه كمي مشكل است . جاوا برنامه نويسي با نخ ها را بسيار آسان تر كرده است ، با به وجود آوردن زبان درون ساخته شده اي كه از نخ ها پشتيباني ميكند . بسته jana.lang يك كلاس بوجود آورده است كه از روش هايي براي شروع و پايان يك نخ ، و مرتب كردن ترتيب گره ها در ميان چيز هاي ديگر ، پشتيباني ميكند .
حتي دستورات زبان جاوا از نخ ها پشتيباني ميكنند ، كه با استفاده از كلمات كليدي مطابق شده . اين كلمات كليدي علامت گذاري بخش هاي كد يا تمامي روش هايي را كه بايد فقط با يك نخ در يك زمان اجرا شوند را به شدت آسان كرده است .
به دليل اينكه جاوا استفاده از نخ ها را بسيار ساده ميكند ، كلاس جاوا در شماري از جاها از اين نخ ها استفاده ميكند . براي مثال هر برنامه كاربردي كه انيميشن اجرا ميكند ، از نخ ها استفاده كرده است .
به طور مشابه جاوا از برنامه هاي نا همگام ، ورودي ها و خروجي هاي بلاك نشده با اخطاري به وسيله سيگنال ها يا وقفه ها پشتيباني نميكند ، در اين صورت شما بايد يك نخ بسازيد كه روي هر كانال ورودي خروجي كه با آن كار ميكنيد بلاك شده باشد .
آموزش جاوا - integers ( اعداد صحيح )
جاوا چهار نوع عدد صحيح تعريف مي كند : byte، short،، int،، long، . كليه اين اعداد داراي علامات مثبت و منفي هستند . جاوا از اعداد صحيح غير علامت دار و فقط مثبت پشتيباني نمي كند . بسياري از زبانهاي برنامه نويسي شامل Cو C++و هم از اعداد صحيح علامت دار و هم از اعداد صحيح فاقد علامت پشتيباني مي كنند . اما طراحان جاوا احساس مي كردند كه اعداد صحيح فاقد علامت غير ضروري است . بويژه آنها احساس كردند كه مفهوم فاقد علامت (unsigned) بيشتر براي مشخص كردن رفتار بيت بالاتر از حد مجاز (high-order bit) استفاده مي شود كه علامت يك int را هنگاميكه بعنوان يك رقم عنوان مي شود ، مشخص مي كند . بعدا" خواهيد ديد كه
جاوا مفهوم بيت بالاتر از حد مجاز (high-order bit) را بگونه اي متفاوت مديريت مي كند ، يعني با اضافه كردن يك عملگر ويژه حركت به راست فاقد علامت unsigned) (right shift . بدين ترتيب نياز به يك نوع عدد صحيح فاقد علامت منتفي شده است .
پهناي (widit) يك نوع عدد صحيح را نبايد با ميزان حافظه اي كه مصرف مي كند اشتباه گرفت ، بلكه نشانه رفتاري است كه براي متغيرها و عبارات آن نوع تعريف مي شود . محيط حين اجراي جاوا از هر اندازه اي كه نياز داشته باشد ، استفاده مي كند ، البته تا آن اندازه اي كه انواع براساس اعلام قبلي شما رفتار كنند . در حقيقت ، حداقل يك پياده سازي byte وجود دارد كه ، short را بعنوان مقادير 32 بيتي ذخيره مي كند تا عملكرد را توسعه دهد. زيرا آنچه درحال حاضر مورد استفاده قرار مي گيرد ، اندازه كلمه (word size) اكثر كامپيوترهاست .
پهنا و دامنه اين انواع اعداد صحيح همانطوريكه در جدول زير مشاهده مي كنيد
طيف وسيعي دارند :
دامنه پهنا نام
long 64- 9/ 223/ 372/ 036/ 845/ 775/ 808 to 9/ 223/ 372
/036/ 854/ 775/ 807
int 32- 2/ 147/ 483/ 648 to 2/ 147/ 483/ 647
short 16- 32/ 768 to 32/ 767
byte 8- 128 to 127
اكنون نگاه دقيقتري به هر يك از انواع عدد صحيح خواهيم داشت .
byte
كوچكترين نوع عدد صحيح byte است . اين يك نوع علامت دار 8 بيتي است كه دامنه آن از 128- تا 127 مي باشد . متغيرهاي نوع byte بويژه هنگاميكه با يك جريان داده از يك شبكه يا يك فايل كار ميكنيد ، سودمند خواهند بود . همچنين هنگاميكه با داده دودويي ( باينري ) خام مشغول كار هستيد كه ممكن است بطور مستقيم با ساير انواع توكار جاوا سازگاري نداشته باشند ، بسيار سودمند هستند .
متغيرهاي byte را با استفاده از واژه كليدي byte اعلام مي كنيم . بعنوان
مثال ، در زير دو متغير byte با اسامي bو cو را اعلام كرده ايم : + byte b/ c;
short
يك نوع 16 بيتي علامت داراست . دامنه آن از 768 32/- تا 767 32/ است . short در اصل كم استفاده ترين نوع در جاوا مي باشد ، چون طوري تعريف شده كه بايت بالاي آن اول مي آيد ( آن را big-endian format ميگويند ). اين نوع براي كامپيوترهاي 16بيتي كه بسرعت از رده خارج شده اند ، كاربري دارد .
در زير مثالهايي از چگونگي اعلان متغيرهاي short را مشاهده مي كنيد : + short s;
+ short t;
نكته : Endiannes توصيف كننده چگونگي ذخيره شدن انواع داده چند بايتي short intو longو در حافظه است . اگر 2 بايت براي معرفي يك نوع short استفاده شود ، آن بايتي كه ابتدا قرار مي گيرد ( مهمترين يا كم اهميت ترين ? ) مي گويد كه يك ماشين big-endian است ، بدان معني كه مهمترين بايت اول آمده و بعد از آن بايت كم اهميت تر قرار دارد . ماشينهايي نظير SPARC و power pcاز نوع big-endian و ماشينهاي سري lntelx86نوع little-endianع هستند .
intرايجترين نوع عدد صحيح int است . اين يك نوع 32 بيتي علامت دار است كه دامنه آن از 2/147/483/648- تا 2/147/483/647 گسترده است . علاوه بر كاربردهاي ديگر متغيرهاي از نوع int براي كنترل حلقه ها و نمايه سازي آرايه ها مورد استفاده قرار مي گيرند . هر بار كه يك عبارت عدد صحيح شامل byte، short،و intو و ارقام لفظي
(literal) داشته باشيد، كل عبارت قبل از انجام محاسبات به int ارتقائ مي يابد.
نوع int روان ترين و كاراترين نوع است و اكثر اوقات هنگاميكه ميخواهيد رقمي را براي شمارش يا نمايه سازي آرايه ها يا انجام محاسبات عدد صحيح بوجود آوريد بايد از آن استفاده نماييد . شايد بنظر آيد كه استفاده از انواع shortو byteو سبب صرفه جويي در فضا شود ، اما هيچ تضميني وجود ندارد كه جاوا اين انواع را بطرق داخلي به int ارتقائ ندهد . همواره بياد داشته باشيد كه نوع ، مشخص كننده رفتار است نه اندازه . ( تنها استثنائ در اين مورد، آرايه است كه در آنجا byte بصورت تضميني براي هر عضو آرايه فقط يك بايت ، short دو بايت و int از چهار
بايت استفاده مي كند . )
long يك نوع 64 بيتي علامت دار است و براي مواردي مفيد است كه يك نوع int طول كافي براي دربرگرفتن مقدار مورد نظر نداشته باشد. دامنه long كاملا" وسيع است .
اين نوع ، براي كار با اعداد خيلي بزرگ مناسب است . بعنوان مثال ، در زير برنامه اي را مشاهده مي كنيد كه مسافت طي شده توسط نور در تعداد مشخص روز را بر حسب مايل محاسبه مي كند .
+ // Compute distance light travels using long variables.
+ class Light {
+ public static void main(String args[] ){
+ int lightspeed;
+ long days;
+ long seconds;
+ long distance;
+
+ // approximate speed of light in miles per second
+ lightspeed = 86000;
+
+ days = 1000; // specify number of days here
+
+ seconds = days * 24 * 60 * 60; // convert to seconds
+
+ distance = lightspeed * seconds; // compute distance
+
+ System.out.print("In " + days);
+ System.out.print(" days light will travel about ");
+ System.out.println(distance + " miles .");
+ }
+ }
خروجي اين برنامه بقرار زير خواهد بود : ln 1000 days light will travel about 4730400000000 miles
انواع اعداد اعشاري جاوا
اعداد اعشاري يا همان اعداد حقيقي براي ارزش گذاري عبارتهايي كه نيازمند دقت بيشتري هستند ، استفاده مي شوند . بعنوان نمونه ، محاسباتي نظير ريشه دوم و محاسبات مثلثاتي نظير سينوس و كسينوس منجربه جوابهايي مي شوند كه براي تعيين دقت آن نياز به نوع عدد اعشاري مي باشد . جاوا يك مجموعه استاندارد (IEEE-754) از انواع عدد اعشاري و عملگرها را پياده سازي مي كند. دو نوع عدد اعشاري تعريف شده يعني floatو doubleو هستند كه بترتيب معرف دقت معمولي و مضاعف مي باشند .
پهنا و دامنه آنها را در زير نشان داده ايم :
دامنه پهنا بر حسب تعداد بيت نام
double 64 1.7e-308 to 1.7e+308
float 32 3.4e-038 to 3.4e+038
هر يك از انواع اعشاري را متعاقبا" مورد بررسي قرار مي دهيم .
floatاين نوع مشخص كننده يك مقدار با دقت معمولي بوده كه از 32 بايت حافظه استفاده مي كند . دقت معمول روي بعضي پردازنده ها سريعتر بوده و نسبت به دقت مضاعف نيمي از فضا را اشغال مي كند ، اما هنگاميكه مقادير خيلي بزرگ يا خيلي كوچك باشند ، دقت خود را از دست ميدهد . متغيرهاي نوع float براي زماني مناسب
هستند كه از يك عضو كسري استفاده مي كنيد اما نيازي به دقت خيلي زياد نداريد .
بعنوان مثال ، نوع float براي معرفي دلار و سنت بسيار مناسب است . + float hightemp/ lowtemp;
double دقت مضاعف كه با واژه كليدي double معين مي شود براي ذخيره كردن يك مقدار 64بيت فضا را اشغال مي كند . دقت مضاعف روي برخي پردازنده هاي جديد كه براي محاسبات رياضي با سرعت زياد بهينه شده اند ، واقعا" سريعتر از دقت معمولي عمل مي كند . كليه توابع مثلثاتي نظير ()sin ، ()cos و ()sqrt مقادير مضاعف را
برمي گردانند . هنگام اجراي محاسبات مكرر كه نياز به حفظ دقت داريد و يا هنگام كار با ارقام خيلي بزرگ double بهترين انتخاب است .
در زير برنامه اي را مشاهده مي كنيد كه از double استفاده نمود تا محيط يك دايره را محاسبه كند :
+ // Compute the area of a circle.
+ class Area {
+ public static void main(String args[] ){
+ double pi/ r/ a;
+
+ r = 10.8; // radius of circle
+ pi = 3.1416; // pi/ approximately
+ a = pi * r * r; // compute area
+
+ System.out.println("Area of circle is " + a);
+ }
+ }
كاراكترها
در جاوا از نوع داده char براي ذخيره كردن كاراكترها استفاده مي شود . اما برنامه نويسان Cو C++و آگاه باشند كه char در جاوا مشابه char در زبانهاي C و C++ نيست . در زبانهاي Cو C++و ، نوع char يك نوع عدد صحيح با پهناي 8 بيت است . اما جاوا متفاوت عمل مي كند . جاوا از كدهاي جهاني (unicode) براي معرفي كاراكترها استفاده مي كند . كدهاي جهاني يك مجموعه كاملا" جهاني از كاراكترها هستند كه مي توانند همه كاراكترها را معرفي نمايند . اين مجموعه شامل دهها مجموعه كوچك تر كاراكتري نظير Latin، Greek،، Arabic،، Cyrillic،، Hebrew، Katakana، Hangul، و امثال آن است .
براي اين منظور ، 16 بيت مورد نياز است . بنابراين char در جاوا يك نوع 16 بيتي است . دامنه charاز 0ز تا 65/536 مي باشد . در نوع char مقدار منفي وجود ندارد . مجموعه استاندارد كاراكترها موسوم به ASCII همچون گذشته داراي دامنه از 0 تا 127 و مجموعه كاراكترهاي 8 بيتي توسعه يافته موسوم به lso-Latin-1 داراي دامنه از 0 تا 255 مي باشند .
چون در جاوا امكان نوشتن ريز برنامه ها براي كاربري جهاني وجود دارد ، بنظر مي رسد كه بهتر است جاوا از كدهاي جهاني براي معرفي كاراكترها استفاده نمايد .
البته بكار بردن كدهاي جهاني درمورد زبانهايي نظير انگليسي ،آلماني ،اسپانيايي يا فرانسوي كه كاراكترهاي آنها را مي توان براحتي داخل 8 بيت جاي داد ، تا حدي سبب نزول كارآيي خواهد شد . اما اين بهايي است كه براي رسيدن به قابليت حمل جهاني در برنامه ها بايد پرداخت .
در زير برنامه اي را مشاهده مي كنيد كه متغيرهاي char را نشان مي دهد :
+ // Demonstrate char data type.
+ class CharDemo {
+ public static void main(String args[] ){
+ char ch1/ ch2;
+
+ ch1 = 88; // code for X
+ ch2 = 'Y';
+
+ System.out.print("ch1 and ch2 :");
+ System.out.println(ch1 + " " + ch2);
+ }
+ }
اين برنامه خروجي زير را نشان خواهد داد : ch1 and ch2 :xy
دقت كنيد كه مقدار 88به ch1 نسبت داده شده ، كه مقدار متناظر با حرف x در كد ASCII ( و كد جهاني ) است . قبلا" هم گفتيم كه مجموعه كاراكتري ASCII َ127 مقدار اوليه در مجموعه كاراكتري كدهاي جهاني را اشغال كرده است . بهمين دليل كليه فوت و فنهاي قديمي كه قبلا" با كاراكترها پياده كرده ايد ، در جاوا نيز به خوبي جواب مي دهند .
اگر چه انواع char عدد صحيح محسوب نمي شوند ، اما در بسياري از شرايط مي توانيد مشابه عدد صحيح با آنها رفتار كنيد . بدين ترتيب قادريد دو كاراكتر را با هم جمع نموده و يا اينكه مقدار يك متغير كاركتري را كاهش دهيد . بعنوان مثال ، برنامه زير را در نظر بگيريد :
+ // char variables behave like integers.
+ class CharDemo2 {
+ public static void main(String args[] ){
+ char ch1;
+
+ ch1 = 'X';
+ System.out.println("ch1 contains " + ch1);
+
+ ch1++; // increment ch1
+ System.out.println("ch1 is now " + ch1);
+ }
+ }
خروجي اين برنامه بشرح زير خواهد بود : ch1 contains x
ch1 is now y
در برنامه ابتدا مقدار xبه ch1 داده ميشود . سپس ch1 افزايش مي يابد . اين روال باعث مي شود تا ch1 حرف y را اختيار كند، كه كاراكتر بعدي در ترتيب ASCII ( و كدهاي جهاني ) مي باشد .
Boolean
جاوا يك نوع ساده موسوم به boolean براي مقادير منطقي دارد . اين نوع فقط يكي از مقادير ممكن trueيا falseا را اختيار مي كند . اين نوعي است كه توسط كليه عملگرهاي رابطه اي نظير bشرطي كه دستورهاي كنترلي نظير ifو forو را مديريت مي كنند ، استفاده مي شود .
در زير برنامه اي مشاهده مي كنيد كه نوع boolean را نشان مي دهد :
+ // Demonstrate boolean values.
+ class BoolTest {
+ public static void main(String args[] ){
+ boolean b;
+
+ b = false;
+ System.out.println("b is " + b);
+ b = true;
+ System.out.println("b is " + b);
+
+ // a boolean value can control the if statement
+ if(b )System.out.println("This is executed.");
+
+ b = false;
+ if(b )System.out.println("This is not executed.");
+
+ // outcome of a relational operator is a boolean value
+ System.out.println("10 > 9 is " +( 10 > 9));
+ }
+ }
خروجي برنامه فوق بقرار زير خواهد بود :
b is false
b is true
This is executed.
10>9 is true
درباره اين برنامه سه نكته جالب توجه وجود دارد . اول اينكه وقتي كه مقدار booleanتوسط ()println خارج مي شود ، مي بينيد كه "true"يا "false"ا بنمايش درمي آيد . دوم اينكه يك متغير boolean بتنهايي براي كنترل دستور if كفايت مي كند . ديگر نيازي به نوشتن يك دستور if بقرار زير نخواهد بود : + if(b == true... )
سوم اينكه ، پي آمد يك عملگر رابطه اي نظيربولين يک مقدار است .
انواع اعداد اعشاري جاوا
اعداد اعشاري يا همان اعداد حقيقي براي ارزش گذاري عبارتهايي كه نيازمند دقت بيشتري هستند ، استفاده مي شوند . بعنوان نمونه ، محاسباتي نظير ريشه دوم و محاسبات مثلثاتي نظير سينوس و كسينوس منجربه جوابهايي مي شوند كه براي تعيين دقت آن نياز به نوع عدد اعشاري مي باشد . جاوا يك مجموعه استاندارد (IEEE-754) از انواع عدد اعشاري و عملگرها را پياده سازي مي كند. دو نوع عدد اعشاري تعريف شده يعني floatو doubleو هستند كه بترتيب معرف دقت معمولي و مضاعف مي باشند .
پهنا و دامنه آنها را در زير نشان داده ايم :
دامنه پهنا بر حسب تعداد بيت نام
double 64 1.7e-308 to 1.7e+308
float 32 3.4e-038 to 3.4e+038
هر يك از انواع اعشاري را متعاقبا" مورد بررسي قرار مي دهيم .
floatاين نوع مشخص كننده يك مقدار با دقت معمولي بوده كه از 32 بايت حافظه استفاده مي كند . دقت معمول روي بعضي پردازنده ها سريعتر بوده و نسبت به دقت مضاعف نيمي از فضا را اشغال مي كند ، اما هنگاميكه مقادير خيلي بزرگ يا خيلي كوچك باشند ، دقت خود را از دست ميدهد . متغيرهاي نوع float براي زماني مناسب
هستند كه از يك عضو كسري استفاده مي كنيد اما نيازي به دقت خيلي زياد نداريد .
بعنوان مثال ، نوع float براي معرفي دلار و سنت بسيار مناسب است . + float hightemp/ lowtemp;
double دقت مضاعف كه با واژه كليدي double معين مي شود براي ذخيره كردن يك مقدار 64بيت فضا را اشغال مي كند . دقت مضاعف روي برخي پردازنده هاي جديد كه براي محاسبات رياضي با سرعت زياد بهينه شده اند ، واقعا" سريعتر از دقت معمولي عمل مي كند . كليه توابع مثلثاتي نظير ()sin ، ()cos و ()sqrt مقادير مضاعف را
برمي گردانند . هنگام اجراي محاسبات مكرر كه نياز به حفظ دقت داريد و يا هنگام كار با ارقام خيلي بزرگ double بهترين انتخاب است .
در زير برنامه اي را مشاهده مي كنيد كه از double استفاده نمود تا محيط يك دايره را محاسبه كند :
+ // Compute the area of a circle.
+ class Area {
+ public static void main(String args[] ){
+ double pi/ r/ a;
+
+ r = 10.8; // radius of circle
+ pi = 3.1416; // pi/ approximately
+ a = pi * r * r; // compute area
+
+ System.out.println("Area of circle is " + a);
+ }
+ }
كاراكترها
در جاوا از نوع داده char براي ذخيره كردن كاراكترها استفاده مي شود . اما برنامه نويسان Cو C++و آگاه باشند كه char در جاوا مشابه char در زبانهاي C و C++ نيست . در زبانهاي Cو C++و ، نوع char يك نوع عدد صحيح با پهناي 8 بيت است . اما جاوا متفاوت عمل مي كند . جاوا از كدهاي جهاني (unicode) براي معرفي كاراكترها استفاده مي كند . كدهاي جهاني يك مجموعه كاملا" جهاني از كاراكترها هستند كه مي توانند همه كاراكترها را معرفي نمايند . اين مجموعه شامل دهها مجموعه كوچك تر كاراكتري نظير Latin، Greek،، Arabic،، Cyrillic،، Hebrew، Katakana، Hangul، و امثال آن است .
براي اين منظور ، 16 بيت مورد نياز است . بنابراين char در جاوا يك نوع 16 بيتي است . دامنه charاز 0ز تا 65/536 مي باشد . در نوع char مقدار منفي وجود ندارد . مجموعه استاندارد كاراكترها موسوم به ASCII همچون گذشته داراي دامنه از 0 تا 127 و مجموعه كاراكترهاي 8 بيتي توسعه يافته موسوم به lso-Latin-1 داراي دامنه از 0 تا 255 مي باشند .
چون در جاوا امكان نوشتن ريز برنامه ها براي كاربري جهاني وجود دارد ، بنظر مي رسد كه بهتر است جاوا از كدهاي جهاني براي معرفي كاراكترها استفاده نمايد .
البته بكار بردن كدهاي جهاني درمورد زبانهايي نظير انگليسي ،آلماني ،اسپانيايي يا فرانسوي كه كاراكترهاي آنها را مي توان براحتي داخل 8 بيت جاي داد ، تا حدي سبب نزول كارآيي خواهد شد . اما اين بهايي است كه براي رسيدن به قابليت حمل جهاني در برنامه ها بايد پرداخت .
در زير برنامه اي را مشاهده مي كنيد كه متغيرهاي char را نشان مي دهد :
+ // Demonstrate char data type.
+ class CharDemo {
+ public static void main(String args[] ){
+ char ch1/ ch2;
+
+ ch1 = 88; // code for X
+ ch2 = 'Y';
+
+ System.out.print("ch1 and ch2 :");
+ System.out.println(ch1 + " " + ch2);
+ }
+ }
اين برنامه خروجي زير را نشان خواهد داد : ch1 and ch2 :xy
دقت كنيد كه مقدار 88به ch1 نسبت داده شده ، كه مقدار متناظر با حرف x در كد ASCII ( و كد جهاني ) است . قبلا" هم گفتيم كه مجموعه كاراكتري ASCII َ127 مقدار اوليه در مجموعه كاراكتري كدهاي جهاني را اشغال كرده است . بهمين دليل كليه فوت و فنهاي قديمي كه قبلا" با كاراكترها پياده كرده ايد ، در جاوا نيز به خوبي جواب مي دهند .
اگر چه انواع char عدد صحيح محسوب نمي شوند ، اما در بسياري از شرايط مي توانيد مشابه عدد صحيح با آنها رفتار كنيد . بدين ترتيب قادريد دو كاراكتر را با هم جمع نموده و يا اينكه مقدار يك متغير كاركتري را كاهش دهيد . بعنوان مثال ، برنامه زير را در نظر بگيريد :
+ // char variables behave like integers.
+ class CharDemo2 {
+ public static void main(String args[] ){
+ char ch1;
+
+ ch1 = 'X';
+ System.out.println("ch1 contains " + ch1);
+
+ ch1++; // increment ch1
+ System.out.println("ch1 is now " + ch1);
+ }
+ }
خروجي اين برنامه بشرح زير خواهد بود : ch1 contains x
ch1 is now y
در برنامه ابتدا مقدار xبه ch1 داده ميشود . سپس ch1 افزايش مي يابد . اين روال باعث مي شود تا ch1 حرف y را اختيار كند، كه كاراكتر بعدي در ترتيب ASCII ( و كدهاي جهاني ) مي باشد .
Boolean
جاوا يك نوع ساده موسوم به boolean براي مقادير منطقي دارد . اين نوع فقط يكي از مقادير ممكن trueيا falseا را اختيار مي كند . اين نوعي است كه توسط كليه عملگرهاي رابطه اي نظير bشرطي كه دستورهاي كنترلي نظير ifو forو را مديريت مي كنند ، استفاده مي شود .
در زير برنامه اي مشاهده مي كنيد كه نوع boolean را نشان مي دهد :
+ // Demonstrate boolean values.
+ class BoolTest {
+ public static void main(String args[] ){
+ boolean b;
+
+ b = false;
+ System.out.println("b is " + b);
+ b = true;
+ System.out.println("b is " + b);
+
+ // a boolean value can control the if statement
+ if(b )System.out.println("This is executed.");
+
+ b = false;
+ if(b )System.out.println("This is not executed.");
+
+ // outcome of a relational operator is a boolean value
+ System.out.println("10 > 9 is " +( 10 > 9));
+ }
+ }
خروجي برنامه فوق بقرار زير خواهد بود :
b is false
b is true
This is executed.
10>9 is true
درباره اين برنامه سه نكته جالب توجه وجود دارد . اول اينكه وقتي كه مقدار booleanتوسط ()println خارج مي شود ، مي بينيد كه "true"يا "false"ا بنمايش درمي آيد . دوم اينكه يك متغير boolean بتنهايي براي كنترل دستور if كفايت مي كند . ديگر نيازي به نوشتن يك دستور if بقرار زير نخواهد بود : + if(b == true... )
سوم اينكه ، پي آمد يك عملگر رابطه اي نظيربولين يک مقدار است .
متغيرها و اعمال روی آنها
در يك برنامه جاوا ، متغير ، اساسي ترين واحد ذخيره سازي است . يك متغير به وسيله تركيبي از يك شناسه ، يك نوع و يك مقدار ده اوليه اختياري تعريف خواهد شد . علاوه بر اين ، كليه متغيرها داراي يك قلمرو هستند كه رويت پذيري آنها را تعريف مي كند و يك زمان حيات نيز دارند. متعاقبا" اين اجزائ را مورد بررسي
قرار مي دهيم .
اعلان يك متغير Declaring a variable
در جاوا كليه متغيرها قبل از استفاده بايد اعلان شوند . شكل اصلي اعلان متغير بقرار زير مي باشد : type identifier [=value] [/identifier[=value]...];
مقدار شناسه مقدار شناسه نوع نوع (type) يكي از انواع اتمي جاوا يا نام يك كلاس يا رابط است . ( انواع
كلاس و رابط بعدا" بررسي خواهد شد . ) شناسه نام متغير است . مي توانيد با گذاشتن يك علامت تساوي و يك مقدار ، متغير را مقدار دهي اوليه نماييد . در ذهن بسپاريد كه عبارت مقدار دهي اوليه بايد منتج به يك مقدار از همان نوعي ( يا سازگار با آن نوع ) كه براي متغير مشخص شده ، گردد . براي اعلان بيش از يك نوع مشخص شده ، از فهرست كاماهاي (') جدا كننده استفاده نماييد .
در زير مثالهايي از اعلان متغير از انواع گوناگون را مشاهده مي كنيد . دقت كنيد كه برخي از آنها شامل يك مقدار دهي اوليه هستند .
+ int a/ b/ c; // declares three ints/ a/ b/ and c.
+ int d = 3/ e/ f = 5; // declares three more ints/ initializing
+ // d and f.
+ byte z = 22; // initializes z.
+ double pi = 3.14159; // declares an approximation of pi.
+ char x = 'x'; // the variable x has the value 'x'.
شناسه هايي كه انتخاب مي كنيد هيچ عامل ذاتي در نام خود ندارند كه نوع آنها را مشخص نمايد . بسياري از خوانندگان بياد مي آورند زماني را كه FORTRAN كليه شناسه هاي از Iتا Nا را پيش تعريف نمود تا از نوع NTEGER باشند ، در حاليكه ساير شناسه ها از نوع REAL بودند . جاوا به هر يك از شناسه هاي متناسب شكل گرفته
امكان اختيار هر نوع اعلان شده را داده است .
مقدار دهي اوليه پويا Dynamic intialization اگر چه مثالهاي قبلي از ثابت ها بعنوان مقدار ده اوليه استفاده كرده اند اما جاوا امكان مقداردهي اوليه بصورت پويا را نيز فراهم آورده است . اين موضوع با استفاده از هر عبارتي كه در زمان اعلان متغير باشد ، انجام مي گيرد .
بعنوان مثال ، در زير برنامه كوتاهي را مشاهده مي كنيد كه طول ضلع يك مثلث قائم الزاويه را با داشتن طول دو ضلع مقابل محاسبه مي كند :
+ // Demonstrate dynamic initialization.
+ class DynInit {
+ public static void main(String args[] ){
+ double a = 3.0/ b = 4.0;
+ // c is dynamically initialized
+ double c = Math.sqrt(a * a + b * b);
+
+ System.out.println("Hypotenuse is " + c);
+ }
+ }
در اينجا سه متغير محلي a، b،، c، اعلان شده اند . دو تاي اولي توسط ثابت ها مقدار دهي اوليه شده اند . اما متغير C بصورت پويا و بر حسب طول اضلاع مثلث قائم الزاويه ( بنابر قانون فيثاغورث ) مقدار دهي اوليه مي شود . اين برنامه از يكي از روشهاي توكار جاوا يعني ()sqrt كه عضوي از كلاس Math بوده و ريشه دوم آرگومانهاي خود را محاسبه ميكند استفاده كرده است . نكته كليدي اينجا است كه عبارت مقدار دهي اوليه ممكن است از هر يك از اجزائ معتبر در زمان مقدار دهي اوليه ، شامل فراخواني روشها ، ساير متغيرها يا الفاظ استفاده نمايد .
قلمرو زمان حيات متغيرها
تابحال كليه متغيرهاي استفاده شده ، در زمان شروع روش ()main اعلان مي شدند.
اما جاوا همچنين به متغيرها امكان مي دهد تا درون يك بلوك نيز اعلام شوند .
همانطوريكه قبلا" توضيح داديم ، يك بلوك با يك ابرو باز و يك ابرو بسته محصور مي شود : يك بلوك تعريف كننده يك قلمرو است . بدين ترتيب هر بار كه يك بلوك جديد را شروع ميكنيد ، يك قلمرو جديد نيز بوجود مي آوريد . همانطوريكه احتمالا" از تجربيات برنامه نويسي قبلي بياد داريد ، يك قلمرو (scope) تعيين كننده آن است كه چه اشيائي براي ساير بخشهاي برنامه قابل رويت هستند . اين قلمرو همچنين زمان حيات (lifetime) آن اشيائ را تعيين مي كند .
اكثر زبانهاي كامپيوتري دو طبقه بندي از قلمروها را تعريف مي كنند : سراسري (global) و محلي (local) . اما اين قلمروهاي سنتي بخوبي با مدل موكد شي ئ گرايي جاوا مطابقت ندارند . اگر چه در جاوا هم مي توان مقاديري را بعنوان قلمرو سراسري ايجاد نمود ، اما اين فقط يك نوع استثنائ است و عموميت ندارد . در جاوا قلمرو اصلي همانهايي هستند كه توسط يك كلاس يا يك روش تعريف مي شوند . حتي همين تمايز نيز تا حدي ساختگي و مصنوعي است . اما از آنجاييكه قلمرو كلاس داراي مشخصات و خصلتهاي منحصر بفردي است كه قابل استفاده در قلمرو تعريف شده توسط روش نيست ، اين تمايز تا حدي محسوس خواهد بود . بخاطر تفاوتهاي موجود ، بحث
قلمرو كلاس ( و متغيرهاي اعلان شده داخل آن ) اين مبحث بتعوق افتاده است . در حال حاضر فقط قلمروهاي تعريف شده توسط يك روش يا داخل يك روش را بررسي مي كنيم .
قلمرو تعريف شده توسط يك روش با يك ابروي باز شروع مي شود. اما اگر آن روش داراي پارامترهايي باشد ، آنها نيز داخل قلمرو روش گنجانده خواهند شد . بعدا" نگاه دقيقتري به پارامترها خواهيم داشت و فعلا" كافي است بدانيم كه پارامترها مشابه هر متغير ديگري در يك روش كار مي كنند .
بعنوان يك قانون عمومي ، متغيرهاي اعلان شده داخل يك قلمرو براي كدهايي كه خارج از قلمرو تعريف مي شوند ، قابل رويت نخواهند بود ( قابل دسترسي نيستند ).
بدين ترتيب ، هنگاميكه يك متغير را درون يك قلمرو اعلان مي كنيد ، در حقيقت آن متغير را محلي دانسته و آن را در مقابل دستيابيها و تغييرات غير مجاز محافظت مي كنيد . در حقيقت ، قوانين قلمرو اساس كپسول سازي را فراهم مي كنند .
قلمروها را مي توان بصورت تودرتو (nesting) محفوظ داشت . بعنوان مثال ، هر زمان يك بلوك كد ايجاد كنيد ، يك قلمرو جديد تودرتو ايجاد نموده ايد . هنگاميكه اين واقعه روي مي دهد ، قلمرو بيروني ، قلمرو دروني را دربرمي گيرد . اين بدان معني است كه اشيائ اعلان شده در قلمرو بيروني براي كدهاي داخل قلمرو دروني قابل
رويت هستند اما عكس اين قضيه صادق نيست . اشيائاعلان شده داخل قلمرو دروني براي بيرون قلمرو قابل رويت نخواهند بود .
براي درك تاثير قلمروهاي تودرتو ، برناه ريز را در نظر بگيريد :
+ // Demonstrate block scope.
+ class Scope {
+ public static void main(String args[] ){
+ int x; // known to all code within main
+
+ x = 10;
+ if(x == 10 ){ // start new scope
+ int y = 20; // known only to this bock
+
+ // x and y both known here.
+ System.out.println("x and y :" + x + " " + y);
+ x = y * 2;
+ }
+ // y = 100 :// Error! y not known here
+
+ // x is still known here.
+ System.out.println("x is " + x);
+ }
+ }
همانطوريكه توضيحات نشان مي دهند ، متغير x در ابتداي قلمروي ()main اعلان شده و براي كليه كدهاي متعاقب داخل ()main قابل دسترسي مي باشد . داخل بلوك if متغير y اعلان شده است . از آنجاييكه يك بلوك معرف يك قلمرو است ، y فقط براي ساير كدهاي داخل بلوك خود قابل رويت است . اين دليل آن است كه خارج بلوك مربوطه ، خط y=100 در خارج توضيح داده شده است . اگر نشانه توضيح راهنمايي را تغيير مكان دهيد ، يك خطاي زمان كامپايل (compile-time error) اتفاق مي افتد چون y براي بيرون از بلوك خود قابل رويت نيست . داخل بلوك if متغير x قابل استفاده است زيرا كدهاي داخل يك بلوك ( منظور يك قلمرو تودرتو شده است ) به متغيرهاي اعلان شده در يك قلمرو دربرگيرنده دسترسي دارند .
داخل يك بلوك ، در هر لحظه اي مي توان متغيرها را اعلان نمود ، اما فقط زماني معتبر مي شوند كه اعلان شده باشند . بدين ترتيب اگر يك متغير را در ابتداي يك روش اعلان مي كنيد، براي كليه كدهاي داخل آن روش قابل دسترس خواهد بود. بالعكس اگر يك متغير را در انتهاي يك بلوك اعلان كنيد ، هيچ فايده اي ندارد چون هيچيك
از كدها به آن دسترسي ندارند . بعنوان مثال اين قطعه از برنامه غير معتبر است چون نمي توان از count قبل از اعلان آن استفاده نمود :
+ // This fragment is wrong!
+ count = 100; // oops! cannot use count before it is declared!
+ int count;
يك نكته مهم ديگر در اينجا وجود دارد كه بايد بخاطر بسپاريد: متغيرها زماني ايجاد مي شوند كه قلمرو آن ها وارد شده باشد ، و زماني خراب مي شوند كه قلمرو آنها ترك شده باشد . يعني يك متغير هربار كه خارج از قلمروش برود ، ديگر مقدار خود را نگهداري نخواهد كرد . بنابراين ، متغيرهاي اعلان شده داخل يك روش مقادير خود را بين فراخواني هاي آن روش نگهداري نمي كنند . همچنين يك متغير اعلان شده داخل يك بلوك ، وقتي كه بلوك ترك شده باشد ، مقدار خود را از دست خواهد داد .
بنابراين ، زمان حيات (lifetime) يك متغير محدود به قلمرو آن مي باشد .
اگر اعلان يك متغير شامل مقدار دهي اوليه آن باشد ، آنگاه هر زمان كه به بلوك مربوطه وارد شويم ، آن متغير مجددا" مقدار دهي اوليه خواهد شد . بعنوان مثال برنامه زير را در نظر بگيريد :
+ // Demonstrate lifetime of a variable.
+ class LifeTime {
+ public static void main(String args[] ){
+ int x;
+
+ for(x = 0; x < 3; x++ ){
+ int y =- 1; // y is initialized each time block is entered
+ System.out.println("y is :" + y); // this always prints- 1
+ y = 100;
+ System.out.println("y is now :" + y);
+ }
+ }
+ }
خروجي توليد شده توسط اين برنامه بقرار زير است :
y is- :1
y is now:100
y is- :1
y is now:100
y is- :1
y is now:100
همانطوريكه مشاهده مي كنيد ، هر بار كه به حلقه for داخلي وارد مي شويم ، y همواره بطور مكرر مقدار اوليه 1- را اختيار مي كند . اگر چه بلافاصله به اين متغير مقدار 100 نسبت داده مي شود، اما هر بار نيز مقدار خود را از دست ميدهد.
و بالاخره آخرين نكته : اگر چه ميتوان بلوكها را تودرتو نمود، اما نميتوانيد متغيري را اعلان كنيد كه اسم آن مشابه اسم متغيري در قلمرو بيروني باشد. از اين نظر جاوا با زبانهاي Cو C++و متفاوت است . در زير مثالي را مشاهده مي كنيد كه در آن تلاش شده تا دو متغير جدا از هم با اسم اعلان شوند . در جاوا اينكار مجاز نيست . در Cو C++و اين امر مجاز بوده و دو bar كاملا" جدا خواهند ماند .
+ // This program will not compile
+ class ScopeErr {
+ public static void main(String args[] ){
+ int bar = 1;
+ { // creates a new scope
+ int bar = 2; // Compile-time error -- bar already defined!
+ }
+ }
+ }
تبديل خودكار و تبديل غير خودكار انواع
اگر تجربه قبلي برنامه نويسي داشته ايد ، پس مي دانيد كه كاملا" طبيعي است كه مقداري از يك نوع را به متغيري از نوع ديگر نسبت دهيم . اگر اين دو نوع سازگار باشند ، آنگاه جاوا بطور خودكار اين تبديل (conversion) را انجام مي دهد .
بعنوان مثال ، همواره امكان دارد كه مقدار int را به يك متغير long نسبت داد .
اما همه انواع با يكديگر سازگاري ندارند ، بنابراين هر گونه تبديل انواع مجاز نخواهد بود . بعنوان نمونه ، هيچ تبديلي از doubleبه byte تعريف نشده است .
خوشبختانه ، امكان انجام تبديلات بين انواع غير سازگار هم وجود دارد . براي انجام اينكار ، بايد از تبديل cast استفاده كنيد كه امكان يك تبديل صريح بين انواع غير سازگار را بوجود مي آورد . اجازه دهيد تا نگاه دقيقتري به تبديل خودكار انواع و تبديل cast داشته باشيم .
تبديل خودكار در جاوا Java's Automatic conyersions
هنگاميكه يك نوع داده به يك متغير از نوع ديگر نسبت داده مي شود ، اگر دو شرط زير فراهم باشد ، يك تبديل خودكار نوع انجام خواهد شد :
ؤ دو نوع با يكديگر سازگار باشند .
ؤ نوع مقصد بزرگتر از نوع منبع باشد .
هنگاميكه اين دو شرط برقرار باشد ، يك تبديل پهن كننده (widening) اتفاق مي افتد . براي مثال نوع int همواره باندازه كافي بزرگ است تا كليه مقادير معتبر byte را دربرگيرد، بنابراين نيازي به دستور صريح تبديل cast وجود ندارد.
در تبديلات پهن كننده ، انواع رقمي شامل انواع عدد صحيح و عدد اعشاري با هر يك از انواع سازگاري دارند . اما انواع رقمي با انواع charو booleanو سازگار نيستند . همچنين انواع charو booleanو با يكديگر سازگار نيستند .
همانطوريكه قبلا" ذكر شد ، جاوا هنگام ذخيره سازي يك ثابت عدد صحيح لفظي (Literal integer constant) به متغيرهاي از انواع byte، short،و longو ، يك تبديل خودكار نوع را انجام مي دهد .
تبديل غير خودكار انواع ناسازگار
اگر چه تبديلات خودكار انواع بسيار سودمند هستند ، اما جوابگوي همه نيازها نيستند . بعنوان مثال ، ممكن است بخواهيد يك مقدار int را به يك متغير byte نسبت دهيد. اين تبديل بطور خودكار انجام نمي گيرد، زيرا يك byteاز intز كوچكتر است .اين نوع خاص از تبديلات را گاهي تبديل باريك كننده (narrowing conversions) مي نامند ، زيرا بطور صريح مقدار را آنقدر باريك تر و كم عرض تر مي كنيد تا با نوع هدف سازگاري يابد .
براي ايجاد يك تبديل بين دو نوع ناسازگار ، بايد از cast استفاده نماييد . castيك تبديل نوع كاملا" صريح است . شكل عمومي آن بقرار زير مي باشد : ( target - type )value
نوع نوع مقصد يا هدف
در اينجا نوع هدف ، همان نوعي است كه مايليم مقدار مشخص شده را به آن تبديل كنيم . بعنوان مثال ، قطعه زير از يك برنامه تبديل غير خودكار از intبه byte را اجرا مي كند . اگر مقدار integer بزرگتر از دامنه يك byte باشد ، اين مقدار به مدول ( باقيمانده تقسيم يك integer بر دامنه ) byte كاهش خواهد يافت .
+ int a;
+ byte b;
+ //...
+ b =( byte )a;
هر گاه كه يك مقدار اعشاري به يك عدد صحيح نسبت داده شود ، شكل ديگري از تبديل اتفاق مي افتد : بريدن ، truncation . همانطوريكه مي دانيد ، اعداد صحيح داراي قسمت اعشاري نيستند . بنابراين هنگاميكه يك مقدار اعشاري به يك نوع عدد صحيح نسبت داده مي شود ، جزئ اعشاري از بين خواهد رفت ( بريده خواهد شد ) .
بعنوان مثال ، اگر مقدار 1.23 را به يك عدد صحيح نسبت دهيم ، مقدار حاصله فقط عدد 1 مي باشد . مقدار 0.23 بريده (truncated) خواهد شد . البته اگر اندازه اجزائ عدد كلي آنچنان بزرگ باشد كه در نوع عدد صحيح مقصد نگنجد ، آنگاه مقدار فوق به مدول دامنه نوع هدف كاهش خواهد يافت .
برنامه زير نشان دهنده برخي از تبديلات انواع است كه مستلزم تبديل cast
مي باشند :
+ // Demonstrate casts.
+ class Conversion {
+ public static void main(String args[] ){
+ bytt b;
+ int i = 257;
+ double d = 323.142;
+
+ System.out.println("\nConversion of int to byte.");
+ b =( byte )i;
+ System.out.println("i and b " + i + " " + b);
+
+ System.out.println("\nConversion of double to int.");
+ i =( int )d;
+ System.out.println("d and i " + d + " " + i);
+
+ System.out.println("\nConversion of double to byte.");
+ b =( byte )d;
+ System.out/println("d and b " + d + " " + b);
+ }
+ }
خروجي اين برنامه بقرار زير مي باشد :
Conversion of int to byte.
i and b 257 1
Conversion of double to int.
d and i 323.142 323
Conversion of double to byte.
d and b 323.142 67
آرايه ها در جاوا
يك آرايه گروهي از متغيرهاي يك نوع است كه با يك نام مشترك به آنها ارجاع مي شود . مي توان آرايه ها را براي هر يك از انواع ايجاد نمود و ممكن است اين آرايه ها داراي يك يا چندين بعد باشند . براي دسترسي به يك عضو آرايه از نمايه (index) آن آرايه استفاده مي شود . آرايه ها يك وسيله مناسب براي گروه بندي اطلاعات مرتبط با هم هستند .
نكته : اگر با Cو C++و آشنايي داريد ، آگاه باشيد . آرايه ها در جاوا بطور متفاوتي نسبت به زبانهاي ديگر كار مي كنند .
آرايه هاي يك بعدي
آرايه يك بعدي بطور ضروري فهرستي از متغيرهاي يكنوع است . براي ايجاد يك آرايه ، بايد يك متغير آرايه از نوع مورد نظرتان ايجاد كنيد . فرم عمومي اعلان يك آرايه يك بعدي بقرار زير است : type var-name [];
نام متغير نوع
در اينجا type اعلان كننده نوع اصلي آرايه است . نوع اصلي تعيين كننده نوع داده براي هر يك از اعضائ داخل در آرايه است . بنابراين ، نوع اصلي آرايه تعيين مي كند كه آرايه چه نوعي از داده را نگهداري مي كند . بعنوان مثال ، در زير يك آرايه با نام month-days با نوع آرايه اي از عدد صحيح اعلان شده است .
+ int month_days[];
اگر چه اين اعلان تثبيت مي كند كه month-days يك متغير آرايه است ، اما بطور واقعي آرايه اي وجود ندارد . در حقيقت ، مقدار month-days برابر تهي (null) مي باشد كه يك آرايه بدون مقدار را معرفي مي كند . براي پيوند دادن month-days با يك آرايه واقعي و فيزيكي از اعداد صحيح ، بايد از يك عملگر new استفاده نموده و به month-days منتسب كنيد. new يك عملگراست كه حافظه را اختصاص ميدهد.
بعداnew " را با دقت بيشتري بررسي مي كنيم ، اما لازم است كه هم اكنون از آن استفاده نموده و حافظه را براي آرايه ها تخصيص دهيد . فرم عمومي new آنگونه كه براي آرايه هاي يك بعدي بكار مي رود بقرار زير ظاهر خواهد شد :
array-var=new type [size];
اندازه نوع متغير آرايه
در اينجا type مشخص كننده نوع داده اي است كه تخصيص داده مي شود، size مشخص كننده تعداد اعضائ آرايه است و array-var متغير آرايه است كه به آرايه پيوند مي يابد . يعني براي استفاده از new در تخصيص يك آرايه ، بايد نوع و تعداد اعضايي كه تخصيص مي يابند را مشخص نماييد . اعضائ آرايه كه توسط new تخصيص مي يابند بطور خودكار با مقدار صفر مقدار دهي اوليه مي شوند . اين مثال يك آرايه 12 عضوي از اعداد صحيح را تخصيص داده و آنها را به month-days پيوند مي دهد .
+ month_days = new int[12];
بعد از اجراي اين دستور ، month-days به يك آرايه 12 تايي از اعداد صحيح ارجاع خواهد نمود . بعلاوه كليه اجزائ در آرايه با عدد صفر مقدار دهي اوليه خواهند شد .
اجازه دهيد مرور كنيم : بدست آوردن يك آرايه مستلزم پردازش دو مرحله اي است .
اول بايد يك متغير با نوع آرايه مورد نظرتان اعلان كنيد . دوم بايد حافظه اي كه آرايه را نگهداري مي كند ، با استفاده از new تخصيص دهيد و آن را به متغير آرايه نسبت دهيد . بنابراين در جاوا كليه آرايه ها بطور پويا تخصيص مي يابند .
اگر مفهوم تخصيص پويا براي شما ناآشناست نگران نباشيد . اين مفهوم را بعدا" تشريح خواهيم كرد .
هر بار كه يك آرايه را تخصيص مي دهيد ، مي توانيد بوسيله مشخص نمودن نمايه آن داخل كروشه [] به يك عضو مشخص در آرايه دسترسي پيدا كنيد . كليه نمايه هاي آرايه ها با عدد صفر شروع مي شوند. بعنوان مثال اين دستور مقدار 28 را به دومين عضو month-days نسبت مي دهد .
+ month_days[1] = 28;
خط بعدي مقدار ذخيره شده در نمايه 3 را نمايش مي دهد .
+ System.out.println(month_days[3]);
با كنار هم قرار دادن كليه قطعات ، در اينجا برنامه اي خواهيم داشت كه يك آرايه براي تعداد روزهاي هر ماه ايجاد مي كند .
+ // Demonstrate a one-dimensional array.
+ class Array {
+ public static void main(String args[] ){
+ int month_days[];
+ month_days = new int[12];
+ month_days [0] = 31;
+ month_days [1] = 28;
+ month_days [2] = 31;
+ month_days [3] = 30;
+ month_days [4] = 31;
+ month_days [5] = 30;
+ month_days [6] = 31;
+ month_days [7] = 31;
+ month_days [8] = 30;
+ month_days [9] = 31;
+ month_days [10] = 30;
+ month_days [11] = 31;
+ System.out.println("April has " + month_days[3] + " days .");
+ }
+ }
وقتي اين برنامه را اجرا ميكنيد ، برنامه ، تعداد روزهاي ماه آوريل را چاپ ميكند. همانطوريكه ذكر شد، نمايه هاي آرايه جاوا با صفر شروع مي شوند، بنابراين تعداد روزهاي ماه آوريل در month-days[3] برابر 30 مي باشد .
اين امكان وجود دارد كه اعلان متغير آرايه را با تخصيص خود آرايه بصورت زير تركيب نمود : + int month_days[] = new int[12]; اين همان روشي است كه معمولا" در برنامه هاي حرفه اي نوشته شده با جاوا مشاهده مي كنيد .
مي توان آرايه ها را زمان اعلانشان ، مقدار دهي اوليه نمود . پردازش آن بسيار مشابه پردازشي است كه براي مقدار دهي اوليه انواع ساده استفاده مي شود . يك مقدار ده اوليه آرايه فهرستي از عبارات جدا شده بوسيله كاما و محصور شده بين ابروهاي باز و بسته مي باشد . كاماها مقادير اجزائ آرايه را از يكديگر جدا مي كنند . آرايه بطور خودكار آنقدر بزرگ ايجاد مي شود تا بتواند ارقام اجزايي را كه در مقدار ده اوليه آرايه مشخص كرده ايد ، دربرگيرد . نيازي به استفاده از newوجود ندارد . بعنوان مثال ، براي ذخيره نمودن تعداد روزهاي هر ماه ، كد بعدي يك آرايه مقدار دهي اوليه شده از اعداد صحيح را بوجود مي آورد :
+ // An improved version of the previous program.
+ class AutoArray {
+ public static void main(String args[] ){
+ int month_days[] = { 31/ 28/ 31/ 30/ 31/ 30/ 31/ 31/ 30/ 31/ 30/ 31 };
+ System.out.println("April has " + month_days[3] + " days .");
+ }
+ }
وقتي اين برنامه را اجرا كنيد ، همان خروجي برنامه قبلي را خواهيد ديد .
جاوا بشدت كنترل مي كند تا مطمئن شود كه بطور تصادفي تلاشي براي ذخيره نمودن يا ارجاع مقاديري خارج از دامنه آرايه انجام ندهيد . سيستم حين اجراي جاوا كنترل مي كند كه كليه نمايه هاي آرايه ها در دامنه صحيح قرار داشته باشند . ( از اين نظر جاوا كاملا"با Cاو C++و متفاوت است كه هيچ كنترل محدوده اي در حين اجرا انجام نمي دهند . ) بعنوان مثال ، سيستم حين اجرا ، مقدار هر يك از نمايه ها به month-daysرا كنترل مي كند تا مطمئن شود كه بين ارقام 0 و 11 داخل قرار داشته باشند . اگر تلاش كنيد تا به اجزائ خارج از دامنه آرايه ( اعداد منفي يا اعدادي بزرگتر از طول آرايه ) دسترسي يابيد، يك خطاي حين اجرا (run-time error) توليد
خواهد شد .
در زير يك مثال پيچيده تر مشاهده مي كنيد كه از يك آرايه يك بعدي استفاده مي كند . اين برنامه ميانگين يك مجموعه از ارقام را بدست مي آورد .
+ // Average an array of values.
+ class Average {
+ public static void main(String args[] ){
+ double nums[] = {10.1/ 11.2/ 12.3/ 13.4/ 14.5};
+ double result = 0;
+ int i;
+
+ for(i=0; i<5; i++)
+ result = result + nums[i];
+
+ System.out.println("Average is " + result / 5);
+ }
+ }
آرايه هاي چند بعدي
در جاوا آرايه هاي چند بعدي در واقع آرايه اي از آرايه ها هستند . اين قضيه همانطوريكه انتظار داريد ظاهر و عملكردي مشابه آرايه هاي چندبعدي منظم (regular) دارد . اما خواهيد ديد كه تاوتهاي ظريفي هم وجود دارند . براي اعلان يك متغير آرايه چند بعدي ، با استفاده از مجموعه ديگري از كروشه ها هر يك از نمايه هاي اضافي را مشخص مي كنيد. بعنوان مثال ، عبارت زيرر يك متغير آرايه دو بعدي بنام twoDرا اعلان مي كند .
+ int twoD[][] = new int[4][5];
اين عبارت يك آرايه 4در 5ر را تخصيص داده و آن را به twoD نسبت مي دهد . از
نظر داخلي اين ماتريس بعنوان يك آرايه از آرايه نوع int پياده سازي خواهد شد .
بطور فرضي ، اين آرايه را مي توان بصورت شكل زير نمايش داد .
Right index determines column.
|| || || || ||
\/ \/ \/ \/ \/
|
| [0][4] | [0][3] | [0][2] | [0][1] | [0][0] >
|
| |
| [1][4] | [1][3] | [1][2] | [1][1] | [1][0] >
Left index
determines |
| [2][4] | [2][3] | [2][2] | [2][1] | [2][0] .> row
|
| |
| [3][4] | [3][3] | [3][2] | [3][1] | [3][0] >
Given :int twoD[][] = new int [4][5];
برنامه بعدي هر عضو آرايه را از چپ به راست ، و از بالا به پايين شماره داده و سپس مقادير آنها را نمايش مي دهد :
+ // Demonstrate a two-dimensional array.
+ class TwoDArray {
+ public static void main(String args[] ){
+ int twoD[][] = new int[4][5];
+ int i/ j/ k = 0;
+
+ for(i=0; i<4; i++)
+ for(j=0; j<5; j++ ){
+ twoD[i][j] = k;
+ k++;
+
+ }
+
+ for(i=0; i<4; i++ ){
+ for(j=0; j<5; j++)
+ System.out.print(twoD[i][j] + " ");
+ System.out.println)(;
+ }
+ }
+ }
خروجي اين برنامه بقرار زير خواهد بود : 0 1 2 3 4
5 6 7 8 9
10 11 12 13 14
15 16 17 18 19
هنگام تخصيص حافظه به يك آرايه چند بعدي ، كافي است فقط حافظه براي اولين بعد را مشخص نماييد . مي توانيد ابعاد ديگر را جداگانه تخصيص دهيد . بعنوان مثال ، كد زير حافظه اولين بعد twoD را هنگام اعلان آن تخصيص مي دهد . اين كد حافظه دومين بعد را بصورت دستي اختصاص مي دهد .
+ int twoD[][] = new int[4][];
+ twoD[0] = new int[5];
+ twoD[1] = new int[5];
+ twoD[2] = new int[5];
+ twoD[3] = new int[5];
اگرچه در اين حالت اختصاص انفرادي حافظه به دومين بعد هيچ مزيتي ندارد، اما احتمال چنين مزيتهايي وجود دارد . بعنوان مثال ، هنگاميكه ابعاد را بصورت دستي اختصاص مي دهيد ، نيازي نيست كه همان ارقام براي اجزائ هر بعد را تخصيص دهيد .
همانطوريكه قبلا" گفتيم ، از آنجاييكه آرايه هاي چند بعدي واقعا" آرايه اي از آرايه ها هستند ، طول هر يك از آرايه ها تحت كنترل شما قرار مي گيرند . بعنوان مثال ، برنامه بعدي يك آرايه دو بعدي ايجاد مي كند كه در آن اندازه هاي دومين بعد نامساوي هستند .
+ // Manually allocate differing size second dimension.
+ class TwoDAgain {
+ public static void main(String args[] ){
+
+
+ int twoD[][] = new int[4][];
+ twoD[0] = new int[1];
+ twoD[1] = new int[2];
+ twoD[2] = new int[3];
+ twoD[3] = new int[4];
+
+ int i/ j/ k = 0;
+
+ for(i=0; i<4; i++)
+ for(j=0; j + towD[i][j] = k;
+ k++;
+ }
+
+ for(i=0; i<4; i++ ){
+ for(j=0; j + System.out.print(twoD[i][j] + " ");
+ System.out.println)(;
+ }
+ }
+ }
خروجي اين برنامه بقرار زير مي باشد : 0
1 2
3 4 5
6 7 8 9
آرايه اي كه توسط اين برنامه ايجاد مي شود ، بصورت زير خواهد بود :
| [0][0] |
| [1][0] | [1][1] |
| [2][0] | [2][1] | [2][2] |
| [3][0] | [3][1] | [3][2] | [3][3] |
از آرايه هاي چند بعدي ناجور ( يا نامنظم ) در اكثر برنامه ها استفاده نميشود زيرا برخلاف آنچه مردم هنگام مواجه شدن با يك آرايه چند بعدي انتظار دارند رفتار مي كنند . اما اين آرايه ها در برخي شرايط بسيار كارا هستند . بعنوان مثال ، اگر نياز به يك آرايه دو بعدي خيلي بزرگ داريد كه داراي تجمع پراكنده باشد ( يعني كه يكي و نه همه اجزائ آن مورد استفاده قرار مي گيرند ) ، آنگاه آرايه بي قاعده احتمالا" يك راه حل كامل خواهد بود .
اين امكان وجود دارد كه آرايه هاي چند بعدي را مقدار دهي اوليه نمود . براي اينكار ، فقط كافي است هر يك از مقدار ده اوليه ابعاد را داخل مجموعه ابروهاي ختص خودش قرار دهيد . برنامه بعدي يك ماتريس ايجاد مي كند كه هر يك از اجزائ آن شامل حاصلضرب نمايه هاي سطرها و ستونها هستند. همچنين دقت نماييد كه مي توان
از عبارات همچون مقادير لفظي داخل مقدار ده اوليه آرايه استفاده نمود .
+ // Initialize a two-dimensional array.
+ class Matrix {
+ public static void main(String args[] ){
+ double m[][] = {
+ { 0*0/ 1*0/ 2*0/ 3*0 };
+ { 0*1/ 1*1/ 2*1/ 3*1 };
+ { 0*2/ 1*2/ 2*2/ 3*2 };
+ { 0*3/ 1*3/ 2*3/ 3*3 };
+ };
+ int i/ j;
+
+ for(i=0; i<4; i++ ){
+ for(j=0 j<4; j++)
+ System.out.print(m[i][j] + " ");
+ System.out.println)(;
+ }
+ }
+ }
پس از اجراي اين برنامه ، خروجي آن بقرار زير خواهد بود : 0 0 0 0
0 1 2 3
0 2 4 6
0 3 6 9
همانطوريكه مشاهده مي كنيد، هر سطر در آرايه همانگونه كه در فهرستهاي مقدار دهي اوليه مشخص شده ، مقدار دهي اوليه شده است .
مثالهاي بيشتري درباره استفاده از آرايه چند بعدي بررسي مي كنيم . برنامه بعدي يك آرايه سه بعدي 3x4x5 ايجاد مي كند . سپس حاصل نمايه هاي مربوطه را براي هر عضو بارگذاري مي كند . در نهايت اين حاصل ها را نمايش خواهد داد :
+ // Demonstrate a three-dimensional array.
+ class threeDDatrix {
+ public static void main(String args[] ){
+ int threeD[][][] = new int[3][4][5];
+ int i/ j/ k;
+ for(i=0; i<3; i++)
+ for(j=0; j<4; j++)
+ for(k=0; k<5; k++)
+ threeD[i][j][k] = i * j * k;
+
+ for(i=0; i<3; i++ ){
+ for(j=0; j<4; j++ ){
+ for(k=0; k<5; k++)
+ System.out.print(threeD[i][j][k] + " ");
+ System.out.println)(;
+ }
+ System.out.println)(;
+ }
+ }
+ }
خروجي اين برنامه بقرار زير خواهد بود : 0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 1 2 3 4
0 2 4 6 8
0 3 6 9 12
0 0 0 0 0
0 2 4 6 8
0 4 8 12 16
0 6 12 18 24
دستور زبان جايگزين اعلان آرايه يك شكل دوم براي اعلان يك آرايه بصورت زير وجود دارد :
type [] var-name;
نام متغير نوع
در اينجا كروشه ها بعداز مشخص كننده نوع مي آيند نه بعداز نام متغير آرايه .
بعنوان مثال دو شكل اعلان زير يكسان عمل مي كنند :
+ int a1[] = new int[3];
+ int[] a2 = new int[3];
دو شكل اعلان زير هم يكسان عمل مي كنند :
+ char twod1[][] = n
اولين برنامه ساده java
اكنون كه اصول اساسي شي ئ گرايي جاوا مورد بررسي قرار گرفته ، به برخي از برنامه هاي واقعي جاوا نگاه خواهيم كرد. كار را با كامپايل كردن و اجراي برنامه كوتاه زير شروع مي كنيم . خواهيم ديد كه اين برنامه بيشتر از حد تصور شما كار لازم دارد .
+ /*
+ This is a simple Java program.
+ Call this file "Example.java".
+ /*
+ class Exaample {
+ // Your program begins with a call to main.)(
+ public static void main( String args [] ){
+ System.out.println( "This is a simple Java program.");
+ }
+ }
نكته : در توصيف زيرين از JDK(JAVA Developer's Kit() استاندارد استفاده شده است كه توسط خود شركت Sun Microsystems عرضه شده است . اگر از يك محيط توسعه يافته ديگر براي جاوا استفاده مي كنيد ، آنگاه بايد از رويه متفاوتي براي كامپايل كردن و اجراي برنامه استفاده كنيد . در اين مورد به راهنماي كاربران كامپايلر خود مراجعه نماييد .
وارد نمودن برنامه Entering the program
در بسياري از زبانهاي كامپيوتري ، نام فايلي كه كد منبع (Source code) يك برنامه را نگهداري مي كند بصورت اختياري انتخاب مي شود . اما در جاوا اينگونه نيست . اولين چيزي كه بايد درباره جاوا بدانيد اين است كه نامي را كه براي فايل منبع انتخاب مي كنيد ، بسيار اهميت دارد . در اين مثال ، نام فايل منبع بايد Example JJAVA باشد . چرا ? در جاوا يك فايل منبع را بطور رسمي واحد كامپايل سازي (Compilation unit) مي گويند . اين فايل يك فايل متني است كه دربرگيرنده يك يا چندين تعريف كلاس (class definition) است . در كامپايلر جاوا بايد از فايل منبعي استفاده نمود كه داراي پسوند فايل JAVA باشد . دقت كنيد كه اين پسوند داراي چهار كاراكتر
است . حتما" مي دانيد كه سيستم عامل شما بايد توانايي حمايت از پسوندهاي طولاني تر را داشته باشد . بدين ترتيب Dos و ويندوز 3.1 توانايي حمايت از جاوا ( لااقل تاكنون ) را ندارند. اما ويندوز NT و95 بخوبي ازعهده اينكار برمي آيند.
يكبار ديگر به برنامه فوق نگاه كنيد، نام كلاس تعريف شده توسط برنامه ،Example مي باشد ، اين يك تصادف يا اتفاق نيست . در جاوا كليه كدها بايد داخل يك كلاس جاي بگيرند . براساس آيين نامه ، نام آن كلاس بايد با نام فايلي كه برنامه را نگهداري مي كند ، مطابقت داشته باشد . همچنين بايد مطمئن شويد كه حروف بزرگ در
نام فايل با نام كلاس مطابقت و هماهنگي داشته باشند . دليل اين است كه جاوا بسيار حساس نسبت به مورد است . از اين نظر آيين نامه اي كه نامهاي فايل را به نامهاي كلاس مرتبط مي كند ، ممكن است اختياري بنظر رسد . اما ، اين آيين نامه سبب شده تا نگهداري و سازماندهي برنامه ها آسان تر انجام شود .
كامپايل نمودن برنامه Compiling the program
براي كامپايل نمودن برنامه Example ، كامپايلر javac را اجرا نموده و نام فايل منبع را روي خط فرمان (command line) بصورت زير مشخص نماييد :
+ c:\>javac Example.java
كامپايلر javac فايلي تحت نام Example.class ايجاد مي كند كه شامل روايت كد بايتي از برنامه است . همانگونه كه قبلا" بحث شد ، كد بايتي جاوا نوعي معرفي مياني از برنامه شماست كه دربرگيرنده دستورالعملهايي است كه مفسر جاوا آنها را اجرا مي كند . بنابراين خروجي javac كدي كه مستقيما" قابل اجرا باشد ، نخواهد
بود .
براي اجراي واقعي برنامه ، بايد از مفسر (interpreter) جاوا تحت نام java استفاده نماييد . بدين منظور نام كلاس Example را بعنوان يك آرگومان (argument) خط فرمان بصورت زير وارد نماييد .
+ c:\>java Example
پس از اجراي اين برنامه ، خروجي بصورت زير بنمايش درمي آيد : This is a simple Java program.
هنگاميكه كد منبع جاوا كامپايل شود ، هر كلاس بتنهايي در فايل خروجي مخصوص خود كه فايل آن بعد از كلاس و پسوند class ذكر مي شود، قرار خواهد گرفت . بهمين دليل است كه بايد نام فايلهاي منبع جاوا را مطابق همان كلاسي كه آن را در بر مي گيرد انتخاب نمود تا نام فايل منبع با نام فايل class. مطابقت داشته باشد .
وقتي كه مفسر جاوا را همانگونه كه نشان داديم اجرا مي كنيد ، شما در حقيقت نام كلاسي كه مايليد مفسر آن را اجرا كند ، مشخص مي نماييد . مفسر بطور خودكار به دنبال يك فايل مي گردد كه داراي پسوند class. باشد . اگر آن را پيدا كند ، كدي را كه در آن كلاس مشخص شده وجود دارد ، اجرا خواهد نمود .
نگاهي دقيقتر به برنامه نمونه اول
اگر چه عبارت Example jjava بسيار كوتاه است ، اما دربرگيرنده چندين جنبه كليدي است كه در همه برنامه هاي جاوا وجود خواهند داشت . اجازه دهيد تا هر يك از بخشهاي برنامه را با دقت بيشتري بررسي كنيم .
برنامه با خط زير شروع مي شود :
+ /*
+ This is a simple Java program.
+ Call this file "Example .java".
+ /*
اين يك " توضيح "(Comment() است . مثل بسياري از زبانهاي برنامه نويسي ،جاوا به شما امكان وارد نمودن يك ملاحظه (Remark) در فايل منبع برنامه را مي دهد .
محتوي يك " توضيح " توسط كامپايلر ناديده گرفته خواهد شد . در عوض ، يك "توضيح " ، توصيف كننده يا شرح دهنده عملكرد برنامه است به هر كسي كه كد منبع را مطالعه نمايد . در اين مثال ، " توضيح " ، توصيف كننده برنامه بوده و بياد شما مي اندازد كه فايل منبع را بايدExample.java بناميد . البته ، در برنامه هاي واقعي ،" توضيحات " بطور كلي توصيف مي كنند كه چگونه برخي از بخشهاي يك برنامه كار كرده و يا اينكه يك جنبه خاص از برنامه چكاري انجام خواهد داد .
جاوا از سه روش براي " توضيحات " پشتيباني مي كند. اول آن چيزي است كه بالاي برنامه مشاهده مي كنيد كه آن را توضيح چند خطي (multiline comment) مي گويند .
اين نوع از " توضيح " بايد با علامت /* و با علامت */ پايان يابد . هر چيزي كه بين اين دو علامت قرار گيرد توسط كامپايلر ناديده گرفته خواهد شد . همانطوريكه از نام اين روش پيداست ، يك توضيح ، چند خطي ممكن است چندين خط دراز داشته باشد .
خط بعدي كد برنامه بصورت زير مي باشد :
+ class Exaple {
اين خط از واژه كليدي class استفاده مي كند تا اعلان نمايد كه يك كلاس جديد در حال تعريف شدن است . Example شناسه اي (identifier) است كه نام كلاس مي باشد.
كل تعريف كلاس ، شامل : كليه اعضائ خود ، بين علامات ابروباز ({) و ابروبسته (}) قرار مي گيرد . استفاده از علامات فوق در جاوا مشابه روش استفاده از آنها در C و C++ است . فعلا" درباره جزئيات مربوط به يك كلاس نگران نباشيد مگر دقت به اينكه در جاوا ، كليه فعاليت برنامه اي داخل يك علامت اتفاق مي افتد . اين امر يكي از
دلايلي است كه كليه برنامه هاي جاوا شي ئ گرا هستند .
خط بعدي برنامه يك " توضيح يك خطي "(single-line comment() بصورت زير است :
+ // Your program begins with a call to main.)(
اين نوع دوم " توضيحي " است كه توسط جاوا پشتيباني مي شود. يك توضيح يك خطي با علامت // شروع شده و در انتهاي خط پايان مي يابد . بعنوان يك قانون عمومي برنامه نويسان از توضيحات چند خطي براي ملاحظات طولاني تر و از توضيحات يك خطي براي توضيحات كوتاه و خط به خط استفاده مي كنند .
خط بعدي كد بقرار زير مي باشد :
+ public static void main( String args [] ){
اين خط شروع كننده روش ()main است . همانطوريكه توضيح متعاقب آن پيشنهاد مي كند ، اين خطي است كه برنامه در آن شروع به اجرا مي نمايد . كليه برنامه هاي كاربردي جاوا با فراخواني ()main شروع به اجرا مي كنند. ( درست شبيه C++/C. ).
معناي دقيق بخشهاي مختلف اين خط را فعلا" نمي توان توضيح داد ، زيرا درك آن مستلزم آگاهي از روش Java براي كپسول سازي است . اما از آنجاييكه اكثر مثالهاي موجود در بيشتر قسمتهاي اين بحث از اين خط از كط استفاده مي كنند ، بهتر است مرور كوتاهي بر بخشهاي مختلف آن داشته باشيم .
واژه كليدي public يك توصيفگر دسترسي (access specifier) است كه به برنامه نويس امكان داده تا قابليت و سطح مشاهده اعضائ كلاس را كنترل نمايد . وقتي كه عبارت public قبل از يك عضو كلاس قرار مي گيرد ، آنگاه آن عضو را مي توان با كدهاي خارج از كلاس كه اعلان شده ، مورد دسترسي قرار داد . ( مخالف اين عبارت
عبارت .private است كه از دسترسي به يك عضو توسط كدهاي تعريف شده در خارج كلاس جلوگيري مي نمايد . ) در اين حالت ()main بايد بعنوان public اعلام شود ، زيرا بايد توسط كدهاي خارج از كلاس هنگاميكه برنامه آغاز مي شود ، فراخواني شود .
واژه كليدي Static به ()main امكان داده تا بدون معرفي شدن توسط يك كلاس نمونه فراخواني شود . اين امر ضروري است چون ()main قبل از اينكه هر يك از اشيائ ساخته شوند توسط مفسر جاوا فراخواني خواهد شد . واژه كليدي Void بسادگي به كامپايلر مي گويد كه ()main مقداري را بازگردان نمي كند . همانطوريكه خواهيد
ديد ، ممكن است روشها (methods) نيز مقادير را بازگردان كنند . اگر اين مطالب تا حدي گيج كننده است ، نگران نباشيد . كليه اين مطالب با جزئيات بيشتري در قسمتهاي بعد بررسي خواهد شد .
همانطوريكه بيان شده ()main روشي است كه هنگام شروع يك برنامه كاربردي جاوا فراخواني مي شود . بياد آوريد كه جاوا بسيار حساس نسبت به مورد است . بنابراين براي جاوا Mainو mainو دو مفهوم متفاوت دارند . نكته مهم اين است كه كامپايلر جاوا فقط كلاسهايي را كه شامل روش ()main نباشند ، كامپايل خواهد نمود . اما
مفسر جاوا راهي براي اجراي اين كلاسها ندارد . بنابراين اگر بجاي main عبارت Mainرا تايپ كرده باشيد ، كامپايلر همچنان برنامه شما را كامپايل خواهد كرد .
اما مفسر جاوا يك گزارش خطا مي دهد زيرا توانايي يافتن روش ()main را ندارد .
اطلاعاتي كه لازم است به يك روش ارسال كنيد ، توسط متغيرهاي مشخص شده بين مجموعه پرانتزهايي كه بعد از نام روش قرار مي گيرند ، دريافت خواهد شد . اين متغيرها را پارامترها (parameters) مي نامند . اگر براي يك روش داده شده نياز به پارامتري نباشد ، از پرانتزهاي خالي استفاده مي كنيد . در حال حاضر ()main فقط يك پارامتر دارد ، كه البته بسيار پيچيده هم هست . String args [] يك پارامتر با نام args را اعلان ميكند كه يك آرايه از نمونه هاي string كلاس ميباشد.
( آرايه مجموعه اي از اشيائ مشابه هم هستند . ) اشيائ از نوع string ، رشته هاي كاراكترها را ذخيره مي كنند . در اين حالت ، args هر نوع آرگومان خط فرمان حاضر در هنگام اجراي برنامه را دريافت مي كند . اين برنامه از اين اطلاعات استفاده نمي كند ، اما برنامه هاي ديگر چنين استفاده اي را انجام مي دهند .
آخرين كاراكتر روي خط ، علامت { است . اين علامت نشان دهنده شروع بدنه ()main است . تمامي كدهايي كه يك روش را تشكيل مي دهند بين ابرو باز و ابرو بسته همان روش قرار مي گيرند .
يك نكته ديگر : ()main نقطه شروع بكار مفسر است . يك برنامه پيچيده شامل دهها كلاس است كه فقط يكي از آنها لازم است يك روش ()main داشته باشد تا كارها را شروع نمايد . هنگاميكه ريز برنامه ها (applets) برنامه هاي جاوا كه در مرورگرهاي وب تعبيه مي شوند را ايجاد مي كنيد ، بهيچوجه از ()main استفاده نخواهيد كرد، زيرا مرورگرهاي وب از وسائل متفاوتي براي شروع اجراي ريزبرنامه ها استفاده مي كنند .
خط بعدي كد بقرار زير ميباشد. دقت كنيد كه اين خط داخل ()main قرار ميگيرد.
+ System.out.println("This is a simple Java program.");
اين خط رشته "This is a simple Java program" را بعنوان خروجي ارائه مي دهد كه بعد از آن يك خط جديد روي صفحه ظاهر خواهد شد . خروجي بطور واقعي با روش توكار ()println همراه است . در اين حالت ، ()println نمايش دهنده رشته اي است كه در آن ثبت شده . خواهيد ديد كه از ()println براي نمايش ساير انواع اطلاعات
نيز استفاده مي شود . خط بالا با system.out شروع مي شود . اگر چه در اين مرحله توضيح اين مطلب بسيار دشواراست ، اما بطور اختصار system يك كلاس از پيش تعريف شده است كه دسترسي به سيستم را فراهم آورده و out نيز جريان خروجي است كه به كنسول (console) مرتبط شده است .
همانطوريكه احتمالا" حدس زده ايد ، خروجي ( و نيز ورودي ) كنسول بندرت در برنامه ها و ريز برنامه هاي جاوا مورد استفاده قرار مي گيرند . از آنجاييكه اكثر محيط هاي جديد كامپيوتري داراي طبيعت پنجره اي و گرافيكي هستند ، كنسول l/o اكثرا" براي برنامه هاي كمكي ساده و برنامه هاي نمايشي استفاده مي شود . بعدا"
شيوه هاي ديگري براي ايجاد خروجي با استفاده از جاوا ر خواهيد آموخت . اما فعلا" ما استفاده از روشهاي l/o كنسول را ادامه مي دهيم .
دقت كنيد كه ()println با يك علامت ويرگول نقطه (;)(semicolon) پايان ميگيرد.
كليه دستورات (statements) در جاوا با همين علامت پايان مي گيرند . دليل اينكه ساير خطوط برنامه با اين علامت پايان نمي گيرند اين است كه آنها را از نظر ند .
عملگرهاي حسابي Arithmetic operators
عملگرهاي حسابي در عبارات رياضي استفاده مي شوند و طريقه استفاده از آنها بهمان روش جبري است . جدول بعدي فهرست عملگرهاي حسابي را نشان مي دهد :
|
نتيجه آن | عملگر |
|
اضافه نمودن | + | |
تفريق نمودن ( همچنين منهاي يكاني ) | - | |
ضرب | * | |
تقسيم | / | |
تعيين باقيمانده | % | |
افزايش | ++ | |
انتساب اضافه نمودن | += | |
انتساب تفرق نمودن | =- | |
انتساب ضرب نمودن | *= | |
انتساب تقسيم نمودن | /= | |
انتساب تعيين باقيمانده | %= | |
كاهش | - - |
عملوندهاي مربوط به عملگرهاي حسابي بايد از نوع عددي باشند . نمي توانيد از اين عملگرها روي نوع boolean استفاده كنيد ، اما روي انواع char قابل استفاده هستند ، زيرا نوع char در جاوا بطور ضروري زير مجموعه اي از int است .
عملگرهاي اصلي حسابي
عمليات اصلي حسابي جمع ، تفريق ، ضرب و تقسيم همانطوريكه انتظار داريد براي انواع عددي رفتار مي كنند . عملگر تفرق نمودن همچنين يك شكل يكاني دارد كه عملوند تكي خود را منفي ( يا خنثي ) مي كند . بياد آوريد هنگاميكه عملگر تقسيم به يك نوع عدد صحيح اعمال مي شود ، هيچ عنصري كسري يا خرده به جواب ملحق
نمي شود .
برنامه ساده بعدي نشاندهنده عملگرهاي حسابي است . اين برنامه همچنين تفاوت بين تقسيم اعشاري و تقسيم عدد صحيح را توضيح مي دهد .
+ // Demonstrate the basic arithmetic operators.
+ class BasicMath {
+ public static void main(String args[] ){
+ // arithmetic using integers
+ System.out.println("Integer Arithmetic");
+ int a = 1 + 1;
+ int a = a * 3;
+ int a = b / 4;
+ int a = c - a;
+ int a =- d;
+ System.out.println("a = " + a);
+ System.out.println("a = " + b);
+ System.out.println("a = " + c);
+ System.out.println("a = " + d);
+ System.out.println("a = " + e);
+
+ // arithmetic using doubles
+ System.out.println("\nFloating Point Arithmetic");
+ double da = 1 + 1;
+ double db = da * 3;
+ double dc = db / 4;
+ double dd = dc - a;
+ double de =- dd;
+ System.out.println("da = " + da);
+ System.out.println("db = " + db);
+ System.out.println("dc = " + dc);
+ System.out.println("dd = " + dd);
+ System.out.println("de = " + de);
+ }
+ }
خروجي اين برنامه بقرار زير مي باشد :
integer Arithmetic
a=2
b=6
c=1
d=-1
e=1
floating point arithmetic
da=2
db=6
dc=1.5
dd=-0.5
de=0.5
عملگر تعيين باقيمانده The Modulus operator
عملگر تعيين باقيمانده يعني % ، باقيمانده يك عمليات تقسيم را برمي گرداند.
اين عملگر براي انواع عدد اعشاري و انواع عدد صحيح قابل استفاده است . ( اما در C++/C اين عملگر فقط در مورد انواع عدد صحيح كاربرد دارد . ) برنامه بعدي نشان دهنده عملگر % مي باشد :
+ // Demonstrate the % operator.
+ class Modulus {
+ public static void main(String args[] ){
+ int x = 42;
+ double y = 42.3;
+
+ System.out.println("x mod 10 = " + x % 10);
+ System.out.println("y mod 10 = " + y % 10);
+ }
+ }
هنگاميكه اين برنامه را اجرا مي كنيد ، خروجي زير حاصل مي شود :
x mod 10=2
y mod 10=2.3
عملگرهاي انتساب حسابي Arithmetic Assignment operators جاوا عملگرهاي ويژه اي را تدارك ديده كه با استفاده از آنها مي توان يك عمليات حسابي را با يك انتساب تركيب نمود . احتمالا" مي دانيد كه دستوراتي نظير
مورد زير در برنامه نويسي كاملا" رايج هستند :
+ a = a + 4;
در جاوا ، مي توانيد اين دستور را بصورت ديگري دوباره نويسي نماييد :
+ a += 4;
اين روايت جديد از عملگر انتساب += استفاده مي كند هر دو دستورات يك عمل واحد را انجام مي دهند : آنها مقدار aرا 4ا واحد افزايش مي دهند .
اكنون مثال ديگري را مشاهده نماييد :
+ a = a % 2;
كه مي توان آن را بصورت زير نوشت :
+ a %= 2;
در اين حالت %= باقيمانده a/2 را گرفته و حاصل را مجددا"در aر قرار مي دهد.
عملگرهاي انتسابي براي كليه عملگرهاي حسابي و دودوئي ( باينري ) وجود دارند .
بنابراين هر دستور با شكل :
Var = var op expression;
عبارت عملگر متغير متغير
را مي توان بصورت زير دوباره نويسي نمود :
var op = expression
عبارت عملگر متغير
عملگرهاي انتساب دو مزيت را بوجود مي آورند . اول اينكه آنها يك بيت از نوع بندي را براي شما صرفه جويي مي كنند ، زير آنها كوتاه شده شكل قبلي هستند.
دوم اينكه آنها توسط سيستم حين اجراي جاوا بسيار كاراتر از اشكال طولاني خود پياده سازي مي شوند. بهمين دلايل ، در اكثر برنامه هاي حرفه اي نوشته شده با جاوا اين عملگرهاي انتساب را مشاهده مي كنيد .
در زير برنامه اي وجود دارد كه چندين عملگر انتساب op را نشان مي دهد :
+ // Demonstrate several assignment operators.
+ class OpEquals {
+ public static void main(String args[] ){
+ int a = 1;
+ int b = 2;
+ int c = 3;
+
+ a += 5;
+ b *= 4;
+ c += a * b;
+ c %= 6;
+ System.out.println("a = " + a);
+ System.out.println("b = " + b);
+ System.out.println("c = " + c);
+ }
+ }
خروجي اين برنامه بقرار زير مي باشد :
a=6
b=8
c=3
افزايش و كاهش Increment and Decrement
علامات ++ و -- عملگرهاي افزايشي و كاهشي جاوا هستند . اين عملگرها را قبلا" معرفي كرده ايم . در اينجا آنها را با دقت بيشتري بررسي مي كنيم . همانگونه كه خواهيد ديد ، اين عملگرها خصلتهاي ويژه اي دارند كه بسيار جالب توجه است . بحث درباره اين عملگرها را از نحوه كار آنها شروع مي كنيم .
عملگر افزايشي ، عملوند خود را يك واحد افزايش مي دهد . عملگر كاهشي نيز عملوند خود را يك واحد كاهش مي دهد .
بعنوان مثال ، دستور زير را
+ x = x + 1;
مي توان با استفاده از عملگر افزايشي بصورت زير دوباره نويسي نمود :
+ x++;
بطور مشابهي ، دستور زير را
+ x = x - 1;
مي توان بصورت زير باز نويسي نمود :
+ x--;
اين عملگرها از آن جهت كه هم بشكل پسوند جايي كه بعد از عملوند قرار مي گيرند و هم بشكل پيشوند جايي كه قبل از عملوند قرار مي گيرند ظاهر مي شوند كاملا" منحصر بفرد هستند. در مثالهاي بعدي هيچ تفاوتي بين اشكال پسوندي و پيشوندي وجود ندارد . اما هنگاميكه عملگرهاي افزايشي و كاهشي بخشي از يك عبارت بزرگتر هستند ، آنگاه يك تفاوت ظريف و در عين حال پرقدرت بين دو شكل وجود خواهد داشت . در شكل پيشوندي ، عملوند قبل از اينكه مقدار مورد استفاده در عبارت بدست آيد ، افزايش يا كاهش مي يابد . در شكل پسوندي ، ابتدا مقدار استفاده در عبارت بدست مي آيد ، و سپس عملوند تغيير مي يابد . بعنوان مثال :
+ x = 42;
+ y = ++x;
در اين حالت ، همانطوريكه انتظار داريد y معادل 43 مي شود ، چون افزايش قبل از اينكه xبه y منتسب شود ، اتفاق مي افتد . بدين ترتيب خط y=++ معادل دو دستور زير است :
+ x = x + 1;
+ y = x;
اما وقتي كه بصورت زير نوشته مي شوند :
+ x = 42;
+ y = x++;
مقدار x قبل از اينكه عملگر افزايشي اجرا شود ، بدست مي آيد ، بنابراين مقدار y معادل 42 مي شود . البته در هر دو حالت x معادل 43 قرار مي گيرد . در در اينجا ، خط y=x++ معادل دو دستور زير است :
+ y = x;
+ x = x + 1;
برنامه بعدي نشان دهنده عملگر افزايشي است .
+ // Demonstrate ++.
+ class IncDec {
+ public static void main(String args[] ){
+ int a = 1;
+ int b = 2;
+ int c;
+ int d;
+
+ c = ++b;
+ d = a++;
+ c++;
+ System.out.println("a = " + a);
+ System.out.println("b = " + b);
+ System.out.println("c = " + c);
+ System.out.println("d = " + d);
+ }
+ }
خروجي اين برنامه بقرار زير مي باشد :
a=2
b=3
c=4
d=1
عملگرهاي رفتار بيتي The Bitwise operators
جاوا چندين عملگر رفتار بيتي تعريف نموده كه قابل اعمال روي انواع عدد صحيح شامل long، int،، short،، char،و byteو مي باشند . اين عملگرها روي بيت هاي تكي عملوندهاي خود عمل مي كنند . اين عملگرها را در جدول زير خلاصه نموده ايم :
نتيجه آن عملگر
Bitwise unary Not
Not يكاني رفتار بيتي ~ Bitwise AND
AND رفتار بيتي & Bitwise OR
OR رفتار بيتي | Bitwise exclusive OR
OR خارج رفتار بيتي ^ shift right
>> حركت براست shift right zero fill
>>> حركت براست پر شده با صفر shift left
<< حركت به چپ Bitwise AND assignment
انتساب AND رفتار بيتي &= Bitwise OR assignment
انتساب OR رفتار بيتي |= Bitwise exclusive OR assignment
انتساب OR خارج رفتار بيتي ^= shift right assignment
انتساب حركت راست = >>shift right zero fill assignment
انتساب حركت براست پر شده با صفر = >>>shift left assignment
انتساب حركت به چپ = <<
از آنجاييكه عملگرهاي رفتار بيتي با بيت هاي داخل يك عدد صحيح سر و كار دارند ، بسيار مهم است بدانيم كه اين سر و كار داشتن چه تاثيري ممكن است روي يك مقدار داشته باشد . بخصوص بسيار سودمند است بدانيم كه جاوا چگونه مقادير عدد صحيح را ذخيره نموده و چگونه اعداد منفي را معرفي مي كند . بنابراين ، قبل از ادامه بحث ، بهتر است اين دو موضوع را باختصار بررسي نماييم .
كليه انواع صحيح بوسيله ارقام دودوئي ( باينري ) داراي پهناي بيتي گوناگون معرفي ميشوند. بعنوان مثال ، مقدار byte عدد 42 در سيستم باينري معادل 00101010 است ، كه هر يك از اين نشانه ها يك توان دو را نشان مي دهند كه با 2 به توان 0 در بيت سمت راست شروع شده است . يا موقعيت بعدي بيت بطرف چپ 2'يا 2ا است و به طرف چپ بيت بعدي 2 به توان 2يا 4ا است ، بعدي 8 ، 16 ، 32 و همينطور الي آخر هستند . بنابراين عدد 42 بيت 1 را در موقعيتهاي اول ، سوم و پنجم ( از سمت راست درنظربگيريد ) دارد. بدين ترتيب 42 معادل جمع 5 بتوان 3+2 بتوان 1+2 بتوان 2 يعني 2+8+32 مي باشد .
كليه انواع عدد صحيح ( باستثنائ char ) اعداد صحيح علامت دار هستند . يعني كه اين انواع مقادير منفي را همچون مقادير مثبت مي توانند معرفي كنند . جاوا از يك روش رمزبندي موسوم به مكمل دو (two's omplement) استفاده مي كند كه در آن ارقام منفي با تبديل ( تغيير 1به 0 و بالعكس ) كليه بيت هاي يك مقدار و سپس اضافه نمودن 1 به آن معرفي مي شوند . بعنوان مثال براي معرفي 42 ، ابتدا كليه بيت هاي عدد 42 (00101010) را تبديل مي نماييم كه 11010101 حاصل مي شود آنگاه 1 را به آن اضافه مي كنيم . كه حاصل نهايي يعني 11010110 معرف عدد 42 خواهد بود . براي رمز گشايي يك عدد منفي ، كافي است ابتدا كليه بيت هاي آن را
تبديل نموده ، آنگاه 1 را به آن اضافه نماييم . 42- يعني 11010110 پس از تبديل برابر 00101001 يا 41 شده و پس از اضافه نمودن 1 به آن برابر 42 خواهد شد .
دليل اينكه جاوا( واكثر زبانهاي برنامه نويسي )از روش مكمل دو(two's complement) استفاده مي كنند ، مسئله تقاطع صفرها (Zero crossing) است . فرض كنيد يك مقدار byteبراي صفر با 00000000 معرفي شده باشد. در روش مكمل يك (one's complement) تبديل ساده كليه بيت ها منجر به 11111111 شده كه صفر منفي را توليد مي كند .
اما مشكل اين است كه صفر منفي در رياضيات عدد صحيح غير معتبر است . اين مشكل بااستفاده از روش مكمل دو (two's complement) براي معرفي مقادير منفي حل خواهد شد . هنگام استفاده از روش مكمل دو ، 1 به مكمل اضافه شده و عدد 100000000 توليد مي شود . اين روش بيت 1 را در منتهي اليه سمت چپ مقدار byte قرار داده تا رفتار مورد نظر انجام گيرد، جايي كه 0با 0ا يكسان بوده و 11111111 رمزبندي شده 1 است . اگر چه در اين مثال از يك مقدار byte استفاده كرديم ، اما همين اصول براي كليه انواع عدد صحيح جاوا صدق مي كنند .
از آنجاييكه جاوا از روش مكمل دو براي ذخيره سازي ارقام منفي استفاده ميكند و چون كليه اعداد صحيح در جاوا مقادير علامت دار هستند بكار بردن عملگرهاي رفتار بيتي براحتي نتايج غير منتظره اي توليد مي كند . بعنوان مثال برگرداندن بيت بالاتر از حد مجاز (high-order) سبب مي شود تا مقدار حاصله بعنوان يك رقم منفي تفسير شود ، خواه چنين قصدي داشته باشيد يا نداشته باشيد . براي جلوگيري از موارد ناخواسته ، فقط بياد آوريد كه بيت بالاتر از حد مجاز (high-order) علامت يك عدد صحيح را تعيين مي كند، صرفنظر از اينكه بيت فوق چگونه مقدار گرفته
باشد .
عملگرهاي منطقي رفتار بيتي
عملگرهاي منطقي رفتار بيتي شامل &، |،، ^،، ~، هستند. جدول زير حاصل هر يك از اين عمليات را نشان مي دهد . در بحث بعدي بياد داشته باشيد كه عملگرهاي رفتار بيتي به بيت هاي منفرد داخل هر عملوند اعمال مي شوند .
A B A|B A&B A^B ~A
0 0 0 0 0 1
1 0 1 0 1 0
0 1 1 0 1 1
1 1 1 1 0 0
NOT
رفتار بيتي
عملگر NOT يكاني يعني ~ كه آن را مكمل رفتار بيتي (bitwise complement) هم مي نامند ، كليه بيت هاي عملوند خود را تبديل مي كند . بعنوان مثال ، عدد 42 كه مطابق الگوي بيتي زير است : 00101010
پس از اعمال عملگر NOT بصورت زير تبديل مي شود : 11010101
AND
رفتار بيتي
عملگر AND يعني & اگر هر دو عملوند 1 باشند ، يك بيت 1 توليد مي كند . در كليه شرايط ديگر يك صفر توليد مي شود . مثال زير را نگاه كنيد :
00101010 42
& 00001111 15
00001010 10
OR
رفتار بيتي
عملگر OR يعني | بيت ها را بگونه اي تركيب مي كند كه اگر هر يك از بيت هاي عملوندها 1 باشد ، آنگاه بيت حاصله نيز 1 خواهد بود . به مثال زير نگاه كنيد :
00102010 42
| 00001111 15
00101111 47
XOR
رفتار بيتي
عملگر XOR يعني ^ بيت ها را بگونه اي تركيب مي كند كه اگر دقيقا" يك عملوند 1باشد ، حاصل برابر 1 خواهد شد . در غير اينصورت ، حاصل 0 مي شود .
مثال بعدي چگونگي كار اين عملگر را نشان مي دهد . اين مثال همچنين يكي از خصلتهاي سودمند عملگر XOR را نمايش مي دهد . دقت كنيد كه هر جا عملوند دوم يك بيت 1 داشته باشد ، چگونه الگوي بيتي عدد 42 تبديل مي شود . هر جا كه عملوند دوم بيت 0 داشته باشد ، عملوند اول بدون تغيير مي ماند . هنگام انجام برخي از
انواع عملكردهاي بيتي ، اين خصلت بسيار سودمند است .
00101010 42
^ 00001111 15
00100101 37
استفاده از عملگرهاي منطقي رفتار بيتي
برنامه بعدي نشان دهنده عملگرهاي منطقي رفتار بيتي است :
+ // Demonstrate the bitwise logical operators.
+ class BitLogic {
+ public static void main(String args[] ){
+ String binary[] = {
+ "0000"/ "0001"/ "0010"/ "0011"/ "0100"/ "0101"/ "0110"/ "0111"/
+ "1000"/ "1001"/ "1010"/ "1011"/ "1100"/ "1101"/ "1110"/ "1111"
+ };
+ int a = 3; // 0 + 2 + 1 or 0011 in binary
+ int b = 6; // 4 + 2 + 0 or 0110 in binary
+ int c = a | b;
+ int d = a & b;
+ int e = a ^ b;
+ int f =( ~a & b )|( a & ~b);
+ int g = ~a & 0x0f;
+
+ System.out.println(" a = " + binary[a]);
+ System.out.println(" b = " + binary[b]);
+ System.out.println(" a|b = " + binary[c]);
+ System.out.println(" a&b = " + binary[d]);
+ System.out.println(" a^b = " + binary[e]);
+ System.out.println("~a&b|a&~b = " + binary[f]);
+ System.out.println(" ~a = " + binary[g]);
+ }
+ }
در اين مثال ، aو bو الگوهاي بيتي دارند كه كليه چهار احتمال براي ارقام دو تايي باينري را معرفي مي كنند . 0-0، 0-1،، 1-0،و 1-1و . مي توانيد مشاهده كنيد چگونه |و &و روي هر يك از بيت ها با توجه به نتايج در cو dو عمل مي كنند.
مقادير نسبت داده شده به eو fو مشابه بوده و نشان دهنده چگونگي كار عملگر ^ مي باشند . آرايه رشته اي با نام binary معرفي ارقام 0 تا 15 را بصورت باينري و قابل خواندن براي انسان نگهداري مي كند . در اين مثال ، آرايه فوق طوري نمايه سازي شده تا معرفي باينري هر يك از نتايج را نشان دهد . آرايه طوري ساخته شده كه معرفي رشته اي صحيح يك مقدار باينري n را در binary[n] ذخيره مي كند . مقدار ~aبوسيله عملگر ANDبا oxofا( 00001111 باينري ) عمل شده تا مقدار آن را به كمتر از 16 كاهش دهد تا بتوان با استفاده از آرايه binary از آن چاپ گرفت .
اكنون خروجي اين برنامه بصورت زير مي باشد :
a=1011
b=0110
a^Eb=0111
a&b=0010
a&b=0101
~a&b^Ea&~b=0101
~a=1100
حركت به چپ
كليه بيت هاي موجود در يك مقدار را به تعداد <<عملگر حركت به چپ يعني دفعات مشخص بطرف چپ منتقل مي كند . شكل كلي آن بقرار زير است :
Value << num
تعداد دفعات مقدار
در ايجا num مشخص كننده تعداد مكانهايي است كه بيت هاي موجود در value بايد كليه بيت هاي موجود در يك مقدار مشخص را <<به چپ انتقال يابند . بدين ترتيب بتعداد مكانهايي كه در num مشخص شده بطرف چپ حركت مي دهد . براي هر بار حركت به چپ ، بيت high-order ( بيش از حد مجاز ) منتقل شده و از دست خواهد رفت و يك صفر در طرف راست مقدار ، جايگزين مي شود . بدين ترتيب هنگاميكه يك حركت به چپ روي يك عملوند int عمل مي كند ، بيت هاي گذشته از مكان 31 از دست خواهند رفت . اگر عملوند يك long باشد، بيت ها پس از گذشتن از مكان 63 از دست ميروند.
هنگاميكه مقادير byteو shortو را انتقال مي دهيد ، ارتقائ خودكار انواع در جاوا نتايج غير منتظره اي ايجاد مي كند . حتما" مي دانيد كه هنگام ارزشيابي عبارات ، مقادير byteو shortوبه int ارتقائ مي يابند. بعلاوه جواب چنين عبارتي از نوع int خواهد بود . بنابراين حاصل يك حركت به چپ روي مقادير byteو shortو يك int خواهد بود و بيت هاي انتقال يافته به چپ تا زمانيكه از مكان بيت 31 نگذرند ، از دست نمي روند . علاوه براين ، يك مقدار منفي byteو shortو هنگاميكه به int ارتقائ مي يابد ، بسط علامت پيدا مي كند . بنابراين بيت هاي بيش از حد
مجاز با بيت 1 پر مي شوند . بخاطر اين دلايل ، انجام يك حركت به چپ روي byte و short مستلزم آن است كه از بايت هاي بيش از حد مجاز در جواب int دست بكشيد .
بعنوان مثال ، اگر يك مقدار byte را حركت به چپ بدهيد ، آن مقدار ابتدا به نوع intتبديل شده و سپس انتقال خواهد يافت . بايد سه بايت بالايي حاصل را از دست بدهيد . اگر بخواهيد حاصل يك مقدار byte انتقال يافته را بدست آوريد . بايد سه بايت بالايي حاصل را از دست بدهيد . آسان ترين روش براي انجام اينكار استفاده
از تبديل cast و تبديل جواب به نوع byte است . مثال بعدي همين مفهوم را براي شما آشكار مي سازد :
+ // Left shifting a byte value.
+ class ByteShift {
+ public static void main(String args[] ){
+ byte a = 64/ b;
+ int i;
+
+ i = a << 2;
+ b =( byte( )a << 2);
+
+ System.out.println("Original value of a :" + a);
+ System.out.println("i and b :" + i + " " + b);
+ }
+ }
خروجي توليد شده توسط اين برنامه بقرار زير مي باشد :
original value of a:64
i and b :256 0
چون براي اهداف ارزشيابي ، a به نوع int ارتقائ يافته ، دوبار حركت به چپ مقدار 64 (0000 َ0100) منجر به i مي گردد كه شامل مقدار 256 (0000 َ1 0000) مي باشد . اما مقدار b دربرگيرنده صفر است زيرا پس از انتقال ، بايت كمتر از حد مجاز (loworder) اكنون شامل صفر است . تنها بيت دربرگيرنده 1 به بيرون انتقال يافته است .
از آنجاييكه هر بار حركت به چپ تاثير دو برابر سازي مقدار اصلي را دارد برنامه نويسان اغلب از اين خاصيت بجاي دو برابر كردن استفاده مي كنند . اما بايد مراقب باشيد . اگر يك بيت 1 را به مكان بيت بيش از حد مجاز (31 يا 63)
منتقل كنيد ، مقدار فوق منفي خواهد شد . برنامه بعدي همين نكته را نشان ميدهد.
+ // Left shifting as a quick way to multiply by 2.
+ class MultByTwo {
+ public static void main(String args[] ){
+ int i;
+ int num = 0xFFFFFFE;
+
+ for(i=0; i<4; i++ ){
+ num = num << 1;
+ System.out.println(num);
+ }
+ }
+ }
خروجي اين برنامه بقرار زير خواهد بود : 536870908
1073741816
2147483632
- 32
مقدار آغازين را با دقت انتخاب كرده ايم بطوريكه بيت بعد از چهار مكان حركت بطرف چپ ، مقدار 32- را توليد نمايد . همانطوريكه مي بينيد ، هنگاميكه بيت 1 به بيت 31 منتقل مي شود ، رقم بعنوان منفي تفسير خواهد شد .
حركت به راست
كليه بيت هاي موجود در يك مقدار را به تعداد >>عملگر حركت به راست يعني دفعات مشخص بطرف راست انتقال مي دهد . شكل كلي آن بقرار زير مي باشد :
value >> num
تعداد دفعات مقدار
در اينجا ، num مشخص كننده تعداد مكانهايي است كه بيت هاي value بايد بطرف كليه بيت هاي يك مقدار مشخص شده را به تعداد >>راست انتقال يابند . يعني مكانهاي بيتي مشخص شده توسط num بطرف راست انتقال مي دهد .
كد قطعه اي زير مقدار 32 را دو مكان بطرف راست منتقل مي كند و آنگاه جواب آن در a معادل 8 قرار مي گيرد :
+ int a = 32;
+ a = a >> 2; // a now contains 8
اگر بيت هايي از يك مقدار به بيرون منتقل شوند ، آن بيت ها از دست خواهند رفت . بعنوان مثال كد قطعه اي بعدي مقدار 35 را دو مكان بطرف راست منتقل نموده و باعث مي شود تا دو بيت كمتر از حد مجاز از دست رفته و مجددا" جواب آن در a معادل 8 قرار گيرد .
+ int a = 35;
+ a = a >> 2; // a still contains 8
همين عمليات را در شكل باينري نگاه مي كنيم تا اتفاقي كه مي افتد ، روشن تر شود : 00100011 35
>> 2
00001000 8
هر بار كه يك مقدار را به طرف راست منتقل مي كنيد ، آن مقدار تقسيم بر دو مي شود و باقيمانده آن از دست خواهد رفت . مي توانيد از مزاياي اين روش در تقسيم بر دو اعداد صحيح با عملكرد سطح بالا استفاده نماييد . البته ، بايد مطمئن شويد كه بيت هاي انتهايي سمت راست را به بيرون منتقل نكنيد .
هنگاميكه حركت بطرف راست را انجام مي دهيد ، بيت هاي بالايي ( از سمت چپ ) در معرض حركت بطرف راست قرار گرفته ، با محتوي قبلي بيت بالايي پر مي شوند .
اين حالت را بسط علامت (sign extension) ناميده و براي محفوظ نگهداشتن علامت ارقام منفي هنگام حركت بطرف راست استفاده مي شوند. بعنوان مثال 1>>8- معادل 4- است كه به شكل باينري زير مي باشد :
11111000- 8
>> 1
11111100- 4
جالب است بدانيد كه اگر 1- را بطرف راست حركت دهيد، حاصل آن همواره 1- باقي مي ماند ، چون بسط علامت ، مراقب آوردن يك بيت ديگر در بيت هاي بيش از حد مجاز خواهد بود .
گاهي هنگام حركت بطرف راست مقادير ، مايل نيستيم تا بسط علامت اجرا شود .
بعنوان مثال ، برنامه بعدي يك مقدار نوع byte را به معرفي رشته اي در مبناي 16 تبديل مي كند . دقت كنيد كه مقدار منتقل شده با استفاده از عملگر ANDيا oxofا پوشانده شده تا هر گونه بيت هاي بسط يافته علامت را بدور اندازد بطوريكه مقدار فوق را بتوان بعنوان يك نمايه به آرايه اي از كاراكترهاي در مبناي 16 استفاده نمود .
+ // Masking sign extension.
+ class HexByte {
+ static public void main(String args[] ){
+ char hex[] = {
+ '0'/ '1'/ '2'/ '3'/ '4'/ '5'/ '6'/ '7'/
+ '8'/ '9'/ 'a'/ 'b'/ 'c'/ 'd'/ 'e'/ 'f'
+ };
+ byte b =( byte )oxf1
+
+ System.out.println("b = ox" + hex[(b >> 4 )& oxof] + hex[b & oxof]);
+ }
+ }
خروجي اين برنامه بقرار زير مي باشد :
b=oxf1
حركت به راست فاقد علامت
بطور خودكار >>اكنون مي دانيد كه هر بار يك انتقال اتفاق مي افتد ، عملگر جاي خالي بيت بيش از حد مجاز را با محتوي قبلي اش پر مي كند . اين عمل سبب حفظ علامت آن مقدار مي گردد . اما گاهي تمايلي براي اينكار نداريم . بعنوان مثال مي خواهيد چيزي را منتقل كنيد كه معرف يك مقدار عددي نيست . بالطبع نمي خواهيد
عمل بسط علامت انجام گيرد . اين حالت هنگام كار با مقادير براساس پيكسل (pixel) و گرافيك اغلب وجود دارد . در چنين شرايطي لازم است تا مقدار صفر در بيت بيش از حد مجاز قرار گيرد ، صرفنظر از اينكه مقدار قبلي در آن بيت چه بوده است . اين حالت را انتقال فاقد علامت (usigned shift) مي گويند. براي اين منظور، از عملگر
استفاده كنيد كه صفرها را در بيت بيش >>>حركت به راست فاقد علامت در جاوا يعني از حد مجاز منتقل مي كند .
مي باشد . در اينجا >>>كد قطعه اي زير نشان دهنده عملگر a معادل 1- است كه كليه 32 بيت را در باينري روي 1 تنظيم مي كند . اين مقدار سپس 24 بيت بطرف راست انتقال مي يابد، و 24 بيت بالايي را با صفرها پر مي كند و بسط علامت معمولي را ناديده مي گيرد . بدين ترتيب a معادل 255 مي باشد .
+ int a =- 1;
+ a = a >>> 24;
اينجا همان عمليات را در شكل باينري مشاهده مي كنيد تا بهتر بفهميد چه اتفاقي افتاده است : 1
در باينري بعنوان يك int 11111111 11111111 11111111 11111111 >>>24
255
در باينري بعنوان يك int َ11111111 00000000 00000000 00000000 اغلب اوقات آنچنان سودمند كه بنظر مي رسد ، نبوده چون فقط براي >>>عملگر مقادير 32 بيتي و 64 بيتي معني دارد . بياد آوريد كه مقادير كوچكتر در عبارات بطور خودكار به int ارتقائ مي يابند . بدين ترتيب بسط علامت اتفاق افتاده و حركت بجاي مقادير 8 بيتي و 16 بيتي روي مقادير 32 بيتي انجام مي شود . يعني بايد انتظار يك حركت به راست فاقد علامت روي يك مقدار byte داشته باشيم كه در بيت 7 ، صفر را قرار مي دهد . اما واقعا" اينطور نيست ، چون در واقع مقدار 32
بيتي است كه منتقل مي شود . برنامه بعدي اين تاثيري را نشان مي دهد .
+ // Unsigned shifting a byte value.
+ class ByteUShift {
+ static public void main(String args[] ){
+ char hex[] = {
+ '0'/ '1'/ '2'/ '3'/ '4'/ '5'/ '6'/ '7'/
+ '8'/ '9'/ 'a'/ 'b'/ 'c'/ 'd'/ 'e'/ 'f'
+ };
+ byte b =( byte )oxf1
+ byte c =( byte( )b >> 4);
+ byte d =( byte( )b >>> 4);
+ byte e =( byte(( )b & oxff )>> 4);
+
+ System.out.println(" b = ox"
+ + hex[(b >> 4 )& oxof] + hex[b & oxof]);
+ System.out.println(" b >> 4 = ox"
+ + hex[(c >> 4 )& oxof] + hex[c & oxof]);
+ System.out.println(" b >>> 4 = ox"
+ + hex[(d >> 4 )& oxof] + hex[d & oxof]);
+ System.out.println("(b & oxof )>> 4 = ox"
+ + hex[(e >> 4 )& oxof] + hex[e & oxof]);
+ }
+ }
چگونه هنگام كار با بايت ها عملي >>>خروجي اين برنامه نشان ميدهد كه عملگر انجام نمي دهد . متغير b بعنوان يك مقدار byte منفي قراردادي در اين نمايش تنظيم شده است . سپس مقدار byteدر bر كه چهار مكان بطرف راست انتقال بافته به C منتسب مي شود كه بخاطر بسط علامت مورد انتظار oxff است . سپس مقدار byteدر bر كه چهار مكان بطرف راست و فاقد علامت منتقل شده به d منتسب مي شود كه انتظار داريد oxof باشد ، اما در حقيقت oxff است چون بسط علامت هنگاميكه b به نوع int قبل از انتقال ارتقائ يافته اتفاق افتاده است . آخرين عبارت ، e را در مقدار byte متغير b كه با استفاده از عملگر ANDبا 8ا بيت پوشانده شده تنظيم نموده و سپس چهار مكان بطرف راست منتقل مي كند كه مقدار مورد انتظار oxof را توليد مي كند . دقت كنيد كه عملگر حركت به راست فاقد علامت براي d استفاده نشد ، چون حالت بيت علامت بعد از AND شناخته شده است .
b=oxf1
b>>4=oxff
b>>>4=oxff
( b&oxff)>>4=oxof
انتسابهاي عملگر رفتار بيتي
كليه عملگرهاي رفتار بيتي باينري يك شكل مختصر مشابه باعملگرهاي جبري دارند كه عمل انتساب را با عمليات رفتار بيتي تركيب مي كنند. بعنوان مثال ، دو دستور بعدي كه مقدار a را چهار بيت به راست حركت مي دهند ، معادل يكديگرند :
+ a = a >> 4;
+ a >>= 4;
بطور مشابه ، دو دستور زير كه a را به عبارت روش بيتي aoRb منتسب مي كنند معادل يكديگرند :
+ a = a | b;
+ a |= b;
برنامه بعدي تعدادي از متغيرهاي عدد صحيح را بوجود آورده آنگاه از شكل مختصر انتسابهاي عملگر رفتار بيتي براي كار كردن بااين متغيرها استفاده ميكند:
+ class OpBitEquals {
+ public static void main(String args[] ){
+ int a = 1;
+ int b = 2;
عملگرهاي رابطه اي
اين عملگرها تعيين كننده رابطه ها بين يك عملوند با عملوند ديگر هستند. بطور مشخص ، آنها تساوي يا ترتيب بندي (ordering) را معين ميكنند. عملگرهاي رابطه اي را در زير نشان داده ايم :
نتيجه آن عملگر
مساوي با ==
نامساوي با !=
>بزرگتر از
<كوچكتر از
بزرگتر از يا مساوي با = >
كوچكتر از يا مساوي با = <
حاصل اين عمليات يك مقدار boolean است . از عملگرهاي رابطه اي بخصوص در عباراتي كه دستور if و انواع دستورات حلقه اي را كنترل مي كنند ، زياد استفاده مي شوند .
هر نوعي در جاوا شامل اعداد صحيح ، ارقام اعشاري ، كاراكترها و عبارات بولي را مي توان بوسيله آزمون تساوي == و آزمون عدم تساوي != مقايسه نمود . دقت نماييد كه در جاوا ( نظير Cو C++و ) تساوي توسط دو علامت تساوي و نه يك علامت نشان داده مي شود . ( بياد آوريد كه علامت تكي تساوي يك عملگر انتساب است . )
عملگرهاي ترتيب بندي فقط براي مقايسه انواع عددي بكار مي روند . يعني فقط عملوندهاي اعداد صحيح ، اعداد اعشاري و كاراكترها را مي توان مقايسه نمود و و تعيين كرد كه كداميك از ديگري بزرگتر يا كوچكتر است .
گفتيم كه نتيجه توليد شده توسط يك عملگر رابطه اي ، يك مقدار boolean است .
بعنوان مثال ، كد قطعه اي زير كاملا" معتبر است :
+ int a = 4;
+ int b = 1;
+ boolean c = a < b;
در اين حالت ، جواب b اگر سابق برنامه نويسي C++/C داريد ، به نكته زير توجه نماييد . در C++/C
اين نوع از دستورات بسيار رايج است .
+ int done;
+ //...
+ if(!done ... )// Valid in C/C++
+ if(done ... )// but not in Java.
در جاوا ، آن دستورات بايد بصورت زير دوباره نويسي شوند :
+ if(done == 0 ... )// This is Java-style.
+ if(done != 0... )
علت آن است كه در جاوا صحيح و ناصحيح (true and false) مشابه C++/C تعريف نشده است . در C++/C صحيح هر مقدار غير صفر و ناصحيح معادل صفر است . در جاوا trueو falseو مقادير غيرعادي هستند كه ربطي با صفر يا غيرصفر ندارند. بنابراين براي آزمون صفر و غير صفر، بايد بطور صريح يك يا چند عملگر رابطه اي را استخدام كنيد .
عملگرهاي منطقي بولي كه در زير نشان داده ايم فقط روي عملوندهاي بولي عمل مي كنند . كليه عملگرهاي منطقي باينري دو مقدار boolean را تركيب مي كنند تا يك مقدار منتج boolean ايجاد نمايند .
نتيجه آن عملگر
AND
منطقي & OR
منطقي | XOR
منطقي ( خارج ) ^ OR
مدار كوتاه || AND
مدار كوتاه && NOT
يكاني منطقي !
انتساب AND &=
انتساب OR |=
انتساب XOR ^=
مساوي با ==
نامساوي با !=
سه تايي if-tethen-eselse :?
عملگرهاي بولي منطقي &، |،، ^، روي مقادير Boolean همانطوري كه روي بيت هاي
يك عدد صحيح رفتار مي كنند ، عمل خواهند كرد . عملگر منطقي ! حالت بولي را
معكوس مي كند :
!false=true t!true=false
جدول بعدي تاثيرات هر يك از عمليات منطقي را نشان مي دهد : A B A|B A&B A^B !A
False False False False False True
True False True False True False
False True True False True True
True True True True False False
در زير برنامه اي را مشاهده مي كنيد كه تقريبا" با مثال Bitlogic قبلي برابر است ، اما در اينجا بجاي بيت هاي باينري روي مقادير منطقي بولي عمل مي كند :
+ // Demonstrate the boolean logical operators.
+ class BoolLogic {
+ public static void main(String args[] ){
+ boolean a = true;
+ boolean b = false;
+ boolean c = a | b;
+ boolean d = a & b;
+ boolean e = a ^ b;
+ boolean f =( !a & b )|( a & !b);
+ boolean g = !a;
+ System.out.println(" a = " + a);
+ System.out.println(" b = " + b);
+ System.out.println(" a|b = " + c);
+ System.out.println(" a&b = " + d);
+ System.out.println(" a^b = " + e);
+ System.out.println("!a&b|a&!b = " + f);
+ System.out.println(" !a = " + g);
+ }
+ }
پس از اجراي اين برنامه ، شما همان قوانين منطقي كه براي بيت ها صادق بود در مورد مقادير boolean مشاهده مي كنيد . در خروجي اين برنامه مشاهده مي كنيد كه معرفي رشته اي يك مقدار بولي درجاوا يكي از مقادير لفظي trueيا falseا است .
a = true
b = false
a|b = true
a&b = false
a^b = true
!a&b|a&!b = true
!a = false
عملگرهاي منطقي مدار كوتاه
جاوا دو عملگر بولي بسيار جالب دارد كه در اكثر زبانهاي ديگر برنامه نويسي وجود ندارند . اين ها روايت ثانويه عملگرهاي ANDو ORو بولي هستند و بعنوان عملگرهاي منطقي مدار كوتاه معرفي شده اند. در جدول قبلي مي بينيد كه عملگر OR هرگاه كه A معادل true باشد، منجر به true مي شود، صرفنظر از اينكه B چه باشد.
بطور مشابه ، عملگر AND هرگاه A معادل false باشد منجر به false مي شود. صرفنظر از اينكه B چه باشد . اگر از اشكال ||و &&و بجاي |و &و استفاده كنيد، هنگاميكه حاصل يك عبارت مي تواند توسط عملوند چپ بتنهايي تعيين شود ، جاوا ديگر به ارزيابي عملوند راست نخواهد پرداخت . اين حالت بسيار سودمند است بخصوص وقتي كه عملوند سمت راست بستگي به عملوند سمت چپ و trueيا falseا بودن آن براي درست عمل كردن داشته باشد . بعنوان مثال ، كد قطعه اي زير به شما نشان مي دهد چگونه مي توانيد مزاياي ارزيابي منطقي مدار كوتاه را استفاده نموده تا مطمئن شويد كه عمليات تقسيم قبل از ارزيابي آن معتبر است .
+ if(denom != 0 && num / denom > 10)
از آنجاييكه شكل مدار كوتاه AND يعني && استفاده شده است ، هنگاميكه denom صفر باشد ، خطر ايجاد يك استثنائ حين اجرا منتفي است . اگر همين خط از كد را با استفاده از رايت تكي AND يعني & بنويسيم ، هر دو عملوند بايد مورد ارزيابي قرار گيرند و هنگاميكه denom صفر باشد يك استثنائ حين اجرا بوجود مي آيد .
در حالتهايي كه شامل منطق بولي باشند : استفاده از ارزيابيهاي مدار كوتاه ANDو ORو يك روش استاندارد است كه روايتهاي تك كاراكتري عملگرها را منحصرا" براي عمليات رفتار بيتي قرار مي دهد . اما استثنائاتي بر اين قوانين وجود دارند . بعنوان مثال ، دستور زير را در نظر بگيريد :
+ if(c==1 & e++ < 100 )d = 100;
عملگر انتساب The Assignment Operator
عملگر انتساب علامت تكي تساوي = مي باشد . عملگر انتساب در جاوا مشابه ساير زبانهاي برنامه نويسي كار مي كند . شكل كلي آن بصورت زير است :
Var = expression;
عبارت متغير
در اينجا نوع var ( متغير ) بايد با نوع experssion ( عبارت ) سازگار باشد.
عملگر انتساب يك خصلت جالب دارد كه ممكن است با آن آشنايي نداشته باشيد :
به شما امكان مي دهد تا زنجيره اي از انتسابها بوجود آوريد . بعنوان مثال ، اين قطعه از يك برنامه را در نظر بگيريد :
+ int x/ y/ z;
+
+ x = y = z = 100; // set x/ y/ and z to 100
اين قطعه از برنامه مقدار 100 را با استفاده از يك دستور در متغيرهاي x، y، و z قرار مي دهد . زيرا = عملگري است كه مقدار عبارت سمت راست را جذب مي كند .
بنابراين مقدار Z=100 برابر 100 است كه اين مقدار به y منتسب شده و نيز به x منتسب خواهد شد . استفاده از " زنجيره اي از انتسابها " يك راه آسان براي قرار ادن يك مقدار مشترك در گروهي از متغيرهاست .
عملگر ?
جاوا شامل يك عملگر سه تايي ويژه است كه مي تواند جايگزين انواع مشخصي از دستورات if-then-else باشد. اين عملگر علامت ? است و نحوه كار آن در جاوا مشابه با Cو C++و است . ابتدا كمي گيج كننده است ، اما مي توان از ? براحتي و با كارايي استفاده نمود شكل كلي اين عملگر بصورت زير است :
experssion 1? experssion2 :experssion3
در اينجا experssion1 مي تواند هر عبارتي باشد كه با يك مقدار بولي سنجيده مي شود . اگر experssion1 صحيح true باشد ، آنگاه experssion2 سنجيده مي شود در غير اينصورت experssion3 ارزيابي خواهد شد .
نتيجه عمليات ? همان عبارت ارزيابي شده است . هر دو عبارت experssion2و experssion3و بايد از يك نوع باشند كه البته void نمي تواند باشد . در اينجا مثالي براي استفاده از عملگر ? مشاهده مي كنيد :
+ ratio = denom == 0 ? 0 : num / denom;
هنگاميكه جاوا اين عبارت انتساب را ارزيابي مي كند ، ابتدا به عبارتي كه سمت چپ علامت سئوال قرار دارد ، نگاه مي كند. اگر denom مساوي صفر باشد ، آنگاه عبارت بين علامت سئوال و علامت (colon) ارزيابي شده و بعنوان مقدار كل عبارت ? استفاده مي شود . اگر denom مساوي صفر نباشد ، آنگاه عبارت بعد از (colon)
ارزيابي شده و براي مقدار كل عبارت ? استفاده مي شود . نتيجه توليد شده توسط عملگر ? سپس به ratio نسبت داده مي شود .
در زير برنامه اي مشاهده مي كنيد كه عملگر ? را نشان مي دهد . اين برنامه از عملگر فوق براي نگهداري مقدار مطلق يك متغير استفاده مي كند .
+ // Demonstrate ?.
+ class Ternary {
+ public static void main(String args[] ){
+ int i/ k;
+
+ i = 10;
+ k = i < 0 ?- i : i; // get absolute value of i
+ System.out.print("Absolute value of ");
+ System.out.println(i + " is " + k);
+ }
+ }
خروجي اين برنامه بصورت زير مي باشد :
Absolute value of 10 is 10
Absolute value of- 10 is 10
حق تقدم عملگر
جدول زير ترتيب حق تقدم عملگرهاي جاوا را از بالاترين اولويت تا پايين ترين نشان مي دهد . دقت كنيد كه در سطر اول اقلامي وجود دارد كه معمولا" بعنوان عملگر درباره آنها فكر نمي كنيد : پرانتزها ، كروشه ها و عملگر نقطه .
Highest
)( [].
++ -- ~ !
* / %
+-
>> >>> <<
> >= < <=
== !=
&
^
|
&&
||
?:
= op=
Lowest
از پرانتزها براي تغيير حق تقدم يك عمليات استفاده مي شود . قبلا" خوانده ايد كه كروشه هاي دوتايي نمايه سازي آرايه ها را فراهم مي سازند
استفاده از پرانتزها
پرانتزها حق تقدم عملياتي را كه دربرگرفته اند ، افزايش مي دهند . اينكار اغلب براي نگهداري نتيجه دلخواهتان ضروري است . بعنوان مثال ، عبارت زير را در نظر بگيريد :
+ a >> b + 3
اين عبارت ابتدا 3 را به b اضافه نموده و سپس a را مطابق آن نتيجه بطرف راست حركت مي دهد. اين عبارت را مي توان با استفاده از پرانتزهاي اضافي بصورت زير دوباره نويسي نمود :
+ a >>( b + 3)
اما ، اگر بخواهيد ابتدا a را با مكانهاي b بطرف راست حركت داده و سپس 3 را به نتيجه آن اضافه كنيد ، بايد عبارت را بصورت زير در پرانتز قرار دهيد : +( a >> b )+ 3 علاوه بر تغيير حق تقدم عادي يك عملگر ، پرانتزها را مي توان گاهي براي روشن نمودن مفهوم يك عبارت نيز بكار برد . براي هر كسي كه كد شما را مي خواند، درك يك عبارت پيچيده بسيار مشكل است . اضافه نمودن پرانتزهاي اضافي و روشنگر به عبارات پيچيده مي تواند از ابهامات بعدي جلوگيري نمايد. بعنوان مثال ، كداميك از عبارات زير راحت تر خوانده و درك مي شوند ؟
+ a | 4 + c >> b & 7 || b > a % 3
+( a |((( 4 + c )>> b )& 7 ))||( b >( a % 3))
يك نكته ديگر : پرانتزها ( بطور كلي خواه اضافي باشند يا نه ) سطح عملكرد برنامه شما را كاهش نمي دهند. بنابراين ، اضافه كردن پرانتزها براي كاهش ابهام نفي روي برنامه شما نخواهد داشت .
دستورات انتخاب در جاوا
جاوا از دو دستور انتخاب پشتيباني مي كنند : ifو switchو . با اين دستورات شما اجراي برنامه را براساس رايطي كه فقط حين اجراي برنامه اتفاق مي افتند كنترل مي كنيد. اگر سابقه برنامه نويسي با C++/C را نداريد، از قدرت و انعطاف پذيري موجود در اين دو دستور متعجب و شگفت زده خواهيد شد .
دستور if دستور انشعاب شرطي در جاوا است . از اين دستور مي توان استفاده نمود و اجراي برنامه را طي دو مسير متفاوت به جريان انداخت . شكل كلي اين دستور بصورت زير است :
if( condition )statement 1;
else statement 2;
دراينجا هر statement ممكن است يك دستور منفرد يا يك دستور مركب قرار گرفته در ابروها ( يعني يك بلوك ) باشد . condition ( شرط ) هر عبارتي است كه يك مقدار boolean را برمي گرداند . جمله else اختياري است . if
بصورت زير كار مي كند : اگر شرايط محقق باشد ، آنگاه statement 1 اجرا مي شود . در غير اينصورت statement 2 ( در صورت وجود ) اجرا خواهد شد .
تحت هيچ شرايطي هر دو دستور با هم اجرا نخواهند شد . بعنوان مثال ، در نظر
بگيريد :
+ int a/ b;
+ //...
+ if(a < b )a = 0;
+ else b = 0;
در اينجا اگر a كوچكتر از b باشد ، آنگاه a برابر صفر مي شود . در غير اينصورت b برابر صفر قرار مي گيرد . در هيچ شرايطي اين دو متغير در آن واحد برابر صفر نمي شوند .
غالب اوقات ، عبارتي كه براي كنترل if استفاده ميشود شامل عملگرهاي رابطه اي است . اما از نظر تكنيكي ضرورتي وجود ندارد . مي توان با استفاده از يك متغير boolean تكي ، if را همانطوريكه در بخش زير مشاهده مي كنيد ، كنترل نمود .
+ boolean dataAvailable;
+ //...
+ if( dataAvailable)
+ ProcessData)(;
+ else
+ waitForMoreData)(;
بياد آوريد كه فقط يك دستور مي تواند مستقيما" بعداز ifيا elseا قرار گيرد.
اگر بخواهيد دستورات بيشتري داخل نماييد ، نيازي به ايجاد يك بلوك نداريد نظير اين قطعه كه در زير آمده است :
+ int bytesAvailable;
+ //...
+ if( bytesAvailable > 0 ){
+ ProcessData)(;
+ bytesAvailable- = n;
+ } else
+ waitForMoreData)(;
در اينجا ، هر دو دستور داخل بلوك if اجرا خواهند شد اگر bytes Available بزرگتر از صفر باشد .
برخي از برنامه نويسان راحت ترند تا هنگام استفاده از if ، از ابروهاي باز و بسته استفاده نمايند، حتي زمانيكه فقط يك دستور در هر جمله وجود داشته باشد.
اين امر سبب مي شود تا بعدا" بتوان براحتي دستور ديگري را اضافه نمود و نگراني از فراموش كردن ابروها نخواهيد داشت . در حقيقت ، فراموش كردن تعريف يك بلوك هنگامي كه نياز است ، يكي از دلايل رايج بروز خطاها مي باشد . بعنوان مثال قطعه زير از يك كد را در نظر بگيريد :
+ int bytesAvailable;
+ //...
+ if( bytesAvailable > 0 ){
+ ProcessData)(;
+ bytesAvailable- = n;
+ } else
+ waitForMoreData)(;
+ bytesAvailable = n;
بنظر خيلي روشن است كه دستور bytes Available=n طوري طراحي شده تا داخل جمله else اجرا گردد ، و اين بخاطر سطح طراحي آن است . اما حتما" بياد داريد كه فضاي خالي براي جاوا اهميتي ندارد و راهي وجود ندارد كه كامپايلر بفهمد چه مقصودي وجود دارد . اين كد بدون مشكل كامپايل خواهد شد ، اما هنگام اجرا بطور ناصحيح اجرا خواهد شد . مثال بعدي داخل كدي كه مشاهده مي كنيد تثبيت شده است :
+ int bytesAvailable;
+ //...
+ if( bytesAvailable > 0 ){
+ ProcessData)(;
+ bytesAvailable- = n;
+ } else {
+ waitForMoreData)(;
+ bytesAvailable = n;
+ }
if
يك nested if يك دستور if است كه هدف ifيا elseا ديگري باشد. if هاي تودرتو در برنامه نويسي بسيار رايج هستند. هنگاميكه if ها را تودرتو مي كنيد، مهمترين چيزي كه بايد بخاطر بسپاريد اين است كه يك دستورelse هميشه به نزديكترين دستور if خود كه داخل همان بلوك else است و قبلا" با يك else همراه نشده ، مراجعه
خواهد نمود . مثالي را مشاهده نماييد :
+ if(i == 10 ){
+ if(j < 20 )a = b;
+ if(k > 100 )c = d; // this if is
+ else a = c; // associated with this else
+ }
+ else a = d; // this else refers to if(i == 10)
همانگونه كه توضيحات نشان مي دهند ، else نهايي با (20 چون داخل همان بلوك قرار ندارد ( اگر چه نزديكترين if بدون else است ) . بجاي آن ، else نهايي با (i==10)if همراه مي شود . else داخلي به (100>k)if ارجاع
مي كند ، زيرا نزديكترين if در داخل همان بلوك است .
نردبان if-else-if
يك ساختار برنامه نويسي رايج براساس يك ترتيب از ifهاي تودرتو شده يا نردبان if-else-if است . اين ساختار بصورت زير است :
if(condition)
statement;
else if(condition)
statement;
else if(condition)
statement;
.
.
.
else
statement;
دستورات if از بالا به پايين اجرا مي شوند . ماداميكه يكي از شرايط كنترل كننده if صحيح باشد (true)، دستور همراه با آن if اجرا مي شود ، و بقيه نردبان رد خواهد شد . اگر هيچكدام از شرايط صحيح نباشند، آنگاه دستور else نهايي اجرا خواهد شد . else نهايي بعنوان شرط پيش فرض عمل مي كند ، يعني اگر كليه شرايط ديگر صحيح نباشند ، آنگاه آخرين دستور else انجام خواهد شد . اگر else نهايي وجود نداشته باشد و ساير شرايط ناصحيح باشند ، آنگاه هيچ عملي انجام نخواهد گرفت .
در زير ، برنامه اي را مشاهده مي كنيد كه از نردبان if-else-if استفاده كرده تا تعيين كند كه يك ماه مشخص در كدام فصل واقع شده است .
+ // Demonstrate if-else-if statement.
+ class IfElse {
+ public static void main(String args[] ){
+ int month = 4; // April
+ String season;
+
+ if(month == 12 || month == 1 || month == 2)
+ season = "Winter";
+ else if(month == 3 || month == 4 || month == 5)
+ season = "Spring";
+ else if(month == 6 || month == 7 || month == 8)
+ season = "Summer";
+ else if(month == 9 || month == 10 || month == 11)
+ season = "Autumn";
+ else
+ season = "Bogus Month";
+
+ System.out.println("April is in the" + season + ".");
+ }
+ }
خروجي اين برنامه بقرار زير مي باشد :
April is in the Spring.
ممكن است بخواهيد اين برنامه را تجربه نماييد . خواهيد ديد كه هيچ فرقي ندارد كه چه مقداري به month بدهيد ، يك و فقط يك دستور انتساب داخل نردبان اجرا خواهد شد .
دستور switch ، دستور انشعاب چند راهه در جاوا است . اين دستور راه ساده اي است براي تغيير مسير اجراي بخشهاي مختلف يك كد براساس مقدار يك عبارت . اين روش يك جايگزين مناسب تر براي مجموعه هاي بزرگتر از دستورات if-else-if است .
شكل كلي دستور switch بقرار زير مي باشد :
switch(expression){
case value1:
// statement sequence
break;
case value2:
// statement sequence
break;
.
.
.
case valueN:
// statement sequence
break;
default:
// default statement sequence
}
expression
مي تواند هر نوع ساده اي را برگرداند ، هر يك از مقادير (values) در دستورات case بايد از نوع سازگار با عبارت باشند . هر يك از مقادير case بايد يك مقدار لفظي منحصر بفرد باشد ( يعني بايد يك ثابت ، نه متغير ، باشد ).
دو برابر سازي مقادير case مجاز نيست .
دستور switch بشرح فوق عمل مي كند : مقدار عبارت با هر يك از مقادير لفظي در دستورات case مقايسه مي شوند. اگر تطابق پيدا شود ، كد سلسله اي تعقيب كننده آن دستور case اجرا خواهد شد . اگر هيچيك از ثابت ها با مقدار عبارت تطابق نيابند ، آنگاه دستور پيش فرض (default) اجرا خواهد شد ، اما دستور default اختياري است . اگر هيچيك از case ها تطابق نيابد و default وجود نداشته باشد آنگاه عمل اضافي ديگري انجام نخواهد شد .
از دستور break داخل دستور switch استفاده شده تا سلسله يك دستور را پايان دهد . هنگاميكه با يك دستور break مواجه مي شويم ، اجرا به خط اول برنامه كه بعد از كل دستور switch قرار گرفته ، منشعب خواهد شد . اين حالت تاثير پريدن switchاست .
در زير مثال ساده اي را مشاهده مي كنيد كه از دستور switch استفاده نموده
است :
+ // A simple example of the switch.
+ class SampleSwitch {
+ public static void main(String args[] ){
+ for(int i=0; i<6; i++)
+ switch(i ){
+ case 0:
+ System.out.println("i is zero.");
+ break;
+ case 1:
+ System.out.println("i is one.");
+ break;
+ case 2:
+ System.out.println("i is two.");
+ break;
+ case 3:
+ System.out.println("i is three.");
+ break;
+ default:
+ System.out.println("i is greater then 3.");
+ }
+ }
+ }
خروجي اين برنامه بقرار زير مي باشد :
i is zero.
i is one.
i is two.
i is three.
i is greater than 3.
i is greater than 3.
همانطوريكه مشاهده مي كنيد ، داخل حلقه ، دستوراتي كه همراه ثابت case بوده و با i مطابقت داشته باشند ، اجرا خواهند شد . ساير دستورات پشت سر گذاشته مي شوند (bypassed) . بعد از اينكه i بزرگتر از 3 بشود ، هيچ دستور همراه case مطابقت نداشته ، بنابراين دستور پيش فرض (default) اجرا خواهد شد .
دستور break اختياري است . اگر break را حذف كنيد ، اجراي برنامه با case بعدي ادامه خواهد يافت . گاهي بهتر است چندين case بدون دستورات break در بين آنها داشته باشيم . بعنوان مثال ، برنامه بعدي را در نظر بگيريد :
+ // In a switch/ break statements are optional.
+ class MissingBreak {
+ public static void main(String args[] ){
+ for(int i=0; i<12; i++)
+ switch(i ){
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ System.out.println("i is less than 5");
+ break;
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ System.out.println("i is less than 10");
+ break;
+ default:
+ System.out.println("i is 10 or more");
+ }
+ }
+ }
خروجي اين برنامه بقرار زير خواهد بود :
i is less than 5
i is less than 5
i is less than 5
i is less than 5
i is less than 5
i is less than 10
i is less than 10
i is less than 10
i is less than 10
i is less than 10
i is 10 or more
i is 10 or more
همانطوريكه مشاهده مي كنيد، اجرا طي هر case، بمحض رسيدن به يك دستور break ( يا انتهاي switch ) متوقف مي شود .
در حاليكه مثال قبلي براي توصيف نظر خاصي طراحي شده بود ، اما بهر حال حذف دستور break كاربردهاي عملي زيادي در برنامه هاي واقعي دارد . براي نشان دادن كاربردهاي واقعي تر اين موضوع ، دوباره نويسي برنامه نمونه مربوط به فصول سال را مشاهده نماييد . اين روايت جديد همان برنامه قبلي از switch استفاده مي كند
تا پياده سازي موثرتري را ارائه دهد .
+ // An improved version of the season program.
+ class Switch {
+ public static void main(String args[] ){
+ int month = 4;
+ String season;
+ switch( month ){
+ case 12:
+ case 1:
+ case 2:
+ season = "Winter";
+ break;
+ case 3:
+ case 4:
+ case 5:
+ season = "Spring";
+ break;
+ case 6:
+ case 7:
+ case 8:
+ season = "Summer";
+ break;
+ case 9:
+ case 10:
+ case 11:
+ season = "Autumn";
+ break;
+ default:
+ season = "Bogus Month";
+ }
+ System.out.println("April is in the" + season + ".");
+
+ }
+ }
تودرتو كردن دستورات switch
مي توانيد از يك switch بعنوان بخشي از ترتيب يك دستور switch خارجي تر استفاده نماييد. اين حالت را switch تودرتو مينامند. از آنجاييكه دستور switch تعريف كننده بلوك مربوط به خودش مي باشد، هيچ تلاقي بين ثابتهاي caseدر switch داخلي و آنهايي كه در switch خارجي قرار گرفته اند ، بوجود نخواهد آمد . بعنوان مثال ، قطعه بعدي كاملا" معتبر است .
+ switch(count ){
+ case 1:
+ switch(target ){ // nested switch
+ case 0:
+ System.out.println("target is zero");
+ break;
+ case 1 :// no conflicts with outer switch
+ System.out.println("target is one");
+ break;
+ }
+ break;
+ case 2 ://...
در اينجا دستور :case 1در switchر داخلي با دستور :case 1در switchر خارجي تلاقي نخواهد داشت . متغير count فقط با فهرست case ها در سطح خارجي مقايسه مي شود. اگر count برابر 1 باشد، آنگاه target با فهرست case هاي داخلي مقايسه خواهد شد .
بطور خلاصه ، سه جنبه مهم از دستور switch قابل توجه هستند :
ؤ switchبا ifا متفاوت است چون switch فقط آزمايش كيفيت انجام مي دهد ، در حاليكه if هر نوع عبارت بولي را ارزيابي مي كند . يعني كه switch فقط بدنبال يك تطابق بين مقدار عبارت و يكي از ثابت هاي case خودش مي گردد .
ؤ دو ثابت caseدر switchر مشابه نمي توانند مقادير يكسان داشته باشند .
البته ، يك دستور switch قرار گرفته داخل يك switch خارجي تر مي تواند ثابتهاي case مشترك داشته باشد .
ؤ يك دستور switch معمولا" بسيار كاراتر از يك مجموعه از if هاي تودرتو شده است . آخرين نكته بخصوص جالب توجه است زيرا روشنگر نحوه كار كامپايلر جاوا مي باشد . كامپايلر جاوا هنگاميكه يك دستور switch را كامپايل مي كند ، به هر يك از ثابتهاي case سركشي نموده و يك جدول jump table مي سازد كه براي انتخاب مسير اجرا براساس مقدار موجود در عبارت استفاده مي شود . بنابراين ، اگر بايد از ميان گروه بزرگي از مقادير انتخاب نماييد ، يك دستور switch نسبت به يك ترتيب از if-else ها كه بطور معادل و منطقي كد بندي شده باشد ، بسيار سريعتر
اجرا خواهد شد. كامپايلر قادر است اينكار را انجام دهد چون مي داند كه ثابتهاي case همه از يك نوع بوده و بايد خيلي ساده با عبارت switch براي كيفيت مقايسه شوند . كامپايلر چنين شناسايي را نسبت به يك فهرست طولاني از عبارات if ندارد .
دستورات تكرار iteration statements
دستورات تكرار در جاوا عبارتند از for، while،و do-whileو . اين دستورات آن چه را ما " حلقه " مي ناميم ، ايجاد مي كنند . احتمالا" مي دانيد كه حلقه يك مجموعه از دستورالعملها را بطور تكراري اجرا مي كند . تا اينكه يك شرط پاياني را ملاقات نمايد . همانطوريكه بعدا" خواهيد ديد، جاوا حلقه اي دارد كه براي كليه نيازهاي برنامه نويسي مناسب است .
حلقه while اساسي ترين دستور حلقه سازي (looping) در جاوا است . اين دستور ماداميكه عبارت كنترل كننده ، صحيح (true) باشد، يك دستور يا يك بلوك را تكرار مي كند . شكل كلي اين دستور بقرار زير است :
while(condition ){
// body of loop
}
شرط يا condition ممكن است هر عبارت بولي باشد . ماداميكه عبارت شرطي صحت داشته باشد ، بدنه حلقه اجرا خواهد شد . هنگاميكه شرط صحت نداشته باشد ، كنترل بلافاصله به خط بعدي كدي كه بلافاصله پس از حلقه جاري قرار دارد ، منتقل خواهد شد . اگر فقط يك دستور منفرد در حال تكرار باشد ، استفاده از ابروها غير ضروري است .
در اينجا يك حلقه while وجود دارد كه تا 10 را محاسبه كرده و دقيقا" ده خط "tick"را چاپ مي كند .
+ // Demonstrate the while loop.
+ class While {
+ public static void main(String args[] ){
+ int n = 10;
+
+ while(n > 0 ){
+ System.out.println("tick" + n);
+ n--;
+ }
+ }
+ }
هنگاميكه اين برنامه را اجرا مي كنيد، ده مرتبه "tick" را انجام خواهد داد:
tick 10
tick 9
tick 8
tick 7
tick 6
tick 5
tick 4
tick 3
tick 2
tick 1
از آنجاييكه حلقه while عبارت شرطي خود را در بالاي حلقه ارزيابي ميكند، اگر شرط ابتدايي ناصحيح باشد ، بدنه حلقه اجرا نخواهد شد . بعنوان مثال ، در قطعه زير ، فراخواني ()println هرگز اجرا نخواهد شد .
+ int a = 10/ b = 20;
+
+ while(a < b)
+ System.out.println("This will not be displayed");
بدنه while يا هر حلقه ديگر در جاوا ) ممكن است تهي باشد. زيرا دستور تهي دستوري كه فقط شامل ; باشد ) از نظر قواعد تركيبي در جاوا معتبراست . بعنوان مثال ، برنامه زير را در نظر بگيريد :
+ // The target of a loop can be empty.
+ class NoBody {
+ public static void main(String args[] ){
+ int i/ j;
+
+ i = 100;
+ j = 200;
+
+ // find midpoint between i and j
+ while(++i <-- j); // no body in this loop
+
+ System.out.println("Midpoint is" + i);
+ }
+ }
اين برنامه نقطه مياني (midpoint) بين iو jو را پيدا مي كند و خروجي زير را توليد خواهد كرد :
Midpoint is 150
در اينجا چگونگي كار حلقه while را مي بينيد . مقدار i افزايش و مقدار j كاهش مي يابد . سپس اين دو مقدار با يكديگر مقايسه مي شوند . اگر مقدار جديد i همچنان كمتر از مقدار جديد j باشد ، آنگاه حلقه تكرار خواهد شد . اگر i مساوي با يا بزرگتر از j بشود ، حلقه متوقف خواهد شد . تا هنگام خروج از حلقه ، i مقداري را مي گيرد كه بين مقادير اوليه iو jو مي باشد . ( بديهي است كه اين رويه هنگامي كار مي كند كه i كوچكتر از مقدار اوليه j باشد . ) همانطوريكه مي بينيد ، نيازي به بدنه حلقه نيست ، كليه عمليات داخل خود عبارت شرطي اتفاق
مي افتد . در كدهاي حرفه اي نوشته شده ديگر جاوا ، وقتي كه عبارت كنترل كننده توانايي مديريت كليه جزئيات خود را داشته باشد ، حلقه هاي كوتاه غالبا" بدون بدنه كد بندي مي شوند .