JavaRush /Java blogi /Random-UZ /Java-da aks ettirish - Foydalanish misollari

Java-da aks ettirish - Foydalanish misollari

Guruhda nashr etilgan
Siz kundalik hayotda "aks ettirish" tushunchasiga duch kelgan bo'lishingiz mumkin. Odatda bu so'z o'zini o'rganish jarayonini anglatadi. Dasturlashda u xuddi shunday ma'noga ega - bu dastur to'g'risidagi ma'lumotlarni tekshirish mexanizmi, shuningdek uni bajarish jarayonida dasturning tuzilishi va harakatini o'zgartirish. Bu erda muhim narsa shundaki, u kompilyatsiya vaqtida emas, balki ish vaqtida amalga oshiriladi. Lekin nima uchun kodni ish vaqtida tekshirish kerak? Siz buni allaqachon ko'rgansiz:/ Reflectiondan foydalanishga misollar - 1Mulohaza yuritish g'oyasi bir sababga ko'ra darhol aniq bo'lmasligi mumkin: shu paytgacha siz doimo ishlayotgan sinflaringizni bilar edingiz. Masalan, siz sinf yozishingiz mumkin Cat:
package learn.javarush;

public class Cat {

   private String name;
   private int age;

   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   public void sayMeow() {

       System.out.println("Meow!");
   }

   public void jump() {

       System.out.println("Jump!");
   }

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public int getAge() {
       return age;
   }

   public void setAge(int age) {
       this.age = age;
   }

@Override
public String toString() {
   return "Cat{" +
           "name='" + name + '\'' +
           ", age=" + age +
           '}';
}

}
Siz bu haqda hamma narsani bilasiz, uning qanday sohalari va usullari borligini ko'rasiz. Albatta, siz qulaylik uchun umumiy sinf bilan meros tizimini yaratishingiz mumkin Animal, agar to'satdan dasturga hayvonlarning boshqa sinflari kerak bo'lsa. Ilgari, biz hatto veterinariya klinikasi sinfini yaratdik, unda siz ota-ona ob'ektidan o'tishingiz mumkin Animalva dastur hayvonni it yoki mushuk bo'lishiga qarab davolaydi. Bu vazifalar unchalik oddiy bo'lmasa-da, dastur kompilyatsiya vaqtida darslar haqida kerakli barcha ma'lumotlarni o'rganadi. Shuning uchun, main()ob'ektni Catveterinariya klinikasining usullariga o'tkazganingizda, dastur bu it emas, balki mushuk ekanligini allaqachon biladi. Endi tasavvur qilaylik, oldimizda boshqa vazifa turibdi. Bizning maqsadimiz kod analizatorini yozishdir. CodeAnalyzerBiz bitta usul bilan sinf yaratishimiz kerak - void analyzeClass(Object o). Bu usul kerak:
  • ob'ekt qaysi sinfga o'tganligini aniqlang va konsolda sinf nomini ko'rsating;
  • ushbu sinfning barcha maydonlarining, shu jumladan shaxsiy maydonlarning nomlarini aniqlang va ularni konsolda ko'rsating;
  • ushbu sinfning barcha usullarining, shu jumladan shaxsiylarning nomlarini aniqlang va ularni konsolda ko'rsating.
Bu shunday ko'rinadi:
public class CodeAnalyzer {

