bileşke tür etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
bileşke tür etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

2 Mayıs 2011 Pazartesi

Bileşke Türler-Sınıflar

Bir önceki yazımızda🔎 programlama dilinin sunduğu kavram dağarcığının çözülmekte olan probleminki ile örtüşmesi durumunda çözümün daha kolay olacağına değinmiş ve Java gibi genel amaçlı dillerde problem ve çözüm uzayları arasındaki kusursuz bir uyum beklentisinin genel olma iddiasıyla çelişeceğini söyleyerek, gerçek çözümün programcıya yeni bileşke tür tanımlama olanağı sağlamaktan geçtiğini ifade etmiştik. Bu yönde sunduğumuz ilk araç ise türdeş verilerin gruplanmasına yarayan diziler olmuştu. İkinci yazımızın konusu ise, türdeş olsun ya da olmasın, birbirleriyle ilişkili verilerin gruplanması ve bu veri öbeği üzerindeki makul işlemlerin tutarlı bir biçimde uygulanmasını sağlamak için soyut veri türleri tanımlamayı olanaklı kılan sınıf kavramı olacak.1 Daha fazla uzatmadan başlayalım.

Matris.java
...

public class Matris {
  ...
} // Matris sınıfının sonu
Problem tanımında geçen ve Java platformunca doğrudan desteklenmeyen bir kavramın Java koduna aktarılması, söz konusu kavramın bizi ilgilendiren özelliklerini içeren bir sınıfın tanımlanması ile olur. Bu ise yukarıdaki gibi bir kod iskeletinin uygun tanımlarla doldurulması yoluyla sağlanır. Tanımların sağlanması sırasında, tanımları sağlayacak gerçekleştirimci(ler) ile sınıftan yararlanacak kullanıcıların farklı kitleler olduğu unutulmamalıdır; gerçekleştirimcilerin aksine kullanıcılar ayrıntılarla ilgilenmeyecek, sunulan işlevselliğin nasıl realize edildiğine değil, ne olduğuna bakacaktır.

Ne demek istediğimizi, Matris sınıfının içini matematikteki matris kavramının özellikleriyle doldurarak gösterelim. Ama ilk önce, nereden başlamamız gerektiği konusunda bir uzlaşıya varalım. Bu noktada, acemi arkadaşlar ve C/C++ gibi dillerden gelip bellek serilimleri ile oynamaya alışmış ustalara bir uyarı: soyut veri türü tanımlamaya türün yapısını belirleyen bellek seriliminden başlamak doğru olmaz! Mesela, aceleyle atılıp matris elemanlarını tutmak için iki boyutlu bir dizi tanımlamaya soyunmak yerinde bir hamle olmayacaktır. Zira, matris elemanlarını tutmak için iki boyutlu dizinin yanısıra, Vector içeren Vector veya sıra no-sütun no çifti anahtarıyla erişilen kıyım tablosu da kullanılabilir. Matris içeriğinin özelliklerine göre bir seçim bazen iyiyken bazen kötü olabilir.2 Ayrıca, bu konudaki bir karar değişikliği gerçekleştirimimizdeki pek çok şeyin değişmesini gerektirecektir ki, kodumuzun güvenilirlik düzeyine olumsuz etkide bulunan bu tür şeylerden kaçınılmalıdır. Bunun yerine, işimize Java karşılığını ifade etmeye çalıştığımız kavramın değişmeyen yönlerini, özniteliklerini, belirleyerek başlamalıyız.

