Java Persistence Transient Annotation’ı
@Transient, bir alanın ya da özelliğin kalıcı olarak depolanmayacağını belirtir.
@Transientprivate Integer geciciBirAlan;
Method Overloading
Method overloading, aynı isme sahip birden fazla methodun parametrelerin sayısı, sırası ya da veri tipleri üzerinden ayrışmalarına imkan sağlayan bir programlama özelliğidir. Bu sayede aynı işlem farklı parametre sayıları ya da farklı veri türleri ile gerçekleştirilebilir ve bu da compile-time polymorphism olarak adlandırılır.
// Parametre sayısı farklı
int topla(int sayi1, int sayi2) {
return sayi1 + sayi2;
}
int topla(int sayi1, int sayi2, int sayi3) {
return sayi1 + sayi2 + sayi3;
}
// Parametrelerin veri tipi farklı
double topla(double sayi1, double sayi2) {
return sayi1 + sayi2;
}
// Parametrelerin sırası farklı
void bilgiYaz(String isim, int yas) {
System.out.println(isim + " - " + yas);
}
void bilgiYaz(int yas, String isim) {
System.out.println(yas + " - " + isim);
}
System.out.println() ile System.out.print() arasındaki fark
System.out.println() ekrana yazdırdıktan sonra yeni satıra geçer. System.out.print() ise yeni satıra geçmeden yazmaya aynı satırda devam eder.
String Class in java
String s1 = “adana”;
Java’da bütün nesneler heap üzerinde String constant pool adlı özel bir hafıza alanında tutulur. Çift tırnak içindeki ifade string literal olarak adlandırılır. s1 ise bir string reference variable yani heap üzerindeki nesnenin adresini tutan bir değişkendir. String literal kullanıldığında öncelikle string pool kontrol edilir. Aynı değer ile oluşturulmuş bir nesne var ise string pool içerisinde, yeni bir nesne oluşturulmaz ve yeni değişken eski nesnenin adresini tutmaya başlar. new operatörü kullanılarak bir nesne oluşturulmak istendiğinde string pool içerisinde aynı değer için bir kontrol yapılmadan doğrudan yeni bir nesne oluşturulur ve string reference variable yeni oluşturulan nesnenin heap içerisindeki (String constant pool) memory address’ini tutar.
String pool nedir?
String s2=”abc”; şeklinde bir tanımlama yaptığımızda JVM, abc literali için String pool içinde arama yapar. Aynı değerde bir String bulursa yeni bir String nesnesi oluşturmaz. Her bir literalin sadece bir kopyası String pool içinde tutulur. Buna da String interning denir.
String sınıfı neden immutable(değişmez) ‘dir ?
- Java sanal makinesi (JVM), String nesnelerini String constant pool adında bir hafıza alanında tutar ve nesneleri bu ‘cache pool’ dan çekerek tekrar tekrar kullanır. String immutable olmasaydı String pool kullanımı mümkün olmazdı. Aynı hafıza adresini işaret eden değişkenler olduğu durumda eğer bir String nesnesinde değişiklik yapılırsa, yeni bir String nesnesi oluşturulur ve değişken yeni nesnenin adresini tutmaya başlar. Diğer değişkenler etkilenmez.
- Immutable olduğu için String nesnesi multi thread işlemlerde kullanılabilir ve bu threadler içinde yeni nesneler oluşturulsa bile başlangıçtaki nesne değiştirilemez.
- Kullanıcı adı, şifre ya da bağlantı URL bilgileri String olarak tutulduğu için, String nesnesi immutable olmasaydı yanlışlıkla method içinde nesne içeriği değiştirilebilir ve bu durum güvenlik tehdidine neden olabilirdi.
- Object object = takeSomeAction(secretInfo);
- Burada secretInfo ana methoda parametre olarak gelmiş olsun ve sonrasında bir DB insert/update işlemine kullanılacak olsun. Immutable olmayan bir String nesnesi takeSomeAction içinde değiştirilebilir ve DB işlemine yanlış bir parametre gönderilebilirdi.
- Object object = takeSomeAction(secretInfo);
- String nesnesi immutable olduğu için nesnenin tuttuğu değer değiştirilemez. Bu durumda nesne üretilirken oluşturulan hashCode’da değiştirilemez. HashCode, HashMap ya da HashTable gibi hash implementasyonlarında kullanılır.
Interface, Abstract Class, Concrete Class Hatırlatma
Interface üzerinde gövdesiz tanımlanan methodlar “public abstract” olarak kabul edilir. Interface içinde tanımlanan değişkenler ise “public static final” özelliğine sahiptir. Bu nedenle SABIT_DEGER, bütün uygulamalar tarafından ortak kullanılan ve sonradan değiştirilemeyen bir sabittir.
Interface’in alt sınıflar tarafından uygulanması gereken soyut methodları private olamaz. Bununla birlikte Java 9 ve sonraki sürümlerde, yalnızca interface içerisinde kullanılmak üzere gövdeli private yardımcı methodlar tanımlanabilir.
public interface SekilArayuz { void alanHesapla(); int cevreHesapla(String parametre); int SABIT_DEGER = 42; }
Bu interface’i implement eden bir abstract sınıf tanımlayalım.
public abstract class Sekil implements SekilArayuz { private String name; public Sekil() { } public Sekil(String parameter) { this.name = parameter; } public String getName() { return name; }}
Sınıfı abstract olarak tanımladığımızda interface içindeki abstract methodları override etmek zorunda değiliz. İsteğe bağlı edilebilir. Abstract sınıflarda new keyword’u kullanılarak yeni nesneler oluşturulamaz. Bu işlemi abstract sınıftan extends edilen alt sınıflar ile yapabiliriz. Abstract sınıflar ortak alanları, constructor’ları ve davranışları alt sınıflara aktarmak amacıyla kullanılır.
Sekil abstract sınıfından türen bir concrete Kare sınıfı oluşturabiliriz.
public class Kare extends Sekil{ public Kare(String name) { super(name); } @Override public void alanHesapla() { } @Override public int cevreHesapla(String parametre) { return 0; }}
constructor içinde super(name) diyerek türetildiği sınıfın name değişkenini kullanır.
Kare concrete bir sınıf olduğu için interface’ten gelen bütün soyut metotları uygulamak zorundadır.
Equals ve HashCode
Equals ile iki nesnenin bellekte aynı yerde tutulup tutulmadığının kontrolü referans (bellek adresini işaret eder) üzerinden kontrol edilir.
class Kisi { private Integer id; private String adi; private String kodu; @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null || getClass() != obj.getClass()) { return false; } Kisi kisi = (Kisi) obj; return Objects.equals(getId(), kisi.getId()) && Objects.equals(getAdi(), kisi.getAdi()) && Objects.equals(getKodu(), kisi.getKodu()); }}
Burada karşılaştırma yaparken kişinin id, adi ve kodu alanları kontrol edilir. Eşitse true döner.
İki farklı nesnenin aynı değerlere sahip olup olmadığına bakılmaksızın aynı referansa sahip olmadıkları sürece equals false döner. İki farklı nesne aynı bellek adresini (referans) göstermediği sürece false döner. Burada iki farklı nesne doğal olarak iki farklı referansı ve iki farklı bellek alanını işaret eder ve false döner. Kişi sınıfının equals methodu bu iki nesnenin içerdiği verinin aynı olduğunu kontrol edebilmek için özelleştirilir ve override edilir.
Ders adlı başka bir sınıf olsun ve Kisi tipinde iki farklı değişken tutsun. Bir ders nesnesine değişken olarak bağlı iki farklı kişi nesnesini karşılaştırmak isteyelim. Spring data jpa kullanıyorsak burada FetchType.EAGER kullandığımızda ders nesnesini çağırırken kişi nesnelerinin de gerçek hallerini çağırırız. Bu şekilde equals methodunu override etmesek dahi kişilerin aynı olup olmadığını bulabiliriz. Ancak FetchType.EAGER yerine FetchType.LAZY kullandığımızda gerçek nesne yerine bir proxy nesne dönebilir. Bu durumda equals methodu override edilmiş olsa dahi bu iki nesne karşılaştırılması false olarak döner. Bunu aşabilmek için seçeneklerden birisi bu proxy nesneleri gerçek nesnelere döndürmektir.
if (kisiProxy instanceof HibernateProxy) { HibernateProxy hibernateProxy = (HibernateProxy) kisiProxy; kisi1 = (Kisi) hibernateProxy.getHibernateLazyInitializer().getImplementation(); }
Equals methodu override edildiğinde hashCode methodu da override edilmelidir. Nesnelerin aynı olduğu durumlarda, aynı hashCode’u döndürdüğümüzden emin olmalıyız. HashCode methodu içinde bütün değişkenleri kullanmak yerine nesnenin unique olma durumun sağlayacak değişkenlerin kullanılması yeterli olur. Nesneleri örneğin bir map üzerinde key olarak kullandığımız durumlarda hashCode kullanışlı olur.
@Override public int hashCode() { int code = Objects.hash(getId(), getAdi(), getKodu()); return code; }
Java’da Stack ve Heap Farkı
- Java’da bellek yönetimi iki ayrı bölgeye ayrılır. Stack ve heap.
- Stack yerel değişkenler gibi kısa ömürlü veriler için kullanılır.
- Heap daha uzun ömürlü verilerin saklanması için kullanılır. Bir sınıfa ait bir nesne heap’te oluşturulur. Bu nesneyi işaret eden referans stack’te saklanır.
UUID
Java’da UUID oluşturmak için java.util.UUID sınıfı kullanılır. Bu sınıf, farklı UUID versiyonlarını (1,3,4,5) destekler. UUID’nin oluşturulması random sayılar veya belirli algoritmalar (Leach-Salz temelinde) kullanılarak yapılır. randomUUID methodu ile rastgele bir UUID oluşturulur.
String uuidToString = UUID.randomUUID().toString();
Bu kod, randomUUID() metodunu kullanarak rastgele bir UUID oluşturur. Bu UUID, 128 bit unique bir değerdir ve 32 byte hexadecimal (onaltılık) gösterim ile ifade edilir ve “8-4-4-4-12” formatında gruplara ayrılmıştır.
import java.util.UUID;public class UUIDTest { public static void main(String[] args) { long zamanDamgasi = System.currentTimeMillis(); long randomDeger = new java.util.Random().nextLong(); UUID uuid = new UUID(zamanDamgasi, randomDeger); System.out.println("Oluşturulan UUID: " + uuid); }}
Yukarıdaki şekilde, sistem saatinden alınan zaman damgası ve rastgele bir değer kullanılarak özel bir UUID oluşturur.
Performans Sorunları
Senaryo 1: Bir request sonucu çok uzun sürelerde dönüyor olabilir. Diyelim ki yaptığımız incelemede method içindeki bir JPQL(Java Persistence Query Language) sorgusunun işlemi yavaşlattığını gördük.
Entity’ler birbirleriyle ilişkili olabilir. Bu entity’lerin çekilme şekilleri EAGER ya da LAZY olabilir (hızlı erişim için LAZY olmalıdır. İlişkili nesnenin tüm elemanları aynı anda çekilmez). Yavaş gelen bu sorgu için öncelikle mümkünse fetch tiplerini LAZY olarak ayarla değilse EAGER olarak dursun. Bundan sonra db’de sorgu yaptığın entity alanlarının karşılığı olan alanlara index ekle. Bunları yapmana rağmen iyileşme olmuyorsa doğrudan native query ile veritabanı sorgulaması yap. Native query her durumda sonucu hızlandıracaktır.
static keyword
Bir değişken ya da method static olarak tanımlanırsa o değişken ya da method sınıfa aittir. Yani yeni bir instance oluşturulmadan da erişilebilir, değiştirilebilir.
final keyword
final, bir değişkenin değiştirilemez olduğunu belirtir. final ile tanımlanan bir sınıf alt sınıflar tarafından türetilemez.
final ile tanımlanan bir method alt sınıflar tarafından override edilemez.
final değişkenlerin mutlaka başlatılması gerekir. Aksi halde derleyici “Variable ‘value’ might not have been initialized” hatası verir.
private final String value ="test"
gibi doğrudan bir tanımlama yapılabilir.
Ya da generic bir değişken tanımı varsa:
private final T value;
değişken constructor ile başlatılmalıdır.
protected BaseId(T value) {
this.value = value;
}
Object Sınıfı
Java’da object sınıfı tüm sınıfların temelidir. Örnek methodlarından biri toString() methodudur.
Bir yanıt yazın