Записки программиста Программирование и не только

7Май/130

AndroidSDK, JDK 64-bit и Windows 7 64-bit

Данная зверушка при попытке запуска SDK Manager или инсталятора ругается на отсутствие JDK, который на самом деле есть.

Решение:
Экспортируем и удаляем ветку реестра
[HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\]

И импортируем её в ветку
[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\]
(путем редактирования путей в файле)

Вуаля, Ондроед находит Жабу.

6Дек/120

[Java] Новый IntelliJ IDEA 12

Вышла новая, 12 версия замечательного IDE.
Офф сайт: http://www.jetbrains.com/idea/index.html

Потыкал пока поверхностно.
+ Новая цветовая схема очень приятная глазу.
+ Добавился редактор форм для Дроида, теперь можно не использовать внешние утилиты

- При переходе на редактор форм и обратно закрывается дерево проекта. Раздражает.
- Очень долго искал, как же переключить схему

По клику можно увидеть еще и палитру элементов

6Ноя/123

Еще немного о классах

Класс - это упрощенное описание каких-нибудь реальных (чаще, правда, выдуманных) вещей.

Вот, например, точка. Единственное, что есть у точки - это её координаты в пространстве. Это её параметры.
Как задать точку на плоскости? Надо нарисовать точку и подписать её: M(1,2), т.е. точка с именем M и координатами х = 1, у = 2.
Это её "конструктор". Т.е. мы "сконструировали" конкретную точку М с конкретными координатами.

На языке Java это будет выглядеть так:

class Point {                       //общее описание точек
    private double x, y;            //параметры точки

    Point(double ax, double ay) {   //конструктор
        x = ax;
        y = ay;
    }
}

Сконструируем точку

Point M = new Point(1, 2);

В данном классе задан один конкретный конструктор. Из-за этого каждая создаваемая точка будет ожидать, что при создании ей назначат координаты.

Т.е. данная строчка работать не будет:

Point M = new Point(); //пустые параметры
//или
Point M = new Point(1); //неполные параметры

Дальше. Класс включает в себя не только параметры, но и методы.
Что такое метод класса? Это вопрос, который мы можем задать или указание, что-либо сделать. Причем и вопрос и указания нужно адресовать уже конкретной сконструированной точке.

Научим точку сообщать свои координаты и перемещаться по одной из координатных осей:

class Point {                       //общее описание точек
    private double x, y;            //параметры точки

    Point(double ax, double ay) {   //конструктор
        x = ax;
        y = ay;
    }

    //запрос координаты Х
    double getX() {
        return x;
    }

    //запрос координаты Y
    double getY() {
        return y;
    }

    //указание сдвинуться по оси X на расстояние dx
    void moveX(double dx) {
        x = x + dx;
    }

    //указание сдвинуться по оси Y на расстояние dy
    void moveY(double dy) {
        y = y + dy;
    }
}

//использование
public class Main {
    public static void main(String[] args) {
        //создаём точку
        Point M = new Point(1, 2);
        //спрашиваем у созданной точки - скажи нам, точка М свои координаты X и Y
        System.out.println("Текущие координаты точки М: x = " + M.getX() + ", у = " + M.getY());
        System.out.println("Сдвинем точку по оси Х на 5 и по оси У на -1");
        //просим точку М - точка М, сдвинься на 5 по оси X
        M.moveX(5);
        //просим точку М - точка М, сдвинься на -1 по оси Y
        M.moveY(-1);
        //спрашиваем у точки М - скажи нам, точка М свои координаты X и Y
        System.out.println("Новые координаты точки М: x = " + M.getX() + ", у = " + M.getY());
    }
}

Еще раз повторю - методы вызываются у конкретной точки - сконструированного объекта.
Класс - это просто заготовка, шаблон, описание.
Т.е. пока точка не создана - мы как бы задумываем её нарисовать.
А вот когда сказали new, это значит, что мы нарисовали конкретную точку в конкретном месте координатной плоскости.

Записи вида

Point.getX();
//или
Point.moveX(5);

Не имеют смысла, потому что описание ничего не знает.
Это как если спросить у человека - "скажи мне координаты точки?"
Логично будет подумать, что он ответит - "какой именно?"
Также делает и компилятор.

