Java

KATEGORİ

Java’da bir listeyi belirli bir tablo alanına göre sıralama

Oluşturduğumuz bir nesneyi örnek bir alana göre (id) sıralamak için Comparator arayüzünü (interface) kullanırız.

Araba nesnesini olusturduğumuzu varsayalım. arabaList isimli bir listemiz olsun.

		Comparator<Araba> comparator = new Comparator<Araba() 
        {
			@Override
			public int compare(Araba a1, Araba a2) {

				return a1.getId() - a2.getId(); 
			}
		};


    Collections.sort(arabaList, comparator);

Aynı işlemi Java 8 lambdas kullanarak aşağıdaki şekilde yapabiliriz.

     

    Comparator<Araba> IDs = (Araba o1, Araba o2) -> o1.getId().compareTo(o2.getId());

    Collections.sort(arabaList, IDs); 

Bu listeyi bir datatable üzerinde bu haliyle göstermek için “order” : [] eklemesi yap.

Java Persistence Transient Annotation’ı

@Transient, bir alanın ya da özelliğin kalıcı olarak depolanmayacağını belirtir.

@Transient
private Integer geciciBirAlan;

Encapsulation

Sınıf özelliklerinin dışarıdan erişilebilirlik durumunu düzenleyen oop konseptidir.

Public

Private

Protected

Method Overloading

Birden fazla method aynı isime sahip olabilir. Aldıkları parametreler farklı olmak şartıyla.

Liste Elemanlarının Toplamını Bulma

Java’da Liste içindeki Integer elemanları toplamak için çok sayıda yöntem bulunmaktadır.

  1. Öncelikle geleneksel yöntemi uygulayalım. Bir methoda parametre olarak gelen liste içindeki elemanları toplayalım.

public int listSum(List<Integer> liste) {

    int sum = 0;

 for(int i : liste){

   sum += i;

}

return sum;

    }

2. Listenin Integer elemanlarını IntStream’e çevirelim ve stream sum methodunu kullanalım.

Yöntem-1 -> Integer::intValue

public int listSum(List<Integer> liste) {

    int sum1 = liste.stream().mapToInt(Integer::intValue).sum();

    return sum1;

    }

 

Yöntem-2 -> Integer::valueOf

public int listSum(List<Integer> liste) {

    int sum1 = liste.stream().mapToInt(Integer::valueOf).sum();

    return sum1;

    }

 

Yöntem-3 -> Lambda (i->i)

public int listSum(List<Integer> liste) {

    int sum1 = liste.stream().mapToInt(i->i).sum();

    return sum1;

    }

Tarih Bilgisini Gün, Ay, Yıl olarak gösterme

java.util paketi altındaki Date sınıfı milisaniye hassasiyetiyle zaman içinde bir anı temsil eder.

Date startDate = …;

startDate isminde bir tarih nesnesi oluşturduğumuzu varsayalım. Bu tarihin gün, ay, yıl bilgilerini göstermek için Calendar sınıfı kullanılır.

Calendar calendar = Calendar.getInstance(); ile yeni bir calendar nesnesi oluşturulur.

calendar.setTime(startDate); ile calendar’ın zamanına startDate nesnesi atanır.

int day = calendar.get(Calendar.DAY_OF_MONTH)

int month = calendar.get(Calendar.MONTH)+1;

int year = calendar.get(Calendar.YEAR);

month değerine bir ekledik çünkü Calendar.MONTH default olarak 0-11 aralığında dönüş yapar.

System.out.println() ile System.out.print() arasındaki fark

println() methodu yazdırma işleminden sonra imleci sonraki satıra taşırken print() methodu imleci aynı satırda tutar.

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 ?

  1. 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.
  2. 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.
  3. 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.
  4. HashCode. 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.

HashMap in Java

HashMap Java Collection kütüphanesinin üyesidir. HashMap sınıfı java.util paketi altındadır. HashMap veriyi key,value çiftleri olarak depolar. Key nesnesi üzerinden value nesnesine erişilir. Key değeri unique’dir. Var olan bir key değeri ile aynı değerde yeni bir key değeri eklenirse, yeni key,value çifti eskisinin yerine geçer.

Key veya value değerleri için sıralama yoktur. Key üzerinden sıralama yapabilen map implementasyonu TreeMap’tir.

HashMap sınıfı synchronized değildir. Eğer birden fazla thread bir map’e eşzamanlı olarak erişiyor ve üzerinde değişiklikler yapıyorlarsa, bu map veri tutarsızlıklarından korunmak için synchronized olmalıdır. Synchronized map yapısı ConcurrentHashMap’tir.

Collections.synchronizedMap(new HashMap<K,V>) ile hashMap thread-safe hale getirilebilir. ConcurrentHashMap kullanmak performans açısından daha verimlidir.

Java.lang.Math.abs()

int, long, float, double parametre alır. Aldığı parametrenin mutlak değerini döner.

int x = -35;

Math.abs(x) -> 35.

ArrayList

Java.util.ArrayList.add(E element)

ArrayList add methodu, elemanı listenin sonuna ekler.

add(int index, E element) methodu da, elemanı listenin belirtilen indeksine ekler.

Interface, Abstract Class, Concrete Class Hatırlatma

Bir arayüz tanımlayalım.

public interface SekilArayuz {

    void alan(); 
    int cevre(String parametre); 
    
    int SABIT_DEGER = 42;
	
}

Interface içinde method access modifier private olamaz.

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.

public class Kare extends Sekil{

	public Kare(String name) {
		super(name);
	}
	
	@Override
	public void alan() {
		
		
	}

	@Override
	public int cevre(String parametre) {
		
		return 0;
	}

}

constructor içinde super(name) diyerek türetildiği sınıfın name değişkenini kullanır.

Java List ve Set Arasındaki Farklar

  • List, elemanları belirli bir sıraya göre ekler. Set sırasızdır. List elemanlarına belirli bir indeks ile ulaşılabilirken set elemanlarında indeksleme olmaz.
  • List, aynı elemanın birden fazla eklenmesine izin verirken Set buna izin vermez.
  • List, verilere ulaşma konusunda daha iyi performans sunabilir ancak elemanların unique olması kontrol edilmez.
  • Elemanların eklenme sırasının korunmasını ve aynı zamanda unique olmasını istiyorsak LinkedHashSet kullanmalıyız.

HashSet ile LinkedHashSet Arasındaki Farklar

  • HashSet, elemanları sırasız şekilde saklar ve elemanlar unique olarak eklenir.
  • LinkedHashSet elemanları sıralı şekilde saklar ve elemanlar unique olarak eklenir.
  • Elemanların eklenme sırasının önemli olduğu ve unique olması gerektiği durumlarda LinkedHashSet kullanılır. Sıranın önemsiz olduğu, elemanlara erişimin (ekleme, görüntüleme) daha hızlı (sırasız oldukları için) olmasının istendiği durumlarda HashSet kullanılı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.