   public static void analyzeClass(Object o) {

       //Вывести название класса, к которому принадлежит an object o
       //Вывести названия всех переменных этого класса
       //Вывести названия всех методов этого класса
   }

}
Endi bu muammo va siz oldin hal qilgan boshqa muammolar o'rtasidagi farq ko'rinadi. Bunday holda, qiyinchilik shundaki, siz ham, dastur ham usulga aniq nima o'tishini bilmaydi analyzeClass(). Siz dastur yozasiz, boshqa dasturchilar undan foydalanishni boshlaydilar, ular bu usulga hamma narsani o'tkazishi mumkin - har qanday standart Java sinfi yoki ular yozgan har qanday sinf. Bu sinfda har qanday miqdordagi o'zgaruvchilar va usullar bo'lishi mumkin. Boshqacha qilib aytadigan bo'lsak, bu holda biz (va bizning dasturimiz) qaysi sinflar bilan ishlashimizni bilmaymiz. Va shunga qaramay, biz bu muammoni hal qilishimiz kerak. Va bu erda standart Java kutubxonasi yordamimizga keladi - Java Reflection API. Reflection API kuchli til xususiyatidir. Rasmiy Oracle hujjatlarida aytilishicha, ushbu mexanizmdan faqat nima qilayotganini juda yaxshi tushunadigan tajribali dasturchilar foydalanishi tavsiya etiladi. Tez orada nima uchun birdaniga oldindan bunday ogohlantirishlar berilganini tushunasiz :) Mana, Reflection API yordamida nima qilish mumkinligi ro'yxati:
  1. Ob'ekt sinfini toping/aniqlang.
  2. Sinf modifikatorlari, maydonlar, usullar, konstantalar, konstruktorlar va yuqori sinflar haqida ma'lumot oling.
  3. Amalga oshirilgan interfeys/interfeyslarga qaysi usullar tegishli ekanligini bilib oling.
  4. Dastur bajarilgunga qadar sinf nomi noma'lum bo'lsa, sinfning namunasini yarating.
  5. Ob'ekt maydonining qiymatini nomi bo'yicha oling va o'rnating.
  6. Ob'ekt usulini nomi bilan chaqiring.
Ta'sirli ro'yxat, a? :) Diqqat qilish:Kod analizatorimizga qaysi sinf ob'ektini o'tkazishimizdan qat'iy nazar, aks ettirish mexanizmi bularning barchasini "parvozda" bajarishga qodir! Keling, misollar bilan Reflection API imkoniyatlarini ko'rib chiqaylik.

Ob'ektning sinfini qanday topish / aniqlash

Keling, asoslardan boshlaylik. Java-ning aks ettirish mexanizmiga kirish nuqtasi Class. Ha, bu juda kulgili ko'rinadi, lekin aks ettirish uchun nima kerak :) Class dan foydalanib Class, biz birinchi navbatda metodimizga o'tgan har qanday ob'ektning sinfini aniqlaymiz. Keling, buni sinab ko'raylik:
import learn.javarush.Cat;

public class CodeAnalyzer {

   public static void analyzeClass(Object o) {
       Class clazz = o.getClass();
       System.out.println(clazz);
   }

   public static void main(String[] args) {

       analyzeClass(new Cat("Barsik", 6));
   }
}
Konsol chiqishi:

class learn.javarush.Cat
Ikki narsaga e'tibor bering. Birinchidan, biz ataylab sinfni Catalohida paketga joylashtirdik.Endi u sinfning to'liq nomini qaytarayotganini learn.javarush;ko'rishingiz mumkin . getClass()Ikkinchidan, biz o'zgaruvchimizni nomladik clazz. Biroz g'alati ko'rinadi. Albatta, uni "sinf" deb atash kerak, lekin "sinf" Java tilida ajratilgan so'z bo'lib, kompilyator o'zgaruvchilarni shunday chaqirishga ruxsat bermaydi. Men undan chiqib ketishim kerak edi :) Xo'sh, yomon boshlanish emas! Imkoniyatlar ro'yxatida yana nima bor edi?

Sinf modifikatorlari, maydonlar, usullar, konstantalar, konstruktorlar va yuqori sinflar haqida ma'lumot olish

Bu allaqachon qiziqroq! Hozirgi sinfda bizda doimiylar va ota-onalar sinfi yo'q. To'liqlik uchun ularni qo'shamiz. Keling, eng oddiy ota-ona sinfini yarataylik Animal:
package learn.javarush;
public class Animal {

   private String name;
   private int age;
}
Keling , sinfimizga Catmerosni va bitta doimiyni qo'shamiz :Animal
package learn.javarush;

public class Cat extends Animal {

   private static final String ANIMAL_FAMILY = "Семейство кошачьих";

   private String name;
   private int age;