1Ноя/120

[Java] this, super

1. Как-то я писал, что не понимаю, зачем вызывать конструктор в конструкторе через this.
Разобрался.
Это для укорачивания кода.

Типа можно так:

class Point {
    int х, у; 
    Point(int х, int у) { 
        this.x = х; 
        this.у = у; 
    }
  
    Point() { 
        this.x = -1; 
        this.у = -1; 
    } 
}

А можно короче:

class Point {
    int х, у; 
    Point(int х, int у) { 
        this.x = х; 
        this.у = у; 
    }
  
    Point() { 
        this(-1, -1);   //конструктор вызывает другой конструктор
    } 
}

Есть одно ограничение на вызов одного конструктора из другого. Такой вызов должен быть первым оператором в вызывающем конструкторе.

2. super
Данная функция служит для вызова конструктора базового класса. Используется, если базовый класс, например, не имеет конструктора по умолчанию

Пример

class X {
   X(int a) { ... }
   ...
}

class Y extends X {
    Y() {
        super(0);
        ...
    }
   ...
}

Также super может использоваться и для явного вызова методов базового класса. Это необходимо, если некоторый метод базового класса был переопределен в порожденном классе.

Пример

class Base {
    int x = 1;
    long y;
    Base(long y) {
        this.y = y;
    }
    Base() {
         this(0);   // вызов конструктора Base(long y)
    }
    public long f() {
        return x*y;
    }
}

class Derived extends Base {
    String name = "";
    Derived(String name, long par) {
          super(par);   // вызов конструктора Base(long y)
          this.name = name;
    }
    public long g(int r) {
         return r+super.f();   // вызов метода f() класса Base
    }
    public long f() {
          x++;
          return 2*y;
    }
}
Метки записи: Нет комментариев
23Окт/1214

[Java] Структуры данных. Список

Список, для начала, возьмем простой линейный односвязный.

Список есть динамическая структура данных.

Каждый элемент списка содержит в себе данные и указатель на следующий элемент.
Возьмем простой вариант данных в списке - просто целые числа.
Элемент списка:

class ListElement {
    ListElement next;    // указатель на следующий элемент
    int data;            // данные
}

Сам список также будет классом.
Будет содержать в себе указатель на первый элемент списка и на последний - head и tail.
Также реализуем методы:
1. Добавление элемента в конец списка
2. Добавление элемента в начало списка
3. Удаление элемента по значению
4. Печать всего списка

class List {
    private ListElement head;       // указатель на первый элемент
    private ListElement tail;       // указатель последний элемент

    void addFront(int data)           //добавить спереди
    {
        ListElement a = new ListElement();  //создаём новый элемент
        a.data = data;              //инициализируем данные. 
                                    // указатель на следующий элемент автоматически инициализируется как null
        if(head == null)            //если список пуст
        {                           //то указываем ссылки начала и конца на новый элемент
            head = a;               //т.е. список теперь состоит из одного элемента
            tail = a;
        }
        else {
            a.next = head;          //иначе новый элемент теперь ссылается на "бывший" первый
            head = a;               //а указатель на первый элемент теперь ссылается на новый элемент 
        }
    }

    void addBack(int data) {          //добавление в конец списка
        ListElement a = new ListElement();  //создаём новый элемент
        a.data = data;
        if (tail == null)           //если список пуст
        {                           //то указываем ссылки начала и конца на новый элемент
            head = a;               //т.е. список теперь состоит из одного элемента
            tail = a;
        } else {
            tail.next = a;          //иначе "старый" последний элемент теперь ссылается на новый
            tail = a;               //а в указатель на последний элемент записываем адрес нового элемента
        }
    }

    void printList()                //печать списка
    {
        ListElement t = head;       //получаем ссылку на первый элемент   
        while (t != null)           //пока элемент существуе
        {
            System.out.print(t.data + " "); //печатаем его данные
            t = t.next;                     //и переключаемся на следующий
        }
    }

    void delEl(int data)          //удаление элемента
    {
        if(head == null)        //если список пуст - 
            return;             //ничего не делаем

        if (head == tail) {     //если список состоит из одного элемента
            head = null;        //очищаем указатели начала и конца
            tail = null;
            return;             //и выходим
        }

        if (head.data == data) {    //если первый элемент - тот, что нам нужен
            head = head.next;       //переключаем указатель начала на второй элемент
            return;                 //и выходим
        }

        ListElement t = head;       //иначе начинаем искать
        while (t.next != null) {    //пока следующий элемент существует
            if (t.next.data == data) {  //проверяем следующий элемент
                if(tail == t.next)      //если он последний
                {
                    tail = t;           //то переключаем указатель на последний элемент на текущий
                }
                t.next = t.next.next;   //найденный элемент выкидываем
                return;                 //и выходим
            }
            t = t.next;                //иначе ищем дальше
        }
    }
}

Тонкости Java-реализации:
Освобождать память не надо - достаточно чтобы на выделенные по new участок не было указателей и тогда сборщик мусора сам её почистит.
Все переменные являются ссылками на объекты, так что нет заморочек с разыменование указателей и передачи адресов.

Испоьзование:

public class Main {
    public static void main(String[] args) {
        List ml = new List();
        ml.addBack(1);
        ml.addBack(2);
        ml.addBack(3);
        ml.addFront(6);

        ml.printList();
        System.out.println();

        ml.delEl(6);
        ml.delEl(5);
        ml.delEl(12);
        ml.delEl(2);

        ml.printList();
        System.out.println();
    }
}

Распечатает:

6 1 2 3 
1 3 

А теперь немного магии :)
С целочисленным списком скучно. Сделаем элемент списка универсальным!
Это можно сделать при помощи "шаблонизации"
Причем какой именно тип - будет автоматически определяться при сборке!

class ListElement<E> {
    ListElement next;
    E data;
}

class List<E> {
    private ListElement head;       // указатель на первый элемент
    private ListElement tail;       // указатель последний элемент

    void addFront(E data)           //добавить спереди
    {
        ListElement a = new ListElement();  //создаём новый элемент
        a.data = data;              //инициализируем данные. 
                                    // указатель на следующий элемент автоматически инициализируется как null
        if(head == null)            //если список пуст
        {                           //то указываем ссылки начала и конца на новый элемент
            head = a;               //т.е. список теперь состоит из одного элемента
            tail = a;
        }
        else {
            a.next = head;          //иначе новый элемент теперь ссылается на "бывший" первый
            head = a;               //а указатель на первый элемент теперь ссылается на новый элемент 
        }
    }

    void addBack(E data) {          //добавление в конец списка
        ListElement a = new ListElement();  //создаём новый элемент
        a.data = data;
        if (tail == null)           //если список пуст
        {                           //то указываем ссылки начала и конца на новый элемент
            head = a;               //т.е. список теперь состоит из одного элемента
            tail = a;
        } else {
            tail.next = a;          //иначе "старый" последний элемент теперь ссылается на новый
            tail = a;               //а в указатель на последний элемент записываем адрес нового элемента
        }
    }

    void printList()                //печать списка
    {
        ListElement t = head;       //получаем ссылку на первый элемент   
        while (t != null)           //пока элемент существуе
        {
            System.out.print(t.data + " "); //печатаем его данные
            t = t.next;                     //и переключаемся на следующий
        }
    }

    void delEl(E data)          //удаление элемента
    {
        if(head == null)        //если список пуст - 
            return;             //ничего не делаем

        if (head == tail) {     //если список состоит из одного элемента
            head = null;        //очищаем указатели начала и конца
            tail = null;
            return;             //и выходим
        }

        if (head.data == data) {    //если первый элемент - тот, что нам нужен
            head = head.next;       //переключаем указатель начала на второй элемент
            return;                 //и выходим
        }

        ListElement t = head;       //иначе начинаем искать
        while (t.next != null) {    //пока следующий элемент существует
            if (t.next.data == data) {  //проверяем следующий элемент
                if(tail == t.next)      //если он последний
                {
                    tail = t;           //то переключаем указатель на последний элемент на текущий
                }
                t.next = t.next.next;   //найденный элемент выкидываем
                return;                 //и выходим
            }
            t = t.next;                //иначе ищем дальше
        }
    }
}