Biraz programlama yapmış olanlarınız, koda dökülmekte olan kavrama ait varlıkların belirleyici özellikleri olan özniteliklerin, aslında altalanlara verilen yeni bir ad olduğunu düşünebilir. Bu yanılgıyı, altalanların sınıf şablonu kullanılarak yaratılacak nesnelerin özel belleğini oluştururken, özniteliklerin diğer özniteliklerdan yararlanarak hesaplanmak suretiyle de temsil edilebileceğini vurgulayarak ortadan kaldıralım. Örnek olarak, bir üçgenin özniteliklerini düşünün. Kenar uzunlukları ve kenarlar arasındaki açılar, değil mi? Peki ama, bu altı özniteliğin altısı da üçgen kavramının karşılığındaki sınıfta altalan olarak karşılık bulmalı mı? Tabii ki hayır! İstenecek olursa, Sinüs kuralından yararlanarak işimizi dört altalanla da görebiliriz. Dolayısıyla, yapacağımız şey, her bir öznitelik için altalan tanımlamak değil, altalanların tanımını sonraya bırakarak öznitelik değerlerini döndüren/güncelleyen erişici/değiştirici metotları sağlamak olmalıdır.
public class Matris {
  public int sıraSayısı() { ??? }
  public int sütunSayısı() { ??? }
  public double eleman(int sıra, int sütun) { ??? }
  public void elemanGüncelle(int sıra, int sütun, double yeniDeğer) { ??? }
  ...
} // Matris sınıfının sonu
Kıs kıs güldüğünüzü görür gibi oluyorum. Ne de olsa, yukarıdaki metotların gövdesini sağlayabilmemiz için ne çeşit bir kap kullanacağımıza ve altalanlarımızın hangi öznitelikler olacağına karar vermemiz gerekiyor. Doğru ama, ben biraz sorumsuzluk göstererek bu itirazınızı umursamayacağım ve sanki çok güvendiğim birileri metot gövdelerini sağlamış gibi davranacağım. Bazen, hayal görmek de işe yarayabilir. Kabul ettiyseniz bir sonraki adıma geçelim: matrisler üzerinde uygulanabilecek işlemleri tanımlamaya. Ancak, ilk önce metot imzalarının yanlış olduğunu düşünenlerin kafalarındaki soruya yanıt verelim: Hayır, imzaların parametrelerinden biri eksik değil. Tanımlanan tüm işlemler bir matris nesnesi üzerinde etki yapacağı için, bu ortak nesnenin tüm metotlara saklı argüman olarak geçirildiği varsayılıyor. Yani, tüm metotlar işlerini ileti alıcı veya hedef nesne olarak da adlandırılan özel bir matris nesnesi bağlamında yapıyor.
public class Matris {
  ...
  public Matris çarp(Matris sağMatris) { ... }
  public Matris çarp(double skalar) { ... }
  public Matris çıkar(Matris sağMatris) { ... }
  public Matris ters() { ... }
  public Matris topla(Matris sağMatris) { ... }
  // diğer işlemler
  ...
} // Matris sınıfının sonu
Dikkatinizi çekmiştir, sınıf tanımımız çarp adında iki metot içeriyor ve metot adları matematikten alışageldiğimiz +, - ve * gibi evrensel işleçler arasından seçilmemiş. Bu, Java'nın metot aşırı yüklemeyi desteklerken işleç aşırı yüklemeyi desteklemediği anlamına gelir. Metot aşırı yükleme bağlamında, farklı imzalara sahip iki veya daha fazla sayıdaki metot aynı aduzayında yer alabilir. Dolayısıyla, metot adı ve parametre listesinden birisinin farklı olması metotların birlikte tanımlanmasına olanak tanıyacaktır. Parametre sayısının veya karşılıklı parametre türlerinin farklı olması, metot imzalarının farklı olması anlamına geleceği için, örneğimizdeki aynı adlı iki metodun aynı sınıfta tanımlanmaları bir sakınca doğurmayacaktır.3

Her şey yolunda gidip de sınıfımızı tamamladıktan sonra, böylesine bir sınıfın kullanımı aşağıda verilen örnekteki gibi olacaktır. main metodunun gövdesinde new işleci kullanılarak yaratılan 3x5'lik iki matrisin toplamı, m1'e, kendisini m2 ile toplamasını sağlayan topla iletisinin gönderilmesiyle elde ediliyor. İleti alıcı rolünü oynayan m1, iletinin gönderilmesi sonrasında çağrılan Matris sınıfındaki aynı adlı metodun çağrılması sırasında saklı argüman olarak geçirilirken m2 sıradan bir argüman olarak geçiriliyor.
...

public class MatrisKullanıcı {
  public static void main(String[] ksa) {
    Matris m1 = new Matris(3, 5);
    Matris m2 = new Matris(3, 5);
    // matrisleri doldur...
    Matris m3 = m1.topla(m2);
    ...
  } // void main(String[]) sonu
} // MatrisKullanıcı sınıfının sonu
Gelelim eksiklerimizi tamamlamaya. Son adımda listelediğimiz işlemlere karşılık gelen metotlar ile başlayalım. Nasıl ilerlememiz gerektiğine topla adlı metodun gerçekleştirimini vererek açıklık getirelim. Bu amaçla sağladığımız aşağıdaki koda dikkat edecek olursanız, metot gövdesinde altalan ve matris içeriğini tutan kaba herhangi bir atıf yok. Her şey, gerçekleştirim ayrıntılarını saklayan erişici/değiştirici metotlar vasıtasıyla yapılıyor. Dolayısıyla, erişici/değiştirici metotların gövdesini sağladığımız an bu metot gerçekleştirimi de işlevsellik kazanmış olacak. Böylesine bir yaklaşımın artısı, gerçekleştirdiğimiz algoritmayı soyut bir biçimde ifade etmemizi olanaklı kılması ve kodun doğruluğu hakkında daha kolay ikna olmamızı sağlamasıdır.

Kodda yabancı gelebilecek bir diğer öğe, this anahtar sözcüğünün kullanılışı. Özel bir tutacak adı olan this, metoda saklı argüman olarak geçirilen hedef nesneye atıfta bulunmak için kullanılır. Mesela, m1.topla(m2) şeklinde ifade edilen gönderi, ileti alıcı konumundaki m1'in this ile eşleştirilmesine neden olacaktır.

Hatalı gibi gözükebilecek bir diğer nokta, ileti alıcının metot gövdesinde kullanılmasının, ad çakışması sonucu derleme hatasının ortaya çıkacağı durumlar dışında, seçimlik olmasıdır. Yani, istenecek olursa this anahtar sözcüğünün kullanımı es geçilebilir. Bundan dolayıdır ki, m adlı yerel değişkenin ilklenmesinde kullanılan ifade hatalı değildir ve this.sütunSayısı() ile eşdeğerdir.
public class Matris {
  ...
  public Matris topla(Matris sağM) {
    int n = this.sıraSayısı(), m = sütunSayısı()
    if (n != sağM.sıraSayısı() ||
        m != sağM.sütunSayısı()) return null;

    Matris sonuç = new Matris(n, m);
    for (int i = 1; i <= n; i++)
      for (int j = 1; j <= m; j++)
        sonuç.elemanGüncelle(i, j, eleman(i, j) + sağM.eleman(i, j));

    return sonuç;
  } // Matris topla(Matris) sonu
  ...
} // Matris sınıfının sonu
Her şey bitti gibi. Ama durun daha nesnemizin yapısına, yani altalanların ne olacağına karar vermedik. İş böyle olunca, nesnelerimizi yaratırken nesne içeriklerinin ilklenmesi için çağrılacak olan yapıcı metotları da yazamadık. O zaman gelin, düşündük taşındık, iki boyutlu bir dizide karar kıldık diyelim ve bu eksikliği de tamamlayalım.
public class Matris {
  public Matris(int sıraSayısı, int sütunSayısı) {
    _kap = new double[sıraSayısı][sütunSayısı];
  } // yapıcı(int, int)