   //...остальная часть класса
}
Endi bizda to'liq to'plam bor! Keling, mulohaza yuritish imkoniyatlarini sinab ko'raylik :)
import learn.javarush.Cat;

import java.util.Arrays;

public class CodeAnalyzer {

   public static void analyzeClass(Object o) {
       Class clazz = o.getClass();
       System.out.println("Name класса: " + clazz);
       System.out.println("Поля класса: " + Arrays.toString(clazz.getDeclaredFields()));
       System.out.println("Родительский класс: " + clazz.getSuperclass());
       System.out.println("Методы класса: " +  Arrays.toString(clazz.getDeclaredMethods()));
       System.out.println("Конструкторы класса: " + Arrays.toString(clazz.getConstructors()));
   }

   public static void main(String[] args) {

       analyzeClass(new Cat("Barsik", 6));
   }
}
Konsolda biz quyidagilarni olamiz:
Name класса: class learn.javarush.Cat
Поля класса: [private static final java.lang.String learn.javarush.Cat.ANIMAL_FAMILY, private java.lang.String learn.javarush.Cat.name, private int learn.javarush.Cat.age]
Родительский класс: class learn.javarush.Animal
Методы класса: [public java.lang.String learn.javarush.Cat.getName(), public void learn.javarush.Cat.setName(java.lang.String), public void learn.javarush.Cat.sayMeow(), public void learn.javarush.Cat.setAge(int), public void learn.javarush.Cat.jump(), public int learn.javarush.Cat.getAge()]
Конструкторы класса: [public learn.javarush.Cat(java.lang.String,int)]
Biz dars haqida juda ko'p batafsil ma'lumot oldik! Va nafaqat ommaviy, balki shaxsiy qismlar haqida ham. Diqqat qilish: private-o'zgaruvchilar ham ro'yxatda ko'rsatiladi. Darhaqiqat, sinfning "tahlili" shu nuqtada tugallangan deb hisoblanishi mumkin: endi usuldan foydalanib, analyzeClass()biz mumkin bo'lgan hamma narsani bilib olamiz. Ammo bular fikrlash bilan ishlashda bizda mavjud bo'lgan barcha imkoniyatlar emas. Keling, oddiy kuzatish bilan cheklanib qolmay, faol harakatga o‘tamiz! :)

Agar dastur bajarilishidan oldin sinf nomi noma'lum bo'lsa, sinfning namunasini qanday yaratish kerak

Standart konstruktordan boshlaylik. Bu hali bizning sinfimizda emas Cat, shuning uchun uni qo'shamiz:
public Cat() {

}
Kod Cataks ettirish (metod createCat()) yordamida ob'ekt yaratish uchun qanday ko'rinishga ega bo'ladi:
import learn.javarush.Cat;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {

   public static Cat createCat() throws IOException, IllegalAccessException, InstantiationException, ClassNotFoundException {

       BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
       String className = reader.readLine();

       Class clazz = Class.forName(className);
       Cat cat = (Cat) clazz.newInstance();

       return cat;
   }

public static Object createObject() throws Exception {

   BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
   String className = reader.readLine();

   Class clazz = Class.forName(className);
   Object result = clazz.newInstance();

   return result;
}

   public static void main(String[] args) throws IOException, IllegalAccessException, ClassNotFoundException, InstantiationException {
       System.out.println(createCat());
   }
}
Konsolga kiring:

learn.javarush.Cat
Konsol chiqishi:

Cat{name='null', age=0}
Bu xato emas: nameva qiymatlari agekonsolda ko'rsatiladi, chunki biz ularning chiqishini toString()sinf usulida dasturlaganmiz Cat. Bu erda biz konsoldan ob'ektini yaratadigan sinf nomini o'qiymiz. Ishlayotgan dastur ob'ektini yaratadigan sinf nomini bilib oladi. Reflectiondan foydalanishga misollar - 3Qisqalik uchun biz istisnolarni to'g'ri ko'rib chiqish uchun kodni o'tkazib yubordik, shunda u misolning o'zidan ko'proq joy egallamaydi. Haqiqiy dasturda, albatta, noto'g'ri ismlar kiritilgan va hokazo vaziyatlarni hal qilishga arziydi. Standart konstruktor juda oddiy narsa, shuning uchun uni ishlatib, sinf misolini yaratish, ko'rib turganingizdek, qiyin emas :) Va usuldan foydalanib, newInstance()biz ushbu sinfning yangi ob'ektini yaratamiz. Sinf konstruktori parametrlarni kirish sifatida qabul qilsa, bu boshqa masala Cat. Keling, standart konstruktorni sinfdan olib tashlaymiz va kodimizni qayta ishga tushirishga harakat qilaylik.

null
java.lang.InstantiationException: learn.javarush.Cat
  at java.lang.Class.newInstance(Class.java:427)
Nimadir noto'g'ri bajarildi! Biz standart konstruktor orqali ob'ekt yaratish usulini chaqirganimiz uchun xatoga yo'l qo'ydik. Ammo hozir bizda bunday dizayner yo'q. Bu shuni anglatadiki, usul ishlaganda, newInstance()aks ettirish mexanizmi eski konstruktorimizdan ikkita parametr bilan foydalanadi:
public Cat(String name, int age) {
   this.name = name;
   this.age = age;
}
Ammo biz parametrlar bilan hech narsa qilmadik, go'yo ular haqida butunlay unutganmiz! Ularni aks ettirish yordamida konstruktorga o'tkazish uchun siz uni biroz o'zgartirishingiz kerak bo'ladi:
import learn.javarush.Cat;

import java.lang.reflect.InvocationTargetException;

public class Main {

   public static Cat createCat()  {

       Class clazz = null;
       Cat cat = null;

       try {
           clazz = Class.forName("learn.javarush.Cat");
           Class[] catClassParams = {String.class, int.class};
           cat = (Cat) clazz.getConstructor(catClassParams).newInstance("Barsik", 6);
       } catch (ClassNotFoundException e) {
           e.printStackTrace();
       } catch (InstantiationException e) {
           e.printStackTrace();
       } catch (IllegalAccessException e) {
           e.printStackTrace();
       } catch (NoSuchMethodException e) {
           e.printStackTrace();
       } catch (InvocationTargetException e) {
           e.printStackTrace();
       }

       return cat;
   }

   public static void main(String[] args) {
       System.out.println(createCat());
   }
}
Konsol chiqishi:

Cat{name='Barsik', age=6}
Keling, dasturimizda nima sodir bo'layotganini batafsil ko'rib chiqaylik. Biz ob'ektlar qatorini yaratdik Class.
Class[] catClassParams = {String.class, int.class};
Ular konstruktorimizning parametrlariga mos keladi (bizda faqat Stringva parametrlari mavjud int). Biz ularni usulga o'tkazamiz clazz.getConstructor()va kerakli konstruktorga kirish huquqiga ega bo'lamiz. Shundan so'ng, faqat kerakli parametrlar bilan usulni chaqirish qoladi newInstance()va ob'ektni bizga kerak bo'lgan sinfga aniq yuborishni unutmang - Cat.
cat = (Cat) clazz.getConstructor(catClassParams).newInstance("Barsik", 6);
Natijada bizning ob'ektimiz muvaffaqiyatli yaratiladi! Konsol chiqishi:

Cat{name='Barsik', age=6}
Davom etaylik :)

Ob'ekt maydonining qiymatini nomi bo'yicha qanday olish va o'rnatish