Изменения минимальны.
Вместо int пишем E. И к имени класса добавляем

<E>

Зато теперь можно сделать вот так:

public class Main {
    public static void main(String[] args) {
        List ml = new List();
        ml.addBack(1.0);
        ml.addBack(2);
        ml.addBack("brrr");
        ml.addFront(6);

        ml.printList();
        System.out.println();
    }
}

В выводе будет:

6 1.0 2 brrr 
22Окт/120

[Java] Проект с пакетами

Предыдущий пример получился страшным и сложным.
Сделаем проще.

1. Создаём обычный проект.
2. Правой кнопкой мыши на папку src - New->Package. Пишем alg
3. Правой кнопкой на alg - New->Java Class. Пишем sort
4. Правой кнопкой мыши на папку src - New->Package. Пишем Hello
5. В файле с классом Hello пишем

import alg.sort;

Всё. Пакет создан и импортирован.

Метки записи: , Нет комментариев
19Окт/120

[Java] Библиотеки и пакеты

Библиотека Java — это сборник классов. Если программе нужен какой-то класс, то нужно подключить библиотеку, в которой этот класс находится.

Пакеты — это механизм, который служит как для работы с пространством имен, так и для ограничения видимости. У каждого файла .java есть 4 внутренних части.
Общая форма исходного файла Java:

  • одиночный оператор package (необязателен)
  • любое количество операторов import (необязательны)
  • одиночное объявление открытого (public) класса
  • любое количество закрытых (private) классов пакета (необязательны)

Оператор package
Сообщает транслятору, в каком пакете должны определяться содержащиеся в данном файле классы. Пакеты задают набор раздельных пространств имен, в которых хранятся имена классов. Если оператор package не указан, классы попадают в безымянное пространство имен, используемое по умолчанию. Если объявляется класс, принадлежащий определенному пакету, например,

package java.awt.image; 

то и исходный код этого класса должен храниться в каталоге java/awt/image

Трансляция классов в пакетах
При попытке поместить класс в пакет, вы сразу натолкнетесь на жесткое требование точного совпадения иерархии каталогов с иерархией пакетов. Нельзя переименовать пакет, не переименовав каталог, в котором хранятся его классы. Эта трудность видна сразу, но есть и менее очевидная проблема.

Оператор import
После оператора package, но до любого определения классов в исходном Java-файле, может присутствовать список операторов import. Общая форма оператора import такова:

import пакет1 [.пакет2].(имякласса|*); 

Здесь пакет1 — имя пакета верхнего уровня, пакет2 — это необязательное имя пакета, вложенного в первый пакет и отделенное точкой. И, наконец, после указания пути в иерархии пакетов, указывается либо имя класса, либо метасимвол звездочка. Звездочка означает, что, если Java-транслятору потребуется какой-либо класс, для которого пакет не указан явно, он должен просмотреть все содержимое пакета со звездочкой вместо имени класса.

import java.util.Date
import java.io.*;

Замечание: Использование * может существенно увеличить время трансляции программы. На скорость работы и размер программы не влияет.

Если в двух пакетах, подключаемых с помощью формы оператора import со звездочкой, есть классы с одинаковыми именами, однако вы их не используете, транслятор не отреагирует. А вот при попытке использовать такой класс, вы сразу получите сообщение об ошибке, и вам придется переписать операторы import, чтобы явно указать, класс какого пакета вы имеете ввиду.

class MyDate extends Java.util.Date { }

Уровни доступа
В языке Java имеется три уровня доступа, определяемых ключевыми словами: private (закрытый), public (открытый) и protected (защищенный).