  public Matris(int sıraSayısı, int sütunSayısı, double ilkDeğer) {
    this(sıraSayısı, sütunSayısı);
    for (int i = 0; i <= sıraSayısı; i++)
      java.util.Arrays.fill(_kap[i], ilkDeğer]);
  } // yapıcı(int, int, double)

  public int satırSayısı() { return _kap.length; }
  public int sütunSayısı() { return _kap[0].length; }
  public double eleman(int i, int j) {
    return _kap[i - 1][j - 1];
  } // double eleman(int, int)
  public double elemanGüncelle(int i, int j, double yeniDeğer) {
    _kap[i - 1][j - 1] = yeniDeğer;
  } // double eleman(int, int)
  ...
  private double[][] _kap;
} // Matris sınıfının sonu
Kod parçasına açıklık getirmeye, hatalı gibi gözükebilecek yapıcı metotlar ile başlayalım. Nesne yaratılması noktasında, yerin ayrılmasını takiben nesnenin tutarlı bir ilk duruma getirilmesini sağlamak görevini gören ve sınıf ile aynı ada sahip yapıcıların dönüş türünün belirtilmesine gerek yoktur.4, 5 new işlecinin kullanılması bağlamında arka planda çağrılan yapıcılar, programcı tarafından doğrudan çağrılmaz. Dikkat çekici ikinci nokta, üç argümanlı yapıcımızın ilk satırı. İleti adı içermeyen bu kullanım, bir yapıcı içinden aynı sınıfın bir diğer yapıcısını çağırmak için kullanılır ve içinde bulunduğu yapıcının birinci komutu olmak zorundadır. Aksi bir durum, derleyici tarafından hata olarak görülecektir. Değinilmesi gereken bir diğer nokta, altalanımızın erişim niteleyicisinin private olması. Bu, altalanın sınıfa özel olduğu ve sadece içinde bulunduğu sınıfın diğer öğeleri tarafından kullanılabileceği anlamını taşır. Nesne yapısını dişarıdan soyutlayan böylesine bir kısıtlamanın sebebini anlamak için altalanın public varsayıldığı aşağıdaki örneği ele alalım.
...

public class MatrisKullanıcı {
  public static void main(String[] ksa) {
    Matris m1 = new Matris(3, 5);
    // matrisi doldur...
    Matris m2 = new Matris(3, 5);
    for (int i = 0; i <= m1.length; i++)
      for (int j = 0; j <= m1[0].length; j++)
        m2[i][j] = m1[i][j] * 7;
    ...
  } // void main(String[]) sonu
} // MatrisKullanıcı sınıfının sonu
Matrisi temsil etmek için iki boyutlu bir dizinin kullanıldığını kabul eden kod parçası, m1 matrisini 7 ile çarpmak için bu ön kabulü kullanmaktadır. Ancak, belki de marjinal bir hız artışı saplantısıyla yazılan bu kod, gerçekleştirimci ile kullanıcı arasında bir bağımlılık oluşturarak aslında zarar vermektedir. Zira, gerçekleştirimcinin matris temsilini daha önceden bahsettiğimiz alternatiflerden birine değiştirmesi, kullanıcı kodunun da—tek gerçekleştirimci kodu varken binlerce kullanıcı kodu olabileceğini unutmayın—değişmesi zorunluluğunu doğuracaktır. Bir başka deyişle, işlevsellik değişmediği halde gerçekleştirimcinin yaptığı bir değişiklik pek çok kullanıcıyı etkileyecektir. Önlenmesi gereken bu felaket senaryosu, kullanıcıya ne sorusunun yanıtını veren ve nadiren değişen öğelerin sunulması, değişme ihtimali bulunan ve nasıl sorusunun yanıtını veren altalanlar ve yardımcı metotlara erişimin kısıtlanması ile mümkün olur.6
 

  1. Yazımızın sınıf kavramını tanıtmaya yönelik olduğu ve anlamayı kolaylaştırmak adına paketler, ayrıksı durumlar gibi daha sonraki yazılarda anlatılacak bazı şeyleri es geçtiği söylenmeli.
  2. Örneğin, genelde küçük matrislerle uğraşacaksak, yerden maliyetli fakat hızlı işlemi olanaklı kılan bir veri yapısı seçilebilir. Ya da, 0 değerinin çok geçtiği seyrek matrislerde 0 değerlerini doğrudan tutmaktansa dolaylı yollardan belirtmeyi tercih edebiliriz....
  3. Dönüş türü bilgisi aşırı yüklemenin geçerliliğinin denetiminde kullanılmaz. Buna göre, sınıfımıza double argüman alıp double döndüren çarp adlı bir metodun eklenmesi hataya neden olacaktır. Böylesine bir kuralın var oluş sebebi, metot dönüş değerinin göz ardı edilmesi opsiyonundan kaynaklanır. Dönüş değerinin göz ardı edilmesi durumunda hangi dönüş türlü metodun kastedildiği bilinemeyeceği için, derleyici ortaya çıkan muallak durumu hata olarak bildirecektir.
  4. Tüm altalanların ilklenmesine gerek yoktur. Bazılarının ilklenmemesi durumunda, Java derleyicisi söz konusu altalanların varsayılan bir ilkdeğere sahip olmasını garanti edecektir. Tamsayılar için 0, kayan noktalı sayılar için 0.0 ve mantıksal değerler için false olan varsayılan ilkdeğer, bileşke türler için tutacağın bir nesneyi göstermediğini belirten null olacaktır.
  5. Bir sınıftaki yapıcı sayısı ihtiyaca göre değişebilir. Gerekli gördüğü takdirde, programcı yapıcı yazmamayı da tercih edebilir. Bu durumda, Java derleyicisi tüm altalanlara uygun ilkdeğerler sağlayan bir argümansız yapıcı sentezleyecektir. Ancak, yapıcı sentezlemenin programcı tarafından sağlanan yapıcıların varlığında söz konusu olmayacağı akılda tutulmalıdır.
  6. Sınıf gerçekleştiriminin bitmesi, her şeyin bittiği anlamına gelmez. Kodun kullanıcılarla buluşması öncesinde, son bir aşama olarak, performansı düşüren noktalarda eniyilemeler (İng., optimization) yapılması düşünülebilir.

20 Nisan 2011 Çarşamba

Bileşke Türler-Diziler

Bir problemin çözümü, problem tanımında geçen varlıkların çözüm ortamındaki araçlar tarafından doğrudan temsil edilmesi durumunda kolaylaşır. Mesela; bir çizimin çizgi, Bézier eğrisi, üçgen, kare gibi çeşitli geometrik nesneleri destekleyen ve bu nesnelere ilişkin çizme, döndürme, öteleme gibi uzmanlaşmış işlemleri sağlayan bir dil kullanılarak oluşturulması daha kolay olacaktır. Matris, denklem gibi matematiksel nesneleri ve ilişkin işlemleri doğrudan destekleyen Maxima ve Matlab gibi dillerin cebir problemleri çözmek için daha uygun olmasının sebebi de aynıdır. Her iki durumda da problem uzayındaki nesneler ile uygulanabilir işlemler ve bu öğelerin soyutlanmasıyla oluşturulan kavramlar ile çözümde kullanılan araçlar arasında eşleme çok basittir. Dolayısıyla, problem uzayını iyi bilen bir programcının üretken olması çok hızlı bir şekilde mümkün olabilmektedir.

