Java se сервлеты и jsp паттерны Gof junit Log4j ant



Pdf көрінісі
бет5/22
Дата11.12.2019
өлшемі8,99 Mb.
#53432
1   2   3   4   5   6   7   8   9   ...   22
Байланысты:
JAVA Methods Programming v2.march2015 (2)


КЛАССЫ И ОБЪЕКТЫ
63
приводит к появлению соответствующего предупреждения и не будет логиче-
ски корректным, хотя и не закончится ошибкой компиляции.
Переопределение  статических  методов  невозможно,  так  как  обращение 
к  статическому  атрибуту  или  методу  осуществляется  посредством  задания 
имени класса, которому они принадлежат.
Статические методы используются при необходимости придать функцио-
нальности  метода  признак  «окончательности»,  «неизменности»  реализации 
алгоритма для данного класса.
Модификатор final
Модификатор final используется для определения констант в качестве члена 
класса, локальной переменной или параметра метода. Методы, объявленные 
как final, нельзя замещать в подклассах. Для классов, объявленных со специ-
фикатором final, нельзя создавать подклассы. Например:
/* # 6 # final-поля и переменные # Card.java */
package
 by.bsu.finalvar;
public
 class Card {
        // инициализированная константа экземпляра
        public final int ID = (int)(Math.random() * 10_000_000);
        // неинициализированная константа
        public final long BANK_ID; // инициализация по умолчанию не производится!
        // { BANK_ID = 11111111L; }  // только один раз!!!
        public Card (long id) {
        // инициализация в конструкторе
                BANK_ID = id; // только один раз!!!
        }
        public final boolean checkRights(final int NUMBER) {
                final int CODE = 72173394; // антишаблон: "Волшебное Число"
                // ID = 1; // ошибка компиляции!
                // NUMBER = 1; // ошибка компиляции!
                // CODE = 1; // ошибка компиляции!
                return CODE == NUMBER + ID;
        }
}
Константа  может  быть  объявлена  как  поле  класса,  но  не  проинициали-
зирована. В этом случае она должна быть проинициализирована в логическом 
блоке класса, заключенном в {}, или конструкторе, но только в одном из ука-
занных мест. Значение по умолчанию константа получить не может в отличие 
от переменных класса. Константы могут быть объявлены в методах как локаль-
ные или как параметры метода. В обоих случаях значения таких констант из-
менять нельзя.

ОСНОВЫ JAVA
64
Абстрактные методы
Абстрактные методы размещаются в абстрактных классах или интерфейсах, 
тела у таких методов отсутствуют и должны быть реализованы в подклассах.
/* # 7 # абстрактный класс и метод # AbstractCardAction.java */
public
 abstract class AbstractCardAction {
       private Long account;
       public AbstractCardAction () { }
       /* тело абстрактного метода отсутствует */
       public abstract void doPayment(double amountPayment);
       public void setAccount(Long account) {
                  this.account = account;
       }
}
При этом становится невозможным создание экземпляра
AbstractCardAction ap = new AbstractCardAction(); // compile error
Спецификатор  abstract  присутствует  здесь  как  в  объявлении  метода,  так 
и в объявлении класса.
В отличие от интерфейсов абстрактный класс может содержать и абстрактные, 
и неабстрактные методы, а может и не содержать ни одного абстрактного метода.
Подробнее абстрактные класса и интерфейсы изучаются в главе «Наследование 
и полиморфизм».
Модификатор native
Приложение  на  языке  Java  может  вызывать  методы,  написанные  на  языке 
С++. Такие методы объявляются с ключевым словом native, которое сообщает 
компилятору, что метод реализован в другом месте. Например:
public native int 
loadCripto(int num);
Методы,  помеченные  native,  можно  переопределять  обычными  методами 
в подклассах.
Модификатор synchronized
При использовании нескольких потоков управления в одном приложении 
необходимо синхронизировать методы, обращающиеся к общим данным. Когда 
интерпретатор обнаруживает synchronized, он включает код, блокирующий до-
ступ  к  данным  при  запуске  потока  и  снимающий  блок  при  его  завершении. 

КЛАССЫ И ОБЪЕКТЫ
65
Вызов  методов  уведомления  о  возвращении  блокировки  объекта  notifyAll(), 
notify() и метода остановки потока wait() класса Object (суперкласса для всех 
классов языка Java) предполагает использование модификатора synchronized
так как эти методы предназначены для работы с потоками.
Логические блоки
При описании класса могут быть использованы логические блоки. Логическим 
блоком называется код, заключенный в фигурные скобки и не принадлежащий 
ни одному методу текущего класса, например:
{  /* код */  }
static
 { /* код */  }
Логические  блоки  чаще  всего  используются  в  качестве  инициализаторов 
полей,  но  могут  содержать  вызовы  методов  и  обращения  к  полям  текущего 
класса. При создании объекта класса они вызываются последовательно, в по-
рядке размещения, вместе с инициализацией полей как простая последователь-
ность операторов, и только после выполнения последнего блока будет вызван 
конструктор  класса.  Операции  с  полями  класса  внутри  логического  блока 
до явного объявления этого поля возможны только при использовании ссылки 
this, представляющей собой ссылку на текущий объект.
Логический блок может быть объявлен со спецификатором static. В этом 
случае он вызывается только один раз в жизненном цикле приложения при 
создании объекта или при обращении к статическому методу (полю) дан-
ного класса.
/* # 8 # использование логических блоков при объявлении класса # Department.java # 
DemoLogic.java */
package
 by.bsu.logic;
public
 class Department {
        {
                System.out.println("logic (1) id=" + this.id);
                // проверка и инициализация параметров конкретного объекта
        }
        static {
                System.out.println("static logic");
                /* проверка и инициализация базовых параметров, необходимых 
                для функционирования приложения (класса) */
        }
        private int id = 7;
        public Department(int id) {
                this.id = id;
                System.out.println("конструктор id=" + id);
        }

ОСНОВЫ JAVA
66
        public int getId() {
                return id;
        }
        { /* не очень хорошее расположение логического блока */
                System.out.println("logic (2) id=" + id);
        }
}
package
 by.bsu.logic;
public
 class DemoLogic {
                public static void main(String[ ] args) {
                                new Department(71);
                                new Department(17);
                }
}
В результате выполнения этой программы будет выведено:
static logic
logic (1) id=0
logic (2) id=7
конструктор id=71
logic (1) id=0
logic (2) id=7
конструктор id=17
Во второй строке вывода поле id получит значение по умолчанию, так как 
память для него выделена при создании объекта, а значение еще не проиници-
ализировано. В третьей строке выводится значение поля id, равное 7, так как 
после инициализации атрибута класса был вызван логический блок, получив-
ший его значение.
Перегрузка методов
Метод называется перегруженным, если существует несколько его версий с од-
ним и тем же именем, но с разным списком параметров. Перегрузка реализует 
«раннее связывание», то есть версия вызываемого метода определяется на этапе 
компиляции. Перегрузка может ограничиваться одним классом. Методы с одина-
ковыми именами, но с различными списком параметров и возвращаемыми значе-
ниями могут находиться в разных классах одной цепочки наследования и также 
будут перегруженными. Если списки параметров идентичны, то имеет место меха-
низм динамического полиморфизма — переопре деление метода.
Статические методы могут перегружаться нестатическими, и наоборот, без 
ограничений.
При вызове перегруженных методов следует избегать ситуаций, когда ком-
пилятор будет не в состоянии выбрать тот или иной метод.

КЛАССЫ И ОБЪЕКТЫ
67
/* # 9 # вызов перегруженных методов # NumberInfo.java */
package
 by.bsu.overload;
public
 class NumberInfo {
          public static void viewNum(Integer i) { // 1
                    System.out.printf("Integer=%d%n", i);
          }
          public static void viewNum(int i) { // 2
                    System.out.printf("int=%d%n", i);
          }
          public static void viewNum(Float f) { // 3 
                    System.out.printf("Float=%.4f%n", f);
          }
          public static void viewNum(Number n) { // 4
                    System.out.println("Number=" + n);
          }
          public static void main(String[ ] args) {
                    Number[ ] num = {new Integer(7), 71, 3.14f, 7.2 };
                    for (Number n : num) {
                                viewNum(n);
                    }
                    viewNum(new Integer(8));
                    viewNum(81);
                    viewNum(4.14f);
                    viewNum(8.2);
          }
}
Может показаться, что в результате компиляции и выполнения данного кода бу-
дут последовательно вызваны все четыре метода, однако в консоль будет выведено:
Number=7
Number=71
Number=3.14
Number=7.2
Integer=8
int=81
Float=4,1400
Number=8.2
То есть во всех случаях при передаче в метод элементов массива был вызван 
четвертый метод. Это произошло вследствие того, что выбор варианта пере-
груженного метода происходит на этапе компиляции и зависит от типа массива 
num. То, что на этапе выполнения в метод передается другой тип (для первых 
трех элементов массива), не имеет никакого значения, так как выбор уже был 
осуществлен заранее.
При непосредственной передаче объекта в метод выбор производится в за-
висимости от типа ссылки на этапе компиляции.

ОСНОВЫ JAVA
68
С одной стороны, этот механизм снижает гибкость, с другой — все возмож-
ные ошибки при обращении к перегруженным методам отслеживаются на эта-
пе компиляции, в отличие от переопределенных методов, когда их некоррект-
ный вызов приводит к возникновению исключений на этапе выполнения.
При перегрузке всегда надо придерживаться следующих правил:
•  не использовать сложных вариантов перегрузки;
•  не использовать перегрузку с одинаковым числом параметров;
•  заменять при возможности перегруженные методы на несколько разных ме-
тодов.
Параметризованные классы
К наиболее важным новшествам версии языка J2SE 5 можно отнести появ-
ление параметризации (generic) классов и методов, позволяющей использовать 
гибкую и в то же время достаточно строгую типизацию, что особенно важно 
при работе с коллекциями. Применение generic-классов для создания типизи-
рованных коллекций будет рассмотрено в главе «Коллекции». Параметризация 
позволяет создавать классы, интерфейсы и методы, в которых тип обрабатыва-
емых данных задается как параметр.
Ниже приведен пример generic-класса с двумя параметрами:
/* # 10 # объявление класса с двумя параметрами # Post.java */
package
 by.bsu.forum;
public
 class Post extends Number> {
          private T1 message;
          private T2 id;
          // методы
}
Здесь  T1,  Т2  —  фиктивные  объектные  типы,  которые  используются  при 
объявлении членов класса и обрабатываемых данных. В качестве типа T2 до-
пустимо использовать только подклассы класса Number. В качестве параме-
тров классов запрещено применять базовые типы.
Объект класса Post можно создать, например, следующим образом:
Post post1 = new Post();
Post post2 = new Post();
или
Post post2 = new Post<>(); // оператор diamond в Java 7
Параметризированные типы обеспечивают типобезопасность. Присваивание 
post1=post2 приводит к ошибке компиляции.
При создании объекта компилятор заменит все фиктивные типы на реаль-
ные и создаст соответствующий им объект, при этом все внешние признаки 

КЛАССЫ И ОБЪЕКТЫ
69
параметризации исчезнут, то есть проверка на принадлежность типу осущест-
вима только в виде:
post1 instanceof Post
тогда как, будет ошибочно
post1 instanceof Post
Ниже приведен пример параметризованного класса Message с конструкто-
рами и методами, также инициализация и исследование поведения объектов 
при задании различных параметров.
/* # 11 # создание и использование объектов параметризованного класса #  
Message.java # Runner.java */
package
 by.bsu.template;
public
 class Message {
          private T value;
          public Message() {
          }
          public Message (T value) {
                    this.value = value;
          }
          public T getValue() {
                    return value;
          }
          public void setValue(T value) {
                    this.value = value;
          }
          public String toString() {
                    if (value == null) { 
                              return null;
                    }
              return value.getClass().getName() + " :" + value;
                    }
}
package
 by.bsu.template;
public
 class Runner {
          public static void main(String[ ] args) {
          // параметризация типом Integer
                    Message ob1 = new Message();
                    ob1.setValue(1); // возможен только тип Integer для метода setValue
                    int v1 = ob1.getValue();
                    System.out.println(v1);
          // параметризация типом String
                    Message ob2 = new Message("Java");
                    String v2 = ob2.getValue();
                    System.out.println(v2);
          // ob1 = ob2; // ошибка компиляции – параметризация нековариантна

ОСНОВЫ JAVA
70
         // параметризация по умолчанию – Object
                    Message ob3 = new Message(); // warning – raw type
                    ob3 = ob1; // нет ошибки компиляции – нет параметризации
                    System.out.println(ob3.getValue()); 
                    ob3.setValue(new Byte((byte)1));
                    ob3.setValue("Java SE 7");
                    System.out.println(ob3); /* выводится тип объекта,
                                                  а не тип параметризации */
                    ob3.setValue(71);
                    System.out.println(ob3);
                    ob3.setValue(null);
          }
}
В результате выполнения этой программы будет выведено:
1
Java
null
java.lang.String: Java SE 7
java.lang.Integer: 71
В рассмотренном примере были созданы объекты типа Messageob1 на осно-
ве типа Integer и ob2 на основе типа String при помощи различных конструкто-
ров. При компиляции вся информация о generic-типах стирается и заменяется 
для членов класса и методов заданными типами или типом Object, если пара-
метр не задан, как для объекта ob3. Такая реализация необходима для обеспече-
ния совместимости с кодом, созданным в предыдущих версиях языка.
Объявление generic-типа в виде , несмотря на возможность использо-
вать любой тип в качестве параметра, ограничивает область применения разра-
батываемого класса. Переменные такого типа могут вызывать только методы 
класса Object. Доступ к другим методам ограничивает компилятор, предупре-
ждая возможные варианты возникновения ошибок.
Чтобы расширить возможности параметризованных членов класса, можно 
ввести ограничения на используемые типы при помощи следующего объявле-
ния класса:
public class 
ValueExt  extends Tип> {
       private T value;
       // поля, конструкторы, методы
}
Такая запись говорит о том, что в качестве типа Т разрешено применять 
только  классы,  являющиеся  наследниками  (подклассами)  реального  класса 
Tип, и, соответственно, появляется возможность вызова методов ограничиваю-
щих (bound) типов.
Часто возникает необходимость в метод параметризованного класса одного 
допустимого  типа  передать  объект  этого  же  класса,  но  параметризованного 

КЛАССЫ И ОБЪЕКТЫ
71
другим  типом.  В  этом  случае  при  определении  метода  следует  применить  
метасимвол  «?».  Метасимвол  также  может  использоваться  с  ограничением  
extends для передаваемого типа.
/* # 12 # использование метасимвола в параметризованном классе # Exam.java # 
Runner.java */
package
 by.bsu.exam;
public class
 Examextends Number> {
          private String name;
          private T mark; // параметр поля
          public Exam(T mark, String name) { // параметр конструктора
                    this.name = name;
                    this.mark = mark;
          }
          public T getMark() { // параметр метода
                    return mark;
          }
          private int roundMark() {
                    return Math.round(mark.floatValue()); // метод класса Number
          }
          public boolean equalsToMark(Exam ob) { // параметр метода
                    return roundMark() == ob.roundMark();
          }
}
package
 by.bsu.exam;
public
 class Runner {
          public static void main(String[ ] args) {
          Exam md1 = new Exam(71.41D,"Progr");// 71.5d
          Exam md2 = new Exam(71.45D, "Progr");// 71.5d
                    System.out.println(md1.equalsToMark(md2));
                    Exam mi = new Exam(71,"Progr");
                    // md1.equalsToMark(mi); // ошибка компиляции: несовместимые типы
          }
}
В результате будет выведено:
true
Метод с параметром Exam может принимать исключительно объекты 
с инициализацией того же типа, что и вызывающий метод объект. Чтобы метод 
equalsToMark() мог распространить свои возможности на экземпляры класса 
Exam, инициализированные любым допустимым типом, его следует перепи-
сать с использованием метасимвола «?» в виде:
public
 boolean equalsToMark(Exam ob) {
             return roundMark() == ob.roundMark();
}

ОСНОВЫ JAVA
72
Тогда  при  вызове  md1.equalsToMark(mi)  ошибки  компиляции  не  возникнет 
и метод выполнит свою расширенную функциональность по сравнению объектов 
класса Exam, инициализированных объектами различных допустимых типов. В про-
тивном случае было бы необходимо создавать новые перегруженные методы.
Для generic-типов существует целый ряд ограничений. Например, невозмож-
но выполнить явный вызов конструктора generic-типа:
class 
FailedOne {
   private T value = new T();
}
так как компилятор не знает, какой конструктор может быть вызван и какой 
объем памяти должен быть выделен при создании объекта.
По аналогичным причинам generic-поля не могут быть статическими, ста-
тические методы не могут иметь generic-параметры или обращаться к generic-
полям, например:
/* # 13 # неправильное объявление и использование полей параметризованного класса 
# FailedTwo.java */
class
 FailedTwo {
          static T1 value;
          T2 id;
          static T1 takeValue() { 
             return value; 
          }
          static void use() { 
              System.out.print(id); 
          }
}
Параметризованные методы
Параметризованный  (generic)  метод  определяет  базовый  набор  операций, 
которые будут применяться к разным типам данных, получаемых методом в ка-
честве параметра, и может быть записан, например, в виде:
extends Тип> returnType methodName(T arg) { }
> T[ ] methodName(int count, T arg) { }
Описание типа должно находиться перед возвращаемым типом. Запись пер-
вого вида означает, что в метод можно передавать объекты, типы которых явля-
ются подклассами класса, указанного после extends. Второй способ объявле-
ния метода никаких ограничений на передаваемый тип не ставит.
Generic-методы  могут  находиться  как  в  параметризованных  классах,  так 
и в обычных. Параметр метода может не иметь никакого отношения к параметру 


Достарыңызбен бөлісу:
1   2   3   4   5   6   7   8   9   ...   22




©engime.org 2024
әкімшілігінің қараңыз

    Басты бет