Jak przechowywać dane w obiektach Java

Ostatnia aktualizacja: styczeń 2020 r

Chociaż przycisk drzemki jest prawdopodobnie najczęściej używanym przyciskiem w budziku, nawet prosta AlarmClockklasa potrzebuje kilku dodatkowych funkcji. Na przykład możesz chcieć kontrolować, jak długo budzik pozostanie w trybie drzemki. Aby dodać taką funkcję, musisz zrozumieć, w jaki sposób Java kontroluje dane.

Programiści używają zmiennych w Javie do przechowywania danych, przy czym wszystkie zmienne mają typ danych i nazwę. Typ danych określa wartości, które może przechowywać zmienna. W tym samouczku dowiesz się, jak typy całkowite przechowują liczby całkowite, typy zmiennoprzecinkowe przechowują liczby rzeczywiste, a typy łańcuchowe przechowują ciągi znaków. Następnie zaczniesz używać zmiennych instancji w klasach Java.

Zmienne i typy pierwotne

Nazywane typami pierwotnymi , całkowitymi i zmiennoprzecinkowymi są najprostszymi typami danych w Javie. Poniższy program ilustruje typ całkowy, który może zawierać zarówno dodatnie, jak i ujemne liczby całkowite. Ten program ilustruje również komentarze, które dokumentują twój kod, ale w żaden sposób nie wpływają na program.

