آموزش جاوا بخش سیزدهم

آموزش جاوا بخش سیزدهم

انباشتن سازندگان Overloading constructors 

علاوه بر انباشتن روشهای معمولی ، می توان روشهای سازنده را نیز انباشته نمود. در حقیقت برای اکثر کلاس هایی که در دنیای واقعی ایجاد مـی کنید ، سازندگان انباشته شده بجای استثنائ یک عادت هستند . در زیر آخرین روایت Box را مشاهده می کنید : 

 class Box {
 double width; 
 double height;
 double depth;
 // This is the constructor for Box.
 Box(double w/ double h/ double d ){
 width = w;
 height = h;
 depth = d;
 }
 // compute and return volume
 double volume (){
 return width * height * depth;
 }
 } 

همانطوری که می بینید ، سازنده Box ()نیازمند سه پارامتر است . یعنی کلیـه اعلانـات اشـیائ Box بایـد سـه آرگومـان بـه سـازنده Box () بگذرانند . بعنوان مثال دستور بعدی فعلا" نامعتبر است

;() Box new = ob Box : 

ازآنجایی که Box ()نیازمند سه آرگومان است ، فراخوانی آن بدون آرگومانها خطا است . این مورد، سوالات مهمی را پیش رو قرار می دهـد. اگر فقط یک box را بخواهید و اهمیتی نمی دهید و یا نمی دانید که ابعاد اولیه آن چه بوده اند ، چه پیش مـی آیـد ? همچنـین ممکـن اسـت بخواهید یک مکعب را با مشخص کردن یک مقدار که برای کلیه ابعاد آن استفاده می شوند، مقدار دهی اولیه نمایید ? آنگونه که قبلا" کلاس Boxنوشته شده ، این گزینه ها در دسترس شما نخواهد بود . خوشبختانه راه حل این مشکلات کاملا" ساده است : خیلـی سـاده تـابع سـازنده Box را انباشته کنید بگونه ای که شرایط توصیف شده را اداره نماید . برنامه بعدی شامل یک روایت توسـعه یافتـه Box اسـت کـه اینکـار را انجام می دهد : 

 /* Here/ Box defines three constructors to initialize
 the dimensions of a box various ways.
 */
 class Box {
 double width;
 double height;
 double depth;
 // constructor used when all dimensions specified
 Box(double w, double h, double d ){ 
 width = w;
 height = h;
 depth = d;
 }
 // constructor used when no dimensions specified
 Box (){
 width =- 1; // use- 1 to indicate
 height =- 1; // an uninitialized
 depth =- 1; // box
 }
 // constructor used when cube is created
 Box(double len ){
 width = height = deoth = len;
 }
 // compute and return volume
 double volume (){
 return width * height * depth;
 }
 }
 class OverloadDemo {
 public static void main(String args[] ){
 // create boxes using the various constructors
 Box mybox1 = new Box(10, 20, 15);
 Box mybox2 = new Box ();
 Box mycube = new Box(7);
 double vol;
 // get volume of first box
 vol = mybox1.volume ();
 System.out.println("Volume of mybox1 is " + vol);
 // get volume of second box
 vol = mybox2.volume (); 
 System.out.println("Volume of mybox2 is " + vol);
 // get volume of cube
 vol = mycube.volume ();
 System.out.println("Volume of mycube is " + vol);
 }
 } 

خروجی این برنامه بقرار زیرمی باشد :  

Volume of mybox1 is 3000
Volume of mybox2 is- 1
Volume of mycube is 343

لغو ( یا جلوگیری از پیشروی ) روش 

در یک سلسله مراتب کلاس ، وقتی یک روش در یک زیر کلاس همان نام و نوع یک روش موجود در کـلاس بـالای خـود را داشـته باشـد، آنگاه میگویند آن روش در زیر کلاس ، روش موجود در کلاس بالا را لغو نموده ) یا از پیشروی آن جلوگیری می نماید ) . وقتی یـک روش لغو شده از داخل یک زیر کلاس فراخوانی می شود ، همواره به روایتی از آن روش که توسط زیر کلاس تعریف شده ، ارجاع خواهد نمـود و روایتی که کلاس بالا از همان روش تعریف نموده ، پنهان خواهد شد . مورد زیر را در نظربگیرید : 

