در این قسمت با نوع داده String آشنا خواهید شد که بسیار پرکاربرد است. همچنین در انتها با تفاوت نوع دادههای اصلی و ارجاعی که مفهومی بسیار مهم است کاملا آشنا خواهید شد.
رشتهها (Strings)
یکی از مهمترین نوع دادههای جاوا رشتهها هستند که جزو نوع دادههای ارجاعی (Reference Data Type) به شمار میروند. در برنامههایی که تاکنون نوشتیم بارها از رشتهها استفاده کردیم. مثلا در کد زیر:
System.out.println("Hello World!");
عبارتی که بین دو “ ” نوشته شده یک رشته است. رشتهها میتوانند شامل حروف الفبا، اعداد و نمادهای دیگر مثل * یا ! و … باشند. رشتهها در برنامهنویسی کاربرد بسیار زیادی دارند و به وفور استفاده میشوند.
ایجاد یک رشته
همانطور که گفتیم رشتهها جزو نوع دادههای ارجاعی هستند. بر خلاف نوع دادههای اصلی که ساختاری ساده داشتند (مثل int و double)، نوع دادههای ارجاعی از کلاس تشکیل میشوند.
برای ایجاد متغیری که بتواند یک رشته را در خود نگهداری کند باید شیئی از کلاس String ایجاد کنیم و به سازنده آن مقدار مورد نظر خود را بدهیم:
String myString = new String("I Love Java"); System.out.println(myString);
در خط دوم کد مقدار متغیر myString را چاپ کردیم. عبارات رشتهای باید بین دو ” ” قرار بگیرند. همانطور که در سازنده کلاس String هم این کار را کردیم.
به دلیل اینکه از کلاس String در برنامهها زیاد استفاده میشود، جاوا برای استفاده این کلاس راه سادهتری فراهم کرده است که در آن هر عبارتی که بین دو ” ” قرار بگیرد یک شی از جنس String است:
String myString = "I Love Java"; System.out.println(myString);
همانطور که دیدید همان کد قبل را این بار به شکل خلاصهتر نوشتیم. برنامهنویسان جاوا از این روش استفاده میکنند.
الحاق رشتهها (String Concatenation)
گاهی اوقات نیاز داریم تا دو یا چند رشته را به هم متصل کنیم. برای این کار میتوانیم از عملگر + استفاده کنیم.
مثال
String name = "Mostafa"; System.out.println("My name is " + name);
در این برنامه دو عبارت رشتهای که یکی از آنها در متغیر name ذخیره شده بود را با استفاده از عملگر + به هم متصل کردیم و آن را به متد println دادیم تا در کنسول چاپ کند.
تغییرناپذیری (Immutability)
تغییرناپذیری یعنی وضعیت یک شی را بعد از ایجاد آن نتوان تغییر داد. در جاوا کلاس String تغییرناپذیر است و محتوای آن نمیتواند تغییر کند. به کد زیر دقت کنید:
String country = "Iran"; country = "My country is " + country;
ابتدا متغیری از جنس String تعریف کردیم و مقداری به آن دادیم. در خط بعد به مقدار قبلی آن یک عبارت جدید اضافه کردیم و مقدار جدید را در همان متغیر قرار دادیم. به نظر میآید که ما محتوای متغیر country را تغییر دادیم اما در اصل یک شی جدید ساختیم و آن را به متغیر country نسبت دادیم.
در کدی که نوشتیم فرآیند زیر رخ میدهد:
String country = new String("Iran"); country = new String("My country is" + country);
در خط دوم شی قبلی در حافظه رها شده و شی جدیدی ساخته میشود و آدرس آن درون متغیر country قرار میگیرد.
تغییرناپذیری رشتهها در اکثر موارد مشکلی ایجاد نمیکند اما اگر بخواهیم مثلا در یک حلقه ۲۰۰ بار محتویات یک متغیر رشتهای را تغییر دهیم آنگاه تعداد زیادی شی ایجاد شده و در حافظه رها میشود و این مسئله میتواند حافظه زیادی اشغال کند که در این موارد از کلاسهای StringBuilder و StringBuffer استفاده میشود.
متدهای پرکاربرد کلاس String
متدهای پرکاربرد کلاس String که میتوانیم از آنها استفاده کنیم به شرح زیر است:
length()
با این متد میتوان طول یک رشته (تعداد کاراکترهای آن) را به دست آورید.
مثال:
String test = "Hello!"; System.out.println(test.length());
equals(String value)
اگر بخواهیم مساویبودن دو رشته را بررسی کنیم به جای عملگر == باید از این متد استفاده کنیم.
مثال:
String name = "ali"; String name2 = "Ali"; System.out.println(name.equals(name2));
دو رشته name و name2 با هم مساوی نیستند (به دلیل بزرگی و کوچکی حرف a) و در نتیجه متد println عبارت false را چاپ میکند.
اگر بخواهیم دو رشته را بدون در نظر گرفتن بزرگی و کوچکی حروف مقایسه کنیم باید از متد equalsIgnoreCase استفاده کنیم.
toUpperCase() و toLowerCase()
برای تبدیل تمام حروف یک رشته به حروف بزرگ یا کوچک از این دو متد استفاده میکنیم.
مثال:
String name = "ali"; String name2 = "ALI"; System.out.println(name.toUpperCase()); System.out.println(name2.toLowerCase());
trim()
این متد فضاهای خالی در ابتدا و انتهای رشته را حذف میکند و مقداری جدید بدون فضاهای خالی برمیگرداند.
مثال:
String name = " Arash "; name = name.trim();
indexOf(String str)
هر کاراکتر در یک عبارت رشته ای یک اندیس دارد مانند آرایهها. اندیس کاراکترها از ۰ شروع میشود. در شکل زیر اندیسهای یک عبارت رشتهای را میبینید:
با متد indexOf میتوان اندیس محل شروع یک رشته را در درون رشته دیگر را به دست آورد.
مثال:
String t = "Hello"; System.out.println(t.indexOf("el"));
همانطور که میبینید اندیس شروع عبارت el در رشته Hello برابر با ۱ است پس متد indexOf مقدار ۱ را برمیگرداند. اگر رشتهای که به این متد میدهیم در رشته اصلی وجود نداشته باشد این متد مقدار -۱ را برمیگرداند.
substring(int beginIndex, int endIndex)
با این متد میتوان قسمتی از یک رشته را به دست آورد. این متد دو پارامتر دارد که اولی اندیس شروع و دومی اندیس پایان است و متد هر کاراکتری که بین این دو اندیس باشد را برمیگرداند.
مثال:
String t = "Hello"; System.out.println(t.substring(1, 3));
در این کد اندیس ۱ متعلق به کاراکتر e است و اندیس ۳ متعلق به کاراکتر l (دومین l). متد substring در این برنامه عبارت el را برمیگرداند نه ell چون پارامتر دوم (endIndex) جزو رشته خروجی حساب نمیشود و فقط کاراکترهای ۱ تا ۲ چاپ میشوند.
تفاوت نوع دادههای اصلی و ارجاعی
با نوع دادههای اصلی آشنا هستید. یک تفاوت عمده بین نوع دادههای اصلی (Primitive) و ارجاعی (Reference) وجود دارد:
متغیرهای از جنس نوع دادههای اصلی یک مقدار را درون خود نگهداری میکنند. مثلا:
int num = 13;
عدد ۱۳ مستقیما در این متغیر ذخیره میشود. یعنی محلی از حافظه به این متغیر اختصاص داده شده و عدد ۱۳ در آن محل قرار میگیرد. همچنین اگر بخواهید بعد از تعریف متغیر مقدار آن را تغییر دهید میتوانید به آسانی این کار را انجام دهید. مثلا:
num = 74;
اما در نوع دادههای ارجاعی روند کار کمی متفاوت است. هر کدام از کلاسهای کتابخانه جاوا و هر کلاسی که شما به عنوان برنامهنویس ایجاد میکنید یک نوع داده ارجاعی به شمار میرود. مثلا اگر کلاسی به نام Car ایجاد کرده باشید با کد زیر:
Car myCar;
یک متغیر از جنس Car ایجاد کردید. حال همانطور که میدانید اگر بخواهید شیئی از کلاس Car بسازید باید به روش زیر عمل کنید:
Car myCar = new Car();
با این کار شیئی از کلاس Car در محلی از حافظه رم به نام Heap ایجاد شده و آدرس آن شی درون متغیر myCar قرار میگیرد.
برنامههای جاوا در حافظه رم دو قسمت اصلی به نام Stack و Heap ایجاد میکنند. یکی از مواردی که در Stack قرار میگیرند متغیرهایی که از جنس نوع دادههای اصلی تعریف میشوند هستند اما اشیائی که در برنامه ایجاد میشوند در Heap قرار میگیرند.
به شکل زیر دقت کنید:
شکلی فرضی از حافظه رم را مشاهده میکنید. در قسمت Stack همانطور که میبینید یک متغیر به نام num از جنس int تعریف شده و مقدار ۱۳ درون آن قرار گرفته است چون همانطور که گفتیم متغیرهایی که از جنس نوع دادههای اصلی تعریف میشوند در Stack قرار میگیرند.
در قسمت Heap یک شی از جنس Car داریم. عبارتی که داخل دایره نوشته شده است آدرس شی در Heap است. البته این یک مثال است و هر شی در Heap آدرس مخصوص به خود را دارد.
متغیر myCar که در Stack تعریف شده حاوی آدرس این شی است. در واقع این متغیر ارجاعی به شی موجود در Heap میباشد.
پس متغیرهایی که از جنس نوع دادههای ارجاعی تعریف میشوند فقط آدرس شی را در خود نگهداری میکنند نه خود شی.
حال به کد زیر دقت کنید:
Car myCar = new Car(); myCar.name = "Ford"; Car mySecondCar = myCar; System.out.println(mySecondCar.name);
ابتدا برای کلاس Car یک مشخصه به نام name تعریف کردیم. در این کد شیئی از جنس Car ساختیم و مقدار مشخصه name آن را برابر با “Ford” قرار دادیم.
به کد Car mySecondCar = myCar دقت کنید. در این کد یک متغیر جدید از جنس Car به نام mySecondCar ایجاد شده و آدرس شیئی که متغیر myCar به آن ارجاع دارد درون متغیر mySecondCar قرار گرفته است.
بنابراین حالا هر دو متغیر به یک شی ارجاع دارند به همین دلیل در خط بعد برنامه عبارت Ford چاپ میشود با اینکه از طریق متغیر mySecondCar به آن دسترسی پیدا کردیم.
میگم وقتی مثلا اینجا:
Car myCar = new Car();
myCar.name = “Ford”;
داریم یه کلاس میسازیم و new میکنیم، مثل زبونهای دیگه نیازی نیست مگه نوع ارجاعی بودن متغیر رو با * قبل متعیر نشون بدیم؟؟!! اصلا نداریم یعنی تو جاوا این * رو؟
مرسی.
نه ببینید اینجا بحث متغیر name نیست. این مثالی که من زدم برای نشون دادن ارجاع دو متغیر به یک آبجکت بود. حالا اگه شما هرچقدر فیلد دیگه هم توی کلاس Car داشته باشی (در مثال آخر) همین قضیه براشون صادقه. چون دو متغیر به یه شی ارجاع دارند.
آره خب، متوجهام.
ولی شاید نتونستم منظورم رو درست بگم آخه تو C++/C وقتی میخواهیم یه متغیر از نوع ارجاعی باشه باید خودمون قبل از اسم متغیر از علامت * استفاده کنیم تا کامپایلر اون رو از نوع ارجاعی درنظر بگیره که باتوجه به توضیحات شما فکر میکنم دیگه تو جاوا اینطور نیست و خودش انگار همه کلاسها یا رشتهها رو از نوع ارجاعی میگیره.(؟)
ممنون.
درسته در جاوا نیازی به این کار نیست و چیزی به نام Pointer وجود نداره.
همونطور که گفتم هر کلاس یک نوع داده ارجاعی به حساب میاد و کلاس String هم از این قاعده مستثنی نیست.
موفق باشید
مختصر ومفید وکامل. مرسی.
عالی عالی عالی
انواع ارجاعی رو بسیار خوب توضیح دادید ممنون