Boshqa dasturchi tomonidan yozilgan sinfdan foydalanayotganingizni tasavvur qiling. Biroq, uni tahrir qilish imkoningiz yo'q. Masalan, JARga qadoqlangan tayyor sinf kutubxonasi. Siz sinf kodini o'qishingiz mumkin, lekin uni o'zgartira olmaysiz. Ushbu kutubxonada sinfni yaratgan dasturchi (bu bizning eski sinfimiz bo'lsin Cat) yakuniy dizayndan oldin etarlicha uxlamadi va maydon uchun qabul qiluvchi va sozlash vositalarini olib tashladi age. Endi bu dars sizga keldi. Bu sizning ehtiyojlaringizga to'liq javob beradi, chunki sizga faqat dasturdagi ob'ektlar kerak Cat. Lekin sizga xuddi shu sohada ular kerak age! Bu muammo: biz maydonga etib bora olmaymiz, chunki uning modifikatori bor privateva qabul qiluvchilar va sozlagichlar ushbu klassning bo'lajak ishlab chiquvchisi tomonidan o'chirilgan :/ Xo'sh, aks ettirish bizga bu vaziyatda ham yordam berishi mumkin! CatBizda sinf kodiga kirish imkoni bor: biz hech bo'lmaganda qanday maydonlar mavjudligini va ular nima deb nomlanganini bilib olamiz. Ushbu ma'lumotlar bilan qurollangan holda biz muammoni hal qilamiz:
import learn.javarush.Cat;

import java.lang.reflect.Field;

public class Main {

   public static Cat createCat()  {

       Class clazz = null;
       Cat cat = null;
       try {
           clazz = Class.forName("learn.javarush.Cat");
           cat = (Cat) clazz.newInstance();

           //с полем name нам повезло - для него в классе есть setter
           cat.setName("Barsik");

           Field age = clazz.getDeclaredField("age");

           age.setAccessible(true);

           age.set(cat, 6);

       } catch (IllegalAccessException e) {
           e.printStackTrace();
       } catch (InstantiationException e) {
           e.printStackTrace();
       } catch (ClassNotFoundException e) {
           e.printStackTrace();
       } catch (NoSuchFieldException e) {
           e.printStackTrace();
       }

       return cat;
   }

   public static void main(String[] args) {
       System.out.println(createCat());
   }
}
Sharhda aytib o'tilganidek, namemaydon bilan hamma narsa oddiy: sinf ishlab chiquvchilari buning uchun sozlashni taqdim etishdi. Bundan tashqari, siz standart konstruktorlardan ob'ektlarni qanday yaratishni allaqachon bilasiz: buning uchun usul mavjud newInstance(). Ammo siz ikkinchi maydon bilan shug'ullanishingiz kerak bo'ladi. Keling, bu erda nima bo'layotganini aniqlaylik :)
Field age = clazz.getDeclaredField("age");
Bu erda biz ob'ektimizdan foydalanib Class clazz, maydonga kirishimiz agemumkin getDeclaredField(). Bizga ob'ekt sifatida yosh maydonini olish qobiliyatini beradi Field age. Lekin bu hali yetarli emas, chunki privatemaydonlarga oddiygina qiymatlar berish mumkin emas. Buni amalga oshirish uchun siz quyidagi usul yordamida maydonni "mavjud" qilishingiz kerak setAccessible():
age.setAccessible(true);
Bu amalga oshiriladigan maydonlarga qiymatlar berilishi mumkin:
age.set(cat, 6);
Ko'rib turganingizdek, bizda teskari aylantirilgan sozlagich mavjud: biz maydonga Field ageuning qiymatini beramiz, shuningdek, ushbu maydon tayinlanishi kerak bo'lgan ob'ektni o'tkazamiz. Keling, usulimizni ishga tushiramiz main()va ko'ramiz:

Cat{name='Barsik', age=6}
Ajoyib, biz hammasini qildik! :) Keling, yana qanday imkoniyatlarimiz borligini ko'ramiz...

Ob'ekt usulini nomi bilan qanday chaqirish mumkin

