JavaRush /Блоги Java /Random-TG /Мулоҳиза дар Java - Намунаҳои истифода

Мулоҳиза дар Java - Намунаҳои истифода

Дар гурӯҳ нашр шудааст
Шумо шояд дар ҳаёти ҳаррӯза бо мафҳуми «инъикос» дучор шуда бошед. Одатан ин калима ба раванди омӯзиши худ ишора мекунад. Дар барномасозӣ маънои якхела дорад - он механизми санҷиши маълумот дар бораи барнома, инчунин тағир додани сохтор ва рафтори барнома ҳангоми иҷрои он мебошад. Дар ин ҷо чизи муҳим он аст, ки он дар вақти иҷро анҷом дода мешавад, на дар вақти компиляция. Аммо чаро codeро дар вақти корӣ тафтиш кунед? Шумо инро аллакай мебинед: / Намунаҳои истифодаи Reflection - 1Идеяи инъикос метавонад бо як сабаб фавран равшан набошад: то ин лаҳза шумо ҳамеша дарсҳоеро медонистед, ки бо онҳо кор мекардед. Хуб, масалан, шумо метавонед як синф нависед 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 +
           '}';
}

}
Шумо дар бораи он ҳама чизро медонед, мебинед, ки он чӣ гуна соҳаҳо ва усулҳо дорад. Бешубҳа, шумо метавонед як системаи меросиро бо синфи умумӣ эҷод кунед Animal, то ки ба осонӣ ба барнома дигар синфҳои ҳайвонот ниёз дошта бошанд. Пештар, мо ҳатто як синфи клиникаи byteорӣ таъсис додем, ки дар он шумо метавонед як an objectи волидайнро гузаронед Animalва барнома ҳайвонро вобаста ба саг ё гурба табобат мекард. Гарчанде ки ин вазифаҳо чандон содда нестанд, барнома тамоми маълумоти заруриро дар бораи синфҳо дар вақти тартибдиҳӣ меомӯзад. Аз ин рӯ, вақте ки шумо main()an objectро бо усул Catба усулҳои синфи клиникаи byteорӣ мегузаред, барнома аллакай медонад, ки ин гурба аст, на саг. Акнун тасаввур мекунем, ки дар назди мо вазифаи дигар истодааст. Ҳадафи мо навиштани таҳлилгари code аст. Мо бояд синферо CodeAnalyzerбо усули ягона эҷод кунем - void analyzeClass(Object o). Ин усул бояд:
  • муайян кунед, ки an object ба кадом синф дода шудааст ва номи синфро дар консол нишон диҳед;
  • номҳои ҳамаи майдонҳои ин синф, аз ҷумла хусусиро муайян кунед ва онҳоро дар консол намоиш диҳед;
  • номҳои ҳамаи усулҳои ин синф, аз ҷумла хусусиро муайян кунед ва онҳоро дар консол намоиш диҳед.
Он чизе монанди ин хоҳад буд:
public class CodeAnalyzer {

