вівторок, 29 січня 2013 р.

Поліморфізм в Java


Поліморфізм — важливий механізм в програмуванні, що дозволяє використовувати спільний інтерфейс для обробки даних різних спеціалізованих типів. Прикладом може слугувати перевантаження методів. З появою ООП концепція поліморфізму розширилась. В контексті об'єктно-орієнтованого програмування, найпоширенішим різновидом поліморфізму є здатність екземплярів підкласу грати роль об'єктів батьківського класу, завдяки чому екземпляри підкласу можна використовувати там, де використовуються екземпляри батьківського класу.

Вступ у поліморфізм об'єктів

Так, уявімо, у нас є клас Солдат та на його основі створено класи Генерал та Сержант. Логічно, що кожен Генерал є солдатом і кожен Сержант є солдатом, проте не кожен солдат є Генералом чи Сержантом. Тож Генерал може виконувати функції звичайного солдата, а солдат функції Генарала не зможе. Все вищесказане в ООП реалізовується через об’єктні змінні, які є поліморфними. Тому в коді ми можемо писати наступні інструкції:

// звичайне створення об'єкту Soldier
Soldier s = new Soldier("Солдат"); 
 
// об'єктна змінна типу Soldier посилається на об'єкт типу General

Soldier s2 = new General("Генерал");  

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

// !!! Помилка приведення типу (солдат не генерал) 
General g=new Soldier("Солдат");   

Можна спробувати здійснити приведення до типу General, компілятор пропустить, проте під час виконання програми знову ж виникне помилка приведення типу (виняток виду: java.lang.ClassCastException:

osvjava.ua.Soldier cannot be cast to osvjava.ua.General ):

General g=(General)new Soldier("Солдат");   // ПОМИЛКА ВИКОНАННЯ!!!

Проте коли ми звертаємося до Генерала як до Солдата, то і функціональні можливості Генерала звужуються. Ми можемо викликати методи класу Солдат, проте з методами класу Генерал будуть проблеми. Наприклад, в класі Soldier є метод getHealth(), а в класі General є метод getSlogan():

//змінна sg посилається на об'єкт типу General 
Soldier sg= new General("Генерал");  
sg.getHealth();          //методи класу Soldier доступні
//   sg.getSlogan();     //методи класу General недоступні

Проте, якщо б метод getSlogan був би у класі Soldier, то викликана б була версія методу getSlogan класу General, оскільки при поліморфізмі заміщення методів все ж відбувається (крім статичних методів).

Якщо нам все ж необхідно звернутися до специфічних методів класу General, які притаманні лише йому, то необхідно створити відповідну об’єктну змінну типу General та здійснити явне приведення типу:

General general=(General)sg;  // наш Генерал тепер повноцінний
general.getSlogan();          // метод класу General доступний

Якщо виникає необхідність визначити, до якого класу належить відповідний об’єкт, то можна використати метод getClass(), який може викликати будь-який об’єкт оскільки він дістається йому від прабатька усіх класів Object:

System.out.println(sg.getClass());
// результат: class osvjava.ua.General

Власне об’єктні змінні класу Object доволі часто застосовується з метою збереження посилань на інші класи. Це дозволяє одночасно працювати з різнотипними об’єктами. Наприклад, можна, тримати різнотипні об’єкти в одному масиві типу Object. Також використання поліморфних об’єктних змінних дозволяє створювати своєрідні універсальні класи та методи (узагальнене програмування). Таким чином непотрібне створення великої кількості перевантаження методів з різним типом параметрів. Власне практично всі внутрішні бібліотеки Java спочатку будувалася таким чином. Щоправда, використання поліморфних змінних може слугувати джерелом багатьох помилок, тому в Java починаючи з JSE 5.0 у мову введено так звані Узагальнення (Generics), які дозволяють більш краще будувати такі універсальні засоби (дивіться детальніше відповідний розділ даного вікіпідручника). Приклади використання поліморфізму

Вище наведено основні правила використання поліморфних об’єктних змінних. Проте, щоб зрозуміти усе вищесказане та користь від поліморфізму розглянемо повноцінний приклад. Тож розробимо певну заготовку для своєрідної гри-стратегії. Наша заготовка гри повинна оперувати трьома об'єктами Soldier, General, Sergeant та проводитиме бій між двома військовими до загибелі одного із них.

Клас Солдат

Спочатку створимо батьківський клас Soldier. Наш солдат як і у всіх іграх матиме певний рівень здоров’я, певний рівень захисту від ударів (броню), отримуватиме удари (поранення) та наноситиме удари іншому солдату.

package osvjava.ua;
import java.util.Random;
 
public class Soldier {
    protected int health; // здоров'я солдата
    protected boolean alive = false; // стан (живий чи мертвий)
    protected int defense = 0; // захист від ударів
    protected static int count = 0; // лічильник створених об’єктів
    private int id = 0; // кожен солдат матиме порядковий номер (П№)
    protected String rank; // ранг солдата ("Солдат", "Генерал", "Сержант" тощо)
 
    /** Конструктор
     * @param rank - ранг солдатау 
     */
    public Soldier(String rank) {
 
        this.rank=rank;
 
        id = ++count; // збільшити count на 1 та присвоїти id;
        health = 100; // встановлюємо рівень здоров'я
        alive = true; // оживляємо солдата
 
        //надаємо солдату рівень захисту випадковим чином (від 0 до 50)
        Random randomGen = new Random();
        defense = randomGen.nextInt(50);
        System.out.println(rank+" П№" + id + " is greated: health=" 
                                + health + ", defense=" + defense);
    }
 
    /**
     * @return здоров'я солдата
     */
    public int getHealth() {
        return health;
    }
 
    /** Дозволяє солдату отримувати пошкодження  
     *  @param hit - сила удару
     *  Метод приватний оскільки отримання удару можливе лише через метод hit()
     */
    private void receiveHit(int hit) {
        if (isAlive() == true) {
 
            // обчислюємо пошкодження
            int damage = defense - hit;
            // якщо удар пробив захист солдат отримує пошкодження
            if (damage > 0) {
                health = health - damage;
 
            } else {
                return; // вийти з методу
            }
 
            //якщо солдат загинув, то вивести відповідне повідомлення
            //в іншому випадку вивести рівень його здоров'я
            if (health <= 0) {
                alive = false;
                System.out.println("[X] "+rank+" П№" + id 
                      + " отримав пошкодження " + damage + " і героїчно загинув");
            } else {
                System.out.println(rank+" П№" + id 
                      + " отримав пошкодження " + damage + ". Залишилось здоров'я " +
                                   health);
            }
        }
    }
 
    /** Метод для нанесення удару
     * @param targetSoldier - кого вдарити
     * @param hit - сила удару
     */
    public void hit(Soldier targetSoldier, int hit) {
        targetSoldier.receiveHit(hit);
    }
 
    /**
     * Перевіряємо стан солдата
     * @return живий(true), ні (false)
     */
    public boolean isAlive() {
        return alive;
    }
 
    /** 
     * Повертає порядковий номер солдата
     * @return id
     */
    public int getId() {
        return id;
    }
 
    /** 
     * Заміщення методу toString класу Object
     * @return опис солдата
     */
    @Override
    public String toString() {
        return rank+" П№" + id + ": здоров'я=" 
                         + health + ", захист=" + defense;
    }
}


Як бачимо в програмі визначено конструктор Soldier(String rank), який проводить початкову ініціалізацію об’єкту, встановлює рівень здоров’я, оживляє, випадковим чином надає захист та присвоює порядковий номер.

Для того, щоб об’єкт когось вдарив у нас є публічний метод hit(Soldier targetSoldier, int hit), при використанні якого потрібно вказати кого вдарити і з якою силою. Даний метод отримавши аргументи викликає метод hit(int hit) того солдата(інший екземпляр класу Soldier), якому наноситься удар вказаної сили. Метод hit() визначає, яке пошкодження нанесено. Якщо сила удару більша рівня захисту, то наноситься пошкодження, яке рівне defense – hit (захист мінус сила удару). Якщо здоров’я солдата стає менше або рівне 0, то солдат вмирає.

Також у нас є додаткові інформаційні методи getHealth, getId та toString, для отримання інформації про стан нашого об’єкту (тобто солдата).

Для підрахунку кількості об’єктів використана статична змінна count, яка спільно використовується усіма екземплярами класу Soldier.

Більшість полів оголошені з модифікатором доступу protected, таким чином до них матимуть доступ лише об’єкти класу Soldier, його підкласи та об’єкти класів даного пакету. Таким чином клас Soldier певним чином інкапсульовано, проте при створенні класу з оголошенням, що він належить до даного пакету принцип інкапсуляції порушується. Ніщо не завадить напряму змінити health чи інше поле. Краще б було оголосити дані поля приватними, проте тоді б код програми ускладнився б, прийшлось би оголошувати більше методів доступу і відповідно розуміння програми ускладнилось би та й створення нащадків також ускладнюється. Ви можете самі пізніше переробити даний клас.

Наступний клас TestBattle створений для тестування бою між солдатами. Він оголошений у іншому пакеті, для правильного використання класу (щоб не було спокуси обійти наш інкапсульований інтерфейс роботи із солдатами). Якщо ви ще не до кінця розібралися, як працювати із пакетами в Java, то ви можете даний файл розмістити в одному ж каталозі з попереднім прикладом і вказати, що вони належать до одного пакету, або й взагалі прибрати написи з вказанням приналежності пакету (інструкції із словом package) у всіх файлах класів.

Для імітації бою у класі TestBattle визначено метод battle (Soldier s1, Soldier s2), в якому солдат 1 та солдат 2 наносять почергово один одному удари із випадковою силою. Бій проводиться до загибелі одного з них, після чого виводить інформація про переможця.

//TestBattle.java
package test.ua;
 
import java.util.Random;
 
import osvjava.ua.Soldier;
 
public class TestBattle {
    Soldier s1=new Soldier("Солдат");
    Soldier s2=new Soldier("Солдат");
 
    public TestBattle() {
        battle (s1, s2);
    }
 
    public void battle(Soldier s1, Soldier s2) {
 
        // бій допоки не виживе хтось один,
        // сила удару встановлюється випадковим чином
        Random gen = new Random();
        while ((s1.isAlive() == true) && (s2.isAlive() == true)) {
            s1.hit(s2, gen.nextInt(100));
            if (s2.isAlive()) { //якщо другий загинув, то мертві не воюють
                s2.hit(s1, gen.nextInt(100));
            }
        }
 
        //виводимо переможця
        if (!s1.isAlive()) {
            //   idWinner = soldiers[0].getId();
            System.out.println("***** Кінець бою. Переміг " + s2 + " *****");
        } else
            System.out.println("***** Кінець бою. Переміг  " + s1 + " *****");
 
    }
 
    public static void main(String[] args) {
      // створюємо об’єкт даного класу, виконання продовжиться з конструктора
       new TestBattle(); 
    }
}


Результат:

Солдат П№1 is greated: health=100, defense=10
Солдат П№2 is greated: health=100, defense=27
Солдат П№1 отримав пошкодження 9. Залишилось здоров'я 91
Солдат П№2 отримав пошкодження 18. Залишилось здоров'я 82
Солдат П№2 отримав пошкодження 19. Залишилось здоров'я 63
Солдат П№1 отримав пошкодження 2. Залишилось здоров'я 89
Солдат П№2 отримав пошкодження 17. Залишилось здоров'я 46
Солдат П№1 отримав пошкодження 6. Залишилось здоров'я 83
Солдат П№2 отримав пошкодження 4. Залишилось здоров'я 42
Солдат П№2 отримав пошкодження 11. Залишилось здоров'я 31
Солдат П№1 отримав пошкодження 1. Залишилось здоров'я 82
Солдат П№2 отримав пошкодження 4. Залишилось здоров'я 27
Солдат П№2 отримав пошкодження 23. Залишилось здоров'я 4
Солдат П№1 отримав пошкодження 8. Залишилось здоров'я 74
[X] Солдат П№2 отримав пошкодження 5 і героїчно загинув
***** Кінець бою. Переміг  Солдат П№1: здоров'я=74, захист=10 *****


Сержанти та Генерали

Тепер же ускладнимо гру. У нас з’являються нові персонажі Генерали та Сержанти. Відмінність у них від звичайних солдат у кількості здоров’я, плюс генерали матимуть свій лозунг та метод його одержання.

// Sergeant.java
package osvjava.ua;
 
public class Sergeant extends Soldier {
    public Sergeant(String rank){
        // спочатку створюється солдат, на основі якого створюється наш сержант
        super(rank); 
        // збільшуємо здоров'я сержанта у 10 раз
        super.health=super.health*10;
        System.out.println("Здоров'я сержанта збільшено в 10 раз");
    } 
}

Як бачимо клас сержанта доволі простий. При оголошенні класу ми написали, що він розширює клас Soldier. Далі визначили конструктор даного методу, який в свою чергу викликає конструктор суперкласу передаючи йому ранг. Якщо б ми б не визначили б конструктори у даних класах, а існували б лише конструктори по замовчуванню. То виклик конструкторів по замовчуванню відбувався б у тій же послідовності. При виклику конструктора Sergeant із нього б викликався конструктор Soldier і лише після створення Солдата відбувалось би створення Сержанта. Такий же механізм реалізовано в даному прикладі явно.

При створенні сержанта інструкцією new Sergeant(“Sergeant”), ми отримуємо наступне:

Сержант П№1 is greated: health=100, defense=45
Здоров'я сержанта збільшено в 10 раз 

Тепер же опишемо нашого Генерала із кількома власними методами і своїм лозунгом.

//General.java
package osvjava.ua;
 
public class General extends Soldier {
 
    private String slogan = "Ніколи не здаватись"; // Лозунг генерала
 
    /** Конструктор
     * @param rank - ранг солдата ("Генерал")
     */
    public General(String rank) {
        // спочатку створюється солдат, на основі якого створюється наш генерал
        super(rank); 
        // збільшуємо здоров'я генерала у 100 раз
        super.health = super.health * 100; 
        System.out.println("Здоров'я генерала збільшено у 100 раз");
    }
 
    /** Тепер заміщується метод toString класу Soldier
     * @return  Стан генерала із лозунгом
     */
    @Override
    public String toString() {
        return "Генерал із здоров'ям " + super.health 
                     + " його лозунг: " + slogan;
    }
 
    /**Отримати лозунг генерала
     * @return лозунг
     */
    public String getSlogan() {
        return slogan;
    }
}


Давайте заставимо нашого генерала битися із сержантом. Оскільки вони розширяють клас Soldier, то нам непотрібно створювати новий метод battle() з параметрами типу General та Sergeant. Створений попередньо метод цілком підходить

Sergeant ser=new Sergeant ("Сержант");
General gen=new General ("Генерал");
battle (ser, gen):

Результат виконання:

