Wyniki egzaminu

Informacje o egzaminie:
  • Zawód: Technik programista
  • Kwalifikacja: INF.04 - Projektowanie, programowanie i testowanie aplikacji
  • Data rozpoczęcia: 8 grudnia 2025 11:56
  • Data zakończenia: 8 grudnia 2025 12:14

Egzamin niezdany

Wynik: 18/40 punktów (45,0%)

Wymagane minimum: 20 punktów (50%)

Udostępnij swój wynik
Szczegółowe wyniki:
Pytanie 1

Jakie informacje można uzyskać na temat metod w klasie Point?

public class Point {
    public void Move(int x, int y) {...}
    public void Move(int x, int y, int z) {...}
    public void Move(Point newPt) {...}
}
A. Zawierają przeciążenie operatora.
B. Służą jako konstruktory w zależności od liczby argumentów.
C. Są przeciążone.
D. Zawierają błąd, ponieważ nazwy metod powinny być różne.
Metody przeciążone mogą mieć tę samą nazwę – różnią się sygnaturą, czyli liczbą i typami argumentów, co jest zgodne z zasadami programowania obiektowego. Przeciążenie metod to nie to samo co przeciążenie operatorów, które polega na definiowaniu nowego sposobu działania operatorów (np. +, -, *). Pełnienie funkcji konstruktorów to inny mechanizm i dotyczy głównie klas i ich instancjonowania, nie przeciążania metod.

Pytanie 2

Podczas programowania kontrolki stepper przedstawionej na ilustracji w aplikacji mobilnej, należy zarządzać zmienną, która zawsze przechowuje jej bieżącą wartość. Jakie zdarzenie można wykorzystać do osiągnięcia tej funkcjonalności?

Ilustracja do pytania
A. Unfocused
B. SizeChanged
C. ValueChanged
D. DescendantAdded
Zdarzenie ValueChanged jest kluczowe w kontekście programowania kontrolek takich jak stepper w aplikacjach mobilnych. To zdarzenie jest wywoływane zawsze, gdy wartość kontrolki zostaje zmieniona przez użytkownika, co umożliwia natychmiastowe przetwarzanie tej zmiany i aktualizację interfejsu użytkownika lub innych powiązanych komponentów. W praktyce, użycie zdarzenia ValueChanged to dobry przykład reaktywnego programowania, gdzie aplikacja reaguje na akcje użytkownika w czasie rzeczywistym. Przy implementacji takiego zdarzenia należy zadbać o poprawne sprawdzanie zakresu wartości, aby uniknąć błędów logicznych. Warto również pamiętać o optymalizacji wydajności takiej obsługi, zwłaszcza w aplikacjach złożonych z wielu komponentów zależnych od wartości steppera. Praktyczne zastosowanie tego zdarzenia można znaleźć w aplikacjach e-commerce, gdzie steppery mogą być używane do wyboru ilości produktów w koszyku, a zmiana wartości natychmiast wpływa na obliczenie ceny całkowitej. Używanie zdarzeń takich jak ValueChanged jest zgodne z dobrymi praktykami projektowania interfejsów użytkownika, poprawiając ich responsywność i interaktywność.

Pytanie 3

Które z wymienionych opcji wspiera osoby niewidome w korzystaniu z witryn internetowych?

A. Ograniczenie liczby grafik na stronie
B. Zmiana rozdzielczości ekranu
C. Umożliwienie modyfikacji czcionki
D. Implementacja czytnika ekranu (screen reader)
Dodanie czytnika ekranu (screen reader) jest kluczowym rozwiązaniem, które znacząco ułatwia osobom niewidomym i słabowidzącym korzystanie z serwisów internetowych. Czytniki ekranu to oprogramowanie przekształcające tekst na stronie internetowej na mowę, co pozwala użytkownikom na interakcję z treścią dostępną w internecie. Technologia ta opiera się na standardach dostępności, takich jak WCAG (Web Content Accessibility Guidelines), które zalecają projektowanie stron przyjaznych dla osób z różnymi niepełnosprawnościami. Przykładem działania czytnika ekranu może być program JAWS, który umożliwia użytkownikom nawigację po stronach internetowych poprzez komendy klawiaturowe oraz odczytywanie treści na głos. Dzięki czytnikom ekranu, osoby niewidome mają możliwość dostępu do informacji, komunikacji oraz interakcji w sieci, co wpisuje się w ideę cyfrowej inkluzji i równości szans. Wprowadzenie czytnika ekranu na stronie internetowej to nie tylko techniczne wsparcie, ale również wyraz odpowiedzialności społecznej, mający na celu zapewnienie, że wszyscy użytkownicy mają równe prawo do korzystania z zasobów w sieci.

Pytanie 4

Jakie jest przeznaczenie polecenia "git merge"?

A. Do zakładania nowego repozytorium
B. Do pobierania aktualizacji zdalnego repozytorium
C. Do łączenia zmian z różnych gałęzi
D. Do usuwania zmian w repozytorium
Polecenie "git merge" służy w Git do łączenia zmian z różnych gałęzi. Kiedy pracujemy w zespole i każdy programista rozwija swój fragment kodu na osobnej gałęzi, w pewnym momencie trzeba te zmiany zebrać do kupy, żeby powstała jedna, wspólna wersja projektu. Tu właśnie pojawia się "merge" – pozwala w prosty sposób dołączyć zmiany z jednej gałęzi do drugiej, najczęściej z feature branch do develop albo main. Praktycznie rzecz biorąc, to polecenie sprawdza się zawsze wtedy, gdy chcemy zintegrować efekty pracy kilku osób lub wersje rozwojowe z główną linią kodu. Moim zdaniem, korzystanie z "git merge" to w zasadzie codzienność w projektach zespołowych, bo prawie nikt już nie pracuje tylko na jednej gałęzi. Warto też pamiętać, że merge może czasem prowadzić do konfliktów, jeśli te same fragmenty plików były zmieniane równolegle – wtedy trzeba ręcznie rozwiązać te rozbieżności. W praktyce, dobrą praktyką jest regularne mergowanie, żeby uniknąć lawiny konfliktów na koniec sprintu. Dla mnie "merge" to narzędzie absolutnie kluczowe, bez którego ciężko sobie wyobrazić sensowną pracę z Gitem. No i jeszcze – to nie to samo co "rebase", chociaż oba służą do integracji zmian, ale w różny sposób. Merge zostawia historię połączeń, co ułatwia śledzenie zmian w większych projektach.

Pytanie 5

Co to jest REST API?

A. Architektura API oparta o zasoby i standardowe operacje HTTP
B. Biblioteka JavaScript do komunikacji z bazami danych
C. Framework do testowania API
D. Protokół sieciowy do transferu danych binarnych
Odpowiedzi wskazujące na frameworki do testowania API, protokoły sieciowe oraz biblioteki JavaScript do komunikacji z bazami danych są mylące, ponieważ nie oddają istoty REST API. Framework do testowania API, choć istotny w procesie tworzenia aplikacji, nie ma nic wspólnego z architekturą REST, która dotyczy sposobu projektowania API opartego na zasobach. Z kolei protokół sieciowy do transferu danych binarnych nie ma zastosowania w kontekście REST API, które używa HTTP jako swojego protokołu komunikacyjnego, a także preferuje formaty tekstowe, takie jak JSON czy XML, do wymiany danych. Ostatnia koncepcja, dotycząca bibliotek JavaScript, myli pojęcia, ponieważ REST API nie jest narzędziem do komunikacji z bazami danych, lecz sposobem na interakcję między różnymi systemami. Typowym błędem myślowym jest utożsamienie REST API z jedną konkretną technologią lub narzędziem, podczas gdy w rzeczywistości jest to zestaw zasad i praktyk dotyczących projektowania API. Kluczowe jest zrozumienie, że REST API to podejście, które może być implementowane w różnych językach i technologiach, a jego celem jest stworzenie prostego, elastycznego i wydajnego interfejsu do komunikacji z zasobami.

Pytanie 6

Po uruchomieniu poniższego kodu w języku C++ na konsoli zobaczymy następujący tekst:

#include <stdio.h>
int main() {
    int a = 5;
    float b = 5.12345;
    double w;
    w = a + b;
    printf("%s dodawania: %d + %.2f = %f", "Wynik", a, b, w);
    return 0;
}
A. dodawania: 5+5.12345=10.123450 Wynik
B. "%s dodawania: %d + %.2f = %f", "Wynik", 5, 5.12345, 10.123450
C. Wynik dodawania: 5+5.12=10.123450
D. "%s dodawania: %d + %.2f=%f", "Wynik", a, b, w
Wiele osób daje się złapać na nieco podchwytliwe formatowanie tekstu i nie do końca rozumie mechanizm działania funkcji printf w języku C++. Kluczowe jest, że printf nie wypisuje dosłownie wszystkiego, co znajduje się w cudzysłowie, tylko podmienia tzw. specyfikatory formatu na konkretne wartości przekazane po przecinku. Próby dosłownego przepisania wywołania funkcji printf jako wyjścia na ekran są mylące – program nie drukuje ""%s dodawania: %d + %.2f = %f"", tylko zamienia to na odpowiednie liczby i teksty. Równie powszechnym błędem jest niedopasowanie precyzji – np. wypisanie 5.12345 zamiast 5.12 przy %.2f, bo printf automatycznie zaokrągla wynik do dwóch miejsc po przecinku w tym miejscu. Część osób pomija też fakt, że argumenty w printf muszą być przekazane w tej samej kolejności, co specyfikatory – co jest bardzo istotne przy większej liczbie zmiennych. Należy zwrócić szczególną uwagę na to, jak typy danych są prezentowane i obsługiwane przez printf: %d to zawsze liczba całkowita, %.2f – liczba zmiennoprzecinkowa dokładnie z dwoma cyframi po przecinku, %f – domyślnie sześć miejsc po przecinku. W praktyce niepoprawne jest zapisywanie wywołania funkcji jako tekst do wyświetlenia albo przestawianie kolejności czy branie wartości z pamięci bez sprawdzenia ich formatu. Z mojego doświadczenia takie potknięcia wynikają raczej z braku praktyki z printf i nieuważnego czytania dokumentacji. Warto zawsze sprawdzać, jak formatowanie będzie wyglądać na wyjściu i testować fragmenty kodu – bo komputer nie domyśli się, co autor miał na myśli, tylko wykona dokładnie to, co zadeklarowano w kodzie.

Pytanie 7

Jakie ma znaczenie "operacja wejścia" w kontekście programowania?

A. Przekazywanie danych do programu z zewnętrznych źródeł
B. Zmiana wartości zmiennych globalnych
C. Naprawianie błędów w kodzie aplikacji
D. Wprowadzanie nowych funkcji do aplikacji
Operacja wejścia w programowaniu polega na przekazywaniu danych do programu z zewnętrznego źródła, takiego jak klawiatura, plik lub strumień danych. W języku C++ typowym przykładem operacji wejścia jest `cin >> zmienna;`, która pobiera dane od użytkownika i przypisuje je do zmiennej. Operacje wejścia są niezbędne w interaktywnych aplikacjach, które wymagają danych od użytkownika w czasie rzeczywistym, umożliwiając dynamiczne przetwarzanie informacji.

Pytanie 8

Co to jest lazy loading w kontekście aplikacji webowych?

A. Metoda kompresji obrazów na stronach internetowych
B. Technika przechowywania danych w pamięci podręcznej przeglądarki
C. Strategia optymalizacji, która opóźnia ładowanie zasobów do momentu, gdy są faktycznie potrzebne
D. Narzędzie do testowania wydajności ładowania strony
Lazy loading to technika optymalizacji wydajności, która polega na opóźnieniu ładowania zasobów, takich jak obrazy, filmy czy skrypty, do momentu, gdy są one rzeczywiście potrzebne. Dzięki temu zwiększa się szybkość ładowania strony, co jest szczególnie ważne w kontekście doświadczeń użytkowników i SEO. Przykładem zastosowania lazy loading może być strona z długą listą produktów, gdzie obrazy dla produktów znajdujących się poza ekranem są ładowane dopiero, gdy użytkownik przewinie stronę w dół. Praktyka ta nie tylko poprawia czas reakcji strony, lecz także redukuje zużycie pasma, co jest korzystne dla użytkowników na urządzeniach mobilnych. Stosując lazy loading, warto pamiętać o dobrych praktykach, takich jak użycie odpowiednich bibliotek JavaScript, które wspierają tę technikę, oraz zapewnienie odpowiednich fallbacków dla starszych przeglądarek. Wprowadzenie lazy loading jest zgodne z zaleceniami optymalizacji wydajności publikowanymi przez Google, które podkreślają znaczenie ładowania tylko niezbędnych zasobów i poprawę UX.

Pytanie 9

Jaką wartość jest w stanie przechować tablica jednowymiarowa?

A. Jedną wartość
B. Wiele wartości pod różnymi indeksami
C. Wiele wartości pod tym samym indeksem
D. Wartość logiczną true lub false
Tablica nie przechowuje tylko jednej wartości – od tego są typy proste, takie jak 'int' czy 'float'. Tablica nie przechowuje wielu wartości pod jednym indeksem – każda wartość zajmuje osobny indeks. Wartości logiczne, takie jak 'true' lub 'false', mogą być elementami tablicy, ale sama tablica nie ogranicza się do przechowywania tylko tych wartości. Kluczową cechą tablicy jest możliwość przechowywania wielu wartości, do których można odwoływać się za pomocą indeksów, co odróżnia je od typów pojedynczych wartości.

Pytanie 10

Który aspekt projektu aplikacji jest kluczowy dla zabezpieczenia danych użytkowników?

A. Koncentracja na wyglądzie interfejsu użytkownika
B. Tworzenie prostych formularzy do rejestracji
C. Zastosowanie zaawansowanych systemów ochrony
D. Pominięcie testowania aplikacji w etapie produkcji
Stosowanie zaawansowanych mechanizmów bezpieczeństwa jest kluczowe dla ochrony danych użytkowników w aplikacjach. Wdrożenie technologii takich jak szyfrowanie, autoryzacja dwuskładnikowa (2FA), firewalle oraz regularne testy penetracyjne pozwala na minimalizację ryzyka ataków i naruszeń danych. Mechanizmy te nie tylko zabezpieczają dane, ale również budują zaufanie użytkowników do aplikacji, co jest niezwykle istotne w branżach takich jak bankowość, medycyna czy e-commerce.

Pytanie 11

Jaką cechą charakteryzuje się sieć asynchroniczna?

A. Dane są przesyłane w sposób nieciągły, bez synchronizacji zegarów
B. Dane są przesyłane jedynie w określonych przedziałach czasowych
C. Wymaga synchronizacji zegarów
D. Jest bardziej niezawodna od sieci synchronicznej
W pierwszej z niepoprawnych odpowiedzi wskazano, że sieci asynchroniczne wymagają synchronizacji zegarów. To stwierdzenie jest błędne, ponieważ sama definicja sieci asynchronicznych opiera się na braku konieczności synchronizacji zegarów. W sieciach tego typu, każdy element systemu działa niezależnie, co umożliwia bardziej elastyczne przesyłanie danych w porównaniu do sieci synchronicznych. W drugiej niepoprawnej odpowiedzi zasugerowano, że dane są przesyłane tylko w ustalonych ramach czasowych. Jest to charakterystyczne dla sieci synchronicznych, gdzie transmisja danych jest ściśle zorganizowana i wymaga precyzyjnego harmonogramu. W sieciach asynchronicznych, dane mogą być wysyłane w dowolnym momencie, co pozwala na efektywniejsze wykorzystanie pasma. Kolejna niepoprawna odpowiedź sugeruje, że sieci asynchroniczne są bardziej niezawodne niż sieci synchroniczne. Chociaż sieci asynchroniczne mają swoje zalety, jak większa elastyczność, nie można jednoznacznie stwierdzić, że są one bardziej niezawodne. Niezawodność sieci zależy od wielu czynników, takich jak jakość urządzeń, protokoły transmisji oraz warunki pracy, a nie tylko od charakterystyki asynchronicznej czy synchronicznej.

Pytanie 12

Który framework jest powszechnie wykorzystywany do tworzenia aplikacji internetowych w języku Python?

A. React.js
B. Angular
C. ASP.NET Core
D. Django
Django to framework stworzony specjalnie do budowy aplikacji webowych w języku Python. Jest jednym z najbardziej popularnych i zaawansowanych frameworków typu full-stack, który oferuje szeroki wachlarz narzędzi umożliwiających szybkie i efektywne tworzenie aplikacji internetowych. Django pozwala na tworzenie aplikacji zgodnych z zasadą DRY (Don't Repeat Yourself), co oznacza minimalizację powtarzalnego kodu. Posiada wbudowany panel administracyjny, system ORM (Object-Relational Mapping) oraz zabezpieczenia przed atakami CSRF i XSS. Dzięki Django programiści mogą skupić się na rozwijaniu logiki biznesowej, a nie na konfiguracji podstawowych funkcji aplikacji, co znacznie skraca czas wdrożenia gotowego produktu.

Pytanie 13

Termin ryzyko zawodowe odnosi się do

A. zagrożenia wypadkowego, które może wystąpić w miejscu pracy
B. efektów zagrożeń wypadkowych, jakie występują w miejscu zatrudnienia
C. prawdopodobieństwa, że zdarzenia niepożądane związane z pracą spowodują straty, w szczególności negatywne skutki zdrowotne dla pracowników
D. ciężkości skutków niepożądanych zdarzeń związanych z pracą
W temacie ryzyka zawodowego nietrudno o pomyłki, bo wiele osób – zwłaszcza zaczynając naukę o BHP – utożsamia je po prostu z obecnością zagrożenia albo skutków wypadków. Takie myślenie prowadzi do uproszczeń, które mogą być niebezpieczne w praktyce. Samo zagrożenie wypadkowe to jeszcze nie ryzyko – to tylko potencjalne źródło szkody. Z kolei efekty zagrożeń, czyli np. liczba wypadków w danym miejscu, to już skutek, a nie samo ryzyko. Można by powiedzieć, że ktoś, kto patrzy tylko na efekty, spogląda wstecz, zamiast oceniać, co może się wydarzyć w przyszłości. Ciężkość skutków, choć ważna, to tylko jeden z elementów oceny ryzyka. Liczy się przecież także, z jakim prawdopodobieństwem wystąpi dane zdarzenie – i dopiero połączenie tych dwóch czynników pozwala mówić sensownie o ryzyku zawodowym. Z mojego punktu widzenia to typowy błąd, że myli się pojęcie zagrożenia z ryzykiem – w rzeczywistości samo występowanie niebezpieczeństwa nie oznacza natychmiastowego ryzyka dla każdego pracownika. Praktycy BHP zawsze podkreślają, że ryzyko zawodowe to ocena, czy dane zagrożenie przy określonych okolicznościach faktycznie może doprowadzić do szkody, i jak duże jest to prawdopodobieństwo. Bez tej analizy trudno planować skuteczne działania prewencyjne i w ogóle realnie podnosić poziom bezpieczeństwa. Warto też pamiętać, że zgodnie ze standardami międzynarodowymi (np. normą ISO 45001) ryzyko zawodowe zawsze dotyczy nie tylko skutków, ale przede wszystkim prawdopodobieństwa ich wystąpienia w praktyce – i to jest właśnie kluczowa różnica względem samego zagrożenia czy skutków wypadków.

Pytanie 14

Jaki typ złośliwego oprogramowania funkcjonuje w tle, zbierając dane o wprowadzanych hasłach?

A. Trojan
B. Spyware
C. Adware
D. Keylogger
Keylogger to takie złośliwe oprogramowanie, które działa w tle i rejestruje wszystkie naciśnięcia klawiszy. To naprawdę niebezpieczne, bo może przechwytywać hasła i inne ważne dane osobowe. Keyloggery mogą się dostawać na komputer na różne sposoby, na przykład przez zainfekowane pliki, złośliwe reklamy albo w ramach innych niebezpiecznych programów. Przykładami takich keyloggerów są Spyrix czy Perfect Keylogger, które mogą monitorować, co robisz na komputerze. Z punktu widzenia bezpieczeństwa, standardy takie jak ISO/IEC 27001 mówią, jak ważna jest ochrona informacji. Dlatego tak istotne jest, żeby użytkownicy mieli świadomość zagrożeń, jakie niosą keyloggery i używali dobrego oprogramowania antywirusowego, żeby chronić swoje urządzenia i dane.

Pytanie 15

Z podanej definicji pola licznik można wywnioskować, iż

class MojaKlasa
{
    private static int licznik;
    ...
A. bieżąca wartość pola jest wspólna dla wszystkich instancji klasy i nie może być zmieniana
B. bieżąca wartość pola jest wspólna dla wszystkich instancji klasy
C. pole nie może być zmieniane w kodzie klasy
D. pole jest związane z określoną instancją klasy i jego wartość jest unikalna tylko dla tej instancji
Warto się chwilę zatrzymać i przeanalizować, skąd biorą się błędne przekonania dotyczące pól statycznych. Bardzo częstym nieporozumieniem jest utożsamianie słowa static z czymś „niezmiennym” albo uniemożliwiającym modyfikacje. Tymczasem static oznacza tyle, że pole nie jest związane z pojedynczym obiektem, tylko z całą klasą – czyli wszystkie obiekty tej klasy dzielą jedną, wspólną wartość licznik. To nie jest tak, że static czyni pole stałym – żeby pole było niezmienne, potrzeba słowa kluczowego final (w Javie), a tutaj go ewidentnie nie ma. Często osoby początkujące mylą static z final i stąd pojawia się przekonanie, że pole nie może być modyfikowane – co nie jest prawdą. Równie błędne jest zakładanie, że każde pole, które nie jest static, jest automatycznie unikalne per instancja. Gdybyśmy usunęli static z definicji licznik, wtedy rzeczywiście każda instancja MojaKlasa miałaby swoją własną wersję tej zmiennej, ale w tym przypadku wszystkie obiekty współdzielą tę samą wartość. Jeszcze inny błąd to przekonanie, że pole prywatne (private) nie może być zmieniane w kodzie klasy – w rzeczywistości private ogranicza dostęp do pola tylko do wnętrza klasy, ale metody tej klasy mają pełne prawo je modyfikować. Tak więc, patrząc z perspektywy dobrych praktyk programistycznych i samej składni, static to po prostu cecha, która decyduje o zakresie współdzielenia pola pomiędzy instancjami, a nie o jego niezmienności czy widoczności na zewnątrz. Moim zdaniem kluczowe jest wyciągnięcie z tego wniosku, że static to współdzielenie, a nie blokada zmian – i na tym polega istota poprawnej odpowiedzi w tym pytaniu.

Pytanie 16

Jedną z zasad standardu WCAG 2.0 jest

A. używanie jednego, odpowiednio dużego rozmiaru czcionki
B. unikanie przedstawiania informacji w formie uproszczonej
C. ograniczanie treści na stronie przez rezygnację z używania alternatywnych tekstów dla obrazów i filmów
D. stosowanie różnych palet kolorystycznych, w tym o wysokim kontraście
Stosowanie różnych palet kolorystycznych, zwłaszcza takich z wysokim kontrastem, to naprawdę kluczowa sprawa w dostępności cyfrowej. Tak mówi standard WCAG 2.0, dokładniej zasada 1 – Postrzegalność. Chodzi tu głównie o kryterium sukcesu 1.4.3 (Kontrast – minimum), które wyraźnie określa, że tekst i treść graficzna muszą być czytelne dla osób z różnymi potrzebami, np. słabszym wzrokiem albo daltonizmem. Moim zdaniem warto sobie uświadomić, jak wielka to różnica. Przykładowo, jeśli tekst na stronie jest szary na białym tle, osoby starsze lub z wadami wzroku mogą po prostu nie być w stanie nic przeczytać. W praktyce, wysokokontrastowe kombinacje, np. biały tekst na czarnym tle lub na żółtym, bardzo poprawiają dostępność strony. Widać to choćby w trybach "high contrast" w systemach Windows – często osoby pracujące długo przy komputerze z tego korzystają, bo wzrok się mniej męczy. Branżowe dobre praktyki zalecają również nie polegać wyłącznie na kolorze podczas przekazywania informacji – przykładowo, jeśli błąd w formularzu jest oznaczony tylko na czerwono, osoba z daltonizmem tego nie zauważy. To wszystko pokazuje, że kolory i kontrast to nie tylko estetyka, ale klucz do równego dostępu. Z mojego doświadczenia, gdy projektuje się strony zgodnie z WCAG, dobrze jest testować różne ustawienia kontrastu albo korzystać z narzędzi jak Colour Contrast Analyser, żeby mieć pewność, że wszyscy użytkownicy skorzystają ze strony bez problemów.

Pytanie 17

Programista aplikacji mobilnych pragnie zmienić swoją ścieżkę kariery na Full-Stack Developera. Wskaż kurs, który powinien wybrać, żeby to osiągnąć?

A. Mastering Cross-platform Developping
B. Complete JavaScript React, SQL, Node.js Cource
C. Ultimate C# Serier from Beginner to Advanced
D. Raster and Vector Graphics with Adobe
Analizując pozostałe propozycje kursów, łatwo zauważyć, że żadna z nich nie daje pełnego zestawu kompetencji wymaganych od pełnoprawnego Full-Stack Developera. Kurs "Mastering Cross-platform Developing" sugeruje skupienie się na tworzeniu aplikacji działających na wielu platformach – najczęściej chodzi tu o frameworki typu React Native, Flutter czy Xamarin. Chociaż brzmi to kusząco, w rzeczywistości skupia się bardziej na rozwoju aplikacji mobilnych niż na pełnym stacku webowym. Z mojego doświadczenia, wybierając tę ścieżkę, często zostaje się ekspertem od warstwy prezentacji, a backend czy bazy danych nadal pozostają poza zasięgiem. "Ultimate C# Series from Beginner to Advanced" to typowy kurs nastawiony na jeden język, w tym przypadku C#. Owszem, C# jest bardzo mocny w środowisku .NET i często wykorzystywany do budowy backendu aplikacji webowych, ale bez znajomości JavaScriptu, frontendu (czyli np. Reacta, Vue, Angular) oraz baz danych, trudno mówić o prawdziwym full-stacku. W dodatku, firmy wymagające full-stacków z reguły wolą osoby, które potrafią łączyć technologie webowe typu JavaScript/Node.js z nowoczesnymi frontendami, bo to jest obecnie standard branżowy. Z kolei "Raster and Vector Graphics with Adobe" to kurs zupełnie z innej bajki – omówienie grafiki rastrowej i wektorowej przydaje się grafikom lub UX/UI designerom, a nie programistom pełnego stosu. To typowy błąd myślowy: wychodzić z założenia, że wystarczą umiejętności graficzne, by być full-stackiem. W praktyce, nawet najbardziej estetyczny interfejs nie znaczy nic bez solidnego backendu i umiejętności pracy z bazami danych. Na rynku pracy szuka się osób, które potrafią obsłużyć całą ścieżkę przepływu danych i logiki, a nie tylko jedną wybraną dziedzinę. Moim zdaniem, wybierając którąkolwiek z tych opcji, nie zdobędzie się pełnego, wszechstronnego portfolio, które pozwoli z powodzeniem konkurować na rynku IT jako full-stack.

Pytanie 18

Który z frameworków pozwala na tworzenie aplikacji z graficznym interfejsem użytkownika oraz obsługą wydarzeń?

A. Django
B. TensorFlow
C. Express.js
D. Qt
Qt to framework umożliwiający projektowanie aplikacji z graficznym interfejsem użytkownika (GUI) oraz obsługą zdarzeń. Qt oferuje narzędzia takie jak Qt Designer, które umożliwiają tworzenie interfejsów metodą 'przeciągnij i upuść'. Jest to jedno z najbardziej wszechstronnych narzędzi do budowy aplikacji wieloplatformowych, umożliwiające tworzenie oprogramowania dla Windows, macOS, Linux oraz urządzeń mobilnych. Qt obsługuje zdarzenia użytkownika, animacje, grafikę 3D i inne zaawansowane funkcjonalności, co czyni go idealnym narzędziem do budowy nowoczesnych aplikacji desktopowych i mobilnych.

Pytanie 19

Na przedstawionym obrazie widać fragment emulacji systemu iOS z prostą aplikacją. Górna część ekranu nachodzi na pasek stanu baterii. Który z poniższych zapisów powinien zostać użyty w miejscu znaków zapytania, aby ustawić jedynie marginesy górne tylko dla systemu iOS?

Ilustracja do pytania
A. x:TypeArguments="Thickness"<br/> iOS= "0, 20, 0, 0"<br/> Android= "0, 0, 0, 0"<br/> WinPhone= "0, 0, 0, 0"
B. x:TypeArguments="Thickness" <br/> (0, 20, 0, 0)
C. x:TypeArguments="Thickness"<br/> iOS= "0, 0, 0, 0"<br/> Android= "0, 20, 0, 0"<br/> WinPhone= "0, 0, 0, 0"
D. x:TypeArguments="Thickness"<br/> iOS= 20
W pierwszej opcji zastosowano jednolity margines 20 jednostek dla wszystkich platform, co jest niewłaściwe, ponieważ ignoruje specyficzne potrzeby i ograniczenia każdej platformy. Na przykład, na iOS margines ten kompensuje status bar, ale na innych platformach może nie być wymagany, co może prowadzić do nieestetycznego wyglądu aplikacji. Druga odpowiedź wprowadza margines tylko dla iOS, ale w niewłaściwym formacie, ponieważ same liczby nie są wystarczające bez poprawnego kontekstu struktury XAML. Trzecia opcja jest błędna, ponieważ przypisuje nieprawidłowe wartości platformie Android, co skutkuje odwrotnym efektem niż zamierzony. Traktowanie wszystkich platform identycznie pod względem marginesów pomija specyfikę każdej z nich, co jest częstym błędem początkowych deweloperów. Niezrozumienie różnic między platformami może prowadzić do aplikacji, które nie są optymalnie dostosowane do każdej z nich. Ważne jest, aby projektanci aplikacji wieloplatformowych zrozumieli, jak różne urządzenia i systemy operacyjne wpływają na sposób, w jaki aplikacja powinna wyglądać i działać, aby zapewnić użytkownikom spójne i wysokiej jakości doświadczenia niezależnie od platformy, na której aplikacja jest uruchamiana. Dostosowywanie UI do specyfiki każdej platformy jest kluczem do sukcesu w tworzeniu profesjonalnych aplikacji mobilnych.

Pytanie 20

Które z poniższych twierdzeń najlepiej charakteryzuje metodę wirtualną?

A. Metoda, która jest zawsze stosowana w konstruktorach danej klasy
B. Metoda, która działa wyłącznie dla statycznych pól danej klasy
C. Metoda, która może być wywoływana tylko przez klasę nadrzędną
D. Metoda, która może być przesłonięta w klasie dziedziczącej
Metoda, która może być wywoływana tylko przez klasę bazową to raczej metoda prywatna lub chroniona, a nie wirtualna. Metody statyczne są związane z klasą, a nie z konkretnymi obiektami, więc nie można ich tak po prostu nadpisać w klasie pochodnej. A tak w ogóle, metody w konstruktorach zazwyczaj nie są wirtualne, bo wywołanie metod wirtualnych w konstruktorze może prowadzić do dziwnych rzeczy – w konstruktorze klasy bazowej składowe klasy pochodnej jeszcze nie są gotowe.

Pytanie 21

Który fragment kodu ilustruje zastosowanie rekurencji?

Blok 1:
int fn(int a) {
  if(a==1) return 1;
  return fn(a-1)+2;
}
Blok 2:
int fn(int a) {
  if(a==1) return 1;
  return (a-1)+2;
}
Blok 3:
int fn(int a) {
  if(a==1) return 1;
  return fun(a-1)+2;
}
Blok 4:
int fn(int a) {
  if(a==1) return 1;
  return 2;
}
A. Blok 4
B. Blok 3
C. Blok 1
D. Blok 2
Blok 1 to typowy przykład rekurencji, czyli sytuacji, gdy funkcja wywołuje samą siebie z innym argumentem (tutaj fn(a-1)). Takie podejście pojawia się w programowaniu bardzo często, szczególnie przy rozwiązywaniu problemów, gdzie rozwiązanie można rozbić na mniejsze, podobne zadania. W Bloku 1 mamy tzw. przypadek bazowy (if(a==1) return 1), czyli moment, w którym dalsza rekurencja się zatrzymuje – bez tego każdy program rekurencyjny skończyłby się przepełnieniem stosu i błędem. Moim zdaniem, dobrze rozumiana rekurencja to jedna z podstaw algorytmiki – spotyka się ją choćby przy obliczaniu silni, ciągu Fibonacciego czy w algorytmach przeszukiwania struktur drzewiastych, np. w operacjach na drzewach binarnych. W praktyce branżowej warto wiedzieć, że rekurencja bywa bardzo elegancka i skraca kod, ale trzeba ją stosować z głową – łatwo przekroczyć limity stosu przy zbyt głębokim wywołaniu albo zapomnieć o przypadku bazowym, przez co program nie kończy działania. W standardach wielu języków (np. C, Java) rekurencja jest narzędziem jak każde inne, ale zawsze powinna być projektowana z myślą o czytelności i efektywności rozwiązania. Często spotykam się z sytuacją, gdzie początkujący próbują wszystko rozwiązywać rekurencyjnie, a to nie zawsze jest optymalne – niektóre problemy lepiej rozwiązać iteracyjnie, choćby ze względu na wydajność. W tym konkretnym kodzie zastosowanie rekurencji jest klasyczne i poprawne, więc zdecydowanie jest to dobry wzór do nauki.

Pytanie 22

Jakiego rodzaju zmiennej użyjesz w C++, aby przechować wartość "true"?

A. bool
B. liczba zmiennoprzecinkowa
C. tekst
D. liczba całkowita
Typ danych 'bool' w języku C++ jest przeznaczony do przechowywania wartości 'true' lub 'false'. Deklaracja 'bool isReady = true;' to przykład poprawnego przypisania wartości logicznej do zmiennej. Typ boolean jest kluczowy w warunkach decyzyjnych, pętlach i operacjach porównawczych. W programowaniu typ 'bool' jest wykorzystywany do kontroli przepływu programu, obsługi wyjątków oraz sprawdzania poprawności danych. Użycie booleanów w kodzie zwiększa jego czytelność i pozwala na bardziej przejrzyste budowanie logiki aplikacji.

Pytanie 23

Co zostanie wyświetlone po wykonaniu poniższego kodu JavaScript?

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('success');
  }, 1000);
});

promise
  .then(res => {
    console.log(res);
    return 'first then';
  })
  .then(res => {
    console.log(res);
  });
A. first then
B. success, first then
C. first then, success
D. success
Podany kod JavaScript korzysta z obietnic (Promises), co jest nowoczesnym podejściem do zarządzania asynchronicznością w JavaScript. W momencie, gdy tworzymy nową obietnicę, wykorzystujemy funkcję `setTimeout`, która po 1 sekundzie wywołuje metodę `resolve`, przekazując tekst 'success'. To jest pierwszy krok, w którym obietnica zostaje spełniona. Następnie, w łańcuchu `then`, pierwsza funkcja `then` przyjmuje wynik obietnicy, czyli 'success', loguje go na konsolę, a następnie zwraca nowy tekst 'first then'. Druga funkcja `then` odbiera ten wynik i również go loguje. W rezultacie na konsoli pojawią się kolejno: 'success' oraz 'first then'. Taki sposób tworzenia łańcuchów obietnic jest zgodny z najlepszymi praktykami programowania asynchronicznego w JavaScript, ponieważ pozwala na czytelniejsze i bardziej zrozumiałe zarządzanie kodem asynchronicznym, eliminując złożoność związaną z tzw. „callback hell”. Warto zaznaczyć, że obietnice mogą być stosowane do radzenia sobie z żądaniami sieciowymi, operacjami na plikach czy innymi długotrwałymi procesami, co czyni je niezbędnym narzędziem w nowoczesnym programowaniu webowym.

Pytanie 24

Na schemacie widoczny jest fragment diagramu blokowego pewnego algorytmu. Ile razy zostanie zweryfikowany warunek n<7?

Ilustracja do pytania
A. 7
B. 5
C. 8
D. 6
Warunek 'n < 7' będzie sprawdzany dokładnie 6 razy. To dlatego, że w klasycznej pętli for czy while liczba powtórzeń jest zawsze o jeden mniejsza niż wartość graniczna. Jak widzisz, w takich schematach liczba porównań idzie w parze z liczbą iteracji, a pętla zatrzymuje się, jak tylko warunek zostanie spełniony.

Pytanie 25

W metodach klasy GoldCustomer dostępne są tylko pola

public class Customer {
    public string Name;
    protected int Id;
    private int Age;
}
public class GoldCustomer: Customer {
    private GoldPoints: int;
}
A. GoldPoints, Name, Id
B. GoldPoints, Name, Id, Age
C. GoldPoints, Name
D. GoldPoints
W tym pytaniu nietrudno pomylić się, bo różnice między public, protected i private czasem wydają się nieintuicyjne – szczególnie jak ktoś dopiero zaczyna przygodę z C#. Najczęstszy błąd to założenie, że wszystkie pola klasy bazowej są automatycznie dostępne w klasach pochodnych. Tak jednak nie jest. Publiczne pole (takie jak Name) będzie widoczne absolutnie wszędzie – także w GoldCustomer. Protected (Id) daje dostęp tylko klasie bazowej i pochodnym, co jest bardzo wygodne przy dziedziczeniu, bo pozwala korzystać z ważnych danych bez upubliczniania ich na zewnątrz. Natomiast private (Age) jest dostępne wyłącznie w ramach klasy, w której zostało zadeklarowane – żadna klasa pochodna nie może go bezpośrednio użyć. W praktyce programistycznej wiele osób myli protected z public, przez co zdarza się, że do protected próbują się dostać spoza klasy dziedziczącej – i wtedy pojawia się frustracja. Z kolei pole GoldPoints zostało zadeklarowane bezpośrednio w GoldCustomer i dzięki temu jest dostępne tylko w tej klasie – nawet inne klasy dziedziczące po GoldCustomer nie będą miały do niego dostępu. Jeśli chodzi o Age, wielu początkujących (i nie tylko!) zakłada, że dziedziczenie daje pełen dostęp do wszystkiego z klasy bazowej, ale to nieprawda – private jest całkowicie zamknięte. Z technicznego punktu widzenia, zrozumienie tej różnicy pozwala nie tylko lepiej projektować własne klasy, ale i unikać błędów, które potem trudno wyłapać, zwłaszcza w większych projektach. Tak więc, praktyka pokazuje, że najbezpieczniejsze jest świadome korzystanie z modyfikatorów dostępu – pomaga to zachować porządek i minimalizuje ryzyko przypadkowych błędów podczas rozwoju aplikacji.

Pytanie 26

Jaka będzie wartość zmiennej x po wykonaniu poniższego kodu?

let x = 0;
for (let i = 0; i < 10; i++) {
  if (i % 2 === 0) continue;
  x += i;
}
A. 30
B. 45
C. 25
D. 20
Wartość zmiennej x po wykonaniu podanego kodu wynosi 25, co jest wynikiem sumy wszystkich nieparzystych liczb od 0 do 9. W pętli for iterujemy od 0 do 9, a dla każdej wartości i sprawdzamy, czy jest parzysta. Jeżeli i jest parzyste, używamy instrukcji continue, co oznacza, że ten krok pętli jest pomijany i przechodzimy do następnej iteracji. W praktyce oznacza to, że wartości takie jak 0, 2, 4, 6, 8 nigdy nie zostaną dodane do zmiennej x. Zostaną natomiast dodane wartości nieparzyste: 1, 3, 5, 7, 9. Ich suma wynosi 1 + 3 + 5 + 7 + 9 = 25. Takie podejście jest typowe w programowaniu, gdyż pozwala na efektywne pomijanie niepotrzebnych wartości, co zwiększa wydajność kodu. Używanie operatora modulo (%) do sprawdzania parzystości jest powszechną praktyką. Tego typu konstrukcje możemy znaleźć w różnych zastosowaniach, na przykład w algorytmach sortujących czy w przetwarzaniu danych, gdzie istotne jest operowanie tylko na wybranych elementach.

Pytanie 27

Na równoważnych pod względem funkcjonalnym listingach fragmentów aplikacji Angular oraz React.js utworzono listę punktowaną, która zawiera:

Definicja typu:

books = ["Harry Potter", "Hobbit", "Władca pierścieni"];

Kod Angular:
<ul>
    <li *ngFor = "let book of books"> {{book}} </li>
</ul>

Kod React.js:
<ul>
    {this.books.map(book => <li key={book}> book </li>)}
</ul>
A. Taką liczbę elementów, ile znajduje się w tablicy books; w każdym punkcie listy umieszczony jest jeden element tablicy.
B. Jedynie jeden element o treści Harry Potter.
C. Tyle elementów, ile znajduje się w tablicy books; w każdym punkcie listy widnieje element o treści {book}.
D. Wyłącznie jeden element o treści Harry Potter, Hobbit, Władca pierścieni.
W pytaniu chodziło o bardzo fundamentalny mechanizm w nowoczesnych frameworkach frontendowych: dynamiczne generowanie elementów listy na podstawie danych z tablicy. Częstym błędem jest doszukiwanie się w kodzie magicznych wartości lub mylenie sposobu działania szablonów z faktycznym efektem końcowym. Przykładowo, jeśli ktoś uzna, że pojawi się jeden element z wypisaną całą tablicą jako tekst, to prawdopodobnie nie rozumie, że każda iteracja *ngFor (w Angularze) czy metoda map (w React.js) generuje fizycznie osobny <li> dla każdego wpisu. To nie jest jedno pole tekstowe, ale wiele autonomicznych elementów. Kolejna rzecz to myślenie, że pojawi się tylko pierwszy element – takie podejście wynika często z niezrozumienia zasady działania pętli w szablonach, gdzie proces rozpoczyna się dla każdego elementu po kolei. Często też myli się składnię Reacta i widzi w niej coś na kształt szablonu z placeholderem {book}, zapominając, że to wyrażenie jest oceniane w JavaScript i renderuje konkretną wartość, a nie dosłowny napis z nawiasami klamrowymi. Moim zdaniem to jeden z najpopularniejszych błędów – traktowanie składni szablonów lub JSX zbyt dosłownie, bez zrozumienia, że to dynamiczne narzędzia. Warto pamiętać, że we współczesnych aplikacjach webowych takie podejście jest normą: nie piszesz ręcznie każdego <li>, tylko generujesz je ze źródła danych. Pominięcie tego mechanizmu prowadzi do kodu nieelastycznego, trudnego w utrzymaniu i skalowaniu. Z doświadczenia wiem, że zrozumienie tej zasady znacznie przyspiesza naukę zarówno Angulara, jak i Reacta, bo ten wzorzec powtarza się praktycznie wszędzie tam, gdzie masz do czynienia z kolekcjami danych i ich wizualizacją w interfejsie użytkownika.

