Кілька корисних класів Java дозволять спростити процес налагодження.

Щоб налагодити додаток, можна вчинити по-різному. По-перше, можна
скористатися штатним відладчиком з якого-небудь програмного пакету.
По-друге, можна самостійно обчислити помилку – логічно, по зовнішніх
ознаками. І по-третє, можна "підглянути", що ж там таке коїться всередині
вашої програми. Для цього, останнього, способу і існують всілякі
утиліти і бібліотеки. Так, в бібліотеці MFC з компілятора Visual C + + є
спеціальні макроси, які під час запуску прикладу пересилають необхідну
інформацію у вікно спеціальної утиліти-монітора (чи не правда, схоже на
підглядання в замкову щілину?). Подібний моніторинг даних старий, як саме
програмування, і дуже простий: потрібно виводити на екран дисплея значення
змінних, розташованих в ділянках програми, що викликають сумніви.
Переглядаючи отримані дані, можна послідовно наблизитися до помилкового
ділянці.


Що стосується Java-додатків, то і тут моніторинг виявляється можливим,
якщо скористатися висновком даних в стандартні потоки виводу і помилок –
System.out і System.err. Часто можна виявити у вихідних текстах таку
рядок:

 System.out.println ("Входимо в конструктор класу");

В результаті виконання даної команди в консольному вікні програми з'явиться
текстовий рядок "Входимо в конструктор класу" – знак того, що виконання
програми відбувається в заданому місці.


Чи не можна спростити моніторинг Java-додатків? Звичайно ж можна. Трохи
фантазії і розуміння, як працюють стандартні потоки out і err, нам
допоможуть.


У вихідних текстах у файлі System.java є описи стандартних потоків:


public final static InputStream in = nullInputStream();

public final static PrintStream out = nullPrintStream();

public final static PrintStream err = nullPrintStream();

З них випливає, що стандартний потік вводу in є посиланням типу
InputStream, а потоки виводу і помилок out і err – посиланнями класу PrintStream.
Віртуальна машина Java ініціалізує ці посилання за допомогою "рідного" коду,
який, на жаль, нам непідконтрольний.


Отже, наш план простий: створивши альтернативні стандартні потоки, упакуємо їх
зручніше і придумаємо комфортабельний інтерфейс доступу до них, що, власне
кажучи, і є головною метою цієї всієї роботи. Почати, зрозуміло, потрібно з
створення класу, що інкапсулює стандартні потоки введення, виведення і помилок.
Назвемо його StdStreams і помістимо його вихідний текст у файл StdStreams.java (див.
Лістинг 1).


Лістинг 1. Створення класу
Stdstreams

 package Mitrich.utils;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
/**
* Даний клас інкапсулює стандартні потоки.
* Він закритий для доступу ззовні і змін.
*/
final class StdStreams
{
private static InputStream in; / / Зарезервовано
private static PrintStream out;
private static PrintStream err;
static {
StdStreams.setIn ( System.in );
StdStreams.setOut( System.out );
StdStreams.setErr( System.err );
}

/**
* Повертає посилання на потік err.
* @return java.io.PrintStream
*/
PrintStream getErr()
{
return err;
}
/**
* Повертає посилання на потік in.
* @return InputStream
*/
InputStream getIn()
{
return in;
}
/**
* Повертає посилання на потік out.
* @return java.io.PrintStream
*/
PrintStream getOut()
{
return out;
}
/**
* Встановлює посилання на потік in
* @param stream java.io.InputStream
*/
static void setIn(InputStream stream)
{
in = stream;
}
/**
* Встановлює посилання на потік err
* @param stream java.io.OutputStream
*/
static void setErr(OutputStream stream)
{
err = new PrintStream(stream);
}
/**
* Встановлює посилання на потік out
* @param stream java.io.OutputStream
*/
static void setOut(OutputStream stream)
{
out = new PrintStream(stream);
}
}


