Takip et

Java String Mülakat Soruları ve Cevapları: Derinlemesine Bir Kılavuz

Java String Mülakat Soruları ve Cevapları: Derinlemesine Bir Kılavuz Java programlama dilinin kalbinde yer alan String sınıfı, metinsel verile

Java String Mülakat Soruları ve Cevapları: Derinlemesine Bir Kılavuz

Java programlama dilinin kalbinde yer alan String sınıfı, metinsel verileri temsil etmek için kullanılan temel bir yapıdır. Gündelik kodlamadan karmaşık kurumsal uygulamalara kadar her yerde karşımıza çıkan String‘ler, Java mülakatlarında da en çok sorulan konulardan biridir. Bir Java geliştiricisinin String‘lerin nasıl çalıştığını, bellek yönetimini, performans özelliklerini ve yaygın kullanım senaryolarını derinlemesine anlaması beklenir. Bu makale, Java String konulu mülakat sorularına kapsamlı cevaplar sunarak, adayların bu alandaki bilgilerini pekiştirmeyi ve mülakatlarda başarılı olmalarını sağlamayı amaçlamaktadır.

String Temelleri: Neden Bu Kadar Önemli?

java.lang.String sınıfı, Java’da değişmez (immutable) karakter dizilerini temsil eder. Değişmezlik, bir String nesnesi oluşturulduktan sonra içeriğinin asla değiştirilemeyeceği anlamına gelir. Bu temel özellik, String‘lerin Java ekosisteminde kritik bir rol oynamasının ana nedenlerinden biridir.

* Değişmezlik (Immutability): String‘lerin değişmez olması, birçok avantajı beraberinde getirir.
* Güvenlik: String‘ler, veritabanı bağlantı parolaları, kullanıcı adları gibi hassas bilgileri saklamak için sıkça kullanılır. Değişmez olmaları, bu bilgilerin bir kez atandıktan sonra kötü niyetli kodlar tarafından değiştirilemeyeceğini garanti eder.
* Thread Güvenliği: Değişmez oldukları için String nesneleri doğal olarak thread-safe’dir. Birden fazla thread aynı String nesnesini güvenle kullanabilir, çünkü içeriği asla değişmeyecektir.
* Önbellekleme ve Performans: String‘ler değişmez olduğu için hash kodları bir kez hesaplanabilir ve önbelleğe alınabilir. Bu, HashMap ve HashSet gibi koleksiyonlarda String‘lerin anahtar olarak verimli bir şekilde kullanılmasını sağlar.
* String Literal Havuzu (SCP): Java, değişmezlik sayesinde String literal havuzunu (String Constant Pool) verimli bir şekilde yönetir.

* String Literal Havuzu (SCP – String Constant Pool): JVM, String literal’lerini (çift tırnak içinde belirtilen String değerleri) özel bir bellek alanında, yani String literal havuzunda saklar. Bir String literal’i oluşturulduğunda, JVM önce bu havuzda aynı değere sahip bir String olup olmadığını kontrol eder. Eğer varsa, mevcut nesnenin referansını döndürür; yoksa, yeni bir String nesnesi oluşturur ve havuza ekleyerek referansını döndürür. Bu mekanizma, bellek tüketimini azaltarak performansı artırır.

String, StringBuffer ve StringBuilder Karşılaştırması

Java’da String‘lerle çalışırken, metin manipülasyonu için sadece String sınıfı değil, aynı zamanda StringBuffer ve StringBuilder sınıfları da kullanılır. Bu üç sınıfın temel farklarını anlamak, doğru senaryoda doğru aracı seçmek açısından kritiktir.

* String:
* Değişmezdir (Immutable).
* Herhangi bir değişiklik (birleştirme, alt dize alma vb.) yeni bir String nesnesi oluşturur.
* Performans açısından, sıkça metin manipülasyonu yapılan durumlarda verimsiz olabilir (çok fazla obje oluşturduğu için).
* Thread-safe’dir, çünkü değişmezdir.
* Genellikle sabit veya nadiren değişen metinler için tercih edilir.