   public static void analyzeClass(Object o) {

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

}
Акнун фарқияти байни ин мушкилот ва боқимондаи мушкилоте, ки шумо қаблан ҳал кардаед, намоён аст. Дар ин ҳолат, мушкилӣ дар он аст, ки на шумо ва на барнома намедонед, ки ба усули дақиқ чӣ интиқол дода мешавад analyzeClass(). Шумо як барнома менависед, дигар барномасозон ба истифодаи он шурӯъ мекунанд, ки метавонанд ба ин усул ҳама чизро гузаранд - ҳама гуна синфи стандартии Java ё ягон синфе, ки онҳо навиштаанд. Ин синф метавонад шумораи ҳар гуна тағирёбандаҳо ва усулҳоро дошта бошад. Ба ибораи дигар, дар ин ҳолат мо (ва барномаи мо) намедонем, ки мо бо кадом синфҳо кор мекунем. Ва аммо, мо бояд ин мушкилотро ҳал кунем. Ва дар ин ҷо китобхонаи стандартии Java ба кӯмаки мо меояд - Java Reflection API. Reflection API як хусусияти пурқуввати забон аст. Ҳуҷҷатҳои расмии Oracle гуфта мешавад, ки ин механизм танҳо аз ҷониби барномасозони ботаҷриба тавсия дода мешавад, ки чӣ кор карданашонро хуб дарк мекунанд. Шумо ба зудӣ хоҳед фаҳмид, ки чаро ба мо ногаҳон ин гуна огоҳиҳо пешакӣ дода мешаванд :) Дар ин ҷо рӯйхати он чизест, ки бо истифода аз Reflection API чӣ кор кардан мумкин аст:
  1. Синфи an objectро муайян кунед/муайян кунед.
  2. Дар бораи тағирдиҳандаҳои синфҳо, майдонҳо, усулҳо, константаҳо, конструкторҳо ва суперсинфҳо маълумот гиред.
  3. Бифаҳмед, ки кадом усулҳо ба интерфейс/интерфейсҳои амалӣ тааллуқ доранд.
  4. Як мисоли синфро эҷод кунед, вақте ки номи синф то иҷро шудани барнома номаълум аст.
  5. Гирифтан ва муқаррар кардани арзиши майдони an object аз рӯи ном.
  6. Усули an objectро бо ном даъват кунед.
Рӯйхати таъсирбахш, ҳа? :) Диққат диҳед:Механизми инъикос қодир аст, ки ҳамаи инро "дар парвоз" иҷро кунад, новобаста аз он ки мо ба кадом an objectи синфӣ ба таҳлилгари codeи худ мегузарем! Биёед бо мисолҳо имкониятҳои Reflection API-ро бубинем.

Чӣ тавр фаҳмидан / муайян кардани синфи an object

Биёед бо асосҳо оғоз кунем. Нуқтаи воридшавӣ ба механизми инъикоси Java ин аст Class. Бале, ин воқеан хандаовар ба назар мерасад, аммо ин барои инъикос аст :) Бо истифода аз синф Class, мо пеш аз ҳама синфи ҳар як an objectи ба усули мо гузаштаро муайян мекунем. Биёед инро санҷем:
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));
   }
}
Натиҷаи консол:

class learn.javarush.Cat
Ба ду чиз диққат диҳед. Аввалан, мо дидаву дониста синфро Catдар як бастаи алоҳида ҷойгир мекунем.Акнун learn.javarush;шумо мебинед, ки он getClass()номи пурраи синфро бармегардонад. Дуюм, мо тағирёбандаи худро номбар кардем clazz. Каме аҷиб ба назар мерасад. Албатта, он бояд "синф" номида шавад, аммо "класс" калимаи ҳифзшуда дар забони Java аст ва компилятор имкон намедиҳад, ки тағирёбандаҳо ин тавр номида шаванд. Ман бояд аз он берун равам :) Хуб, оғози бад нест! Дар рӯйхати имкониятҳо мо боз чӣ доштем?

Чӣ тавр дар бораи тағирдиҳандаҳои синфҳо, майдонҳо, усулҳо, константаҳо, конструкторҳо ва суперклассҳо маълумот гирифтан мумкин аст

Ин аллакай ҷолибтар аст! Дар синфи ҳозира мо на доимӣ дорем ва на синфи волидайн. Биёед онҳоро барои пуррагӣ илова кунем. Биёед соддатарин синфи волидайнро созем Animal:
package learn.javarush;
public class Animal {

   private String name;
   private int age;
}
Ва биёед Catмеросро аз Animalва як доимӣ ба синфи худ илова кунем:
package learn.javarush;

public class Cat extends Animal {

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

   private String name;
   private int age;

   //...остальная часть класса
}
Ҳоло мо маҷмӯи пурра дорем! Биёед имкониятҳои инъикосро санҷем :)
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));
   }
}
Ин аст он чизе ки мо дар консол мегирем:
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)]
Мо дар бораи синф маълумоти муфассал гирифтем! Ва на танҳо дар бораи ҷамъиятӣ, балки дар бораи қисмҳои хусусӣ. Диққат диҳед: private-тағйирёбандаҳо инчунин дар рӯйхат нишон дода мешаванд. Дарвоқеъ, “таҳлил”-и синфро дар ин лаҳза пурра метавон ҳисоб кард: акнун бо истифода аз усул analyzeClass()ҳама чизи имконпазирро меомӯзем. Аммо инҳо на ҳама имкониятҳое мебошанд, ки мо ҳангоми кор бо инъикос дорем. Биёед, бо мушохидаи оддй махдуд нашавем ва ба амалиёти фаъолона гузарем! :)

Чӣ тавр намунаи синфро эҷод кардан мумкин аст, агар номи синф пеш аз иҷрои барнома номаълум бошад

Биёед бо конструктори пешфарз оғоз кунем. Он ҳанӯз дар синфи мо нест Cat, пас биёед онро илова кунем:
public Cat() {

}
Ин аст, ки code барои сохтани an object Catбо истифода аз инъикос (метод createCat()) чӣ гуна хоҳад буд:
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());
   }
}
Ба консол ворид шавед:

learn.javarush.Cat
Натиҷаи консол:

Cat{name='null', age=0}
Ин хато нест: арзишҳо nameва ageдар консол нишон дода мешаванд, зеро мо баромади онҳоро дар усули toString()синф барномарезӣ кардем Cat. Дар ин ҷо мо номи синферо мехонем, ки an objectи онро аз консол эҷод мекунем. Барномаи иҷрошаванда номи синферо, ки an objectи онро эҷод мекунад, меомӯзад. Намунаҳои истифодаи Reflection - 3Барои кӯтоҳ кардан, мо codeро барои коркарди дурусти истисноҳо сарфи назар кардем, то он нисбат ба худи мисол ҷойгоҳи бештарро ишғол накунад. Дар як барномаи воқеӣ, бешубҳа, кор кардан лозим аст, ки вазъиятҳое, ки номҳои нодуруст ворид карда шудаанд ва ғайра. Конструктори пешфарз як чизи хеле содда аст, бинобар ин сохтани мисоли синф бо истифода аз он, тавре мебинед, душвор нест :) Ва бо истифода аз метод, newInstance()мо an objectи нави ин синфро эҷод мекунем. Ин масъалаи дигар аст, агар созандаи синф Catпараметрҳоро ҳамчун вуруд қабул кунад. Биёед конструктори пешфарзро аз синф хориҷ кунем ва кӯшиш кунем, ки рамзи худро дубора иҷро кунем.

null
java.lang.InstantiationException: learn.javarush.Cat
  at java.lang.Class.newInstance(Class.java:427)
Чизе хато кард! Мо хато гирифтем, зеро мо усули эҷоди an objectро тавассути созандаи пешфарз даъват кардем. Аммо ҳоло мо чунин тарроҳе надорем. Ин маънои онро дорад, ки вақте ки усул кор мекунад, newInstance()механизми инъикос конструктори кӯҳнаи моро бо ду параметр истифода мебарад:
public Cat(String name, int age) {
   this.name = name;
   this.age = age;
}
Аммо мо бо параметрҳо ҳеҷ коре накардаем, гӯё ки мо онҳоро комилан фаромӯш кардаем! Барои он ки онҳоро бо истифода аз инъикос ба конструктор интиқол диҳед, шумо бояд онро каме тағир диҳед:
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());
   }
}
Натиҷаи консол:

Cat{name='Barsik', age=6}
Биёед ба он чизе ки дар барномаи мо рӯй дода истодааст, муфассалтар назар андозем. Мо як қатор an objectҳоро офаридаем Class.
Class[] catClassParams = {String.class, int.class};
Онҳо ба параметрҳои созандаи мо мувофиқат мекунанд (мо танҳо параметрҳоро дорем Stringва int). Мо онҳоро ба усул мегузарем clazz.getConstructor()ва ба конструктори зарурӣ дастрасӣ пайдо мекунем. Пас аз ин, танҳо даъват кардани метод newInstance()бо параметрҳои зарурӣ боқӣ мемонад ва фаромӯш накунед, ки an objectро ба синфе, ки ба мо лозим аст, ба таври возеҳ партоед - Cat.
cat = (Cat) clazz.getConstructor(catClassParams).newInstance("Barsik", 6);
Дар натича an objectи мо бомуваффакият ба вучуд оварда мешавад! Натиҷаи консол:

