20-bo‘lim
Amaliy loyiha - talabalar jurnali
Barcha o'rganilganni bitta dasturda birlashtiramiz: record modellar, interfeys, kolleksiyalar, Stream API, fayl saqlash, istisnolar va testlar.
Ushbu bo‘lim mundarijasi
Oxirgi bo'lim. Yangi mavzu yo'q - o'rgangan hamma narsangizni bitta ishlaydigan dasturda birlashtiramiz.
Vazifa: talabalar jurnali. Dastur talabalar va baholarni saqlaydi, fayldan o'qiydi, hisobot chiqaradi va o'zini sinovdan o'tkazadi.
Loyiha tuzilmasi #
Haqiqiy Maven loyihasida:
jurnal/
src/
main/java/uz/softromeda/jurnal/
Main.java
model/
Talaba.java
Baho.java
Daraja.java
ombor/
JurnalOmbori.java
FaylOmbori.java
xizmat/
HisobotXizmati.java
test/java/uz/softromeda/jurnal/
HisobotXizmatiTest.java
pom.xml
Bu darslikda misollar qisqa bo'lishi uchun sinflar bitta faylda ko'rsatiladi.
1-qadam. Model #
Ma'lumot tashiydigan turlar uchun record (11-bo'lim):
import java.util.Objects;
public class Model {
public static void main(String[] args) {
Talaba t = new Talaba(1, "Husanboy Qodirov", "A");
Baho b = new Baho(1, "Matematika", 92);
System.out.println(t);
System.out.println(b);
System.out.println("Daraja(92): " + Daraja.aniqla(92));
System.out.println("Daraja(68.5): " + Daraja.aniqla(68.5));
try {
new Baho(1, "Fizika", 150);
} catch (IllegalArgumentException x) {
System.out.println("Tekshiruv: " + x.getMessage());
}
}
}
record Talaba(int id, String ism, String guruh) {
Talaba {
Objects.requireNonNull(ism, "ism null bo'lmasin");
if (ism.isBlank()) {
throw new IllegalArgumentException("ism bo'sh bo'lmasin");
}
ism = ism.strip();
}
}
record Baho(int talabaId, String fan, int qiymat) {
Baho {
if (qiymat < 0 || qiymat > 100) {
throw new IllegalArgumentException(
"Ball 0..100 oralig'ida: " + qiymat);
}
}
}
enum Daraja {
QONIQARSIZ(0), QONIQARLI(60), YAXSHI(75), ALO(90);
private final int engKam;
Daraja(int engKam) {
this.engKam = engKam;
}
static Daraja aniqla(double ball) {
Daraja natija = QONIQARSIZ;
for (Daraja d : values()) {
if (ball >= d.engKam) {
natija = d;
}
}
return natija;
}
}
Talaba[id=1, ism=Husanboy Qodirov, guruh=A]
Baho[talabaId=1, fan=Matematika, qiymat=92]
Daraja(92): ALO
Daraja(68.5): QONIQARLI
Tekshiruv: Ball 0..100 oralig'ida: 150
record bepul toString, equals va hashCode berdi; ixcham
konstruktor tekshiruvni kelib chiqish joyida bajardi.
2-qadam. Ombor interfeysi #
Saqlash usulini interfeys ortiga yashiramiz (10-bo'lim) - shunda keyin fayl o'rniga bazaga o'tish oson bo'ladi:
import java.util.*;
public class Ombor {
public static void main(String[] args) {
JurnalOmbori ombor = new XotiraOmbori();
ombor.talabaQosh(new Talaba(1, "Husanboy", "A"));
ombor.talabaQosh(new Talaba(2, "Malika", "B"));
ombor.bahoQosh(new Baho(1, "Matematika", 92));
System.out.println("Talabalar: " + ombor.talabalar().size());
System.out.println("Baholar: " + ombor.baholar().size());
try {
ombor.talabaQosh(new Talaba(1, "Boshqa odam", "B"));
} catch (IllegalArgumentException x) {
System.out.println("Xato: " + x.getMessage());
}
try {
ombor.talabalar().add(new Talaba(3, "Nodira", "A"));
} catch (UnsupportedOperationException x) {
System.out.println("Tashqaridan o'zgartirib bo'lmaydi");
}
}
}
interface JurnalOmbori {
void talabaQosh(Talaba talaba);
void bahoQosh(Baho baho);
List<Talaba> talabalar();
List<Baho> baholar();
}
class XotiraOmbori implements JurnalOmbori {
private final List<Talaba> talabalar = new ArrayList<>();
private final List<Baho> baholar = new ArrayList<>();
@Override
public void talabaQosh(Talaba talaba) {
boolean bor = talabalar.stream()
.anyMatch(t -> t.id() == talaba.id());
if (bor) {
throw new IllegalArgumentException(talaba.id() + " raqami band");
}
talabalar.add(talaba);
}
@Override
public void bahoQosh(Baho baho) {
baholar.add(baho);
}
@Override
public List<Talaba> talabalar() {
return Collections.unmodifiableList(talabalar);
}
@Override
public List<Baho> baholar() {
return Collections.unmodifiableList(baholar);
}
}
record Talaba(int id, String ism, String guruh) {
}
record Baho(int talabaId, String fan, int qiymat) {
}
Talabalar: 2
Baholar: 1
Xato: 1 raqami band
Tashqaridan o'zgartirib bo'lmaydi
unmodifiableList nega kerakAgar ichki ArrayList ni to'g'ridan-to'g'ri qaytarsak, chaqiruvchi
kod unga element qo'shishi mumkin - ya'ni talabaQosh dagi
takroriylik tekshiruvini chetlab o'tadi.
Collections.unmodifiableList faqat o'qishga ruxsat beruvchi
ko'rinish qaytaradi. Ma'lumot bir xil, lekin uni o'zgartirish yo'li
faqat sinf metodlari orqali qoladi.
Bu 9-bo'limdagi inkapsulyatsiya qoidasining amaliy ko'rinishi.
3-qadam. Hisobotlar #
Hisoblashlar Stream API bilan (16-bo'lim):
import java.util.*;
import java.util.stream.*;
public class Reyting {
static final List<Talaba> TALABALAR = List.of(
new Talaba(1, "Husanboy Qodirov", "A"),
new Talaba(2, "Malika Tursunova", "B"),
new Talaba(3, "Nodira Saidova", "A"),
new Talaba(4, "Aziza Rahimova", "B"));
static final List<Baho> BAHOLAR = List.of(
new Baho(1, "Matematika", 92), new Baho(1, "Fizika", 85),
new Baho(2, "Matematika", 78), new Baho(2, "Fizika", 88),
new Baho(3, "Matematika", 65), new Baho(3, "Fizika", 72),
new Baho(4, "Matematika", 95), new Baho(4, "Fizika", 91));
public static void main(String[] args) {
List<Yozuv> reyting = reyting();
System.out.println("Reyting:");
for (Yozuv y : reyting) {
System.out.printf(Locale.ROOT, " %-18s %s %5.1f%n",
y.ism(), y.guruh(), y.ortacha());
}
Map<String, Double> guruhlar = reyting.stream()
.collect(Collectors.groupingBy(Yozuv::guruh, TreeMap::new,
Collectors.averagingDouble(Yozuv::ortacha)));
System.out.println("\nGuruhlar:");
guruhlar.forEach((g, o) ->
System.out.printf(Locale.ROOT, " %s: %.1f%n", g, o));
Map<String, Double> fanlar = BAHOLAR.stream()
.collect(Collectors.groupingBy(Baho::fan, TreeMap::new,
Collectors.averagingInt(Baho::qiymat)));
System.out.println("\nFanlar:");
fanlar.forEach((f, o) ->
System.out.printf(Locale.ROOT, " %-12s %.1f%n", f, o));
double umumiy = BAHOLAR.stream()
.mapToInt(Baho::qiymat)
.average()
.orElse(0);
System.out.printf(Locale.ROOT, "%nUmumiy o'rtacha: %.2f%n", umumiy);
}
static List<Yozuv> reyting() {
return TALABALAR.stream()
.map(t -> new Yozuv(t.ism(), t.guruh(), ortacha(t.id())))
.sorted(Comparator.comparingDouble(Yozuv::ortacha).reversed())
.toList();
}
static double ortacha(int talabaId) {
return BAHOLAR.stream()
.filter(b -> b.talabaId() == talabaId)
.mapToInt(Baho::qiymat)
.average()
.orElse(0);
}
}
record Talaba(int id, String ism, String guruh) {
}
record Baho(int talabaId, String fan, int qiymat) {
}
record Yozuv(String ism, String guruh, double ortacha) {
}
Reyting:
Aziza Rahimova B 93.0
Husanboy Qodirov A 88.5
Malika Tursunova B 83.0
Nodira Saidova A 68.5
Guruhlar:
A: 78.5
B: 88.0
Fanlar:
Fizika 84.0
Matematika 82.5
Umumiy o'rtacha: 83.25
Locale.ROOT va TreeMap::newIkkala tafsilot ham ataylab qo'yilgan:
Locale.ROOT-printfsonni madaniyatdan qat'i nazar bir xil chiqarsin (4-bo'lim). Usiz rus tiliga sozlangan tizimda93,0chiqardi;TreeMap::new-groupingBysukut bo'yichaHashMapqaytaradi va uning tartibi bashoratli emas (12 va 16-bo'limlar).
Bu ikki qoida darslik davomida takrorlangan edi. Amaliy loyihada ular bir joyda uchradi - shuning uchun ularni eslab qolish muhim.
4-qadam. Faylga saqlash #
CSV ko'rinishida saqlaymiz (18-bo'lim):
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.*;
import java.util.stream.*;
public class FaylgaSaqlash {
public static void main(String[] args) throws IOException {
Path yol = Files.createTempFile("jurnal", ".csv");
List<Talaba> asl = List.of(
new Talaba(1, "Husanboy", "A"),
new Talaba(2, "Malika", "B"));
saqla(yol, asl);
List<Talaba> oqilgan = yukla(yol);
System.out.println("Saqlandi va o'qildi: " + oqilgan.size() + " ta");
oqilgan.forEach(t -> System.out.println(" " + t));
System.out.println("Asli bilan teng: " + asl.equals(oqilgan));
Files.delete(yol);
System.out.println("Yo'q fayldan: " + yukla(yol).size() + " ta");
}
static void saqla(Path yol, List<Talaba> talabalar) throws IOException {
List<String> qatorlar = new ArrayList<>();
qatorlar.add("id,ism,guruh");
for (Talaba t : talabalar) {
qatorlar.add(t.id() + "," + t.ism() + "," + t.guruh());
}
Files.write(yol, qatorlar, StandardCharsets.UTF_8);
}
static List<Talaba> yukla(Path yol) throws IOException {
if (!Files.exists(yol)) {
return List.of();
}
try (Stream<String> qatorlar =
Files.lines(yol, StandardCharsets.UTF_8)) {
return qatorlar
.skip(1)
.filter(q -> !q.isBlank())
.map(q -> q.split(","))
.map(q -> new Talaba(Integer.parseInt(q[0]), q[1], q[2]))
.toList();
}
}
}
record Talaba(int id, String ism, String guruh) {
}
Saqlandi va o'qildi: 2 ta
Talaba[id=1, ism=Husanboy, guruh=A]
Talaba[id=2, ism=Malika, guruh=B]
Asli bilan teng: true
Yo'q fayldan: 0 ta
"Asli bilan teng: true" - bu record ning qiymat bo'yicha
tenglik xususiyati (11-bo'lim). Oddiy class bo'lganida false
chiqardi, chunki bular turli obyektlar.
"Yo'q fayldan: 0 ta" - fayl mavjud emasligi xato emas. Dastur
birinchi marta ishga tushganda fayl hali yo'q. null emas, bo'sh
ro'yxat qaytariladi (17-bo'lim) - shunda chaqiruvchi kod hech narsa
tekshirmasdan davom etadi.
5-qadam. Xatolarni boshqarish #
import java.util.*;
public class Xatolar {
public static void main(String[] args) {
Jurnal jurnal = new Jurnal();
qosh(jurnal, 1, "Husanboy", "A");
qosh(jurnal, 1, "Boshqa odam", "B");
qosh(jurnal, 2, "", "A");
qosh(jurnal, 3, "Malika", "B");
System.out.println("\nNatijada " + jurnal.soni() + " ta talaba");
}
static void qosh(Jurnal j, int id, String ism, String guruh) {
try {
j.qosh(id, ism, guruh);
System.out.println("OK: " + id + " - " + ism);
} catch (IllegalArgumentException x) {
System.out.println("XATO: " + x.getMessage());
}
}
}
class Jurnal {
private final Map<Integer, String> talabalar = new LinkedHashMap<>();
int soni() {
return talabalar.size();
}
void qosh(int id, String ism, String guruh) {
Objects.requireNonNull(ism, "ism null bo'lmasin");
if (ism.isBlank()) {
throw new IllegalArgumentException(
"ism bo'sh bo'lmasin (id=" + id + ")");
}
if (talabalar.containsKey(id)) {
throw new IllegalArgumentException(id + " raqami band");
}
talabalar.put(id, ism.strip() + " (" + guruh + ")");
}
}
OK: 1 - Husanboy
XATO: 1 raqami band
XATO: ism bo'sh bo'lmasin (id=2)
OK: 3 - Malika
Natijada 2 ta talaba
Xato bo'lgan yozuvlar o'tkazib yuborildi, dastur esa to'xtamadi (13-bo'lim).
6-qadam. Testlar #
Mantiqni sinovdan o'tkazamiz (19-bo'lim):
public class Testlar {
private static int otdi = 0;
private static int yiqildi = 0;
public static void main(String[] args) {
teng(Daraja.ALO, Daraja.aniqla(95), "95 ball");
teng(Daraja.ALO, Daraja.aniqla(90), "aynan 90 - chegara");
teng(Daraja.YAXSHI, Daraja.aniqla(89.9), "chegaradan bir oz past");
teng(Daraja.YAXSHI, Daraja.aniqla(75), "aynan 75");
teng(Daraja.QONIQARLI, Daraja.aniqla(60), "aynan 60");
teng(Daraja.QONIQARSIZ, Daraja.aniqla(59.9), "60 dan past");
teng(Daraja.QONIQARSIZ, Daraja.aniqla(0), "nol ball");
yakun();
}
static void teng(Object kutilgan, Object haqiqiy, String nom) {
if (java.util.Objects.equals(kutilgan, haqiqiy)) {
otdi++;
System.out.println(" OK " + nom);
} else {
yiqildi++;
System.out.println(" XATO " + nom
+ ": " + kutilgan + " != " + haqiqiy);
}
}
static void yakun() {
System.out.println();
System.out.println("O'tdi: " + otdi + ", yiqildi: " + yiqildi);
}
}
enum Daraja {
QONIQARSIZ(0), QONIQARLI(60), YAXSHI(75), ALO(90);
private final int engKam;
Daraja(int engKam) {
this.engKam = engKam;
}
static Daraja aniqla(double ball) {
Daraja natija = QONIQARSIZ;
for (Daraja d : values()) {
if (ball >= d.engKam) {
natija = d;
}
}
return natija;
}
}
OK 95 ball
OK aynan 90 - chegara
OK chegaradan bir oz past
OK aynan 75
OK aynan 60
OK 60 dan past
OK nol ball
O'tdi: 7, yiqildi: 0
Testlarning yarmi chegaraviy qiymatlar - aynan 90, 75, 60 va ulardan bir oz past. Xato odatda shu yerda bo'ladi.
7-qadam. To'liq dastur #
Hammasini birlashtiramiz:
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.*;
import java.util.stream.*;
public class Main {
public static void main(String[] args) throws IOException {
Path yol = Files.createTempFile("jurnal-yakuniy", ".csv");
Jurnal jurnal = new Jurnal();
jurnal.talabaQosh(new Talaba(1, "Husanboy Qodirov", "A"));
jurnal.talabaQosh(new Talaba(2, "Malika Tursunova", "B"));
jurnal.talabaQosh(new Talaba(3, "Nodira Saidova", "A"));
jurnal.talabaQosh(new Talaba(4, "Aziza Rahimova", "B"));
int[][] ballar = {{92, 85}, {78, 88}, {65, 72}, {95, 91}};
for (int i = 0; i < ballar.length; i++) {
jurnal.bahoQosh(new Baho(i + 1, "Matematika", ballar[i][0]));
jurnal.bahoQosh(new Baho(i + 1, "Fizika", ballar[i][1]));
}
jurnal.saqla(yol);
Jurnal oqilgan = Jurnal.yukla(yol);
System.out.println("=== TALABALAR JURNALI ===");
System.out.println("Fayldan o'qildi: "
+ oqilgan.talabalar().size() + " talaba, "
+ oqilgan.baholar().size() + " baho");
System.out.println("\nReyting:");
for (Yozuv y : oqilgan.reyting()) {
System.out.printf(Locale.ROOT, " %-18s %s %5.1f %s%n",
y.ism(), y.guruh(), y.ortacha(),
Daraja.aniqla(y.ortacha()));
}
System.out.println("\nGuruhlar:");
oqilgan.guruhlar().forEach((g, o) ->
System.out.printf(Locale.ROOT, " %s: %.1f%n", g, o));
System.out.printf(Locale.ROOT, "%nUmumiy o'rtacha: %.2f%n",
oqilgan.umumiyOrtacha());
System.out.println("A'lochilar soni: " + oqilgan.reyting().stream()
.filter(y -> Daraja.aniqla(y.ortacha()) == Daraja.ALO)
.count());
Files.delete(yol);
}
}
record Talaba(int id, String ism, String guruh) {
}
record Baho(int talabaId, String fan, int qiymat) {
}
record Yozuv(String ism, String guruh, double ortacha) {
}
enum Daraja {
QONIQARSIZ(0), QONIQARLI(60), YAXSHI(75), ALO(90);
private final int engKam;
Daraja(int engKam) {
this.engKam = engKam;
}
static Daraja aniqla(double ball) {
Daraja natija = QONIQARSIZ;
for (Daraja d : values()) {
if (ball >= d.engKam) {
natija = d;
}
}
return natija;
}
}
class Jurnal {
private final List<Talaba> talabalar = new ArrayList<>();
private final List<Baho> baholar = new ArrayList<>();
void talabaQosh(Talaba t) {
Objects.requireNonNull(t, "talaba null bo'lmasin");
if (talabalar.stream().anyMatch(x -> x.id() == t.id())) {
throw new IllegalArgumentException(t.id() + " raqami band");
}
talabalar.add(t);
}
void bahoQosh(Baho b) {
baholar.add(Objects.requireNonNull(b));
}
List<Talaba> talabalar() {
return Collections.unmodifiableList(talabalar);
}
List<Baho> baholar() {
return Collections.unmodifiableList(baholar);
}
List<Yozuv> reyting() {
return talabalar.stream()
.map(t -> new Yozuv(t.ism(), t.guruh(), ortacha(t.id())))
.sorted(Comparator.comparingDouble(Yozuv::ortacha).reversed())
.toList();
}
Map<String, Double> guruhlar() {
return reyting().stream()
.collect(Collectors.groupingBy(Yozuv::guruh, TreeMap::new,
Collectors.averagingDouble(Yozuv::ortacha)));
}
double umumiyOrtacha() {
return baholar.stream().mapToInt(Baho::qiymat).average().orElse(0);
}
private double ortacha(int talabaId) {
return baholar.stream()
.filter(b -> b.talabaId() == talabaId)
.mapToInt(Baho::qiymat)
.average()
.orElse(0);
}
void saqla(Path yol) throws IOException {
List<String> qatorlar = new ArrayList<>();
qatorlar.add("tur,a,b,c");
for (Talaba t : talabalar) {
qatorlar.add("T," + t.id() + "," + t.ism() + "," + t.guruh());
}
for (Baho b : baholar) {
qatorlar.add("B," + b.talabaId() + ","
+ b.fan() + "," + b.qiymat());
}
Files.write(yol, qatorlar, StandardCharsets.UTF_8);
}
static Jurnal yukla(Path yol) throws IOException {
Jurnal j = new Jurnal();
if (!Files.exists(yol)) {
return j;
}
try (Stream<String> qatorlar =
Files.lines(yol, StandardCharsets.UTF_8)) {
List<String> tozalar =
qatorlar.skip(1).filter(s -> !s.isBlank()).toList();
for (String q : tozalar) {
String[] p = q.split(",");
if (p[0].equals("T")) {
j.talabaQosh(
new Talaba(Integer.parseInt(p[1]), p[2], p[3]));
} else {
j.bahoQosh(new Baho(Integer.parseInt(p[1]), p[2],
Integer.parseInt(p[3])));
}
}
}
return j;
}
}
=== TALABALAR JURNALI ===
Fayldan o'qildi: 4 talaba, 8 baho
Reyting:
Aziza Rahimova B 93.0 ALO
Husanboy Qodirov A 88.5 YAXSHI
Malika Tursunova B 83.0 YAXSHI
Nodira Saidova A 68.5 QONIQARLI
Guruhlar:
A: 78.5
B: 88.0
Umumiy o'rtacha: 83.25
A'lochilar soni: 1
Nimalar ishlatildi #
| Bo'lim | Loyihada qayerda |
|---|---|
| 3 | double va int tanlovi, aniqlik |
| 4 | Locale.ROOT bilan formatlash, strip() |
| 5 | switch va enum |
| 8 | static va obyekt metodlari |
| 9 | Inkapsulyatsiya, unmodifiableList |
| 10 | JurnalOmbori interfeysi |
| 11 | record, ixcham konstruktor, enum |
| 12 | List, Map, TreeMap |
| 13 | IllegalArgumentException, try/catch |
| 16 | stream, filter, map, groupingBy, averagingDouble |
| 17 | Objects.requireNonNull, bo'sh ro'yxat qaytarish |
| 18 | Path, Files, try-with-resources, UTF-8 |
| 19 | Chegaraviy testlar |
Keyingi qadamlar #
Eng yaxshi mashq - shu dasturga yangi imkoniyat qo'shish:
- Talabani o'chirish va tahrirlash;
- Chorak tushunchasi - baholarni vaqt bo'yicha ajratish;
- Konsol menyusi -
Scannervawhile (true)bilan; - JSON ga eksport - Jackson yoki Gson kutubxonasi bilan;
JurnalOmborining SQLite varianti - bu saytdagi SQL va MySQL darsligi bilan birga.
Har bir qo'shimcha uchun avval test yozing, keyin kodni.
Java asoslari tugadi. Yo'nalishlar:
- Veb backend - Spring Boot; bu Java dunyosidagi eng katta yo'nalish. Avval bu saytdagi SQL va MySQL darsligini o'ting;
- Android - Android SDK va Kotlin; Java bilimingiz to'g'ridan-to'g'ri ishlaydi;
- Qurish vositalari - Maven va Gradle ni chuqurroq;
- Arxitektura - Dizayn naqshlari va Dasturiy ta'minot muhandisligi darsliklari;
- Algoritmlar - Algoritmlar va ma'lumotlar tuzilmasi darsligi; suhbatlarda eng ko'p so'raladigan mavzu.
Boshqa til ko'rmoqchi bo'lsangiz, bu saytda C# darsligi ham bor - u Java'ga juda o'xshash va farqlarni solishtirish foydali.
- Maven bilan
jurnalloyihasini yarating. Talaba,Bahorecord'larini vaDarajaenum'ini yozing.- Ixcham konstruktorda tekshiruv qo'shing.
JurnalOmboriinterfeysini va xotira variantini yozing.unmodifiableListbilan tashqi o'zgartirishni to'sing.- Stream API bilan reyting hisobotini chiqaring.
groupingByvaTreeMap::newbilan guruh hisobotini qo'shing.- Ma'lumotni CSV ga saqlang va qayta o'qing.
Daraja.aniqlauchun chegaraviy testlar yozing.- Barcha sonlarni
Locale.ROOTbilan chiqaring.
Xulosa #
- Ma'lumot turlari uchun
record, mantiq uchunclass. - Tekshiruvni ixcham konstruktorda, kelib chiqish joyida bajaring.
- Saqlash usulini interfeys ortiga yashiring.
- Tashqariga
unmodifiableListqaytaring, ichki ro'yxatni emas. - Hisobotlarni Stream API bilan yozing.
- Sonlarni
Locale.ROOT, guruhlarniTreeMap::newbilan chiqaring - natija hamma joyda bir xil bo'lsin. - Fayl yo'q bo'lishi xato emas - bo'sh natija qaytaring.
- Fayl bilan ishlaganda UTF-8 ni oshkora ko'rsating.
- Testlarni chegaraviy qiymatlarga yo'naltiring.
Darslik tugadi. Endi sizda ishlaydigan Java dasturi va uni kengaytirish uchun yetarli bilim bor. Omad!
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.