Различия в стеке JAVA
Автор:Eve Cole
Время обновления:2009-11-30 17:08:19
-
1. Стек и куча — это места, используемые Java для хранения данных в Ram. В отличие от C++, Java автоматически управляет стеком и кучей, и программисты не могут напрямую задавать стек или кучу.
2. Преимущество стека в том, что скорость доступа выше, чем у кучи, уступая только регистрам, расположенным непосредственно в ЦП. Но недостатком является то, что размер и время жизни данных, хранящихся в стеке, должны быть определены, а также отсутствует гибкость. Кроме того, данные стека могут быть общими, подробности см. в пункте 3. Преимущество кучи заключается в том, что она может динамически распределять размер памяти, и не нужно заранее сообщать компилятору время жизни. Сборщик мусора Java автоматически соберет данные, которые больше не используются. Но недостатком является то, что из-за необходимости динамического выделения памяти во время выполнения скорость доступа низкая.
3. В Java существует два типа типов данных.
Один из них — примитивные типы, всего их 8 типов, а именно int, short, long, byte, float, double, boolean, char (обратите внимание, что базового типа строки не существует). Этот тип определения определяется в такой форме, как int a = 3; long b = 255L и называется автоматической переменной. Стоит отметить, что автоматические переменные хранят литеральные значения, а не экземпляры классов, то есть они не являются ссылками на классы. Класса здесь нет. Например, int a = 3; где a — ссылка, указывающая на тип int, указывающий на буквальное значение 3. Данные этих литеральных значений известны по размеру и времени жизни (эти литеральные значения жестко определены в определенном программном блоке, а значения полей исчезают после выхода из программного блока. В целях скорости они исчезают). существуют в стеке.
Кроме того, у стека есть очень важная особенность: данные, хранящиеся в стеке, могут быть разделены. Предположим, мы также определяем:
интервал а = 3;
интервал б = 3;
Компилятор сначала обрабатывает int a = 3; сначала он создает ссылку на переменную a в стеке, а затем ищет адрес с литеральным значением 3. Если он его не находит, он открывает адрес для хранения литерала. значение 3, а затем a указывает на адрес 3. Далее обрабатывается int b = 3; после создания ссылочной переменной b, поскольку в стеке уже есть буквальное значение 3, b напрямую указывает на адрес 3. Таким образом, возникает ситуация, когда a и b одновременно указывают на 3.
Важно отметить, что ссылка на это буквальное значение отличается от ссылки на объект класса. Предположим, что ссылки двух объектов класса одновременно указывают на один и тот же объект. Если одна ссылочная переменная объекта изменяет внутреннее состояние объекта, то другая ссылочная переменная объекта немедленно отразит это изменение. Напротив, изменение значения литеральной ссылки не приводит к изменению значения другой ссылки на литерал. Как и в приведенном выше примере, после того, как мы определили значения a и b, мы установили a=4, тогда b не будет равно 4, но все равно будет равно 3; Внутри компилятора, когда он встречает a = 4;, он повторно проверяет, есть ли в стеке буквальное значение 4. Если нет, он повторно открывает адрес для сохранения значения 4, если оно уже существует; , он будет напрямую указывать на этот адрес. Следовательно, изменение значения a не повлияет на значение b.
Другой — данные классов-оболочек, таких как Integer, String, Double и другие классы, которые обертывают соответствующие базовые типы данных. Все эти типы данных существуют в куче. Java использует оператор new(), чтобы явно сообщить компилятору, что они будут создаваться динамически по мере необходимости во время выполнения, поэтому это более гибко, но недостатком является то, что это занимает больше времени. 4. Строка — это данные специального типа-обертки. То есть его можно создать в виде String str = new String( "abc" ); или в виде String str = "abc" ; (для сравнения, до JDK 5.0 вы этого никогда не делали). видел Integer i = 3; выражение, поскольку классы и литеральные значения не могут использоваться взаимозаменяемо, за исключением String, это выражение возможно, поскольку компилятор выполняет преобразование Integer i = new Integer(3). фон) . Первый представляет собой стандартизированный процесс создания классов, то есть в Java все является объектом, а объекты — экземплярами классов, и все они создаются в форме new(). Некоторые классы в Java, такие как класс DateFormat, могут возвращать вновь созданный класс через метод getInstance() класса, что, похоже, нарушает этот принцип. Не совсем. Этот класс использует шаблон синглтон для возврата экземпляра класса, но этот экземпляр создается внутри класса с помощью new(), а getInstance() скрывает эту деталь извне. Тогда почему в String str="abc" ; экземпляр не создается через new(). Нарушает ли это вышеуказанный принцип? На самом деле нет.
5. О внутренней работе String str = "abc". Java внутренне преобразует этот оператор в следующие шаги:
(1) Сначала определите ссылочную переменную объекта с именем str для класса String: String str;
(2) Найдите в стеке адрес со значением «abc». Если нет, откройте адрес с литеральным значением «abc», затем создайте новый объект o класса String и добавьте символы o Строковое значение указывает на этот адрес, и объект, на который ссылается o, записывается в стеке рядом с этим адресом. Если адрес со значением «abc» уже существует, объект o найден и возвращается адрес o.
(3) Укажите str на адрес объекта o.
Стоит отметить, что обычно строковые значения в классе String сохраняются напрямую. Но, как и в этом случае String str = "abc"; строковое значение сохраняет ссылку на данные, хранящиеся в стеке!
Чтобы лучше проиллюстрировать эту проблему, мы можем проверить ее с помощью следующих кодов.
Строка str1 = "abc";
Строка str2 = "abc";
System.out.println(str1==str2); //истина;
Обратите внимание, что мы не используем здесь str1.equals(str2); потому что это будет сравнивать, равны ли значения двух строк. Знак ==, согласно инструкциям JDK, возвращает true только тогда, когда обе ссылки указывают на один и тот же объект. Здесь мы хотим увидеть, указывают ли str1 и str2 на один и тот же объект.
Результаты показывают, что JVM создала две ссылки str1 и str2, но создала только один объект, и обе ссылки указывали на этот объект.
Давайте пойдем еще дальше и изменим приведенный выше код на:
Строка str1 = "abc";
Строка str2 = "abc";
стр1 = "BCD";
System.out.println(str1 + "," + str2 //bcd, abc);
System.out.println(str1==str2); //ложь
Это означает, что изменение назначения приводит к изменению ссылки на объект класса, а строка str1 указывает на другой новый объект! И str2 по-прежнему указывает на исходный объект. В приведенном выше примере, когда мы изменили значение str1 на «bcd», JVM обнаружила, что в стеке нет адреса для хранения этого значения, поэтому она открыла этот адрес и создала новый объект, строковое значение которого указывает на этот адрес. .
Фактически, класс String спроектирован так, чтобы быть неизменяемым. Если вы хотите изменить его значение, вы можете это сделать, но JVM незаметно создает новый объект на основе нового значения во время выполнения, а затем возвращает адрес этого объекта по ссылке на исходный класс. Хотя этот процесс создания полностью автоматизирован, все-таки он занимает больше времени. В среде, чувствительной к требованиям времени, это будет иметь определенные неблагоприятные последствия.
Еще раз измените исходный код:
Строка str1 = "abc";
Строка str2 = "abc";
стр1 = "BCD";
Строка стр3 = стр1;
System.out.println(str3); //BCD
Строка str4 = "BCD";
System.out.println(str1 == str4); //истина;
Ссылка на объект str3 указывает непосредственно на объект, на который указывает str1 (обратите внимание, что str3 не создает новый объект). После того, как строка str1 изменит свое значение, создайте ссылку на строку str4 и укажите на новый объект, созданный, поскольку строка 1 изменила значение. Можно обнаружить, что на этот раз str4 не создал новый объект, тем самым снова реализовав совместное использование данных в стеке.
Давайте еще раз посмотрим на следующий код.
Строка str1 = новая строка («abc»);
Строка str2 = "abc";
System.out.println(str1==str2); //ложь
Создаются две ссылки. Создаются два объекта. Две ссылки указывают на два разных объекта соответственно.
Строка str1 = "abc";
Строка str2 = новая строка («abc»);
System.out.println(str1==str2); //ложь
Создаются две ссылки. Создаются два объекта. Две ссылки указывают на два разных объекта соответственно.
Приведенные выше два фрагмента кода показывают, что пока new() используется для создания нового объекта, он будет создан в куче, а его строковое значение будет храниться отдельно, даже если оно совпадает с данными в стеке. , он не будет использоваться совместно с данными в стеке.
6. Значение класса-оболочки типа данных не может быть изменено. Не только значение класса String не может быть изменено, но и все классы-оболочки типов данных не могут изменять свои внутренние значения. 7. Выводы и предложения:
(1) Когда мы определяем класс, используя такой формат, как String str = «abc»;, мы всегда считаем само собой разумеющимся, что создаем объект str класса String. Бойтесь ловушек! Возможно, объект не был создан! Единственное, что можно сказать наверняка, это то, что создается ссылка на класс String. Что касается того, указывает ли эта ссылка на новый объект, это необходимо рассматривать в соответствии с контекстом, если только вы явно не создадите новый объект с помощью метода new(). Поэтому более точным утверждением будет создание ссылочной переменной str, указывающей на объект класса String. Эта ссылочная переменная объекта указывает на класс String со значением «abc». Четкое понимание этого очень помогает в устранении труднообнаружимых ошибок в программе.
(2) Использование String str = «abc» может в определенной степени повысить скорость работы программы, поскольку JVM автоматически определит, необходимо ли создавать новый объект, исходя из фактического положения данных в стеке. . Что касается кода String str = new String("abc");, то новые объекты всегда создаются в куче, независимо от того, равны ли их строковые значения или необходимо создавать новые объекты, тем самым увеличивая нагрузку по программе. Эта идея должна быть идеей режима легковеса, но неизвестно, применяет ли этот режим внутренняя реализация JDK.
(3) При сравнении того, равны ли значения в классе упаковки, используйте методquals(); при проверке того, указывают ли ссылки двух классов упаковки на один и тот же объект, используйте ==.
(4) Из-за неизменяемой природы класса String, когда переменной String необходимо часто менять свое значение, вам следует рассмотреть возможность использования класса StringBuffer для повышения эффективности программы.