Лекция 03. Основы классов

Классы и объекты

Основы

Классы и объекты

Пример описания класса:

class Point {
    int x, y;   // экземплярные переменные
    Point() {   // конструктор
      x=0; y=0;
    }
    Point (int x1,int y1) { // конструктор
      x=x1; y=y1;
    }
    int getX() {return x;}
    int getY() {return y;}
}

Пример создания экземпляра класса и ссылки на него:

Point p1 = new Point();
Point p2 = p1;

Класс Коробка:

class Box {
    int width; // ширина коробки
    int height; // высота коробки
    int depth; // глубина коробки

    // Конструктор
    Box(int w,int h,int d) {
        width = w;
        height = h;
        depth = d;
    }
    // вычисляем объём коробки
    int getVolume() {
        return width * height * depth;
    }
}

Hello world

Еще одна версия Hello, world!. Создаем экземпляр класса, внутри которого объявлен main.

public class GreetHW
{
    String text="Hello, world!";
    void print() {
        System.out.println(text);
    }
    public static void main(String[] args) {
        GreetHW greet=new GreetHW();
        greet.print();
    }
}

Статические переменные

Объявление: static <type> <name>

Обращение: <classname>.<varname>

  • создаются в единственном экземпляре;
  • существуют вне зависимости от объектов класса;
  • создаются JVM в момент первого обращения к классу;
  • допускают обращение до создания объектов класса;

Статические методы

Статические методы:

  • могут вызывать только другие статические методы
  • должны обращаться только к статическим переменным
  • внутри статических методов нельзя использовать ссылки this и super

Статический блок

Объявление:

static
{
   // ....
}

Статический блок кода выполняется один раз при первоначальной загрузке класса

public class Fruits {
    static String[] list = new String[3];

    static {
      list[0]="Apple";
      list[1]="Orange";
      list[2]="Banana";
    }
    public static void main(String[] args) {
        for(int i=0;i<list.length;i++)
            System.out.println(list[i]);
    }
}

Использование this

Указатель на экземпляр this может использоваться для вызова одного конструктора из другого

public class Rectangle
{
    private int x, y, width, height;
    public Rectangle()  {
        this(0, 0, 0, 0);
    }
    public Rectangle(int width, int height) {
        this(0, 0, width, height);
    }
    public Rectangle(int x, int y, int width, int height) {
        this.x = x; this.y = y;
        this.width = width; this.height = height;
    }
}

Управление доступом

В Java используется похожая на С++ модель разграничения доступа к элементам классов.

Тем не менее, у нее есть ряд отличий:

  • Спецификатор доступа пишется перед каждым элементом класса;
  • Отсутствие спецификатора перед элементом тоже задает уровень доступа.

Правила доступа сведены в следующую таблицу:

Пользователь Private Отсутствие Protected Public
Один и тот же класс Yes Yes Yes Yes
Подкласс класса того же пакета No Yes Yes Yes
Класс пакета No Yes Yes Yes
Подкласс класса из другого пакета No No Yes Yes
Класс другого пакета No No No Yes

Пакеты

  • В первой строке файла может быть 1 необязательный оператор package.
  • В следующих строках может быть 1 или несколько необязательных операторов import- Далее идут описания классов и интерфейсов.
  • Среди классов, описанных в одном файле, только один может быть объявлен с модификатором public.

Свойства пакетов:

  • Каждый пакет предоставляет уникальное пространство имен для своего содержимого.
  • Допустимы вложенные пакеты.

Рассмотрим процесс разработки пакета для работы с простыми числами Prime.

package Prime;

public class Prime {
    public static boolean testPrime(int value) {
        boolean isprime=true;
        for(int i=2;i*i<=value;i++)
            if(value%i==0) {
                isprime=false;
                break;
            }
        return isprime;
    }
    public static int nextPrime(int begin) {
        while(!testPrime(++begin));
        return begin;
    }
    public static int nPrime(int begin,int num) {
        while(num>0) {
          while(!testPrime(++begin));
          num--;
        }
        return begin;
    }
}

Добавим в пакет еще один файл с классом Factorization:

package Prime;

public class Factorization
{
    public static void factorIt(int n) {
        for(int i=2;i*i<=n;i++) {
            if(n % i==0) {
                System.out.print(" "+i);
                n/=i;
            }
        }
        System.out.println();
    }
}

Оба файла Prime.java и Factorization.java помещаем в каталог Prime.

Демо-файл (класс), использующий пакет Prime:

import Prime.*;

public class Demo
{
    public static void main(String[] args) {
        for(int i=1;i<15;i++)
           System.out.println(Prime.nPrime(1,i));
        Factorization.factorIt(123456);
    }
}

Структура проекта:

.
├── Demo.class
├── Demo.java
└── Prime
    ├── Factorization.class
    ├── Factorization.java
    ├── Prime.class
    └── Prime.java

При вложенности пакетов имена разделяют точкой, а структура каталогов на диске соответствует вложениям.

anton.alg.Prime  ->  anton/alg/Prime

Перечисления

enum Season
{
       WINTER, SPRING, SUMMER, AUTUMN
}
..
Season season = Season.SPRING;
if (season == Season.SPRING) {
    season = Season.SUMMER;
}
System.out.println(season);

Объявляя enum, мы неявно создаем класс, производный от java.lang.Enum

Конструкция

enum Season {  }

эквивалентна

class Season extends java.lang.Enum { }

Явным образом наследоваться от java.lang.Enum не позволяет компилятор, но:

System.out.println(Season.class.getSuperclass());

дает вывод:

class java.lang.Enum

Класс, созданный компилятором для реализации перечисления – enum-класс, а возможные значения перечисляемого типа – элементы enum.

Элементы перечисления - экземпляры enum-класса, доступные статически.

Их статическая доступность позволяет нам выполнять сравнение с помощью оператора сравнения ссылок.

Season season = Season.SUMMER;
if (season == Season.AUTUMN)
  season = Season.WINTER;

Любой enum-класс наследует java.lang.Enum, который содержит ряд методов полезных для всех перечислений.

Пример:

Season season = Season.WINTER;
System.out.println("season.name()=" + season.name() +
       " season.toString()=" + season.toString() +
       " season.ordinal()=" + season.ordinal());

Вывод:

season.name()=WINTER
season.toString()=WINTER
season.ordinal()=0

Задача: получить элемент enum по его строковому представлению.

Решение: В каждом enum-классе компилятор автоматически создает специальный статический метод:

public static EnumClass valueOf(String name)},

который возвращает элемент перечисления EnumClass с названием, равным name.

Пример использования:

String name = "WINTER"; Season season = Season.valueOf(name);

Результат: переменная season будет равна Season.WINTER

Если элемент не будет найден, то будет выброшен IllegalArgumentException, а если name равен null -

NullPointerException.

Иногда необходимо получить список всех элементов enum-класса во время выполнения. Для этих целей в каждом enum-классе компилятор

создает метод:

public static EnumClass[ ] values()

Пример использования:

System.out.println(Arrays.toString(Season.values()));

Вывод:

[WINTER, SPRING, SUMMER, AUTUMN]

Обратите внимание, что ни метод valueOf(), ни метод

values() не определен в классе java.lang.Enum.

Вместо этого они автоматически добавляются компилятором на этапе

компиляции enum-класса.

Методы с переменным числом аргументов

Для указания аргумента переменной длины используют три точки (...).

Например:

static void vaTest(int ... v) { … }

Эта синтаксическая конструкция указывает компилятору, что метод vaTest () может вызываться с нулем или более аргументов.

В результате v неявно объявляется как массив типа int [ ]. Таким образом, внутри метода vaTest () доступ к v

осуществляется с использованием синтаксиса обычного массива.
class VarArgs
{
   static void vaTest(int ... v) {
     System.out.println("Кол-во аргументов: " + v.length);
     for (int i=0;i<v.length;i++)  {
        System.out.print (v[i] + " ");
     }
     public static void main(String args[]) {
        vaTest (10); //1 аргумент
        vaTest (1, 2, 3); //3 аргумента
        vaTest (); // без аргументов
     }
}

Вместе с параметром переменной длины массив может содержать обычные параметры. Однако параметр переменной длины должен быть последним

параметром, объявленным методом.

Вопросы для самоконтроля