Сержант П№1 is greated: health=100, defense=12
Здоров'я сержанта збільшено в 10 раз
Генерал П№2 is greated: health=100, defense=29
Здоров'я генерала збільшено у 100 раз
Сержант П№1 отримав пошкодження 1. Залишилось здоров'я 999
Генерал П№2 отримав пошкодження 4. Залишилось здоров'я 9996
...
Сержант П№1 отримав пошкодження 12. Залишилось здоров'я 4
Генерал П№2 отримав пошкодження 20. Залишилось здоров'я 5173
Генерал П№2 отримав пошкодження 6. Залишилось здоров'я 5167
[X] Сержант П№1 отримав пошкодження 8 і героїчно загинув
***** Кінець бою. Переміг Генерал із здоров'ям 5167 його лозунг: Ніколи не здаватись ***** 


Як і можна було сподіватись, сили були занадто нерівні:). Генерал переміг

Таким чином завдяки тому, що наш метод battle використовує об’єктні змінні типу Soldier він без проблем приймає змінні типу Sergiant та General. Звичайно, що при цьому дані дочірні класи повинні битися через інтерфейс описаний у батьківському класі. Якщо вони реалізовуватимуть бій власним чином, то метод battle можливо не працюватиме правильно.

Крім того згадаймо метод hit(Soldier targetSoldier, int hit) класу Soldier. Там використовується посилання на об’єкт класу Soldier проте після успадкування даного методу він продовжує правильно працювати і з об’єктами Sergeant та General. Це також можливо завдяки поліморфізму.

Створюємо Армію

Також ми можемо зібрати усіх переможців в одну армію і підрахувати загальне здоров’я даної армії.

Ось клас Army, доволі простий, лише додає солдат до армії і в ньому існує метод, який підраховує здоров’я армії:

package osvjava.ua;
 
public class Army {
    protected int num=99;
    protected Soldier[] soldiers;
    static int count=0;
 
    public Army() {
        soldiers=new Soldier[num];
    }
 
    /** Метод зарахування солдата в армію
     * @param soldier
     * @return true - солдата додано, false - помилка
     */
    public boolean addSoldier(Soldier soldier) {
        if (count>=num) return false;
        this.soldiers[count]=soldier;
        count++;
 
        return true;
    }
 
    /** Підрахунок здоров'я армії
     * @return Сумарне здоров'я усіх солдат в армії
     */
    public int calcArmyHealth() {
        int armyHealth=0;
        for (int i = 0; i < count; i++) {
           armyHealth+=soldiers[i].getHealth();
        }
 
        return armyHealth;
    }
}

А ось клас для тестування:

package test.ua;
import java.util.Random;
 
import osvjava.ua.Army;
import osvjava.ua.General;
import osvjava.ua.Sergeant;
import osvjava.ua.Soldier;
 
 
public class TestBattle2 {
    Sergeant ser=new Sergeant ("Сержант");
    General gen=new General ("Генерал");
    Soldier[] s= new Soldier[100];
    Army army=new Army();
 
    public TestBattle2() { 
 
        s[0] = new Soldier("Солдат");
        s[1] = new Soldier("Солдат");
        s[2] = new Soldier("Солдат");
        s[3] = new Soldier("Солдат");
 
        army.addSoldier(battle (ser, gen));
        army.addSoldier(battle (s[0], s[1]));
        army.addSoldier(battle (s[2], s[3]));
 
        System.out.println("Сумарне здоров'я армії "+ army.calcArmyHealth());
 
    }
 
    public Soldier battle(Soldier s1, Soldier s2) {
 
        // бій допоки не вижеве хтось один,
        // сила удару встановлюється випадковим чином
        Random gen = new Random();
        while ((s1.isAlive() == true) && (s2.isAlive() == true)) {
            s1.hit(s2, gen.nextInt(100));
            if (s2.isAlive()) { //якщо другий загинув, то мертві не воюють
                s2.hit(s1, gen.nextInt(100));
            }
        }
 
        //виводимо переможця
        if (!s1.isAlive()) {
            //   idWinner = soldiers[0].getId();
            System.out.println("***** Кінець бою. Переміг " + s2 + " *****");
            return s2;
        } else{
            System.out.println("***** Кінець бою. Переміг  " + s1 + " *****");
            return s1;
        }
 
    }
 
    public static void main(String[] args) {
       new TestBattle2();
    }
}

Як бачимо у класі Army для того, щоб просумувати здоров’я усіх солдат, нам не знадобилося три масиви типу Soldier, Sergeant та General, замість цього ми використали один масив Soldier, у який поміщаємо як рядових солдат так і генералів та сержантів. Метод army.calcArmyHealth() підбиває усе здоров’я армії простим перебором елементів масиву. Зверніть увагу як модифіковано метод battle у класі TestBattle2, тепер він повертає посилання на переможця: тип повернення Soldier.

Як вже зазначалося у вступі до поліморфізму, якщо ми хочемо викликати методи дочірніх класів, то прийдеться здійснити явне приведення до типу дочірнього класу, інакше методи General будуть недоступні ззовні, хоча всередині себе об’єкт без проблем застосовує власні методи та поля. Також, якщо в дочірньому класі перевизначено метод батьківського класу(в даному випадку метод toString), то навіть при використанні об’єктного посилання батьківського класу, метод усе рівно буде заміщено (override) (крім статичних методів). Нестатичні методи можна перевизначити у дочірньому класі, проте при поліморфізмі вони не будуть заміщатися. При виклику статичного метода об'єкту типу General, через об’єктну змінну типу Soldier буде викликаний метод Soldier, а не визначений в General одноіменний статичний метод. Все це потрібно враховувати при створенні власних програм. Тому виділіть час і поекспериментуйте з механізмами успадкування і поліморфізму, хоча б на даних прикладах.

Структура створених вищенаведених класів зображено на UML діаграмі. UML діаграма класів



 Власне, щоб не повторювати постійно метод battle при тестуванні, його б було доцільно розмістити у відповідному класі Battle. Можете зробити це самостійно. Також було б непогано реалізувати бій між арміями, а не тільки між двома солдатами.

понеділок, 10 грудня 2012 р.

JVM creation failed

JVM creation failed - повідомлення, яке з'являється в NetBeans IDE 7.1.2 при запуску. Методом "наукового втику" виявив, що є певний конфлікт з продуктами ABBY lingvo та Adobe Reader. Закриваємо ці програми і запускаємо усе проходить. В NetBeans IDE 7.2.1 дану проблему видалили.

Можна було б забути про даний глюк. Проте виявилось, що ще один продукт Oracle страждає на ту ж слабкість. Oracle JDeveloper 11g видає схожу помилку: Unable to create istance of the Java Virtual Machine..... Вигружаю Abby lingvo і девелопер запускається. Після чого лінгво можна запускати.


четвер, 1 листопада 2012 р.

Як розкрутити свій сайт (блог)

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

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


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

3. Зареєструйтесь на сайтах реєстраторах, на яких можна завестися лічильником відвідуваності. На зразок: http://top.topua.net/ . Власне самі лічильники вам і не сильно потрібні, але дані сайти можуть приводити до вас додаткових відвідувачів. Плюс вони міститимуть посилання на ваш сайт, а чим більше посилань на ваш сайт тим краще. Їх часто враховують пошуковики, та й без того можуть приводити кілька відвідувачів вдень.

4. Проставте посилання на ваш сайт на кількох інших сайтах, форумах, обговореннях. Де тільки зможете. Звичайно, що занадто спамити не потрібно, а то ще попадете в чорний список. Але хоча б кілька десятків помістіть.

5. Не зайвим буде сервіс від google: гугл вебмастер,  сервіс дозволяє стежити за станом вашого сайту, вести статистику відвідуваності окремих публікацій та звідки заходять, покращити індексацію вашого сайту, знаходити деякі проблеми. Іншим сервісом для власників сайтів від гугл є Гугл Аналітика Аналогічні сервіси доступні і в інших пошукових системах. Якщо не шкода грошей, то можна також провести рекламну компанію, наприклад, з допомогою Google AdWords.

6. По можливості проставте побільше ключових слів на ваших сторінках. Додавайте теги та мітки. Адже одним з критерієм від якого залежить рейтинг вашого сайту в пошуці є частота входження певних слів.

7.  Розміщуйте інформацію про нові товари чи статті в соціальних мережах. Благо є різноманітні сервіси, які роблять це автоматично через натиснення однієї кнопки, Like, facebok, twitter і т.п. Обов'язково розмість такі кнопки у ваших статтях, щоб читачі самі поширювали інформацію про вас.

8. І саме головне не очікуйте, що якщо ви це все зробили за один день, до вас хлине маса відвідувачів уже завтра, пошуковикам  і користувачам потрібний певний час на те, щоб полюбити вас:). Розкрутка сайту - це постійний процес. Звичайно ви можете його розкрутити через спам на форумах, проте така слава скороминуча. Найкращим вирішенням є все ж наповнення сайту різноманітною інформацією на популярну тематику. Ряд пошуковиків ведуть статистику популярності запитів та й ви самі швидко побачити, які теми цікавлять більше ваших читачів. Нехай щастить!


вівторок, 30 жовтня 2012 р.

Як вивчити англійську мову і для чого вона технарям?

Вчиш англійську в школі, вчиш, а результат нульовий. Потім ідеш в університет там тебе знову нею мучать і все рівно як не говорив нею так і не говориш. Граматичні правила ніяк в голову не вдовбуються. Ніби і вчиш слова та матеріал занять і сяк-так домашні вправи виконуєш. Більш того якось пробуєш себе занурювати в англійську, якісь самовчителі почитувати, музику слухати, а все рівно не йде. Думаю ситуація знайома багатьом? Аналогічні проблеми були і в мене, поділюсь власним досвідом з підтягування англійської. Щоправда, я б не сказав, що я  нею вільно володію, але принаймні англомовні блоги, довідники та книжки, з допомогою лінгво, читати виходить доволі непогано і швидко.

Мотивація


Перше, що вам потрібно це усвідомити для чого вона вам. Звичайно можна багато говорити, що це найпоширеніша мова і важко буде за кордоном без неї, але це уже всі чули і це дійсно для всіх і особливо якось не стимулює. Цей стимул починає працювати лише після того як люди побувають за кордоном  і напряму наткнуться на мовний бар'єр. А ось реальний стимул, це те що без англійської вас неможливо рахувати професійним технарем. Особливо це стосується програмістів.  Хоча і схемотехнікам без англійської не обійтись.. В основному українською та російською наявна інформація для навчання елементарним, початковим речам. Зокрема, переважна більшість книг наявних російською мовою - це комерційні видання, які орієнтовані на широкий загал. А це передусім студенти. Доволі мало більш професійних видань. А вузькоспеціалізованих взагалі на пальцях можна перерахувати. Крім того ця література уже на початок виходу перекладу застаріла на кілька років. Сучасні ж інформаційні технології розвиваються доволі швидко і вам потрібно встигати за знаннями. Крім того багато речей залишаються неперекладеними, як то хелпи по мовах програмування, хелпи до програмних засобів і бібліотек мов програмування. А це все значить, що без англійської ви постійно натикатиметесь на значні труднощі з пошуком інформації, що сповільнятиме вашу роботу. а отже напряму впливатиме на рівень ваших доходів.

Як вчитися


Коли ви усвідомили, що англійська вам справді потрібна можна приступати.

1. Перш за все, якщо ви повний новачок знайдіть самовчитель, краще озвучений і ознайомтесь з правилами читання та вивчіть хоча б кілька десятків слів англійською. Щоправда в англійській мові велика частина слів читається поза правилами, але є стандартні буквосполучення, які переважно читається однаково як то -оо- читається у, чи -tion - читається шн і т.п. це спростить вам процес вивчення нових слів і читання літератури. Звичайно, що ви зразу всі правила не вивчите, але принаймні будете розуміти чому ряд слів читаються саме так.

2. Запишіться на курси англійської чи найміть репетитора.  Для чого вам курси? звичайно, якщо ви маєте значну силу волі  то можете навчатись самі. Але принаймні на початках вам потрібна людина, яка виправить вашу вимову і якось виправлятиме ваші помилки. Крім того як показує мій власний досвід, доволі важко заставити себе постійно займатись англійською. Тому краще якщо у ваш час буде організований таким чином, що ви не зможете ухилятися від заняття англійською мовою. А найкраще це зробити, якщо ви братимете заняття в когось. Щоправда репетитор це більш дорожчий варіант, курси все ж коштують дишевши. При чому при виборі курсів зверніть увагу на кількість людей в групі. Групи повинні бути невеликими близько десятка, максимум 15. Плюс зверніть увагу на відгуки про дані курси. Переважно хороші курси доволі популярні і розрекламовані, дозволяють самостійно вибрати собі викладача англійської та передбачають спілкування з native-спікером, а по простому з людьми для яких англійська рідна.

3. Зубріть слова. Незалежно від того чи ви займаєтесь самі чи ходити на курси чи репетитора. Поповнюйте постійно свій словниковий запас. Якщо ви ідете по якісь програмі, то вивчайте ті слова, які вам попадаються у вашому підручнику чи задають на курсах і навіть понад це. Чому слова важливі? Основна причина в тому, що без слів ви не зможете успішно освоювати граматичні правила. Ось чому я порадив спочатку набути елементарний словниковий запас, а тоді іти на курси англійської. Якщо ви не знаєте слів, при вивченні граматики ви витрачаєте розумові зусилля на згадування слів, а не на самій граматиці, не говорячи уже про говоріння англійською, де вам швидко треба згадувати слова і формувати правильним чином з них речення. Ось одна з причин того, чому в школі люди не роблять значних успіхів в англійській. Перед проступанням до заданих письмових вправ чи зубріння якогось тексту, спочатку бажано вивчити всі слова, а тоді приступати до самого завдання. І тут можу дати кілька порад із власного досвіду:
  • заведіть словник для слів. Як його вести знають усі зі школи. Три колонки: слово, транскрипція, значення
  • тренуйтесь в написанні. При вивченні слів із словника тренуйтесь в їхньому написанні пишучи багаторазово слово, при цьому повторюйте про себе його значення.  (В школі в середніх класах вам задавали таку вправу, але коли учні її роблять, то основною їх метою є написання кількох рядочків заради виконання завдання вчителя, а не вивчити ці слова). Завчили написання одного слова переходіть до іншого. Дійдете до п'ятого чи десятого слова спробуйте повернутись і закривши колонку зі словом, читаючи значення попробуйте згадати слово і його написання, напишіть. Далі переходьте до другого, якщо якесь не можете правильно написати, напишіть його ще декілька раз. Завчили перший десяток переходьте до наступного десятка. При цьому час від часу вертайтесь, повторюйте уже завчені слова. В результаті написання слів ввійде у вас в підсвідомість. На рахунок ефективності 25 кадру не знаю, але щодо якісного вивчення при написанні слів даю гарантію, перевірено на собі.
  • заведіть карточки для слів. Коли вивчите написання слів, то слова все рівно потрібно повторювати, оскільки сьогодні ви їх пам'ятаєте, а завтра уже забули. На невеликих карточках з однієї сторони пишіть англійське слово з другої значення слова українською. Я, наприклад, розрізав канцелярські карточки для тимчасових записів пополам і писав на них. І аналогічно до вивчення написання перебирайте карточки починаючи з десятка і поступово збільшуючи їх додаючи слова. Дивитесь українська значення - говорите англійське слово і навпаки. Таким чином мені свого часу вдавалося за день вивчати близько 50-100 слів. Коли карточок стане забагато, виберіть ті які помиляєтесь і продовжуйте вчити. Ті що вивчили не викидайте, а раз на тиждень чи на два тижні повертайтесь і повторюйте їх. Дехто радить ще наклеїти карточки із назвою англійською на всі побутові речі і кожного дня повторяйте як називається та чи інша річ. Особисто я таким способом не користувався, але в цьому є свій сенс і чув що дієво.
 4. Занурюйтесь в англійську. Коли ви вже набрали певний словниковий запас, освоїли трішки граматику, то можна починати більш глибше занурення в англомовне середовище. Якщо ви ходите на курси, то вам таке середовище уже організували, але цього не достатньо. Вам передусім потрібна професійна лексика, тому починайте читати професійну літературу, хелпи. Поставте собі англомовні програми, дивіться фільми, які вам цікаві і слухайте музику. Корисним є здійснення перекладів з англійської.  Ви можете завести свій блог і виставляти там свої переклади цікавої інформації з англомовних сайтів, або ж перекладати статті з англійської на українську у Вікіпедії, які вам цікаві.

