Java'da “final class” ın anlamı nedir?


569

Java hakkında bir kitap okuyorum ve tüm sınıfı ilan edebileceğinizi söylüyor final. Bunu kullanacağım bir şey düşünemiyorum.

Programlamaya yeni başladım ve programcıların bunu programlarında gerçekten kullanıp kullanmadığını merak ediyorum . Eğer yaparlarsa, ne zaman kullanırlar, böylece daha iyi anlayabilir ve ne zaman kullanacağımı bilirim.

Java nesne yönelimliyse ve bir sınıf finalbildirirseniz, sınıfın nesnelerin özelliklerine sahip olduğu fikrini durdurmaz mı?

Yanıtlar:


531

Her şeyden önce, bu makaleyi öneriyorum: Java: Ne zaman bir final sınıfı oluşturmak


Eğer yaparlarsa, ne zaman kullanırlar, böylece daha iyi anlayabilir ve ne zaman kullanacağımı bilirim.

Bir finalsınıf sadece bir sınıftır uzatılamaz .

(Sınıftaki nesnelere yapılan tüm göndermelerin, sanki bildirilmiş gibi davranacağı anlamına gelmez final.)

Bir sınıfı final olarak ilan etmek yararlı olduğunda, bu sorunun cevaplarında yer almaktadır:

Java nesne yönelimliyse ve bir sınıf finalbildirirseniz, sınıfın nesnelerin özelliklerine sahip olduğu fikrini durdurmaz mı?

Bir anlamda evet.

Bir sınıfı final olarak işaretleyerek, kodun o kısmı için dilin güçlü ve esnek bir özelliğini devre dışı bırakırsınız. Bununla birlikte, bazı sınıflar alt sınıflamayı iyi bir şekilde dikkate alacak şekilde tasarlanmamalıdır (ve bazı durumlarda olamaz ). Bu durumlarda, OOP'yi sınırlasa bile sınıfı nihai olarak işaretlemek mantıklıdır. (Ancak, bir final sınıfının başka bir final olmayan sınıfı da genişletebileceğini unutmayın.)


39
Cevaba eklemek için Etkili Java prensiplerinden biri miras yerine kompozisyonu tercih etmektir. Nihai anahtar kelimenin kullanımı da bu ilkenin uygulanmasına yardımcı olur.
Riggy

9
"Bunu esas olarak verimlilik ve güvenlik nedenleriyle yapıyorsunuz." Bu ifadeyi oldukça sık duyuyorum (Wikipedia bile bunu söylüyor) ama hala bu argümanın ardındaki mantığı anlamıyorum. Birisi, nihai olmayan bir java.lang.String'in nasıl verimsiz veya güvensiz hale geleceğini açıklamaya değer mi?
MRA

27
@MRA Bir String'i parametre olarak kabul eden bir yöntem oluşturursam, bunun değişmez olduğunu varsayarım, çünkü Dizeler vardır. Bunun sonucunda, String nesnesindeki herhangi bir yöntemi güvenle çağırabileceğimi ve iletilen String'i değiştiremediğimi biliyorum. Dize'yi genişletmek ve gerçek Dize'yi değiştirmek için alt dizenin uygulanmasını değiştirmek olsaydım, değişmez olmasını beklediğiniz Dize nesnesi artık değiştirilemez.
Cruncher

1
@Sortofabeginner Ve tüm String yöntemlerinin ve alanlarının son olmasını istediğinizi söylediğiniz anda, ek işlevlere sahip bir sınıf oluşturabilmeniz için ... Bu noktada sadece bir dize ve bu dizede çalışan yöntemler oluşturun.
Cruncher

1
@Shay final (diğer şeylerin yanı sıra) bir nesneyi değişmez yapmak için kullanılır, bu yüzden birbirleriyle hiçbir ilgileri olmadığını söyleyemem. Buraya bakın docs.oracle.com/javase/tutorial/essential/concurrency/…
Celeritas

184

Java'da finaldeğiştiricili öğeler değiştirilemez!

