1. Знание указывает на контроль объекта и памяти
1. Процесс инициализации переменных Java, включая локальные переменные, переменные -члены (переменные экземпляра и переменные класса).
2. В зависимости от наследования, когда тип типа и типа времени сбора отличаются от типа с компиляцией времени используемой ориентировочной переменной объекта, существует разница в свойствах и методах доступа к объекту.
3. Окончательные характеристики модификатора.
2. Процесс деления и инициализации переменных Java
Переменные программы Java могут быть примерно разделены на переменные члена и локальные переменные. Переменные члена можно разделить на переменные экземпляра (нестатические переменные) и переменные класса (статические переменные). Как правило, локальные переменные, с которыми мы сталкиваемся, появятся в следующих ситуациях:
(1) Формальный параметр: локальные переменные, определенные в подписи метода, присваиваются вызывающим абонент и исчезают по мере того, как метод заканчивается.
(2) Локальные переменные в методе: локальные переменные, определенные в методе, должны быть инициализированы (присваивайте начальное значение) в методе и исчезают, когда инициализация переменной начинается и заканчивается.
(3) Локальные переменные в блоке кода: локальные переменные, определенные в блоке кода, должны быть инициализированы (назначение начальных значений), которые должны отображаться в блоках кода. Они вступит в силу по мере завершения инициализации и умирают по мере того, как заканчивается блок кода.
пакет com.zlc.array; открытый класс Testfield {{String B; // Если не инициализировано, компилятор сообщит о локальной переменной B, возможно, не была инициализирована System.out.println (b); } public static void main (string [] args) {int a; // Если не инициализировано, компилятор сообщит о локальной переменной A, возможно, не была инициализирована System.out.println (a); }} Переменные члена, модифицированные Static, являются переменными класса, которые принадлежат к самому классу. Переменные члена, которые не изменяются со статическими, являются переменными экземпляра. Экземпляры, принадлежащие этому классу, в одном и том же JVM каждый класс может соответствовать одному объекту класса, но каждый класс может создавать несколько объектов Java. (То есть переменная класса требует только одного куска памяти, и каждый раз, когда класс создает экземпляр, ей необходимо выделить кусок пространства с переменной экземпляра)
Процесс инициализации переменных экземпляра: с точки зрения синтаксиса программа может выполнять инициализацию переменных экземпляра в трех местах:
(1) Укажите начальное значение при определении переменной экземпляра.
(2) Укажите исходное значение, например, переменные в нестатических блоках.
(3) Укажите начальное значение, например, переменные в конструкторе.
Среди них время инициализации двух методов (1) и (2) раньше, чем у (3) в конструкторе, и два порядка инициализации (1) и (2) определяются в порядке, которые они расположены в исходном коде.
пакет com.zlc.array; открытый класс Testfield {public Testfield (int age) {System.out.println ("Инициализировать это. this.age = возраст; } {System.out.println ("Инициализировать в нестатических блоках"); возраст = 22; } // Инициализировать int age = 15; public static void main (string [] args) {testfield field = new Testfield (24); System.out.println ("final age ="+field.age); }} Результат выполнения: инициализировать это .age = 15 в конструкторе инициализации в нестатическом блоке
Окончательный возраст = 24
Если вы знаете, как использовать Javap, вы можете использовать Javap -c XXXX (файл класса), чтобы увидеть, как составлен класс Java.
При определении переменной экземпляра указать начальное значение. В блоке инициализации статус оператора, указывающий начальное значение для переменной экземпляра, является равным. После того, как компилятор будет скомпилирован и обработан, все они упоминаются в конструкторе. Вышеупомянутый возраст int = 15 будет разделен на следующие два шага для выполнения:
1) int возраст; При создании объекта Java система выделяет память объекту в соответствии с оператором.
2) возраст = 15; Это утверждение будет извлечено в конструктор класса Java и выполнено.
Процесс инициализации переменных класса: с точки зрения синтаксиса программа может инициализировать и назначать значения переменным класса из двух мест.
(1) Укажите начальное значение при определении переменной класса.
(2) Укажите начальное значение для переменных класса в статическом блоке.
Два порядка выполнения совпадают с их договоренностью в исходном коде. Давайте приведем небольшой аномальный пример:
пакет com.zlc.array; Class TestStatic {// Демо -тестовый экземпляр TestStatic Class TestStatic Teal Static TestStatic Demo = New TestStatic (15); // Участник класса static int age = 20; // переменная экземпляра Curage int curage; Public TestStatic (int Years) {// TODO Автогенерированный конструктор Curage = Age - Years; }} public Class Test {public static void main (string [] args) {system.out.println (teststatic.demo.curage); TestStatic StaticDemo = New TestStatic (15); System.out.println (staticdemo.curage); }} Результат вывода напечатан в двух линиях. Одним из них является распечатка переменной экземпляра экземпляра демонстрации атрибута класса TestStatic, а второе - вывести атрибут экземпляра TestStatic через статику объекта Java. В соответствии с процессом инициализации переменной переменной экземпляра и класса переменных, которые мы проанализировали выше, мы можем сделать это:
1) На первом этапе инициализации при загрузке класса выделяйте пространство памяти для переменных класса демо и возраст. В настоящее время значения по умолчанию демонстрации и возраста являются нулевыми и 0 соответственно.
2) На втором этапе инициализации программа назначает начальные значения демо и возрасту в последовательности. TestStatic (15) должен вызвать конструктор TestStatic. В настоящее время возраст = 0, поэтому результат печати составляет -15. Когда StaticDemo инициализируется, возраст был назначен 20, поэтому результат выхода составляет 5.
3. Разница между наследственными переменными членами и наследственными методами членов в отношениях наследования
При создании любого объекта Java программа всегда будет называть нестатический блок и конструктор родительского класса сначала родительского класса, и, наконец, называть нестатический блок и конструктор этого класса. Призыв к конструктору родительского класса через конструктор подкласса, как правило, делится на две ситуации: одна - неявный вызов, а другой - супер -дисплей, чтобы вызвать конструктор родительского класса.
Метод класса дочернего класса может вызвать переменную экземпляра родительского класса. Это связано с тем, что класс ребенка наследует родительский класс и получит переменные члена и методы родительского класса. Тем не менее, метод родительского класса не может получить доступ к переменной экземпляра детского класса, потому что родительский класс не знает, какой класс он будет наследовать и какие переменные члена его подкласса добавит. Конечно, в некоторых экстремальных примерах родительский класс все еще может вызвать переменную класса ребенка. Например: класс дочернего класса переписывает метод родительского класса и обычно печатает значение по умолчанию, потому что переменная экземпляра детского класса не была инициализирована в настоящее время.
пакет com.zlc.array; класс отец {int age = 50; publicather () {// todo с генерируемым конструктором System.out.out.println (this.getClass ()); //this.sonmethod (); не может позвонить info (); } public void info () {System.out.println (age); }} public Class Son расширяет отца {int age = 24; Public Son (int age) {// todo Автогенерированный конструктор Stub this.age = age; } @Override public void info () {// todo Автогенерированный метод STUB System.err.println (AGE); } public static void main (string [] args) {new Son (28); } // Специфичный метод подкласса public void sonmethod () {System.out.println ("Son Method"); }} Согласно нашему нормальному выводу, конструктор родительского класса неявно вызывается через подкласс, и метод Info () вызывается в конструкторе родительского класса (примечание: я не сказал, что родительский класс называется). Теоретически, он выводит переменную экземпляра возраста родительского класса. Ожидается, что результат печати будет 50, но фактический результат вывода - 0. Анализ причины:
1) Распределение памяти объектов Java не завершено в конструкторе. Конструктор только завершает процесс назначения инициализации. То есть, прежде чем называть конструктор родительского класса, JVM классифицировал пространство памяти для объекта Son. Это пространство хранит два возрастных атрибута, один - возраст подкласса, а другой - возраст родительского класса.
2) При вызове нового сына (28) текущий этот объект представляет собой объект, который является сыном подкласса. Мы можем распечатать object.getClass () и получить результат класса com.zlc.array.son. Тем не менее, текущий процесс инициализации осуществляется в конструкторе родительского класса, и его нельзя вызвать через это.
3) Когда тип времени компиляции переменной отличается от типа времени выполнения, при доступе к переменной экземпляра его эталонного объекта через переменную, значение переменной экземпляра определяется типом объявленной переменной. Однако, когда метод экземпляра объекта, который он ссылается через переменную, поведение метода определяется объектом, который он фактически ссылается. Следовательно, информационный метод подкласса называется здесь, поэтому возраст подкласса печатается. Поскольку возраст еще не был срочно инициализирован, значение по умолчанию составляет 0.
С точки зрения непрофессионала, когда объявленный тип не соответствует настоящему новому типу, используемый атрибут - это родительский класс, а называемый метод - это ребенок.
Через javap -c мы можем более непосредственно понять, почему существует большая разница между наследственными атрибутами и методами. Если мы удалим метод переписывания информации сына подкласса в приведенном выше примере, в настоящее время будет вызовет метод информации родительского класса, поскольку при компиляции метод информации родительского класса будет перенесен на подкласс, а переменная члена репутации будет оставлена в родительском классе и не перенесена. Таким образом, подкласс и родительский класс имеют переменные экземпляра с тем же именем. Если подкласс переписывает метод родительского класса с тем же именем, метод подкласса полностью перезаписывает метод родительского класса (например, почему Java разработан так, я не очень ясен). Переменные с тем же именем могут существовать и не перезаписать одновременно. Подклассы методов с тем же именем полностью перезаписывают метод того же имени родительского класса.
В целом, для эталонной переменной при доступе к переменной экземпляра объекта он ссылается на переменную, значение переменной экземпляра зависит от типа при объявленной переменной, и при доступе к методу объекта, который он ссылается через переменную, поведение метода зависит от типа объекта, который он фактически ссылается.
Наконец, я рассмотрю это с небольшим случаем:
пакет com.zlc.array; класс животное {int age; public Animal () {} public Animal (int Age) {// TODO Автогенерированный конструктор STUB This.age = age; } void run () {System.out.println ("Animal Run"+AGE); }} класс собака расширяет животное {int age; String name; public Dog (int age, string name) {// todo автоматически генерируется конструктором. this.name = name; } @Override void run () {System.out.println ("Dog Run"+age); }} public class testextends {public static void main (string [] args) {Animal Animal = New Animal (5); System.out.println (Animal.age); Animal.run (); Собака = новая собака (1, "xiaobai"); System.out.println (Dog.age); Dog.run (); Животное 2 = новая собака (11, "Вангкай"); System.out.println (Animal2.age); Animal2.run (); Животное животное3; животное3 = собака; System.out.println (Animal3.age); Animal3.run (); }} Если вы хотите вызвать метод родительского класса: вы можете вызвать его через Super, но супер -ключевое слово не ссылается на какой -либо объект, и его нельзя использовать в качестве реальной эталонной переменной. Заинтересованные друзья могут изучить это самостоятельно.
Выше приведено примеры переменных и методов. Переменные класса и методы класса намного проще, поэтому напрямую используют имена классов. Методы гораздо более удобны, и вы не столкнетесь с таким большим количеством проблем.
4. Использование окончательных модификаторов (особенно замена макросов)
(1) Inal может изменить переменные. После того, как переменной, измененной Final, назначено начальное значение, ее нельзя было присвоить снова.
(2) INAL может изменить метод, и окончательный модифицированный метод не может быть переписан.
(3) INAL может изменять классы, а классы, измененные в конечном итоге, не могут получить подклассы.
Указанное начальное значение, которое переменная, измененная с помощью окончательного, должно быть отображено:
Например, переменные, которые являются окончательными измененными, начальное значение может быть назначено только в следующих трех указанных позициях.
(1) Укажите начальное значение при определении конечной переменной экземпляра.
(2) Укажите начальное значение для конечной переменной экземпляра в нестатическом блоке.
(3) Укажите начальное значение для конечной переменной экземпляра в конструкторе.
Они в конечном итоге будут упомянуты в конструкторе для инициализации.
Для переменных классов, указанных с окончательным: начальные значения могут быть назначены только в двух указанных местах.
(1) Укажите начальное значение при определении конечной переменной класса.
(2) Укажите начальное значение для конечной переменной класса в статическом блоке.
Также обрабатывается компилятором, в отличие от переменных экземпляра, все указываются переменные класса для назначения начальных значений в статических блоках, в то время как переменные экземпляра упоминаются для конструкторов.
Существует другая особенность переменных класса, модифицированных Final, которая является «заменой макроса». Когда модифицированная переменная класса удовлетворяет начальному значению при определении переменной, начальное значение может быть определена во время компиляции (например,: 18, «AAAA», 16.78 и другие прямые величины), тогда переменная класса, измененная конечной, не является переменной, а система будет рассматривать ее как «макро -переменную» (которую мы часто называем константой). Если начальное значение может быть определена во время компиляции, оно не будет упомянуто в статическом блоке для инициализации, а начальное значение будет непосредственно заменено конечной переменной в определении класса. Давайте приведем пример возраста минус год:
пакет com.zlc.array; Class TestStatic {// Демо -тестовый экземпляр TestStatic Class TestStatic Teal Static TestStatic Demo = New TestStatic (15); // Класс -возраст окончательный статический возраст int = 20; // переменная экземпляра Curage int curage; Public TestStatic (int Years) {// TODO Автогенерированный конструктор Curage = Age - Years; }} public Class Test {public static void main (string [] args) {system.out.println (teststatic.demo.curage); Teststatic static1 = new TestStatic (15); System.out.println (static1.curage); }} В настоящее время возраст модифицируется окончательным, поэтому при компиляции все возрасты в родительском классе становятся 20, а не переменной, так что результат вывода может соответствовать нашим ожиданиям.
Особенно при сравнении строк, это может быть отображено больше
пакет com.zlc.array; открытый класс TestString {static String static_name1 = "java"; статическая строка static_name2 = "me"; Статическая строка static statci_name3 = static_name1+static_name2; окончательная статическая строка final_static_name1 = "java"; окончательная статическая строка final_static_name2 = "me"; // Добавить окончательный или нет, его можно заменить на макрос спереди. Final String final_statci_name3 = final_static_name1+final_static_name2; public static void main (string [] args) {string name1 = "java"; String name2 = "me"; Строка name3 = name1+name2; // (1) System.out.println (name3 == "javame"); // (2) system.out.println (teststring.statci_name3 == "javame"); // (3) system.out.println (teststring.final_statci_name3 == "javame"); }} Нечего сказать об использовании окончательных методов модификации и классов, просто то, что один не может быть переписан с помощью подклассов (например, частных), а другой не может получить подклассы.
При изменении локальных переменных с окончательными, Java требует, чтобы локальные переменные, доступные к внутренним классам, были изменены с помощью окончательного. Есть причина. Для обычных локальных переменных их объем остается внутри метода. Когда метод заканчивается, локальная переменная исчезает, но внутренний класс может генерировать неявное «закрытие», что заставляет локальную переменную оставаться отделенной от метода, где она находится.
Иногда поток будет новым в методе, а затем вызывается локальная переменная метода. В настоящее время переменная изменения должна быть объявлена как окончательная изменена.
5. Метод расчета памяти заполнения объектов
Используйте методы Freememory (), TotalMemory () и MaxMemory () в классах java.lang.runtime, чтобы измерить размер объекта Java. Этот метод обычно используется, когда необходимо точно определить много ресурсов. Этот метод почти бесполезен для реализации кеша производственных систем. Преимущество этого метода заключается в том, что тип данных не зависит от размера, и различные операционные системы могут получить за занятость памяти.
Он использует API отражения для прохождения иерархии переменных элементов объекта и вычислить размер всех исходных переменных. Этот подход не требует так много ресурсов и может использоваться для кэшированных реализаций. Недостатком является то, что исходный размер типа различен, а различные реализации JVM имеют разные методы расчета.
После JDK5.0, API инструментария предоставляет метод GetObjectSize для расчета размера памяти, занятого объектом.
По умолчанию размер указанного объекта не рассчитывается. Чтобы рассчитать указанный объект, вы можете использовать отражение для его получения. Следующий метод - это реализация, представленная в вышеуказанной статье, которая вычисляет размер эталонного объекта:
Общедоступный класс SizeOfagent {статический инструмент Inst; / ** Инициализирует агент*/ public static void premain (String Agentargs, Instrumentation Instp) {Inst = Instp; } /*** Возвращает размер объекта без субъектов членов. * @param o Объект, чтобы получить размер * @return Object Size */public Static Long SizeOf (Object O) {if (inst == null) {Throw New allogalStateException («Не может получить доступ к среде инструментов. } return inst.getObjectSize (o); } /** * Вычисляет полный размер объекта, итерации по * графику иерархии. * @param объект для вычисления размера * @return Object Size */ public static long fullsizeof (Object obj) {map <объект, объект> посещение = new IdentityHashmap <object, object> (); Stack <object> Stack = new Stack <Object> (); длинный результат = InternalSizeof (obj, стек, посещение); while (! Stack.isempty ()) {result += internalsizeof (stack.pop (), stack, pisit); } visited.clear (); результат возврата; } private static boolean skipobject (Object obj, map <объект, объект> посетил) {if (obj instanceOf string) {// skip interned string if (obj == ((string) obj) .Intern ()) {return true; }} return (obj == null) // skip посещаемый объект || Посетил.containskey (obj); } private static long internalsizeof (Object obj, стек <object> стек, карта <объект, объект> посещение) {if (skipobject (obj, посещение)) {return 0; } посещение.put (obj, null); длинный результат = 0; // Получить размер объекта + примитивные переменные + точки членов результат + = sizeofagent.sizeof (obj); // обработать все элементы массива класс clazz = obj.getClass (); if (clazz.isarray ()) {if (clazz.getName (). length ()! = 2) {// пропустить примитивный массив типа int length = array.getLength (obj); for (int i = 0; i <length; i ++) {stack.add (array.get (obj, i)); }} return result; } // обрабатывать все поля объекта while (clazz! = null) {field [] fields = clazz.getDeclaredFields (); for (int i = 0; i <fields.length; i ++) {if (! modifier.isstatic (fields [i] .getModifiers ())) {if (fields [i] .getType (). ISprimitiate ())) {продолжение; // пропустить примитивные поля} else {fields [i] .setAccessible (true); try {// объекты, которые должны быть оценены, помещаются в стек ObjectToadd = fields [i] .get (obj); if (objectToadd! = null) {Stack.add (objectToadd); }} catch (allogalaccessexception ex) {assert false; }}}}} clazz = clazz.getSuperClass (); } return Result; }}