Bu belge, Java programlama dilinde daha etkili kod yazmanıza yardımcı olacak 50 önemli ipucu içermektedir. Her ipucu, kısa bir açıklama ve örnek kod içerir.
1. Static Factory Metodları Kullanın
Yapıcılar yerine static factory metodları kullanmak, nesne oluşturma sürecini daha esnek hale getirir.
javapublic class User { private String name; private int age; private User(String name, int age) { this.name = name; this.age = age; } public static User createAdult(String name) { return new User(name, 18); } public static User createChild(String name) { return new User(name, 10); } }
2. Builder Desenini Kullanın
Çok parametreli yapıcılar yerine Builder deseni kullanmak, daha okunabilir ve esnek kod sağlar.
javapublic class Pizza { private final String size; private final boolean cheese; private final boolean pepperoni; private Pizza(Builder builder) { this.size = builder.size; this.cheese = builder.cheese; this.pepperoni = builder.pepperoni; } public static class Builder { private final String size; private boolean cheese = false; private boolean pepperoni = false; public Builder(String size) { this.size = size; } public Builder cheese(boolean value) { cheese = value; return this; } public Builder pepperoni(boolean value) { pepperoni = value; return this; } public Pizza build() { return new Pizza(this); } } } // Kullanım Pizza pizza = new Pizza.Builder("large") .cheese(true) .pepperoni(true) .build();
3. Singleton Desenini Enum ile Uygulayın
Singleton deseni uygulamanın en güvenli yolu, enum kullanmaktır.
javapublic enum Singleton { INSTANCE; public void doSomething() { // method implementation } } // Kullanım Singleton.INSTANCE.doSomething();
4. Gereksiz Nesne Oluşturmaktan Kaçının
Gereksiz nesne oluşturmak performansı düşürebilir. Mümkün olduğunda, nesneleri yeniden kullanın.
java// Kötü (her çağrıda yeni bir nesne oluşturur) String s = new String("hello"); // İyi (String havuzunu kullanır) String s = "hello";
5. Kullanılmayan Nesneleri Temizleyin
Artık kullanılmayan nesneleri null'a atayarak, garbage collector'a bu nesnelerin temizlenebileceğini bildirin.
javapublic class Stack { private Object[] elements; private int size = 0; public Object pop() { if (size == 0) throw new EmptyStackException(); Object result = elements[--size]; elements[size] = null; // Kullanılmayan referansı temizle return result; } }
6. finalizer ve cleaner Kullanımından Kaçının
finalizer ve cleaner metodları öngörülemez ve genellikle tehlikelidir. Bunun yerine, AutoCloseable arayüzünü uygulayın.
javapublic class Resource implements AutoCloseable { public void doSomething() { // resource kullanımı } @Override public void close() { // resource temizleme işlemleri } } // Kullanım try (Resource resource = new Resource()) { resource.doSomething(); } // close() otomatik olarak çağrılır
7. try-with-resources Kullanın
Kapatılması gereken kaynaklar için try-with-resources kullanın. Bu, kodunuzu daha temiz ve hata ayıklamasını daha kolay hale getirir.
javatry (BufferedReader br = new BufferedReader(new FileReader(path))) { return br.readLine(); }
8. Eşitlik için equals() Metodunu Geçersiz Kılın
Nesnelerin mantıksal eşitliğini kontrol etmek için equals() metodunu geçersiz kılın.
javapublic class Point { private final int x; private final int y; // constructor @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Point)) return false; Point point = (Point) o; return x == point.x && y == point.y; } }
9. equals() Geçersiz Kılındığında hashCode() da Geçersiz Kılın
equals() metodunu geçersiz kıldığınızda, her zaman hashCode() metodunu da geçersiz kılın. Bu, HashMap ve HashSet gibi hash tabanlı koleksiyonlarda doğru çalışmayı sağlar.
javapublic class Point { private final int x; private final int y; // constructor ve equals metodu @Override public int hashCode() { return Objects.hash(x, y); } }
10. toString() Metodunu Her Zaman Geçersiz Kılın
toString() metodunu geçersiz kılmak, nesnelerinizin anlamlı string temsillerini sağlar ve hata ayıklamayı kolaylaştırır.
javapublic class Point { private final int x; private final int y; // constructor, equals ve hashCode metodları @Override public String toString() { return String.format("Point(%d, %d)", x, y); } }
11. Değiştirilemez Sınıflar Oluşturun
Mümkün olduğunca değiştirilemez (immutable) sınıflar oluşturun. Bu, thread-safe ve daha güvenli kod yazmanıza yardımcı olur.
javapublic final class ImmutablePoint { private final int x; private final int y; public ImmutablePoint(int x, int y) { this.x = x; this.y = y; } public int getX() { return x; } public int getY() { return y; } }
12. Kalıtım Yerine Kompozisyonu Tercih Edin
Kalıtım yerine kompozisyon kullanmak, daha esnek ve bakımı kolay kod sağlar.
java// Kalıtım yerine public class Stack<E> extends Vector<E> { ... } // Kompozisyon kullanarak public class Stack<E> { private Vector<E> vector = new Vector<>(); public void push(E item) { vector.add(item); } public E pop() { return vector.remove(vector.size() - 1); } }
13. Soyut Sınıflar Yerine Arayüzleri Tercih Edin
Arayüzler, çoklu kalıtıma izin verir ve daha esnek bir tasarım sağlar.
javapublic interface Drawable { void draw(); } public class Circle implements Drawable { @Override public void draw() { // Çember çizme işlemi } }
14. Fonksiyonel Arayüzler İçin Lambda İfadelerini Kullanın
Java 8 ve sonrasında, anonim sınıflar yerine lambda ifadelerini kullanın. Bu, kodunuzu daha kısa ve okunabilir hale getirir.
java// Anonim sınıf kullanımı Collections.sort(list, new Comparator<String>() { @Override public int compare(String s1, String s2) { return s1.length() - s2.length(); } }); // Lambda ifadesi kullanımı Collections.sort(list, (s1, s2) -> s1.length() - s2.length());
15. Stream API'sini Etkin Kullanın
Java 8 ile gelen Stream API'si, koleksiyonlar üzerinde fonksiyonel stil işlemler yapmanıza olanak tanır.
javaList<String> filtered = list.stream() .filter(s -> s.startsWith("A")) .map(String::toUpperCase) .collect(Collectors.toList());
16. Optional Kullanarak null Dönüşlerden Kaçının
Metotlarınızdan null döndürmek yerine Optional kullanın. Bu, NullPointerException riskini azaltır.
javapublic Optional<User> findUserById(int id) { // User bulunursa if (userExists(id)) { return Optional.of(new User(id)); } // User bulunamazsa return Optional.empty(); } // Kullanım Optional<User> user = findUserById(1); user.ifPresent(System.out::println);
17. Checked Exceptions'ları Dikkatli Kullanın
Checked exceptions'ları sadece geri kazanılabilir durumlarda kullanın. Aksi takdirde, runtime exceptions kullanmayı düşünün.
java// Checked exception public void readFile(String path) throws IOException { // Dosya okuma işlemleri } // Runtime exception public int divide(int a, int b) { if (b == 0) { throw new IllegalArgumentException("Divisor cannot be zero"); } return a / b; }
18. Standart Exceptions Kullanın
Mümkün olduğunca, kendi özel exception sınıflarınızı oluşturmak yerine Java'nın standart exception sınıflarını kullanın.
java// İyi kullanım throw new IllegalArgumentException("Age must be positive"); // Yerine // throw new InvalidAgeException("Age must be positive");
19. Hata Kodları Yerine Exceptions Kullanın
Metotlarınızdan hata kodları döndürmek yerine exceptions fırlatın. Bu, hata yönetimini daha net ve güvenilir hale getirir.
java// Kötü public int withdraw(double amount) { if (amount > balance) { return -1; // Hata kodu } balance -= amount; return 0; // Başarı kodu } // İyi public void withdraw(double amount) throws InsufficientFundsException { if (amount > balance) { throw new InsufficientFundsException(); } balance -= amount; }
20. Generics Kullanın
Tip güvenliği sağlamak ve runtime hatalarını önlemek için generics kullanın.
java// Generic olmayan versiyon List list = new ArrayList(); list.add("hello"); String s = (String) list.get(0); // Cast gerekiyor // Generic versiyon List<String> list = new ArrayList<>(); list.add("hello"); String s = list.get(0); // Cast gerekmiyor
21. Raw Tipleri Kullanmaktan Kaçının
Java 5'ten beri, raw tipler yerine parameterize tipleri kullanın.
java// Kötü (raw tip) List list = new ArrayList(); // İyi (parameterize tip) List<String> list = new ArrayList<>();
22. Wildcard Generics'i Anlamaya Çalışın
Wildcard generics, kodunuzu daha esnek hale getirebilir.
java// Üst sınır wildcard public void processElements(List<? extends Number> list) { for (Number n : list) { // işlemler } } // Alt sınır wildcard public void addNumbers(List<? super Integer> list) { list.add(1); list.add(2); }
23. Varargs'ı Dikkatli Kullanın
Varargs kullanışlı olabilir, ancak dikkatli kullanılmalıdır.
javapublic void printAll(String... strings) { for (String s : strings) { System.out.println(s); } } // Kullanım printAll("Hello", "World"); printAll("One", "Two", "Three");
24. Null Yerine Boş Koleksiyonlar Döndürün
Null bir koleksiyon döndürmek yerine, boş bir koleksiyon döndürün. Bu, NullPointerException riskini azaltır.
java// Kötü public List<Customer> getCustomers() { if (customerData.isEmpty()) { return null; } return customerData; } // İyi public List<Customer> getCustomers() { return new ArrayList<>(customerData); // Boş olabilir, ama asla null değil }
25. Defensive Copying Kullanın
Değiştirilebilir nesneleri parametre olarak alan veya döndüren metotlarda defensive copying kullanın.
javapublic final class Period { private final Date start; private final Date end; public Period(Date start, Date end) { this.start = new Date(start.getTime()); this.end = new Date(end.getTime()); if (this.start.compareTo(this.end) > 0) throw new IllegalArgumentException(); } public Date getStart() { return new Date(start.getTime()); } public Date getEnd() { return new Date(end.getTime()); } }
28. Soyut Sınıflar Yerine Arayüzleri Tercih Edin
Arayüzler, çoklu kalıtıma izin verir ve daha esnek bir tasarım sağlar.
javapublic interface Drawable { void draw(); } public interface Resizable { void resize(); } public class Circle implements Drawable, Resizable { @Override public void draw() { // Çizim işlemi } @Override public void resize() { // Yeniden boyutlandırma işlemi } }
29. Arayüzleri Sadece Tip Tanımlamak İçin Kullanın
Arayüzler, sabitleri gruplamak için kullanılmamalıdır.
java// Kötü kullanım public interface PhysicalConstants { static final double AVOGADROS_NUMBER = 6.022_140_857e23; static final double BOLTZMANN_CONSTANT = 1.380_648_52e-23; } // İyi kullanım public final class PhysicalConstants { private PhysicalConstants() {} // Örneklemeyi engelle public static final double AVOGADROS_NUMBER = 6.022_140_857e23; public static final double BOLTZMANN_CONSTANT = 1.380_648_52e-23; }
30. Tag Sınıfları Yerine Sınıf Hiyerarşisini Tercih Edin
Tag alanları kullanan sınıflar yerine, sınıf hiyerarşisi kullanın.
java// Kötü (tag sınıfı) class Figure { enum Shape { RECTANGLE, CIRCLE }; final Shape shape; // rectangle özelliği double length; double width; // circle özelliği double radius; // constructor... } // İyi (sınıf hiyerarşisi) abstract class Figure { abstract double area(); } class Rectangle extends Figure { final double length; final double width; Rectangle(double length, double width) { this.length = length; this.width = width; } @Override double area() { return length * width; } } class Circle extends Figure { final double radius; Circle(double radius) { this.radius = radius; } @Override double area() { return Math.PI * (radius * radius); } }
31. İç İçe Sınıfları Uygun Şekilde Kullanın
İç içe sınıflar, kodun organizasyonunu ve enkapsülasyonu artırabilir.
javapublic class OuterClass { private int value; public class InnerClass { public void printValue() { System.out.println("Value is " + value); } } public static class StaticNestedClass { public void printSomething() { System.out.println("Static nested class"); } } } // Kullanım OuterClass outer = new OuterClass(); OuterClass.InnerClass inner = outer.new InnerClass(); OuterClass.StaticNestedClass staticNested = new OuterClass.StaticNestedClass();
32. Erişim Belirleyicilerini Doğru Kullanın
Sınıfları ve üyelerini mümkün olduğunca erişilmez yapın.
javapublic class Account { private double balance; // Sadece bu sınıf içinden erişilebilir public void deposit(double amount) { if (amount > 0) { balance += amount; } } public void withdraw(double amount) { if (amount > 0 && balance >= amount) { balance -= amount; } } public double getBalance() { return balance; } }
33. Public Sınıflarda Public Alanlar Yerine Accessor Metodları Kullanın
Encapsulation'ı korumak için, public sınıflarda public alanlar yerine accessor metodları kullanın.
java// Kötü public class Point { public double x; public double y; } // İyi public class Point { private double x; private double y; public double getX() { return x; } public double getY() { return y; } public void setX(double x) { this.x = x; } public void setY(double y) { this.y = y; } }
34. Değiştirilebilirliği Minimize Edin
Mümkün olduğunca değişmez (immutable) sınıflar oluşturun.
javapublic final class Complex { private final double re; private final double im; public Complex(double re, double im) { this.re = re; this.im = im; } public double realPart() { return re; } public double imaginaryPart() { return im; } public Complex plus(Complex c) { return new Complex(re + c.re, im + c.im); } // diğer metodlar... }
35. Kompozisyonu Kalıtıma Tercih Edin
Kalıtım yerine kompozisyon kullanmak, daha esnek ve bakımı kolay kod sağlar.
java// Kalıtım yerine public class Stack<E> extends Vector<E> { ... } // Kompozisyon kullanarak public class Stack<E> { private Vector<E> vector = new Vector<>(); public void push(E item) { vector.add(item); } public E pop() { return vector.remove(vector.size() - 1); } }
36. Metotları Aşırı Yükleme Konusunda Dikkatli Olun
Aşırı yüklenmiş metotlar, açık ve tahmin edilebilir olmalıdır.
javapublic class CollectionClassifier { public static String classify(Set<?> s) { return "Set"; } public static String classify(List<?> lst) { return "List"; } public static String classify(Collection<?> c) { return "Unknown Collection"; } } // Kullanım Collection<?>[] collections = { new HashSet<String>(), new ArrayList<BigInteger>(), new HashMap<String, String>().values() }; for (Collection<?> c : collections) System.out.println(classify(c)); // Tüm çağrılar "Unknown Collection" döndürür!
37. Varargs'ı Dikkatli Kullanın
Varargs kullanışlı olabilir, ancak dikkatli kullanılmalıdır.
java// Tehlikeli varargs kullanımı static int min(int... args) { if (args.length == 0) throw new IllegalArgumentException("Too few arguments"); int min = args[0]; for (int i = 1; i < args.length; i++) if (args[i] < min) min = args[i]; return min; } // Daha güvenli alternatif static int min(int firstArg, int... remainingArgs) { int min = firstArg; for (int arg : remainingArgs) if (arg < min) min = arg; return min; }
38. Null Yerine Boş Koleksiyonlar veya Diziler Döndürün
Null bir koleksiyon veya dizi döndürmek yerine, boş bir koleksiyon veya dizi döndürün.
java// Kötü public List<Customer> getCustomers() { if (customerData.isEmpty()) { return null; } return customerData; } // İyi public List<Customer> getCustomers() { return new ArrayList<>(customerData); // Boş olabilir, ama asla null değil } // Diziler için public Cheese[] getCheeses() { return cheesesInStock.toArray(new Cheese[0]); }
39. Kontrol Edilmiş İstisnaları Dikkatli Kullanın
Kontrol edilmiş istisnalar (checked exceptions) sadece geri kazanılabilir durumlarda kullanılmalıdır.
java// Kontrol edilmiş istisna public void readFile(String filename) throws IOException { // Dosya okuma işlemleri } // Kontrol edilmemiş istisna public int divide(int a, int b) { if (b == 0) { throw new IllegalArgumentException("Divisor cannot be zero"); } return a / b; }
40. Standart İstisna Sınıflarını Kullanın
Mümkün olduğunca, kendi özel istisna sınıflarınızı oluşturmak yerine Java'nın standart istisna sınıflarını kullanın.
java// Yaygın kullanılan standart istisnalar throw new IllegalArgumentException(); throw new IllegalStateException(); throw new NullPointerException(); throw new IndexOutOfBoundsException(); throw new ConcurrentModificationException(); throw new UnsupportedOperationException();
41. İstisnalar İçin Bilgilendirici Mesajlar Throw Edin
İstisna mesajları, hatanın nedenini açıkça belirtmelidir.
java// Kötü throw new IllegalArgumentException(); // İyi throw new IllegalArgumentException("Employees must be at least 18 years old.");
42. Metodların Throw Ettiği Tüm İstisnaları Belgeleyin
Bir metodun fırlatabileceği tüm istisnaları, @throws etiketiyle Javadoc'ta belgeleyin.
java/** * Belirtilen kullanıcıyı veritabanından siler. * * @param userId Silinecek kullanıcının ID'si * @throws SQLException Veritabanı hatası durumunda * @throws UserNotFoundException Belirtilen ID'ye sahip kullanıcı bulunamazsa */ public void deleteUser(int userId) throws SQLException, UserNotFoundException { // Metot implementasyonu }
43. İstisna Ayrıntılarını Günlüğe Kaydedin
Catch bloklarında, istisna ayrıntılarını günlüğe kaydedin.
javatry { // Riskli işlem } catch (Exception e) { logger.log(Level.SEVERE, "İşlem başarısız oldu", e); throw e; // Yeniden fırlatma (gerekirse) }
44. Senkronizasyonu Dikkatli Kullanın
Senkronizasyon, thread güvenliği sağlar ancak performansı düşürebilir. Sadece gerektiğinde kullanın.
javapublic class ThreadSafeCounter { private int count = 0; public synchronized void increment() { count++; } public synchronized int getCount() { return count; } }
45. Thread-safe Sınıflar İçin Senkronize Erişimi Tercih Edin
Thread-safe sınıflar oluştururken, senkronize erişimi tercih edin.
javapublic class SafePoint { private int x, y; public synchronized int getX() { return x; } public synchronized int getY() { return y; } public synchronized void setXY(int x, int y) { this.x = x; this.y = y; } }
46. Aşırı Senkronizasyondan Kaçının
Aşırı senkronizasyon, performans sorunlarına ve hatta deadlock'lara neden olabilir.
java// Kötü (aşırı senkronizasyon) public class ListHelper<E> { public synchronized boolean putIfAbsent(List<E> list, E x) { boolean absent = !list.contains(x); if (absent) list.add(x); return absent; } } // İyi (daha iyi performans) public class ListHelper<E> { public boolean putIfAbsent(List<E> list, E x) { synchronized(list) { boolean absent = !list.contains(x); if (absent) list.add(x); return absent; } } }
47. Lazy Initialization'ı Dikkatli Kullanın
Lazy initialization, gereksiz nesne oluşturmayı önleyebilir, ancak dikkatli kullanılmalıdır.
java// Thread-safe lazy initialization private static class FieldHolder { static final FieldType field = computeFieldValue(); } private static FieldType getField() { return FieldHolder.field; }
48. Sistem Özelliklerini Dikkatli Kullanın
Sistem özelliklerini kullanırken dikkatli olun, çünkü bunlar JVM'in dışından değiştirilebilir.
java// Sistem özelliği kullanımı String userDir = System.getProperty("user.dir"); // Varsayılan değerle kullanım String configDir = System.getProperty("custom.config.dir", "/default/config/path");