/ * * To też jest komentarz. Kompilator ignoruje wszystko od * pierwszego / * do „gwiazdki ukośnika” kończącego komentarz. * * Oto „ukośnik” kończący komentarz. * / public class IntegerTest {public static void main (String [] args) {// Oto deklaracja zmiennej int o nazwie anInteger, // której podajesz początkową wartość 100. int anInteger = 100; // Zadeklaruj i zainicjuj anInteger System.out.println (anInteger); // Wyprowadza 100 // Możesz również wykonywać działania arytmetyczne na typach pierwotnych, używając // standardowych operatorów arytmetycznych. anInteger = 100 + 100; System.out.println (anInteger); // Wyświetla 200}}

Java wykorzystuje również typy zmiennoprzecinkowe, które mogą przechowywać liczby rzeczywiste, czyli liczby zawierające miejsce dziesiętne. Oto przykładowy program:

public class DoubleTest {public static void main (String [] args) {// Oto deklaracja podwójnej zmiennej o nazwie aDouble. // Dajesz również aDouble początkową wartość 5,76. podwójne aDouble = 5,76; // Zadeklaruj i zainicjuj aDouble System.out.println (aDouble); // Wyświetla 5.76 // Możesz także wykonywać działania arytmetyczne z typami zmiennoprzecinkowymi. aPodwójne = 5,76 + 1,45; System.out.println (aDouble); // Wyświetla 7.21}}

Spróbuj uruchomić powyższe programy. Pamiętaj, że musisz je skompilować, zanim będziesz mógł je uruchomić:

javac * .java java IntegerTest java DoubleTest 

Java wykorzystuje cztery typy całkowite i dwa typy zmiennoprzecinkowe, które zawierają różne zakresy liczb i zajmują różne ilości miejsca w pamięci, jak pokazano w poniższych tabelach.

Typy całkowe

RODZAJ Bajt Krótki Int Długo
SIZE (bity) 8 16 32 64
ZASIĘG -128 do 127 -32 768 do 32767 -2 147 483 648 do 2 147 483 647 -263 do 263-1

Typy zmiennoprzecinkowe (format IEEE 754)

 
RODZAJ Zmiennoprzecinkowe o pojedynczej precyzji Zmiennoprzecinkowe podwójnej precyzji
SIZE (bity) 32 64
ZASIĘG +/- 1,18x10-38 do +/- 3,4x1038 +/- 2,23x10-308 do +/- 1,8x10308

Typ string przechowuje ciągi i uchwyty je inaczej niż całki i pływające rodzaje punkt obsługi numerów. Język Java zawiera Stringklasę reprezentującą łańcuchy. Deklarujesz ciąg przy użyciu typu Stringi inicjujesz go ciągiem w cudzysłowie, sekwencją znaków zawartych w podwójnych cudzysłowach, jak pokazano poniżej. Możesz również połączyć dwa ciągi za pomocą +operatora.

// Fragment kodu // Deklaracja zmiennej s typu String, // i inicjalizacja za pomocą ciągu znaków w cudzysłowie "Hello". String s = "Witaj"; // Konkatenacja ciągu w s z łańcuchem ujętym w cudzysłów "Świat" Ciąg t = s + "Świat"; System.out.println (t); // Wyświetla Hello World

Zmienny zakres

Oprócz typu ważną cechą zmiennej jest również zasięg . Zakres określa, kiedy zmienna jest tworzona i niszczona oraz gdzie programista może uzyskać dostęp do zmiennej w programie. Miejsce w programie, w którym deklarujesz zmienną, określa jej zakres.

Do tej pory omówiłem zmienne lokalne , które przechowują tymczasowe dane używane w metodzie. Deklarujesz zmienne lokalne wewnątrz metod i możesz uzyskać do nich dostęp tylko z poziomu tych metod. Oznacza to, że możesz pobrać tylko zmienne lokalne anInteger, które były używane IntegerTesti aDoubleużywane w DoubleTest, z głównej metody, w której zostały zadeklarowane, i nigdzie indziej.

Możesz zadeklarować zmienne lokalne w ramach dowolnej metody. Poniższy przykładowy kod deklaruje lokalną zmienną w AlarmClock snooze()metodzie:

public class AlarmClock {public void snooze () {// Czas drzemki w milisekundach = 5 sekund snoozeInterval = 5000; System.out.println ("ZZZZZ for:" + snoozeInterval); }}

Możesz dostać się snoozeIntervaltylko z snooze()metody, która jest tam, gdzie zadeklarowałeśsnoozeInterval, as shown here: 

public class AlarmClockTest {public static void main (String [] args) {AlarmClock aClock = new AlarmClock (); aClock.snooze (); // To nadal jest w porządku. // Następna linia kodu to BŁĄD . // Nie możesz uzyskać dostępu do snoozeInterval poza metodą snooze. snoozeInterval = 10000; }}

Parametry metody

Parametr sposobu , który posiada zakres zbliżony do zmiennej lokalnej, to kolejny typ zmiennej. Parametry metod przekazują argumenty do metod. Deklarując metodę, określasz jej argumenty na liście parametrów. Przekazujesz argumenty, gdy wywołujesz metodę. Parametry metody działają podobnie do zmiennych lokalnych, ponieważ mieszczą się w zakresie metody, z którą są połączone, i mogą być używane w całej metodzie. Jednak w przeciwieństwie do zmiennych lokalnych parametry metody uzyskują wartość od obiektu wywołującego, gdy wywołuje metodę. Oto modyfikacja budzika, która umożliwia przejście do pliku snoozeInterval.

public class AlarmClock {public void snooze (long snoozeInterval) {System.out.println ("ZZZZZ for:" + snoozeInterval); }}
public class AlarmClockTest {public static void main (String [] args) {AlarmClock aClock = new AlarmClock (); // Przekaż interwał drzemki podczas wywoływania metody. aClock.snooze (10000); // Drzemka na 10000 ms. }}

Zmienne składowe: jak obiekty przechowują dane

Zmienne lokalne są przydatne, ale ponieważ zapewniają tylko tymczasowe przechowywanie, ich wartość jest ograniczona. Ponieważ ich żywotność obejmuje długość metody, w której są zadeklarowane, zmienne lokalne są porównywane do notatnika, który pojawia się za każdym razem, gdy odbierasz połączenie telefoniczne, ale znika po rozłączeniu się. Taka konfiguracja może być przydatna do robienia notatek, ale czasami potrzebujesz czegoś bardziej trwałego. Co ma robić programista? Wprowadź zmienne składowe .

Zmienne składowe - z których są dwie, instancja i statyczne - tworzą część klasy.

Zmienny zakres i żywotność

Programiści implementują zmienne instancji, aby zawierały dane przydatne dla klasy. Zmienna instancji różni się od zmiennej lokalnej charakterem zakresu i czasem życia. Cała klasa stanowi zakres zmiennej instancji, a nie metoda, w której została zadeklarowana. Innymi słowy, programiści mogą uzyskać dostęp do zmiennych instancji w dowolnym miejscu w klasie. Ponadto czas życia zmiennej instancji nie zależy od żadnej określonej metody klasy; to znaczy, że jego żywotność to czas życia instancji, która go zawiera.

Instancje to rzeczywiste obiekty, które tworzysz na podstawie projektu, który projektujesz w definicji klasy. Deklarujesz zmienne instancji w definicji klasy, wpływające na każdą instancję, którą tworzysz z planu. Każda instancja zawiera te zmienne instancji, a dane przechowywane w tych zmiennych mogą się różnić w zależności od instancji.

Rozważ AlarmClockklasę. Przeniesienie snoozeIntervaldo snooze()metody nie jest świetnym projektem. Wyobraź sobie, że za każdym razem, gdy szukałeś przycisku drzemki, musisz wpisać w budziku czas drzemki. Zamiast tego po prostu podaj cały budzik snoozeInterval. Uzupełniasz to za pomocą zmiennej instancji w AlarmClockklasie, jak pokazano poniżej:

public class AlarmClock { // You declare snoozeInterval here. This makes it an instance variable. // You also initialize it here. long m_snoozeInterval = 5000; // Snooze time in millisecond = 5 secs. public void snooze() { // You can still get to m_snoozeInterval in an AlarmClock method // because you are within the scope of the class. System.out.println("ZZZZZ for: " + m_snoozeInterval); } } 

You can access instance variables almost anywhere within the class that declares them. To be technical about it, you declare the instance variable within the class scope, and you can retrieve it from almost anywhere within that scope. Practically speaking, you can access the variable anywhere between the first curly bracket that starts the class and the closing bracket. Since you also declare methods within the class scope, they too can access the instance variables.

You can also access instance variables from outside the class, as long as an instance exists, and you have a variable that references the instance. To retrieve an instance variable through an instance, you use the dot operator together with the instance. That may not be the ideal way to access the variable, but for now, complete it this way for illustrative purposes:

public class AlarmClockTest { public static void main(String[] args) { // Create two clocks. Each has its own m_snoozeInterval AlarmClock aClock1 = new AlarmClock(); AlarmClock aClock2 = new AlarmClock(); // Change aClock2 // You'll soon see that there are much better ways to do this. aClock2.m_snoozeInterval = 10000; aClock1.snooze(); // Snooze with aClock1's interval aClock2.snooze(); // Snooze with aClock2's interval } } 

Wypróbuj ten program, a zobaczysz, że aClock1nadal ma interwał 5000, podczas gdy aClock2ma interwał 10000. Ponownie, każda instancja ma własne dane instancji.

Nie zapominaj, że definicja klasy jest tylko planem, więc zmienne instancji tak naprawdę nie istnieją, dopóki nie utworzysz instancji z planu. Każda instancja klasy ma własną kopię zmiennych instancji, a schemat definiuje, jakie będą te zmienne instancji.

JavaWorld

Kapsułkowanie

Encapsulation is one of the foundations of object-oriented programming. When using encapsulation, the user interacts with the type through the exposed behavior, not directly with the internal implementation. Through encapsulation, you hide the details of a type's implementation. In Java, encapsulation basically translates to this simple guideline: "Don't access your object's data directly; use its methods."

That is an elementary idea, but it eases our lives as programmers. Imagine, for example, that you wanted to instruct a Person object to stand up. Without encapsulation, your commands could go something like this: "Well, I guess you'd need to tighten this muscle here at the front of the leg, loosen this muscle here at the back of the leg. Hmmm -- need to bend at the waist too. Which muscles spark that movement? Need to tighten these, loosen those. Whoops! Forgot the other leg. Darn. Watch it -- don't tip over ..." You get the idea. With encapsulation, you would just need to invoke the standUp() method. Pretty easy, yes?

Some advantages to encapsulation:

  • Abstraction of detail: The user interacts with a type at a higher level. If you use the standUp() method, you no longer need to know all the muscles required to initiate that motion.
  • Isolation from changes: Changes in internal implementation don't affect the users. If a person sprains an ankle, and depends on a cane for a while, the users still invoke only the standUp() method.
  • Correctness: Users can't arbitrarily change the insides of an object. They can only complete what you allow them to do in the methods you write.

Here's a short example in which encapsulation clearly helps in a program's accuracy:

// Bad -- doesn't use encapsulation public class Person { int m_age; } public class PersonTest { public static void main(String[] args) { Person p = new Person(); p.m_age = -5; // Hey -- how can someone be minus 5 years old? } } // Better - uses encapsulation public class Person { int m_age; public void setAge(int age) { // Check to make sure age is greater than 0. I'll talk more about // if statements at another time. if (age > 0) { m_age = age; } } } public class PersonTest { public static void main(String[] args) { Person p = new Person(); p.setAge(-5); // Won't have any effect now. } } 

Nawet ten prosty program pokazuje, jak możesz wpaść w kłopoty, jeśli masz bezpośredni dostęp do wewnętrznych danych klas. Im większy i bardziej złożony program, tym ważniejsza staje się hermetyzacja. Pamiętaj też, że wiele programów zaczyna się od małych, a następnie rozwija się w nieskończoność, dlatego ważne jest, aby zaprojektować je poprawnie, od samego początku. Aby zastosować hermetyzację AlarmClock, możesz po prostu utworzyć metody manipulowania interwałem drzemki.

Uwaga o metodach

Metody mogą zwracać wartości używane przez obiekt wywołujący. Aby zwrócić wartość, zadeklaruj zwrotny typ, który nie jest unieważniony, i użyj returninstrukcji. getSnoozeInterval()Sposób przedstawiony w przykładzie poniżej ilustruje to.

Napisz program