1. Zmienne referencyjne
W języku Java zmienne mogą być dwojakiego rodzaju: zmienne typu pierwotnego i wszystkie inne. To dotyczy „wszystkich innych”, o których teraz porozmawiamy.
W rzeczywistości bardziej poprawne byłoby stwierdzenie, że istnieją zmienne typu pierwotnego i zmienne referencyjne . Czym więc są te zmienne referencyjne?
W przeciwieństwie do typów pierwotnych, które umożliwiają przechowywanie wartości bezpośrednio w zmiennych, zmienne typu referencyjnego przechowują odniesienia do obiektów. Te. gdzieś w pamięci znajduje się obiekt, a zmienna referencyjna po prostu przechowuje adres tego obiektu w pamięci (odniesienie).
Wartości bezpośrednio wewnątrz zmiennych przechowują tylko typy pierwotne , wszystkie inne typy przechowują tylko odniesienie do obiektu . Wcześniej nawiasem mówiąc, spotkałeś się już z dwoma takimi typami zmiennych – są to zmienne typu String
i zmienne typu tablicowego .
Zarówno tablica, jak i ciąg znaków są obiektami przechowywanymi gdzieś w pamięci. Zmienne typu String
i zmienne tablicowe przechowują tylko odwołania do obiektów.
Zmienne int a, int b и double d
są prymitywne i przechowują wartość w sobie.
Zmienna String str
jest zmienną referencyjną i przechowuje adres (referencję) obiektu typu String
w pamięci.
Podczas przypisywania wartości pierwotnej do zmiennej typu pierwotnego jej wartość jest kopiowana (duplikowana). Przy przypisywaniu do zmiennej referencyjnej kopiowany jest tylko adres obiektu , podczas gdy sam obiekt nie jest kopiowany .
2. Istota linków
Jaka jest podstawowa różnica między zmiennymi referencyjnymi a zmiennymi pierwotnymi?
Zmienna pierwotna jest jak pudełko: można w niej przechowywać pewną wartość. Zmienna referencyjna jest bardziej jak kartka papieru z numerem telefonu.
samochód kontra kluczyki do samochodu
Wyobraź sobie, że postanawiasz podarować znajomemu samochód na urodziny. Nie zapakujesz go w pudełko i nie będziesz woził ze sobą: samochód jest na to za duży.
O wiele wygodniej jest zabrać ze sobą tylko kluczyki do samochodu i wystarczająco pojemny dla nich box. A twój przyjaciel wszystko zrozumie, kiedy wyciągnie klucze z pudełka. Nie musisz nosić ze sobą całego samochodu, gdy możesz po prostu przekazać kluczyki.
Mężczyzna kontra jego numer telefonu
Lub inna opcja: osoba i jej numer telefonu. Numer telefonu nie jest samą osobą, ale numer telefonu może być używany do dzwonienia do niego, proszenia go o jakieś informacje, wydawania poleceń.
Łącze służy również do interakcji z obiektem. Wszystkie obiekty oddziałują na siebie za pomocą łączy. Zamiast „wymiany ludzi” wystarczy wymienić się numerami telefonów.
Podczas przypisywania wartości do zmiennej pierwotnej jej wartość jest kopiowana (duplikowana). Podczas przypisywania wartości do zmiennej referencyjnej kopiowany jest tylko adres obiektu („numer telefonu”), podczas gdy sam obiekt nie jest kopiowany.
Referencja ma jeszcze jedną zaletę: możemy przekazać referencję do obiektu jakiejś metodzie, a ta metoda będzie mogła modyfikować (zmieniać) nasz obiekt, korzystając z referencji do niego, wywołując jego metody i uzyskując dostęp do danych wewnątrz obiektu.
3. Przydział referencji
Przypisanie zmiennych referencyjnych po prostu przypisuje adres obiektu w pamięci. Same obiekty nie pojawiają się ani nie znikają.
Takie podejście pozwala uniknąć kopiowania dużych ilości pamięci. Jeśli trzeba gdzieś przekazać bardzo duży obiekt, po prostu przekazujemy do tej metody referencję do obiektu i to wszystko. Link zajmuje znacznie mniej miejsca.
Rozmiar wszystkich zmiennych referencyjnych (niezależnie od typu) jest taki sam i wynosi 4 bajty (jako typ int). Ale! Jeśli Twoja aplikacja działa na 64-bitowej maszynie Java, wszystkie odwołania będą miały rozmiar 8 bajtów (64 bity).
Linki mogą być przypisane tylko do siebie. Nie możesz zmieniać referencji ani przypisywać im dowolnych wartości:
Kod | Opis |
---|---|
|
Więc możesz |
|
A tak to niemożliwe |
|
I tak jest to niemożliwe |
4. Pusty link -null
A co przechowuje zmienna referencyjna, jeśli nic nie zostało jeszcze do niej przypisane?
I przechowuje puste odwołanie - null . null
to specjalne słowo kluczowe w Javie, które oznacza brak referencji (puste referencje). Wartość null
można przypisać dowolnej zmiennej referencyjnej.
Wszystkie zmienne referencyjne, o ile nie przypisano im odniesienia, mają wartość null
.
Przykłady:
Kod | Opis |
---|---|
|
Zmienna String name ma domyślną wartość: null . Zmienna int age ma domyślną wartość: 0 . |
Zmienne lokalne bez wartości są uważane za niezainicjowane zarówno dla typów pierwotnych, jak i typów referencyjnych.
Jeśli zmienna zawiera odniesienie do jakiegoś obiektu i chcesz wymazać wartość zmiennej, po prostu nadaj jej odniesienie zerowe.
Kod | Opis |
---|---|
|
s przechowuje łącze null . s przechowuje odniesienie do obiektu łańcuchowego s przechowuje odniesienienull |
5. Przekazywanie referencji do metod
Jeśli jakakolwiek metoda ma jako parametry zmienne referencyjne , przekazanie do nich wartości jest dokładnie takie samo, jak podczas pracy ze zwykłymi zmiennymi. Zmiennej parametru po prostu przypisuje się wartość innej zmiennej.
Przykład:
Kod | Opis |
---|---|
|
Metoda fill wypełnia przekazaną tablicę array przekazaną wartością value . |
Gdy metoda jest wywoływana, do fill
zmiennej array
przypisywana jest referencja do tablicy data
. Zmiennej value
przypisywana jest referencja do obiektu łańcuchowego „Hello”.
Oto jak wyglądałaby sytuacja w pamięci przed wywołaniem metody fill
:
Oto jak będzie wyglądać sytuacja podczas działania metody fill
:
Zmienne data
i array
referencje (przechowuj referencje) do tej samej tablicy kontenerów w pamięci.
Zmienna value
przechowuje odwołanie do obiektu typu string Hello
.
Komórki tablicowe przechowują również tylko odwołania do obiektów Hello
.
W rzeczywistości nie dochodzi do powielania obiektów - kopiowane są tylko łącza.
6. Porównanie z C/C++
Czasami programiści Javy są pytani podczas wywiadu: W jaki sposób dane są przekazywane do metod w Javie? Czasami też wyjaśniają: przez odniesienie czy przez wartość?
To pytanie pochodzi z języka C++ - nie ma sensu w języku Java . W Javie zmiennym parametru zawsze po prostu przypisuje się wartości zmiennych argumentów. Tak więc poprawną odpowiedzią byłoby - według wartości .
Ale bądź przygotowany na wyjaśnienie swojego stanowiska , ponieważ. możesz natychmiast zarzucić, że „typy pierwotne są przekazywane przez wartość, a typy referencyjne są przekazywane przez referencję”.
Źródła tego problemu wynikają z faktu, że wielu programistów Javy było w przeszłości programistami C++. I tam pytanie „w jaki sposób parametry są przekazywane do metod” odegrało bardzo ważną rolę.
W Javie wszystko jest jednoznaczne: typy pierwotne przechowują wartości, typy referencyjne również przechowują wartość - referencję. Chodzi o to, co należy uważać za wartość zmiennej.
W C++ zmienna może przechowywać zarówno odwołanie do obiektu, jak i sam obiekt. To samo dotyczyło typów pierwotnych: można było przechowywać wartość w zmiennej lub zadeklarować zmienną jako odwołanie do typu int
. Dlatego, aby się nie pomylić, programiści C++ zawsze nazywają referencję do obiektu referencją , a sam obiekt jest zawsze nazywany wartością.
W C++ łatwo może się zdarzyć, że jedna zmienna zawiera obiekt, a druga referencję do tego samego obiektu. Dlatego pytanie o to, co przechowuje w sobie zmienna – sam obiekt, czy tylko odniesienie do niego – było bardzo ważne. Po przekazaniu do metody obiektowej została skopiowana (jeśli została przekazana przez wartość), a nie skopiowana (jeśli została przekazana przez referencję).
Java nie ma tej dwoistości, a poprawna odpowiedź brzmi: parametry do metod Java są przekazywane przez wartość . Tyle, że w przypadku zmiennych referencyjnych ta wartość jest referencją.
GO TO FULL VERSION