* StringBuffer:
* Değişebilirdir (Mutable).
* Metin üzerinde yapılan değişiklikler mevcut nesne üzerinde gerçekleşir, yeni bir obje oluşturulmaz.
* Thread-safe’dir. Metotları synchronized anahtar kelimesiyle işaretlenmiştir, bu da birden fazla thread’in aynı anda StringBuffer nesnesine erişmesini engeller.
* Performans açısından String‘den daha iyidir, ancak StringBuilder‘dan daha yavaştır (synchronization overhead nedeniyle).
* Çoklu thread ortamlarında metin manipülasyonu gerektiğinde kullanılır.

* StringBuilder:
* Değişebilirdir (Mutable).
* Metin üzerinde yapılan değişiklikler mevcut nesne üzerinde gerçekleşir.
* Thread-safe değildir. Metotları synchronized değildir.
* Performans açısından StringBuffer‘dan daha hızlıdır (synchronization overhead olmadığı için).
* Tekli thread ortamlarında yoğun metin manipülasyonu gerektiğinde tercih edilir.

Özet:
| Özellik | String | StringBuffer | StringBuilder |
| :————– | :———– | :————- | :————– |
| Değişmezlik | Değişmez | Değişebilir | Değişebilir |
| Thread-Safe | Evet | Evet | Hayır |
| Performans | Düşük (sık değişiklikte) | Orta | Yüksek |
| Kullanım | Sabit metinler, koleksiyon anahtarları | Çoklu thread metin manipülasyonu | Tekli thread metin manipülasyonu |

Sıkça Sorulan Mülakat Soruları ve Detaylı Cevaplar

Aşağıda, Java String konusunda mülakatlarda sıkça karşılaşabileceğiniz sorular ve detaylı cevapları bulunmaktadır.

1. Java’da String nedir ve neden değişmezdir (immutable)?

String, Java’da karakter dizilerini temsil eden bir sınıftır (java.lang.String). Değişmez olması, bir String nesnesi oluşturulduktan sonra içeriğinin (karakter dizisinin) asla değiştirilemeyeceği anlamına gelir. Bir String üzerinde herhangi bir “değişiklik” yaptığınızda (örneğin, birleştirme veya alt dize alma), aslında orijinal String nesnesi değişmez; bunun yerine, yeni bir String nesnesi oluşturulur ve bu yeni nesne istenen değişikliği içerir.

Değişmezliğin Nedenleri:
* Güvenlik: String‘ler, dosya yolları, ağ bağlantı adresleri, veritabanı parolaları gibi hassas bilgileri temsil etmek için sıkça kullanılır. Değişmez olmaları, bu bilgilerin bir kez atandıktan sonra dışarıdan değiştirilemeyeceğini garanti eder.
* Thread Güvenliği: Değişmez String nesneleri, doğal olarak thread-safe’dir. Birden fazla thread aynı String nesnesini paylaşabilir ve kullanabilir, çünkü içeriği asla değişmeyeceği için senkronizasyon mekanizmalarına ihtiyaç duymazlar.
* Performans ve Önbellekleme: String‘ler değişmez olduğu için hash kodları bir kez hesaplanabilir ve önbelleğe alınabilir. Bu, String‘lerin HashMap ve HashSet gibi koleksiyonlarda anahtar olarak verimli bir şekilde kullanılmasını sağlar.
* String Literal Havuzu (SCP): Değişmezlik, Java’nın String literal havuzunu (String Constant Pool) verimli bir şekilde yönetmesine olanak tanır. Aynı değere sahip String literal’leri tek bir nesne olarak havuzda saklanabilir, bu da bellek kullanımını optimize eder.

Örnek:

String s = "Merhaba"; // "Merhaba" literal havuzda oluşturulur.
s = s + " Dünya";    // "Merhaba Dünya" adında YENİ bir String nesnesi oluşturulur.
                     // Orijinal "Merhaba" nesnesi değişmeden kalır.
                     // s değişkeni artık yeni nesneyi referans eder.

2. String literal’i ile new anahtar kelimesi kullanarak String oluşturmak arasındaki fark nedir?

String oluşturmanın iki ana yolu vardır ve bunlar bellek yönetimi açısından önemli farklılıklar gösterir:

* String Literal Kullanarak: Çift tırnak içinde bir String değeri belirtildiğinde (örneğin, "hello"), bu bir String literal’idir. Java bu durumda String literal havuzunu (String Constant Pool – SCP) kullanır. JVM önce SCP’de aynı değere sahip bir String nesnesi olup olmadığını kontrol eder.
* Eğer varsa, mevcut nesnenin referansını döndürür.
* Eğer yoksa, yeni bir String nesnesi oluşturur, SCP’ye ekler ve referansını döndürür.
Bu yöntem, bellek optimizasyonu sağlar çünkü aynı değerdeki String‘ler için tek bir nesne kullanılır.