// Method overriding.
 class A {
 int i, j;
 A(int a, int b ){
 i = a;
 j = b;
 }
 // display i and j
 void show (){
 System.out.println("i and j :" + i + " " + j);
 }
 }
 class B extends A {
 int k;
 B(int a, int b, int c ){
 super(a, b);
 k = c;
 }
 // display k -- this overrides show ()in A
 void show (){
 System.out.println("k :" + k);
 } 
 }
 class Override {
 public static void main(String args[] ){
 B subOb = new B(1, 2, 3);
 subOb.show (); // this calls show ()in B
 }
 } 

حاصل تولید شده توسط این برنامه بقرار زیرمی باشد :  

k:3 

وقتی show ()روی یک شی ئ از نوع B فراخوانی می شود ، روایتی از show که داخلB تعریف شده مورد استفاده قرار میگیرد. یعنـی که ، روایت show ()داخلB ، روایت اعلان شده در A را لغو می کند . اگرمی خواهیـد بـه روایـت کـلاس بـالای یـک تـابع لغـو شـده دسترسی داشته باشید ، این کار را با استفاده از super انجام دهید . بعنوان مثال ، در این روایت از B روایت کلاس بالای show ()داخـل روایت مربوط به زیر کلاس فراخوانی خواهد شد . این امربه کلیه متغیرهای نمونه اجازه می دهد تا بنمایش درآیند .  

 class B extends A {
 int k;
 B(int a, int b, int c ){
 super(a, b);
 k = c;
 }
 void show (){
 super.show (); // this calls A's show ()
 System.out.println("k :" + k);
 }
 }

اگر این روایت از A را در برنامه قبلی جایگزین نمایید، خروجی زیر را مشاهده می کنید :  

i and j :1 2
k:3 