Pytanie 28

Jak zostanie przedstawiony poniższy kod XAML?

Ilustracja do pytania
A. Rysunek 1
B. Rysunek 3
C. Rysunek 2
D. Rysunek 4
Błędy w rozumieniu XAML mogą prowadzić do niepoprawnego renderowania interfejsu użytkownika. W przypadku przedstawionego kodu kluczowym elementem jest zrozumienie struktury StackLayout który domyślnie ustawia elementy w pionie chyba że określono Orientation jako Horizontal jak w przypadku etykiety Zgoda RODO i przełącznika co może być mylące gdyż sugeruje potrzebę zwrócenia uwagi na szczegóły. W innych rysunkach jak na przykład Rysunek 1 brak pola nazwisko wskazuje na błędne zrozumienie struktury kodu gdzie Entry dla nazwiska jest obecne ale nie jest widoczne co wskazuje na potrzebę poprawnego zrozumienia jak StackLayout renderuje elementy. Podobnie Rysunek 3 zawiera dodatkowe przyciski co sugeruje błędne zinterpretowanie przycisków nieobecnych w kodzie. Zrozumienie Slidera jest również kluczowe gdyż jego wartości i kolory wpływają na potencjalne błędne renderowanie suwaka w innych rysunkach. Błędy mogą wynikać z ignorowania tak kluczowych elementów jak IsToggled dla przełącznika co determinuje jego początkowy stan i jest często pomijanym aspektem przez mniej doświadczonych programistów. Stąd istotne jest dokładne zrozumienie jak poszczególne właściwości wpływają na końcowy wygląd interfejsu aby uniknąć typowych błędów projektowych i zapewnić funkcjonalność zgodną z oczekiwaniami użytkownika.

Pytanie 29

Jakie narzędzie jest wykorzystywane do zgłaszania błędów w projektach IT?

A. Photoshop
B. JIRA
C. Git
D. Blender
JIRA to jedno z najczęściej wykorzystywanych narzędzi do zarządzania projektami i raportowania błędów w projektach informatycznych. JIRA umożliwia śledzenie błędów, zarządzanie zadaniami oraz efektywne monitorowanie postępów prac nad projektem. Dzięki integracji z innymi narzędziami, takimi jak Confluence czy Bitbucket, JIRA stanowi kompleksowe rozwiązanie wspierające zespoły deweloperskie. Raportowanie błędów w JIRA pozwala na łatwe przypisywanie ich do odpowiednich członków zespołu, dodawanie załączników i komentarzy oraz monitorowanie statusu danego problemu, co usprawnia proces zarządzania jakością oprogramowania.

Pytanie 30

Co oznacza termin 'immutability' w programowaniu funkcyjnym?

A. Funkcje mogą być przypisywane do zmiennych
B. Kod może być wykonywany równolegle
C. Stan obiektu nie może być modyfikowany po jego utworzeniu
D. Obiekty są automatycznie usuwane z pamięci
Termin 'immutability' w programowaniu funkcyjnym odnosi się do właściwości obiektów, które po utworzeniu nie mogą być modyfikowane. W kontekście programowania funkcyjnego, gdzie funkcje są kluczowym składnikiem, immutability jest fundamentalnym założeniem, które pozwala na tworzenie bardziej przewidywalnych i bezpiecznych aplikacji. Kiedy obiekty są niemodyfikowalne, każde ich 'zmiana' generuje nowy obiekt, zamiast aktualizować istniejący, co eliminuję problemy związane z nieprzewidywalnym stanem aplikacji. Przykładem może być język programowania Scala, gdzie kolekcje, takie jak List, są niemodyfikowalne z założenia. Z perspektywy dobrych praktyk, immutability przyczynia się do łatwiejszej analizy kodu, testowania jednostkowego oraz równoległego przetwarzania danych. Ponadto, programowanie funkcyjne, bazujące na tej koncepcji, sprzyja tworzeniu czystych, modularnych i łatwych do przetestowania aplikacji.

Pytanie 31

Jaką strukturę danych można zrealizować, korzystając jedynie z wymienionych poniżej metod:

push(arg) – dodaje element
pop() – usuwa ostatnio dodany element
peek() – zwraca ostatnio dodany element bez usuwania
isEmpty() – sprawdza czy istnieją dane w strukturze
A. stos
B. kolejka
C. drzewo binarne
D. tablica
Pomyłka przy tym pytaniu jest dość zrozumiała, bo wiele osób myli dostępne operacje ze strukturą, którą zamierzają użyć. Metody push, pop, peek i isEmpty wskazują wyraźnie na stos, jednak nietrudno ulec wrażeniu, że można je zastosować także w tablicy czy nawet kolejce. Problem w tym, że tablica daje bezpośredni dostęp do dowolnego elementu (indeksowanie), a tych operacji tutaj nie mamy — bez get(index) czy set(index, value) nie da się użyć jej w typowy sposób, więc zamienia się bardziej w zwykły pojemnik, niż prawdziwą tablicę. Kolejka natomiast wymaga operacji typu enqueue (dodanie na koniec) i dequeue (usunięcie z początku), czyli tzw. FIFO (First-In-First-Out), czego tutaj nie zrealizujesz samymi push i pop - one zawsze odnoszą się do końca struktury, nie do jej początku. Drzewo binarne to już zupełnie inna para kaloszy: wymaga rozbudowanych operacji na węzłach i gałęziach, a dostępne tu metody nawet nie zbliżają się do tego typu funkcjonalności — nie ma dodawania dzieci, przeszukiwania czy sprawdzania położenia w strukturze. Typowym błędem jest też utożsamianie samych nazw operacji z ogólną strukturą danych, bez zastanowienia się, jaki dokładnie model dostępu do danych one umożliwiają. Warto zapamiętać, że jeśli dostępne masz tylko push, pop, peek i isEmpty, to jesteś w świecie stosu - i tylko jego.

Pytanie 32

Fragment kodu w języku JavaScript to