* new Anahtar Kelimesi Kullanarak: new String("hello") gibi bir ifade kullanıldığında, Java her zaman yığın bellekte (heap) yeni bir String nesnesi oluşturur. Ayrıca, eğer "hello" literal’i daha önce SCP’de yoksa, bir kopyası da SCP’ye eklenir. Yani, new ile oluşturulduğunda genellikle iki nesne oluşma potansiyeli vardır: biri heap’te, diğeri SCP’de (eğer henüz yoksa).

Örnek:

String s1 = "Java";          // SCP'de "Java" nesnesi oluşur (veya mevcutsa kullanılır).
String s2 = "Java";          // s2, s1 ile aynı SCP'deki nesneyi referans eder.
String s3 = new String("Java"); // Heap'te yeni bir "Java" nesnesi oluşur.
                                // Ayrıca, eğer yoksa SCP'ye de "Java" eklenir.

System.out.println(s1 == s2); // true (aynı referans)
System.out.println(s1 == s3); // false (farklı referanslar, biri SCP'de, diğeri heap'te)
System.out.println(s1.equals(s3)); // true (içerikleri aynı)

3. == operatörü ile .equals() metodu String‘ler için nasıl farklı çalışır?

Bu, String mülakatlarının en temel ve en sık sorulan sorularından biridir.

* == Operatörü: Referans karşılaştırması yapar. İki String referansının aynı bellek adresini (yani aynı nesneyi) işaret edip etmediğini kontrol eder.
* .equals() Metodu: İçerik karşılaştırması yapar. İki String nesnesinin karakter dizisi içeriklerinin aynı olup olmadığını kontrol eder. String sınıfı, Object sınıfındaki equals() metodunu geçersiz kılarak bu davranışı sağlar.

Örnek:

String str1 = "Hello";
String str2 = "Hello";
String str3 = new String("Hello");
String str4 = "World";

System.out.println(str1 == str2);        // true (str1 ve str2 aynı SCP nesnesini referans eder)
System.out.println(str1 == str3);        // false (str1 SCP'de, str3 heap'te farklı nesneleri referans eder)
System.out.println(str1.equals(str2));   // true (içerikleri aynı)
System.out.println(str1.equals(str3));   // true (içerikleri aynı)
System.out.println(str1.equals(str4));   // false (içerikleri farklı)

4. String birleştirme (+ operatörü) nasıl çalışır ve performans etkileri nelerdir?

Java’da + operatörü, String‘leri birleştirmek için kullanılır. Ancak, String‘lerin değişmez olması nedeniyle, bu operatörün kullanımı arka planda farklı işlemler tetikler.

* Arka Plan İşleyişi: Java derleyicisi, + operatörünü kullanarak yapılan String birleştirmelerini optimize eder.
* Java 5 Öncesi: Derleyici, StringBuffer sınıfını kullanarak birleştirme işlemini gerçekleştirirdi.
* Java 5 ve Sonrası: Derleyici, daha hızlı olan StringBuilder sınıfını kullanarak birleştirme işlemini gerçekleştirir.
Yani, String s = "A" + "B" + "C"; ifadesi derleyici tarafından yaklaşık olarak new StringBuilder().append("A").append("B").append("C").toString(); şeklinde yorumlanır.

* Performans Etkileri:
* Tek seferlik birleştirme: Az sayıda String‘i birleştirirken ("A" + "B" + "C" gibi), derleyicinin optimizasyonu sayesinde performans sorunu yaşanmaz.
* Döngü içinde birleştirme: Bir döngü içinde String‘leri + operatörüyle birleştirmek ciddi performans sorunlarına yol açabilir. Çünkü her iterasyonda yeni bir StringBuilder nesnesi oluşturulur, append çağrılır ve ardından toString() metoduyla yeni bir String nesnesi oluşturulur. Bu, çok sayıda geçici obje oluşmasına ve gereksiz bellek tahsisine neden olur.

Kötü Örnek (Döngü içinde +):

String result = "";
for (int i = 0; i < 1000; i++) {
    result = result + i; // Her iterasyonda yeni bir String objesi oluşturulur
}

İyi Örnek (Döngü içinde StringBuilder):

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    sb.append(i); // Mevcut StringBuilder objesi üzerinde değişiklik yapılır
}
String result = sb.toString();

5. String bir null değer alabilir mi? null bir String üzerinde metot çağırırsak ne olur?

Evet, bir String referansı null değer alabilir. Bu, String referansının hiçbir String nesnesini işaret etmediği anlamına gelir.

Eğer null bir String referansı üzerinde herhangi bir metot çağırırsak (örneğin, length(), equals(), substring() gibi), NullPointerException (NPE) fırlatılır. Bu, Java'da sıkça karşılaşılan bir çalışma zamanı hatasıdır.

Örnek:

String myString = null;
// System.out.println(myString.length()); // Bu satır NullPointerException fırlatır

Bu nedenle, bir String referansını kullanmadan önce null olup olmadığını kontrol etmek iyi bir pratiktir:

if (myString != null) {
    System.out.println(myString.length());
} else {
    System.out.println("String null olduğu için işlem yapılamaz.");
}

6. String'i tersine çevirmenin farklı yolları nelerdir?

Bir String'i tersine çevirmenin birkaç yaygın yolu vardır:

* StringBuilder.reverse() Metodu ile: En basit ve verimli yoldur.

String original = "Java";
    StringBuilder sb = new StringBuilder(original);
    String reversed = sb.reverse().toString();
    System.out.println(reversed); // avaj

* Döngü Kullanarak: Manuel olarak karakter karakter tersine çevirme.

String original = "Hello";
    String reversed = "";
    for (int i = original.length() - 1; i >= 0; i--) {
        reversed = reversed + original.charAt(i); // Performans açısından StringBuilder daha iyi olur
    }
    System.out.println(reversed); // olleH

Döngü içinde StringBuilder kullanmak daha performanslıdır:

String original = "Hello";
    StringBuilder sbReversed = new StringBuilder();
    for (int i = original.length() - 1; i >= 0; i--) {
        sbReversed.append(original.charAt(i));
    }
    String reversed = sbReversed.toString();
    System.out.println(reversed); // olleH

* Java 8 Stream API ile (Daha Az Yaygın ama Modern):

String original = "Stream";
    String reversed = original.chars()
                              .mapToObj(c -> (char) c)
                              .collect(StringBuilder::new, StringBuilder::append, StringBuilder::append)
                              .reverse()
                              .toString();
    System.out.println(reversed); // maertS

7. Bir String'in palindrom olup olmadığını nasıl kontrol edersiniz?

Bir String'in palindrom olması, baştan ve sondan okunduğunda aynı olması anlamına gelir (örneğin, "madam", "kayak").

* StringBuilder.reverse() ile Karşılaştırma:

public boolean isPalindrome(String str) {
        if (str == null || str.isEmpty()) {
            return true; // Boş veya null string'ler palindrom kabul edilebilir
        }
        String reversedStr = new StringBuilder(str).reverse().toString();
        return str.equals(reversedStr);
    }
    // Kullanım: isPalindrome("madam") -> true, isPalindrome("hello") -> false

* İki İşaretçi (Two-Pointer) Yaklaşımı: Bu yöntem, ek bellek kullanmadan ve daha verimli bir şekilde kontrol sağlar.

public boolean isPalindromeTwoPointers(String str) {
        if (str == null || str.isEmpty()) {
            return true;
        }
        int left = 0;
        int right = str.length() - 1;

        while (left < right) {
            if (str.charAt(left) != str.charAt(right)) {
                return false;
            }
            left++;
            right--;
        }
        return true;
    }
    // Kullanım: isPalindromeTwoPointers("level") -> true, isPalindromeTwoPointers("world") -> false

8. Bir String'deki belirli bir karakterin veya kelimenin kaç kez geçtiğini nasıl bulursunuz?

* Karakter Sayısı İçin (Döngü ve charAt()):

public int countChar(String str, char c) {
        int count = 0;
        for (int i = 0; i < str.length(); i++) {
            if (str.charAt(i) == c) {
                count++;
            }
        }
        return count;
    }
    // Kullanım: countChar("Mississippi", 's') -> 4