Keling, oldingi misoldan vaziyatni biroz o'zgartiraylik. Aytaylik, sinf ishlab chiqaruvchisi Catmaydonlar bilan xatoga yo'l qo'ydi - ikkalasi ham mavjud, ular uchun qabul qiluvchilar va sozlagichlar mavjud, hammasi joyida. Muammo boshqacha: u bizga kerak bo'lgan usulni shaxsiy qildi:
private void sayMeow() {

   System.out.println("Meow!");
}
Natijada, biz Catdasturimizda ob'ektlarni yaratamiz, lekin ularning usulini chaqira olmaymiz sayMeow(). Bizda miyovlamaydigan mushuklar bo'ladimi? Juda g'alati :/ Buni qanday tuzatishim mumkin? Yana bir bor Reflection API yordamga keladi! Biz talab qilinadigan usulning nomini bilamiz. Qolganlari texnika masalasidir:
import learn.javarush.Cat;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Main {

   public static void invokeSayMeowMethod()  {

       Class clazz = null;
       Cat cat = null;
       try {

           cat = new Cat("Barsik", 6);

           clazz = Class.forName(Cat.class.getName());

           Method sayMeow = clazz.getDeclaredMethod("sayMeow");

           sayMeow.setAccessible(true);

           sayMeow.invoke(cat);

       } catch (ClassNotFoundException e) {
           e.printStackTrace();
       } catch (NoSuchMethodException e) {
           e.printStackTrace();
       } catch (IllegalAccessException e) {
           e.printStackTrace();
       } catch (InvocationTargetException e) {
           e.printStackTrace();
       }
   }

   public static void main(String[] args) {
       invokeSayMeowMethod();
   }
}
Bu erda biz xususiy maydonga kirish bilan bir xil tarzda harakat qilamiz. Avval biz kerakli usulni olamiz, u sinf ob'ektiga kiritilgan Method:
Method sayMeow = clazz.getDeclaredMethod("sayMeow");
Yordam bilan getDeclaredMethod()siz shaxsiy usullarga "qo'llashingiz" mumkin. Keyin usulni chaqiriladigan qilib qo'yamiz:
sayMeow.setAccessible(true);
Va nihoyat, biz kerakli ob'ekt bo'yicha usulni chaqiramiz:
sayMeow.invoke(cat);
Usulni chaqirish ham "teskari chaqiruv"ga o'xshaydi: biz nuqta ( cat.sayMeow()) yordamida ob'ektni kerakli usulga ko'rsatishga odatlanganmiz va aks ettirish bilan ishlashda biz uni chaqirish kerak bo'lgan ob'ektni o'tkazamiz. . Konsolda nima bor?

Meow!
Hammasi chiqdi! :) Endi siz Java-dagi aks ettirish mexanizmi bizga qanday keng imkoniyatlar berayotganini ko'rasiz. Qiyin va kutilmagan vaziyatlarda (yopiq kutubxonadagi sinf misollarida bo'lgani kabi) bu bizga juda ko'p yordam berishi mumkin. Biroq, har qanday buyuk kuch kabi, u ham katta mas'uliyatni nazarda tutadi. Fikrlashning kamchiliklari haqida Oracle veb-saytining maxsus bo'limida yozilgan . Uchta asosiy kamchiliklar mavjud:
  1. Hosildorlik pasayadi. Ko'zgu yordamida chaqiriladigan usullar odatda chaqirilgan usullarga qaraganda pastroq samaradorlikka ega.

  2. Xavfsizlik cheklovlari mavjud. Ko'zgu mexanizmi ish vaqtida dasturning harakatini o'zgartirishga imkon beradi. Ammo haqiqiy loyihadagi ish muhitingizda buni amalga oshirishga imkon bermaydigan cheklovlar bo'lishi mumkin.

  3. Ichki ma'lumotlarning oshkor etilishi xavfi. Shuni tushunish kerakki, aks ettirishdan foydalanish to'g'ridan-to'g'ri inkapsulyatsiya tamoyilini buzadi: bu bizga shaxsiy maydonlarga, usullarga va hokazolarga kirishga imkon beradi. O'ylaymanki, OOP tamoyillarini to'g'ridan-to'g'ri va qo'pol ravishda buzish faqat o'ta o'ta og'ir holatlarda, sizning ixtiyoringizda bo'lmagan sabablarga ko'ra muammoni hal qilishning boshqa usullari mavjud bo'lmaganda qo'llanilishi kerakligini tushuntirishga hojat yo'q.

Ko'zgu mexanizmidan oqilona va faqat undan qochib bo'lmaydigan holatlarda foydalaning va uning kamchiliklari haqida unutmang. Bu bizning ma'ruzamizni yakunlaydi! Bu juda katta bo'lib chiqdi, lekin bugun siz juda ko'p yangi narsalarni o'rgandingiz :)
Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION