Arşiv

‘Java Teknolojileri’ kategorisi için arşiv

String.substring() ilginçliği

Salı, 25 Oca 2011 Yorum yapılmamış

Benim de geçenlerde şuradaki bir blog yazısından öğrendiğim String sınıfının substring() metodu ile ilgili ilginç bir durumdan bahsedeyim. Yazacaklarımın çoğu çeviri değerinde.

String a = “abcdefgh”;
a = a.substring(0,3);

şeklinde bir kod ile başlangıçta oluşturduğumuz String tipinde a nesnesinin ilk 3 karakterini ayrıştırıp yine kendisine atamış oluyoruz. Oluşan nesne yeni bir String nesnesidir. Çünkü String değişmez(immutable) bir tiptir. Bir kez oluşturulduktan sonra değişmezler yani. Integer, Double gibi ilkel tipleri sarmalayan tipler de öyledirler.

String nesnesinde;

value; char[] tipinde bir değişken. String nesnesinde bulunan karakterleri tutuyor.
count;int tipinde bir değişken. String nesnesinde bulunan karakter sayısını tutuyor.
offset;int tipinde bir değişken. String nesnesinin ilk karakterinin indeksini tutuyor.

şeklinde 3 alan bulunuyor.

substring() işlemi ile char[] dizisi (value) yeni oluşan String nesnesine doğrudan kopyalanıyor. Fakat count ve offset alanlarına uygun karşılıkları atanıyor. Dolayısıyla a nesnesini System.out.print(a); şeklinde konsola yazdıracak olursak “abc” değerlerini görüyoruz. Garip olan şu ki Reflection ile a nesnesinin sahip olduğu karakter dizisine ulaştığımızda gördüğümüz değer “abcdefgh”. (Reflection ile nasıl nesnenin gerçek “value” değerini gördüğümüzün çok önemi olmadığı için kod örneğini yazmaya gerek yok hani). Buradan oluşan yeni String nesnesinin count ve offset değerleri farklı olsa da karakter dizisinin birebir kopya olduğunu anlıyoruz. Örnek koda şuradan bakılabilir.

Bu durum hafıza kullanımında gereksiz şişmelere yol açabiliyor. Özellikle de sık sık substring() metotu ile büyük String nesneleri ayrıştıran bir işlem varsa. Yeri gelmişken bir String nesnesinin hafızada ne kadar yer kapladığını da şu şekilde hesaplıyoruz.

Yukarıda bahsettiğimiz int tipinde count ve offset değişkenlere ilave olarak bir de hashCode için int tipinde bir değişken tutuluyor. Bu 3 int tipindeki değişken 4′er baytdan 12 bayt, nesnenin “object header” denen kimlik bilgisi de 8 bayt yer kaplıyor. Boş karakter dizisi olan char[] değişkeni de 8 bayt karater dizisi, 4 bayt dizinin uzunluğunu tutmak için bulunan değişken, ve 4 bayt da toplamda 16′nın 12′ye en yakın katı olan 16′ya tamamlamak için olmak üzere; 16 bayt yer kaplıyor. 12+8+16=36′yı da bir sonraki 8′in katı olan 40′a tamamlıyoruz. Bu da boş bir String nesnesinin 40 bayt yer kapladığını gösteriyor.

Kabaca şöyle bir formül çıkıyor;
- String nesnesinde bulunan karakter sayısını iki ile çarp,
- 38 ekle,
- Sonuç 8′in katı bir sayı değilse sonraki ilk 8′in katı olan sayıya yuvarla

20 karakter içeren bir String nesnesi;
20*2 = 40,
40+38=78,
78′i de 8′in sonraki ilk katına tamamlarsak ->80 bayt yer kaplıyor

Mevzuya dönecek olursak bu gibi hafıza şişmelerini engellemek için substring() metodundan dönen String nesnesini yine bir String nesnesinin yapılandırıcısına parametre olarak geçiyoruz.

String a = new String(a.substring(0,3));

Bu arada aynı durum String sınıfının trim() metodu için de geçerli. Farkında olmaya değer bir ilginçlik.

Hibernate @Version – OptimisticLockException

Çarşamba, 29 Ara 2010 Yorum yapılmamış