* Karakter Sayısı İçin (Java 8 Stream API):

public long countCharStream(String str, char c) {
        return str.chars().filter(ch -> ch == c).count();
    }
    // Kullanım: countCharStream("Banana", 'a') -> 3

* Kelime Sayısı İçin (split() metodu):

public int countWord(String text, String word) {
        if (text == null || word == null || text.isEmpty() || word.isEmpty()) {
            return 0;
        }
        // Kelime sınırlarını dikkate almak için regex kullanabiliriz
        String[] words = text.split("\\s+"); // Boşluklara göre ayır
        int count = 0;
        for (String w : words) {
            if (w.equalsIgnoreCase(word)) { // Büyük/küçük harf duyarsız karşılaştırma
                count++;
            }
        }
        return count;
    }
    // Kullanım: countWord("Hello world hello Java", "hello") -> 2

* Kelime Sayısı İçin (indexOf() metodu):

public int countWordIndexOf(String text, String word) {
        int count = 0;
        int lastIndex = 0;
        while (lastIndex != -1) {
            lastIndex = text.indexOf(word, lastIndex);
            if (lastIndex != -1) {
                count++;
                lastIndex += word.length();
            }
        }
        return count;
    }
    // Kullanım: countWordIndexOf("banana", "ana") -> 2

9. String'deki yinelenen karakterleri nasıl kaldırırsınız?

Bir String'deki yinelenen karakterleri kaldırmanın birkaç yolu vardır:

* HashSet Kullanarak (Sıra Önemli Değilse): HashSet sadece benzersiz öğeleri sakladığı için idealdir.

public String removeDuplicates(String str) {
        if (str == null || str.isEmpty()) {
            return str;
        }
        StringBuilder sb = new StringBuilder();
        HashSet seen = new HashSet<>();
        for (char c : str.toCharArray()) {
            if (seen.add(c)) { // Karakteri eklemeye çalış, başarılı olursa (yani yeni ise) true döner
                sb.append(c);
            }
        }
        return sb.toString();
    }
    // Kullanım: removeDuplicates("programming") -> progamni

* LinkedHashSet Kullanarak (Sırayı Korumak İsterseniz): LinkedHashSet, ekleme sırasını koruyan benzersiz öğeler listesi sağlar.

public String removeDuplicatesPreserveOrder(String str) {
        if (str == null || str.isEmpty()) {
            return str;
        }
        StringBuilder sb = new StringBuilder();
        LinkedHashSet seen = new LinkedHashSet<>();
        for (char c : str.toCharArray()) {
            seen.add(c); // LinkedHashSet otomatik olarak yinelenenleri görmezden gelir
        }
        for (Character c : seen) {
            sb.append(c);
        }
        return sb.toString();
    }
    // Kullanım: removeDuplicatesPreserveOrder("programming") -> progamin

10. String'i int'e ve int'i String'e nasıl dönüştürürsünüz?

* String'den int'e Dönüştürme:
* Integer.parseInt(String s): String'i ilkel int tipine dönüştürür.
* Integer.valueOf(String s): String'i Integer nesnesine dönüştürür.
Her iki metot da geçersiz bir formatla karşılaştığında NumberFormatException fırlatabilir.

String strNum = "123";
    try {
        int num1 = Integer.parseInt(strNum); // num1 = 123
        Integer num2 = Integer.valueOf(strNum); // num2 = 123 (Integer nesnesi)
        System.out.println(num1 + ", " + num2);
    } catch (NumberFormatException e) {
        System.err.println("Geçersiz sayı formatı: " + e.getMessage());
    }

* int'ten String'e Dönüştürme:
* String.valueOf(int i): En yaygın ve önerilen yoldur.
* Integer.toString(int i): Integer sınıfının statik metodudur.
* Boş String ile birleştirme ("" + i): Derleyici arka planda StringBuilder kullanarak dönüştürme yapar.

int num = 456;
    String str1 = String.valueOf(num);    // "456"
    String str2 = Integer.toString(num);  // "456"
    String str3 = "" + num;               // "456"
    System.out.println(str1 + ", " + str2 + ", " + str3);

11. intern() metodunun amacı nedir?

