Składnia Javy — Java syntax
Składnia z Java odnosi się do zbioru zasad określających, w jaki sposób program Java jest napisane i interpretowane.
Składnia pochodzi głównie z C i C++ . W przeciwieństwie do C++, w Javie nie ma funkcji ani zmiennych globalnych, ale istnieją składowe danych, które są również uważane za zmienne globalne . Cały kod należy do klas, a wszystkie wartości są obiektami . Jedynym wyjątkiem są typy pierwotne , które nie są reprezentowane przez instancję klasy ze względu na wydajność (chociaż mogą być automatycznie konwertowane na obiekty i odwrotnie przez autoboxing ). Niektóre funkcje, takie jak przeciążanie operatora lub typy liczb całkowitych bez znaku, zostały pominięte, aby uprościć język i uniknąć możliwych błędów programistycznych.
Składnia Java była stopniowo rozszerzana w trakcie wielu głównych wydań JDK , a teraz obsługuje takie możliwości, jak programowanie generyczne i literały funkcyjne (nazywane wyrażeniami lambda w Javie). Od 2017 r. dwa razy w roku wypuszczana jest nowa wersja JDK, a każde wydanie przynosi kolejne ulepszenia języka.
Podstawy
Identyfikator
Identyfikator jest nazwą elementu w kodzie . Podczas wybierania nazw elementów należy przestrzegać pewnych standardowych konwencji nazewnictwa . W identyfikatorach w Javie rozróżniana jest wielkość liter .
Identyfikator może zawierać:
- Dowolny znak Unicode, który jest literą (w tym literami numerycznymi, takimi jak cyfry rzymskie ) lub cyfrą.
- Znak waluty (np. ¥).
- Łączenie znaku interpunkcyjnego (takiego jak _ ).
Identyfikator nie może:
- Zacznij od cyfry.
- Być równe zarezerwowanemu słowu kluczowemu, literałowi zerowemu lub literałowi logicznemu.
Słowa kluczowe
abstrakcyjny | kontyntynuj | dla | Nowy | przełącznik |
zapewniać | domyślny | iść do | pakiet | zsynchronizowany |
logiczne | robić | Jeśli | prywatny | ten |
przerwa | podwójnie | przybory | chroniony | rzucić |
bajt | w przeciwnym razie | import | publiczny | rzuty |
Obudowa | wyliczenie | wystąpienie | powrót | przejściowy |
łapać | rozciąga się | int | niski | próbować |
zwęglać | finał | berło | statyczny | var |
klasa | wreszcie | długie | ścisłe fp | próżnia |
stały | pływak | rodzinny | Super | lotny |
podczas |
Literały
Liczby całkowite | |
---|---|
binarny (wprowadzony w Java SE 7) | 0b11110101 ( 0b, a następnie liczba binarna) |
ósemkowy | 0365 ( 0, a następnie liczba ósemkowa) |
szesnastkowy | 0xF5 ( 0x, po którym następuje liczba szesnastkowa) |
dziesiętny | 245 (liczba dziesiętna) |
Wartości zmiennoprzecinkowe | |
pływak | 23.5F , .5f , 1.72E3F (ułamek dziesiętny z opcjonalnym wskaźnikiem wykładnika, a następnie F ) |
0x.5FP0F , 0x.5P-6f ( 0x a następnie ułamek szesnastkowy z obowiązkowym wskaźnikiem wykładnika i sufiksem F ) | |
podwójnie | 23,5D , 0,5 , 1,72E3D (ułamek dziesiętny z opcjonalnym wskaźnikiem wykładnika, a następnie opcjonalnym D ) |
0x.5FP0 , 0x.5P-6D ( 0x, a następnie ułamek szesnastkowy z obowiązkowym wskaźnikiem wykładnika i opcjonalnym sufiksem D ) | |
Literały znakowe | |
zwęglać | 'a' , 'Z' , '\u0231' (znak lub znak ucieczki, ujęty w pojedyncze cudzysłowy) |
Literały logiczne | |
logiczne | prawda , fałsz |
literał zerowy | |
odwołanie zerowe | zero |
Literały ciągów | |
Strunowy | „Hello, World” (sekwencja znaków i ucieczki znaków ujęte w podwójne cudzysłowy) |
Znaki uciekają w ciągi | |
Znak Unicode | \u3876 ( \u, po którym następuje szesnastkowy punkt kodu Unicode do U+FFFF) |
Ucieczka ósemkowa | \352 (liczba ósemkowa nieprzekraczająca 377, poprzedzona odwrotnym ukośnikiem) |
Podawanie linii | \n |
Zwrot karetki | \r |
Kanał formularza | \F |
Ukośnik wsteczny | \\ |
Pojedynczy cytat | \' |
Cudzysłów | \" |
Patka | \T |
Backspace | \b |
Literały całkowite mają int
domyślnie long
typ, chyba że typ jest określony przez dodanie L
lub l
sufiks do literału, np 367L
. . Od wersji Java SE 7 możliwe jest umieszczanie podkreśleń między cyframi w celu zwiększenia czytelności; na przykład numer 145608987 można zapisać jako 145_608_987 .
Zmienne
Zmienne to identyfikatory powiązane z wartościami. Są deklarowane przez zapisanie typu i nazwy zmiennej i opcjonalnie są inicjowane w tej samej instrukcji przez przypisanie wartości.
int count; //Declaring an uninitialized variable called 'count', of type 'int'
count = 35; //Initializing the variable
int count = 35; //Declaring and initializing the variable at the same time
W jednej instrukcji można zadeklarować i zainicjować wiele zmiennych tego samego typu, używając przecinka jako ogranicznika.
int a, b; //Declaring multiple variables of the same type
int a = 2, b = 3; //Declaring and initializing multiple variables of the same type
Od wersji Java 10 stało się możliwe automatyczne wywnioskowanie typów zmiennych za pomocą var
.
// stream will have the FileOutputStream type as inferred from its initializer
var stream = new FileOutputStream("file.txt");
// An equivalent declaration with an explicit type
FileOutputStream stream = new FileOutputStream("file.txt");
Bloki kodu
Separatory { i} oznacza blok kodu i nowy zakres. Członkowie klasy i treść metody są przykładami tego, co może żyć w tych nawiasach klamrowych w różnych kontekstach.
Wewnątrz treści metod nawiasy klamrowe mogą być używane do tworzenia nowych zakresów w następujący sposób:
void doSomething() {
int a;
{
int b;
a = 1;
}
a = 2;
b = 3; // Illegal because the variable b is declared in an inner scope..
}
Uwagi
Java ma trzy rodzaje komentarzy: tradycyjne komentarze , komentarze końca wiersza i komentarze dokumentacji .
Tradycyjne komentarze, znane również jako komentarze blokowe, zaczynają się /*
i kończą na */
, mogą obejmować wiele wierszy. Ten typ komentarza wywodzi się z C i C++.
/* This is a multi-line comment.
It may occupy more than one line. */
Komentarze końca wiersza zaczynają się od //
i rozciągają się do końca bieżącego wiersza. Ten typ komentarza jest również obecny w C++ i nowoczesnym C.
// This is an end-of-line comment
Komentarze dokumentacji w plikach źródłowych są przetwarzane przez narzędzie Javadoc w celu wygenerowania dokumentacji. Ten typ komentarza jest identyczny z tradycyjnymi komentarzami, z wyjątkiem tego, że zaczyna się /**
i jest zgodny z konwencjami zdefiniowanymi przez narzędzie Javadoc. Technicznie rzecz biorąc, komentarze te są specjalnym rodzajem tradycyjnych komentarzy i nie są szczegółowo zdefiniowane w specyfikacji języka.
/**
* This is a documentation comment.
*
* @author John Doe
*/
Typy uniwersalne
Klasy w pakiecie java.lang są niejawnie importowane do każdego programu, o ile żadne jawnie importowane typy nie mają takich samych nazw. Ważne z nich to:
- java.lang.Object
- Najlepszy typ Javy . Nadklasa wszystkich klas, które nie deklarują klasy nadrzędnej. Wszystkie wartości można przekonwertować na ten typ, chociaż w przypadku wartości pierwotnych wiąże się to z automatycznym pakowaniem .
- java.lang.String
- Podstawowy typ łańcucha Java. Niezmienne . Niektóre metody leczenia każdego UTF-16 jednostki kodu jako „znak”, ale metody ma być przekształcona na
int[]
to skutecznie UTF-32 są również dostępne. - java.lang.Throwable
- nadtyp wszystkiego, co można wyrzucić lub złapać za pomocą instrukcji
throw
icatch
instrukcji Javy .
Struktura programu
Aplikacje Java składają się z kolekcji klas. Klasy istnieją w pakietach, ale mogą być również zagnieżdżone w innych klasach.
main
metoda
Każda aplikacja Java musi mieć punkt wejścia. Dotyczy to zarówno aplikacji z interfejsem graficznym, jak i aplikacji konsoli. Punktem wejścia jest main
metoda. Z main
metodą może istnieć więcej niż jedna klasa , ale klasa główna jest zawsze definiowana zewnętrznie (na przykład w pliku manifestu ). Metoda musi być static
i jest przekazywana argumentami wiersza poleceń jako tablica ciągów. W przeciwieństwie do C++ lub C# nigdy nie zwraca wartości i musi zwrócić void
.
public static void main(String[] args) {
}
Pakiety
Pakiety są częścią nazwy klasy i służą do grupowania i/lub odróżniania nazwanych jednostek od innych. Innym celem pakietów jest zarządzanie dostępem kodu wraz z modyfikatorami dostępu. Na przykład java.io.InputStream
jest to w pełni kwalifikowana nazwa klasy, InputStream
która znajduje się w pakiecie java.io
.
Pakiet jest deklarowany na początku pliku z package
deklaracją:
package myapplication.mylibrary;
public class MyClass {
}
Klasy z public
modyfikatorem należy umieścić w plikach o tej samej nazwie i rozszerzeniu java oraz umieścić w zagnieżdżonych folderach odpowiadających nazwie pakietu. Powyższa klasa myapplication.mylibrary.MyClass
będzie miała następującą ścieżkę: myapplication/mylibrary/MyClass.java
.
Deklaracja importowa
Typ deklaracji przywozowej
Deklaracja importu typu umożliwia odwoływanie się do nazwanego typu za pomocą prostej nazwy, a nie pełnej nazwy zawierającej pakiet. Deklaracje importowe mogą być deklaracjami importowymi jednego typu lub deklaracjami importowymi na żądanie . Deklaracje importowe muszą być umieszczone na górze pliku kodu po deklaracji paczki.
package myPackage;
import java.util.Random; // Single type declaration
public class ImportsTest {
public static void main(String[] args) {
/* The following line is equivalent to
* java.util.Random random = new java.util.Random();
* It would've been incorrect without the import.
*/
Random random = new Random();
}
}
Deklaracje importu na żądanie są wymienione w kodzie. „Import typu” importuje wszystkie typy pakietu. „Import statyczny” importuje członków pakietu.
import java.util.*; /*This form of importing classes makes all classes
in package java.util available by name, could be used instead of the
import declaration in the previous example. */
import java.*; /*This statement is legal, but does nothing, since there
are no classes directly in package java. All of them are in packages
within package java. This does not import all available classes.*/
Statyczna deklaracja przywozowa
Ten typ deklaracji jest dostępny od wersji J2SE 5.0 . Deklaracje importu statycznego umożliwiają dostęp do statycznych elementów członkowskich zdefiniowanych w innej klasie, interfejsie, adnotacji lub wyliczeniu; bez podania nazwy klasy:
import static java.lang.System.out; //'out' is a static field in java.lang.System
public class HelloWorld {
public static void main(String[] args) {
/* The following line is equivalent to:
System.out.println("Hi World!");
and would have been incorrect without the import declaration. */
out.println("Hello World!");
}
}
Deklaracje importu na żądanie umożliwiają zaimportowanie wszystkich pól typu:
import static java.lang.System.*;
/* This form of declaration makes all
fields in the java.lang.System class available by name, and may be used instead
of the import declaration in the previous example. */
Stałe wyliczenia mogą być również używane z importem statycznym. Na przykład to wyliczenie znajduje się w pakiecie o nazwie screen
:
public enum ColorName {
RED, BLUE, GREEN
};
Możliwe jest użycie statycznych deklaracji importu w innej klasie do pobrania stałych wyliczenia:
import screen.ColorName;
import static screen.ColorName.*;
public class Dots {
/* The following line is equivalent to 'ColorName foo = ColorName.RED',
and it would have been incorrect without the static import. */
ColorName foo = RED;
void shift() {
/* The following line is equivalent to:
if (foo == ColorName.RED) foo = ColorName.BLUE; */
if (foo == RED) foo = BLUE;
}
}
Operatorzy
Operatory w Javie są podobne do tych w C++ . Jednak nie ma delete
operatora ze względu na mechanizmy garbage collection w Javie i nie ma operacji na wskaźnikach, ponieważ Java ich nie obsługuje. Inną różnicą jest to, że Java ma nieoznaczony operator przesunięcia w prawo ( >>>
), podczas gdy znak prawego operatora przesunięcia w C jest zależny od typu. Operatory w Javie nie mogą być przeciążane .
Precedens | Operator | Opis | Łączność |
---|---|---|---|
1 |
()
|
Wywołanie metody | Od lewej do prawej |
[]
|
Dostęp do tablicy | ||
.
|
Wybór członka klasy | ||
2 |
++ --
|
Przyrost i dekrementacja przyrostka | |
3 |
++ --
|
Przyrost i dekrementacja prefiksu | Od prawej do lewej |
+ -
|
Jednoargumentowy plus i minus | ||
! ~
|
Logiczne NIE i bitowe NIE | ||
(type) val
|
Wpisz obsadę | ||
new
|
Instancja klasy lub tworzenie tablicy | ||
4 |
* / %
|
Mnożenie, dzielenie i moduł (reszta) | Od lewej do prawej |
5 |
+ -
|
Dodawanie i odejmowanie | |
+
|
Łączenie ciągów | ||
6 |
<< >> >>>
|
Bitowe przesunięcie w lewo, przesunięcie w prawo ze znakiem i przesunięcie w prawo bez znaku | |
7 |
< <=
|
Relacje „mniejsze niż” i „mniejsze lub równe” | |
> >=
|
Relacje „większe niż” i „większe niż lub równe” | ||
instanceof
|
Porównanie typów | ||
8 |
== !=
|
Relacyjne „równe” i „nie równe” | |
9 |
&
|
Bitowe i logiczne AND | |
10 |
^
|
Bitowe i logiczne XOR (wyłączne lub) | |
11 |
|
|
Bitowe i logiczne LUB (włącznie lub) | |
12 |
&&
|
Warunek logiczny-AND | |
13 |
||
|
Logiczne warunkowe-LUB | |
14 |
c ? t : f
|
Warunek trójkowy (patrz ?: ) | Od prawej do lewej |
15 |
=
|
Proste zadanie | |
+= -=
|
Przypisanie według sumy i różnicy | ||
*= /= %=
|
Przypisanie według produktu, ilorazu i reszty | ||
<<= >>= >>>=
|
Przypisanie przez bitowe przesunięcie w lewo, przesunięcie w prawo ze znakiem i przesunięcie w prawo bez znaku | ||
&= ^= |=
|
Przypisanie przez bitowe AND, XOR i OR |
Struktury kontrolne
Instrukcje warunkowe
if
oświadczenie
Instrukcje if w Javie są podobne do tych w C i używają tej samej składni:
if (i == 3) doSomething();
if
Instrukcja może zawierać opcjonalny else
blok, w którym to przypadku staje się instrukcją if-then-else:
if (i == 2) {
doSomething();
} else {
doSomethingElse();
}
Podobnie jak C, konstrukcja else-if nie obejmuje żadnych specjalnych słów kluczowych, jest tworzona jako sekwencja oddzielnych instrukcji if-then-else:
if (i == 3) {
doSomething();
} else if (i == 2) {
doSomethingElse();
} else {
doSomethingDifferent();
}
Zauważ też, że operator ?: może być użyty zamiast prostej instrukcji if, na przykład
int a = 1;
int b = 2;
int minVal = (a < b) ? a : b;
switch
oświadczenie
Oświadczenia switch w Javie można użyć byte
, short
, char
oraz int
(uwaga: nie long
) prymitywnych typów danych lub ich odpowiednie typy owijki. Począwszy od J2SE 5.0, możliwe jest użycie typów wyliczeniowych . Począwszy od Java SE 7, możliwe jest użycie ciągów. W instrukcjach nie można używać innych typów odwołańswitch
.
Możliwe wartości są wymienione za pomocą case
etykiet. Te etykiety w Javie mogą zawierać tylko stałe (w tym stałe wyliczenia i stałe łańcuchowe). Wykonanie rozpocznie się po etykiecie odpowiadającej wyrażeniu w nawiasach. Opcjonalna default
etykieta może być obecna, aby zadeklarować, że następujący po niej kod zostanie wykonany, jeśli żadna z etykiet case nie odpowiada wyrażeniu.
Kod każdej etykiety kończy się break
słowem kluczowym. Możliwe jest pominięcie go, powodując przejście do następnej etykiety, jednak zwykle podczas kompilacji zostanie zgłoszone ostrzeżenie.
switch (ch) {
case 'A':
doSomething(); // Triggered if ch == 'A'
break;
case 'B':
case 'C':
doSomethingElse(); // Triggered if ch == 'B' or ch == 'C'
break;
default:
doSomethingDifferent(); // Triggered in any other case
break;
}
switch
wyrażenia
Od wersji Java 14 stało się możliwe używanie wyrażeń przełącznika, które wykorzystują nową składnię strzałek:
var result = switch (ch) {
case 'A' -> Result.GREAT;
case 'B', 'C' -> Result.FINE;
default -> throw new ThisIsNoGoodException();
};
Alternatywnie istnieje możliwość wyrażenia tego samego za pomocą yield
instrukcji, chociaż zaleca się preferowanie składni strzałki, ponieważ pozwala uniknąć problemu przypadkowych przebić.
var result = switch (ch) {
case 'A':
yield Result.GREAT;
case 'B':
case 'C':
yield Result.FINE;
default:
throw new ThisIsNoGoodException();
};
Instrukcje iteracyjne
Instrukcje iteracyjne to instrukcje, które są wykonywane wielokrotnie, gdy dany warunek zostanie oceniony jako prawdziwy. Od J2SE 5.0 Java ma cztery formy takich instrukcji.
while
pętla
W while
pętli test jest wykonywany przed każdą iteracją.
while (i < 10) {
doSomething();
}
do ... while
pętla
W do ... while
pętli test jest wykonywany po każdej iteracji. W konsekwencji kod jest zawsze wykonywany przynajmniej raz.
// doSomething() is called at least once
do {
doSomething();
} while (i < 10);
for
pętla
for
pętle w Javie zawierają inicjator, warunek i wyrażenie licznika. Możliwe jest dołączenie kilku wyrażeń tego samego rodzaju, używając przecinka jako ogranicznika (z wyjątkiem warunku). Jednak w przeciwieństwie do C, przecinek jest tylko ogranicznikiem, a nie operatorem.
for (int i = 0; i < 10; i++) {
doSomething();
}
// A more complex loop using two variables
for (int i = 0, j = 9; i < 10; i++, j -= 3) {
doSomething();
}
Podobnie jak C, wszystkie trzy wyrażenia są opcjonalne. Poniższa pętla jest nieskończona:
for (;;) {
doSomething();
}
Ulepszona for
pętla
Ulepszone for
pętle są dostępne od wersji J2SE 5.0 . Ten typ pętli używa wbudowanych iteratorów nad tablicami i kolekcjami, aby zwrócić każdy element w danej kolekcji. Każdy element jest zwracany i osiągalny w kontekście bloku kodu. Gdy blok jest wykonywany, następny element jest zwracany, dopóki nie ma żadnych pozostałych elementów. W przeciwieństwie do C# ten rodzaj pętli nie obejmuje specjalnego słowa kluczowego, ale zamiast tego używa innego stylu notacji.
for (int i : intArray) {
doSomething(i);
}
Skocz oświadczenia
Etykiety
Etykiety otrzymują punkty w kodzie używanym przez instrukcje break
i continue
. Zauważ, że goto
słowo kluczowe Java nie może być używane do przeskakiwania do określonych punktów w kodzie.
start:
someMethod();
break
oświadczenie
break
Oświadczenie wybucha od najbliższej pętli lub switch
instrukcji. Egzekucja jest kontynuowana w wyciągu po wyciągniętym wyciągu, jeśli taki istnieje.
for (int i = 0; i < 10; i++) {
while (true) {
break;
}
// Will break to this point
}
Z zewnętrznej pętli można wyrwać się za pomocą etykiet:
outer:
for (int i = 0; i < 10; i++) {
while (true) {
break outer;
}
}
// Will break to this point
continue
oświadczenie
continue
Oświadczenie zaprzestaje bieżącej iteracji bieżącej instrukcji kontroli i rozpoczyna następną iterację. Poniższa while
pętla w poniższym kodzie odczytuje znaki przez wywołanie getChar()
, pomijając instrukcje w treści pętli, jeśli znaki są spacjami:
int ch;
while (ch == getChar()) {
if (ch == ' ') {
continue; // Skips the rest of the while-loop
}
// Rest of the while-loop, will not be reached if ch == ' '
doSomething();
}
Etykiety można określić w continue
zestawieniach i break
zestawieniach:
outer:
for (String str : stringsArr) {
char[] strChars = str.toCharArray();
for (char ch : strChars) {
if (ch == ' ') {
/* Continues the outer cycle and the next
string is retrieved from stringsArr */
continue outer;
}
doSomething(ch);
}
}
return
oświadczenie
return
Oświadczenie jest używany do zakończenia realizacji sposobu i zwraca wartość. Wartość zwracana przez metodę jest zapisywana po return
słowie kluczowym. Jeśli metoda zwraca cokolwiek oprócz void
, musi użyć return
instrukcji, aby zwrócić pewną wartość.
void doSomething(boolean streamClosed) {
// If streamClosed is true, execution is stopped
if (streamClosed) {
return;
}
readFromStream();
}
int calculateSum(int a, int b) {
int result = a + b;
return result;
}
return
Instrukcja kończy wykonywanie natychmiast, z wyjątkiem jednego przypadku: jeśli instrukcja zostanie napotkana w try
bloku i zostanie uzupełniona przez finally
, kontrola jest przekazywana do finally
bloku.
void doSomething(boolean streamClosed) {
try {
if (streamClosed) {
return;
}
readFromStream();
} finally {
/* Will be called last even if
readFromStream() was not called */
freeResources();
}
}
Oświadczenia dotyczące obsługi wyjątków
try-catch-finally
sprawozdania
Wyjątki są zarządzane w try
... catch
blokach.
try {
// Statements that may throw exceptions
methodThrowingExceptions();
} catch (Exception ex) {
// Exception caught and handled here
reportException(ex);
} finally {
// Statements always executed after the try/catch blocks
freeResources();
}
Instrukcje w obrębie try
bloku są wykonywane, a jeśli którakolwiek z nich zgłasza wyjątek, wykonywanie bloku jest przerywane, a wyjątek jest obsługiwany przez catch
blok. Może istnieć wiele catch
bloków, w którym to przypadku wykonywany jest pierwszy blok ze zmienną wyjątku, której typ jest zgodny z typem zgłoszonego wyjątku.
Java SE 7 wprowadziła również klauzule multi-catch oprócz klauzul uni-catch. Ten typ klauzul catch umożliwia Javie obsługę różnych typów wyjątków w jednym bloku, pod warunkiem, że nie są one podklasami względem siebie.
try {
methodThrowingExceptions();
} catch (IOException | IllegalArgumentException ex) {
//Both IOException and IllegalArgumentException will be caught and handled here
reportException(ex);
}
Jeśli żaden catch
blok nie pasuje do typu zgłoszonego wyjątku, wykonywanie bloku zewnętrznego (lub metody) zawierającego instrukcję try
... catch
jest przerywane, a wyjątek jest przekazywany poza blokiem zawierającym (lub metodą). Wyjątek jest propagowany w górę przez stos wywołań, dopóki pasujący catch
blok nie zostanie znaleziony w jednej z aktualnie aktywnych metod. Jeśli wyjątek propaguje aż do najwyższej main
metody bez catch
znalezienia pasującego bloku, tekstowy opis wyjątku jest zapisywany w standardowym strumieniu wyjściowym.
Instrukcje w finally
bloku są zawsze wykonywane po blokach try
i catch
, niezależnie od tego, czy został zgłoszony wyjątek, a nawet jeśli osiągnięto return
instrukcję. Takie bloki są przydatne do dostarczania kodu czyszczącego, który gwarantuje zawsze wykonanie.
catch
I finally
bloki są opcjonalne, a przynajmniej jedna lub druga musi być obecny w związku z try
bloku.
try
-oświadczenia o zasobach
try
Instrukcje -with-resources to specjalny typ try-catch-finally
instrukcji wprowadzony jako implementacja wzorca dispose w Java SE 7. W try
instrukcji -with-resources po try
słowie kluczowym następuje inicjalizacja jednego lub więcej zasobów, które są zwalniane automatycznie po try
wykonaniu bloku skończone. Zasoby muszą wdrożyć java.lang.AutoCloseable
. try
Instrukcje -with-resources nie muszą mieć catch
ani finally
bloku, w przeciwieństwie do normalnych try-catch-finally
instrukcji.
try (FileOutputStream fos = new FileOutputStream("filename");
XMLEncoder xEnc = new XMLEncoder(fos)) {
xEnc.writeObject(object);
} catch (IOException ex) {
Logger.getLogger(Serializer.class.getName()).log(Level.SEVERE, null, ex);
}
Od Javy 9 możliwe jest użycie już zadeklarowanych zmiennych:
FileOutputStream fos = new FileOutputStream("filename");
XMLEncoder xEnc = new XMLEncoder(fos);
try (fos; xEnc) {
xEnc.writeObject(object);
} catch (IOException ex) {
Logger.getLogger(Serializer.class.getName()).log(Level.SEVERE, null, ex);
}
throw
oświadczenie
throw
Stwierdzenie stosuje się wyjątek, i kończy się realizację bloku lub metody. Zgłoszone wystąpienie wyjątku jest zapisywane po throw
instrukcji.
void methodThrowingExceptions(Object obj) {
if (obj == null) {
// Throws exception of NullPointerException type
throw new NullPointerException();
}
// Will not be called, if object is null
doSomethingWithObject(obj);
}
Kontrola współbieżności wątków
Java posiada wbudowane narzędzia do programowania wielowątkowego . Do celów wątku synchronizacjisynchronized
oświadczenie zawarte w języku Java.
Aby zsynchronizować blok kodu, jest on poprzedzony synchronized
słowem kluczowym, po którym następuje obiekt blokady w nawiasach. Gdy wątek wykonujący dotrze do zsynchronizowanego bloku, uzyskuje blokadę wzajemnego wykluczenia , wykonuje blok, a następnie zwalnia blokadę. Żadne wątki nie mogą wejść do tego bloku, dopóki blokada nie zostanie zwolniona. Jako blokady można użyć dowolnego typu odwołania innego niż null.
/* Acquires lock on someObject. It must be of
a reference type and must be non-null */
synchronized (someObject) {
// Synchronized statements
}
assert
oświadczenie
assert
oświadczenia są dostępne od wersji J2SE 1.4 . Tego typu instrukcje są używane do tworzenia asercji w kodzie źródłowym, które można włączać i wyłączać podczas wykonywania dla określonych klas lub pakietów. Do zadeklarowania asercji assert
używa się słowa kluczowego, po którym następuje wyrażenie warunkowe. Jeśli oblicza się, false
gdy instrukcja jest wykonywana, zgłaszany jest wyjątek. Ta instrukcja może zawierać dwukropek, po którym następuje inne wyrażenie, które będzie działać jako komunikat szczegółowy wyjątku.
// If n equals 0, AssertionError is thrown
assert n != 0;
/* If n equals 0, AssertionError will be thrown
with the message after the colon */
assert n != 0 : "n was equal to zero";
Typy prymitywne
Typy pierwotne w Javie obejmują typy całkowite, liczby zmiennoprzecinkowe, jednostki kodu UTF-16 i typ logiczny. W Javie nie ma typów bez znaku z wyjątkiem char
typu, który jest używany do reprezentowania jednostek kodu UTF-16. Brak typów bez znaku jest kompensowany przez wprowadzenie operacji przesunięcia w prawo bez znaku ( >>>
), która nie występuje w C++. Niemniej jednak krytykowano brak zgodności z C i C++, który to powoduje.
Typy prymitywne | |||||
---|---|---|---|---|---|
Wpisz imię | Klasa owijarki | Wartość | Zasięg | Rozmiar | Domyślna wartość |
byte
|
java.lang.Byte
|
liczba całkowita | -128 do +127 | 8-bitowy (1-bajtowy) |
0
|
short
|
java.lang.Short
|
liczba całkowita | -32 768 do +32 767 | 16-bitowy (2-bajtowy) |
0
|
int
|
java.lang.Integer
|
liczba całkowita | -2147483648 do +2147483647 | 32-bitowy (4-bajtowy) |
0
|
long
|
java.lang.Long
|
liczba całkowita | -9 223 372 036 854 775 808 do +9 223 372 036 854 775 807 |
64-bitowy (8-bajtowy) |
0
|
float
|
java.lang.Float
|
liczba zmiennoprzecinkowa | ±1.401298E−45 do ±3.402823E+38 | 32-bitowy (4-bajtowy) |
0.0f
|
double
|
java.lang.Double
|
liczba zmiennoprzecinkowa | ±4.94065645841246E-324 do ±1.79769313486232E+308 |
64-bitowy (8-bajtowy) |
0.0
|
boolean
|
java.lang.Boolean
|
Boole'a |
true lub false
|
1-bitowy (1-bitowy) |
false
|
char
|
java.lang.Character
|
Jednostka kodu UTF-16 ( znak BMP lub część pary zastępczej) |
'\u0000' Poprzez '\uFFFF'
|
16-bitowy (2-bajtowy) |
'\u0000'
|
char
niekoniecznie odpowiada jednemu znakowi. Może reprezentować część pary zastępczej , w którym to przypadku punkt kodowy Unicode jest reprezentowany przez sekwencję dwóch char
wartości.
Boksowanie i rozpakowywanie
Ta funkcja języka została wprowadzona w J2SE 5.0 . Boks to operacja konwersji wartości typu pierwotnego na wartość odpowiadającego typu referencyjnego, która służy jako opakowanie dla tego konkretnego typu pierwotnego. Rozpakowywanie to odwrotna operacja konwersji wartości typu referencyjnego (wcześniej zapakowanego) na wartość odpowiedniego typu pierwotnego. Żadna operacja nie wymaga jawnej konwersji.
Przykład:
int foo = 42; // Primitive type
Integer bar = foo; /* foo is boxed to bar, bar is of Integer type,
which serves as a wrapper for int */
int foo2 = bar; // Unboxed back to primitive type
Typy referencyjne
Typy odwołań obejmują typy klas, typy interfejsów i typy tablic. Po wywołaniu konstruktora na stercie tworzony jest obiekt, a do zmiennej przypisywane jest odwołanie. Gdy zmienna obiektu wyjdzie poza zakres, odwołanie jest przerywane, a gdy nie ma żadnych odniesień, obiekt zostaje oznaczony jako śmieci. Po pewnym czasie śmieciarz zbiera je i niszczy.
Zmienna referencyjna ma miejsce, null
gdy nie odwołuje się do żadnego obiektu.
Tablice
Tablice w Javie są tworzone w czasie wykonywania, podobnie jak instancje klas. Długość tablicy jest zdefiniowana podczas tworzenia i nie można jej zmienić.
int[] numbers = new int[5];
numbers[0] = 2;
numbers[1] = 5;
int x = numbers[0];
Inicjatory
// Long syntax
int[] numbers = new int[] {20, 1, 42, 15, 34};
// Short syntax
int[] numbers2 = {20, 1, 42, 15, 34};
Tablice wielowymiarowe
W Javie tablice wielowymiarowe są reprezentowane jako tablice tablic. Technicznie rzecz biorąc, są one reprezentowane przez tablice odniesień do innych tablic.
int[][] numbers = new int[3][3];
numbers[1][2] = 2;
int[][] numbers2 = {{2, 3, 2}, {1, 2, 6}, {2, 4, 5}};
Ze względu na naturę tablic wielowymiarowych, podtablice mogą mieć różną długość, więc tablice wielowymiarowe nie muszą być prostokątne, w przeciwieństwie do C:
int[][] numbers = new int[2][]; //Initialization of the first dimension only
numbers[0] = new int[3];
numbers[1] = new int[2];
Klasy
Klasy są podstawą języka zorientowanego obiektowo, takiego jak Java. Zawierają członków, którzy przechowują i manipulują danymi. Klasy są podzielone na najwyższego poziomu i zagnieżdżone . Klasy zagnieżdżone to klasy umieszczone wewnątrz innej klasy, które mogą uzyskiwać dostęp do prywatnych członków klasy otaczającej. Klasy zagnieżdżone obejmują klasy składowe (które można zdefiniować z modyfikatorem static dla prostego zagnieżdżania lub bez niego dla klas wewnętrznych), klasy lokalne i klasy anonimowe .
Deklaracja
Klasa na najwyższym poziomie |
class Foo {
// Class members
}
|
---|---|
Klasa wewnętrzna |
class Foo { // Top-level class
class Bar { // Inner class
}
}
|
Klasa zagnieżdżona |
class Foo { // Top-level class
static class Bar { // Nested class
}
}
|
Klasa lokalna |
class Foo {
void bar() {
class Foobar {// Local class within a method
}
}
}
|
Klasa anonimowa |
class Foo {
void bar() {
new Object() {// Creation of a new anonymous class extending Object
};
}
}
|
Instancja
Niestatyczne elementy członkowskie klasy definiują typy zmiennych instancji i metod, które są powiązane z obiektami utworzonymi z tej klasy. Aby utworzyć te obiekty, należy utworzyć instancję klasy przy użyciu new
operatora i wywołania konstruktora klasy.
Foo foo = new Foo();
Dostęp do członków
Elementy członkowskie obu wystąpień i klas statycznych są dostępne za pomocą .
operatora (kropka).
Uzyskiwanie dostępu do elementu
członkowskiego wystąpienia Do elementów członkowskich wystąpienia można uzyskać dostęp za pośrednictwem nazwy zmiennej.
String foo = "Hello";
String bar = foo.toUpperCase();
Uzyskiwanie dostępu do statycznego elementu klasy
Statyczne elementy członkowskie są dostępne przy użyciu nazwy klasy lub dowolnego innego typu. Nie wymaga to tworzenia instancji klasy. Statyczne elementy członkowskie są deklarowane przy użyciu static
modyfikatora.
public class Foo {
public static void doSomething() {
}
}
// Calling the static method
Foo.doSomething();
Modyfikatory
Modyfikatory to słowa kluczowe używane do modyfikowania deklaracji typów i członków typu. Przede wszystkim istnieje podgrupa zawierająca modyfikatory dostępu.
-
abstract
— Określa, że klasa służy tylko jako klasa bazowa i nie można jej utworzyć. -
static
— Używany tylko dla klas składowych, określa, że klasa składowa nie należy do określonej instancji klasy zawierającej. -
final
- Klasy oznaczone jakofinal
nie mogą być rozszerzane i nie mogą mieć żadnych podklas. -
strictfp
— Określa, że wszystkie operacje zmiennoprzecinkowe muszą być wykonywane zgodnie z normą IEEE 754 i zabrania używania zwiększonej precyzji do przechowywania wyników pośrednich.
Modyfikatory dostępu
Te modyfikatory dostępu , lub modyfikatory spadkowe ustaw dostępności klas, metod i innych członków. Członkowie oznaczeni jako public
są osiągalni z dowolnego miejsca. Jeśli klasa lub jej element członkowski nie ma żadnych modyfikatorów, zakładany jest dostęp domyślny.
public class Foo {
int go() {
return 0;
}
private class Bar {
}
}
Poniższa tabela pokazuje, czy kod w klasie ma dostęp do klasy lub metody w zależności od lokalizacji klasy uzyskującej dostęp i modyfikatora klasy lub elementu klasy, do którego uzyskuje się dostęp:
Modyfikator | Ta sama klasa lub klasa zagnieżdżona | Inna klasa w tym samym pakiecie | Klasa rozszerzona w innym pakiecie | Nierozszerzony wewnątrz innego opakowania |
---|---|---|---|---|
private
|
tak | nie | nie | nie |
domyślny (pakiet prywatny) | tak | tak | nie | nie |
protected
|
tak | tak | tak | nie |
public
|
tak | tak | tak | tak |
Konstruktory i inicjatory
Konstruktor jest specjalna metoda zwana gdy obiekt jest inicjowany. Jego celem jest inicjalizacja członków obiektu. Główne różnice między konstruktorami a zwykłymi metodami polegają na tym, że konstruktory są wywoływane tylko wtedy, gdy tworzona jest instancja klasy i nigdy niczego nie zwracają. Konstruktory są deklarowane jako wspólne metody, ale są nazwane po klasie i nie określono typu zwracanego:
class Foo {
String str;
Foo() { // Constructor with no arguments
// Initialization
}
Foo(String str) { // Constructor with one argument
this.str = str;
}
}
Inicjatory to bloki kodu, które są wykonywane podczas tworzenia klasy lub wystąpienia klasy. Istnieją dwa rodzaje inicjatorów, inicjatory statyczne i inicjatory wystąpienia .
Inicjatory statyczne inicjują pola statyczne podczas tworzenia klasy. Deklaruje się je za pomocą static
słowa kluczowego:
class Foo {
static {
// Initialization
}
}
Klasa jest tworzona tylko raz. Dlatego inicjatory statyczne nie są wywoływane więcej niż raz. Wręcz przeciwnie, inicjatory instancji są automatycznie wywoływane przed wywołaniem konstruktora za każdym razem, gdy tworzona jest instancja klasy. W przeciwieństwie do konstruktorów inicjatory wystąpień nie mogą przyjmować żadnych argumentów i ogólnie nie mogą zgłaszać żadnych sprawdzonych wyjątków (z wyjątkiem kilku specjalnych przypadków). Inicjatory instancji są deklarowane w bloku bez żadnych słów kluczowych:
class Foo {
{
// Initialization
}
}
Ponieważ Java ma mechanizm wyrzucania śmieci, nie ma destruktorów . Jednak każdy obiekt ma finalize()
metodę wywoływaną przed wyrzucaniem elementów bezużytecznych, którą można zastąpić w celu zaimplementowania finalizacji.
Metody
Wszystkie instrukcje w Javie muszą znajdować się w metodach. Metody są podobne do funkcji, z wyjątkiem tego, że należą do klas. Metoda ma wartość zwracaną, nazwę i zwykle pewne parametry, które są inicjowane, gdy jest wywoływana z pewnymi argumentami. Podobnie jak w C++, metody nie zwracające nic mają typ zwracany zadeklarowany jako void
. W przeciwieństwie do C++, metody w Javie nie mogą mieć domyślnych wartości argumentów , a metody są zwykle przeciążane.
class Foo {
int bar(int a, int b) {
return (a*2) + b;
}
/* Overloaded method with the same name but different set of arguments */
int bar(int a) {
return a*2;
}
}
Metoda jest wywoływana za pomocą .
notacji na obiekcie lub w przypadku metody statycznej również na nazwie klasy.
Foo foo = new Foo();
int result = foo.bar(7, 2); // Non-static method is called on foo
int finalResult = Math.abs(result); // Static method call
Słowo throws
kluczowe wskazuje, że metoda zgłasza wyjątek. Wszystkie zaznaczone wyjątki muszą być wymienione na liście rozdzielanej przecinkami.
void openStream() throws IOException, myException { // Indicates that IOException may be thrown
}
Modyfikatory
-
abstract
- Metody abstrakcyjne mogą być obecne tylko w klasach abstrakcyjnych , takie metody nie mają treści i muszą być nadpisane w podklasie, chyba że sama jest abstrakcyjna. -
static
— Sprawia, że metoda staje się statyczna i dostępna bez tworzenia instancji klasy. Jednak metody statyczne nie mogą uzyskać dostępu do niestatycznych elementów członkowskich w tej samej klasie. -
final
— Deklaruje, że metody nie można przesłonić w podklasie. -
native
— Wskazuje, że ta metoda jest implementowana za pośrednictwem JNI w kodzie zależnym od platformy. Rzeczywista implementacja odbywa się poza kodem Java, a takie metody nie mają treści. -
strictfp
- Deklaruje ścisłą zgodność z IEEE 754 przy wykonywaniu operacji zmiennoprzecinkowych. -
synchronized
— Deklaruje, że wątek wykonujący tę metodę musi uzyskać monitor. W przypadkusynchronized
metod monitor jest instancją klasy lubjava.lang.Class
jeśli metoda jest statyczna. - Modyfikatory dostępu — identyczne z tymi używanymi z klasami.
Varargs
Ta funkcja języka została wprowadzona w J2SE 5.0 . Ostatni argument metody może być zadeklarowany jako zmienny parametr arności, wówczas metoda staje się metodą zmiennej arności (w przeciwieństwie do metod o stałej arności ) lub po prostu metodą varargs . Pozwala to na przekazanie do metody jako parametrów zmiennej liczby wartości zadeklarowanego typu - w tym bez parametrów. Te wartości będą dostępne wewnątrz metody jako tablica.
void printReport(String header, int... numbers) { //numbers represents varargs
System.out.println(header);
for (int num : numbers) {
System.out.println(num);
}
}
// Calling varargs method
printReport("Report data", 74, 83, 25, 96);
Pola
Pola lub zmienne klasy mogą być deklarowane w treści klasy w celu przechowywania danych.
class Foo {
double bar;
}
Pola można inicjować bezpośrednio po zadeklarowaniu.
class Foo {
double bar = 2.3;
}
Modyfikatory
-
static
- Sprawia, że pole staje się elementem statycznym. -
final
— Umożliwia inicjalizację pola tylko raz w konstruktorze lub w bloku inicjującym lub podczas jego deklaracji, w zależności od tego, co nastąpi wcześniej. -
transient
— Wskazuje, że to pole nie będzie przechowywane podczas serializacji . -
volatile
— Jeśli pole jest zadeklarowanevolatile
, zapewnione jest, że wszystkie wątki widzą spójną wartość zmiennej.
Dziedzictwo
Klasy w Javie mogą dziedziczyć tylko z jednej klasy. Klasę można wyprowadzić z dowolnej klasy, która nie jest oznaczona jako final
. Dziedziczenie jest deklarowane za pomocą extends
słowa kluczowego. Klasa może odwoływać się do siebie za pomocą this
słowa kluczowego, a do swojej bezpośredniej nadklasy za pomocą super
słowa kluczowego.
class Foo {
}
class Foobar extends Foo {
}
Jeśli klasa nie określa swojej nadklasy, niejawnie dziedziczy po java.lang.Object
klasie. Zatem wszystkie klasy w Javie są podklasami Object
klasy.
Jeśli nadklasa nie ma konstruktora bez parametrów, podklasa musi określić w swoich konstruktorach, jakiego konstruktora nadklasy użyć. Na przykład:
class Foo {
public Foo(int n) {
// Do something with n
}
}
class Foobar extends Foo {
private int number;
// Superclass does not have constructor without parameters
// so we have to specify what constructor of our superclass to use and how
public Foobar(int number) {
super(number);
this.number = number;
}
}
Nadrzędne metody
W przeciwieństwie do C++, wszystkie final
metody niebędące metodami w Javie są wirtualne i mogą być nadpisane przez klasy dziedziczące.
class Operation {
public int doSomething() {
return 0;
}
}
class NewOperation extends Operation {
@Override
public int doSomething() {
return 1;
}
}
Klasy abstrakcyjne
Klasa abstrakcyjna jest klasa, która jest niekompletna lub uznać za niekompletny. Klasy normalne mogą mieć metody abstrakcyjne, to znaczy metody, które są zadeklarowane, ale jeszcze nie zaimplementowane, tylko jeśli są klasami abstrakcyjnymi. Klasa C ma metody abstrakcyjne, jeśli spełniony jest jeden z poniższych warunków:
- C jawnie zawiera deklarację metody abstrakcyjnej.
- Każda z nadklas języka C ma metodę abstrakcyjną, a C nie deklaruje ani nie dziedziczy metody, która ją implementuje.
- Bezpośredni superinterfejs C deklaruje lub dziedziczy metodę (która jest zatem z konieczności abstrakcyjna), a C nie deklaruje ani nie dziedziczy metody, która ją implementuje.
- Można utworzyć instancję podklasy klasy abstrakcyjnej, która sama nie jest abstrakcyjna, co skutkuje wykonaniem konstruktora dla klasy abstrakcyjnej, a zatem wykonaniem inicjatorów pól dla instancji zmiennych tej klasy.
package org.dwwwp.test;
/**
* @author jcrypto
*/
public class AbstractClass {
private static final String hello;
static {
System.out.println(AbstractClass.class.getName() + ": static block runtime");
hello = "hello from " + AbstractClass.class.getName();
}
{
System.out.println(AbstractClass.class.getName() + ": instance block runtime");
}
public AbstractClass() {
System.out.println(AbstractClass.class.getName() + ": constructor runtime");
}
public static void hello() {
System.out.println(hello);
}
}
package org.dwwwp.test;
/**
* @author jcrypto
*/
public class CustomClass extends AbstractClass {
static {
System.out.println(CustomClass.class.getName() + ": static block runtime");
}
{
System.out.println(CustomClass.class.getName() + ": instance block runtime");
}
public CustomClass() {
System.out.println(CustomClass.class.getName() + ": constructor runtime");
}
public static void main(String[] args) {
CustomClass nc = new CustomClass();
hello();
//AbstractClass.hello();//also valid
}
}
Wyjście:
org.dwwwp.test.AbstractClass: static block runtime
org.dwwwp.test.CustomClass: static block runtime
org.dwwwp.test.AbstractClass: instance block runtime
org.dwwwp.test.AbstractClass: constructor runtime
org.dwwwp.test.CustomClass: instance block runtime
org.dwwwp.test.CustomClass: constructor runtime
hello from org.dwwwp.test.AbstractClass
Wyliczenia
Ta funkcja języka została wprowadzona w J2SE 5.0 . Technicznie wyliczenia są rodzajem klasy zawierającej w swoim ciele stałe wyliczenia. Każda stała enum definiuje wystąpienie typu enum. Klasy wyliczenia nie mogą być tworzone w dowolnym miejscu, z wyjątkiem samej klasy wyliczenia.
enum Season {
WINTER, SPRING, SUMMER, AUTUMN
}
Stałe wyliczenia mogą mieć konstruktory, które są wywoływane podczas ładowania klasy:
public enum Season {
WINTER("Cold"), SPRING("Warmer"), SUMMER("Hot"), AUTUMN("Cooler");
Season(String description) {
this.description = description;
}
private final String description;
public String getDescription() {
return description;
}
}
Wyliczenia mogą mieć treść klasy, w takim przypadku są traktowane jako klasy anonimowe rozszerzające klasę enum:
public enum Season {
WINTER {
String getDescription() {return "cold";}
},
SPRING {
String getDescription() {return "warmer";}
},
SUMMER {
String getDescription() {return "hot";}
},
FALL {
String getDescription() {return "cooler";}
};
}
Interfejsy
Interfejsy to typy, które nie zawierają pól i zazwyczaj definiują pewną liczbę metod bez rzeczywistej implementacji. Przydają się do definiowania kontraktu z dowolną liczbą różnych implementacji. Każdy interfejs jest domyślnie abstrakcyjny. Metody interfejsu mogą mieć podzbiór modyfikatorów dostępu w zależności od wersji językowej strictfp
, co ma taki sam efekt jak dla klas, a także static
od wersji Java SE 8.
interface ActionListener {
int ACTION_ADD = 0;
int ACTION_REMOVE = 1;
void actionSelected(int action);
}
Implementacja interfejsu
Interfejs jest implementowany przez klasę za pomocą implements
słowa kluczowego. Dozwolone jest zaimplementowanie więcej niż jednego interfejsu, w którym to przypadku są one zapisywane po implements
słowach kluczowych na liście oddzielonej przecinkami. Klasa implementująca interfejs musi przesłonić wszystkie jego metody, w przeciwnym razie musi być zadeklarowana jako abstrakcyjna.
interface RequestListener {
int requestReceived();
}
class ActionHandler implements ActionListener, RequestListener {
public void actionSelected(int action) {
}
public int requestReceived() {
}
}
//Calling method defined by interface
RequestListener listener = new ActionHandler(); /*ActionHandler can be
represented as RequestListener...*/
listener.requestReceived(); /*...and thus is known to implement
requestReceived() method*/
Interfejsy funkcjonalne i wyrażenia lambda
Funkcje te zostały wprowadzone wraz z wydaniem Java SE 8. Interfejs automatycznie staje się interfejsem funkcjonalnym, jeśli definiuje tylko jedną metodę. W tym przypadku implementacja może być reprezentowana jako wyrażenie lambda zamiast implementować ją w nowej klasie, co znacznie upraszcza pisanie kodu w stylu funkcjonalnym . Interfejsy funkcjonalne można opcjonalnie opatrzyć @FunctionalInterface
adnotacją, która poinstruuje kompilator, aby sprawdził, czy interfejs rzeczywiście jest zgodny z definicją interfejsu funkcjonalnego.
// A functional interface
@FunctionalInterface
interface Calculation {
int calculate(int someNumber, int someOtherNumber);
}
// A method which accepts this interface as a parameter
int runCalculation(Calculation calculation) {
return calculation.calculate(1, 2);
}
// Using a lambda to call the method
runCalculation((number, otherNumber) -> number + otherNumber);
// Equivalent code which uses an anonymous class instead
runCalculation(new Calculation() {
@Override
public int calculate(int someNumber, int someOtherNumber) {
return someNumber + someOtherNumber;
}
})
Typy parametrów Lambdy nie muszą być w pełni określone i można je wywnioskować z interfejsu, który implementuje. Ciało Lambdy można napisać bez bloku treści i return
instrukcji, jeśli jest tylko wyrażeniem. Również dla tych interfejsów, które mają tylko jeden parametr w metodzie, można pominąć nawiasy okrągłe.
// Same call as above, but with fully specified types and a body block
runCalculation((int number, int otherNumber) -> {
return number + otherNumber;
});
// A functional interface with a method which has only a single parameter
interface StringExtender {
String extendString(String input);
}
// Initializing a variable of this type by using a lambda
StringExtender extender = input -> input + " Extended";
Odniesienia do metod
Nie ma potrzeby używania lambd, gdy istnieje już nazwana metoda zgodna z interfejsem. Ta metoda może być przekazana zamiast lambda przy użyciu odwołania do metody. Istnieje kilka rodzajów odwołań do metod:
Typ referencyjny | Przykład | Równoważna lambda |
---|---|---|
Statyczny | Integer::sum |
(number, otherNumber) -> number + otherNumber
|
Uwiązany | "LongString"::substring |
index -> "LongString".substring(index)
|
Rozwiązany | String::isEmpty |
string -> string.isEmpty()
|
Konstruktor klas | ArrayList<String>::new |
capacity -> new ArrayList<String>(capacity)
|
Konstruktor tablicy | String[]::new |
size -> new String[size]
|
Kod, powyżej którego wywołania runCalculation
można zastąpić następującym przy użyciu odwołań do metod:
runCalculation(Integer::sum);
Dziedzictwo
Interfejsy mogą dziedziczyć z innych interfejsów, podobnie jak klasy. W przeciwieństwie do klas może dziedziczyć z wielu interfejsów. Możliwe jest jednak, że kilka interfejsów ma pole o tej samej nazwie, w którym to przypadku staje się pojedynczym niejednoznacznym elementem członkowskim, do którego nie można uzyskać dostępu.
/* Class implementing this interface must implement methods of both
ActionListener and RequestListener */
interface EventListener extends ActionListener, RequestListener {
}
Metody domyślne
Java SE 8 wprowadziła domyślne metody do interfejsów, co pozwala programistom na dodawanie nowych metod do istniejących interfejsów bez naruszania kompatybilności z klasami, które już implementują interfejs. W przeciwieństwie do zwykłych metod interfejsu, metody domyślne mają treść, która zostanie wywołana w przypadku, gdy klasa implementująca go nie zastąpi.
interface StringManipulator {
String extendString(String input);
// A method which is optional to implement
default String shortenString(String input) {
return input.substring(1);
}
}
// This is a valid class despite not implementing all the methods
class PartialStringManipulator implements StringManipulator {
@Override
public String extendString(String input) {
return input + " Extended";
}
}
Metody statyczne
Metody statyczne to kolejna funkcja języka wprowadzona w Java SE 8. Zachowują się one dokładnie tak samo, jak w klasach.
interface StringUtils {
static String shortenByOneSymbol(String input) {
return input.substring(1);
}
}
StringUtils.shortenByOneSymbol("Test");
Metody prywatne
W wersji Java 9 dodano prywatne metody. Interfejs może mieć metodę z treścią oznaczoną jako prywatna, w takim przypadku nie będzie ona widoczna dla klas dziedziczących. Można go wywołać z metod domyślnych w celu ponownego użycia kodu.
interface Logger {
default void logError() {
log(Level.ERROR);
}
default void logInfo() {
log(Level.INFO);
}
private void log(Level level) {
SystemLogger.log(level.id);
}
}
Adnotacje
Adnotacje w Javie to sposób na osadzenie metadanych w kodzie. Ta funkcja języka została wprowadzona w J2SE 5.0 .
Typy adnotacji
Java ma zestaw predefiniowanych typów adnotacji, ale można zdefiniować nowe. Deklaracja typu adnotacji to specjalny typ deklaracji interfejsu. Są deklarowane w taki sam sposób jak interfejsy, z wyjątkiem tego, że interface
słowo kluczowe jest poprzedzone @
znakiem. Wszystkie adnotacje są niejawnie rozszerzane java.lang.annotation.Annotation
i nie mogą być rozszerzane z niczego innego.
@interface BlockingOperations {
}
Adnotacje mogą mieć te same deklaracje w treści, co wspólne interfejsy, dodatkowo mogą zawierać wyliczenia i adnotacje. Główną różnicą jest to, że deklaracje metod abstrakcyjnych nie mogą mieć żadnych parametrów ani rzucać żadnych wyjątków. Mogą również mieć wartość domyślną, którą deklaruje się za pomocą default
słowa kluczowego po nazwie metody:
@interface BlockingOperations {
boolean fileSystemOperations();
boolean networkOperations() default false;
}
Wykorzystanie adnotacji
Adnotacje mogą być używane w dowolnym rodzaju deklaracji, niezależnie od tego, czy jest to pakiet, klasa (w tym wyliczenia), interfejs (w tym adnotacje), pole, metoda, parametr, konstruktor lub zmienna lokalna. Mogą być również używane ze stałymi wyliczeniami. Adnotacje są deklarowane przy użyciu @
znaku poprzedzającego nazwę typu adnotacji, po którym pary element-wartość są zapisywane w nawiasach. Wszystkim elementom bez wartości domyślnej należy przypisać wartość.
@BlockingOperations(/*mandatory*/ fileSystemOperations,
/*optional*/ networkOperations = true)
void openOutputStream() { //Annotated method
}
Oprócz formy ogólnej istnieją dwie inne formy deklarowania adnotacji, które są skrótami. Adnotacja znacznika jest formą skróconą, stosuje się ją, gdy do elementów nie są przypisane żadne wartości:
@Unused // Shorthand for @Unused()
void travelToJupiter() {
}
Druga krótka forma nazywana jest adnotacją pojedynczego elementu . Jest używany z typami adnotacji zawierających tylko jeden element lub w przypadku, gdy istnieje wiele elementów, ale tylko jeden element nie ma wartości domyślnej. W formularzu adnotacji pojedynczego elementu nazwa elementu jest pomijana, a zamiast niej zapisywana jest tylko wartość:
/* Equivalent for @BlockingOperations(fileSystemOperations = true).
networkOperations has a default value and
does not have to be assigned a value */
@BlockingOperations(true)
void openOutputStream() {
}
Generyki
Generics lub parametryzowane typy lub parametryczny polimorfizm to jedna z głównych funkcji wprowadzonych w J2SE 5.0 . Zanim wprowadzono generyki, wymagane było jawne zadeklarowanie wszystkich typów. Dzięki rodzajom stała się możliwa praca w podobny sposób z różnymi typami bez deklarowania dokładnych typów. Głównym celem generyków jest zapewnienie bezpieczeństwa typów i wykrywanie błędów w czasie wykonywania podczas kompilacji. W przeciwieństwie do C#, informacje o użytych parametrach nie są dostępne w czasie wykonywania z powodu typu erasure .
Klasy ogólne
Klasy można sparametryzować, dodając zmienną typu w nawiasach ostrych ( <
i >
) po nazwie klasy. Umożliwia to użycie tej zmiennej typu w składowych klas zamiast rzeczywistych typów. Może istnieć więcej niż jedna zmienna typu, w którym to przypadku są one deklarowane na liście rozdzielanej przecinkami.
Możliwe jest ograniczenie zmiennej typu do podtypu jakiejś określonej klasy lub zadeklarowanie interfejsu, który musi być zaimplementowany przez typ. W tym przypadku do zmiennej typu dołączane jest extends
słowo kluczowe, po którym następuje nazwa klasy lub interfejsu. Jeśli zmienna jest ograniczona zarówno klasą, jak i interfejsem lub jeśli istnieje kilka interfejsów, najpierw zapisywana jest nazwa klasy, a następnie nazwy interfejsów ze &
znakiem używanym jako ogranicznik.
/* This class has two type variables, T and V. T must be
a subtype of ArrayList and implement Formattable interface */
public class Mapper<T extends ArrayList & Formattable, V> {
public void add(T array, V item) {
// array has add method because it is an ArrayList subclass
array.add(item);
}
}
Gdy zmienna typu sparametryzowanego jest deklarowana lub tworzona jest instancja, jej typ jest zapisywany dokładnie w tym samym formacie, co w nagłówku klasy, z wyjątkiem tego, że rzeczywisty typ jest zapisywany w miejscu deklaracji zmiennej typu.
/* Mapper is created with CustomList as T and Integer as V.
CustomList must be a subclass of ArrayList and implement Formattable */
Mapper<CustomList, Integer> mapper = new Mapper<CustomList, Integer>();
Od wersji Java SE 7 możliwe jest użycie rombu ( <>
) zamiast argumentów typu, w którym to przypadku zostanie wywnioskowany ten drugi. Poniższy kod w Java SE 7 jest odpowiednikiem kodu z poprzedniego przykładu:
Mapper<CustomList, Integer> mapper = new Mapper<>();
Podczas deklarowania zmiennej dla typu sparametryzowanego możliwe jest użycie symboli wieloznacznych zamiast jawnych nazw typów. Symbole wieloznaczne są wyrażane poprzez pisanie ?
znaku zamiast rzeczywistego typu. Możliwe jest ograniczenie możliwych typów do podklas lub nadklas określonej klasy, wpisując extends
słowo kluczowe lub super
słowo kluczowe, po którym następuje nazwa klasy.
/* Any Mapper instance with CustomList as the first parameter
may be used regardless of the second one.*/
Mapper<CustomList, ?> mapper;
mapper = new Mapper<CustomList, Boolean>();
mapper = new Mapper<CustomList, Integer>();
/* Will not accept types that use anything but
a subclass of Number as the second parameter */
void addMapper(Mapper<?, ? extends Number> mapper) {
}
Ogólne metody i konstruktory
Użycie generyków może być ograniczone do pewnych konkretnych metod, ta koncepcja dotyczy również konstruktorów. Aby zadeklarować sparametryzowaną metodę, zmienne typu są zapisywane przed typem zwracanym metody w tym samym formacie, co w przypadku klas ogólnych. W przypadku konstruktora zmienne typu są deklarowane przed nazwą konstruktora.
class Mapper {
// The class itself is not generic, the constructor is
<T, V> Mapper(T array, V item) {
}
}
/* This method will accept only arrays of the same type as
the searched item type or its subtype*/
static <T, V extends T> boolean contains(T item, V[] arr) {
for (T currentItem : arr) {
if (item.equals(currentItem)) {
return true;
}
}
return false;
}
Interfejsy ogólne
Interfejsy można sparametryzować w podobny sposób jak klasy.
interface Expandable<T extends Number> {
void addItem(T item);
}
// This class is parameterized
class Array<T extends Number> implements Expandable<T> {
void addItem(T item) {
}
}
// And this is not and uses an explicit type instead
class IntegerArray implements Expandable<Integer> {
void addItem(Integer item) {
}
}
Zobacz też
Bibliografia
- Patricka Naughtona , Herberta Schildta . Java 2: The Complete Reference , wydanie trzecie. Firmy McGraw-Hill, 1999. ISBN 0-07-211976-4
- Vermeulen; Jednochodziec; Bumgardner; Metz; Misfeldt; Szur; Thompsona (2000). Elementy stylu Java . Wydawnictwo Uniwersytetu Cambridge. Numer ISBN 0-521-77768-2.
- Gąsiątko, Jakub ; Radość, Bill ; Steele, Facet ; Bracha, Gillad (2005). Specyfikacja języka Java (3rd ed.). Addison-Wesley Zawodowiec . Źródło 2008-12-03 .
Zewnętrzne linki
- Specyfikacja języka Java, wydanie trzecie Autorytatywny opis języka Java
- Java SE 10 API Dokumentacja Javadoc