Hibernate, @Version ile işaretlenen kolon sayesinde bir oturumda açılan varlık nesnesinin, diğer bir oturumda değiştirilmesine izin vermeyerek OptimisticLockException fırlatır. Bu istisna yakalanarak ilgili işlemler yapılabilir. Değişiklik yapılmak istenen varlık nesnesinin başka bir oturumda değiştirildiği belirtilerek kullanıcı yönlendirilebilir örneğin.

@Version ile işaretlenmiş kolon Long, Integer veya TimeStamp tipinde olabilir. Hibernate bu nesne (veritabanında karşılığı olan satır) üzerinde gerçekleştirilen persist() ve sonraki her bir merge() işlemi ile beraber versiyon kolonunun değerini bir arttırır.

@Entity
public class Flight implements Serializable {

@Version
@Column(name=”OPTLOCK”)
private Integer version {

}

}

Şöyle ifade edebiliriz sanırım; Nesnenin veritabanından okudunduğu zamanki versiyonuna ilkVersiyon, yapılan değişiklikler veritabanına yansıtılmadan hemen evvelki versiyona sonVersiyon dersek Hibernate API’nin değişiklikleri veritabanına yansıtan metodu olan EntityManager.flush() ilkVersiyon.equals(sonVersiyon) kontrolu yapar. İki versiyon değeri eşitse değişiklikleri veritabanına yansıtır, farklıysa -ki bu ilgili satırın bir başka oturumda değiştirildiği anlamına gelir- OptimisticLockException fırlatır. Bu durumdaki nesne “stale object” tanımına girer.

Bahsettiğim hata;

javax.ejb.EJBException: javax.persistence.OptimisticLockException: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)

gibi bir hatadır.

Burada dikkat edilmesi gereken EntityManager sınıfının executeUpdate() metodunun bu versiyon alanının değerini arttırmadığı. Bu yüzden ilgili varlık sınıfları için böylesi bir işlem yapılmadığına dikkat etmek gerekiyor ki neden OptimisticLockException almıyoruz diye debelenmeyelim.

Ve java gurusu abilerimizden Hakan Uygun der ki, bu yöntem ilgili nesneyi barındıran her veritabanı işlemi için (yukarıdaki gibi bir versiyon numarası eşitliği gerçekleştirmek adına) bir sorgu daha çekmek demek. Dolayısıyla bu kolonu öyle ilgili ilgisiz her varlık sınıfına eklememek gerekir. Ayrıca bir çözüm değil, sadece böylesi eş zamanlı düzenleme işlemlerinde alınabilecek hataları engellemek adına uygulanabilecek bir yöntemdir.

Konuyla ilgili daha detaylı bilgi için şuraya bakılabilir.

Eclipse Debug Pratikleri

Cumartesi, 18 Ara 2010 Yorum yapılmamış

Eclipse’in bir kaç debug görünümü özelliğinden bahsedeyim.

Drop to frame drop_to_frame

Bu sayede herhangi bir debug işlemi sırasında en baştaki breakpoint noktasında dönebiliyorsunuz. Tabi bu arada çalışan kodun meydan getirdiği değişiklikleri geri alma gibi bir özellik söz konusu değil.

Watchpoint

Herhangi bir genel değişkenin (global variable) olduğu satıra çift tıklayarak eklenebilir. Böylece değişken her değişikliğe uğradığında debug görünümüne dönüş yapılabilir.

Add java exception breakpoint addJavaException

Bu özellik ile de kodun çalışması sırasında meydana gelebilecek herhangi bir istisna yakalanıp, bu istisna durumunun incelenebilmesini sağlanıyor. Burada eklenecek istisna Java’da bütün istisnaların türediği sınıf olan “Exception” seçilirse mesela bütün istisna durumlarında Eclipse debug görünümüne dönebiliyor, veya özel bir “Exception” (OptimisticLockException, SQLGrammarException vs.) türü belirterek onu yakalayabiliyoruz. Bu daha çok yakalanmayan veya yakalama gereği duyulmayan “NullPointerException”, “IndexOutOfBoundsException” gibi istisna durumlarında faydalı olur gibi geliyor.

Skip all breakpointsskipAllBreakPoints

Pratikte çok işe yarayan, çoğu Eclipse kullanıcısının biliyor olması gereken bir özellik. Bu sayede tek tek breakpoint leri devre dışı bırakmak yerine, bütün breakpoint leri devre dışı bırakarak kodun normal çalışmasını takip edebiliyoruz.

Enable when – Hit count

Son olarak, pratikte çok ihtiyaç olmazmış gibi gözükse de, bir breakpoint in ne zaman aktif olacağını o breakpoint in özellikler kısmından “Enable when” kısmına yazacağımız kod ile belirleyebiliyoruz. Şöyle bir durumda işe yaramışlığına şahidim; Diyelim ki 200 elamana sahip bir listede for döngüsü ile işlem yapıyoruz. Hatanın da 50 ila 60. elamanlar arasında bir yerlerde olduğunu biliyoruz.

for(int i = 0; i < list.size(); i++){
...
}

Böyle bir durumda “Enable When” kısmına i >= 50; yazarak ilgili breakpoint in 50.adımla beraber aktif olmasını sağlamış, biraz zaman biraz sabır kazanmış oluyoruz.

Yine ilgili breakpoint in özellikler kısmından kaç kereden sonra pasif olacağını da “Hit count” ile belirleyebiliyoruz.

Java – Object – Clone() Metodu

Perşembe, 25 Kas 2010 5 yorum

Java’da bütün sınıfların türediği Object sınıfının bir metodu olan clone() ile bir nesnenin kopyasını almak mümkün. Fakat pratikte bir takım sıkıntılar ile karşılaşılabiliyor. Bunun sebebi clone() metodunun primitive olmayan tipler için, nesnenin kopyasını almak yerine bu nesnenin referansını kopyalıyor olması. Buna “Shadow Copy” deniyor. Bu gibi durumlarda clone() metodunu ezip, nasıl bir kopyalama yapacağını belirlememiz gerekiyor. Sınıf tipi (Class Type) alanları da “gerçekten” kopyaladığımız bu tür kopyalamaya da “Deep Copy” deniyor.

Tabi kopyasını almak istediğimiz sınıfın “Cloneable” arayüzünü gerçeklemesi gerekiyor. Bu şekilde sınıf tanımında “implements Cloneable” şeklinde bir belirtim yapmazsak “CloneNotSupportedException” fırlatılıyor. Daha sonra bir “Deep Copy” gerekiyor ise, ki gerçekte muhtemelen gerekiyordur, clone() metodunu eziyoruz (override). Bu gibi “Deep Copy” yapmak istediğimiz durumlarda yapabileceğimiz iki şey var. Birincisi gerçek kopyasını almak istediğimiz sınıfın sınıf tipindeki alanlarini da “Cloneable” yapıp bahsi geçen clone() metodunda bu clone() metotlarını çağırıp, bu sınıf tipindeki alanlara atamak.

@Override
public Object clone() {
Employee e = null;
try {
e = (Employee) super.clone();
Department d = e.getDepartment().clone();
e.setDepartment(d);
} catch (CloneNotSupportedException cnsex) {
//TODO:catch this
}
return e;
}

İkincisi ise clone() metodunda gerçek nesneler oluşturarak bunları kopyalanmak istenen nesnenin sınıf tipindeki alanlara atamak.

@Override
public Object clone() {
Employee e = null;
try {
e = (Employee) super.clone();
e.setSalary(BigDecimal.ZERO);
} catch (CloneNotSupportedException cnsex) {
//TODO:catch this
}
return e;
}

Önemli bir diğer ilke de kopyalama sonucu oluşan kopya nesne ile kopyalanan asıl nesne için kopyaNesne != asılNesne, yine kopya işlemi sonrası ilk aşamada kopyaNesne.equals(asılNesne) ve kopyaNesne.getClass() != asılNesne.getClass() ifadelerinin her zaman doğru değer döndürmesi gerektiğidir. Son olarak kopyalanmak istenen sınıfın List veya Set gibi Collection sınıfından türeyen alanları mevcutsa ve bunları da gerçekten kopyalamak istiyorsak, bu listelerin her bir elemanını tek tek kopyalamamız gerektiğini unutmamak gerekiyor.

