Pull to refresh

Как я заново открыл для себя инкапсуляцию в java.

Reading time2 min
Views19K
Я всегда считал, что Java — лаконичный и красивый (в плане концепции) язык с четкой структурой, позволяющей расширять эту структуру и на всевозможные фреймворки, там самым помогающая привнести порядок и в код конечного программиста. И, прежде всего, я считал, что java — это 100% ОО язык! Но недавно мне попался код, после которого я вечер ходил возмущался. Код совершенно несложный для понимания даже людей несведующий в java.
Допустим у нас есть класс А в некотором пакете:

package test;

public class A {
	private int x = 33;
	
	public void test(){
		System.out.println(x);
	}
}

Как видно поле x является защищенным и, согласно доке от sun, это поле не видимо ни в подклассах А, ни в пакете test, ни тем более в других пакетах. Поэтому у нас есть все основания считатать, что метод test() возвращает всегда число 33. Более того, мы бы могли использовать это предположение в своем коде, завязав на этом какую-либо логику.
Далее, имеем класс в любом другом пакете (но в пределах этой же JVM):

package access;

public class X {

	public void getAccess() throws Exception{
		A a = new A();
		Field x = a.getClass().getDeclaredField("x");
		x.setAccessible(true);
		x.setInt(a, x.getInt(a)+1);
		System.out.print("protected field: ");
		a.test();
	}
}

Вот и все. Все наши предположения о инкапсуляции и неизменности поля А.х развеяны. Метод А.test() теперь возвращает число 34 (и, если мы завязали на этом какую-нибудь логику, то наш код вряд ли теперь будет работать корректно). И даже может показаться, что нарушена одна из трех заповедей ООП — инкапсуляция. Во всем виновен программист пакет java.lang.reflect. Этот пакет предоставляет возможности для работы с объектами на этапе выполнения программы. Т.к. практически все классы этого объекта работают с динамическими структурами, то он довольно медленно работает и должен использоваться лишь в исключительных случаях. Однако возможность его применения остается и нарушение инкапсуляции налицо.

Впоследствии, покапавшись с этим пакетом, я нашел, что за соблюдение безопасности и доступа отвечает специальный класс SecurityManager и, если его запустить, то все становится на свои места. Если нашему классу А добавить статический инициализатор:

public class A {
	static{
		if(System.getSecurityManager()==null)
			System.setSecurityManager(new SecurityManager());
	}

То финт ушам, какой получился в предыдущем примере, уже не пройдет. Т.ч. все в порядке, Java по-прежнему ОО язык :) Единственное, для чего я написал этот пост — предупредить (это не сигнал к действию, просто нужно это тоже иметь в виду) всех явистов:

РАЗРАБОТЧИК, БДИ!
При отсутствии SecurityManager на этапе выполнения в твой класс может залезть кто-угодно и изменить что-угодно!
Tags:
Hubs:
+13
Comments68

Articles

Change theme settings