String sınıfının intern() metodu, String literal havuzu (SCP) ile ilgilidir. Bir String nesnesi üzerinde intern() metodu çağrıldığında:
* JVM, String literal havuzunda bu nesnenin içeriğine eşit bir String olup olmadığını kontrol eder.
* Eğer varsa, havuzdaki mevcut String'in referansını döndürür.
* Eğer yoksa, bu String nesnesinin bir kopyasını havuza ekler ve ardından bu yeni eklenen nesnenin referansını döndürür.

intern() metodu, new anahtar kelimesiyle oluşturulmuş String nesnelerini SCP'ye "dahil etmek" veya SCP'deki eşdeğerine erişmek için kullanılabilir. Bu, bellek optimizasyonu sağlayabilir ve == operatörü ile içerik karşılaştırması yapma imkanı sunabilir.

Örnek:

String s1 = new String("Java"); // Heap'te yeni obje
String s2 = "Java";             // SCP'de obje (veya mevcutsa kullanılır)
String s3 = s1.intern();        // s1'in içeriğine eşit olan SCP'deki objeyi referans eder

System.out.println(s1 == s2);    // false (farklı referanslar)
System.out.println(s2 == s3);    // true (s2 ve s3 aynı SCP objesini referans eder)

12. StringJoiner sınıfı nedir ve ne zaman kullanılır?

StringJoiner sınıfı, Java 8 ile birlikte tanıtılmıştır ve birden fazla CharSequence'ı (genellikle String'leri) belirli bir ayırıcı (delimiter), isteğe bağlı bir önek (prefix) ve isteğe bağlı bir sonek (suffix) kullanarak birleştirmek için tasarlanmıştır. Özellikle dinamik olarak bir String oluşturmanız ve elemanlar arasına virgül, tire gibi ayırıcılar eklemeniz gerektiğinde çok kullanışlıdır.

String.join() metodu da benzer bir işlev görür ancak StringJoiner daha fazla esneklik sunar (önek ve sonek gibi).

Örnek:

import java.util.StringJoiner;

StringJoiner sj = new StringJoiner(", ", "{", "}"); // Ayırıcı, Önek, Sonek
sj.add("Apple");
sj.add("Banana");
sj.add("Cherry");

System.out.println(sj.toString()); // {Apple, Banana, Cherry}

// Boş StringJoiner için varsayılan değer
StringJoiner emptySj = new StringJoiner("-");
System.out.println(emptySj.toString()); // Çıktı: (boş string)
emptySj.setEmptyValue("NO_ITEMS");
System.out.println(emptySj.toString()); // NO_ITEMS

13. equals() ve hashCode() metotlarının String sınıfı için sözleşmesi nasıldır?

String sınıfı, Object sınıfından miras aldığı equals() ve hashCode() metotlarını geçersiz kılar. Bu metotlar için belirli bir sözleşme vardır:

* equals(Object obj):
* İki String nesnesinin içeriklerini karşılaştırır.
* Eğer her iki String de aynı karakter dizisini temsil ediyorsa true döner, aksi takdirde false döner.
* Büyük/küçük harf duyarlıdır.
* Object sınıfının equals sözleşmesine uyar: yansıtıcı (reflexive), simetrik (symmetric), geçişli (transitive), tutarlı (consistent) ve null için false döner.

* hashCode():
* String'in içeriğine göre bir hash kodu hesaplar ve döndürür.
* equals() metodu ile karşılaştırıldığında eşit olan iki String nesnesinin mutlaka aynı hash kodunu döndürmesi gerekir.
* Bu, HashMap ve HashSet gibi hash tabanlı koleksiyonlarda String'lerin anahtar olarak doğru bir şekilde çalışmasını sağlar. String değişmez olduğu için hash kodu bir kez hesaplanıp önbelleğe alınabilir.

Örnek:

String s1 = "test";
String s2 = "test";
String s3 = new String("test");

System.out.println(s1.equals(s2)); // true
System.out.println(s1.equals(s3)); // true

System.out.println(s1.hashCode()); // Aynı değer
System.out.println(s2.hashCode()); // Aynı değer
System.out.println(s3.hashCode()); // Aynı değer

14. String'leri büyük/küçük harf duyarlılığı olmadan nasıl karşılaştırırsınız?

String'leri büyük/küçük harf duyarlılığı olmadan karşılaştırmanın iki ana yolu vardır:

