2 Конструкторы, конструкторы с параметром, перегрузка конструкторов, конструкторы по умолчанию. Ключевое слово this.

 

В Java разработчик классов может гарантировать инициализацию каждого объекта, обеспечив специальный метод, называемый конструктором. Если класс имеет конструктор, Java автоматически вызывает конструктор, когда создается объект, прежде чем пользователь сможет взять его в руки. Поэтому инициализация гарантируется. Следующая сложность состоит в названии метода. Есть две проблемы. Первая заключается в том, что любое имя, которое вы используете, может совпасть с именем, которое вы захотите использовать в качестве члена класса. А вторая заключается в том, что, так как компилятор отвечает за вызов конструктора, то он всегда должен знать, какой метод вызывать. Поэтому, имя конструктора совпадает с именем класса.

Вот пример класса с конструктором:

class Rock {

  Rock() {

    System.out.println("Creating Rock");

  }

}

public class SimpleConstructor {

  public static void main(String[] args) {

          new Rock();

  }

}

 

Как и любой другой метод, конструктор может иметь аргументы, которые позволят вам указать способ создания объекта.

class Rock2 {

  Rock2(int i) {

    System.out.println(

      "Creating Rock number " + i);

  }

}

public class SimpleConstructor2 {

  public static void main(String[] args) {

    for(int i = 0; i < 10; i++)

      new Rock2(i);

  }

}

 

Поскольку имя конструктора является предопределенным именем класса, то может быть только одно имя конструктора. Но что, если вы хотите создавать объект более чем одним способом?  Вам нужно два конструктора, один не принимает, а другой принимает в качестве аргумента String, который является именем файла, из которого инициализируется объект. Ода они являются конструкторами, так что они должны иметь одно и то же имя — имя класса. Таким образом, перегрузка методов необходима для получения возможности использования одного и того же имени метода с разными типами аргументов.

Есть простое правило: каждый перегруженный метод должен иметь уникальный список типов аргументов. Даже различия в порядке следования аргументов существенны для различения двух методов.

 

Как упоминалось ранее, конструктор по умолчанию является единственным конструктором без аргументов, который используется для создания объекта. Если вы создаете класс, который не имеет конструкторов, компилятор автоматически создаст конструктор по умолчанию вместо вас. Например:

class Bird {

  int i;

}

public class DefaultConstructor {

  public static void main(String[] args) {

    Bird nc = new Bird();

  }

}

Строка

new Bird();

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

 

Предположим, вы находитесь внутри метода и хотите получить ссылку на текущий объект. Так как эта ссылка передается компилятором в тайне, здесь нет идентификатора для нее. Однако для этих целей существует ключевое слово: this. Ключевое слово this, которое может использоваться только внутри метода, производит ссылку на объект, который вызвал метод. Вы можете трактовать эту ссылку, как и любой другой объект. Например, в инструкции return, когда вы хотите вернуть ссылку на текущий объект:

public class Leaf {

  int i = 0;

  Leaf increment() {

    i++;

    return this;

  }

 

3. Инициализация переменных и массивов в Java.

 

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

  void f() {

    int i;

    i++;

  }

вы получите сообщение об ошибке, которое скажет, что возможно, что i не инициализирована. Конечно, компилятор мог бы дать i значение по умолчанию, но это больше похоже на ошибку программиста, а значение по умолчанию может ее скрыть. Заставлять программиста выполнять инициализацию лучше с точки зрения нахождения ошибок.

Однако если примитивный тип является членом-данным класса, происходящее немного отличается. Так как любой метод может инициализировать или использовать данные, становится не практично заставлять пользователя инициализировать их соответствующим значением перед использованием. Однако также не безопасно оставлять их, заполненными всяким мусором, так как каждая переменная - член класса примитивного типа гарантированно получает инициализирующее значение.

 

Массив - это просто последовательность либо объектов, либо примитивных типов, которые все имеют один тип и упакованы вместе под одним идентификатором. Массивы определяются и используются с квадратными скобками оператора индексирования [ ]. Для определения массива вы просто указываете имя типа, за которым следуют пустые квадратные скобки:

int[] a1;

Компилятор не позволяет вам объявить величину массива. Это происходит из-за свойств “ссылок”. Все, что вы имеете в этой точке - это ссылка на массив, и здесь не резервируется место для массива. Для создания хранилища для массива вы должны написать выражение инициализации. Для массивов, инициализация может быть выполнена в любом месте вашего кода, но вы также можете использовать особый вид выражения инициализации, которая должна происходить в точке создания. Эта особая инициализация обеспечивает набор значений, заключенных в фигурные скобки. О резервировании хранилища (эквивалентно использованию new) в этом случае заботится компилятор. Например:

int[] a1 = { 1, 2, 3, 4, 5 };

Что, если вы не знаете, сколько элементов вам потребуется в вашем массиве, когда вы пишите программу? Вы просто используете new для создания элементов массива. Здесь new работает даже для создания массива примитивных. Если вы имеете дело с массивом не примитивных объектов, вы должны всегда использовать new. Это происходит из-за использования ссылок, так как вы создаете массив ссылок.

 

4. Статические поля и методы (static). Модификатор final. Примеры.

 

Один из них, если вы хотите иметь только одну часть хранилища для определенных данных, не зависимо от того, сколько объектов создано, или даже если не было создано объектов этого класса. Второй, если вам нужен метод, который не ассоциируется с объектом определенного класса. То есть, вам нужен метод, который вы можете вызвать, даже если объект не создан. Вы можете достигнуть этих эффектов с помощью ключевого слова static. Когда вы говорите о чем-то static, это означает, что данные или метод не привязаны к определенному экземпляру объекта класса. Даже если вы никогда не создадите объект этого класса, вы сможете вызвать статический метод или получить доступ к части статических данных. Как обычно, не статические данные и методы вы создаете объект и используете его для доступа к данным или методам, так как не статические данные и методы должны знать определенный объект, с которым они работают. Конечно, так как статическим методам не нужно создавать объект до их использования, они не могут получить прямой доступ к не статическим членам или методам простым вызовом этих методов без указания имени объекта (так как не статические члены и методы должны быть привязаны к определенному объекту).

Чтобы сделать член-данное или член-метод статическим, вы просто помещаете ключевое слово перед определением. Например, следующий код производит статический член-данное и инициализирует его:

class StaticTest {

    static int i = 47;

}

 

В Java ключевое слово final имеет слегка разные значения в зависимости от контекста, но в основном, оно определяется так "Это не может быть изменено".

В случае константы во время компиляции компилятор свертывает константу до значения в любых вычислениях, где она используется; при этом, нагрузка при вычислениях во время работы программы может быть значительно снижена. В Java константы такого рода должны быть примитивного типа и объявлены с использованием final. Значение должно быть определено во время определения переменной, как и любой константы.

При использовании final с объектами, а не с примитивными типами получается несколько не тот эффект. С примитивами, final создает константу значения, а с объектами - ссылку, final создает ссылку - константу. Как только ссылка инициализируется на какой-то объект, она уже не может быть в последствии перенаправлена на другой объект. Однако сам объект может быть модифицирован; Java не предоставляет способа создать объект - константу. Эти же ограничения накладываются и на массивы, поскольку они тоже объекты.

 

class Value {

  int i = 1;

}

 

public class FinalData {

  // Может быть константой во время компиляции

  final int i1 = 9;

  static final int VAL_TWO = 99;

  // Обычная public константы:

  public static final int VAL_THREE = 39;

  // Не может быть константой во время компиляции:

  final int i4 = (int)(Math.random()*20);

  static final int i5 = (int)(Math.random()*20);

 

  Value v1 = new Value();

  final Value v2 = new Value();

  static final Value v3 = new Value();

  // Массивы:

  final int[] a = { 1, 2, 3, 4, 5, 6 };

 

  public void print(String id) {

    System.out.println(

      id + ": " + "i4 = " + i4 +

      ", i5 = " + i5);

  }

  public static void main(String[] args) {

    FinalData fd1 = new FinalData();

    //! fd1.i1++; // Ошибка: значение не может быть изменено

    fd1.v2.i++; // Объект не константа!

    fd1.v1 = new Value(); // OK -- не final

    for(int i = 0; i < fd1.a.length; i++)

      fd1.a[i]++; // Объект не константа!

    //! fd1.v2 = new Value(); // Ошибка: Нельзя

    //! fd1.v3 = new Value(); // изменить ссылку

    //! fd1.a = new int[3];

 

    fd1.print("fd1");

    System.out.println("Creating new FinalData");

    FinalData fd2 = new FinalData();

    fd1.print("fd1");

    fd2.print("fd2");

  }

} ///:~

 

Java позволяет создавать пустые (чистые) final объекты (blank final), это такие поля данных, которые были объявлены как final но при этом не были инициализированы значением.

Java позволяет Вам так же создавать и аргументы final определением их таким образом прямо в списке аргументов. Это означает, что внутри метода Вы не сможете изменить этот аргумент или его ссылку.

Существует две причины для final методов. Первая - закрытие методов, от возможной модификации при наследовании класса. Такой подход применяется если Вы хотите быть уверенны, что этот метод не будет переопределен в дочерних классах и поведение класса не изменится. Вторая причина - final методы более эффективны.

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

 

Hosted by uCoz