let modulo = (x, y) => x % y;
A. Definicja zmiennej typu tablicowego
B. Prototyp metody klasy
C. Definicja funkcji strzałkowej
D. Prototyp interfejsu
Wiele osób rozpoczynających naukę JavaScript często myli różne elementy składni, szczególnie gdy pojawiają się nowoczesne rozwiązania pochodzące z nowszych wersji języka. Cofnijmy się do analizy dostępnych odpowiedzi. 'Prototyp metody klasy' to termin odnoszący się do programowania obiektowego w JavaScript, gdzie metody są przypisywane do właściwości prototype konstruktora klasy, co pozwala wszystkim instancjom korzystać ze wspólnej implementacji. Jednak w tym przypadku nie mamy tu żadnej klasy, ani jawnego prototype, tylko zwykłą funkcję przypisaną do zmiennej. To zupełnie inny mechanizm. Następnie 'definicja zmiennej typu tablicowego' – to bardzo częste nieporozumienie. W JavaScript tablice tworzy się za pomocą nawiasów kwadratowych [], np. let arr = [1,2,3]. W podanym kodzie nie widać takiej składni, więc nie ma tu mowy o tablicy. Często myli się to, bo argumenty funkcji są czasem brane za elementy tablicy, ale to tylko parametry funkcji. Natomiast 'prototyp interfejsu' – to raczej pojęcie ze świata TypeScript lub programowania silniej typowanego (np. Java, C#). W czystym JavaScript nie istnieje coś takiego jak interfejs w sensie typów i ich prototypów. To może wynikać z mylenia pojęć, bo JavaScript ma prototypowe dziedziczenie, ale nie ma formalnych interfejsów. Moim zdaniem, takie nieporozumienia biorą się z mieszania terminologii pochodzącej z różnych paradygmatów programowania. W podanym przykładzie kluczowe jest zauważenie, że mamy do czynienia z funkcją strzałkową, która została wprowadzona, żeby upraszczać składnię i podnieść czytelność kodu. To nie jest ani metoda klasy, ani tablica, ani interfejs – po prostu nowoczesny sposób na definiowanie funkcji.

Pytanie 33

Zaprezentowany fragment kodu w języku C# tworzy hasło. Wskaż zdanie PRAWDZIWE dotyczące charakterystyki tego hasła:

var random = new Random();
string pulaZnakow = "abcdefghijklmnopqrstuwxyzABCDEFGHIJKLMNOPQRSTUWXYZ0123456789";

int dlPuli = pulaZnakow.Length - 1;
char znak;
string wynik = "";

for(int i = 0; i < 8; i++)  {
    znak = pulaZnakow[random.Next(0, dlPuli)];
    wynik += znak;
}
A. Jest maksymalnie 7-znakowe, co wynika z wartości zmiennej i
B. Ma co najmniej 8 znaków oraz zawiera małe i wielkie litery oraz cyfry
C. Może zawierać zarówno małe, jak i wielkie litery, cyfry oraz symbole
D. Może zawierać małe i wielkie litery oraz cyfry
Patrząc na pozostałe odpowiedzi, nietrudno zauważyć typowe pomyłki, które pojawiają się przy analizie kodu generującego hasła. Często spotykam się z błędnym założeniem, że jeśli mamy losowy wybór ze znacznej puli znaków, to automatycznie generowane hasło może zawierać symbole czy inne znaki specjalne. W tym przypadku pulaZnakow wyraźnie ogranicza się tylko do liter – małych i wielkich – oraz cyfr, stąd nie ma możliwości, by pojawił się jakikolwiek symbol, co już obala pierwszą odpowiedź. Następna rzecz – twierdzenie, że hasło ma co najmniej 8 znaków – jest nieścisłe: kod ustawia pętlę for na osiem powtórzeń, więc hasło będzie miało zawsze dokładnie 8 znaków, ani więcej, ani mniej (chyba że ręcznie zmodyfikujemy kod), więc nie jest to 'co najmniej'. Sformułowanie o długości „maksymalnie 7-znakowej” wynika najpewniej z nieuważnego czytania zakresu powtórzeń pętli: for (int i = 0; i < 8; i++) oznacza 8 iteracji, czyli 8 znaków, a nie 7. Często w praktyce ludzie gubią się w liczeniu zakresów, bo zaczynają od zera, ale liczba iteracji jest tu bezpośrednio związana z warunkiem i<wartość>. To są pomyłki, które łatwo popełnić, gdy nie czyta się kodu bardzo uważnie albo nie ćwiczyło się podobnych przypadków na własnych przykładach. Wreszcie, niektóre odpowiedzi próbują dodać jakieś cechy hasła (np. obecność symboli), których w kodzie po prostu nie ma, bo pulaZnakow na to nie pozwala. W branży bezpieczeństwa takie detale są bardzo istotne i weryfikacja puli znaków to pierwszy krok przy analizie generatorów haseł. Zawsze warto szczegółowo patrzeć, co faktycznie znajduje się w zmiennej określającej dostępne znaki, a nie zakładać, że kod jest bardziej zaawansowany, niż jest w rzeczywistości.

Pytanie 34

Rozpoczęcie tworzenia procedury składowej o nazwie dodajUsera w MS SQL wymaga użycia poleceń

A. add dodajUsera procedure
B. create dodajUsera procedure
C. create procedure dodajUsera
D. add procedure dodajUsera
Pytanie o polecenie rozpoczynające tworzenie procedury składowej w MS SQL często sprawia trudność, bo w różnych systemach bazodanowych bywa różnie, a niektóre sformułowania mogą się wydawać intuicyjne przez analogię do angielskiego czy innych technologii. W rzeczywistości jednak tylko 'create procedure nazwa' jest akceptowany przez silnik SQL Servera. Użycie słów 'add' lub błędnej kolejności, jak 'add dodajUsera procedure' lub 'add procedure dodajUsera', to typowy błąd wynikający z przenoszenia logiki z innych poleceń SQL, jak 'ALTER TABLE ... ADD COLUMN', gdzie rzeczywiście 'add' służy do dodawania kolumn czy indeksów, a nie procedur. Zdarza się, że ktoś, kto zaczyna naukę, myli pojęcia i traktuje dodawanie procedury jak dodawanie kolumny czy rekordu – niestety, w projektowaniu struktury bazy 'add' nie służy do tworzenia żadnych obiektów typu procedury, funkcje, widoki. Z kolei sformułowanie 'create dodajUsera procedure' wygląda jakby ktoś próbował dosłownie przetłumaczyć z angielskiego, ale nie zachował poprawnej kolejności składniowej. W T-SQL bardzo ważna jest właśnie kolejność słów kluczowych – 'create procedure' jest całościową instrukcją, a nazwa idzie dopiero po tym. W praktyce, jeżeli użyjesz którejkolwiek z tych niepoprawnych form, otrzymasz błąd składniowy. Moim zdaniem warto zawsze sięgać po dokumentację lub IntelliSense w Management Studio – tam od razu widać, co jest obsługiwane przez silnik. Częstym błędem jest też oczekiwanie, że SQL będzie działał jak inne języki programowania, gdzie kolejność czasem nie ma takiego znaczenia – w SQL wszystko musi być zgodne ze ścisłą składnią. Dlatego tak ważne jest wyrobienie sobie nawyku korzystania z poprawnych konstrukcji, bo potem na egzaminach, w pracy czy przy migracjach na inne wersje serwera można wtopić mnóstwo czasu na debugowanie takich drobiazgów.

Pytanie 35

Z analizy złożoności obliczeniowej algorytmów sortowania dla dużych zbiorów danych (powyżej 100 elementów) wynika, że najefektywniejszą metodą jest algorytm sortowania:

sortowanie bąbelkoweO(n²)
sortowanie przez wstawianieO(n²)
sortowanie przez scalanieO(n log n)
sortowanie przez zliczanieO(n)
sortowanie kubełkoweO(n²)
A. kubełkowego
B. przez zliczanie
C. bąbelkowego
D. przez scalanie
Sortowanie bąbelkowe jest jednym z najwolniejszych algorytmów i rzadko jest używane w praktyce ze względu na złożoność O(n^2). Sortowanie kubełkowe może być szybkie, ale jego efektywność zależy od równomiernego rozkładu danych. Sortowanie przez scalanie jest bardziej uniwersalne, ale ma większą złożoność obliczeniową niż Counting Sort i może być mniej efektywne dla dużych zbiorów danych o ograniczonym zakresie.

Pytanie 36

Zaprezentowany wykres ilustruje wyniki przeprowadzonych testów

Ilustracja do pytania
A. funkcjonalności
B. wydajności
C. użyteczności
D. ochrony
Wykres przedstawia czasy odpowiedzi strony internetowej co jest kluczowe w kontekście testów wydajnościowych. Testy wydajnościowe mają na celu zmierzenie jak system radzi sobie pod określonym obciążeniem i jak szybko potrafi odpowiedzieć na zapytania użytkowników. Tego typu analiza pomaga zidentyfikować potencjalne wąskie gardła w infrastrukturze IT. Przykładowo jeżeli czasy odpowiedzi DNS lub połączenia są zbyt długie może to wskazywać na potrzebę optymalizacji serwerów DNS lub infrastruktury sieciowej. Testy te są nieodłącznym elementem zapewnienia jakości oprogramowania a ich prawidłowe wykonanie wpływa na doświadczenia użytkowników końcowych. Dobra praktyka w branży IT zakłada regularne przeprowadzanie testów wydajnościowych w celu monitorowania stabilności systemu w warunkach zbliżonych do rzeczywistych. Warto również zauważyć że narzędzia takie jak JMeter czy LoadRunner są powszechnie używane do przeprowadzania takich testów co umożliwia symulację różnorodnych scenariuszy obciążenia i analizę wyników w czasie rzeczywistym.

Pytanie 37

Zamieszczony kawałek kodu w języku C# tworzy hasło. Wskaż prawdziwe stwierdzenie dotyczące cech tego hasła?

var random = new Random();
string pulaZnakow =
    "abcdefghijklmnopqrstuwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
int dlPuli = pulaZnakow.Length - 1;
char znak;
string wynik = "";

for(int i = 0; i < 8; i++)  {
    znak = pulaZnakow[random.Next(0, dlPuli)];
    wynik += znak;
}
A. Ma 8 znaków lub więcej i zawiera małe oraz wielkie litery, a także cyfry.
B. Ma maksymalną długość 7 znaków, co ustala zmienna i.
C. Może zawierać małe oraz wielkie litery, cyfry i znaki specjalne.
D. Może zawierać małe i wielkie litery oraz cyfry.
Analizując przedstawiony fragment kodu, możemy odrzucić kilka błędnych przekonań zawartych w proponowanych odpowiedziach. Niektóre błędne odpowiedzi sugerują, że hasło może zawierać symbole. Jednak przeglądając zmienną pulaZnakow, widać, że nie zawiera ona żadnych symboli specjalnych, a jedynie małe i wielkie litery oraz cyfry. Inne niepoprawne stwierdzenie mówi, że hasło może być co najwyżej 7-znakowe z powodu zmiennej i. W rzeczywistości, zmienna i wskazuje na iterację pętli kontrolowanej przez warunek i < 8, co oznacza, że hasło jest dokładnie 8-znakowe, a nie 7-znakowe lub krótsze. Kolejna niepoprawna sugestia dotyczy tego, że hasło jest dłuższe niż 8 znaków oraz zawiera symbole, co również jest niezgodne z analizą kodu. Ważne jest, aby rozumieć, jak działają pętle i jaka jest rola zmiennych w kodzie generacji haseł. W zaawansowanych systemach zabezpieczeń często dodaje się symbole specjalne do puli znaków, jednak ten konkretny kod tego nie czyni. Dobrze jest pamiętać, że w bezpieczeństwie kluczowa jest nie tylko długość hasła, ale także jego złożoność i różnorodność użytych znaków. Prawidłowa analiza kodu pozwala uniknąć błędnych założeń i dostarcza solidnych podstaw do zrozumienia mechanizmów generacji losowych haseł.

Pytanie 38

W środowisku IDE przeznaczonym do tworzenia aplikacji okienkowych zdefiniowano okno Form1. Aby wprowadzić zmiany w ustawieniach, w kolejności: tytuł okna na górnym pasku, standardowy kursor na strzałkę oraz kolor tła okna, należy dostosować następujące pola w oknie Properties:

Ilustracja do pytania
A. (Name), UseWaitCursor, BackgroundImage
B. Text, UseWaitCursor, BackColor
C. (Name), Cursor, BackgroundImage
D. Text, Cursor, BackColor
W pracy z IDE do aplikacji okienkowych, bardzo łatwo pomylić znaczenie niektórych właściwości formularza, bo wiele z nich brzmi podobnie albo wydaje się spełniać podobną funkcję. Przykładowo, pole (Name) w oknie Properties nie ma żadnego wpływu na to, co widzi użytkownik w tytule okna – to jest nazwa techniczna obiektu w kodzie, czyli identyfikator, którym odwołujemy się do formularza w trakcie programowania. To megaważna różnica! BackgroundImage natomiast służy do ustawienia grafiki jako tła, a nie koloru – czasem ktoś myli BackColor z BackgroundImage, bo oba dotyczą wyglądu, ale BackColor to czysty kolor, bez obrazków. UseWaitCursor to kolejny haczyk – ta właściwość aktywuje specjalny kursor oczekiwania (taki kręcący się kółek lub klepsydrę), a nie domyślny wskaźnik w kształcie strzałki. Ustawienie tego pola na true sprawia, że użytkownik ma wrażenie, iż aplikacja coś przetwarza – super przydatne w operacjach długotrwałych, ale nie do standardowej obsługi okna. Cursor daje pełną kontrolę nad rodzajem kursora i to jest najlepszy wybór, gdy chodzi o standardowego pointera. Wreszcie, częsty błąd to branie pod uwagę właściwości, które po prostu nie dotyczą zadanego aspektu wizualnego formularza – na przykład mylenie tego, co widzi użytkownik, z tym, jak programista nazywa obiekt. Moim zdaniem, to taki typowy skrót myślowy: patrzymy na nazwę w oknie Properties i wydaje nam się, że to musi być to, co się zmienia na ekranie, a tu czasem logika IDE płata figle. W praktyce, poprawne podejście to zawsze zajrzeć do dokumentacji lub przeprowadzić szybki test – zmienić wartość i zobaczyć, co się realnie dzieje w oknie podglądu. To podstawowa umiejętność każdego, kto na serio chce projektować wygodne i profesjonalne interfejsy, zamiast działać na ślepo.

Pytanie 39

Przykład wywołania funkcji zamien w języku C++ może wyglądać w następujący sposób:

void zamien(int *a, int *b) {
    int tmp;
    tmp = *a;
    *a = *b;
    *b = tmp;
}
A. zamien(m, n); // m, n - zmienne całkowite
B. zamien(*a, *b); // a, b - zmienne całkowite
C. zamien(12, 34)
D. zamien(&a, &b); // a, b - zmienne całkowite
Patrząc na inne podane propozycje, łatwo zauważyć, że każda z nich zawiera typowy błąd związany z mechaniką przekazywania danych do funkcji w C++. Dużo osób na początku myśli, że można po prostu przekazać liczby bezpośrednio, jak w zamien(12, 34), ale to mija się z celem, bo funkcja oczekuje wskaźników, czyli adresów zmiennych, które mają być zamienione. Przekazanie samych wartości (czy to literałów, czy konkretnych zmiennych) skutkuje tym, że funkcja pracuje na kopiach tych wartości, a nie na oryginalnych danych. Taki kod się nawet nie skompiluje, bo kompilator jasno wymaga wskaźników jako argumentów. Z kolei zamien(*a, *b) też jest mylące, bo tu przekazujemy już nie wskaźniki, ale wartości, na które one wskazują. To znowu powoduje, że funkcja dostaje kopie, a nie adresy, więc nie może zmienić oryginałów. Bardzo łatwo tu pomylić dereferencję z przekazaniem wskaźnika – moim zdaniem to jeden z najczęstszych błędów początkujących, bo na pierwszy rzut oka wydaje się logiczne, że skoro funkcja operuje na wskaźnikach, to wystarczy podać gwiazdkę. No ale niestety, to jest właśnie ta typowa pułapka. Podobnie zamien(m, n) wygląda jak klasyczne wywołanie funkcji, ale znowu – przekazujemy wartości, nie adresy, więc zamiana zachodzi tylko na kopiach i nie widać efektu poza funkcją. W praktyce taki kod nie spełnia celu, dla którego ktoś napisał funkcję operującą na wskaźnikach. Takie nieporozumienia wynikają często z nieznajomości podstaw przekazywania parametrów w C++. Programowanie niskopoziomowe jest pod tym względem wymagające, bo trzeba zawsze wiedzieć, czy manipuluje się oryginałem czy tylko tymczasową kopią, a to różnica zasadnicza. Z mojego doświadczenia wynika, że najlepiej uczyć się przez praktykę: napisać prostą funkcję, przekazać różne typy argumentów i samemu zobaczyć, co się dzieje z ich wartościami po powrocie z funkcji. To bardzo szybko wyjaśnia, dlaczego przekazywanie wskaźników lub referencji jest tak ważne w sytuacjach, gdy chcemy faktycznie wpłynąć na dane przekazane do funkcji. Warto też podkreślić, że kompilator nie pozwoli na wywołanie funkcji z niezgodnym typem argumentu – i to jest jedna z tych rzeczy, które ratują przed większą katastrofą w działającym programie.

Pytanie 40

Co oznacza pojęcie 'hoisting' w JavaScript?

A. Technika optymalizacji kodu przez silnik JavaScript
B. Metoda ładowania skryptów z zewnętrznych źródeł
C. Mechanizm zarządzania pamięcią w przeglądarce
D. Proces podnoszenia deklaracji zmiennych i funkcji na górę zakresu
Omawiając inne odpowiedzi, warto zauważyć, że niektóre z nich mogą wprowadzać w błąd. Na przykład, technika optymalizacji kodu przez silnik JavaScript nie ma nic wspólnego z hoistingiem. Hoisting nie jest związany z optymalizacją, ale z tym, jak JavaScript interpretuje kod. Optymalizacja kodu odnosi się do sposobu, w jaki silniki JavaScript, takie jak V8, przetwarzają kod, aby działał szybciej, poprzez różne techniki, takie jak JIT (Just-In-Time) compilation, a nie przez przenoszenie deklaracji. Z kolei mechanizm zarządzania pamięcią dotyczy sposobu, w jaki JavaScript radzi sobie z pamięcią w czasie wykonania, co również nie jest bezpośrednio związane z hoistingiem. Pamięć jest zarządzana przez garbage collector, który usuwa nieużywane obiekty. Metoda ładowania skryptów z zewnętrznych źródeł także nie ma związku z hoistingiem, ponieważ odnosi się to do sposobu, w jaki zasoby są pobierane i wykonywane w przeglądarkach. Wszystkie te koncepcje, choć dotyczą JavaScriptu, nie mają związku z mechanizmem hoistingu, co prowadzi do powszechnych nieporozumień wśród programistów. Dlatego tak ważne jest zrozumienie, jak działa hoisting, aby uniknąć błędów w kodzie oraz poprawić zrozumienie działania JavaScriptu.