5. Знайдіть з ким можна б було поспілкуватися англійською.  Усне спілкування теж є важливою складовою вивчення англійської. Якщо ви розпочнете навчання на курсах, то там вам забезпечать певну розмовну практику. Крім того корисним є спілкування з іноземцями. Благо є інтернет і ви просто можете переписуватися з кимось у соцмережах, або спілкуватися вживу по скайпу.

Головне постійно працюйте і результат не забариться!


понеділок, 29 жовтня 2012 р.

Використовуємо Java Database Connectivity

Представляю вирізку з Java для чайників Баррі Берда по роботі з базою даних.Розглядається з'єднання з базою даних, запис даних та подання запитів. Далі оригінальний текст.

JDBC та Java DB

Коли я вперше розпочав роботу з базами даних, моєю найбільшою проблемою було з’єднання з базою даних. Я написав весь необхідний Java код. (Насправді, я скопіював весь Java код з якоїсь книги.) Програмна частина була простою. Найважчою частиною було змусити мій код знайти базу даних в системі.

Частиною проблеми було те, що шлях в який ваш код взаємодії з базою даних залежить від виду вашої системи та виду бази даних, що встановлена в вашій системі. Книга, якою я користувався не могла описати в деталях усе, тому що деталі  відрізнялися від комп’ютера до комп’ютера. Зараз же я пишу мій власний розділ про з’єднання з базою даних. Що ж я як автор робитиму?

На моє щастя, набір розробника Java (JDK) поставляється з власною вбудованою базою даних – Java DB. Java DB базується на базі даних Apache Derby і є доволі безпечною, полегшеною і стандартизованою. Java DB виконується самостійно від решти Java JDK. Java гуру представили Java DB з випуском Java 6.

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

А що якщо ви не використовуєте Java DB? Що якщо всі ваші дані зберігаються в іншому типі бази даних; як то MySQL, PostgreSQL, SQLLite, Oracle, Microsoft Access, DB2 або в будь-якій іншій? Тоді Java має рішення для вас! Класи Java Database Connectivity (JDBC) надають загальний доступ до більшості систем управління базами даних. Просто візьміть драйвер для вашої улюбленої системи, модифікуйте два рядочки коду в прикладах даного розділу і ваш код готовий до виконання.


Створення даних

JDBC розміщується у двох пакетах: java.sql та javax.sql, які є частиною Java API. Приклади даного розділу використовують класи java.sql. Перший приклад наведений в роздруківці 16-1.

Роздруківка 16-1: Створення бази даних, таблиці та вставлення даних.

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

class CreateTable {
public static void main(String args[]) {
    
    final String DRIVER =
        "org.apache.derby.jdbc.EmbeddedDriver";
    final String CONNECTION =
        "jdbc:derby:AccountDatabase;create=true";
    try {
        Class.forName(DRIVER).newInstance();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    try (Connection connection =
         DriverManager.getConnection(CONNECTION);
        Statement statement =
         connection.createStatement()) {
        statement.executeUpdate("create table ACCOUNTS"
                +"(NAME VARCHAR(32) NOT NULL PRIMARY KEY,"
                +" ADDRESS VARCHAR(32), BALANCE FLOAT) ");
        statement.executeUpdate("insert into ACCOUNTS"
                +" values ('Barry Burd', '222 Cyber Lane', 24.02)");
        statement.executeUpdate("insert into ACCOUNTS values"
                +" ('Joe Dow', '111 Luddite Street', 55.63)");
        
    } catch (SQLException e) {
        e.printStackTrace();
    }
    }
}

Щоб використовувати MySQL замість Java DB, зробіть деякі зміни в лістингу 16-1. Змініть значення DRIVER на "com.mysql.jdbc.Driver". Змініть значення CONNECTION на "jdbc:mysql://localhost/AccountDatabase;
create=true". Зробіть подібні зміни для DB2, Oracle та інших баз даних

Щоб запустити наведений код, вам необхідно мати файл, що містить відповідний драйвер бази даних, і даний файл повинен бути розміщений там, де Java зможе його знайти. В прикладах даного розділу я з’єднуюсь з базою даних Java DB, знаної як база даних Apache Derby. Драйвер знаходиться у файлі з назвою derby.jar, який зазвичай знаходиться в директорії JDK db/lib. Щоб зробити db/lib/derby.jar доступним для моєї програми, я додаю даний файл до Java classpath.

Спосіб в який додати .jar файл до classpath залежить від виду середовища розробки (IDE) яким ви користуєтесь та виду вашої операційної системи. В Eclipse, я вибираю Project->Properties->Java Build Path. Тоді я клацаю на кнопці Add External JARs і переходжу до директорії db/lib. Для інших IDEs, кроки є дещо відмінними.

Коли ви виконуєте код в роздруківці 16-1, то здається що нічого не стається. Програма запускається і зупиняється. Так і має бути. Код не має видимого виводу, тому що весь вивід направляється в базу даних. Тож, щоб побачити результат коду в роздруківці 16-1, ви повинні переглянути зміни в самій базі даних. Тож продовжуйте читати!

В попередньому абзаці, я написав що виконання роздруківки 16-1 є жахливо непримітним. Я написав що «здається нічого не сталося» і що «Код не має видимого виводу». Проте, якщо ви розглянете ближче, то зможете знайти деякі докази виконання коду роздруківки 16-1. Зокрема, ваш жорсткий диск матиме декілька додаткових файлів після виконання цього коду. Один з цих файлів, називатиметься derby.log. Він міститиме текст опису запуску і зупинки програмного забезпечення бази даних Derby.  Ви також можете знайти нову папку з назвою derbyDB, який міститиме log файли, файл service.properties та папку повну .dat файлів. (Ці .dat файли містять увесь матеріал збережений в базі даних.) Якщо ви використовуєте Eclipse, ви можете зробити дані файли і папки видимими вибравши відповідну гілку оглядача пакетів та далі вибравши File->Refresh.

Викорисовуємо SQL команди

В роздруківці16-1 суть коду лежить в трьох викликах executeUpdate. Кожен виклик executeUpdate містить рядок символів узятих в лапки. Щоб зробити код читабельним, я порізав кожен рядок на частини та розділив дані частини знаком плюс (оператором конкатенації). Плюс у Java виконує подвійну роботу. Для чисел, плюс виконує додавання. Для рядків, плюс – це знак для поєднання двох рядків докупи, утворюється один з’єднаний рядок (string).

Ви можете зробити рядок в подвійних лапках як завгодно довгим. Коли ви добираєтесь до правого краю екрану, перестаньте друкувати. Якщо ви хочете побачити цілий рядок без прокрутки, то вам необхідно розділити його на частини, як це зроблено в роздруківці 16-1. Просто розділіть частини знаком плюс.

Ви не можете розділити Java рядок просто натиснувши Enter або перемістивши на новий рядок. Коли ви розпочинаєте рядок з подвійної лапки("), то заключна подвійна лапка має бути на одному текстовому рядку коду.

Якщо ви знайомі з SQL (Structured Query Language – мова структурованих запитів), командний рядок у виклику executeUpdate зрозумілий для вас. Якщо ні, візьміть примірник SQL для Чайників, 7-ме видання, Алена Тейлора (SQL For Dummies, 7th Edition, by Allen G. Taylor.). Дані командні рядки не є частиною мови Java. В Java ці команди є лише рядками символів, які ви згодовуєте методу executeUpdate.  Дані рядки,  написані на SQL, створюють нову таблицю і додають ряд даних до таблиці. Коли ви пишете програму для бази даних на Java, ви просто пишете звичайні команди SQL і вставляєте їх у виклики методів Java.

В коді даного рядка строго дотримано технічні прийоми описані в JDBC версії 1.0. Пізніші версії класів JDBC підтримують інший спосіб запису даних (scrollable result sets), що надає вам метод insertRow, який заощаджує ваші зусилля, щодо повного написання командних рядків SQL.



З’єднання та роз’єднання

Окрім викликів методу executeUpdate, код в роздруківці 16-1 має ще деякий матеріал. Далі подається скорочений виклад  значення кожної частини коду:

  •   Class.forName: знаходить драйвер бази даних
Говорить до бази даних, що вам необхідний програмний посередник (драйвер бази даних). Драйвери можуть бути різних видів та розмірів, багато з них доволі дорогі. Роздруківка 16-1 застосовує невеликий, безкоштовний драйвер — Derby JDBC Embedded driver. Код для даного драйвера розміщується в Java класі EmbeddedDriver. Даний клас розміщений всередині пакету org.apache.derby.jdbc.

Щоб використати клас  EmbeddedDriver, ви викликаєте метод Class.forName. Хочете вірте, хочете ні проте Java API має клас під назвою Class. Клас Class містить інформацію про класи, що доступні в віртуальній машині Java (JVM). В лістингу 16-1, виклик Class.forName заглядає в клас org.apache.derby.jdbc.EmbeddedDriver. Після того як екземпляр EmbeddedDriver завантажений, ви можете приступити до з’єднання з базою даних.

  •   DriverManager.getConnection: Встановлює сесію з зазначеною базою даних
Метод getConnection проживає в Java класі з назвою DriverManager.  В роздруківці 16-1 виклик getConnection створює базу даних AccountDatabase та відкриває з’єднання з базою даних, Звичайно, що AccountDatabase можливо  вже існує перед стартом коду роздруківки 16-1. Якщо це так, то текст ;create=true у виклику getConnection не матиме ефекту.

В параметрі getConnection зверніть увагу на двокрапку. В коді не просто вказується назва бази даних AccountDatabase, крім того для класу DriverManager вказується, який протокол застосовувати для з’єднання з базою даних. Код jdbc:derby:  — теж саме, що http: у веб-адресах, тобто ми вказуємо комп’ютеру застосовувати jdbc протокол для спілкування з derby протоколом,  який передасть усе до вашої AccountDatabase.

  •     Connection.createStatement: створити інструкцію
Це виглядає дивно, проте в Java Database Connectivity, ви створюєте простий об’єкт statement. Після того як ви його створили, ви можете використовувати об’єкт багато раз з різноманітними SQL рядками, щоб посилати різні команди базі даних. Тож, перед  тим як викликати метод statement.executeUpdate, вам необхідно мати відповідний об’єкт statement.  Виклик connection.createStatement створює об’єкт statement для вас.

  •   try…catch…:  розпізнаємо винятки, що можуть виникнути в коді

Ви мабуть знаєте, що деякі виклики методів викидають контрольовані винятки. Контрольований виняток – це виняток який має бути розпізнаний у коді виклику. Тож, виклик Class.forName може викинути три типи винятків і згодом код в роздруківці 16-1 може викинути SQLException. Щоб розпізнати дані винятки, я додав інструкцію try-catch в мій код.

  •  try-with-resources:  вивільняє, які б не були, ресурси
Кожне з’єднання і кожна інструкція бази даних бере деякі системні ресурси. Коли ви закінчуєте використовувати ці ресурси, ви звільняєте їх.  Ви маєте явно вказати це викликом методу close, і ви мусите зробити це в інструкції try-catch.

Але там же є ще інструкція catch (зловити)! Коли щось іде неправильно, це не може просто так відбуватися. Це завжди трапляється через недбалість в коді. Тож, що трапиться, якщо catch викине власні винятки? І що станеться згодом, де ваш код пробує закрити усе з’єднання? Чи буде усе правильно закрито?

Щоб зробити усе заразом, Java 7 має нову інструкцію try-with-resources (try з ресурсами). Try-with-resources діє як давніша інструкція try. Проте в новій інструкції ви додаєте дужки після слова try. Всередині дужок, ви розміщуєте деякі інструкції, що створюють ресурси. (В роздруківці 16-1 інструкціями в дужками є виклики getConnection та createStatement.) Інструкції розділяються крапкою з комою.

Інструкція try-with-resources автоматично закриває і звільняє ваші ресурси вкінці виконання інструкцій. Більш того, try-with-resources, бере на себе усю брудну роботу щодо рішення усіх помилок при закритті таких ресурсів.

Видобування даних

Що доброго в базі даних, якщо ви не можете отримати дані з неї?  В даній секції, ви задаватимете запити до бази даних, що була створена в роздруківці 16-1. Код задання запитів показаний в роздруківці 16-2.

Роздруківка 16-2: Створення запитів

import static java.lang.System.out;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.NumberFormat;

class GetData {
public static void main(String args[]) {
    NumberFormat currency =
     NumberFormat.getCurrencyInstance();
    final String DRIVER =
        "org.apache.derby.jdbc.EmbeddedDriver";
    final String CONNECTION =
        "jdbc:derby:AccountDatabase";
    try {
        Class.forName(DRIVER).newInstance();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    try (Connection connection =
        DriverManager.getConnection(CONNECTION);
        Statement statement =
          connection.createStatement();
        ResultSet resultset =
         statement.executeQuery(
          "select * from ACCOUNTS")) {

            while(resultset.next()) {
            out.print(resultset.getString("NAME"));
            out.print(", ");
            out.print(resultset.getString("ADDRESS"));
            out.print(" ");
            out.println(currency.format(
            resultset.getFloat("BALANCE")));
            }
    } catch (SQLException e) {
        e.printStackTrace();
    }
}
}


Щоб використовувати MySQL замість Java DB, зробіть наступні зміни в роздруківці 16-2: змініть значення DRIVER на “com.mysql.jdbc.Driver”. Змініть значення  CONNECTION на “jdbc:mysql://localhost/ AccountDatabase;create=true”. Зробіть подібні зміни для DB2, Oracle та інших баз даних.

Результат виконання коду з роздруківки 16-2 зображено на рисунку 16-1. В коді задаються запити в базу даних і згодом поступово кожен рядок з бази даних роздруковується.

Рисунок 16-1 – Отримання даних з бази даних
________________________________
Barry Burd, 222 Cyber Lane 24,02 грн.
Joe Dow, 111 Luddite Street 55,63 грн.
________________________________

Роздруківка 16-2 починається із звичного вже виклику forName, getConnection та createStatement. Далі в коді викликається executeQuery і подається виклик з SQL командою. Для тих хто знає SQL команди, той знає, що дана особлива команда бере всі дані з таблиці ACCOUNT (таблиці створеної в роздруківці 16-1).

Сутність, що повертається з виклику executeQuery має тип java.sql.ResultSet. (Це різниця між executeUpdate та executeQuery — excuteQuery повертає множину даних,  executeUpdate ні.) Дана множина-результат виглядає  так же як і таблиця бази даних. Подібно до оригінальної таблиці, множина розділена на ряди і стовпці. Кожен ряд містить дані одного облікового запису: ім’я, адрес і об’єм балансу.

Після того як ви викликали executeQuery і отримали вашу множину-результат, ви можете проходити покроково по рядах множини-результату.  Щоб зробити це потрібно створити цикл і перевіряти на початку кожної ітерації умову resultset.next(). Кожен раз коли викликається resultset.next() робиться дві речі:

  •     відбувається перехід до наступного ряду множини-результату (наступного облікового запису), звичайно, якщо він існує
  •   вам говориться чи існує даний ряд через повертається булевого значення – true або false

Якщо resultset.next() повертає true, множина-результат має інший ряд. Комп’ютер переходить до наступного ряду і в циклі поступово вибираються дані з даного ряду. Якщо ж resultsetset.next() повертає false, множина-результат не має більше рядів. Ви стрибаєте за межі циклу і починаєте усе закривати.

Тож, якщо ряд існує, то переходимо всередину циклу роздруківки 16-2.  Ви видобуваєте дані з ряду множини-результату викликаючи методи getString та getFloat. Раніше в роздруківці 16-1 ви створили таблицю ACCOUNTS із стовпцями NAME, ADDRESS та BALANCE.  Тут же в роздруківці 16-2 ви отримуєте дані з даних стовпців викликаючи методи зразка getДеякийТип і згодовуєте оригінальні назви стовпців даним методам. Після того як ви отримали дані, ви виводити їх на екран комп’ютера.

Кожен екземпляр ResultSet має декілька непоганих методів getДеякийТип. В залежності від того який тип даних поміщений в стовпчику, ви можете викликати методи: getArray, getBigDecimal, getBlob, getInt, getObject, getTimestamp і декілька інших.


четвер, 11 жовтня 2012 р.

Робота з COM-портом на Java

Для робот з портами Java в JDK немає стандартного пакету. Тому необхідно встановити додаткову бібліотеку для роботи з послідовними та паралельними портами. Послідовний (COM-порт) доволі поширений в даний час, багато апаратного обладнання працює на ньому. Навіть в сучасному він використовується доволі часто. Сучасні прилади підключаються до USB, проте в багатьох використовується перехідник USB-COM, що дозволяють розробникам програмного забезпечення працювати з приладом через добре знайомий і легкий інтерфейс RS232 (COM).

Тож, що нам потрібно? Існує декілька java бібліотек для роботи з COM-портом , які побудовані з використанням Native-методів. Свого часу була популярна javax.Comm, її можна використовувати до цих пір, проте в даний час бібліотеку ніхто не підтримує, її розвиток не відбувається. На зміну javax.Comm прийшла інша java бібліотека – RXTX (http://rxtx.qbang.org/wiki/index.php/Main_Page). Методи в RXTX для роботи з COM-портом ідентичні до методів javax.Comm.

Встановлення

Встановлення бібліотеки можна здійснити декількома способами. Їх можна знайти в документації на сайтах підтримки RXTX.

1. Найпростіший спосіб:

• Знайти де у вас встановлене JDK, наприклад:  c:\Program Files\Java\jdk1.6.0_01\
• Скопіювати rxtxParallel.dll до c:\Program Files\Java\jdk1.6.0_01\jre\bin\
• Скопіювати rxtxSerial.dll до c:\Program Files\Java\jdk1.6.0_01\jre\bin\
 • Скопіювати RXTXcomm.jar до c:\Program Files\Java\jdk1.6.0_01\jre\lib\ext\

Якщо у вас JRE і вам просто необхідно виконати необхідну програму, яка використовує RXTX, то зробити теж саме але для відповідних каталогів JRE. Звичайно даний спосіб поганий для кінцевих користувачів вашого продукту. Оскільки не всі користувачі зможуть легко зробити необхідні маніпуляції з файлами.

2.Тому другий спосіб - це включити бібліотеку у jar файл вашого продукту. Якщо ви використовуєте NetBeans, то у проекті де знаходяться java файли необхідно створити каталог resources і помістити туди rxtxSerial.dll. .

Другий крок – це додавання власне java файлів, для цього можна завантажити джерельний код(source) бібліотеки з офіційного сайту і з папки src взяти усі .java файли і закинути в src-папку проекту по структурі каталогів gnu/io.

Детальніше можна знайти в різноманітній документації. Зокрема як підключити rxtx у Eclipse написано тут: http://rxtx.qbang.org/wiki/index.php/Using_RXTX_In_Eclipse

Для початку можете спробувати перший спосіб.



Програмування

Перейдемо власне до програмування. В інтернеті я знайшов хорошу реалізацію класу SerialPortHandler, який спрощує роботу з RXTX. Тому наведу дещо перероблений варіант далі (Оригінал: http://embeddedfreak.wordpress.com/2008/08/08/how-to-open-serial-port-using-rxtx/)

Для роботи необхідно перш за все, отримати ідентифікатор послідовного порту:

CommPortIdentifier portId =
  CommPortIdentifier.getPortIdentifier("COM4");

Далі необхідно відкрити порт, тобто вказати системі, що ви з ним працюєте:

SerialPort serialPort =(SerialPort) portId.open("Name of program", 5000);

У вищенаведеному рядочку ми відсилаємо назву програми і задаємо інтервал очікування відповіді. В даному випадку 5000 мс – 5 секунд. Якщо відповідь не приходить, то отримуємо виняток IOException.

Якщо пройшло все добре, порт знайдено в системі і вам надано ID для роботи з ним, то далі необхідно налаштувати порт для роботи з ним:

int baudRate = 9600; // 9600bps
try {
  serialPort.setSerialPortParams(
    baudRate,
    SerialPort.DATABITS_8,
    SerialPort.STOPBITS_1,
    SerialPort.PARITY_NONE);
} catch (UnsupportedCommOperationException ex) {
  System.err.println(ex.getMessage());
}

Незабуваємо задати контроль потоку:

try {
  serialPort.setFlowControlMode(
        SerialPort.FLOWCONTROL_NONE);
  // OR
  // If CTS/RTS is needed
  //serialPort.setFlowControlMode(
  //      SerialPort.FLOWCONTROL_RTSCTS_IN | 
  //      SerialPort.FLOWCONTROL_RTSCTS_OUT);
} catch (UnsupportedCommOperationException ex) {
  System.err.println(ex.getMessage());
}

Для того щоб записувати і отримувати дані з порта, необхідно отримати потоки вводу-виводу.

OutputStream outStream = serialPort.getOutputStream();
InputStream inStream = serialPort.getInputStream();

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



public class SerialPortHandler {
    private SerialPort serialPort;
    private OutputStream outStream;
    private InputStream inStream;

    public void connect(String portName) throws IOException {
        try {
            // Obtain a CommPortIdentifier object for the port you want to open
            CommPortIdentifier portId =
                    CommPortIdentifier.getPortIdentifier(portName);

            // Get the port's ownership
            serialPort =
                    (SerialPort) portId.open("Demo application", 5000);

            // Set the parameters of the connection.
            setSerialPortParameters();

            // Open the input and output streams for the connection. If they won't
            // open, close the port before throwing an exception.
            outStream = serialPort.getOutputStream();
            inStream = serialPort.getInputStream();
        } catch (NoSuchPortException e) {
            throw new IOException(e.getMessage());
        } catch (PortInUseException e) {
            throw new IOException(e.getMessage());
        } catch (IOException e) {
            serialPort.close();
            throw e;
        }
    }

    /**
     * Get the serial port input stream
     * @return The serial port input stream
     */
    public InputStream getSerialInputStream() {
        return inStream;
    }

    /**
     * Get the serial port output stream
     * @return The serial port output stream
     */
    public OutputStream getSerialOutputStream() {
        return outStream;
    }

    /**
     * Sets the serial port parameters
     */
    private void setSerialPortParameters() throws IOException {
        int baudRate = 57600; // 57600bps

        try {
            // Set serial port to 57600bps-8N1..my favourite
            serialPort.setSerialPortParams(
                    baudRate,
                    SerialPort.DATABITS_8,
                    SerialPort.STOPBITS_1,
                    SerialPort.PARITY_NONE);

            serialPort.setFlowControlMode(
                    SerialPort.FLOWCONTROL_NONE);
        } catch (UnsupportedCommOperationException ex) {
            throw new IOException("Unsupported serial port parameter");
        }
    }
}
Ряд прикладів по використанню RXTX можна знайти тут: http://rxtx.qbang.org/wiki/index.php/Examples

пʼятницю, 21 вересня 2012 р.

Узагальнення (Generics).

Вступ

 В JDK 5.0 з’явилось декілька нових розширень до мови програмування Java. Одне з таких нововведень – це узагальнення (generics).

 Ви можливо вже знайомі з подібними конструкціями з інших мов, найімовірніше з шаблонами (templates) в С++. Якщо так, то ви побачите, що є як подібності так і важливі відмінності. Якщо ви незнайомі з подібними конструкціями звідкілясь, це на краще, ви можете розпочати вивчення з нуля без помилкових уявлень.

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

Ось типовий приклад даного виду:

List myIntList = new LinkedList(); // 1
myIntList.add(new Integer(0)); // 2
Integer x = (Integer) myIntList.iterator().next(); // 3


Приведення в рядку 3 є злегка надокучливим. Зазвичай, програміст знає, який тип даних поміщено у відповідний список. Проте, приведення є важливим. Компілятор може лише гарантувати, що ітератор поверне Object. Щоб забезпечити коректне присвоєння змінній типу Integer, необхідне приведення до типу.

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

А що якщо програміст зможе вказати свої справжні наміри і задати список з конкретними обмеженнями щодо типів даних для розміщення у ньому? Це основна ідея узагальнень. Ось версія програмного фрагменту даного вище з використанням узагальнень:

List<Integer> 
    myIntList = new LinkedList<Integer>(); // 1'
myIntList.add(new Integer(0)); // 2'
Integer x = myIntList.iterator().next(); // 3'


Запримітьте оголошення типу для змінної myIntList. Написавши List<Integer>, зазначаємо, що це не довільний список, а цілочисельний список. Ми говоримо, що List є узагальненим інтерфейсом, що отримує параметр типу – в даному випадку, Integer. Ми також зазначаємо параметр типу, коли створюємо об’єкт списку.

Зазначте, також, що приведення в рядку 3 відкинуто. Зараз, ви мабуть думаєте, що ми перетасували безладдя в коді . Замість приведення до Integer в рядку 3, ми маємо Integer як параметр типу в рядку 1. Проте, тут є значна велика відмінність. Тепер компілятор може провірити коректність типів в програмі під час компіляції. Коли ми кажемо, що myIntList є оголошеним з типом List<Integer>, це говорить нам дещо про змінну myIntList, яка міститеми правильні дані, коли б змінна не застосовувалась і компілятор гарантуватиме це. А приведення ж нам говорить інше, що програміст думає, що це вірно в даній точці коду.

В результаті ж, особливо в великих програмах, це покращує читабельність та робастність (правильну роботу) програми.



Визначення простого узагальнення

Ось невеликий витяг з визначень інтерфейсів List та Iterator в пакеті java.util:
public interface List <E> {
    void add(E x);
    Iterator<E> iterator();
}

public interface Iterator<E> {
    E next();
    boolean hasNext();
}

Усе це повинне бути зрозуміле вам, окрім вмісту в кутових дужках. Це – оголошення формального параметру типу інтерфейсів List and Iterator.

Параметри типу можуть застосовуватись усюди в оголошенні узагальнення, особливо де ви застосовуєте звичайні типи (щоправда існують деякі важливі обмеження; дивість секцію The Fine Print)

У вступі, ви побачили виклик (invocation) узагальненого типу при оголошенні List, як то List<Integer>. При виклику (так званого параметризованного типу) всі входження формального параметра типу (E в данному випадку) заміняються актуальним аргументом типу (в даному випадку Integer).

Ви можливо уявили, що List<Integer> створює версію List де E замінюється на Integer:
public interface IntegerList {
    void add(Integer x);
    Iterator<Integer> iterator();
}

Дане уявлення корисне, проте дещо помилкове.

Воно користе, тому що параметризований тип List<Integer> справді має методи подібні до наведеного представлення.

Проте воно помилкове, тому що власне саме оголошення узагальнень ніколи не доповнюється таким чином. Не існує багатьох копій коду – ні в текстовому, ні в двійковому, ні на диску чи в пам’яті. Якщо ви С++ програміст, ви зрозумієте, що це значна відмінність від шаблонів C++.

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

Параметри типу є аналогами до звичайних параметрів, що використовуються в методах та конструкторах. Коли метод викликається актуальний аргумент підставляється у формальний параметр, і тіло методу виконується. Коли здійснюється оголошення узагальнення, актуальний аргумент типу підставляється у формальний параметр типу.

Зазначимо правила найменування. Ми рекомендуємо щоб ви використовували стислі назви (по можливості простий символ) для параметрів типу. Краще уникати нижнього регістру символі в таких назвах, що робить формальні параметри типу відмінними від звичайних класів та інтерфейсів. Багато контейнерних типів використовують E, для елементів, як в прикладах наведених вище. Ми розглянемо деякі інші умовності пізніше в прикладах.

Generics  - Java Tutorials (Bonus)
Частковий переклад: Volodimirg 

Оригінал повністю: http://docs.oracle.com/javase/tutorial/extra/generics/index.html