* equalsIgnoreCase(String anotherString) Metodu: Bu, String sınıfının sağladığı özel bir metottur ve iki String'in içeriklerini büyük/küçük harf ayrımı yapmadan karşılaştırır. En basit ve önerilen yöntemdir.

String s1 = "Java";
    String s2 = "java";
    String s3 = "JAVA";

    System.out.println(s1.equals(s2));          // false (büyük/küçük harf duyarlı)
    System.out.println(s1.equalsIgnoreCase(s2)); // true
    System.out.println(s1.equalsIgnoreCase(s3)); // true

* toLowerCase() veya toUpperCase() Kullanarak: Her iki String'i de aynı harf durumuna (tümü küçük veya tümü büyük) dönüştürüp ardından equals() metoduyla karşılaştırabilirsiniz. Ancak bu, yeni String nesneleri oluşturduğu için equalsIgnoreCase()'dan daha az verimli olabilir.

String s1 = "Hello";
    String s2 = "hELLo";

    System.out.println(s1.toLowerCase().equals(s2.toLowerCase())); // true
    System.out.println(s1.toUpperCase().equals(s2.toUpperCase())); // true

15. compareTo() metodu ne işe yarar?

String sınıfının compareTo(String anotherString) metodu, iki String'i sözlükbilimsel (lexicographical) olarak karşılaştırır. Yani, sözlük sırasına göre hangi String'in önce geldiğini belirler.

* Eğer çağıran String (bu String), anotherString'den sözlükbilimsel olarak küçükse (önce geliyorsa), negatif bir tam sayı döner.
* Eğer çağıran String (bu String), anotherString'den sözlükbilimsel olarak büyükse (sonra geliyorsa), pozitif bir tam sayı döner.
* Eğer iki String eşitse, sıfır döner.

Karşılaştırma, String'lerin karakterlerinin Unicode değerlerine göre yapılır.

Örnek:

String s1 = "apple";
String s2 = "banana";
String s3 = "apple";
String s4 = "Apple";

System.out.println(s1.compareTo(s2)); // Negatif bir sayı (çünkü 'a' < 'b')
System.out.println(s2.compareTo(s1)); // Pozitif bir sayı (çünkü 'b' > 'a')
System.out.println(s1.compareTo(s3)); // 0 (çünkü eşitler)
System.out.println(s1.compareTo(s4)); // Pozitif bir sayı (çünkü küçük 'a' > büyük 'A')
                                      // (ASCII/Unicode değerleri farklı)
System.out.println(s1.compareToIgnoreCase(s4)); // 0 (büyük/küçük harf duyarsız)

16. trim() ve strip() metotları arasındaki fark nedir? (Java 11+)

Hem trim() hem de strip() metotları bir String'in başındaki ve sonundaki boşluk karakterlerini kaldırmak için kullanılır, ancak farklı boşluk tanımları kullanırlar. strip() metodu Java 11 ile tanıtılmıştır.

* trim(): Sadece ASCII boşluk karakterlerini (yani, U+0020 olan boşluk ve '\t', '\n', '\r' gibi kontrol karakterlerini) kaldırır. Character.isWhitespace(char) metodunun eski tanımını kullanır.

* strip(): Unicode boşluk karakterlerini de kaldırır. Character.isWhitespace(char) metodunun yeni tanımını kullanır, bu da daha geniş bir boşluk karakteri yelpazesini (örneğin, non-breaking space U+00A0 gibi) kapsar. strip() ayrıca stripLeading() (sadece baştaki boşlukları kaldırır) ve stripTrailing() (sadece sondaki boşlukları kaldırır) versiyonlarına da sahiptir.

Örnek:

String text = "  \t Hello World \u2005 "; // \u2005 bir Unicode boşluk karakteridir (Four-per-em space)

System.out.println("Orijinal: '" + text + "'");
System.out.println("trim():   '" + text.trim() + "'"); // "Hello World \u2005" (Unicode boşluk kalır)
System.out.println("strip():  '" + text.strip() + "'"); // "Hello World" (Unicode boşluk da kaldırılır)

String text2 = "\u2005leading and trailing\u2005";
System.out.println("stripLeading(): '" + text2.stripLeading() + "'"); // "leading and trailing\u2005"
System.out.println("stripTrailing(): '" + text2.stripTrailing() + "'"); // "\u2005leading and trailing"