در اینجا ، super.show()روایت کلاس بالای show ()را فراخوانی می کند . لغو روش فقط زمانی اتفاق می افتد که اسامی و نوع دو روش یکسان باشند . اگر چنین نباشد ، آنگاه دو روش خیلی ساده انباشته (overioaded (خواهند شد . بعنوان مثال ، این روایت اصلاح شده مثال قبلی را در نظر بگیرید : 

 // Methods with differing type signatures are overloaded -- not
 // overridden.
 class A {
 int i, j;
 A(int a, int b ){
 i = a;
 j = b;
 }
 // display i and j
 void show (){
 System.out.println("i and j :" + i + " " + j);
 }
 }
 // Create a subclass by extending class A.
 class B extends A {
 int k;
 B(int a, int b, int c ){
 super(a, b);
 k = c;
 }
 // overload show ()
 void show(String msg ){
 System.out.println(msg + k);
 }
 }
 class Override {
 public static void main(String args[] ){
 B subOb = new B(1, 2, 3); 
 subOb.show("This is k :"); // this calls show ()in B
 subOb.show (); // this calls show ()in A
 }
 } 

حاصل تولید شده توسط این برنامه بقرار زیرمی باشد :  

This is k:3
i and j :1 2 

روایتshow ()درB ر یک پارامتر رشته ای می گیرد . این عمل سبب متفاوت شدن تاییدیه نوع آن از نـوع موجـود در A شـده ، کـه هـیچ پارامتری را نمی گیرد. بنابراین نباشتگی ( یا مخفی شدن اسم ) اتفاق نمی افتد . 

روشهای Native  

اگر چه بندرت اتفاق می افتد، اما گهگاه شرایطی پیش می آید که مـی خواهیـد یـک زیـر روال (subroutine (نوشـته شـده توسـط سـایر زبان های غیر از جاوا را فراخوانی نمایید . معمولا" ، چنین زیر روالی بصورت کد قابل اجرا برای cpu و محیط خاصی که در آن کار می کنید عرضه می شود یعنی بصورت کد بومی . (Native (بعنوان مثال ممکن است بخواهید یک زیر روال کد بومی را فراخوانی کنید تـا بـه زمـان اجرای سریعتری برسید. یا ممکن است بخواهید از یک کتابخانه تخصصی شده متفرقه نظیر یک بسته آماری اسـتفاده نماییـد . امـا از آنجاییکـه برنامه های جاوا به کد بایتی کامپایل می شوند و سپس توسط سیستم حین اجرای جاوا تفسیر خواهند شد ، بنابراین بنظرمی رسد فراخوانی یک زیر روال کد بومی از داخل برنامه جاوا غیر ممکن باشد .خوشبختانه ، این نتیجه گیری غلط است . جاواواژه کلیدی native را تدارک دیده که برای اعلان روشهای کدهای بومی استفاده می شود. هربار که این روشها اعلان شوند می توانید آنها را از درون برنامه جاوای خود فراخوانی نمایید . درست مثل فراخوانی هر روش دیگری در جاوا . برای اعلان یک روش بومی ، اصلاحگر nativeرا قبـل از روش قـرار دهیـد. امـا بدنه ای برای روش تعریف نکنید ، بعنوان مثال : 

 public native int meth (); 

هر بار که یک روش بومی را اعلان نمودید ، مجبورید روش بومی نوشته و یکسری مراحل پیچیده تر را تعقیب کنیـد تـا آن را بـه کـد جـاوای خود پیوند دهید . نکته : مراحل دقیقی که لازم است طی نمایید ممکن است برای محیط ها و روایتهای گوناگون جاوا متفاوت باشند . همچنین زبانی که برای پیاده سازی روش بومی اسـتفاده مـی کنیـد ، مـوثر خواهـد بـود . بحـث بعـدی از JDK) ، 02.1 version(و ابزارهـای آن استفاده میکند. این یک محیط windows 95/NT را فرض میکند .زبانی که برای پیـاده سـازی روش بـومی اسـتفاده شـده ، زبـان C مـی باشد . آسانترین شیوه برای درک این پردازش ، انجام یک کار عملی است . برای شـروع برنامـه کوتـاه زیـر را کـه از یـک روش native موسوم به test ()استفاده می کند وارد نمایید :  

 // A simple example that uses a native method.
 public class NativeDemo {
 int i;
 int j;
 public static void main(String args[] ){
 NativeDemo ob = new NativeDemo ();
 ob.i = 10;
 ob.j = ob.test (); // call a native method
 System.out.println("This is ob.j :" + ob.j);
 } 
 // declare native method
 public native int test ();
 // load DLL that contains static method
 static {
 System.loadLibrary("NativeDemo");
 }
 } 

دقت کنید که روش test ()بعنوان native اعلان شده و بدنه ای نـدارد . ایـن روشـی اسـت کـه مـا در C باختصـار پیـاده سـازی میکنـیم . همچنین به بلوک static دقت نمایید .همانطوریکه قبلا" گفتیم : یک بلوک static فقط یکبار اجرا می شود ، و آنهـم زمـانی اسـت کـه برنامه شما شروع باجرا می نماید ( یا دقیق تر بگویم ، وقتی که کلاس آن برای اولین بار بارگذاری می شـود ) . در ایـن حالـت ، از ایـن بلـوک استفاده شده تا" کتابخانه پیوند پویا ()dynamic  Link Library) "را کـه دربرگیرنـده پیـاده سـازی بـومی test ()اسـت ، بارگـذاری نماید . کتابخانه فوق توسط روش LoadLibrary ()بارگذاری مسشود که بخشی از کلاس System است . شکل عمومی آن بقرار زیـر است : 

Static void LoadLibrary( string filename) 

در اینجا ، filename رشته ای است که نام فایلی که کتابخانه را نگهداری میکند توصیف می کند . بـرای محـیط وینـدوز 95/NT فـرض شده که این فایل پسوند DLL ، داشته باشد . بعد از اینکه برنامه را وارد کردید، آن را کامپایل کنید تا NativeDemo.class تولید شـود. سپس ، باید ازjava.exe استفاده نموده تا دو فایل تولید نمایید: 

کـه NativeDemo.c و1.NativeDemo و اسـت . شـما NativeDemo.h را در پیـاده سـازی test ()مـی گنجانیـد . فایـل NativeDemo.c یک فایل stub است که دربرگیرنده میزان کوچکی از کد است که رابط بین روش بومی شما و سیسـتم حـین اجـرای جاوا را تدارک می بیند .برای تولید h.NativeDemo ، از دستور بعدی استفاده نمایید javahNativeDemo : این دستور یـک فایـل سرآیند موسوم به NativeDemo.h را ایجاد می کند. این فایل باید در فایـل C کـه test ()را پیـاده سـازی مـی کنـد ، گنجانـده شـود. حاصل تولید شده توسط این فرمان بقرار زیرمی باشد : 

/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class NativeDemo */
#ifndef_ Included_NativeDemo
#define_ Included_NativeDemo 
typedef struct ClassNativeDemo {
long j;
long j;
} ClassNativeDemo;
HandleTo(NativeDemo);
#ifdef __ cplusplus
extern "C" {
#endif
extern long NativeDemo_test(struct HNativeDemo *);
#ifdef __ cplusplus
}
#endif
#endif

به چند چیز مهم درباره این فایل دقت نمایید. اول اینکه ساختار clssNativeDemoدو عضو را دربرمی گیردi: وj و. این ، متغیرهای نمونـه تعریـف شـده توســط NativeDemo در فایــل جـاوا را معــین مـی کنـد . دوم اینکــه ، مـاکرو HandleTo ، ()نـوع ســاختار HNativeDemoرا ایجاد می کند ، که برای ارجاع به شیئی که test ()را فراخوانی می کند مورد استفاده قرار می گیرد . به خط بعدی ، توجه ویژه ای مبذل دارید، که الگویی برای تابع test ()که ایجاد کرده اید را تعریف می کند : 

 extern long NativeDemo_test(struct HNativeDemo *); 

دقت نمایید که نام تابع ، NativeDemo-test است . باید این را بعنوان نام تابع بومی که پیاده سازی می کنید ، بکار برید . یعنی که بجـای ایجاد یک تابع C موسوم به test ، ()یک تابع موسوم به NativeDemo-test  ()را ایجاد می کنید . پیشوند NativeDemo اضافه شده است چون مشخص می کند که روش test ()بخشی از کلاسNativeDemo می باشد . بیـاد آوریـد ، کـلاس دیگـری ممکـن اسـت روش test ()بومی خود را تعریف نماید که کاملا" با آنکه توسط NativeDemo اعلان شده ، متفاوت است . پیشوند گذاری نام روش بومی با نام کلاس فراهم کننده ، شیوه ای است برای تمایز بین روایتهای مختلف . بعنوان یک قانون عمومی ، توابع بومی ، یک نـام کـلاس کـه در آن اعلان شده اند را بعنوان یک پیشوند قبول می کنند . برگشت NativeDemo-test  ()از نوع long است ، اگرچـه داخـل برنامـه جـــاوا کـــه آن را فراخـــوانی مـــی کنـــد بعنـــوان یـــک int توصـــیف شـــده اســـت . دلیـــل ایـــن امـــر بســـیار ســـاده است . در جاوا ، اعداد صحیح مقادیر 32 بیتی هستند . در C ، یک عدد صحیح long لازم است حداقل 32 بیت برای یک نوع عدد صحیح تضــمین نمایــد . یــک چیــز دیگــر دربــاره الگــوی NativeDemo-test  ()شــایان توجــه اســت . ایــن الگــو یــک پارامتر از نوع *HNativeDemo تعریف می کند. کلیه روشهای بومی حداقل یک پارامتر دارند که اشاره گری اسـت بـه شـیئی کـه روش بومی را فراخوانی نموده است . این پارامتر ضرورتا" شبیه This است . می توانید از این پارامتر استفاده کرده تا دسترسی بـه متغیرهـای نمونـه شیئی که روش را فراخوانی می کند ، داشته باشید

 .برای تولید  NativeDemoC فایل  stub ، از این دستور استفاده کنید stubs-java NativeDemo  :  

فایل تولید شده توسط این دستور بصورت زیرمی باشد :  

/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Stubs for class NativeDemo */
/* SYMBOL :"NativeDemo/test ()I"/ Java_Native_test_stub */
__ declspec(dllexport )stack_item
*Java_NativeDemo_test_stub(stack_item *_P_/struct execenv *_ EE )_{
extern long NativeDemo_test(vide *);
_ P_[0].i = NativeDemo_test_(P_[0].p);
return_ P _+ 1;
} 

شما این فایل را کامپایل نموده و با فایل پیاده سازی خود پیوند می دهید . پس از تولید فایلهای سرآیند و stub ضـروری ، مـی توانیـد پیـاده سازی خود از test ()را بنویسید . از روایتی که در زیرنشان داده ایم ، استفاده نمایید : 

 /* This file contains the C version of the
 test ()method.
 */
 #include
 #include "NativeDemo.h"
 #include
 long NativeDemo_test(struct HNativeDemo *this)
 {
 printf("This is inside the native method.\n");
 printf("this->i :%ld\n"/ unhand(this.-)i);
 return( unhand(this-)>i);
 } 

دقت نمایید که این فایل دربرگیرنده stubpreamble.h است که شامل اطلاعات رابط سازی (interfacing) است . این فایل توسـط کامپایلر جاوا برای شما فراهم می شود . فایل سرآیند NativeDemo قبلا" توسط java ایجاد شده بود . پس از ایجـاد c.test بایـد آن را و NativeDemo.c را کامپایل نمایید . بعد این دو فایل را بعنوان یک ( DLL کتابخانه پیوند پویـا ) بـا یکـدیگر پیونـد دهیـد . بـرای انجام اینکار با کامپایلر میکروسافت C++/C ، از خط فرمان بعدی استفاده نمایید CL/LD NativeDemo.ctest.c  : این فرمـان یـک فایل تحت عنوان NativeDemo.dll تولید می کند . هر بار که این کار انجام شود ، مـی توانیـد برنامـه جـاوا را اجـرا کنیـد. انجـام اینکـار خروجی بعدی را تولید می کند :

This is inside the native method.
this->i :10
This is ob.j :20 

داخل c.test به استفاده از unhand ()توجه نمایید . این ماکرو توسط جاوا تعریف شـده و یـک اشـاره گـر را بـه نمونـه سـاختار class NativeDemoکه در NativeDemo.h تعریف شده و همراهی شیئی است که test ()را فراخوانی می کند ، برمی گردانـد . در کـل هرگاه نیاز به دسترسی به اعضائ یک کلاس جاوا داشته باشید ، از unhand ()استفاده می کنید . 

یادآوری : توضیحاتی که استفاده از native را محصور کرده اند، بستگی به پیاده سازی و محیط دارند . علاوه بر این ، رفتار مشخصی کـه در آن به کد جاوا رابط می سازید قابل تغییر است . باید مستندات موجود در سیسـتم توسـعه جـاوا خـود را مطالعـه نماییـد تـا جزئیـات مربـوط بـه روشهای بومی را درک کنید

. مشکلات مربوط به روشهای بومی 

روشهای بومی بنظرمی رسد تعهد بزرگی را پیشنهاد می کنند زیرا آنها به شما اجازه می دهند تا دسترسی به پایه موجـود روالهـای کتابخانـه ای اتان را بدست آورده و امکان اجرای حین اجرای سریعتر را به شما عرضه می کنند . اما آنها همچنین دو مشکل اصلی را نشان می دهنـد: فـرار امنیت بالقوه و کاهش قابلیت حمل . اجازه دهید باختصار این دو مورد را بررسی نماییم . 

یک روش بومی ، یک ریسک امنیتی را مطرح می کند . از آنجاییکه یک روش بومی کد واقعی ماشین را اجرا می کند ، می تواند به هر بخش از رایانه میزبان دسترسی داشته باشد. یعنی که ، کد بومی منحصربه محیط اجرایی جاوا نیست . این کد امکان حمله ویروسی را هـم مـی دهـد. بهمین دلیل ریز برنامه ها نمی توانند از روشهای بومی استفاده نمایند. همچنین ، بارگذاری DLL ها می تواند محدود شـود و بارگـذاری آنهـا منوط به تصدیق مدیر امنیتی باشد . 

دومین مشکل ، قابلیت حمل است . چون کد بومی داخل یک DLL گنجانده شده ، باید روی ماشینی که در حال اجرای برنامـه جـاوا اسـت ، حاضر باشد. بعلاوه ، چون هر روش بومی بستگی به cpu و سیستم عامل دارد، هر DLL بطور وراثتی غیر قابل حمل میشـود .بـدین ترتیـب ، یک برنامه جاوا که از روشهای بومی استفاده می کند، فقط قادر است وی یک ماشین که برای آن یک DLL سازگار نصب شده باشـد ، اجـرا شود. 

 

 

امیدواریم از این مقاله بهره کافی را برده باشید وبرای شما مفید بوده باشد.

سؤالات خود در رابطه با این مقاله و همچنین انتقادات و پیشنهادات خود را در قسمت نظرات با ما در میان بگذارید


این مقاله فایلی برای دانلود ندارد
برای ارسال نظر نیاز است وارد سایت شوید. در صورت نداشتن حساب کاربری عضو شوید.