Özel amaçlı diller için geçerli olan bu gözlem, Java gibi genel amaçlı olma iddiasındaki diller için geçerli değildir. Böylesine bir yaklaşım, söz konusu dili değişik ilgi gruplarının kavramlarıyla hantallaştıracak ve diğer ilgi gruplarına kullanılmaz hale getirecektir. Bunun yerine programlama dili, ilkel türler🔎 üzerine inşa edilen bir yeni tür tanımlama olanağı sağlar. İşte, yavaş yavaş Java'yı Java yapan bölgeye giriş yapacağımız bu yazıda, Fortran'ın ilk uyarlamalarından bu yana bizimle olan en eski [ve eskimeyen] tür işleciyle (İng., type operator) oluşturulan bileşke türlere, dizilere göz atacağız.1 Ancak, kısaca türdeş verilerin gruplanmasıyla oluşturulan bileşke türler olarak tanımlanabilecek dizilere girmeden önce, diğer bileşke türlerde de sıklıkla kullanacağımız terminolojiyi oluşturacağız.

TutacakVeNesne.java
public class TutacakVeNesne {
  public static void main(String[] ksa) {
    String ad1 = new String("Tevfik");
    String ad2 = new String("Tevfik");
    String ad3 = ad1;
    System.out.println(ad1 == ad2);
  } // void main(String[]) sonu
} // TutacakVeNesne sınıfının sonu
$ javac -encoding utf-8 TutacakVeNesne.java
$ java TutacakVeNesne
false

Yukarıdaki programın çalıştırılması sonrasında 6. satırda yapılan eşitlik denetimi, kimilerinizin beklentilerine aykırı olarak, standart çıktıya—değiştirilmediği müddetçe ekran—false yazacaktır. Bunun sebebi 6.satıra gelinmesiyle oluşan bellek seriliminin aşağıdaki temsili resmi ile açıklanabilir. Görüleceği gibi, 3. ve 4. satırda yaratılan iki nesneye üç değişken kullanılarak atıfta bulunulmaktadır.


Nesne yaratmak için kullanılan new işleci, belleğin yığın bölgesinde yeri ayrılan nesnenin işlenebilmesi için bir tutacak2 döndürmekte ve nesne döndürülen bu tutacağın değerine sahip tanımlayıcılar yoluyla dolaylı bir biçimde kullanılmaktadır. Buna göre, örneğimizde iki nesne yaratılmakta ve söz konusu nesnelerin tutacakları ad1 ve ad2 değişkenlerini ilklemek için kullanılmaktayken, ad1'in ad3'ü ilklemekte kullanılmasıyla iki tutacak3 da aynı nesneyi gösterir hale getirilmektedir. Bunun doğal bir sonucu olarak, tutacakları karşılaştırarak işini gören 6. satırdaki eşitlik denetimi de false döndürmektedir. Çünkü, temsil ettikleri nesnelerin içeriği aynı olmakla birlikte iki tutacak da farklı nesneleri göstermektedir. Eşitlik denetiminin nesne içeriği göz önüne alınarak yapılması isteniyorsa, ad1 tutacağına temsil ettiği nesneyi ad2 tutacağının temsil ettiği nesne ile karşılaştırmasını söyleyen equals iletisinin gönderilmesi gerekir. Aşağıdaki gibi yapılacak ileti gönderiminin sonrasında, ad1'in arkasındaki nesnenin türü olan String sınıfındaki equals metodu çağrılacak ve işlem tamamlanacaktır.
System.out.println(ad1.equals(ad2));
Programcının, yapısı hakkında tahminde bulunup nesneyi tutarsızlığa yol açabilecek binbir yoldan denetimsiz bir şekilde kullanımının önüne geçen bu özellik—tutacak nesne ayrımı—Java'nın programcının bilerek veya bilmeyerek kod güvenliğini tehlikeye atmasına izin vermeyeceğini gösterir. Her şey, nesnenin yaratılması sonrasında döndürülen tutacak aracılığıyla gönderilecek iletilerin çağrılmasına neden olacağı metotlar vasıtasıyla yapılır.4 Tutacak türü ile uyumlu olmayan ileti gönderimleri derleyici tarafından reddedilecektir.

Gelelim dizilere. Derleyici tarafından özel bir biçimde ele alınan sınıflar olan dizi türlerine ait nesneler, diğer bileşke türlü değerler gibi, yaratılır, kullanılır, ihtiyaç duyulmadıkları ilk noktada çöpe dönüşür ve gerekli olursa kapladıkları bellek alanı tekrar kullanılmak üzere çöp toplayıcı tarafından geri döndürülür. Ayrıca, bir dizi nesnesinin kullanımı sırasında eleman sayısının gereksinime göre artmayacağı, bu özelliğin nesnenin yaratılması noktasında sabitlendiği akıldan çıkarılmamalıdır. Aşağıdaki örnek üzerinden görelim.

Diziler.java
import java.util.Scanner;
import static java.lang.System.out;

public class Diziler {
  public static void main(String[] ksa) {
    Scanner grdKnl = new Scanner(System.in);
    int[] notlar = new int[3];
    for (int i = 1; i <= 3; i++) {
      out.print(i + ". notu giriniz: ");
      notlar[i - 1] = grdKnl.nextInt();
    }
    out.println("İkinci dizinin eleman sayısı: ");
    int[] yeniNotlar = new int[grdKnl.nextInt()];
    yeniNotlar = notlar;
    yeniNotlar[1] = notlar[1] + 5;
  } // void main(String[]) sonu
} // Diziler sınıfının sonu
Dizi nesnelerimizin yaratıldığı satırları ele alalım. 7 nolu satırdaki ilkleme komutunda dizi tutacağının adı olan notlar, int[] türüne sahip tanımlanıyor ve her üç elemanı da 0 ilk değerine sahip bir dizi nesnesini gösterecek şekilde ilkleniyor. 13. satırda, benzer bir işlem standart girdiden sağlanacak sayıda elemana sahip bir başka dizi için tekrarlanıyor. Dikkatinizden kaçmamıştır, her iki tutacak da int[] türlü ilan ediliyor; her iki durumda da eleman sayısına dair bilgi tanımda yer almıyor. Diğer bir deyişle, notlar ve yeniNotlar tutacaklarının ikisi de aynı türe sahip olacak şekilde tanımlanıyor.

Dizilere uygulanabilecek işlemler, eleman değerlerinin sorgulanması ve güncellenmesine sınırlıdır. Her iki işlem de, hedef elemanın sırasını belirleyen aritmetiksel deyimin arasına yerleştirildiği köşeli ayraç çiftini ([ ve ]) kullanır. İki işlemden hangisinin istendiği ayraç çiftinin kullanım yerinden anlaşılır. Örneğin, 15. satırdaki atama komutunun sol tarafındaki kullanım söz konusu diziye güncelleme yapıldığını gösterirken, sağ taraftaki kullanım sorgulama yapıldığına işaret eder. İşleme konu elemanın sırasını tayin eden ve indis olarak da adlandırılan aritmetik deyimin 0-başlangıçlı bir değer döndürmesi gerektiği unutulmamalıdır. Buna bağlı olarak, notlar dizisinin son elemanına aşağıdaki ifadeler ile erişilebilir. Opsiyonları anlamaya çalışırken, her dizi nesnesinde bulunan length adlı altalanın dizinin eleman sayısını tuttuğunu unutmayın.
notlar[2] = 333; // notlar[3] değil!!!
notlar[notlar.length - 1] = 333; // Daha iyi bir seçenek.
Belirlendikten sonra değişmeyen eleman sayısı, yenitNotlar dizisinde olduğu gibi çalışma anında belirlenebileceği için yukarıdaki kullanımlardan ikincinin seçilmesi yerinde olacaktır. Aksine bir tercih, her eleman sayısı değişiminde kodun ilişkin yerlerinde değişiklik yapma ihtiyacını doğurur.