Bu, son sınıfları, son değişkenleri ve son yöntemleri içerir:

  • Final sınıfı başka bir sınıf tarafından genişletilemez
  • Son bir değişkene başka bir değer atanamaz
  • Son bir yöntem geçersiz kılınamaz

40
Gerçek soru neden değil neyi .
Francesco Menzani

8
"Java'da finaldeğiştiricili öğeler değiştirilemez!" İfadesi çok kategoriktir ve aslında tamamen doğru değildir. Grady Booch'un dediği gibi "Bir nesnenin durumu, davranışı ve kimliği vardır". Referansı nihai olarak işaretlendikten sonra bir nesnenin kimliğini değiştiremesek de , alanlarına olmayan yeni değerler atayarak (elbette, sahip olduğu) durumunu değiştirme şansımız var final. Oracle Java Sertifikası (1Z0-808, vb.) almayı planlamak, bunu akılda tutmalıdır çünkü sınavda bu konu ile ilgili sorular olabilir ...
Igor Soudakevitch

33

Güvenlik nedeniyle bir sınıfın mirasını önlemek istediğinizde finalin önemli olduğu bir senaryo . Bu, çalıştırdığınız kodun birisi tarafından geçersiz kılınamayacağından emin olmanızı sağlar .

Optimizasyon için başka bir senaryo: Java derleyicisinin son sınıflardan bazı işlev çağrılarını satır içine aldığını hatırlıyorum. Bu nedenle, çağırırsanız a.x()ve a bildirilirse final, derleme zamanında kodun ne olacağını biliyoruz ve çağrı işlevine satır içi olabilir. Bunun gerçekten yapılıp yapılmadığı hakkında hiçbir fikrim yok, ama sonuçta bir olasılık.


7
Inlining normalde sadece çalışma zamanında tam zamanında derleyici tarafından yapılır. Nihai olmadan da çalışır, ancak JIT derleyicisinin, genişleyen sınıfların olmadığından (veya bu genişletme sınıflarının bu yönteme dokunmadığından) emin olmak için biraz daha çalışması vardır.
Paŭlo Ebermann


24

En iyi örnek

public final class Dize

bu değişmez bir sınıftır ve genişletilemez. Tabii ki, sınıfın değişmez olması için final yapmaktan daha fazlası var.


Hehe, bazen Rube Goldergian geliştiricilerini kendilerinden korur.
Zoidberg

16

Konuyla ilgili okuma: Bob Martin'in Açık-Kapalı Prensibi .

Anahtar teklif:

Yazılım Varlıkları (Sınıflar, Modüller, İşlevler, vb.) Uzantı için açık ancak Değiştirme için kapalı olmalıdır.

finalAnahtar kelimenin yöntemleri veya sınıflar üzerinde kullanılan olsun, Java Bunu zorlamak araçtır.


6
@Sean: Bunu ilan etmek finalsınıfı açıktan ziyade uzatma için kapatmıyor mu ? Yoksa tam anlamıyla mı alıyorum?
Goran Jovic

4
@ Goran küresel olarak finali uyguluyor, evet. Anahtar, modifikasyon istemediğiniz yerlerde finali uygulamaktır (ve elbette uzatma için iyi kancalar sağlamak)
Sean Patrick Floyd

26
OCP'de "değişiklik", kaynak kodun değiştirilmesini ve "uzantı", uygulama devralmasını ifade eder. Bu nedenle, finaluygulama kodunun değiştirilmek üzere kapatılmasını, ancak kalıtım yoluyla genişletilmeye açık olmasını istiyorsanız, bir sınıf / yöntem bildiriminde kullanılması mantıklı olmaz.
Rogério

1
@Rogerio Bahar Çerçeve Referansından (MVC) referans (ve yorum) ödünç aldım . IMHO bu orijinal versiyondan çok daha mantıklı.
Sean Patrick Floyd

Uzatma öldü. Faydasız. Decimated. Yerlebir edilmiş. OCP umrumda değil. Bir sınıfı genişletmek için asla bir bahane yoktur.
Josh Woodcock

15

Sınıf hiyerarşisini bir ağaç olarak hayal ediyorsanız (Java'da olduğu gibi), soyut sınıflar sadece dallar olabilir ve son sınıflar sadece yaprak olabilen sınıflardır. Bu kategorilerin hiçbirine girmeyen sınıflar hem dal hem de yaprak olabilir.

Burada OO ilkelerinin ihlali yoktur, final sadece güzel bir simetri sağlamaktır.

Uygulamada, nesnelerinizin değişmez olmasını istiyorsanız veya bir API yazıyorsanız, API kullanıcılarına sınıfın sadece uzantı için tasarlanmadığını bildirmek için final kullanmak istersiniz.


13

Anahtar kelimenin finalkendisi bir şeyin nihai olduğu ve hiçbir şekilde değiştirilmemesi gerektiği anlamına gelir. Bir sınıf işaretliyse finalgenişletilemez veya alt sınıflanamaz. Ama soru şu, neden bir sınıfı işaretliyoruz final? IMO'nun çeşitli nedenleri vardır:

  1. Standardizasyon: Bazı sınıflar standart fonksiyonlar yerine getirirler ve bunlar örneğin string manipülasyonları veya matematiksel fonksiyonlarla ilgili çeşitli fonksiyonları yerine getiren sınıflar vb.
  2. Güvenlik nedenleri : Bazen çeşitli kimlik doğrulama ve parola ile ilgili işlevleri yerine getiren sınıflar yazarız ve bunların başkaları tarafından değiştirilmesini istemeyiz.

İşaretleme sınıfının finalverimliliği artırdığını duydum ama açıkçası bu argümanı fazla ağırlık taşıyamadım.

Java nesne yönelimliyse ve bir sınıf finali beyan ederseniz, sınıfın nesnelerin özelliklerine sahip olduğu fikrini durdurmaz mı?

Belki evet, ama bazen amaçlanan amaç budur. Bazen bunu, bu sınıfın genişletilebilme yeteneğinden ödün vererek daha büyük güvenlik vb. Faydalar elde etmek için yaparız. Ancak bir final sınıfı gerekirse bir sınıfı genişletebilir.

Bir yan notta , kalıtım yerine kompozisyonu tercih etmeliyiz ve finalanahtar kelime aslında bu prensibi uygulamaya yardımcı olur.


6

Sınıf "finali" yaparken dikkatli olun. Çünkü son sınıf için bir birim testi yazmak istiyorsanız, Michael C. Tüyler'in "Eski Kod ile Etkili Çalışma" kitabında açıklanan bağımlılık kırıcı tekniği "Alt Sınıf ve Geçersiz Kılma Yöntemi" ni kullanmak için bu son sınıfı alt sınıflandıramazsınız. . Bu kitapta Feathers, "Cidden, mühürlü ve finalin yanlış başlı bir hata olduğuna, programlama dillerine asla eklenmemesi gerektiğine inanmak kolaydır. Ama asıl hata bizde yatıyor. kontrolümüz dışında olan kütüphaneler için sadece sorun istiyoruz. "


6

final class yeni yöntemler eklediğinizde herkese açık API'yı kırmaktan kaçınabilir

BaseSınıfınızın 1. sürümünde bunu yaptığınızı varsayalım :

public class Base {}

ve bir müşteri şunları yapar:

class Derived extends Base {
    public int method() { return 1; }
}

Sonra sürüm 2'de bir methodyöntem eklemek istiyorsanız Base:

class Base {
    public String method() { return null; }
}

istemci kodunu kırar.

final class BaseBunun yerine kullanmış olsaydık, istemci devralamazdı ve yöntem ekleme API'yı kıramazdı.


5

Sınıf işaretliyse final, sınıfın yapısı harici bir şeyle değiştirilemez demektir. Bunun en görünür olduğu yer, geleneksel polimorfik kalıtım yaptığınız zaman, temelde class B extends Aişe yaramaz. Temel olarak, kodunuzun bazı bölümlerini (kapsamı) korumanın bir yoludur .

Açıklığa kavuşturmak için, sınıf finalişaretleme alanlarını olduğu gibi işaretlemez ve finalnesne özelliklerini korumaz, bunun yerine gerçek sınıf yapısını korur.


1
Nesne özellikleri ne anlama geliyor? Sınıfın son olarak bildirilmesi durumunda sınıfın üye değişkenini değiştirebileceğim anlamına mı geliyor? Yani son sınıfın tek amacı mirasın önlenmesidir.
Adam Lyu

5

SON SINIF SORUNUNU ADRES ETMEK İÇİN:

Bir sınıfı final yapmak için iki yol vardır. Birincisi, sınıf bildiriminde final anahtar sözcüğünü kullanmaktır:

public final class SomeClass {
  //  . . . Class contents
}

Bir sınıfı final yapmanın ikinci yolu, tüm kurucularını özel olarak ilan etmektir:

public class SomeClass {
  public final static SOME_INSTANCE = new SomeClass(5);
  private SomeClass(final int value) {
  }

Son olarak işaretlemek, bu Test sınıfına bir bakış göstermenin gerçek bir final olduğunu öğrenirseniz size sorun çıkarır. ilk bakışta herkese açık görünüyor.

public class Test{
  private Test(Class beanClass, Class stopClass, int flags)
    throws Exception{
    //  . . . snip . . . 
  }
}

Ne yazık ki, sınıfın tek kurucusu özel olduğundan, bu sınıfı genişletmek imkansızdır. Test sınıfı söz konusu olduğunda, sınıfın final olması için bir neden yoktur. Test sınıfı, örtük son sınıfların nasıl sorunlara neden olabileceğine iyi bir örnektir.

Bu yüzden, bir sınıfı yapıcı özel yaparak örtülü olarak final yaptığınızda bunu son olarak işaretlemelisiniz.


4

Son sınıf, genişletilemeyen bir sınıftır. Ayrıca, alt sınıflar tarafından geçersiz kılınamayacağını belirten yöntemler kesin olarak bildirilebilir.

API'leri veya kitaplıkları yazarsanız ve temel davranışı değiştirmek için genişletilmekten kaçınmak istiyorsanız, sınıfın alt sınıflara ayrılmasını önlemek özellikle yararlı olabilir.


4

Bir sınıfı final olarak tutmanın bir avantajı: -

String sınıfı, kimsenin yöntemlerini geçersiz kılamaması ve işlevselliği değiştirmemesi için son olarak tutulur. örneğin, kimse length () yönteminin işlevselliğini değiştiremez. Her zaman bir dizenin uzunluğunu döndürür.

Bu sınıfın geliştiricisi kimsenin bu sınıfın işlevselliğini değiştirmesini istemedi, bu yüzden onu final olarak tuttu.



3

Java'da nihai anahtar kelime aşağıdaki durumlar için kullanır.

  1. Nihai Değişkenler
  2. Nihai Yöntemler
  3. Final Sınıfları

Java'da son değişkenler yeniden atanamaz, son sınıflar genişletilemez ve son yöntemler geçersiz kılınamaz.


1

Final sınıfları genişletilemez. Dolayısıyla, bir sınıfın belirli bir şekilde davranmasını istiyorsanız ve birisinin yöntemleri geçersiz kılmasını istemiyorsanız (muhtemelen daha az verimli ve daha kötü niyetli kodla), tüm sınıfı, olmak istemediğiniz nihai veya özel yöntemler olarak ilan edebilirsiniz. değişti.

Bir sınıfı bildirmek, bir sınıfın somutlaştırılmasını engellemediğinden, sınıfın bir nesnenin özelliklerine sahip olmasını durduracağı anlamına gelmez. Sadece yöntemlere, sınıfta ilan edildikleri şekilde sadık kalmak zorunda kalacaksınız.


1

FINAL'ı "hattın sonu" olarak düşünün - bu adam artık yavru üretemez. Bu şekilde gördüğünüzde, sınıfa bir 'satır sonu' işaretçisi işaretlemenizi gerektiren çok sayıda gerçek dünya senaryosu var. Etki Alanına Dayalı Tasarımdır - etki alanınız belirli bir ENTITY (sınıf) alt sınıf oluşturamazsa, bunu FINAL olarak işaretleyin.

Sizi bir "son olarak etiketlenmelidir" sınıfını devralmaktan alıkoyan hiçbir şey olmadığını belirtmeliyim. Ancak bu genellikle "kalıtımın kötüye kullanılması" olarak sınıflandırılır ve yapılır, çünkü çoğu zaman bazı işlevleri sınıfınızdaki temel sınıftan devralmak istersiniz.

En iyi yaklaşım, etki alanına bakmak ve tasarım kararlarınızı dikte etmesine izin vermektir.


1

Yukarıda anlatıldığı gibi, eğer hiç kimse yöntemin işlevselliğini değiştiremezse, o zaman nihai olarak bildirebilirsiniz.

Örnek: İndirme / karşıya yükleme için uygulama sunucusu dosya yolu, dizeyi ofsete göre bölme, bu yöntemlerin bu yöntem işlevlerinin değiştirilmemesi için Son olarak bildirebilirsiniz. Ve böyle bir final yöntemini ayrı bir sınıfta istiyorsanız, o sınıfı Final sınıfı olarak tanımlayın. Bu yüzden Final sınıfı, final yönteminin final olmayan sınıfta açıklanabileceği ve tanımlanabileceği tüm nihai yöntemlere sahip olacaktır.



1

Diyelim ki Employeeyöntemi olan bir sınıfınız var greet. Ne zaman greetyöntem olarak adlandırılır basitçe yazdırır Hello everyone!. Bu , yöntemin beklenen davranışıdır .greet

public class Employee {

    void greet() {
        System.out.println("Hello everyone!");
    }
}

Şimdi, aşağıda gösterildiği gibi GrumpyEmployeealt sınıfı Employeeve geçersiz kılma greetyöntemini bırakın .

public class GrumpyEmployee extends Employee {

    @Override
    void greet() {
        System.out.println("Get lost!");
    }
}

Şimdi aşağıdaki kodu sayHelloyöntemi bir göz atın . Tek gereken Employeebir parametre olarak örneği ve bunun derdi umuduyla Greet yöntemini çağıran Hello everyone!Ama biz olsun Get lost!. Davranıştaki bu değişiklik,Employee grumpyEmployee = new GrumpyEmployee();

public class TestFinal {
    static Employee grumpyEmployee = new GrumpyEmployee();

    public static void main(String[] args) {
        TestFinal testFinal = new TestFinal();
        testFinal.sayHello(grumpyEmployee);
    }

    private void sayHello(Employee employee) {
        employee.greet(); //Here you would expect a warm greeting, but what you get is "Get lost!"
    }
}

Bu durum olabilir kaçınılması durumunda Employeesınıf yapıldı final. StringClass olarak bildirilmezse, arsız bir programcının neden olabileceği kaos miktarını hayal edin final.


1

Final sınıfı daha fazla genişletilemez. Java'da kalıtımsal bir sınıf oluşturmamıza gerek yoksa, bu yaklaşımı kullanabiliriz.

Bir sınıfta geçersiz kılmamak için belirli yöntemler yapmamız gerekirse, son anahtar kelimeyi önüne koyabiliriz. Orada hala miras var.


-1

Nesne Yönelimi kalıtım ile ilgili değildir, kapsülleme ile ilgilidir. Ve kalıtım kapsüllemeyi bozar.

Bir sınıf finali ilan etmek birçok durumda mükemmeldir. Bir renk veya bir miktar para gibi bir “değeri” temsil eden herhangi bir nesne nihai olabilir. Kendi başlarına duruyorlar.

Kitaplıklar yazıyorsanız, türetilmeleri için açıkça girintili olmadıkça sınıflarınızı sonlandırın. Aksi takdirde, insanlar sınıflarınızı türetebilir ve varsayımlarınızı / değişmezlerinizi kırarak yöntemleri geçersiz kılabilir. Bunun güvenlikle ilgili etkileri de olabilir.

“Etkili Java” da Joshua Bloch, miras için açık bir şekilde tasarlamayı veya yasaklamanızı önerir ve miras için tasarlamanın o kadar kolay olmadığını belirtir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.