Cat{name='Barsik', age=6}
Биёед идома диҳем :)

Чӣ тавр гирифтан ва муқаррар кардани арзиши майдони an object бо ном

Тасаввур кунед, ки шумо синферо истифода мебаред, ки аз ҷониби барномасози дигар навишта шудааст. Бо вуҷуди ин, шумо имкони таҳрир кардани онро надоред. Масалан, китобхонаи синфҳои омодаи дар JAR бастабандишуда. Шумо метавонед рамзи синфро хонед, аммо шумо онро тағир дода наметавонед. Барномасозе, ки синфро дар ин китобхона офаридааст (бигзор он синфи кӯҳнаи мо бошад Cat) пеш аз тарҳрезии ниҳоӣ хоби кофӣ нагирифт ва гетерҳо ва танзимкунандагонро барои майдон хориҷ кард age. Акнун ин синф ба шумо омад. Он ба талаботи шумо пурра қонеъ мекунад, зеро ба шумо танҳо an objectҳо дар барнома лозиманд Cat. Аммо ба шумо онҳо бо ҳамон соҳа лозиманд age! Ин мушкилот аст: мо ба майдон расида наметавонем, зеро он тағирдиҳанда дорад privateва гирандагон ва танзимкунандагон аз ҷониби таҳиягари эҳтимолии ин синф хориҷ карда шудаанд :/ Хуб, инъикос низ метавонад дар ин вазъият ба мо кӯмак кунад! CatМо ба codeи синф дастрасӣ дорем: мо ҳадди аққал фаҳмем, ки он кадом майдонҳо дорад ва онҳо чӣ ном доранд. Бо ин маълумот, мо мушкилоти худро ҳал мекунем:
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());
   }
}
Тавре ки дар шарҳ гуфта шудааст, nameҳама чиз бо майдон оддӣ аст: таҳиягарони синф барои он танзимкунанда пешниҳод карданд. Шумо инчунин аллакай медонед, ки чӣ гуна an objectҳоро аз конструкторҳои пешфарз эҷод кунед: барои ин усул вуҷуд дорад newInstance(). Аммо шумо бояд бо майдони дуюм кор кунед. Биёед бифаҳмем, ки дар ин ҷо чӣ мешавад :)
Field age = clazz.getDeclaredField("age");
Дар ин ҷо мо бо истифода аз an objectи худ Class clazzба майдон ageбо истифода аз getDeclaredField(). Он ба мо имкон медиҳад, ки майдони синну солро ҳамчун an object ба даст орем Field age. Аммо ин ҳанӯз кофӣ нест, зеро privateба майдонҳо танҳо қиматҳо таъин кардан мумкин нест. Барои ин, шумо бояд майдонро бо истифода аз усули "дастрас" созед setAccessible():
age.setAccessible(true);
Ба он майдонҳое, ки барои онҳо ин кор анҷом дода мешавад, метавонанд арзишҳо таъин карда шаванд:
age.set(cat, 6);
Тавре ки шумо мебинед, мо як навъ танзимкунанда дорем, ки чаппа шудааст: мо ба майдон Field ageарзиши онро таъин мекунем ва инчунин an objectеро, ки ин майдон бояд ба он таъин карда шавад, интиқол медиҳем. Биёед усули худро иҷро кунем main()ва бубинем:

Cat{name='Barsik', age=6}
Аҷоиб, мо ҳамаашро кардем! :) Биёед бубинем, ки мо боз чӣ имкониятҳо дорем...

Чӣ тавр усули an objectро бо ном даъват кардан мумкин аст