Değineceğimiz bir diğer husus, eleman değerlerinin dizi nesnesinin yaratıldığı noktada sağlanması durumunda kullanılabilecek ve kaynak kodu anlamak açısından olumlu bir katkıda bulunan toptan ilkleme komutudur. Aşağıda örnekleri verilen bu komut sayesinde, dizi elemanlarına tanım noktasından farklı bir yerde atama yapılarak kodun uzamasına gerek kalmaz. Dikkat edecek olursanız, toptan ilklemenin kullanıldığı new işlecine dizimizin eleman sayısının verilmesi söz konusu değildir; programcının hatalı bir biçimde belirleme ihtimali bulunan bu değeri derleyici kendisi belirler.
int[] notlar = new int[]{60, 70, 80};
int[] yeniNotlar =
        new int[]{grdKnl.nextInt(), grdKnl.nextInt()};
Daha sonraki kimi yazılarımızda dönme şansını bulacağımız diziler konusunu, şu uyarıyı yineleyerek kapatalım: dizi türleri, derleyici tarafından özel bir biçimde ele alınan sınıflardır. Buna göre, örneğimizdeki notlar tutacağının yeniNotlar'a atanması, yeniNotlar'ın eskiden gösterdiği dizi nesnesini çöpe dönüştürür ve her iki tutacağın da aynı dizi nesnesini paylaşmasına neden olur. Dolayısıyla, bir dizi tutacağı yoluyla yapılacak güncelleme diğeri aracılığyla da görülecektir. Aşağıdaki temsili bellek seriliminden bunun nedenini daha kolay görebilirsiniz.
yeniNotlar = notlar;
yeniNotlar[1] = notlar[1] + 5;

  1. Dizilerin ilk olma şerefi, çalışmakta olan programın karalama defteri olarak düşünülebilecek birincil belleğin, genelde "kutucuklar" dizisi olarak kavramsallaştırılmasına dayanır. Dolayısıyla, sakın ola ki, bileşke türler içinde ilk olması nedeniyle dizilerin yararsız olduğunu düşünmeyin. Daha sonraki yazılarda değineceğimiz kimi olumsuz yönlerine rağmen, veri yapısı olarak dizilerin de kullanımının uygun olduğu pek çok durum olacaktır.
  2. Diğer dillerden aşırma sözcükleri kullanmayı profesyonelliğin belirleyici özelliği zannedenler, ki bu arkadaşlar genelde bir şey anlatmak için değil anlatmamak için konuşurlar, hendıl (İng., handle) demeyi yeğleyebilirler. Ancak, sözcük tercihimdeki mantığı açıklarsam bu arkadaşları da sanırım tarafımıza kazanabiliriz. Bunun için, bir tavayı yemek pişirmekte nasıl kullandığınızı düşünün. Tavanın tutacağı olan sapıyla değil mi? Dolayısıyla, nasıl ki, aklı başında insanlar tavayı sapı yardımıyla kullanırlar, nesneye aracısız erişim imkanının olmadığı Java'da nesneler tutacakları aracılığıyla kullanılırlar.

    Bu noktada, nadiren de olsa tutacak yerine tutamak sözcüğünü önerenlere de ufak bir tavsiyem var. Bir şeye tutunmakta kullanılan gövde üzerindeki oyuk veya çıkıntıların adı olan tutamak, maalesef, doğru bir seçim değil. Çünkü, adını koymaya çalıştığımız kavram nesnenin gövdesinde bulunmuyor ve yapılan atamalarla değişik zamanlarda değişik nesneleri temsil edebiliyor.
  3. Bundan sonraki anlatımımızda, "... nesnesinin tutacağına sahip tanımlayıcı" demektense, tutacak demeyi tercih edeceğiz.
  4. Nesne paradigmasına sadık kalınarak yapılan bu anlatım her zaman geçerli olmayacaktır. static olarak nitelenen metotlar, sadece tüm sınıf üyelerinin paylaştığı ortak özellikleri kullanarak işini görür ve ileti gönderilmeden doğrudan çağrılırlar.