Bu kadar yazdık ettik ama yine de Java gurusu abilerimiz nesne kopyalama işlemleri için clone() metoduna çok güvenmeyip ve bağlı kalmayıp bunun yerine istediğimiz şekilde kopyalamayı gerçekleştiren yapılandırıcı metotlar yazmamızı salık veriyorlar bize.

Güncelleme : Örnek koda ve kod yorumlarına şuradan bakılabilir. Umarım daha açıklayıcı olmuştur.

Ayrıca konu ile ilgili faydalı olduğunu düşündüğüm şuraya da bakmakta fayda var.

Kategoriler:Java Etiketler:, , ,

Java Collections.sort() – Collator

Pazar, 29 Ağu 2010 1 yorum

Java’nın Collections sınıfının faydalı bir bir metotundan bahsedeyim. Listeleri istediğimiz gibi sıralamamız sağlayan sort() metodu.

Bu gibi sıralama işlemlerinde en çok yaşanan sıkıntılardan bir tanesi String türünde verileri kıyaslarken ortaya çıkan Türkçe karakter sorunu. Şırnak, Çanakkale, Ordu illerini sıraladığımızda Çanakkale, Ordu, Şırnak sırasını beklerken mesela, Ordu-Çanakkale-Şırnak şeklinde bir sıranın ortaya çıkması gibi. Bu gibi durumlarda yerel karakter duyarlılığıyla kıyas yapabilen ve parametre olarak bir Locale alabilen Collator sınıfı yardımcı olacaktır.

Bahsettiğimiz sort() metodunun iki versiyonu bulunuyor;

Collections.sort(List < T > list); ArrayList, LinkedList, Vector gibi List interface ini implement eden bir sınıfı parametre olarak alıyor. Metodu bu şekli ile kullanabilmemiz için listenin barındırdığı elemanların tipi olan sınıf, Comparable sınıfını implement etmek durumunda.

Sadece kitap adını tutan, Kitap adında basit bir sınıfımız olduğunu düşünelim. Bu sınıf implement ettiği Comparable interface’ nin compareTo() metodunu implement etmek durumunda.

public class Kitap implements Comparable {

private String ad;

public String getAd() {
return ad;
}

public void setAd(String ad) {
this.ad = ad;
}

@Override
public int compareTo(Kitap kitap) {
Collator collator = Collator.getInstance(Locale.getDefault());
//veya Collator trCollator = Collator.getInstance(new Locale(“tr”, “TR”));
return collator.compare(this.getAd(), o.getAd());
}
}

Artık kitaplarımızı şu şekilde sıralayabiliriz;


List<Kitap> kitapListesi = getKitaplar();
Collections.sort(kitapListesi);

Collections.sort(List < T > list, Comparator < T > comparator) ; metodun bu versiyonu biri List diğeri de Comparator interface lerini implement eden sınıfları parametre olarak alıyor. Kitaplarımızı sıralama işlemi için bu kez Comparator interface ini implement eden Comparator bir sınıf tanımlıyoruz. Yine kitap adına göre sıraladığımızı varsayarsak compareTo() metodu şu şekilde olacaktır;

class KitapComparator implements Comparator<Kitap>{

@Override
public int compare(Kitap kitap1, Kitap kitap2) {
Collator collator = Collator.getInstance(Locale.getDefault());
//veya Collator trCollator = Collator.getInstance(new Locale(“tr”, “TR”));
return collator.compare(kitap1.getAd, kitap2.getAd());
}
}

gibi kitaplarımızı karşılaştıran bir sınıf tanımladıktan sonra


List<Kitap> kitapListesi = getKitaplar();
Collections.sort(kitapListesi, new KitapComparator());

gibi bir ifade ile de kitapListesi sıralanmış bir hal alıyor.

Benzer şekilde gereklilikler doğrultusunda comparator sınıflar tanımlayıp ihtiyaç halinde kullanılabilir.

Lambdaj Kütüphanesi

Cumartesi, 21 Ağu 2010 1 yorum

Java Collections API sınıflarını kullanırken bolca for döngüleri kurar veya bu tip veriler üzerinden istediğimiz işlemleri gerçekleştirmek için çeşitli kod blokları yazarız. Lambdaj kütüphanesi bu tip kod blokları ile yaptığımız işlemleri çoğu kere tek satırlık kodlarla halletmemizi sağlayan bir arayüz sunuyor bize. Böylece listelere bir veri kaynağıymışcasına hemen her türlü seçim, sıralama, gruplama, dönüştürme, indeksleme ve benzeri işlemleri gerçekleştirebiliyoruz. Hatta zaman zaman for döngüleri ile halledemeyeceğimiz işleri bile karşılayabiliyor.

