2013-04-12 10 views
6

Chcę zaprojektować klasę, aby zademonstrować niezmienność, przyrostowo.Chroń ostatnie zmienne przed odbiciem

Poniżej prostą klasę

public class ImmutableWithoutMutator { 
    private final String firstName; 
    private final String lastName; 
    private final int age; 

    public ImmutableWithoutMutator(final String firstName, final String lastName, final int age) { 
     this.firstName = firstName; 
     this.lastName = lastName; 
     this.age = age; 
    } 

    @Override 
    public String toString() { 
     return String.format(
       "ImmutableWithoutMutator [age=%s, firstName=%s, lastName=%s]", 
       age, firstName, lastName); 
    } 
} 

Teraz mogę jeszcze wyłom w użyciu odbicia, przy użyciu następującego kodu.

import java.lang.reflect.Field; 

public class BreakImmutableUsingReflection { 
    public static void main(String[] args) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { 
     ImmutableWithoutMutator immMutator = new ImmutableWithoutMutator(
       "firstName", "lastName", 2400); 
     System.out.println(immMutator); 

     // now lets try changing value using reflection 
     Field f = ImmutableWithoutMutator.class.getDeclaredField("age"); 
     f.setAccessible(true); 
     f.set(immMutator, 2000); 

     System.out.println(immMutator); 
    } 
} 

Moje pytanie brzmi: nie zmodyfikowałem modyfikatorów dla pól za pomocą interfejsu API Reflect. W jaki sposób kod nadal może mutować ostatnie pola?

+0

Możliwy duplikat http://stackoverflow.com/questions/1615163/modifying-final-fields-in-java?rq=1 – Patashu

Odpowiedz

10

Jest to oczekiwane zachowanie - to po prostu coś, czego nie powinno się robić.

Z dokumentacji dla Field.set:

Jeśli pole bazowy jest ostateczna metoda zgłasza IllegalAccessException chyba setAccessible (true) udało się do tego obiektu, a pole ostrości nie jest statyczny. Ustawienie końcowego pola w ten sposób ma sens tylko podczas deserializacji lub rekonstrukcji wystąpień klas z pustymi polami końcowymi, zanim zostaną udostępnione innym częściom programu. Używanie w dowolnym innym kontekście może mieć nieprzewidywalne skutki, w tym przypadki, w których inne części programu nadal używają oryginalnej wartości tego pola.

Teraz, jeśli potem chce się kłócić to łamie wszystkie rodzaje zabezpieczeń, trzeba zadać sobie pytanie, dlaczego masz kodu nie ufasz być rozsądny działa z wystarczających uprawnień, aby wywołać field.setAccessible(true). Zasadniczo ochrona jest zapewniona przez menedżera bezpieczeństwa - ale jeśli używasz menedżera bezpieczeństwa, który powstrzymuje cię od zrobienia tego, możesz strzelić sobie w stopę.

+4

'if (author.getName(). Equals (" Jon Skeet ")) { this.upvote(); } else {this.readAnswer(); } ' –

+0

@ Jon-skeet Dzięki za szczegółowe wyjaśnienie. – Vivek

2

Możesz chronić przed setAccessible przy użyciu SecurityManager. Możesz go określić w wierszu poleceń lub utworzyć dynamicznie, tak jak robią to: here.

Ale nawet jeśli zabezpieczysz się przed odbiciem, twój kod nadal nie będzie chroniony przed bibliotekami modyfikacji kodu bajtowego, takimi jak ASM.

Nie powinieneś więc polegać na tym, że twoje pola są ostateczne.

Powiązane problemy