НМЦ

ОБ’ЄКТНО ОРІЄНТОВАНЕ ПРОГРАМУВАННЯ

Електронний посібник

 

ВФПО

Модуль 7

 

7. Консоль. Файлова система. Графічний інтерфейс користувача

7.1. Основи вводу-виводу. Робота с консоллю та файловою системою

7.2. Робота з базами даних. JDBC. Entity Classes

7.3. Створення графічного інтерфейсу користувача

7.4. Обробка подій від інтерфейсних елементів

7.5. Тонке налагодження інтерфейсу користувача

 

 

Відеозапис лекції:

 

 

Частина 1: Робота с консоллю та файловою системою

https://drive.google.com/file/d/1yCBwGhoNQ6k9bCeo73rSaB5HeFDwklWQ/view

 

Потік вводу даних з консолі можна організувати одним з двох способів:

*    з допомогою потоку вводу байтів;

*    з допомогою потоку вводу символів.

У цьому випадку дані, що вводяться з консолі, зчитуються з стандартного потоку вводу, якому відповідає змінна System.in з пакету java.lang. Іншими словами, змінна System.in посилається на стандартний потік вводу, яким, за замовчуванням, вважається клавіатура.

У мові Java для реалізації операцій вводу з консолі використовується клас InputStreamReader. Цей клас є підкласом абстрактного базового класу Reader і призначений для конвертування байт в символи.

Щоб отримати об’єкт типу InputStreamReader, потрібно створити екземпляр цього типу за наступною загальною формою:

InputStreamReader objStream = new InputStreamReader(input_stream);

тут

*    objStream – екземпляр, що зв’язаний з потоком вводу символів;

*    inputStream – потік вводу символів.

У випадку консолі, цим потоком виступає System.in.

Отже, для потоку System.in, щоб створити екземпляр (об’єкт) типу InputStreamReader, потрібно викликати наступний конструктор

*    InputStreamReader objStream = new InputStreamReader(System.in);

В пакеті java.lang реалізовано дві змінні, що зв’язані з потоком запису даних на консоль:

*    System.out. Ця змінна відповідає потоку виведення на консоль;

*    System.err. Ця змінна зв’язана з потоком виведення помилок.

Для роботи з виведенням на консоль розроблено спеціальний клас PrintStream, який є похідним від класу OutputStream. У цьому класі основними є методи print() та println().

Щоб створити потік виводу, зв’язаний з консоллю, потрібно оголосити екземпляр класу PrintStream.

При створенні екземпляру потрібно зв’язати його з відповідним потоком як показано в наступному прикладі:

public static void main(String[] args) throws IOException

{

  // Створення потоку виводу. Клас PrintStream.

  // 1. Зв'язати посилання outStream з потоком виводу System.out

  PrintStream outStream = new PrintStream(System.out);

 

  // 2. Зв'язати посилання errStream з потоком виводу System.err

  PrintStream errStream = new PrintStream(System.err);

 

  // 3. Використати обидва посилання

  outStream.println("Hello world!"); // Метод println()

  errStream.print("Some error..."); // Метод print()

}

Результат виконання програми:

Hello world!

Some error...

Як відомо, з консоллю зв’язана стандартна змінна System.in. Щоб прочитати символи з консолі без буферизації, можна використати можливості класу InputStreamReader. Клас InputStreamReader є підкласом абстрактного класу Reader. Клас InputStreamReader призначений для конвертування байт у символи.

Основним методом класу InputStreamReader є метод read(), який має дві перевантажені реалізації. Перша реалізація має наступну загальну форму

*      int read();

У цьому випадку метод повертає код введеного символу з консолі.

Загальна форма другої реалізації методу наступна

*      int read(char[] ac, int index, int count);

Тут метод заповнює масив ac типу char[] введеними значеннями символів з консолі. Параметр count задає кількість символів, які потрібно вставити у масив ac. Для масиву ac попередьно має бути виділена пам’ять. Другий варіант методу повертає кількість символів, що можуть бути прочитані.

Щоб реалізувати буферизацію під час читання символів з консолі, потрібно екземпляр класу InputStreamReader помістити в оболонку об’єкту класу BufferedReader. У цьому випадку загальна форма конструктора класу BufferedReader наступна

*      BufferedReader(Reader потік_читання_введених_даних)

Тут потік_читання_введених_данихдеякий потік даних (файл, консоль тощо). У випадку консолі тут задається екземпляр класу InputStreamReader (дивіться попередній пункт).

Такий підхід реалізує патерн Декоратор (Decorator). У патерні Декоратор екземпляр одного класу служить оболонкою для екземпляру іншого класу. Таким чином відбувається нашаровування об’єктів.

Нехай задано символьний (текстовий) файл, який потрібно вивести на консоль. Читання рядків з файлу можна реалізувати з допомогою методу read() класу FileReader. Рядок з файлу читається у наперед визначену ділянку пам’яті – буфер.

Щоб визначити кінець файлу, потрібно використати метод ready() класу FileReader. Якщо кінець файлу, то метод повертає false.

import java.io.*;

...

public static void main(String[] args) throws IOException

{

  // Зчитати рядки з файлу та вивести їх на консоль

  FileReader fr = new FileReader("strings.txt");

  char buffer[] = new char[1000]; // буфер для читання рядків

 

  // Цикл читання рядків з файлу

  do {

    fr.read(buffer); // Зчитати рядок в буфер buffer

    System.out.println(buffer); // Вивести рядок на консоль

  } while (fr.ready()); // перевірка, чи не кінець файлу

 fr.close();

}

Нехай задано деякий рядок типу String. Потрібно записати цей рядок у файл шляхом використання перенаправлення потоку виводу у файл.

 

Щоб реалізувати запис рядка у файл, потрібно виконати наступні дії:

Øстворити екземпляр класу FileOutputStream з іменем файлу, в який буде здійснюватись запис рядка;

Øстворити екземпляр класу PrintStream, в конструктор якого помістити створений екземпляр класу FileOutputStream;

Ø   використати один з методів print() або println() для запису рядка у файл.

 

Фрагмент програмного коду, що здійснює перенаправлення потоку з рядка в файл, наведено нижче

import java.io.*;

...

public static void main(String[] args) throws IOException

{

  // Направити вивід з рядка String у файл з іменем "output.txt"

  // 1. Вхідні дані

  String s = "www.bestprog.net"; // рядок, що записується у файл

  String filename = "output.txt"; // ім'я файлу, в який записується рядок

  // 2. Створити екземпляр класу FileOutputStream

  FileOutputStream fs = new FileOutputStream(filename);

  // 3. Створити екземпляр класу PrintStream, в який помістити екземпляр FileOutputStream

  PrintStream ps = new PrintStream(fs);

  // 4. Записати рядок "www.bestprog.net" у файл "output.txt"

  ps.println(s);

  // 5. Закрити файл

  ps.close();

}

За цим прикладом можна реалізувати функцію, яка записує масив рядків у файл.


 

 

Відеозапис лекції:

 

 

Частина 1: Робота з базами даних JDBC

https://drive.google.com/file/d/1rVCYoOKWJ7k3yYBVLnzq9vUGekrNaBZh/view

 

Java DataBase Connectivity (з'єднання з базами даних на Java), скорочено JDBC) – прикладний програмний інтерфейс Java, який визначає методи, з допомогою яких програмне забезпечення на Java здійснює доступ до бази даних. JDBC – це платформонезалежний промисловий стандарт взаємодії Java-застосунків з різноманітними СУБД, реалізований у вигляді пакета java.sql, що входить до складу Java SE.

 

В основі JDBC лежить концепція так званих драйверів, що дозволяють отримувати з'єднання з базою даних за спеціально описаним URL. Драйвери можуть завантажуватись динамічно (під час роботи програми). Завантажившись, драйвер сам реєструє себе й викликається автоматично, коли програма вимагає URL, що містить протокол, за який драйвер "відповідає".

JDBC API містить два основні типи інтерфейсів: перший – для розробників застосунків і другий (нижчого рівня) – для розробників драйверів.

З'єднання з базою даних описується класом, що реалізує інтерфейс java.sql.Connection. Маючи з'єднання з базою даних, можна створювати об'єкти типу Statement, використовувані для здійснення запитів до бази даних мовою SQL.

Існують такі види типів Statement, що відрізняються своїм призначенням:

*      java.sql.Statement – загального призначення;

*      java.sql.PreparedStatement – служить для здійснення запитів, котрі містять підставні параметри (позначаються символом '?' у тілі запиту);

*      java.sql.CallableStatement – призначений для виклику збережених процедур.

Клас java.sql.ResultSet дозволяє легко обробляти результати запитів.

 

Перевагами JDBC вважаються:

Ø простота розробки: розробник може не знати специфіки бази даних, з якою працює;

Ø код не змінюється, якщо компанія переходить на іншу базу даних;

Ø не треба встановлювати громіздку клієнтську програму;

Ø до будь-якої бази можна під'єднатись через легко зрозумілий URL.

 

Цей приклад використовує вільний драйвер JDBC для MySQL, який легко встановлюється в більшості дистрибутивів Linux через стандартні репозиторії:

package javaapplication1;

import java.sql.*;

public class Main {

    public static void main(String[] args) throws SQLException

    {

        /**

        * цей рядок вказує драйвер DB.

        * розкоментуйте, якщо прописуєте драйвер вручну

        */

        //Class.forName("com.mysql.jdbc.Driver").newInstance();

        Connection conn = DriverManager.getConnection(

            "jdbc:mysql://localhost:3306/db_name",

            "user", "password");    

        if (conn==null)

        {

            System.out.println("Немає з'єднання з БД!");

            System.exit(0);

        }       

        Statement stmt = conn.createStatement();

        ResultSet rs = stmt.executeQuery("SELECT * FROM users");

        while(rs.next())

        {

            System.out.println(rs.getRow() + ". " + rs.getString("firstname") + "\t" + rs.getString("lastname"));

        }      

        // if(rs!=null)rs.close();       

        /**

         * stmt.close();

         * При закритті Statement автоматично закриваються

         * всі пов'язані з ним відкриті об'єкти ResultSet

         */       

        // if(stmt!=null)stmt.close();

        stmt.close();

    }

}

 

 

Відеозапис лекції:

 

 

Частина 1: Створення графічного інтерфейсу користувача

https://drive.google.com/file/d/1Id9ykSNHFcC7n2TGNtpVaGOcODFq8LRu/view

 

Для реалізації графічного інтерфейсу (GUI) в Java існують два основні пакети класів:

*    Abstract Window Toolkit (AWT)

*    Swing

Перевагами першого є простота використання, інтерфейс подібний до інтерфейсу операційної системи та дещо краща швидкодія, оскільки базується на засобах ОС, щоправда має обмежений набір графічних елементів. Другий пакет Swing реалізує власний Java-інтерфейс. Цей пакет створювався на основі AWT, і має набагато більше можливостей та більшу кількість графічних елементів. Swing-компоненти ще називають полегшеними (англ. lightweight), оскільки вони написані повністю на Java і, через це платформонезалежні.

Принципи роботи із обома пакетами схожі, тож опанувавши роботу з одним, робота з іншим не матиме труднощів. Swing містить великий набір компонентів для розробки графічного інтерфейсу користувача (GUI). Оскільки Swing – це повніший інструментарій ніж AWT, тому основну увагу ми приділимо саме йому. Робота з AWT компонентами майже аналогічна, щоправда їх дещо менше. Для розрізнення компонентів інструментаріїв, компоненти Swing містять літеру "J" спереду назви (JButton, JPanel тощо). Назви AWT-компонентів не мають цієї літери спереду.

Swing та AWT є частиною JFC (Java Foundation Classes), що в свою чергу становить левову частку стандартної платформи Java. Класи Swing розміщуються в пакеті javax.swing та його підпакетах. Суфікс прийнято ставити для нестандартних розширень, проте конкретно ці пакети є стандартними, починаючи з JDK 1.2.

Вікно верхнього рівня (таке що не містить в середині іншого вікна) в мові Java називається фреймом (frame – каркас). У бібліотеці AWT для цього вікна призначений клас Frame, а в бібліотеці SwingJFrame.

Наступний код демонструє, як можна створити фрейм розміром 300×200 пікселів:

import javax.swing.JFrame;

public class SimpleFrame

{

  public static void main(String[] args)

  {

    JFrame frame = new JFrame();     // створити фрейм

    frame.setSize(300, 200);         // задаємо ширину і висоту фрейму

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // дії при закритті

   //фрейму

    frame.setVisible(true); // показати фрейм на екрані (зробити видимим)

  }

}

Проте розміщувати увесь код в main() поганий стиль, оскільки, наприклад, коли вам доведеться додавати нові фрейми та інші елементи, код може стати доволі заплутаним. Крім того, метод main статичний і потрібно враховувати деякі особливості роботи у статичному контексті. Тому в main() часто намагаються залишити лише найнеобхідніше. І взагалі, якщо якийсь графічний елемент потребує значного коду, то бажано роботу з ним розмістити окремо. В нашому випадку всю роботу з фреймом можна доручити класу, який розширюватиме клас JFrame.

Наступний переписаний код робить те ж саме, що і попередній. На перший погляд може здатися, що такий спосіб є доволі незручним, насправді ж у великих проєктах це виправдано.

import javax.swing.*;

public class SimpleFrame

{

   public static void main(String[] args)

   {

      MyFrame frame = new MyFrame();

      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

      frame.setVisible(true);

   }

}

class MyFrame extends JFrame

{

   public MyFrame()

   {

      setSize(FRAME_WIDTH, FRAME_HEIGHT);

   }

   public static final int FRAME_WIDTH = 350;

   public static final int FRAME_HEIGHT = 200;

}

Програма складається з двох класів, хоча метод MyFrame() можна б було розмістити і в класі SimpleFrame. Проте, на думку багатьох програмістів, краще відділяти клас, який запускає програму на виконання, від класу, в якому описується інтерфейс користувача. Одною з причин такого є краща читабельність коду програми.

Фрейм це контейнер, в який поміщають всі інші компоненти (меню, кнопки, прапорці та інші елементи графічного елемента). Сам клас JFrame, який реалізує фрейм складається з чотирьох областей (pane), що накладаються одна на одну: коренева область (root pane), область шару (layered pane), прозора область (glass pane) та область вмісту (content pane). Перші три застосовують для створення та обслуговування меню. Для роботи з графічними елементами застосовується область вмісту, в яку і додають компоненти. Додати компонент можна наступним чином:

Container contentPane = frame.getContentPane();

Component c=....;

contentPane.add (c);

// з версії JDK 1.5 реалізований метод JFrame.add()

// що переадресовує виклик методу області вмісту contentPane.add()

// і тепер можна просто писати: add (c)

Щоправда напряму у фреймі додавати компоненти не прийнято. Для цього використовується спеціальний компонент-контейнер – панель (panel), що додається до фрейму. Після цього в панель можна додавати різні графічні компоненти.

Для початку спробуємо намалювати прямокутник. Щоб додати відповідну панель, в якій буде здійснюватись малювання, необхідно:

визначити клас, що розширює клас JPanel;

замістити (перевизначити) в цьому класі метод paintComponent().

class MyPanel extends JPanel{

   public void paintComponent(Graphics g){

       .....// код, що здійснює малювання

   }

}


 

7.4. Обробка подій від інтерфейсних елементів

 

Відеозапис лекції:

 

 

Частина 1: Обробка подій від інтерфейсних елементів

https://drive.google.com/file/d/1sGitRZjTSHdvjCIHApkyY7Xpcg6GVycV/view

 

Натиснення кнопки, закриття вікна, клацання мишкою – все це є прикладами подій, які отримує операційна система і передає відповідній програмі на обробку. Програміст повинен передбачити як потрібно обробляти дані події. Розглянемо як обробка подій реалізовується в Java.

В Java запропонована, так звана, модель делегування подій (event delegation model). Джерело події (event source) породжує подію, після чого вона передається в обробник подій (event listener – дослівно слухач події). При цьому будь-який об’єкт може бути призначеним як обробник деякої події. Така модель доволі гнучка, оскільки кожен програміст може вибрати зручний для нього спосіб обробки події (де саме її обробляти), проте інколи текст програми може бути дещо заплутаним для тих хто не звик до такої моделі.

Інформація про подію інкапсулюється в об’єкті події (event object). Всі події описуються підкласами java.util.EventObject. Як приклади, можна навести підкласи ActionEvent та WindowEvent. Перші об’єкти породжують кнопки, а другі вікна.

Джерела подій містять методи, які дозволяють зв’язати його з обробниками подій. Коли виникає подія, джерело повідомляє про неї усіх зареєстрованих обробників. Обробники подій на основі інформації у об’єкті події визначає як реагувати на ту чи іншу подію.

 

При розробці графічного інтерфейсу розробнику необхідного здійснити наступне:

Ø Створити клас, який оброблятиме подію чи ряд подій і, який реалізовуватиме відповідний інтерфейс

Ø Створити джерела подій (вікна, кнопки, смуги прокрутки тощо)

Ø Зв’язати джерела подій з обробниками подій

 

Сказане демонструє наступний фрагмент програми:

ActionListener listener = . . .;    // створити обробник подій

JButton button = new JButton("Ok"); // створюємо кнопку

button.addActionListener(listener);  // зв'язуємо кнопку з обробником подій

Клас, який реалізовуватиме інтерфейс ActionListener повинен мати метод actionPerformed() який в якості параметру отримуватиме об’єкт ActionEvent.

class MyListener implements ActionListener

{

. . .

public void actionPerformed(ActionEvent event)

{

// тут відбувається реакція на натиснення кнопки

. . .

}

}

Реалізувати такий механізм можна кількома способами. Часто також замість створення окремого класу обробника використовують внутрішні класи і навіть внутрішні анонімні класи. Також в якості обробника події може виступати і сам клас, в якому описується графічний інтерфейс користувача.

Класи-адаптери являють собою пусту реалізацію інтерфейсів-слухачів, що мають більше одного методу. Їх імена складаються із імені події і слова Adapter. Наприклад, для дії з мишею є два класи-адаптери. Виглядають вони дуже просто:

public abstract class MouseAdapter implements MouseListener{

public void mouseClicked(MouseEvent e){}

public void mousePressed(MouseEvent e){}

public void mouseReleased(MouseEvent e){}

public void mouseEntered(MouseEvent e){}

public void mouseExited(MouseEvent e){}

}

 

public abstract class MouseMotionAdapter implements MouseMotionListener{

public void mouseDragged(MouseEvent e){}

public void mouseMoved(MouseEvent e){}

}

Замість того щоб реалізувати інтерфейс, можна розширять ці класи. Крім вже згадуваних трьох класів, існують ще класи ComponentAdapter, ContainerAdapter, FocusAdapter і KeyAdapter.

 

 

Відеозапис лекції:

 

 

Частина 1: JavaFx Tutorial, Introduction And First GUI Window

https://youtu.be/XToB42ngkYM

 

JavaFX – платформа та набір інструментів для створення насичених інтернет-застосунків (англ. Rich Internet Applications, RIA) з можливістю підвантаження медіа та змісту.

 

Вперше продемонстровано Sun Microsystems на Міжнародній конференції Java-розробників JavaOne у травні 2007. JavaFX містить у собі набір утиліт, за допомогою яких веб-розробники та дизайнери можуть швидко створювати та надавати розвинуті інтернет-застосунки для десктопів, мобільних пристроїв, телебачення та інших платформ.

JavaFX складається з JavaFX Script і JavaFX Mobile. Починаючи з випуску JavaFX 2.0 забезпечено можливість створення JavaFX-застосунків, написаних цілком мовою Java. Для розробки застосунків доступний багатий графічний і мультимедійний API, що спрощує створення візуальних програм.

 

 

Цікавою особливістю є те, що вигляд та поведінку програм, написаних на JavaFX, можно налаштовувати за допомогою каскадних таблиць стилів (CSS – Cascading Style Sheets). Цей підхід, який історично був першим застосований для веб-сайтів, дозволяє відокремити зовнішній вигляд програм (інтерфейс користувача) від реалізації, що дозволяє програмістам концентруватись на кодуванні. Турбота про графічний інтерфейс тепер лежить на плечах графічних дизайнерів, які налаштовують зовнішній вигляд за допомогою скриптової мови FXML та технології CSS, а програмісти зосереджені на розробці бізнес-логіки додатка. Нижче наведено вигляд додатку із різними налаштуваннями CSS.

Існує два шляхи створення інтерфейсу користувача з JavaFX: використовувати файл розмітки FXML чи програмувати все на Java. Для більшості випадків ми будемо використовувати XML (.fxml). Цей спосіб більше підходить для збереження роздільності контролера та представлення один від іншого. В подальшому, ми зможемо використовувати графічний Scene Builder для візуального редагування нашого XML. А це означає, що нам не потрібно працювати з XML напряму.

Також потрібно створити основний Java клас, який запускає наш додаток з RootLayout.fxml.

Створений клас MainApp.java наслідує клас Application та вміщує два методи. Це базова структура, що потрібна для для запуску JavaFX додатку. Для нас важливий метод start(Stage primaryStage). Він автоматично викликається коли додаток запускається з методу main.

Як бачите, метод start(...) приймає екземпляр класу Stage в ролі параметра. На рисунку знизу представлена структура будь-якого JavaFX додатку:

 

 

Це наче театральна п'єса: Stage є основним контейнером, який, як правило, представляє вікно з рамками та стандартними кнопками закрити, мінімізувати та максимізувати. Всередину Stage додається Scene, яка, звичайно, може бути замінена іншою Scene. Всередину Scene вже додаються стандартні компоненти типу AnchorPane, TextBox та інші.

Якщо додаток не може знайти вказаного fxml файлу, ви отримаєте наступне повідомлення про помилку:

java.lang.IllegalStateException: Location is not set.

Для вирішення проблеми перевірте правильність шляхів до файлу та правильність написання його назви.

Детально створення і функціонування JavaFX-додатків описані в офіційній документації Javahttps://college.page.link/hCHs.

 

Додаткові матеріали

1. Основи програмування на Java – безкоштовний відеокурс (Prometheus): https://courses.prometheus.org.ua/courses/EPAM/JAVA101/2016_T2/about

2. Java Professional – авторський відеокурс (Бабич О.В, ITVDN): https://www.youtube.com/watch?v=H77pBuf582M&list=PLvItDmb0sZw9DXLBDs4IBcalvA1Nx56o9

3. Основи програмування на Java – безкоштовний відеокурс (Prometheus): https://courses.prometheus.org.ua/courses/EPAM/JAVA101/2016_T2/about

4. Java Professional – авторський відеокурс (Бабич О.В, ITVDN): https://www.youtube.com/watch?v=H77pBuf582M&list=PLvItDmb0sZw9DXLBDs4IBcalvA1Nx56o9

5. Основи програмування на Java – безкоштовний відеокурс (Prometheus): https://courses.prometheus.org.ua/courses/EPAM/JAVA101/2016_T2/about

6. Java Professional – авторський відеокурс (Бабич О.В, ITVDN): https://www.youtube.com/watch?v=H77pBuf582M&list=PLvItDmb0sZw9DXLBDs4IBcalvA1Nx56o9

Основи програмування на Java – безкоштовний відеокурс (Prometheus): https://courses.prometheus.org.ua/courses/EPAM/JAVA101/2016_T2/about

7. Java Professional – авторський відеокурс (Бабич О.В, ITVDN): https://www.youtube.com/watch?v=H77pBuf582M&list=PLvItDmb0sZw9DXLBDs4IBcalvA1Nx56o9

8. Основи програмування на Java – безкоштовний відеокурс (Prometheus): https://courses.prometheus.org.ua/courses/EPAM/JAVA101/2016_T2/about

9. Java Professional – авторський відеокурс (Бабич О.В, ITVDN): https://www.youtube.com/watch?v=H77pBuf582M&list=PLvItDmb0sZw9DXLBDs4IBcalvA1Nx56o9

 

 

Попередня тема

На початок

Наступна тема