private модификатор отсутствует private protected protected public
тот же класс да да да да да
подкласс в том же пакете нет да да да да
независимый класс в том же пакете нет да нет да да
подкласс в другом пакете нет нет да да да
независимый класс в другом пакете нет нет нет нет да
  • Элемент, объявленный public, доступен из любого места
  • Все, что объявлено private, доступно только внутри класса, и нигде больше
  • Если у элемента вообще не указан модификатор уровня доступа, то такой элемент будет виден из подклассов и классов того же пакета. (по умолчанию)
  • Чтобы элемент был доступен извне пакета, но только подклассам того класса, которому он принадлежит, нужно объявить такой элемент protected
  • Чтобы элемент был доступен только подклассам, причем независимо от того, находятся ли они в данном пакете или нет — используйте комбинацию private protected

Небольшой пример
Создаём новый проект и добавляем в папку src новый Пакет "alg" (New -> Package)
В папке пакета создаем класс sort (New -> Java Class)

В новом классе пишем наши уже знакомые функции сортировки с обязательным модификатором доступа public.
В итоге должно получиться следующее:

package alg;

public class sort {
    public void gnom(int[] mas) {
        int p = 2;
        int i = 1;
        for (; i < mas.length; ) {
            if (mas[i - 1] <= mas[i]) {
                p++;
                i = p;
            } else {
                int a = mas[i];
                mas[i] = mas[i - 1];
                mas[i - 1] = a;
                i--;
                if (i == 0) {
                    p++;
                    i = p;
                }
            }
        }
    }

    public void gnom(double[] mas) {
        int p = 2;
        int i = 1;
        for (; i < mas.length; ) {
            if (mas[i - 1] <= mas[i]) {
                p++;
                i = p;
            } else {
                double a = mas[i];
                mas[i] = mas[i - 1];
                mas[i - 1] = a;
                i--;
                if (i == 0) {
                    p++;
                    i = p;
                }
            }
        }
    }
}

Сохраняем. Создаём еще один проект. Или открываем старый.

Добавляем созданый модуль alg также как описывалось в предыдущей статье

Делаем новый класс и пишем туда первой строчкой:

import alg.sort;

Всё. Мы подключили наш пакет с функциями сортировки!
Дописываем функцию main

В итоге:

import alg.sort;

class Hello {
    public static void main(String args[]) {
        int masI[] = {5, 5, 9, 8, 0, -5, 9, 10};
        double masD[] = {5.9, 5.7, 9.6, 8.9, 0.7, -5.6, 9.9, 10.0};
        sort s1 = new sort();
        s1.gnom(masI);
        s1.gnom(masD);
}
18Окт/120

Java API

Очень полезная ссылка.
Документация по всем стандартным классам языка
http://docs.oracle.com/javase/7/docs/api/index.html

Навигация как всегда на высоте.

Искать либо в индексе по имени функции/класса/пакета (справа вверху Index)
Либо по имени класса (слева All Classes)

Метки записи: Нет комментариев
11Окт/121

[Java] Подключаем чужие исходники в IntelliJ IDEA

По следам статьи

В статье предлагается попробовать себя в роли разработчика Рогалика (Rogue-like) - игрушки, в которых вместо графики используется ASCII символы.

Разберем, как же подключить правильно сторонние библиотеки, чтобы всё работало.

Необходимо скачать две (а не одну, как сказано в статье) библиотеки: jcurses и libjcsi
Для скачивания исходников для libjcsi понадобится SVN клиент (надо будет написать про них немного)
Или можно взять тут: http://axis.bplaced.net/wp-content/uploads/2012/10/libjcsi.zip
Качаем исходники (Source) и распаковываем их куда-нибудь. У меня это C:\JavaLib\jcurses и C:\JavaLib\libjcsi

Создаём новый модуль (или открываем старый) и добавляем туда новый класс Rogalic.

Заходим в File -> Project Structure
Раздел Module, вкладка Dependencies.

Жмём + -> Module

Выбираем Create module from existing sources и жмем "..."

Находим исходники

Жмем Next до упора и потом Finish (всё по умолчанию, все окна практически пустые)

Повторяем тоже самое для libjcsi

Теперь добавляем зависимости.
Жмем на модуль Net и затем справа на "+" -> Module dependency

Выбираем там Jcurses

Теперь выбираем наш родной модуль и повторяем операцию. Только теперь выбираем Net

Закрываем всё. Должно получиться как-то вот так

Теперь пишем в классе Rogalic следующее:

import java.util.ArrayList;
import java.util.Properties;

import net.slashie.libjcsi.CSIColor;
import net.slashie.libjcsi.CharKey;
import net.slashie.libjcsi.ConsoleSystemInterface;
import net.slashie.libjcsi.wswing.WSwingConsoleInterface;

public class Rogalic {
    private static Rogalic instance;
    private static ConsoleSystemInterface csi;
    private boolean stop;
    private int x,y;
    public static void main(String[] args)
    {
        Properties text = new Properties();
        text.setProperty("fontSize", "15");
        text.setProperty("font", "roguelike.ttf");
        csi = new WSwingConsoleInterface("RogueLike", text);
        instance = new Rogalic();
        instance.run();
    }

    public void run()
    {
        stop = false;
        x = 0; y = 0;
        while (!stop)
        {
            csi.cls();
            csi.print(x,y,'@', CSIColor.WHITE); // отрисовка игрока
            csi.refresh();
            handleKeys(); // обработка клавиатуры
        }
        System.exit(0);
    }

    private void handleKeys(){
        CharKey dir = csi.inkey();
        if(dir.isUpArrow()&& (y-1 >= 0)){
            y--;
        }
        if(dir.isDownArrow() && (y+1 < 25)){
            y++;
        }
        if(dir.isLeftArrow() && (x-1 >= 0)){
            x--;
        }
        if(dir.isRightArrow() && (x+1 < 80)){
            x++;
        }
        if(dir.code == CharKey.Q){
            stop = true;
        }
    }
}

Запускаем!
Если всё сделано правильно, то должно появиться окно с довольной "собакой (@) - главным героем всех Рогаликов. Ну, почти всех :)
Им можно шаволить курсорными стрелками.

Метки записи: , , 1 комментарий
9Окт/120

[Java] Консольный ввод/вывод

Любимое занятие - написание консольных диалоговых приложений :)

Во время работы метода System.in.read возможно возникновение исключений. Поэтому заключаем фрагмент кода, выполняющий эту функцию, в блок try-catch.

import java.util.StringTokenizer;    //добавляем класс для обработки строк

public class ioTest {
    public static void main(String args[]) {
        byte bKbd[] = new byte[256];		// буфер
        String szStr = "";					// итоговая строка
        StringTokenizer st;					// преобразователь

        System.out.print("Print here: ");
        try
        {
            // Метод read считывает символы с клавиатуры и записывает
			// их в массив байт с именем bKbd. Этот метод возвращает 
			// управление, когда пользователь закончил ввод и нажал 
			// клавишу <Enter>. При этом в переменную iCnt записывается 
			// количество прочитанных символов.
            int iCnt = System.in.read(bKbd);

            // Строка String содержит 16-разрядные символы Unicode.
            // Чтобы преобразовать массив байт в строку Unicode, мы
			// задаем значение старшего байта во втором параметре 
			// конструктора, равное нулю.
            szStr = new String(bKbd, 0, iCnt);

            // Удаление символов возврата каретки и перевода строки
			// выполняется при помощи класса StringTokenizer, предназначенного 
			// для разбора текстовых строк. Создавая объект этого класса,
			// мы передаем конструктору через второй параметр список 
			// символов-разделителей
            st = new StringTokenizer(szStr, "\r\n");

            // Метод nextElement возвращает первый элемент строки до разделителя,
			// то есть в нашем случае всю строку до символов возврата каретки и 
			// перевода строки.
            szStr = new String((String)st.nextElement());
        }
        catch(Exception ex)
        {
            System.out.println(ex.toString());
        }

        //чисто для красоты - разделяем строку на слова
        String v[] = szStr.split(" ");
        for(int j = 0; j < v.length; j++)
        {
            System.out.println(j + "-е слово: " + v[j]);
        }
    }
}

Как пользоваться:
Запускаем, внизу в консоли тыкаем в белое поле и набираем строку. Ввод заканчиваем нажатием клавиши

Как-то вот так:

Print here: Это странная консольная программа!
0-е слово: Это
1-е слово: странная
2-е слово: консольная
3-е слово: программа!

Process finished with exit code 0
Метки записи: , Нет комментариев