17. isEmpty() ve isBlank() metotları arasındaki fark nedir? (Java 11+)

Bu iki metot da bir String'in içeriğinin boş olup olmadığını kontrol etmek için kullanılır, ancak farklı boşluk tanımları vardır. isBlank() metodu Java 11 ile tanıtılmıştır.

* isEmpty(): Bir String'in uzunluğunun sıfır olup olmadığını kontrol eder. Eğer String'in uzunluğu 0 ise true döner, aksi takdirde false döner. Sadece boşluk karakterlerinden oluşan bir String için false döner.

String s1 = "";        // Uzunluk 0
    String s2 = " ";       // Uzunluk 1
    String s3 = "  \t  "; // Uzunluk > 0

    System.out.println(s1.isEmpty()); // true
    System.out.println(s2.isEmpty()); // false
    System.out.println(s3.isEmpty()); // false

* isBlank(): Bir String'in boş olup olmadığını veya sadece boşluk karakterlerinden (boşluk, tab, yeni satır vb. dahil olmak üzere Unicode boşluk karakterleri) oluşup oluşmadığını kontrol eder. Eğer String boşsa veya sadece boşluk karakterlerinden oluşuyorsa true döner, aksi takdirde false döner.

String s1 = "";
    String s2 = " ";
    String s3 = "  \t  ";
    String s4 = "Hello";

    System.out.println(s1.isBlank()); // true
    System.out.println(s2.isBlank()); // true
    System.out.println(s3.isBlank()); // true
    System.out.println(s4.isBlank()); // false

isBlank() metodu, kullanıcı girişlerini veya dosya okumalarını işlerken sadece boşluklardan oluşan satırları filtrelemek için çok kullanışlıdır.

Performans İpuçları

* Döngülerde StringBuilder veya StringBuffer Kullanımı: Yoğun String birleştirme işlemleri (özellikle döngü içinde) gerektiğinde, + operatörü yerine StringBuilder (tek thread) veya StringBuffer (çoklu thread) kullanın. Bu, gereksiz String nesnelerinin oluşmasını engelleyerek performansı artırır ve bellek tüketimini azaltır.
* Gereksiz String Objeleri Oluşturmaktan Kaçınma: new String("literal") yerine String literal = "literal" kullanın. Bu, String literal havuzunu kullanarak bellekten tasarruf etmenizi sağlar.
* intern() Metodunun Dikkatli Kullanımı: intern() metodu, String literal havuzunu kullanarak bellek optimizasyonu sağlayabilir, ancak her zaman performans artışı sağlamaz. Büyük String'ler için havuzda arama ve kopyalama işlemleri maliyetli olabilir. Genellikle, çok sayıda aynı String değerinin olduğu ve == ile karşılaştırmanın gerektiği özel durumlar için düşünülmelidir.
* String.join() ve StringJoiner Kullanımı: Java 8 ile gelen bu yapılar, String dizilerini veya koleksiyonlarını birleştirmek için daha okunaklı ve verimli yollar sunar.

Sonuç

Java String sınıfı, basit görünse de altında yatan karmaşık mekanizmaları ve performans etkileri olan güçlü bir yapıdır. Mülakatlarda String'ler hakkında derinlemesine bilgi sahibi olmak, bir geliştiricinin Java çekirdek prensiplerine olan hakimiyetini gösterir. Değişmezlik, String literal havuzu, String, StringBuffer ve StringBuilder arasındaki farklar gibi temel kavramları anlamak ve yaygın manipülasyon tekniklerini bilmek, yalnızca mülakatlarda değil, günlük kodlama pratiklerinde de daha sağlam ve verimli uygulamalar geliştirmenize yardımcı olacaktır. Bu makalede ele alınan sorular ve cevaplar, Java String konusundaki bilginizi pekiştirmek ve bir sonraki teknik mülakatınızda kendinize güvenle cevap vermek için kapsamlı bir kaynak sağlamaktadır.

Yorumlar
İçeriği beğendiniz mi? Bir tartışma başlatın veya görüşlerinizi paylaşın.
Yorum Yaz

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

E-posta Bülteni
Yazılım Topluluğuna Katılın
En son güncellemeleri, yaratıcı ipuçlarını ve özel kaynakları doğrudan e-posta kutunuza alın. Tasarım ve inovasyonun geleceğini birlikte keşfedelim.