Zajęcia 2 - Składnia języka Java
Najprostszy program w języku Java
// Main.java public class Main { public static void main(String[] args) { System.out.println("Hello World!"); } }
System- predefiniowana klasa w bibliotece standardowej Javy, zawierająca m.in. strumienie wejścia/wyjścia (in,out,err)public static void main(String[] args)- punkt wejścia aplikacji
Słowa kluczowe
abstract assert boolean break byte case catch char class const continue default do double else enum exports extends final finally float for goto if implements import instanceof int interface long module native new non-sealed open opens package permits private protected provides public record requires return sealed short static strictfp super switch synchronized this throw throws to transient transitive try uses var void volatile while with yield
true, false i null - literały, także zarezerwowane słowa
Pakiet (paczka)
- Pakiet (ang. package) - grupuje klasy, pozwala uniknąć konfliktów nazw (odpowiednik przestrzeni nazw), struktura katalogów odpowiadająca hierarchii pakietów
- java.lang - pakiet zawierający podstawowe klasy Javy (
String,Math,System, itp.), jedyna paczka importowana domyślnie - struktura pakietów odzwierciedla strukturę katalogów w projekcie
- Importowanie pakietów za pomocą dyrektywy
import
import java.util.Scanner; // import pojedynczej klasy Scanner import java.util.*; // import wszystkich klas z pakietu
Definiowanie własnych paczek
- Dodajemy katalog o nazwie odpowiadającej pakietowi (wewnątrz modułu/projektu).
- Wewnątrz katalogu tworzymy plik
.javao nazwie klasy. - Na początku pliku dodajemy deklarację pakietu, np.:
package myMain;
// Main.java package myMain; public class Main { public static void main(String[] args) { System.out.println("Hello World!"); } }
Komentarze w języku Java
Komentarz jednoliniowy:
// TO JEST KOMENTARZ
Komentarz blokowy:
/*
TO JEST KOMENTARZ
I TO TEŻ JEST KOMENTARZ
*/
Typy danych w języku Java
Typy proste (ang. primitives):
- całkowitoliczbowe:
byte,short,int,long - zmiennoprzecinkowe:
float,double - logiczne:
boolean(true/false, nie jest liczbą) - znakowe:
char
Typy złożone (obiektowe):
String, tablice,Object, klasy użytkownika, interfejsy, kolekcje (List,Set,Map), itp.
Dla typów prostych istnieją klasy opakowujące (wrappery), które rozszerzają Number:
int → Integer, double → Double, float → Float, itd.
Deklaracja zmiennych
// typy proste int a, b = 42; double d = 3.14; boolean flag = true; char c = 'a'; // typy obiektowe Scanner sc = new Scanner(System.in); String str = "Hello"; int [] arr = {1, 2, 3, 4, 5};
- zmienne typu obiektowego przechowują referencję (adres) do obiektu w pamięci, a nie sam obiekt
- domyślne wartości dla typów prostych:
0dla liczb,falsedlaboolean,\u0000dlachar - domyślna wartość dla typów obiektowych:
null(nie wskazuje na żaden obiekt)
Stałe liczbowe i znakowe
int a = 42; // stała całkowitoliczbowa (int) int b = 0x2A; // stała szesnastkowa (42 w systemie szesnastkowym) int c = 0b101010; // stała binarna (42 w systemie binarnym) int d = 042; // stała ósemkowa (34 w systemie ósemkowym) long e = 42L; // stała typu long int f = 3_000_000; // podkreślenia dla czytelności (3000000) float x1 = 3.14f; // stała typu float double x2 = 3.14; // stała zmiennoprzecinkowa (double) double x3 = 1.5e-3; // stała w notacji naukowej (0.0015) char c1 = 'A'; // stała znakowa (pojedynczy znak) char c2 = 65; // stała znakowa jako liczba (również 'A', bo 65 to kod ASCII dla 'A') char c3 = '\u0041'; // stała znakowa w notacji Unicode (również 'A') char c4 = '\101'; // stała znakowa w notacji ósemkowej (również 'A') char c5 = '\n'; // stała znakowa dla nowej linii String str1 = "Hello"; // stała łańcuchowa (ciąg znaków) String str2 = "Line1\nLine2"; // stała łańcuchowa z nową linią
Rzutowanie typów prostych
- Rzutowanie rozszerzające (automatyczne, bezstratne):
byte→short→int→long→float→double - Rzutowanie zawężające (wymagane jawnie; możliwa utrata informacji):
double→float→long→int→short→byte
int i = (int) 3.14;
- Uwaga: w operacjach arytmetycznych automatyczna promocja wyrażeń
byte,shortichardoint
byte b = 10; b = b*2; // BŁĄD, bo wynik jest int, a nie byte b = (byte)(b*2); // OK, rzutowanie zawężające, wynik jest byte
Tablice
Jednowymiarowa:
Typ[] tablica1D = { ... }; Typ[] tablica1D = new Typ[rozmiar]; Typ tablica1D[] = new Typ[rozmiar]; // alternatywna składnia
Wielowymiarowa:
Typ[][] tablica2D = { { ... }, ..., { ... } }; Typ[][] tablica2D = new Typ[rozmiar1][rozmiar2]; Typ[][] tablica2D = new Typ[rozmiar1][]; // tablica tablic
Dostęp do elementów:
tablica[i]- i-ty element jednowymiarowej tablicytablica[i][j]- element tablicy dwuwymiarowej- indeksowanie od 0
- java.util.Arrays (Java SE 17 & JDK 17) - klasa z metodami pomocniczymi do pracy z tablicami
Łańcuchy znaków
Java posiada rozbudowaną bibliotekę do obsługi łańcuchów znaków (String), dokumentacja:
String str = "abc"; // is equivalent to: char data[] = {'a', 'b', 'c'}; String str = new String(data);
- Stringi są niemodyfikowalne (immutable) - każda operacja tworzy nowy obiekt String
- Do modyfikacji łańcuchów znaków służą klasy
StringBuilderiStringBuffer
Operatory
- Arytmetyczne:
+,-,*,/,%,++,-- - Logiczne:
&&,||,!,^,&,|(bez skracania) - Bitowe (dla
intilong):&,|,~,^,<<,>>,>>>(bez znaku) - Porównania:
==,!=,>,<,>=,<= - Przypisania i operatory skrócone:
=,+=,-=,*=,/=,%=,&=,|=,^=,>>=,<<=
Biblioteka matematyczna
Java dostarcza klasę Math z wieloma przydatnymi funkcjami.
Dokumentacja: Math (Java SE 17 & JDK 17) (oracle.com)
Instrukcja warunkowa if
if (warunek) { // ... } else if (innyWarunek) { // ... } else { // ... }
- warunek musi być typu
boolean
Operatory logiczne i wyrażenia warunkowe
Operatory logiczne bez skracania
if(x != 0 & 10/x > 1) { // BŁĄD, bo x może być równe 0, a wtedy dzielenie przez zero spowoduje wyjątek }
Operatory skracające
if(x != 0 && 10/x > 1) { // OK, bo jeśli x jest równe 0, to drugi operand nie jest sprawdzany }
Operacje warunkowe tylko dla typu boolean:
int x = 5; if (!x) { /* BŁĄD, bo x nie jest typu boolean */ } if (x > 0) { /* OK, bo x > 0 jest wyrażeniem typu boolean */ }
Instrukcja switch
switch (wyrazenie) { case wartosc1: // ... break; case wartosc2: // ... break; default: // ... }
- wyrażenie musi być typu
byte,short,char,int,Stringlub wyliczeniowego (enum)
Pętla while, do while
while (warunek) { // ... }
do { // ... } while (warunek);
Pętle for / foreach
for (inicjalizacja; warunek; inkrementacja) { // ... }
for (Typ zmienna : tablica) { // foreach (pętla rozszerzona) }
- zmienna w pętli foreach tylko do odczytu (nie można jej modyfikować, bo jest tylko kopią elementu z tablicy)
Instrukcje skoku
break- przerywa najbliższą pętlę lub instrukcjęswitchcontinue- przeskakuje do następnej iteracji pętligoto- nie istnieje w Javie (ale jest słowem kluczowym)return- zwraca wartość z metody i kończy jej wykonanietry,catch,finally- obsługa wyjątków. Jeżeli jakaś metoda deklaruje, że operacja rzuca wyjątek, to musi być on obsłużony lub zadeklarowany w sygnaturze metody (throws).
try { // ... } catch (Wyjatek e) { // ... } finally { // ... }
break / continue - skok do etykiety
label: while (warunek) { // ... while(innyWarunek) { // ... if (jeszczeInnyWarunek) { break label; // przerywa pętlę oznaczoną etykietą } } }
label: for (int i = 0; i < n; i = i + 1) { // ... for (int j = 0; j < m; j = j +1) { // ... if (warunek) { continue label; // przeskakuje do następnej iteracji // pętli oznaczonej etykietą } } }
Konwencja nazewnictwa
- klasy i interfejsy: PascalCase (np.
Main,Scanner,String) - metody i zmienne: camelCase (np.
main,nextLine,myVariable) - stałe: UPPER_SNAKE_CASE (np.
MAX_VALUE,PI) - pakiety: lowercase (np.
java.util,myapp)
Operacje I/O w konsoli
Strumień wejściowy System.in - odczyt danych z konsoli:
- klasa
BufferedReaderiInputStreamReader
var reader = new BufferedReader(new InputStreamReader(System.in)); String line = reader.readLine(); // czyta linię tekstu Integer.parseInt(line); // konwersja tekstu na liczbę całkowitą
- klasa
Scanner
Scanner sc = new Scanner(System.in); sc.nextLine(); // czyta linię tekstu sc.nextInt(); // czyta liczbę całkowitą
Strumień wyjściowy System.out
System.out.println("Hello World!"); // wypisuje tekst i przechodzi do nowej linii System.out.print("Hello World!"); // wypisuje tekst bez przechodzenia do nowej linii System.out.printf("Wartość: %.2f", 3.14159); // formatowanie jak w C
Ćwiczenie: silnia
Stwórz pakiet factorial i napisz program FactorialCalculator, który czyta nieujemne $n$ i oblicza silnię $n!$
$$n! = 1 \cdot 2 \cdot ... \cdot n$$.
Spróbuj zrealizować program w taki sposób, aby działał poprawnie dla dużych wartości n (np. 1000). Użyj typu java.math.BigInteger.
Zadanie 1 - Aproksymacja funkcji wykładniczej
Napisz program, który oblicza przybliżenie funkcji wykładniczej $e^x$ przy pomocy szeregu Taylora:
$$e^x = \sum_{n=0}^{\infty} \frac{x^n}{n!}$$
obliczając sumę $n$ pierwszych wyrazów tego szeregu. Użyj typu java.math.BigDecimal do przechowywania wyniku i zapewnij odpowiednią precyzję obliczeń. Kolejne wyrazy szeregu $t_0, t_1, t_2, ...$ umieść w tablicy.
Wskazówki:
- użyj rekurencji wyrazu: $t_0=1, t_{k+1}=t_k \cdot \frac{x}{k+1}$ aby uniknąć wielokrotnego liczenia silni.
- przy dzieleniu liczb
BigDecimalokreśl kontekst matematycznyMathContext(lubscaleiRoundingMode) aby uniknąćArithmeticExceptiondla nieskończonych rozwinięć.
Wejście programu:
- liczba rzeczywista $x$,
- liczba całkowita $n$ (liczba wyrazów szeregu, domyślnie 100),
- precyzja obliczeń (liczba cyfr znaczących, domyślnie $50$).
Wartości domyślne są używane, gdy użytkownik poda pustą linię zamiast liczby.
Wyjście programu:
- przybliżenie $e^x$,
- porównanie z wartością obliczoną przy pomocy funkcji
Math.exp(x).
