@LaYof

Как исправить конкретный cлучай со stackoverflow? Как понять java?

Скажите пожалуйста, ну с какой, блин, стороны в этом примере классы друг от друга зависят друг от друга.
А точнее класс Lois от класса Pitter. Проблема в том, что происходит переполнение стека и программа выдает ошибку. Я новичок и прошу не ругаться за мои, возможно тупые ошибки.eb586b323b8749eaa96e5c47a8ce9e14.jpg78f8ab6d2f634772ac181bc223904bf1.jpg
  • Вопрос задан
  • 541 просмотр
Решения вопроса 1
pi314
@pi314
Президент Солнечной системы и окрестностей
Эти классы зависят друг от друга, т.к. в одном создается экземпляр другого, и наоборот. Само по себе это не страшно, но у Вас это происходит и там, и там прямо в инициализации (то место, где объявляются поля и им тут же присваивается новый экземпляр другого класса).
public class Lois {
  Pitter husbend = new Pitter();
  ...

public class Pitter {
  Lois wife = new Lois();
...

Инициализация выполняется всякий раз, когда создается экземпляр этого класса:
...  = new Lois();
 //or
    ... = new Pitter();

причем, совершенно неважно, где и почему программа пытается его создавать.

Соответственно, попытка (изначально - где-то в другом месте программы) создать экземпляр любого из этих классов приводит к тому, что для этого сначала потребуется создать новый экземпляр второго, для чего потребуется создать новый экземпляр первого и т.д. до бесконечности. В результате эта самая первая попытка никогда не завершится... точнее, завершится, переполнением стека.

Что такое переполнение стека и почему оно тут происходит? Любое создание объекта (экземпляра класса) "внутри" означает просто-напросто "вызов метода" (статической инициализации, затем конструктора; только после возврата из конструктора объект считается созданным и ссылка на экземпляр может быть чему-то там присвоена). А "вызов метода" означает (немного упрощенно) не что иное, как "положить на стек точку возврата, передаваемые параметры метода и перейти по адресу метода". Метод забирает со стека параметры и точку возврата, в конце кладет на стек свой результат (если он есть), и совершает переход по точке возврата. Если за ней в программе присвоение (как в ваших классах), результат забирается со стека и "присваивается". Таким образом, при нормальном вызове стек остается "чистым". А если вызов уходит в бесконечную цепочку других вызовов, как у Вас, то до очистки дело никогда не доходит, и стек рано или поздно заканчивается. Вот тогда и прилетает StackOverflowError.

Упрощено суть того, что в Вас происходит, можно проиллюстрировать вот так:
public class Main {

	public static void main(String[] args) {
		// You can run JVM with:
		//	-XX:MaxJavaStackTraceDepth=-1 (unlimited number of stack trace frames)
		//  -Xss4m (play around with stack size)
		//
		try {
			first(); // causes stack overflow!
		} catch (StackOverflowError e) {
			System.err.println("Depth (frames):" + e.getStackTrace().length);
		}
	}
	
	static int first(){
		return second();
	}

	static int second(){
		return first();
	}
}

Можете запустить и посмотреть, что будет :)

Чтоб этого не происходило, нужно переделать код, например, вот так:
public class Lois {

	Pitter myHusband = new Pitter(this);
	
	public Lois(Pitter whoIsMyHusband){
		myHusband = whoIsMyHusband;
	}
	
	//...
}

public class Pitter {
	
	Lois myWife = new Lois(this);
	
	public Pitter(Lois whoIsMyWife) {
		myWife = whoIsMyWife;
	}
	
	// ...
}
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
@void_phoenix
У вас скорее всего при создании Lois Вызывается new Pitter, при создании которого вызывается new Lois, при создании которого вызывается new Pitter... Повторять до переполнения стека.
Ответ написан
Комментировать
@LaYof Автор вопроса
Вот класс Pitterc64606b4caa2422992c104fdb072fc22.png
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы