17. مثال هایی از استفاده موفقیت آمیز و ناموفق از اختیاری را بیان کنید
فرض کنید یک سری مقادیر مشخصی داریم که از طریق آنها جریان را طی می کنیم و در نهایت مقداری اختیاری دریافت می کنیم :Optional<String> stringOptional = Stream.of("a", "ab", "abc", "abcd")
.filter(str -> str.length() >= 3)
.findAny();
همانطور که انتظار می رود، باید مقدار را از این اختیاری دریافت کنیم . فقط استفاده از get() راه بدی است:
String result = stringOptional.get();
اما این روش قرار است مقدار را از Optional بگیرد و به ما برگرداند؟ این البته درست است، اما اگر معنی داشته باشد. خوب، اگر مقادیر موجود در استریم متفاوت بود و در نهایت یک Optional خالی دریافت میکردیم ، وقتی میخواهیم با استفاده از متد get() مقداری از آن بگیریم، موارد زیر پرتاب میشود: که خوب نیست. در این مورد، بهتر است از ساختارهای زیر استفاده کنید:
-
String result = null; if (stringOptional.isPresent()) { stringOptional.get(); }
در این مورد، ما در حال بررسی هستیم که ببینیم آیا عنصر در اختیاری است یا خیر . اگر نه، رشته حاصل مقدار قدیمی خود را دارد.
-
String result = stringOptional.orElse("default value");
در این حالت مقداری از مقدار پیشفرض را مشخص میکنیم که در صورت خالی بودن Optional به رشته حاصل داده میشود .
-
String result = stringOptional.orElseThrow(() -> new CustomException());
در این مورد، زمانی که Optional خالی است ، ما خودمان یک استثنا می اندازیم .
18. آیا می توان یک روش اصلی را نهایی اعلام کرد؟
بله، البته، هیچ چیز ما را از اعلام نهایی متد main() باز نمی دارد . کامپایلر خطا ایجاد نمی کند. اما شایان ذکر است که هر روشی پس از اعلام نهایی به آخرین روش تبدیل می شود - نه لغو. اگر چه، چه کسی اصلی را دوباره تعریف می کند ؟19. آیا امکان واردات یک بسته/کلاس دو بار وجود دارد؟ عواقب آن چه می تواند باشد؟
بله، تو میتونی. عواقب؟ ما چند واردات غیر ضروری خواهیم داشت که Intelijj IDEA آنها را به رنگ خاکستری نشان می دهد، یعنی. استفاده نشده20. ریخته گری چیست؟ چه زمانی می توانیم یک ClassCastException دریافت کنیم؟
ریخته گری یا ریخته گری نوع ، فرآیند تبدیل یک نوع داده به نوع داده دیگر است: به صورت دستی (ریخته گری ضمنی) یا خودکار (ریخته گری نوع صریح). تبدیل خودکار توسط کامپایلر و تبدیل دستی توسط توسعه دهنده انجام می شود. نوع ریخته گری برای کلاس های اولیه و کلاس ها تا حدودی متفاوت است، بنابراین ما آنها را جداگانه در نظر خواهیم گرفت. انواع اولیه نمونه ای از ریخته گری اتوماتیک انواع اولیه:int value = 17;
double convertedValue = value;
همانطور که می بینید، هیچ دستکاری اضافی به جز علامت = در اینجا لازم نیست. نمونه ای از ریخته گری دستی انواع اولیه:
double value = 17.89;
int convertedValue = (int)value;
در این مورد، میتوانیم یک ریختهگری دستی را مشاهده کنیم که با استفاده از (int) پیادهسازی میشود ، که به موجب آن قسمت بعد از کاما کنار گذاشته میشود و مقدار convertedValue دارای مقدار - 17 خواهد بود. درباره ریختهگری انواع اولیه در این مقاله بیشتر بخوانید . خب حالا بیایید به سراغ اشیا برویم. انواع مرجع برای انواع مرجع، ریختهگری خودکار برای کلاسهای نسل به کلاسهای والد امکانپذیر است. به این چند شکلی نیز می گویند . فرض کنید یک کلاس Lion داریم که از کلاس Cat به ارث میبرد . در این حالت، تبدیل خودکار به صورت زیر خواهد بود:
Cat cat = new Lion();
اما با یک بازیگر صریح ، همه چیز تا حدودی پیچیده تر است، زیرا هیچ عملکردی برای بریدن بیش از حد، مانند موارد اولیه وجود ندارد. و فقط تبدیل صریح فرم را انجام دهید:
Lion lion= (Lion)new Cat();
با یک خطا مواجه می شوید: در واقع می توانید متدهایی را به کلاس نسل Lion اضافه کنید که در ابتدا در کلاس Cat نبودند و سپس سعی کنید آنها را فراخوانی کنید، زیرا نوع شی شما Lion می شود . خب هیچ منطقی در این کار وجود ندارد. بنابراین، باریک کردن نوع تنها زمانی امکان پذیر است که شی اصلی از نوع Lion باشد اما بعداً به یک کلاس والد فرستاده شده است:
Lion lion = new Lion();
Cat cat = lion;
Lion newLion = (Lion)cat;
همچنین، برای اطمینان بیشتر، یک قالب باریک برای اشیا با استفاده از ساختار instanceOf توصیه میشود :
if (cat instanceof Lion) {
newLion = (Lion)new Cat();
}
در این مقاله در مورد بازیگران نوع مرجع بیشتر بخوانید .
21. چرا فریمورک های مدرن عمدتاً فقط از استثناهای بررسی نشده استفاده می کنند؟
من فکر میکنم همه اینها به این دلیل است که کنترل استثناهای بررسی شده همچنان کد اسپاگتی است که در همه جا تکرار میشود، اما واقعاً در همه موارد مورد نیاز نیست. در چنین مواردی، انجام پردازش در چارچوب آسانتر است، تا بار دیگر این کار به دوش توسعهدهندگان منتقل نشود. بله، البته ممکن است یک موقعیت اضطراری پیش بیاید، اما همین استثناهای بررسی نشده را میتوان به روشی راحتتر، بدون زحمت پردازش در تلاش و بدون عبور بیشتر از روشها، مدیریت کرد. فقط کافی است استثنا را به برخی از پاسخ های HTTP در ExceptionHandler تبدیل کنید.22. واردات استاتیک چیست؟
هنگام استفاده از داده های ایستا (متدها، متغیرها)، نمی توانید خود شی را ایجاد کنید، بلکه آن را با نام کلاس انجام دهید، اما حتی در این مورد ما به یک پیوند به کلاس نیاز داریم. همه چیز با آن ساده است: با استفاده از واردات معمولی اضافه می شود. اما اگر از یک متد استاتیک بدون نوشتن نام کلاس استفاده کنیم، مثل اینکه یک متد ثابت کلاس فعلی است، چه؟ این امر با واردات ثابت امکان پذیر است! در این صورت باید static import و لینک آن متد را بنویسیم . مانند این، برای مثال، یک روش ثابت از کلاس Math برای محاسبه مقدار کسینوس:import static java.lang.Math.cos;
در نتیجه، میتوانیم از متد بدون تعیین نام کلاس استفاده کنیم:
double result = cos(60);
همچنین میتوانیم به سادگی تمام متدهای استاتیک یک کلاس را با استفاده از import static بارگذاری کنیم:
import static java.lang.Math.*;
23. رابطه بین متدهای hashCode() وquals() چیست؟
طبق گفته Oracle ، قانون این است: اگر دو شی با هم برابر باشند (یعنی متد ()quals true را برمی گرداند )، آنها باید کد هش یکسانی داشته باشند. در عین حال، فراموش نکنید که دو شی مختلف می توانند کد هش یکسانی داشته باشند. برای درک اینکه چرا ()quals و hashCode() همیشه به صورت جفتی بازنویسی می شوند، موارد زیر را در نظر بگیرید:-
هر دو روش نادیده گرفته می شوند.
در این مورد، دو شی متفاوت با حالت های داخلی یکسان برابر () - true ، در حالی که hashCode() هر دو یک عدد را برمی گرداند.
معلوم می شود که همه چیز خوب است، زیرا این قانون رعایت می شود.
-
هر دو روش نادیده گرفته نمی شوند.
در این مورد، دو شی متفاوت با حالتهای داخلی یکسان، در صورت برابری() false برمیگردانند ، زیرا مقایسه با ارجاع از طریق عملگر == انجام میشود .
متد hashCode() نیز مقادیر متفاوتی را برمی گرداند (به احتمال زیاد) زیرا مقدار تبدیل شده آدرس مکان حافظه را تولید می کند. اما برای یک شیء، این مقدار یکسان خواهد بود، همانطور که در این مورد () برابر فقط زمانی true برمی گردد که مراجع به همان شی اشاره کنند.
معلوم می شود که در این مورد همه چیز اوکی است و قاعده رعایت شده است.
-
Overridden برابر است() ، نه hashCode() باطل شده است .
در این حالت، برای دو شی متفاوت با حالت های داخلی یکسان، ()quals true را برمی گرداند و ()hashCode (به احتمال زیاد) مقادیر متفاوتی را برمی گرداند.
این نقض قانون است، بنابراین انجام این کار توصیه نمی شود.
-
()quals لغو نمی شود ، hashCode() لغو می شود .
در این حالت، برای دو شی متفاوت با حالت های داخلی یکسان، () quals مقدار false و ()hashCode مقادیر یکسان را برمی گرداند.
نقض قانون وجود دارد، بنابراین رویکرد نادرست است.
24. کلاس های BufferedInputStream و BufferedOutputStream چه زمانی استفاده می شوند؟
InputStream برای خواندن داده ها بایت به بایت از برخی منابع و OutputStream برای نوشتن بایت به بایت داده ها استفاده می شود. اما عملیات بایت بایت می تواند بسیار ناخوشایند باشد و نیاز به پردازش اضافی دارد (به منظور خواندن/نوشتن متون به طور معمول). در واقع، برای ساده کردن چنین رکوردهای بایتی، BufferedOutputStream معرفی شد و BufferedInputStream برای خواندن معرفی شد . این کلاسها چیزی بیش از بافرهایی نیستند که دادهها را جمعآوری میکنند و به شما امکان میدهند با دادهها نه بایت به بایت، بلکه توسط کل بستههای داده (آرایهها) کار کنید. هنگامی که BufferedInputStream ایجاد می شود، نمونه ای از نوع InputStream را در سازنده خود می گیرد که داده ها از آن خوانده می شود:BufferedInputStream bufferedInputStream = new BufferedInputStream(System.in);
byte[] arr = new byte[100];
bufferedInputStream.read(arr);
System.in یک شی InputStream است که داده ها را از کنسول می خواند. یعنی با استفاده از این شی BufferedInputStream ، میتوانیم دادهها را از InputStream با نوشتن آن در آرایه عبوری بخوانیم. معلوم می شود که این یک نوع بسته بندی کلاس InputStream است . آرایه arr از این مثال آرایه ای است که داده ها را از BufferedInputStream دریافت می کند . این به نوبه خود داده ها را از InputStream با آرایه دیگری می خواند که به طور پیش فرض دارای اندازه 2048 بایت است. همین امر برای BufferedOutputStream نیز صادق است : یک نمونه از نوع OutputStream باید به سازنده ارسال شود ، که در آن داده ها را در کل آرایه ها می نویسیم:
byte[] arr = "Hello world!!!".getBytes();
BufferedOutputStream bufferedInputStream = new BufferedOutputStream(System.out);
bufferedInputStream.write(arr);
bufferedInputStream.flush();
System.out یک شی OutputStream است که داده ها را روی کنسول می نویسد. متد flush () دادهها را از BufferedOutputStream به OutputStream میفرستد و BufferedOutputStream را در این فرآیند شستشو میدهد . بدون این روش چیزی ثبت نمی شود. و مشابه مثال قبلی: arr آرایه ای است که داده ها از آن در BufferedOutputStream نوشته می شود . از آنجا در یک آرایه متفاوت در OutputStream نوشته می شوند که به طور پیش فرض دارای اندازه 512 بایت است. اطلاعات بیشتر در مورد این دو کلاس را در مقاله بخوانید .
25. تفاوت بین کلاس های java.util.Collection و java.util.Collections چیست؟
مجموعه یک رابط است که سر سلسله مراتب مجموعه است. کلاس هایی را معرفی می کند که به شما امکان می دهد کل گروه های اشیاء را ایجاد، حاوی و تغییر دهید. روش های زیادی برای این کار ارائه شده است، مانند add() ، remove() ، contain() و سایرین. رابط های اصلی کلاس Collection :-
Set رابطی است که مجموعه ای را توصیف می کند که حاوی عناصر منحصر به فرد (غیر تکرار شونده) نامرتب است.
-
لیست یک رابط است که ساختار داده ای را توصیف می کند که دنباله ای مرتب از اشیاء را ذخیره می کند. این اشیاء شاخص (عدد) خود را دریافت می کنند که با استفاده از آن می توانید با آنها تعامل داشته باشید: گرفتن، حذف، تغییر، بازنویسی.
-
صف یک رابط است که یک ساختار داده را با عناصر ذخیره سازی به شکل یک صف که از قانون پیروی می کند - FIFO - First In First Out - توصیف می کند .
-
addAll(Collection<? super T> collection, T...element) - عناصر پاس شده از نوع T را به مجموعه اضافه می کند .
-
copy(List<? super T> dest, List<? extensions T> src) - همه عناصر را از لیست src به لیست در dest کپی می کند .
-
valaList() - یک لیست خالی را برمی گرداند.
-
max(Collection<? extends T> collection, Comparator<? super T> comp) - حداکثر عنصر یک مجموعه داده شده را طبق ترتیب مشخص شده توسط مقایسه کننده مشخص شده برمی گرداند.
-
unmodifiableList(List<? لیست T> را گسترش می دهد) - یک نمایش غیرقابل تغییر از لیست تصویب شده را برمی گرداند.
GO TO FULL VERSION