Elbetteki kod okunurluğunu arttırıp, bakımının kolaylaşmasını sağlarken performansı aşağı çekiyor. Her ne kadar performanstan ciddi kayıplar yaşatabilse de, varlığının bilinmesi kimi zamanlar çözüm getirmekte zorlandığımız problemler için faydalı olacaktır. Bu konuda Lambdaj ile normal tekrarlamalı yöntemler arasındaki performans kıyaslaması yapan şu makaleye göz atılabilir.

Lambdaj‘ ın yetenekleri ile ilgili bazı örnekler vermek gerekirse;

Person tipinde nesneler tutan listemizde her bir Person nesnesinin adını tek satırda bilmem ne olarak setlemek;

List personInFamily = getPersons();
forEach(personInFamily).setLastName(“Dalton”);

Person tipinde nesneler tutan listemizde yaşı 30′dan büyük olanları ayıklamak;

List = getPersons();
List oldFriends = filter(having(on(Person.class).getAge(), greaterThan(30)), meAndMyFriends);

Person tipinde nesneler tutan listemizde toplam yaşı bulmak;

int totalAge = sum(meAndMyFriends, on(Person.class).getAge());

Person tipinde nesneler tutan listemizi yaşa göre sıralamak;

List sorted = sort(persons, on(Person.class).getAge());

Person tipinde nesneler tutan listemizdeki Person nesnelerinin isimlerini birleştirmek

String friendNames = joinFrom(myFriends).getFirstName();

Person tipinde nesneler tutan listemizden adı Ali olan kaydi bulmak;

List personList = getPersons();
List personListOfAli = select(personList , having( on(Person.class).getFirstName(), equalTo(“Ali”)));

Örnek kodlar aynı zamanda projenin web sayfası olan şuradan alınmıştır. Ayrıca şuradaki dökümandan daha ayrıntılı bilgi alabilir.

Object references an unsaved transient instance

Cuma, 07 May 2010 Yorum yapılmamış

Hibernate API ‘yi kullananlar TransientObjectException:Object references an unsaved transient instance – save the transient instance before flushing hatası işle karşılaşmışlardır mutlaka. Şöyle bir örnek üzerinden açıklayacak olursak; Araba ve Kullanici adli iki modelimiz olduğunu ve Araba modelinde

@ManyToOne()
@JoinColumn(name = “KULLANICI_ID”)
private Kullanici kullanici;

şeklinde bir Kullanici alanımız olduğunu varsayalım. Aksiyon sınıfında;


araba.setKullanici(kullanici);
entityManager.persist(araba);
entityManager.flush();

şeklinde bir kod bloğu yukarıdaki hatayı almamıza sebep olacaktır. Kullanici nesnesi sessionda varolmasına rağmen henüz veritabaninda mevcut değildir, yani “transient object” tanımına girmektedir. Araba tablosunda “foreign key” olarak bulunan KULLANICI_ID alanına yerleştirilecek bir ID yoktur ortalıklarda. Dolayısıyla ilişki kurulamaz. Hatayı iki şekilde gidermek mümkün. Birincisi kod bloğunu;


araba.setKullanici(kullanici);
entityManager.persist(araba.getKullanici());
entityManager.flush();

entityManager.persist(araba);
entityManager.flush();

şeklinde düzenleyip önce Kullanici nesnesini veritabanına yazmak, ikincisi de ilişkiyi

@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinColumn(name = “KULLANICI_ID”)
private Kullanici kullanici;

şeklinde tanımlayarak bu Araba veritabanına kaydedilirken ve güncellenirken Kullanici nesnesinin de veritabanina yazilmasini sağlamak. Elbette ki sağlıklı olan annotation da ilişkinin özelliğini bildirerek bu gibi kontrolleri ve performansı API nin kendisine bırakmak.

Benzer bir durum OneToMany() ilişkilerde de sözkonusu. Çözüm yine benzer şekilde.