Оскільки створений нами клас не потребує успадкування, він реалізований як
final. Всередині нього розташовуються три поля, що зберігають посилання на використовувані нами
потоки (відзначені модифікаторами доступу private і static). Чому вони зроблені
закритими для доступу (privat), цілком зрозуміло: нема чого змінювати їх безпосередньо. А
ось додавання static вимагає пояснення. Справа в тому, що в Java статичні поля
инициализируются відразу ж після створення екземпляра класу. Таким чином,
посилання на потоки гарантовано инициализируются перед використанням і, що
більш важливо, ці поля стають унікальними для всіх екземплярів класу
StdStream. З цього випливає, що зміна посилань на потоки – глобально, і все
класи, які звертаються за сервісом до StdStream, звертаються до одного й того ж
полю. Зверніть увагу, що посилання на стандартний потік in вважається
зарезервованої (нам вона не потрібна, але чим чорт не жартує, а раптом згодом
стане в нагоді …). Оскільки всі посилання виконані як static, то їх ініціалізацію
ми виробляємо в статичному блоці.


Щоб мати можливість читати і змінювати посилання на потоки (а це нам потрібно),
у нас є трійка методів для читання посилань і трійка методів для установки
нових посилань. Всі читають методи починаються з префікса get, а встановлюють
– З приставки set. Уважний читач помітить, що в якості типу параметрів
методів setOut () і setErr () виступає клас OutputStream, хоча, як раніше
зазначалося, потоки System.out і System.err мають тип PrintStream. Це
пояснюється дуже просто: абстрактний клас OutputStream є предком всіх
потокових класів, і тому в якості параметра можна підставити посилання на
будь-який клас-спадкоємець від OutputStream, що зручно. Захотіли вивести дані в
файл, а не на консоль – будь ласка!


Користувальницький інтерфейс для моніторингових класів повинен бути зручним.
Довелося трохи повозитися і придумати такі класи, які було б легко
запам'ятати і ще легше використовувати, наприклад клас SetIn (див. Лістинг 2),
записаний у файлі SetIn.java нижче.


Лістинг 2. Створення класу
SetIn

 package Mitrich.utils;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Даний клас є для перенаправлення
* Потоку in
* Зарезервований для особистих цілей користувача
*/
public final class SetIn
{
/**
* Запобігає створення екземпляра класу
*/
private SetIn() { }
/**
* Переспрямувати потік in на інший потік
*/
public static void to(InputStream stream)
{
StdStreams.setIn(stream);
}
/**
* Встановлює потік in на System.in
*/
public static void toDefault()
{
StdStreams.setIn( System.in );
}
}

Він включає в себе опис самого класу і кількох його методів. На порожній
закритий конструктор не варто звертати уваги – він служить лише для того, щоб
який-небудь "умілець" не зміг створити екземпляр класу SetIn оператором new
(Спробувавши, він отримає від компілятора відмову). Щоб екземпляр класу
створювався автоматично, його методи повинні бути статичними. Тоді при
зверненні до одного з них віртуальна машина Java сама завантажить об'єкт класу
SetIn і виконає викликаний метод. Друга мета, яка переслідувалася при
описі методів як static, – змусити користувача завжди вживати імена
методів разом з ім'ям класу, а, якщо ви пам'ятаєте, посилання на статичні поля і
методи класу можлива лише при використанні повного імені, що включає ім'я
класу, якому поля і методи належать. Так що, якщо ми хочемо
перевизначити потік in в інше місце, то директива буде виглядати приблизно
так:

 FileInputStream s = new FileInputStream ("SomeFile.dat");

SetIn.to(s);

Як бачите, перепризначення потоку введення читається як природна фраза на
англійською мовою, чого ми, власне, і хотіли досягти. Сам метод реалізований
елементарно: він звертається до класу StdStreams, про який ми вже говорили, і
встановлює потік введення викликом методу StdStreams.setIn (). Тепер ви напевно
зрозуміли, навіщо ми оголосили клас StdStreams з областю видимості package – він
видно лише для класів, які розташовані всередині того ж самого пакету.


Але повернемося до класу SetIn. Другий метод скидає останню виконану
перепризначення і встановлює потік введення в початкове положення на System.in.
Виклик цього методу теж схожий на природний англійську мову:

SetIn.toDefault();

Аналогічним чином реалізовані і класи перенаправлення стандартного потоку
висновку (див. Лістинг 3) і стандартного потоку помилок (см.
Лістинг 4
)


Лістинг 3. Створення класу
перенаправлення стандартного потоку виводу

 package Mitrich.utils;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Даний клас є для перенаправлення
* Потоку out
*/
public final class SetOut
{
/**
* Запобігає створення екземпляра класу
*/
private SetOut() { }
/**
* Переспрямувати потік out на інший потік
*/
public static void to(OutputStream stream)
{
StdStreams.setOut(stream);
}
/**
* Встановлює потік out на System.out
*/
public static void toDefault()
{
StdStreams.setOut( System.out );
}
}

Лістинг 4. Створення класу
стандартного потоку помилок

 package Mitrich.utils;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Даний клас є для перенаправлення
* Потоку err
*/
public final class SetErr
{
/**
* Запобігає створення екземпляра класу
*/
private SetErr() { }
/**
* Переспрямувати потік err на інший потік
*/
public static void to(OutputStream stream)
{
StdStreams.setErr(stream);
}
/**
* Встановлює потік err на System.err
*/
public static void toDefault()
{
StdStreams.setErr( System.err );
}
}

Ще один важливий для нас клас – Debug. Він виконує роль рубильника, яким
ми або включаємо, або вимикаємо моніторинг. Як це робиться, ви зрозумієте з лістингу 5. Конструкція

Debug.on();

включає можливість моніторингу, а

Debug.off

її вимикає. Перевірити поточний стан можна, вставивши вираз, подібне

if(Debug.isOn()) …

Так само як і попередні класи, Debug не допускає створення своїх екземплярів
оператором new, і всі його методи описані як static, щоб їх не можна було
вживати у відриві від імені класу (див. Лістинг 5).


Лістинг 5. Створення класу
Debug

package Mitrich.utils; / ** * Даний клас відіграє роль прапора
дозволу * або заборони моніторингу * / public final class Debug {/ ** * За
замовчуванням трасування виключена * / private static boolean onFlag = false; / ** *
Не дає створити екземпляр класу * / private Debug () {} / ** * Повертає
стан прапора моніторингу * @ return boolean * / public static boolean isOn () {
return Debug.onFlag;} / ** * Скидає прапор стану моніторингу * / public
static void off () {Debug.onFlag = false;} / ** * Встановлює прапор стану
моніторингу * / public static void on () {Debug.onFlag = true;}}

І ось ми впритул підійшли до основного класу нашої маленької бібліотеки,
який, власне, і виконує моніторинг. Два його методу – err () і out () –
пересилають дані відповідно в потік помилок і потік виводу. Але, як
показує початковий текст (див. Лістинг 6), ці оператори перевантажені, завдяки
чому вони можуть виводити в потік дані будь-якого типу.


Лістинг 6. Створення основного класу,
виконує моніторинг

 package Mitrich.utils;
import java.io.PrintStream;
/**
* Даний клас зроблений для зручності моніторингу даних.
*/
public final class TraceTo
{
static StdStreams streams = new StdStreams();
/**
* Запобігає створення екземплярів класу
*/
private TraceTo() { }
/**
* Виводить дані параметра value в потік помилок
* @param value char[]
*/
public static void err(char[] value)
{
if(Debug.isOn())
streams.getErr().println(value);
}
/**
* Виводить дані параметра value в потік помилок
* @param value byte
*/
public static void err(byte value)
{
if(Debug.isOn())
streams.getErr().println(value);
}
/**
* Виводить дані параметра value в потік помилок
* @param value char
*/
public static void err(char value)
{
if(Debug.isOn())
streams.getErr().println(value);
}
/**
* Виводить дані параметра value в потік помилок
* @param value double
*/
public static void err(double value)
{
if(Debug.isOn())
streams.getErr().println(value);
}
/**
* Виводить дані параметра value в потік помилок
* @param value float
*/
public static void err(float value)
{
if(Debug.isOn())
streams.getErr().println(value);
}
/**
* Виводить дані параметра value в потік помилок
* @param value int
*/
public static void err(int value)
{
if(Debug.isOn())
streams.getErr().println(value);
}
/**
* Виводить дані параметра value в потік помилок
* @param value long
*/
public static void err(long value)
{
if(Debug.isOn())
streams.getErr().println(value);
}
/**
* Виводить дані параметра value в потік помилок
* @param msg java.lang.Object
*/
public static void err(Object value)
{
if(Debug.isOn())
streams.getErr().println(value.toString());
}
/**
* Виводить дані параметра value в потік помилок
* @param msg java.lang.String
*/
public static void err(String value)
{
if(Debug.isOn())
streams.getErr().print(value);
}
/**
* Виводить дані параметра value в потік помилок
* @param value short
*/
public static void err(short value)
{
if(Debug.isOn())
streams.getErr().println(value);
}
/**
* Виводить дані параметра value в потік помилок
* @param value boolean
*/
public static void err(boolean value)
{
if(Debug.isOn())
streams.getErr().println(value);
}
/**
* Пересилає дані параметра value в потік виводу
* @param value char[]
*/
public static void out(char[] value)
{
if(Debug.isOn())
streams.getOut().println(value);
}
/**
* Пересилає дані параметра value в потік виводу
* @param value byte
*/
public static void out(byte value)
{
if(Debug.isOn())
streams.getOut().println(value);
}
/**
* Пересилає дані параметра value в потік виводу
* @param value char
*/
public static void out(char value)
{
if(Debug.isOn())
streams.getOut().println(value);
}
/**
* Пересилає дані параметра value в потік виводу
* @param value double
*/
public static void out(double value)
{
if(Debug.isOn())
streams.getOut().println(value);
}
/**
* Пересилає дані параметра value в потік виводу
* @param value float
*/
public static void out(float value)
{
if(Debug.isOn())
streams.getOut().println(value);
}
/**
* Пересилає дані параметра value в потік виводу
* @param value int
*/
public static void out(int value)
{
if(Debug.isOn())
streams.getOut().println(value);
}
/**
* Пересилає дані параметра value в потік виводу
* @param value long
*/
public static void out(long value)
{
if(Debug.isOn())
streams.getOut().println(value);
}
/**
* Пересилає дані параметра value в потік виводу
* @param msg java.lang.Object
*/
public static void out(Object value)
{
if(Debug.isOn())
streams.getOut().println(value.toString());
}
/**
* Пересилає дані параметра value в потік виводу
* @param msg java.lang.String
*/
public static void out (String value)
{
if(Debug.isOn())
streams.getOut().print(value);
}
/**
* Пересилає дані параметра value в потік виводу
* @param value short
*/
public static void out(short value)
{
if(Debug.isOn())
streams.getOut().println(value);
}
/**
* Пересилає дані параметра value в потік виводу
* @param value boolean
*/
public static void out(boolean value)
{
if(Debug.isOn())
streams.getOut().println(value);
}
}

Методи влаштовані елементарно. Все, що вони роблять, так це перевіряють, включена
Чи налагодження (ми вже акцентували увагу на цьому), і якщо так, то виводять
дані методом println () класу PrintStream. Виходить, що методи err () і
out () – просто оболонки навколо вже наявних перевантажених методів. Виняток
становлять методи, що виводять дані типів String і Object. Для даних типу
String застосовується висновок методом print (), який не переводить курсор на
наступний рядок. Це зручно, якщо необхідно склеїти кілька рядків в один або
помістити висновок даних змінної безпосередньо за текстовим коментарем.
Якщо ж хочете зробити переклад каретки, просто додайте в кінець рядка символ
“". Для даних типу Object ситуація інша. Щоб зробити інформацію про виведених
даних корисного, посилання на клас-аргумент слід привести до типу String. Для
більшості класів це означає послідовну друк значень всіх
внутрішніх полів у вигляді рядка.


Використання класу Trace нічим не відрізняється від використання вже описаних
класів:


TraceTo.err ("Сталася помилка з кодом");
TraceTo.err(errorCode);

Як бачите, все просто і природно. А щоб перевірити роботу нашої
бібліотеки, спробуйте написати своє власне програма, яка б виводила
дані в стандартні або файлові потоки, включало й вимикали моніторинг і т.
д. Це чудова практика, яка допоможе вам освоїти техніку використання
моніторингових класів

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*