Биёед вазъиятро аз мисоли пештара каме дигар кунем. Фарз мекунем, ки таҳиягари синф Catбо майдонҳо хато кардааст - ҳарду дастрасанд, барои онҳо қабулкунанда ва танзимкунанда мавҷуданд, ҳама чиз хуб аст. Мушкилот дигар аст: вай усули хусусиеро сохт, ки ба мо бешубҳа лозим аст:
private void sayMeow() {

   System.out.println("Meow!");
}
Дар натиҷа, мо Catдар барномаи худ an objectҳоро эҷод мекунем, аммо методи онҳоро даъват карда наметавонем sayMeow(). Оё мо гурбаҳое дорем, ки мияав намекунанд? Хеле аҷиб :/ Инро чӣ гуна метавонам ислоҳ кунам? Бори дигар, API Reflection ба наҷот меояд! Мо номи усули заруриро медонем. Боқимонда як масъалаи техника аст:
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();
   }
}
Дар ин ҷо мо ҳамон тавре амал мекунем, ки дар ҳолати дастрасӣ ба майдони хусусӣ. Аввалан мо усули лозимаро мегирем, ки он дар an objectи синф фаро гирифта шудааст Method:
Method sayMeow = clazz.getDeclaredMethod("sayMeow");
Бо кӯмак, getDeclaredMethod()шумо метавонед ба усулҳои хусусӣ "дастрас кунед". Минбаъд мо усулро қобor даъват мекунем:
sayMeow.setAccessible(true);
Ва ниҳоят, мо усулро дар an objectи дилхоҳ даъват мекунем:
sayMeow.invoke(cat);
Даъвати усул низ ба "занги баръакс" монанд аст: мо одат кардаем, ки an objectро бо истифода аз нуқта ( cat.sayMeow()) ба усули зарурӣ нишон диҳем ва ҳангоми кор бо инъикос ба усул an objectеро мегузарем, ки онро аз он даъват кардан лозим аст. . Мо дар консол чӣ дорем?

Meow!
Ҳама чиз кор кард! :) Акнун шумо мебинед, ки механизми инъикос дар Java ба мо чӣ гуна имкониятҳои васеъ медиҳад. Дар ҳолатҳои душвор ва ғайричашмдошт (ба мисли мисолҳо бо синф аз китобхонаи пӯшида), он воқеан метавонад ба мо кӯмаки зиёд расонад. Аммо, вай мисли хар як кувваи бузург масъулияти калонро дар назар дорад. Дар бораи камбудиҳои инъикос дар як бахши махсуси вебсайти Oracle навишта шудааст. Се камбудиҳои асосӣ вуҷуд доранд:
  1. Хосилнокии мехнат паст мешавад. Усулҳое, ки бо истифода аз инъикос даъват карда мешаванд, нисбат ба усулҳое, ки маъмулан даъват мешаванд, самаранокии камтар доранд.

  2. Маҳдудиятҳои бехатарӣ мавҷуданд. Механизми инъикос ба шумо имкон медиҳад, ки рафтори барномаро дар вақти иҷро тағир диҳед. Аммо дар муҳити кории шумо дар лоиҳаи воқеӣ метавонад маҳдудиятҳое вуҷуд дошта бошанд, ки ба шумо имкон намедиҳанд, ки ин корро кунед.

  3. Хавфи ифшои маълумоти дохилӣ. Фаҳмидани он муҳим аст, ки истифодаи инъикос мустақиман принсипи инкапсуляцияро вайрон мекунад: он ба мо имкон медиҳад, ки ба майдонҳо, усулҳо ва ғайра дастрасӣ пайдо кунем. Ба фикрам фахмондан лозим нест, ки бевосита ва дагалона вайрон кардани принципхои ООП бояд танхо дар холатхои шадидтарин, вакте ки дигар роххои халли масъала бо сабабхои аз шумо вобаста нест, мурочиат кардан лозим аст.

Механизми инъикосро окилона ва танхо дар вазъияте истифода баред, ки аз он канорагирӣ кардан мумкин нест ва камбудиҳои онро фаромӯш накунед. Бо ин лекцияи мо ба охир мерасад! Ин хеле калон буд, аммо имрӯз шумо бисёр чизҳои навро омӯхтед :)
Шарҳҳо
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION