<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>C, PHP, VB, .NET &#187; Java</title>
	<atom:link href="http://www.cphpvb.net/category/java/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.cphpvb.net</link>
	<description>блогът на Филип Петров</description>
	<lastBuildDate>Wed, 28 Jul 2010 20:26:01 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Задачи от изпит 10.12.2009г.</title>
		<link>http://www.cphpvb.net/java/4694-%d0%b7%d0%b0%d0%b4%d0%b0%d1%87-%d0%be%d1%82-%d0%b8%d0%b7%d0%bf%d0%b8%d1%82-10-12-2009%d0%b3/</link>
		<comments>http://www.cphpvb.net/java/4694-%d0%b7%d0%b0%d0%b4%d0%b0%d1%87-%d0%be%d1%82-%d0%b8%d0%b7%d0%bf%d0%b8%d1%82-10-12-2009%d0%b3/#comments</comments>
		<pubDate>Sun, 13 Dec 2009 09:34:05 +0000</pubDate>
		<dc:creator>Филип Петров</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.cphpvb.net/?p=4694</guid>
		<description><![CDATA[Задача 1. Напишете интерфейс “Геометрична Фигура” (Figure), в който се дефинират действията “сравнение на текущата фигура с друга подадена като параметър” (метод equals, който трябва да връща стойност от тип boolean) и “отпечатване на информацията за фигурата на екрана в конзолата” (print).
Задача 2. Напишете абстрактен клас “равнинна фигура с три определящи параметри” (PlainFigure), който имплементира [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Задача 1</strong>. Напишете интерфейс “Геометрична Фигура” (Figure), в който се дефинират действията “сравнение на текущата фигура с друга подадена като параметър” (метод equals, който трябва да връща стойност от тип boolean) и “отпечатване на информацията за фигурата на екрана в конзолата” (print).<span id="more-4694"></span></p>
<p><strong>Задача 2</strong>. Напишете абстрактен клас “равнинна фигура с три определящи параметри” (PlainFigure), който имплементира интерфейс Figure. Член променливи на класа са трите размерности на фигурата (d1, d2 и d3 от тип double) с права за достъп “public” и “final” (тоест те са константи, които ще бъдат инициализирани по-късно по време на изпълнение).</p>
<p>a) Напишете конструктор с подадени параметри, който подрежда входните параметри по големина и така с тях инициализира размерностите на фигурата (тоест d1 трябва да е с най-малка стойност, d2 със средна и d3 с най-голяма).</p>
<p>б) Реализирайте методите print (отпечатва размерностите в конзолата) и еquals (проверява дали размерностите на текущия обект и на подадения са равни).</p>
<p>в) Дефинирайте абстрактни методи “лице” (getArea() с връщана стойност от тип double) и “периметър” (getPerimeter() с връщана стойност от тип double).</p>
<p><strong>Задача 3</strong>. Напишете клас “Триъгълник” (Triangle), който наследява класа PlainFigure, като за трите определящи параметъра се приемат страните на триъгълника.</p>
<p>а) Напишете конструктора (с подадени параметри) така, че ако трите подадени параметъра не могат да образуват триъгълник (условието за съществуване на триъгълник е страните да са с положителна дължина и да са изпълнени: d1+d2&gt;d3, d2+d3&gt;d1 и d1+d3&gt;d2), то да бъде върнато изключение с име “TriangleException”.</p>
<p>б) Реализирайте метода getPerimeter() (периметърът на триъгълник е P=d1+d2+d3).</p>
<p>в) Реализирайте метода getArea() (лицето на триъгълник по Хереоновата формула е S = sqrt[p.(p-d1).(p-d2).(p-d3)], където p = P/2 е полупериметърът на триъгълника).</p>
<p><strong>Задача 4</strong>. Реализирайте статичен метод в клас Triangle, който показва дали два триъгълника (обекти от клас Triangle подадени като входни параметри) са подобни (тоест коефициентите на пропорционалност на всяка двойка от страните на триъгълниците трябва да са равни).</p>
<p><strong>Задача 5</strong>. Реализирайте класа на изключението TriangleException.</p>
<p><em>Забележка 1: Независимо кои две напълно решени задачи носят оценка среден </em><em>(3</em><em>). Всяка допълнително решена задача добавя една единица към оценката.</em></p>
<p><em>Забележка 2: Не си губете времето да правите </em><em>get и </em><em>set методи – размерностите са константи </em><em>(т.е. </em><em>set методи не могат да съществуват</em><em>) и са с</em><em> ниво на достъп </em><em>public (т.е. </em><em>get методи са безсмислени</em><em>). Също така не пишете конструктори по подразбиране – в задачите това не се изисква.</em></p>
]]></content:encoded>
			<wfw:commentRss>http://www.cphpvb.net/java/4694-%d0%b7%d0%b0%d0%b4%d0%b0%d1%87-%d0%be%d1%82-%d0%b8%d0%b7%d0%bf%d0%b8%d1%82-10-12-2009%d0%b3/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Примерна задача 4</title>
		<link>http://www.cphpvb.net/java/4639-%d0%bf%d1%80%d0%b8%d0%bc%d0%b5%d1%80%d0%bd%d0%b0-%d0%b7%d0%b0%d0%b4%d0%b0%d1%87%d0%b0-4/</link>
		<comments>http://www.cphpvb.net/java/4639-%d0%bf%d1%80%d0%b8%d0%bc%d0%b5%d1%80%d0%bd%d0%b0-%d0%b7%d0%b0%d0%b4%d0%b0%d1%87%d0%b0-4/#comments</comments>
		<pubDate>Sun, 06 Dec 2009 16:11:07 +0000</pubDate>
		<dc:creator>Филип Петров</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.cphpvb.net/?p=4639</guid>
		<description><![CDATA[Задача 1: Създайте клас „Картина”, в който се описва името на картината, нейният автор, цена, година на създаване и уникален номер. Дефинирайте конструктори по подразбиране (данните се четат от клавиатурата) и чрез подадени параметри. Направете необходимите get и set методи. Напишете метод, който отпечатва информацията за картината в подходящ вид в конзолата.
Задача 2: Създайте клас „Картини”, [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Задача 1</strong>: Създайте клас „Картина”, в който се описва името на картината, нейният автор, цена, година на създаване и уникален номер. Дефинирайте конструктори по подразбиране (данните се четат от клавиатурата) и чрез подадени параметри. Направете необходимите get и set методи. Напишете метод, който отпечатва информацията за картината в подходящ вид в конзолата.</p>
<p><strong>Задача 2</strong>:<span id="more-4639"></span> Създайте клас „Картини”, който съдържа списък от обекти от клас „Картина”. Напишете необходимите конструктори (инициализират празен списък), get и set методи. Напишете метод за добавяне на картина към списъка (подава се обект „Картина” като входен параметър). Направете проверка за дублиране – ако такава картина вече има в списъка, то да се върне изключение или да се отпечата текст в конзолата. Напишете метод, който изтрива картина по подаден уникален номер.</p>
<p><strong>Задача 3</strong>: Напишете метод за отпечатване на информацията за картините на даден автор (името му се подава като параметър, а ако се подаде празен низ да се отпечатва информацията за всички картини).</p>
<p><strong>Задача 4</strong>: В клас „Картини” напишете метод, който намира най-скъпата или най-скъпите картини (ако са повече от една с равни цени) и отпечатва информация за тях на екрана. Напишете метод, който връща средната цена на картините от даден автор (името на автора се подава като параметър, като ако се подаде празен String, то да се върне средната цена на картините на всички автори).</p>
<p><strong>Задача 5</strong>: Създайте main метод, който демонстрира функционалността на задачи от 1 до 4, като изписва меню на екрана, например:</p>
<p><em>1. Добави картина<br />
2. Изтрий картина<br />
3. Информация за картина<br />
4. &#8230;<br />
5. Изход</em></p>
<p>При избор на дадена опция потребителят да бъде прехвърлен в подменю, в което се извършва избраната операция, а след завършването й – да се връща обратно в главното меню.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cphpvb.net/java/4639-%d0%bf%d1%80%d0%b8%d0%bc%d0%b5%d1%80%d0%bd%d0%b0-%d0%b7%d0%b0%d0%b4%d0%b0%d1%87%d0%b0-4/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Примерна задача 3</title>
		<link>http://www.cphpvb.net/java/4582-%d0%bf%d1%80%d0%b8%d0%bc%d0%b5%d1%80%d0%bd%d0%b0-%d0%b7%d0%b0%d0%b4%d0%b0%d1%87%d0%b0-3/</link>
		<comments>http://www.cphpvb.net/java/4582-%d0%bf%d1%80%d0%b8%d0%bc%d0%b5%d1%80%d0%bd%d0%b0-%d0%b7%d0%b0%d0%b4%d0%b0%d1%87%d0%b0-3/#comments</comments>
		<pubDate>Sat, 28 Nov 2009 21:45:30 +0000</pubDate>
		<dc:creator>Филип Петров</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.cphpvb.net/?p=4582</guid>
		<description><![CDATA[Задача 1: Създайте клас &#8222;работник на хонорар&#8220;, в който са дефинирани лично, фамилно име, заплащане (левове за час) и личен номер (id) от тип int. Направете конструктори по подразбиране (въвежда се информацията от клавиатурата) и чрез подадени параметри. Създайте необходимите get и set методи. Напишете метод, който отпечатва информация за работника на екрана в подходящ [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Задача 1</strong>: Създайте клас &#8222;работник на хонорар&#8220;, в който са дефинирани лично, фамилно име, заплащане (левове за час) и личен номер (id) от тип int. Направете конструктори по подразбиране (въвежда се информацията от клавиатурата) и чрез подадени параметри. Създайте необходимите get и set методи. Напишете метод, който отпечатва информация за работника на екрана в подходящ вид. Напишете метод, който по подаден параметър &#8222;брой изработени часове&#8220; връща парите, които работникът трябва да получи.<span id="more-4582"></span></p>
<p><strong>Задача 2</strong>: Създайте клас &#8222;обикновен работник&#8220;, който наследява класа &#8222;работник на хонорар&#8220;, в който се добавя информация за броя часове, които работникът е изработил за текущата седмица. Направете конструктори по подразбиране (стандартно работниците работят 40 часа седмично) и чрез подадени параметри. Създайте необходимите get и set методи. Напишете метод, който връща седмичната заплата на работника. Всички часове до 40-тия се пресмятат умножени с коефициент 1.0, а наднормените часове се заплащат с коефициент 1.5. Предефинирайте методът за отпечатване на информацията за работника така, че да се отпечата на екрана и новата информация (седмичната му заплата). Направете проверки за валидност на данните &#8211; един работник НЕ може да работи повече от 84 часа седмично. Ако се опитате да създадете такъв, то трябва да се хвърли изключение.</p>
<p><strong>Задача 3</strong>: Създайте клас &#8222;работник с възможност за нощна смяна&#8220;, който наследява класа &#8222;обикновен работник&#8220; и добавя информация за броят дни, в които работникът е работил нощем. Направете конструктори по подразбиране (стандартно работниците не работят нощни смени) и чрез подадени параметри. Създайте необходимите get и set методи. Предефинирайте метода връщаш седмичната заплата така, че нощните смени да бъдат заплатени по коефициент 1.5 (счита се, че продължителността на една нощна смяна е 8 работни часа). Наднормените часове не могат да бъдат нощна смяна (т.е. не се умножават два пъти с коефициента). Направете проверки за валидност на данните &#8211; ако работник има поне една нощна смяна, то той вече не може да работи повече от 72 часа седмично, а броят на нощните му смени за седмица не може да е повече от 5.</p>
<p><strong>Задача 4</strong>: Създайте клас &#8222;списък с работници&#8220;, в който се пазят списъци за работниците на хонорар, обикновените работници и работниците с възможност за нощна смяна. Направете конструктор по подразбиране, който инициализира празни списъци. Направете метод, който по подаден параметър един от трите типа работници го добавя в съответния списък. Трябва да се направи проверка дали служител с такъв уникален номер (id) вече съществува (ако да &#8211; да се &#8222;хвърли&#8220; exception). Направете метод, който изтрива работник по подадена информация &#8222;уникален номер на служител&#8220;. Ако такъв работник не съществува &#8211; да се хвърли изключение. Направете метод, който по подаден параметър &#8222;уникален номер&#8220; отпечатва информацията за работника. Ако той е &#8222;обикновен работник&#8220; или &#8222;работник с възможност за нощна смяна&#8220; да се отпечата и неговата текуща седмична заплата.<br />
Направете метод за отпечатване на информацията за всички работници на хонорар.<br />
Направете метод за отпечатване на информацията за всички обикновени работници.<br />
Направете метод за отпечатване на информацията за всички работници с възможност за нощна смяна.<br />
Направете метод за отпечатване на информацията на всички налични работници, като ги раздели по групи.<br />
Направете метод, който намира средната седмична заплата на всички работници (тези, които са на хонорар не участват в изчисленията).</p>
<p><strong>Задача 5</strong>: Демонстрирайте функционалността на създадените класове чрез написване на main метод.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cphpvb.net/java/4582-%d0%bf%d1%80%d0%b8%d0%bc%d0%b5%d1%80%d0%bd%d0%b0-%d0%b7%d0%b0%d0%b4%d0%b0%d1%87%d0%b0-3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Примерна задача 2</title>
		<link>http://www.cphpvb.net/java/4486-%d0%bf%d1%80%d0%b8%d0%bc%d0%b5%d1%80%d0%bd%d0%b0-%d0%b7%d0%b0%d0%b4%d0%b0%d1%87%d0%b0-2/</link>
		<comments>http://www.cphpvb.net/java/4486-%d0%bf%d1%80%d0%b8%d0%bc%d0%b5%d1%80%d0%bd%d0%b0-%d0%b7%d0%b0%d0%b4%d0%b0%d1%87%d0%b0-2/#comments</comments>
		<pubDate>Fri, 06 Nov 2009 12:25:36 +0000</pubDate>
		<dc:creator>Филип Петров</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.cphpvb.net/?p=4486</guid>
		<description><![CDATA[1. Да се създаде клас &#8222;точка&#8220; с член променливи &#8222;координати по осите x и y&#8220;. Създайте необходимите конструктори и get методи. Защитете координатите така, че да не могат да бъдат променяни след като бъдат инициализирани. Създайте метод отпечатващ координатите на точката. Създайте статичен метод, който сравнява координатите на две точки и показва дали те съвпадат:
class [...]]]></description>
			<content:encoded><![CDATA[<p><strong>1. Да се създаде клас &#8222;точка&#8220;</strong> с член променливи &#8222;координати по осите x и y&#8220;. Създайте необходимите конструктори и get методи. Защитете координатите така, че да не могат да бъдат променяни след като бъдат инициализирани. Създайте метод отпечатващ координатите на точката. Създайте статичен метод, който сравнява координатите на две точки и показва дали те съвпадат:<span id="more-4486"></span></p>
<pre>class Point{
  final private double x;
  final private double y;
  public Point(){
    this.x = 0;
    this.y = 0;
  }
  public Point(double x, double y){
    this.x = x;
    this.y = y;
  }
  public double getX(){
    return this.x;
  }
  public double getY(){
    return this.y;
  }
  public String show(){
    return ("("+this.x+", "+this.y+")");
  }
  public static boolean equals(Point x, Point y){
    if( (x.getX()==y.getX()) &amp;&amp; (x.getY() == y.getY()) ) return true;
    else return false;
  }
}</pre>
<p><strong>2. Да се създаде клас „множество”</strong> съдържащ списък от точки. Създайте член-методи за добавяне на точка към множеството, добавяне на множество точки към множеството, изтриване на точка от множеството и за отпечатване на екрана точките от множеството. Създайте необходимите изключения (в едно множество НЕ може да има дублиращи се точки; НЕ можем да изтрием точка, която не съществува в множеството). Създайте метод за отпечатване на координатите на множеството точки. Създайте статични методи за обединение, сечение и разлика на подадени две множества:</p>
<pre>class AddPointException extends Exception{
  public AddPointException(Point x){
    super("The point "+x.show()+" is already in the set");
  }
  public AddPointException(String msg){
    super(msg);
  }
  public String toString(){
    return "The point is already in the set";
  }
}

class PointDoesNotExistsException extends Exception{
  public PointDoesNotExistsException(Point x){
    super("The point "+x.show()+" does not exists");
  }
  public PointDoesNotExistsException(String msg){
    super(msg);
  }
  public String toString(){
    return "The point does not exists";
  }
}

class Set{
  java.util.LinkedList&lt;Point&gt; list;
  public Set(){
    this.list = new java.util.LinkedList&lt;Point&gt;();
  }
  public boolean inSet(Point x){
    java.util.Iterator it = this.list.iterator();
    while(it.hasNext()){
      if(Point.equals((Point)it.next(), x)) return true;
    }
    return false;
  }
  public java.util.LinkedList&lt;Point&gt; getList(){
    return this.list;
  }
  public void addPoint(Point x) throws AddPointException{
    if(this.inSet(x)) throw new AddPointException(x);
    else this.list.addLast(x);
  }
  public void removePoint(Point x) throws PointDoesNotExistsException{
    boolean removed = false;
    for (int i=0; i&lt;this.list.size(); i++){
      if (Point.equals(this.list.get(i),x)){
        this.list.remove(i);
        removed = true;
        break;
      }
    }
    if (!removed) throw new PointDoesNotExistsException(x);
  }
  public void print(){
    for (int i=0; i&lt;this.list.size(); i++){
      System.out.println(""+(i+1)+". "+this.list.get(i).show());
    }
  }
  public void addSet(Set newlist){
    for(int i=0; i&lt;newlist.getList().size(); i++){
      try{
        this.addPoint((Point)newlist.getList().get(i));
      }
      catch(AddPointException e){}
    }
  }
  public static Set union(Set x, Set y){
    Set result = new Set();
    result.addSet(x);
    result.addSet(y);
    return result;
  }
  public static Set intersect(Set x, Set y){
    Set result = new Set();
    firstset: for (int i=0; i&lt;x.getList().size(); i++){
      for (int j=0; j&lt;y.getList().size(); j++){
        if(Point.equals(x.getList().get(i),y.getList().get(j))){
          try{
            result.addPoint(x.getList().get(i));
            continue firstset;
          }
          catch (AddPointException e){
            System.err.println("Some bad logic error has occured???");
          }
        }
      }
    }
    return result;
  }
  public static Set complement(Set x, Set y){
    Set result = new Set();
    for (int i=0; i&lt;x.getList().size(); i++){
      boolean found = false;
      for (int j=0; j&lt;y.getList().size(); j++){
        if(Point.equals(x.getList().get(i),y.getList().get(j))){
          found = true;
          break;
        }
      }
      if (!found){
        try{
          result.addPoint(x.getList().get(i));
        }
        catch (AddPointException e){
          System.err.println("Some bad logic error has occured???");
        }
      }
    }
    return result;
  }
}</pre>
<p><strong>3. Демонстрирайте функционалността на създадените класове</strong>, чрез написване на главна програма с main метод:</p>
<pre>public class mainprogram{
  public static void main(String[] args){
    Set points = new Set();
    // adding three points
    try{
      points.addPoint(new Point(2,3));
      points.addPoint(new Point(5,6));
      points.addPoint(new Point(-5,-6));
      System.out.println("Three points added successfully");
    }
    catch (AddPointException e){
      System.out.println(e.getMessage());
    }

    // Trying to add duplicate point
    try{
      points.addPoint(new Point(2,3));
      System.out.println("Another point added successfully");
    }
    catch (AddPointException e){
      System.out.println(e.getMessage());
    }

    // Removing point
    try{
      points.removePoint(new Point(5,6));
      System.out.println("Point (5,6) removed");
    }
    catch (PointDoesNotExistsException e){
      System.out.println(e.getMessage());
    }
    // Trying to remove non existing point
    try{
      points.removePoint(new Point(12,23));
      System.out.println("Point (12,23) removed");
    }
    catch (PointDoesNotExistsException e){
      System.out.println(e.getMessage());
    }

    // Otpechatvane na sashtestvuvashtite tochki v mnojestvoto
    System.out.println("\nFirst set points:");
    points.print();
    System.out.println();

    // Sazdavane na vtoro mnojestvo ot tri tochki
    Set points2 = new Set();
    try{
      points2.addPoint(new Point(2,3));
      points2.addPoint(new Point(-9,-9));
      points2.addPoint(new Point());
    }
    catch (AddPointException e){
      System.out.println(e.getMessage());
    }
    System.out.println("Second set points:");
    points2.print();
    System.out.println();

    // Obediniavane na mnojestvata
    System.out.println("Union:");
    Set up1p2 = Set.union(points, points2);
    up1p2.print();

    // Sechenie na mnojestvata
    System.out.println("\nIntersection:");
    Set ip1p2 = Set.intersect(points, points2);
    ip1p2.print();

    // Izvajdane na mnojestva
    System.out.println("\nComplement");
    Set cp1p2 = Set.complement(points, points2);
    cp1p2.print();
  }
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.cphpvb.net/java/4486-%d0%bf%d1%80%d0%b8%d0%bc%d0%b5%d1%80%d0%bd%d0%b0-%d0%b7%d0%b0%d0%b4%d0%b0%d1%87%d0%b0-2/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Примерна задача 1</title>
		<link>http://www.cphpvb.net/java/4459-%d0%bf%d1%80%d0%b8%d0%bc%d0%b5%d1%80%d0%bd%d0%b0-%d0%b7%d0%b0%d0%b4%d0%b0%d1%87%d0%b0-1/</link>
		<comments>http://www.cphpvb.net/java/4459-%d0%bf%d1%80%d0%b8%d0%bc%d0%b5%d1%80%d0%bd%d0%b0-%d0%b7%d0%b0%d0%b4%d0%b0%d1%87%d0%b0-1/#comments</comments>
		<pubDate>Sat, 31 Oct 2009 14:46:15 +0000</pubDate>
		<dc:creator>Филип Петров</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.cphpvb.net/?p=4459</guid>
		<description><![CDATA[1. Дефинирайте клас, описващ дати от календара.
а) Дефинирайте конструктор по подразбиране и такъв с параметри за задаване на дата.
б) Дефинирайте за него подходящите get и set методи.
в) Направете необходимите проверки за валидност на данните (ако не са валидни да се генерира изключение):
calException.java:
package example;
// Клас дефиниращ изключение "невалидна дата"
public class calException extends Exception{
  public calException(){
 [...]]]></description>
			<content:encoded><![CDATA[<p><strong>1. Дефинирайте клас, описващ дати от календара</strong>.</p>
<p>а) Дефинирайте конструктор по подразбиране и такъв с параметри за задаване на дата.</p>
<p>б) Дефинирайте за него подходящите get и set методи.</p>
<p>в) Направете необходимите проверки за валидност на данните (ако не са валидни да се генерира изключение):<span id="more-4459"></span></p>
<p><em>calException.java</em>:</p>
<pre>package example;
// Клас дефиниращ изключение "невалидна дата"
public class calException extends Exception{
  public calException(){
    super("Invalid date");
  }
  public calException(String msg){
    super(msg);
  }
  public String toString(){
    return "Invalid date";
  }
}</pre>
<p><em>calendar.java</em>:</p>
<pre>package example;
public class calendar{
  private int day;
  private int month;
  private int year;
  // Конструктор по подразбиране
  // данните се четат от клавиатурата
  // възможно е s.nextInt() да хвърли InputMismatchException!
  public calendar() throws calException, java.util.InputMismatchException{
    java.util.Scanner s = new java.util.Scanner(System.in);
    System.out.print("Enter day: ");
    this.day = s.nextInt();
    System.out.print("Enter month: ");
    this.month = s.nextInt();
    System.out.print("Enter year: ");
    this.year = s.nextInt();
    if (this.validate(this.day, this.month, this.year)== false){
      throw new calException();
    }
  }
  // Конструктор с параметри за създаване на дата
  public calendar(int day, int month, int year) throws calException{
    if (this.validate(day, month, year) == false) throw new calException();
    else{
      this.day = day;
      this.month = month;
      this.year = year;
    }
  }
  // Метод за валидиране на дата (за вътрешно ползване на класа)
  private boolean validate(int day, int month, int year){
    if (day &lt; 1) return false;
    if (month &lt; 1 || month &gt; 12) return false;
    switch(month){
      case 1: case 3: case 5: case 7: case 8: case 10: case 12:
        if(day &gt; 31) return false;
        break;
      case 4: case 6: case 9: case 11:
        if(day &gt; 30) return false;
        break;
      case 2:
        if (year%400==0 || (year%4==0 &amp;&amp; year%100!=0)){
           if (day &gt; 29) return false;
        }
        else{
          if (day &gt; 28) return false;
        }
        break;
    }
    return true;
  }
  // get методи:
  public int getDay(){
    return this.day;
  }
  public int getMonth(){
    return this.month;
  }
  public int getYear(){
    return this.year;
  }
  // set методи:
  public void setDay(int day) throws calException{
    if (this.validate(day, this.month, this.year)==false) throw new calException();
    else this.day = day;
  }
  public void setMonth(int month) throws calException{
    if (this.validate(this.day, month, this.year)==false) throw new calException();
    else this.month = month;
  }
  public void setYear(int year) throws calException{
    if (this.validate(this.day, this.month, year)==false) throw new calException();
    else this.year = year;
  }
}</pre>
<p><strong>2. Дефинирайте клас, описващ студент като включите следните данни</strong>:</p>
<ul>
<li> Име;</li>
<li>Факултетен номер;</li>
<li>Дата на раждане – използвайте класа от зад.1;</li>
<li>Среден успех.</li>
</ul>
<p>а) Дефинирайте конструктор по подразбиране и с параметри за инициализиране на обекта.</p>
<p>б) Дефинирайте подходящите get и set методи.</p>
<p>в) Дефинирайте метод, който по <strong>зададен параметър</strong> текуща дата, връща възрастта на студента като брой навършени години.</p>
<p>г) Дефинирайте статичен метод, който сравнява двама студента по успех (връща -1, 0 или 1 в зависимост от това дали първият е с по-висок, равен или по-нисък успех от втория).</p>
<p><em>student.java</em>:</p>
<pre>package example;
public class student{
  private String name;
  private long fnum;
  private calendar birthdate;
  private double gpa;
  // Конструктор по подразбиране
  public student() throws calException, java.util.InputMismatchException{
    java.util.Scanner s = new java.util.Scanner(System.in);
    System.out.print("Enter name: ");
    name = s.nextLine();
    System.out.print("Enter fnum: ");
    fnum = s.nextLong();
    birthdate = new calendar();
    System.out.print("Enter GPA: ");
    gpa = s.nextDouble();
  }
  // Конструктор с подадени параметри
  public student(String name, int day, int month, int year, long fnum, double gpa)
    throws calException{
    this.name = name;
    this.fnum = fnum;
    this.gpa = gpa;
    birthdate = new calendar(day, month, year);
  }
  // Втори конструктор с подадени параметри
  public student(String name, long fnum, double gpa, calendar birthdate){
    this.name = name;
    this.fnum = fnum;
    this.gpa = gpa;
    this.birthdate = birthdate;
  }
  // Get методи
  public String getName(){
    return this.name;
  }
  public calendar getBirthdate(){
    return this.birthdate;
  }
  public double getGPA(){
    return this.gpa;
  }
  public long getFNum(){
    return this.fnum;
  }
  // Set методи
  void setName(String name){
    this.name = name;
  }
  void setFNum(long fnum){
    this.fnum = fnum;
  }
  void setBirthdate(calendar birthdate){
    this.birthdate = birthdate;
  }
  void setGPA(double GPA){
    this.gpa = gpa;
  }
  // Метод връщащ годините на студента спрямо подадена текуща дата
  public int showYears(calendar currentDate){
    if (currentDate.getMonth() &gt; this.birthdate.getMonth()){
      return (currentDate.getYear() - this.birthdate.getYear());
    }
    else{
      if (currentDate.getMonth() &lt; this.birthdate.getMonth()){
        return (currentDate.getYear() - this.birthdate.getYear() - 1);
      }
      else{
        if (currentDate.getDay() &gt; this.birthdate.getDay()){
          return (currentDate.getYear() - this.birthdate.getYear());
        }
        else{
          return (currentDate.getYear() - this.birthdate.getYear() - 1);
        }
      }
    }
  }
  // Статичен метод сравняващ успеха на двама студенти
  public static int compare(student s1, student s2){
    if (s1.getGPA() &gt; s2.getGPA()) return -1;
    else{
      if (s1.getGPA() &gt; s2.getGPA()) return 1;
      else return 0;
    }
  }
}</pre>
<p><strong>3. Дефинирайте клас, описващ студентите от вашия поток</strong>, като включите:</p>
<ul>
<li>Списък от 100 студенти – използвайте класа от зад. 2 като информация за студентите включени в списъка;</li>
<li>Поток номер;</li>
<li>Специалност.</li>
</ul>
<p>а) Дефинирайте конструктор по подразбиране и с параметри за инициализиране на обекта.</p>
<p>б) Дефинирайте подходящите get и set методи.</p>
<p>в) Дефинирайте метод, който добавя студент в потока. Обръщаме внимание, че факултетния номер на студент би следвало да бъде уникално число. Методът да връща true или false в зависимост дали добавянето е успешно.</p>
<p>г) Дефинирайте метод, който изтрива студент от потока по зададен параметър факултетен номер. Методът връща true или false в зависимост дали е изтрит или не.</p>
<p>д) Дефинирайте метод, който търси има ли студент по зададени като параметър факултетен номер и да връща обект от тип student.</p>
<p>е) Предефенирайте метода от д) така, че да работи и подаден параметър &#8222;име на студент&#8220; (може да връща повече от един намерен студент).</p>
<p>ж) Дефинирайте метод, който намира студента с максимален успех от потока. Обръщаме внимание, че е възможно е да има повече от един студент с максимален успех.</p>
<p><em>potok.java</em>:</p>
<pre>package example;
public class potok{
  private static final int MAXSTUDENTS = 100;
  private student[] list;
  private int firstFreeSlot;
  private int potokN;
  private int specN;
  // Конструктор по подразбиране
  public potok() throws java.util.InputMismatchException{
    java.util.Scanner s = new java.util.Scanner(System.in);
    System.out.print("Enter group N: ");
    this.potokN = s.nextInt();
    System.out.print("Enter specialty N: ");
    this.specN = s.nextInt();
    this.list = new student[MAXSTUDENTS];
    this.firstFreeSlot = 0;
  }
  // Конструктор с подадени параметри
  public potok(int potokN, int specN){
    this.potokN = potokN;
    this.specN = specN;
    this.list = new student[MAXSTUDENTS];
    this.firstFreeSlot = 0;
  }
  // Get методи
  public int getPotokN(){
    return this.potokN;
  }
  public int getSpecN(){
    return this.specN;
  }
  public student getStudent(int i){
    return this.list[i];
  }
  // Set методи
  void setPotokN(int potokN){
    this.potokN = potokN;
  }
  void setSpecN(int specN){
    this.specN = specN;
  }
  // Метод добавящ студент в потока
  boolean addStudent(student s){
    if (this.firstFreeSlot == this.MAXSTUDENTS){
      System.err.println("Max students exceeded "+this.MAXSTUDENTS);
      return false;
    }
    // Търси за вече съществуващ студент
    for (int i=0; i &lt; this.firstFreeSlot; i++){
      if (s.getFNum() == this.list[i].getFNum()){
        System.err.println("Student with fnum "+s.getFNum()+" already exists!");
        return false;
      }
    }
    // Добавя студент в първия наличен свободен слот
    this.list[this.firstFreeSlot] = s;
    this.firstFreeSlot++;
    return true;
  }
  // Метод изтриващ студент от потока
  boolean delStudent(long fnum){
    for (int i=0; i &lt; this.firstFreeSlot; i++){
      if (fnum == this.list[i].getFNum()){
        if (i == firstFreeSlot - 1){
          this.list[i] = null;
          this.firstFreeSlot--;
          return true;
        }
        else{
          this.list[i] = this.list[(this.firstFreeSlot-1)];
          this.list[(this.firstFreeSlot-1)] = null;
          this.firstFreeSlot--;
          return true;
        }
      }
    }
    return false;
  }
  // Търсене на студент по факултетен номер
  student findStudent(long fnum){
    for (int i=0; i &lt; this.firstFreeSlot; i++){
      if (fnum == this.list[i].getFNum()){
        return this.list[i];
      }
    }
    return null;
  }
  // Търсене на студент по име (неуникално)
  java.util.LinkedList&lt;student&gt; findStudent(String name){
    java.util.LinkedList&lt;student&gt; result = new java.util.LinkedList&lt;student&gt;();
    for (int i=0; i &lt; this.firstFreeSlot; i++){
      if (name.equalsIgnoreCase(this.list[i].getName())){
        result.add(this.list[i]);
      }
    }
    return result;
  }
  // Намира най-добрите студенти в потока
  java.util.LinkedList&lt;student&gt; getBestStudents(){
    // Ако все още няма ваведени студенти
    if (this.firstFreeSlot == 0){
      System.err.println("No students in the group");
      return null;
    }
    // Намира най-добрия успех
    double maxGPA = this.list[0].getGPA();
    int place = 0;
    for (int i=1; i &lt; this.firstFreeSlot; i++){
      if (this.list[i].getGPA() &gt; maxGPA){
        maxGPA = this.list[i].getGPA();
        place = i;
      }
    }
    // Натрупва в списък най-добрите студенти
    java.util.LinkedList&lt;student&gt; result = new java.util.LinkedList&lt;student&gt;();
    for (int i=place; i &lt; this.firstFreeSlot; i++){
      if (this.list[i].getGPA() == maxGPA){
        result.add(this.list[i]);
      }
    }
    return result;
  }
}</pre>
<p><strong>4. Дефинирайте метод, който определя среден успех на потока</strong> и връща клас, съдържащ номер на потока, специалност и среден успех.</p>
<p><em>potokstats.java</em>:</p>
<pre>package example;
public class potokstats{
  private int potokN;
  private int specN;
  private double srusp;
  public potokstats(int potokN, int specN, double srusp){
    this.potokN = potokN;
    this.specN = specN;
    this.srusp = srusp;
  }
  public void print(){
    System.out.println("Sp | Gr | GPA");
    System.out.println(this.specN+" | "+this.potokN+" | "+this.srusp);
  }
}</pre>
<p>Добавяме в <em>potok.java</em>:</p>
<p>// Намира средния успех на потока<br />
potokstats getSrUsp(){<br />
if(firstFreeSlot == 0){<br />
System.err.println(&#8222;No students in this group yet&#8220;);<br />
return null;<br />
}<br />
double srusp = 0;<br />
for (int i=0; i &lt; this.firstFreeSlot; i++){<br />
srusp += this.list[i].getGPA();<br />
}<br />
srusp /= this.firstFreeSlot;<br />
return new potokstats(this.potokN, this.specN, srusp);<br />
}</p>
<p><strong>5. Демонстрирайте функционалността на създадените класове</strong></p>
<p><em>mainprog.java</em>:</p>
<pre>package example;
class mainprog{
  public static void main(String[] args){
    // Създава поток от студенти
    potok p = new potok(1, 1);
    // Добавя 10 студента
    for (int i=0; i&lt;3; i++){
      student s = null;
      try{
        s = new student();
      }
      catch(java.util.InputMismatchException e){
        System.err.println("Invalid input data!");
        i--;
      }
      catch (calException e){
        System.err.println(e.getMessage());
      }
      finally{
        try{
          while(System.in.available() &gt; 0) System.in.skip(System.in.available());
        }
        catch(java.io.IOException e){}
      }
      if(s!=null) p.addStudent(s);
    }
    // Изтрива студент с фак.номер 1212
    if (p.delStudent(1212)) System.out.println("Successfully deleted student 1212");
    else System.out.println("Student 1212 not found and cannot be deleted");
    // Намира студент с фак.номер 1313 и показва името му и годините му
    System.out.print("Student with fnum 1313 is: ");
    student s1313 = p.findStudent(1313);
    if (s1313 != null){
      try{
        calendar curcal = new calendar(30,10,2009);
        System.out.println(s1313.getName()+" who is "+s1313.showYears(curcal)+" years old");
      }
      catch (calException e){
        System.out.println("Bad calendar");
      }
    }
    else{
      System.out.println("Student 1313 not found and cannot be printed");
    }
    // Сравнява оценките на студент 1313 и 1414:
    System.out.println("Compare s1313 and s1414: ");
    student s1414 = p.findStudent(1414);
    if(s1313!=null &amp;&amp; s1414!=null){
      int c = student.compare(s1313, s1414);
      if(c &lt; 0) System.out.println("s1313 is better");
      else if(c &gt; 0) System.out.println("s1414 is better");
      else System.out.println("s1313 and s1414 are equal");
    }
    else System.out.println("cannot compare because one or both do not exists");
    // Търсим студентите с име Peter и им отпечатва факултетните номера и оценките
    System.out.println("The student(s) with name Peter are:");
    java.util.LinkedList&lt;student&gt; peterStudents = p.findStudent("Peter");
    if (peterStudents != null){
      for (int i=0; i&lt;peterStudents.size(); i++){
        System.out.print(peterStudents.get(i).getFNum()+" "+peterStudents.get(i).getGPA());
        System.out.println();
      }
    }
    // Намира най-добрите студенти в потока
    System.out.println("Best students are:");
    java.util.LinkedList&lt;student&gt; bestStudents = p.getBestStudents();
    if (bestStudents != null){
      for (int i=0; i&lt;bestStudents.size(); i++){
        System.out.print(bestStudents.get(i).getFNum()+" "+bestStudents.get(i).getName());
        System.out.print(" "+bestStudents.get(i).getGPA());
        System.out.println();
      }
    }
    // Среден успех на потока
    potokstats ps = p.getSrUsp();
    System.out.println("Statistics for the group:");
    ps.print();
  }
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.cphpvb.net/java/4459-%d0%bf%d1%80%d0%b8%d0%bc%d0%b5%d1%80%d0%bd%d0%b0-%d0%b7%d0%b0%d0%b4%d0%b0%d1%87%d0%b0-1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Списъци, динамични масиви, стекове и опашки</title>
		<link>http://www.cphpvb.net/java/4415-lists-arrays-stacks-queues/</link>
		<comments>http://www.cphpvb.net/java/4415-lists-arrays-stacks-queues/#comments</comments>
		<pubDate>Mon, 26 Oct 2009 18:53:32 +0000</pubDate>
		<dc:creator>Филип Петров</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.cphpvb.net/?p=4415</guid>
		<description><![CDATA[1. Масиви: Когато се заговори за понятието &#8222;списък&#8220; всеки начинаещ програмист моментално се досеща за най-често използваната в учебните примери структура &#8211; масив. Масивите представляват наредени елементи от един и същи тип данни (може да бъде както примитивен тип, така и данни от определен клас). Те са с константна дължина (т.е. точно определен брой елементи). [...]]]></description>
			<content:encoded><![CDATA[<p><strong>1. Масиви:</strong> Когато се заговори за понятието &#8222;списък&#8220; всеки начинаещ програмист моментално се досеща за най-често използваната в учебните примери структура &#8211; масив. Масивите представляват наредени елементи от един и същи тип данни (може да бъде както примитивен тип, така и данни от определен клас). Те са с константна дължина (т.е. точно определен брой елементи). Това е колкото полезно (няма опасност от &#8222;утечки на памет&#8220;), толкова и неудобно (трябва да се пазим от проблема с &#8222;препълване&#8220;, често се заделя повече памет отколкото е необходима, и др.). Можем да обобщим проблемите свързани с използването на масив в три точки:<span id="more-4415"></span></p>
<ol>
<li>Обикновено заделяме повече памет, отколкото използваме. Когато говорим за данни с обем в малки граници (брой ученици в клас, работници в малка фирма, продуктова гама от традиционни стоки и др.), това не е проблем. Когато данните обаче варират (унифициран софтуер за малки и големи фирми, данни записани в зависимост от натовареността и посещаемостта на уебсайт и др.), то използването на масив започва да изглежда като голямо разхищение на памет;</li>
<li>Ако не желаем да изпадаме в ситуация, в която не можем да добавяме повече елементи, то трябва да се направи доста тежка откъм процесорно време операция:<br />
a) Заделя се памет за нов масив с 1 или няколко елемента по-голям от стария;<br />
б) Копират се данните от стария масив в новия;<br />
в) Премахва се указателят към стария масив и се дава на GarbageCollector да освободи паметта.</li>
<li>Най-често ние не &#8222;изтриваме&#8220; елементите на масив, а просто слагаме стойност &#8222;null&#8220; в съответната клетка (както беше в горния пример). По този начин паметта остава заделена и не може да се освободи. Както и в точка 1 &#8211; това е разхищение на памет. Алтернативата с реално изтриване на елемент също изисква много процесорно време и е бавна операция:<br />
a) Заделя се памет за нов масив с 1 по-малък от стария;<br />
б) Копират се всички елементи от стария в новия масив, с изключение на изтривания;<br />
в) Премахва се указателят към стария масив и се дава на GarbageCollector да освободи паметта.</li>
</ol>
<p>Поради изброените по-горе проблеми, когато имаме данни заемащи голям обем памет и/или когато работим с множества от елементи които не са с фиксирани граници, то традиционните масиви не са препоръчителна структура от данни. Вместо тях е удобно да използваме т.нар.  &#8222;списъци&#8220; или &#8222;динамични масиви&#8220;. Традиционните масиви следва да бъдат използвани предимно в случаите, когато работим с фиксирано количество данни.</p>
<p><strong>2. Списъци:</strong> в Java съществува клас LinkedList или в превод &#8222;свързан списък&#8220;. Това е динамична структура от данни (без ограничение на максимален брой елементи, стига да има достатъчно памет). Елементите в LinkedList са &#8222;наредени на опашка&#8220;, т.е. имаме винаги един първи, един последен елемент, а всички останали имат свой &#8222;предходен&#8220; и &#8222;следващ&#8220;. Такива списъци се наричат &#8222;двусвързани&#8220;.</p>
<p>LinkedList се дефинира по следния начин:</p>
<pre>java.util.LinkedList&lt;Integer&gt; list = new java.util.LinkedList&lt;Integer&gt;();</pre>
<p>Естествено в ъгловите скоби вместо Integer (цяло число) може да стои всякакъв друг тип данни или обект. Нека сега разгледаме най-важните методи:</p>
<p>а) Добавяне на елемент в края на списъка:</p>
<pre>list.add(new Integer(1));
list.addLast(new Integer(2)); // еквивалентно на първото</pre>
<p>б) Добавяне на елемент в началото на списъка:</p>
<pre>list.addFirst(new Integer(3));</pre>
<p>в) Размер (брой елементи на списък):</p>
<pre>System.out.println("\nList size: "+list.size());</pre>
<p>г) Отпечатване на съдържанието на списъка чрез итератор:</p>
<pre>java.util.Iterator it = list.iterator();
while(it.hasNext()){
   System.out.print(it.next()+" ");
}</pre>
<p>Може да приемете, че обектът java.util.Iterator се използва като &#8222;указател&#8220; към текущия елемент на списък. В началото той винаги &#8222;сочи&#8220; към първия елемент. Показания пример е стандартен за &#8222;обхождане&#8220; на списък елемент по елемент.</p>
<p>д) Добавяне на елемент на определена позиция:</p>
<pre>list.add(2, new Integer(25)); // първи аргумент е позиция, а втори стойност</pre>
<p>е) Прочитане на елемент от определена позиция:</p>
<pre>System.out.println("The element at position 2 is: "+list.get(2));</pre>
<p>ж) Прочитане на първи и последен елементи:</p>
<pre>System.out.println("The first element is is: "+list.getFirst());
System.out.println("The last element is is: "+list.getLast());</pre>
<p>з) Отпечатване на съдържанието на списъка БЕЗ итератор:</p>
<pre>for (int i=0; i&lt;list1.size(); i++){
 System.out.print(list1.get(i)+" ");
}</pre>
<p>и) Изтриване на елемент: тук освен фактическото премахване на елемента имаме и функционалност същевременно да прочетем стойността й. Така например този вид списък може лесно да бъде използван като стек:</p>
<pre>int first = list.removeFirst();
System.out.println("First element: "+first+" removed...");
int last = list.removeLast();
System.out.println("Last element: "+last+" removed...");
int x = list.remove(1);
System.out.println("Element with index 1: "+x+" removed...");
System.out.println("Now list contains:");
it = list.iterator();
while(it.hasNext()){
   System.out.print(it.next()+" ");
}</pre>
<p>й) Изтриване на всички елементи в списък:</p>
<pre>list.clear();</pre>
<p>к) Проверка дали списък е празен или не:</p>
<pre>if (list.isEmpty()) System.out.println("List is empty");</pre>
<p>л) &#8222;Клониране&#8220; на списък:</p>
<pre>java.util.LinkedList list = new java.util.LinkedList();
list.add(new Integer(1));
list.addLast(new Integer(2));
list.addFirst(new Integer(3));
java.util.LinkedList listClone = <strong>list.clone();</strong></pre>
<p>м) Търсене на елемент в списък:</p>
<pre>if(list.contains(3)) System.out.println("List has element with val 3");</pre>
<p>н) Промяна на елемент с нова стойност:</p>
<pre>list.set(1, new Integer(25)); // първи аргумент индекс, а втори стойност</pre>
<p>о) Добавяне на един списък в края на друг или вмъкването му в даден индекс:</p>
<pre>java.util.LinkedList list1 = new java.util.LinkedList();
list1.add(new Integer(1));
list1.addLast(new Integer(1));
list1.addFirst(new Integer(1));
java.util.LinkedList list2 = new java.util.LinkedList();
list2.add(new Integer(2));
list2.addLast(new Integer(2));
list2.addFirst(new Integer(2));
<strong>list1.addAll(list2);
list1.addAll(1, list2);</strong></pre>
<p>Виждате, основната разлика между LinkedList и Array е в липсата на предварително фиксиране на размера на структурата. Един списък може да бъде толкова голям, колкото налична памет има.</p>
<p>Въпреки, че на няколко места използвахме термина &#8222;индекс&#8220;, все пак трябва да знаете, че то не е коректно. Физически в паметта обектите на LinkedList са разположени в различни места, а просто се пазят &#8222;указатели&#8220; (т.нар. итератори) към следващия елемент. Така всъщност ние не знаем къде точно се намира елемент №4 от самото начало &#8211; налага се да тръгнем от елемент №1, през №2 и №3, за да стигнем до търсения. Така въпреки, че остава скрито за нас като потребители, извличането на елемент от даден индекс е значително по-бавна операция от тази при масивите. Изтриването на елемент също включва в себе си първо &#8222;намиране&#8220; на елемента. Така можем да заключим, че LinkedList наистина спестява памет в сравнение с масивите, но за сметка на това може да се каже, че е по-бавен.</p>
<p><strong>3. Динамични Масиви:</strong> &#8211; В общи линии можем да приемем, че динамичните масиви (ArrayList) са нещо средно между свързани списъци и обикновени масиви. Всъщност те се опитват да комбинират предимствата и на едните и на другите. Един ArrayList първоначално заделя определен обем от памет подобно на стандартния масив. Елементите, които добавяме се натрупват един след друг в тази памет (т.е. тук имаме истинско понятие &#8222;индекс&#8220;, защото елементите са на поредни места в паметта). Достъпването до елемент е бързо както при масивите. Изтриването на елемент също &#8211; просто последващите се преместват назад. Проблемът със следенето за препълване на масив обаче липсва &#8211; ArrayList си разширява капацитета автоматично. Вече демонстрирахме подобна функционалност при <a href="http://www.cphpvb.net/java/4194-stringbuffer/">динамичните низове (StringBuffer и StringBuilder)</a>.</p>
<p>ArrayList работи подобно на LinkedList. Някои методи са премахнати (addFirst, addLast, removeLast, removeLast). Ето само списък с наличните познати методи (няма да даваме примери, понеже синтаксисът е аналогичен на този на LinkedList):</p>
<p>add(елемент) &#8211; добавя елемент в края на списъка;<br />
add(индекс, елемент) &#8211; добавя елемент в даден индекс (и премества следващите напред);<br />
clear() &#8211; изтрива всички елементи;<br />
contains(елемент) &#8211; търси елемента в списъка и връща true или false ако е намерен или не;<br />
get(индекс) &#8211; връща стойността на елемент от даден индекс;<br />
isEmpty() &#8211; връща true или false в зависимост дали списъка е празен;<br />
remove(индекс) &#8211; изтрива елемент на даден индекс;<br />
set(индекс, елемент) &#8211; променя стойността на елемент на даден индекс с нова стойност);<br />
size() &#8211; връща броя елементи на масива;</p>
<p>Новите методи спрямо LinkedList са:</p>
<p>a) Подсигуряване за минимален капацитет:</p>
<pre>java.util.ArrayList&lt;Integer&gt; arrlist = new java.util.ArrayList&lt;Integer&gt;();
<strong>arrlist.ensureCapacity(10);</strong></pre>
<p>Понеже автоматичното разширяване на динамичен масив може да бъде &#8222;скъпа&#8220; операция (ако няма място за разширяването), то с ensureCapacity ние се подсигуряваме, че имаме достатъчно голям размер. Всъщност показаното в горния пример е грешен метод &#8211; можем да подсигурим размера директно чрез конструктора при инициализация:</p>
<pre>java.util.ArrayList&lt;Integer&gt; arrlist = new java.util.ArrayList&lt;Integer&gt;(<strong>10</strong>);</pre>
<p>Използвайте ensureCapacity() ако е необходимо да разширявате масива с много елементи при извършаването на дадена операция.</p>
<p>б) indexOf(елемент) &#8211; връща индекса на първото срещане на елемента вътре в списъка (не се поддържа търсене от &#8222;елемент нататък&#8220;, както беше при клас String);</p>
<p>в) lastIndexOf(елемент) &#8211; индекса на последното срещане на елемента вътре в списъка;</p>
<p>г) removeRange(индекс &#8222;от&#8220;, индекс &#8222;до&#8220;) &#8211; изтрива елементите от първия до втория индекс подадени като аргумент. Например list.removeRange(2,4); изтрива елементи от 2 до 4 включително;</p>
<p>д) trimToSize() &#8211; &#8222;свива&#8220; капацитета на масива до броя на елементите му. Обикновено го използваме, за да освободим памет в случаи когато знаем, че няма да има повече добавяне на елементи.</p>
<p><strong>4. Опашка и стек:</strong> Като частни случаи на двусвързаният списък (LinkedList) са списъците от тип Queue (опашка) и Stack (стек). При опашката можете да добавяте елементи само в края на списъка и премахване на елемент само от началото на списъка. При опашката операции &#8222;търсене&#8220; и &#8222;извличане по индекс&#8220; няма. Опашките се използват често при създаване на интернет приложения, при които трябва да обработваме поредни заявки в стил FIFO (first in first out).</p>
<p>При стека пък се добавят елементи (push) само в края на списъка и се изтриват (pop) пак само в края на списъка. Има и операция &#8222;прочитане на елемент&#8220;, която връща (peek) стойността на последния елемент без да го изтрива. Тази структура е от тип LIFO (last in first out). Представете си стека като натрупана купчина от книги. За да достигнете до най-долната трябва преди това да премахнете една по една по-горните над нея.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cphpvb.net/java/4415-lists-arrays-stacks-queues/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Изключения дефинирани от потребителя</title>
		<link>http://www.cphpvb.net/java/4397-user-defined-exceptions/</link>
		<comments>http://www.cphpvb.net/java/4397-user-defined-exceptions/#comments</comments>
		<pubDate>Wed, 21 Oct 2009 18:35:32 +0000</pubDate>
		<dc:creator>Филип Петров</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.cphpvb.net/?p=4397</guid>
		<description><![CDATA[Вече се запознахме подробно как се хвърлят изключения в Java. Веднага остава въпроса &#8222;можем ли ние сами да си дефинираме изключения&#8220;. Отговорът е положителен.
Всичко, което трябва да направите при дефиниране на изключение е да създадете клас, който наследява клас Exception:
public class CarsExample{
  public static void main(String args[]) throws Exception {
    try{
 [...]]]></description>
			<content:encoded><![CDATA[<p>Вече се запознахме подробно как се хвърлят <a href="http://www.cphpvb.net/java/4174-throw-exceptions/" target="_blank">изключения в Java</a>. Веднага остава въпроса &#8222;можем ли ние сами да си дефинираме изключения&#8220;. Отговорът е положителен.</p>
<p>Всичко, което трябва да направите при дефиниране на изключение е да създадете клас, който наследява клас Exception:<span id="more-4397"></span></p>
<pre>public class CarsExample{
  public static void main(String args[]) throws Exception {
    try{
      Car c = new Car(-150, "BMW X5");
    }
<strong>    catch (SpeedException e){
      System.out.println(e.toString());
    }</strong>
  }
}

<strong>class SpeedException extends Exception{
  private int speed;
  public SpeedException(int speed){
    this.speed = speed;
  }
  public String toString(){
    return "Speed of Car must not be negative: "+this.speed;
  }
}</strong>

class Car{
  private int speed;
  private String model;
  public Car(int speed, String model)<strong> throws SpeedException</strong>{
    if (speed &lt; 0) <strong>throw new SpeedException(speed)</strong>;
    else{
      this.speed = speed;
      this.model = model;
    }
  }
  public void showInfo(){
    System.out.println("The car "+this.model+" has maxspeed of "+this.speed);
  }
  public int getSpeed(){
    return this.speed;
  }
  public void setSpeed(int speed) <strong>throws SpeedException</strong>{
    if (speed &lt; 0) <strong>throw new SpeedException(speed)</strong>;
    else this.speed = speed;
  }
  public int compare(Car opponent){
    if (this.speed &lt; opponent.getSpeed()) return 1;
    else{
      if (this.speed &gt; opponent.getSpeed()) return -1;
      else return 0;
    }
  }
}</pre>
<p>Виждате, че при изключенията няма абсолютно нищо страшно. Единственото, за което трябва да се погрижите е да предефинирате метод &#8222;toString()&#8220;. Колкото до другите методи &#8211; printStackTrace() си съществува и го наследяваме директно от клас Exception. Проблем се явява метод &#8222;getMessage()&#8220;, който ако не се предефинира, то ще върне резултат null. Затова обикновено предаваме и съобщение към супер класа:</p>
<pre>class SpeedException extends Exception{
  private int speed;
<strong>  public SpeedException(int speed){
    super("Speed of car must not be negative: "+speed);
    this.speed = speed;
  }
  public SpeedException(int speed, String msg){
    super(msg);
    this.speed = speed;
  }</strong>
  public String toString(){
    return "Speed of Car must not be negative: "+this.speed;
  }
}</pre>
<p>Вече можете да извиквате метод &#8222;getMessage()&#8220; без проблем. Виждате, че дефинирането на изключения е изключително лесно. Това е и мощен метод за контрол над интегритета на данните в клас.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cphpvb.net/java/4397-user-defined-exceptions/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Wait, notify и notifyAll</title>
		<link>http://www.cphpvb.net/java/4391-wait-notify-and-notifyall/</link>
		<comments>http://www.cphpvb.net/java/4391-wait-notify-and-notifyall/#comments</comments>
		<pubDate>Mon, 19 Oct 2009 18:54:21 +0000</pubDate>
		<dc:creator>Филип Петров</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.cphpvb.net/?p=4391</guid>
		<description><![CDATA[Вече се запознахме с методът sleep() за нишки в Java, както и възможността да прекъснем &#8222;спането&#8220; на нишката чрез метод interrupt().  Използването на метод sleep() всъщност прехвърля текущата нишка в &#8222;Not Runnable&#8220; статус за определен период от време и по този начин дава процесорно време на другите нишки. Важно е да се спомене, че ако [...]]]></description>
			<content:encoded><![CDATA[<p>Вече се запознахме с методът sleep() за нишки в Java, както и възможността да прекъснем &#8222;спането&#8220; на нишката чрез метод interrupt().  Използването на метод sleep() всъщност прехвърля текущата нишка в &#8222;Not Runnable&#8220; статус за определен период от време и по този начин дава процесорно време на другите нишки. Важно е да се спомене, че ако методът, който е извикал sleep(), е синхронизиран (synchronized), то никой не може да достъпи обектите в него по време на неговия &#8222;sleep&#8220; период! Извикването на &#8222;interrupt()&#8220; за тази нишка ще прекъсне sleep() преждевременно.</p>
<p>Когато имаме обект (говорим за който и да е обект създаден с оператор new), то разполагаме с нестатичен метод Object.wait(). Този метод на пръв поглед предизвиква същия ефект както Thread.sleep() &#8211; прехвърля текущата нишка (тази, която е извикала метода) в &#8222;Not Runnable&#8220; статус за определено време. Такъв обект се нарича &#8222;заключващ обект&#8220; за нишката. Първата разлика е, че Object.wait() може да бъде извикан само в синхронизиран метод. Втората разлика е, че Object.wait() може да приспи нишката за неопределено време, докато Thread.sleep() е с фиксирано. Освен това ако имаме Thread T, то T.sleep() ще &#8222;приспи&#8220; нишката T, докато T.wait() ще приспи текущата нишка (тази, която е извикала метод T.wait()), докато някой друг не извика T.notify().<span id="more-4391"></span></p>
<p>Преди да дадем пример трябва да кажем, че wait() и notify() са final методи за клас Object. Понеже Object е базов шаблон за клас, който всеки наследява по подразбиране, то всеки един клас в Java ги притежава.</p>
<p>В първият пример ще демонстрираме как можем да накараме една нишка да &#8222;заспи&#8220; докато не се извършат необходимите стартирани в нея изчисления:</p>
<pre>public class waitNotifyExample{
  public static void main(String[]args){
    ThreadExample T = new ThreadExample();
    T.start();
    synchronized(T){
      System.out.println("Waiting until calculations finish...");
      try{
        T.wait();
      }
      catch (InterruptedException e){}
    }
    System.out.println("Result: "+T.sum);
  }
}

class ThreadExample extends Thread{
  public static int sum=0;
  public void run(){
    synchronized (this){
      this.sum();
      System.out.println("Finished summing. Now the program can continue...");
      this.notify();
    }
  }
  public void sum(){
    System.out.println("Suming the first 500 integers...");
    for(int i=1; i&lt;=500; i++){
      this.sum = this.sum+i;
    }
  }
}</pre>
<p>Преди да бъде извикан &#8222;wait()&#8220; методът за обекта, нишката е длъжна да се синхронизира по него (т.е. други методи не могат да го модифицират). След това цялата нишка се &#8222;приспива&#8220; в т.нар. &#8222;wait list&#8220; (от примера по-горе приспахме главната нишка на main метода). В последствие друга нишка има възможност да &#8222;събуди&#8220; обекта чрез метод &#8222;notify()&#8220; (това направи самата нишка, по която бяхме приспали main метода).</p>
<p>В този пример показахме как главната нишка (main) извиква друга нишка и изчаква докато тя си свърши работата. Нека погледнем и обратния вариант &#8211; главната нишка стартира нова нишка и новата нишка изчаква докато главната не я уведоми:</p>
<pre>public class myfirstprogram {
  public static void main(String args[]) throws Exception {
    System.out.println("Starting thread...");
    MyThread T =  new MyThread("MyThread");
    System.out.println("Main thread printing dots...");
    for (int i = 0; i &lt; 50; i++) {
      Thread.sleep(50);
      System.out.print(".");
    }
    T.start();
  }
}

class MyThread implements Runnable{
  boolean ready;
  Thread T;
  String name;
  MyThread(String name){
    this.ready = false;
    this.name = name;
    this.T = new Thread(this, name);
    this.T.start();
  }
  synchronized void suspendThread(){
    System.out.println(this.name+" suspended until main finishes...");
    while (!ready){
      try{
        this.wait();
      }
      catch(InterruptedException e){}
    }
    System.out.println("\n"+this.name+" is now resumed!");
  }
  synchronized void start(){
    ready = true;
    notify();
  }
  public void run() {
    this.suspendThread();
  }
}</pre>
<p>Нека сега покажем още един пример, в който две нишки се &#8222;блокират&#8220; една друга докато изпълнението на съответните методи не приключи. Имаме два класа и две инстанции &#8211; обект, който внася пари и обект, който ги прибира. Нека погледнем следният пример:</p>
<pre>public class WaitNotifyExample{
  public static void main(String args[]) {
    MoneyQueue q = new MoneyQueue();
    MoneyGiver mg = new MoneyGiver(q, "MoneyGiver");
    MoneyFetcher mf = new MoneyFetcher(q, "MoneyFetcher");
  }
}

class MoneyQueue{
  double moneyTransfer;
  boolean haveMoneyInAccount = false;

  synchronized double getMoney(String name) {
    if(haveMoneyInAccount == false){
      try {
        this.wait();
      }
      catch(InterruptedException e) {
        System.out.println("This should not happen in this program");
      }
    }
    System.out.println(name +" fetched " + moneyTransfer);
    haveMoneyInAccount = false;
    this.notify();
    return moneyTransfer;
  }

  synchronized void addMoney(){
    if(haveMoneyInAccount == true){
      try {
        this.wait();
      }
      catch(InterruptedException e) {
        System.out.println("This should not happen in this program");
      }
    }
    System.out.print("Add money: ");
    java.util.Scanner s = new java.util.Scanner(System.in);
    double moneyTransfer = s.nextDouble();
    this.moneyTransfer = moneyTransfer;
    haveMoneyInAccount = true;
    this.notify();
  }
}

class MoneyGiver implements Runnable{
  String name;
  Thread T;
  MoneyQueue q;
  public MoneyGiver(MoneyQueue q, String name){
    this.q = q;
    this.name = name;
    T = new Thread(this, "MoneyGiver");
    T.start();
  }
  public void run() {
    while(true){
      q.addMoney();
    }
  }
}

class MoneyFetcher implements Runnable{
  double money;
  String name;
  Thread T;
  MoneyQueue q;
  public MoneyFetcher(MoneyQueue q, String name) {
    this.q = q;
    this.name = name;
    money = 0;
    T = new Thread(this, "MoneyFetcher");
    T.start();
  }
  public void run() {
    while(true) {
      this.money += q.getMoney(this.name);
      System.out.println(this.name+" now have "+this.money);
    }
  }
}</pre>
<p>MoneyQueue е клас, в който са дефинирани два синхронизирани метода &#8211; addMoney и getMoney. Освен това създаваме два обекта &#8211; MoneyGiver (вкарващ пари в системата) и MoneyFetcher (взимащ тези пари и натрупващ ги в собствената си сметка).</p>
<p>Идеята на примера е следната &#8211; в началото в MoneyQueue няма никакви пари, което е описано чрез променливата haveMoneyInAccount. Двата обекта MoneyGiver и MoneyFetcher се стартират в две нишки. MoneyFetcher моментално се опитва да вземе пари, но тъй като такива няма, той изпада в спящ режим (в метод getMoney() изпадаме в статус wait(), с което извикващия метод започва да чака). При MoneyGiver ситуацията е точно обратната &#8211; виждаме, че няма пари в опашката и затова се поисква въвеждане на такива от клавиатурата. В момента в който вкараме пари в опашката, то haveMoneyInAccount става true и се извиква this.notify(), с което &#8222;събуждаме&#8220; другата нишка. MoneyGiver мометално ще изника същия метод (addMoney) отново, но този път ще забележи, че все още има пари за взимане в опашката и затова ще изпадне в wait() статус (докато парите бъдат взети). MoneyFetcher вече е &#8222;събуден&#8220; &#8211; той ще вземе парите от опашката и ще укаже, че в нея вече няма пари (тоест при следващо извикване на същия метод той самия пак ще е в wait() статус). Естествено отново се извиква this.notify(), за да &#8222;събудим&#8220; обекта MoneyGiver. Тази поредност в случая може да се повтаря до безкрайност.</p>
<p>Методът notifyAll() е по-специален &#8211; той &#8222;събужда&#8220; всички нишки, които са попаднали в wait() статус. Попринцип когато пишете програми трябва много добре да прецените колко нишки евентуално могат да заспят. Ако е само една, то notify() е достатъчен. Ако са много, то е по-вероятно да ви трябва notifyAll(). Ако използвате notify() и повече от една нишка заспи, то рискувате нишките ви да &#8222;зациклят&#8220;. Въпреки това не препоръчваме тактиката да използвате винаги notifyAll() &#8211; ако това ви се налага, но не е очаквано, то по-добре си напишете програмата по-добре.</p>
<p>Обикновено използваме notifyAll() когато нишките имат споделен ресурс по който те се заключват:</p>
<pre>public class myfirstprogram {
  public static void main(String args[]) throws Exception {
    ThreadsResource tr = new ThreadsResource();
    MyThread T1 = new MyThread("MyThread1", tr);
    MyThread T2 = new MyThread("MyThread2", tr);
    for (int i = 0; i &lt; 50; i++){
      Thread.sleep(50);
      System.out.print(".");
    }
    System.out.println();
    tr.resume();
  }
}

class ThreadsResource{
  boolean ready = false;
  synchronized void suspendThread(){
    System.out.print(Thread.currentThread().getName());
    System.out.println(" suspended until main finishes...");
    while(!ready){
      try{
        this.wait();
      }
      catch(InterruptedException e){}
    }
    System.out.println(Thread.currentThread().getName()+" is now resumed!");
  }
  synchronized void resume() {
    ready = true;
    this.notifyAll();
  }
}

class MyThread implements Runnable{
  ThreadsResource tr;
  Thread T;
  MyThread(String name, ThreadsResource tr){
    this.tr = tr;
    this.T = new Thread(this, name);
    this.T.start();
  }
  public void run() {
    tr.suspendThread();
  }
}</pre>
<p>Виждате, че когато споделеният ресурс ThreadsResource извика &#8222;notifyAll()&#8220;, то всички нишки, които са &#8222;заспали&#8220; чрез него се &#8222;събуждат&#8220;. Това например е често използвана тактика при многопотребителски игри, при които синхронизацията е важна.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cphpvb.net/java/4391-wait-notify-and-notifyall/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Синхронизация на нишки</title>
		<link>http://www.cphpvb.net/java/4382-threads-sync/</link>
		<comments>http://www.cphpvb.net/java/4382-threads-sync/#comments</comments>
		<pubDate>Sat, 17 Oct 2009 18:07:18 +0000</pubDate>
		<dc:creator>Филип Петров</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.cphpvb.net/?p=4382</guid>
		<description><![CDATA[Когато имаме многонишково приложение, то ние много често работим и със споделени ресурси. Нека демонстрираме с един пример &#8211; имаме масив с наредени числа. Имаме две нишки &#8211; такава, която променя числата с произволни и такава, която сортира числата по големина:
public class myfirstprogram{
 public static void main(String[] args){
   ArrayClass arr = new ArrayClass();
 [...]]]></description>
			<content:encoded><![CDATA[<p>Когато имаме многонишково приложение, то ние много често работим и със споделени ресурси. Нека демонстрираме с един пример &#8211; имаме масив с наредени числа. Имаме две нишки &#8211; такава, която променя числата с произволни и такава, която сортира числата по големина:<span id="more-4382"></span></p>
<pre>public class myfirstprogram{
 public static void main(String[] args){
   ArrayClass arr = new ArrayClass();
   ChangeThread c = new ChangeThread("Change Thread", arr);
   SortThread s = new SortThread("Sort Thread", arr);
   c.T.start();
   s.T.start();
   try{
     c.T.join();
     s.T.join();
   }
   catch(java.lang.InterruptedException e){}
   arr.showArray();
 }
}

class ArrayClass{
  int[] arr;
  public ArrayClass(){
    this.arr = new int[200];
    for(int i=this.arr.length-1; i&gt;=0; i--){
      this.arr[i] = i;
    }
  }
  public void changeArray(){
    for(int i=0; i&lt;this.arr.length; i++){
      this.arr[i] = (int)Math.round(Math.random()*100);
    }
    System.out.println("Change finished");
  }
  public void sortArray(){
    java.util.Arrays.sort(this.arr);
    System.out.println("Sort finished");
  }
  public void showArray(){
    for(int i: this.arr){
      System.out.print(i+" ");
    }
  }
}

class SortThread implements Runnable {
  private String threadName;
  ArrayClass arr;
  Thread T;
  public SortThread(String threadName, ArrayClass arr){
    this.threadName = threadName;
    this.T = new Thread(this, this.threadName);
    this.arr = arr;
  }
  public void run(){
    arr.sortArray();
  }
}

class ChangeThread implements Runnable {
  private String threadName;
  ArrayClass arr;
  Thread T;
  public ChangeThread(String threadName, ArrayClass arr){
    this.threadName = threadName;
    this.arr = arr;
    this.T = new Thread(this, this.threadName);
  }
  public void run() {
    arr.changeArray();
  }
}</pre>
<p>При изполнение на програмата ще забележите, че нито числата са подредени произволно, нито масива е сортиран напълно. Ще има няколко поредици от сортирани числа &#8211; нещо, което определено нито една от нишките не е пожелала.</p>
<p>За да поправим този проблем, когато една нишка &#8222;бърка&#8220; в данните на друга, то използваме т.нар. &#8222;синхронизирани методи&#8220;. Въвежда се функционалност подобна на тази при транзакциите при бази от данни &#8211; достъпваните данни се &#8222;заключват&#8220;, променят се и се &#8222;отключват&#8220; за достъп от други:</p>
<pre>public class myfirstprogram{
 public static void main(String[] args){
   ArrayClass arr = new ArrayClass();
   ChangeThread c = new ChangeThread("Change Thread", arr);
   SortThread s = new SortThread("Sort Thread", arr);
   c.T.start();
   s.T.start();
   try{
     c.T.join();
     s.T.join();
   }
   catch(java.lang.InterruptedException e){}
   arr.showArray();
 }
}

class ArrayClass{
  int[] arr;
  public ArrayClass(){
    this.arr = new int[200];
    for(int i=this.arr.length-1; i&gt;=0; i--){
      this.arr[i] = i;
    }
  }
<strong>  public synchronized void changeArray()</strong>{
    for(int i=0; i&lt;this.arr.length; i++){
      this.arr[i] = (int)Math.round(Math.random()*100);
    }
    System.out.println("Change finished");
  }
<strong>  public synchronized void sortArray()</strong>{
    java.util.Arrays.sort(this.arr);
    System.out.println("Sort finished");
  }
  public void showArray(){
    for(int i: this.arr){
      System.out.print(i+" ");
    }
  }
}

class SortThread implements Runnable {
  private String threadName;
  ArrayClass arr;
  Thread T;
  public SortThread(String threadName, ArrayClass arr){
    this.threadName = threadName;
    this.T = new Thread(this, this.threadName);
    this.arr = arr;
  }
  public void run(){
    arr.sortArray();
  }
}

class ChangeThread implements Runnable {
  private String threadName;
  ArrayClass arr;
  Thread T;
  public ChangeThread(String threadName, ArrayClass arr){
    this.threadName = threadName;
    this.arr = arr;
    this.T = new Thread(this, this.threadName);
  }
  public void run() {
    arr.changeArray();
  }
}</pre>
<p>Когато една нишка извика &#8222;синхронизиран&#8220; метод от един обект, то всички други нишки, които в същия момент извикат същия или друг синхронизиран метод от същия обект &#8222;заспиват&#8220; и изчакват изпълнението си в опашка. В този смисъл ако първо се стартира ChangeThread, то той ще извика метод changeArray(). Ако стартираме в същия момент SortThread, то той ще извика функцията sortArray(). Тъй като и двете извикани функции са синхронизирани (с ключова дума synchronized), то SortThread ще &#8222;заспи&#8220; и ще изчака ChangeThread да си свърши работата. Едва след това метод sortArray() ще бъде стартиран.</p>
<p>Единствените методи, които не могат да бъдат синхронизирани са конструкторите. При тях естествено такава операция е напълно безсмислена &#8211; не е възможно две нишки да &#8222;създават&#8220; един и същи обект едновременно.</p>
<p>В предишни статии ние споменахме за два обекта, които се различаваха по това как са реализирани техните методи. Това бяха StringBuilder (притежаващ не-синхронизирани методи) и StringBuffer (чийто методи са синхронизирани). Когато пишем подобни класове е хубаво винаги да преценяваме дали те ще бъдат използвани в многонишкови приложения или не. Синхронизирането трябва да се изпълнява много внимателно &#8211; в противен случай е напълно възможно при да се появят трудни за локализиране &#8222;бъгове&#8220; в последствие при реална експлоатация на софтуера.</p>
<p>Понякога обаче ни се налага да не правим синхронизация на абсолютно целия метод, а само на фрагмент от него. Така на практика оптимизираме програмите, като заключваме нишките за по-кратко време. Синтаксисът е следният:</p>
<pre>Оператори 1;
synchronized(&lt;име на обект&gt;){
   Оператори 2;
}
Оператори 3;</pre>
<p>По този начин по време на изпълнението на групата &#8222;Оператори 2&#8243; обектът дефиниран в блока ще бъде заключен за другите нишки. Чак след излизане от блока те ще бъдат отключени. От предишния пример бихме могли да постигнем аналогична функционалност ако променим методите changeArray и sortArray по следния начин:</p>
<pre>  public void changeArray(){
<strong>    synchronized(this){
      for(int i=0; i&lt;this.arr.length; i++){
        this.arr[i] = (int)Math.round(Math.random()*100);
      }
    }</strong>
    System.out.println("Change finished");
  }
  public void sortArray(){
<strong>    synchronized(this){
      java.util.Arrays.sort(this.arr);
    }</strong>
    System.out.println("Sort finished");
  }</pre>
<p>Виждаме, че вече сме оградили само и единствено блокът, където се прави промяна на данните. Извикването на System.out.println() не променя никакви данни и поради тази причина няма нужда да бъде изчакван от другите нишки.</p>
<p>Още повече &#8211; не е задължително блоковете да заключват всички обекти от текущия клас (в горния пример заключихме за синхронизация this). Можем да заключваме само и единствено обектът, с който работим, а именно &#8211; arr.this:</p>
<pre>  public void changeArray(){
    synchronized(<strong>this.arr</strong>){
      for(int i=0; i&lt;this.arr.length; i++){
        this.arr[i] = (int)Math.round(Math.random()*100);
      }
    }
    System.out.println("Change finished");
  }
  public void sortArray(){
    synchronized(<strong>this.arr</strong>){
      java.util.Arrays.sort(this.arr);
    }
    System.out.println("Sort finished");
  }</pre>
<p>По този начин е съвсем възможно друга нишка да достъпва и променя други &#8222;член-обекти&#8220; от текущия клас.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cphpvb.net/java/4382-threads-sync/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Нишки</title>
		<link>http://www.cphpvb.net/java/4366-threads/</link>
		<comments>http://www.cphpvb.net/java/4366-threads/#comments</comments>
		<pubDate>Fri, 16 Oct 2009 17:18:40 +0000</pubDate>
		<dc:creator>Филип Петров</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.cphpvb.net/?p=4366</guid>
		<description><![CDATA[Понятието &#8222;нишки&#8220; можете да разгледате като &#8222;разклонение на една програма на подпрограми, които работят едновременно&#8220;. Представете си например един HTTP сървър. Работата, която извършва е да прехвърля данните поискани от даден клиент. Когато обаче има повече от един клиент едновременно, то ако нямаме &#8222;многонишковост&#8220; те трябва да се изчакват един друг на &#8222;опашка&#8220;. Ако имаме [...]]]></description>
			<content:encoded><![CDATA[<p>Понятието &#8222;нишки&#8220; можете да разгледате като &#8222;разклонение на една програма на подпрограми, които работят едновременно&#8220;. Представете си например един HTTP сървър. Работата, която извършва е да прехвърля данните поискани от даден клиент. Когато обаче има повече от един клиент едновременно, то ако нямаме &#8222;многонишковост&#8220; те трябва да се изчакват един друг на &#8222;опашка&#8220;. Ако имаме един клиент, който изтегля огромно количество информация, то всички останали би трябвало да го изчакват, а това определено не е уместно. Пускането на нишка за всеки един клиент означава, че ние ще обслужваме всички с равно количество процесорно време, независимо кой се е свързал с HTTP сървъра първи, кой втори и кой последен.<span id="more-4366"></span></p>
<p>Първо трябва да правите разлика между &#8222;многонишковост&#8220; и &#8222;многозадачност&#8220;. Когато говорим за &#8222;многозадачност&#8220;, то се има в предвид изпълнението на множество &#8222;процеси&#8220; (програми) от операционната система едновременно. Тяхното &#8222;едновременно&#8220; изпълнение не се контролира от тях, а именно от операционната система. При &#8222;многонишковост&#8220; говорим за контрол над изпълнението на &#8222;подпрограми&#8220; от самата програма. Затова стартирането на едно и също приложение два пъти НЕ създава &#8222;две нишки&#8220;, а създава &#8222;два процеса&#8220;.</p>
<p>По същество всяка една програма, която демонстрирахме досега се състоеше от една единствена нишка. Тя от своя страна има право да &#8222;пусне&#8220; още една или повече нишки. Естествено никой не ограничава и те да пускат &#8222;под-нишки&#8220; от своя страна. По този начин казваме, че се създава &#8222;дърво от нишки&#8220;. Важното в случая е, че една нишка не може да съществува ако нишката, която я е извикала престане да съществува.</p>
<p>Има два начина за &#8222;пускане на нишка&#8220;. Първият е чрез наследяване на системния клас &#8222;Thread&#8220;:</p>
<pre>public class ThreadExample extends Thread{
   public void run(){
      ... оператори ...
   }
}</pre>
<p>Другият е чрез имплементирането на интерфейс Runnable:</p>
<pre>public class ThreadExample implements Runnable{
   public void run(){
      ... оператори ...
   }
}</pre>
<p>При първият метод директно наследяваме функционалността на клас Thread и добавяме детайли в неговия &#8222;run()&#8220; метод. Във втория ние само имплементираме интерфейсът Runnable, а в последствие предаваме инстанция на създадения клас като параметър за инстанция на клас Thread &#8211; така имаме доста по-голяма гъвкавост за промени и по-голям контрол. Затова ще демонстрираме първият метод само като функционалност, но ще наблегнем на вторият.</p>
<p>Интерфейсът Runnable сам по себе си не е нещо интересно. Той съдържа само и единствено дефиниция за съществуване на метод &#8222;public void run();&#8220;. С други думи думите &#8222;implements Runnable&#8220; не правят нищо друго освен да ви задължат да дефинирате нестатичен метод &#8222;run()&#8220;. Така все пак стигаме до заключението, че истинската работа с нишките се води от клас Thread. За да сме пределно ясни трябва да кажем, че самият клас Thread имплементира Runnable интерфейс!.</p>
<p>За стартиране на Thread се използва метод &#8222;start()&#8220;. Ето как би изглеждала нашата първа програма имплементираща нишки. В случая показваме двата метода за стартиране на нишка, които имплементират една и съща функционалност:</p>
<pre>public class myfirstprogram{
 public static void main(String[] args){
   // Създаване на нишка с подаден обект имплементиращ Runnable:
   Thread T1 = new Thread(new RunnableExample());
   T1.start();

   // Създаване на нишка, чрез наследник на Thread:
   ThreadExtendExample T2 = new ThreadExtendExample();
   T2.start();
 }
}

class RunnableExample implements Runnable {
  public void run() {
    System.out.println("Аз съм нишка!");
  }
}

class ThreadExtendExample extends Thread {
    public void run() {
        System.out.println("Аз също съм нишка!");
    }
}</pre>
<p>За да демонстрираме функционалността за &#8222;едновременно изпълнение&#8220;, нека напишем следната програма:</p>
<pre>public class myfirstprogram{
 public static void main(String[] args){
   Thread T1 = new Thread(new RunnableExample("T1"));
   Thread T2 = new Thread(new RunnableExample("T2"));
   T1.start();
   T2.start();
 }
}

class RunnableExample implements Runnable {
  private String threadName;
  public RunnableExample(String threadName){
    this.threadName = threadName;
  }
  public void run() {
    for (int i=0; i&lt;10; i++){
      System.out.print(this.threadName+":"+i+"; ");
    }
  }
}</pre>
<p>Едно примерно изпълнение би било следното:</p>
<pre>T1:0; T1:1; T1:2; T1:3; T1:4; <strong>T1:5; T2:0; T2:1; T2:2; T1:6;
T1:7; T1:8; T1:9; T2:3; T2:4;</strong> T2:5; T2:6; T2:7; T2:8; T2:9;</pre>
<p>Понеже операциите са сравнително прости и изключително бързи (отпечатване на число на екрана), то първите няколко отпечатани числа на екрана са от T1, а последните от T2 (както по реда на изпълнение на методите start()). Все пак обаче в средата на изходните данни се вижда ясно, че двете нишки са се &#8222;засичали&#8220; идна друга, т.е. T2.start() не е изчакало T1.start() да завърши.</p>
<p>Клас Thread съдържа важни функции за контролиране на нишките. Ще разгледаме трите основни от тях поотделно:</p>
<p><strong>1. Thread.sleep</strong>(милисекунди): &#8222;приспива&#8220; текущата нишка и дава шанс на другите нишки да работят през това време:</p>
<pre>public class myfirstprogram{
 public static void main(String[] args){
   Thread T1 = new Thread(new RunnableExample("T1"));
   Thread T2 = new Thread(new RunnableExample("T2"));
   T1.start();
   T2.start();
 }
}

class RunnableExample implements Runnable {
  private String threadName;
  public RunnableExample(String threadName){
    this.threadName = threadName;
  }
  public void run() {
    for (int i=0; i&lt;5; i++){
<strong>      try{
        Thread.sleep(1000);
        System.out.print(this.threadName+":"+i+"; ");
      }
      catch (InterruptedException e){
        return;
      }</strong>
    }
  }
}</pre>
<p>В този пример караме нишката да отпечатва съобщенията през интервали от 1 секунда. Реализирано по този начин ще видим, че изходът е много по-равномерен:</p>
<pre>T1:0; T2:0; T1:1; T2:1; T1:2; T2:2; T1:3; T2:3; T2:4; T1:4;</pre>
<p>&#8222;Приспиването на нишки&#8220; се използва често, за да се дефинира приоритет на едни нишки спрямо други. Обикновено не бихме искали една по-маловажна подпрограма да може да отнеме прекалено много ресурси и по този начин да забавя работата на всички останали. Вземете за пример сайт за обработка на видео в който има два вида потребители &#8211; такива, които гледат видео клипове и такива, които ги качват и сървъра в последствие в отделна нишка преработва в удобен файлов формат. Определено &#8222;гледането на клипове&#8220; са приоритетни операции спрямо преработката на клипове (тя може да се забави в зависимост от натовареността на системата). Затова можем да &#8222;приспиваме&#8220; нишките за преработка на видео за малки периоди от време и по този начин да даваме &#8222;повече въздух&#8220; на тези, която изпращат клиповете до потрибителите за гледане.</p>
<p><strong>2. Thread.interrupt()</strong>: Изключението от тип InterruptedException вече подсказа, че нишките могат да се прекъсват. Попринцип в старите версии на Java съществува метод Thread.stop(), но силно препоръчваме да го забравите веднага (ако работите с по-нова версия на Java, той вече не може да се използва тъй или иначе). Остарелия метод Thread.stop() прекъсваше нишката моментално. Досещате се, че по този начин можем лесно да загубим данни, да оставим файлове отворени и всякакви други поразии на софтуера. Вместо това по-добрият метод е &#8222;да кажем на нишката, че трябва да прекъсне&#8220;, тя да си довърши работата и да се спре сама. Можете веднага да си направите аналогия с разликата нормално затваряне на програма и &#8222;убиване на процеса&#8220; чрез операционната система.</p>
<p>Когато извикаме метод &#8222;Thread.interrupt()&#8220;, то ние именно &#8222;съобщаваме&#8220; на нишката, че трябва да спре. После в самата нишка ние трябва да имаме метод за прихващане на това събитие. Ако вътре в самата нишка използваме Object.wait(), Thread.join() или Thread.sleep() и ние извикаме прекъсване, то тези методи &#8222;виждат&#8220; прекъсването и автоматично &#8222;хвърлят&#8220; InterruptedException. Именно затова когато извиквахме &#8222;Thread.sleep()&#8220; в миналия пример ние трябваше да прихванем това изключение. В него просто извикахме &#8222;return&#8220; и приключихме изпълнението на нишката. Нека дадем един пример:</p>
<pre>public class myfirstprogram{
 public static void main(String[] args){
   Thread T1 = new Thread(new RunnableExample("T1"));
   T1.start();

   try{
     Thread.sleep(500);
   }
   catch(InterruptedException e){}

   System.out.println("Interrupting thread...");
   T1.interrupt();
 }
}

class RunnableExample implements Runnable {
  private String threadName;
  public RunnableExample(String threadName){
    this.threadName = threadName;
  }
  public void run() {
    for (int i=0; i&lt;50; i++){
      try{
        Thread.sleep(100);
        System.out.println(this.threadName+":"+i+"; ");
      }
      catch (InterruptedException e){
        System.out.println("I was interrupted!");
        return;
      }
    }
  }
}</pre>
<p>Резултатът от изпълнението ще бъде:</p>
<pre>T1:0;
T1:1;
T1:2;
T1:3;
Interrupting thread...
I was interrupted!</pre>
<p>След малко практика ще забележите обаче, че прекъсването никак не настъпва моментално. Вземете например този пример:</p>
<pre>public class myfirstprogram{
 public static void main(String[] args){
   Thread T1 = new Thread(new RunnableExample("T1"));
   T1.start();

   try{
     Thread.sleep(50);
   }
   catch(InterruptedException e){}

   System.out.println("Interrupting thread");
   T1.interrupt();
 }
}

class RunnableExample implements Runnable {
  private String threadName;
  public RunnableExample(String threadName){
    this.threadName = threadName;
  }
  public void run() {
    while(true){
      try{
        Thread.sleep(1);
        double time = System.currentTimeMillis();
        while(System.currentTimeMillis()-time &lt; 5000){
          System.out.print(".");
        }
        System.out.println();
      }
      catch (InterruptedException e){
        System.out.println("I was interrupted!");
        return;
      }
    }
  }
}</pre>
<p>Нишката изписва точки на екрана в продължение на 5 секунди, след това заспива за 1 милисекунда и продължава да изписва точки на екрана за нови 5 секунди (и така до безкрайност, защото всичко се намира в цикъл while(true)). В главната програма ние стартираме тази нишка, изчакваме 5 милисекунди и я прекъсваме, чрез T1.interrupt(). Ще забележите обаче, че нишката ще продължи да изписва точки на екрана още няколко секунди преди да спре. Това е защото най-вероятно прекъсването ще съвпадне с изпълнението на тялото на вложения while цикъл, който още не е завършил. Чак когато се достигне до Thread.sleep(1) ще бъде хвърлен InterruptedException, който ще бъде прихванат и нишката ще спре според инструкциите в секцията catch.</p>
<p>Какво обаче да правим ако не използваме Thread.sleep() или други готови методи, които следят за interrupt или пък сме започнали алгоритъм, който ще изиска много време преди да се &#8222;върнем обратно&#8220; към някоя от тези методи? Естествено се досещаме, че сами можем да следим дали нишката е прекъсната. Ако използваме наследяване на клас Thread, то ни върши работа метод &#8222;interrupted()&#8220; връщащ boolean. В нашата имплементация можем да я реализираме сами:</p>
<pre>public class myfirstprogram{
 public static void main(String[] args){
<strong>   RunnableExample T1Obj = new RunnableExample("T1");
   Thread T1Thread = new Thread(T1Obj);</strong>
   T1Thread.start();

   try{
     Thread.sleep(50);
   }
   catch(InterruptedException e){}

   System.out.println("Interrupting thread");
<strong>   T1Obj.interrupt();
   T1Thread.interrupt();</strong>
 }
}

class RunnableExample implements Runnable {
<strong>  private boolean isInterrupted;</strong>
  String threadName;
  public RunnableExample(String threadName){
    this.threadName = threadName;
    this.isInterrupted = false;
  }
<strong>  public void interrupt(){
    this.isInterrupted = true;
  }</strong>
  private void stopMsg(){
    System.out.println("I was interrupted!");
  }
  public void run() {
    while(<strong>this.isInterrupted == false</strong>){
      try{
        Thread.sleep(1);
        double time = System.currentTimeMillis();
        while(System.currentTimeMillis()-time &lt; 5000 <strong>&amp;&amp; this.isInterrupted == false</strong>){
          System.out.print(".");
        }
        System.out.println();
      }
      catch (InterruptedException e){
        this.stopMsg();
        return;
      }
    }
    stopMsg();
  }
}</pre>
<p>Казано накратко ние създаваме една променлива, която ни съобщава дали нишката е прекъсната или не. После в самата програма ние трябва периодично да проверяваме дали случайно тя не е променена.</p>
<p><strong>3. Thread.join</strong>() &#8211; не-статичен метод на обект от тип Thread. Когато бъде извикан, то извикващата нишка трябва да изчака изпълнението на притежателя на метода. По този начин нишките се изчакват една друга. Нека се върнем към един по-стар пример, но този път го преработим така, че класът RunnableExample сам в себе си съдържа нишката си (всъщност много често използвана практика):</p>
<pre>public class myfirstprogram{
 public static void main(String[] args){
   RunnableExample T1 = new RunnableExample("T1");
   RunnableExample T2 = new RunnableExample("T2");
   // Стартираме нишките като член-променливи на класовете
   T1.T.start();
   T2.T.start();
   // Караме главната програма да "заспи" докато свършат нишките
   try{
<strong>     T1.T.join();
     T2.T.join();</strong>
   }
   catch(InterruptedException e){
     System.out.println("What? Interrupt should NOT happen at all!");
   }
   System.out.println("Main finished!");
 }
}

class RunnableExample implements Runnable {
  private String threadName;
  // Вече нишката е член-променлива на класа
  Thread T;
  public RunnableExample(String threadName){
    this.threadName = threadName;
    // Инициализираме нишката вътре в конструктора
    this.T = new Thread(this, this.threadName);
  }
  public void run() {
    for (int i=0; i&lt;10; i++){
      try{
        // Нарочно слагаме произволно време за "заспиване"
        Thread.sleep((int)Math.random()*1000);
        System.out.print(this.threadName+":"+i+"; ");
      }
      catch (InterruptedException e){
        return;
      }
    }
    System.out.println("\nThread "+this.threadName+ "finished!");
  }
}</pre>
<p>Ще видите, че main() метода ще изчака докато и двете нишки са свършили и чак тогава ще изпише съобщението &#8222;Main finished!&#8220;.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cphpvb.net/java/4366-threads/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Константи в Java</title>
		<link>http://www.cphpvb.net/java/4354-final-in-java/</link>
		<comments>http://www.cphpvb.net/java/4354-final-in-java/#comments</comments>
		<pubDate>Thu, 15 Oct 2009 19:53:04 +0000</pubDate>
		<dc:creator>Филип Петров</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.cphpvb.net/?p=4354</guid>
		<description><![CDATA[Константите в Java се създават чрез ключова дума &#8222;final&#8220;. Както е по дефиниция &#8211; константите задължително се инициализират и след това не могат да бъдат променяни:
   final int x = 5;
   // x = 6; &#60;&#60;&#60; Не е възможно
В Java обаче е възможно константата само да се декларира и да бъде [...]]]></description>
			<content:encoded><![CDATA[<p>Константите в Java се създават чрез ключова дума &#8222;final&#8220;. Както е по дефиниция &#8211; константите задължително се инициализират и след това не могат да бъдат променяни:</p>
<pre>   final int x = 5;
   // x = 6; &lt;&lt;&lt; Не е възможно</pre>
<p>В Java обаче е възможно константата само да се декларира и да бъде инициализирана по-късно:<span id="more-4354"></span></p>
<pre>   final int x;
   java.util.Scanner s = new java.util.Scanner(System.in);
   x = s.nextInt();</pre>
<p>Все пак след тази инициализация x не може да бъде променяна, т.е. се държи напълно като константа. Така всъщност казваме, че имаме два типа константи:</p>
<ol>
<li>Създадени по време на компилация (първия пример);</li>
<li>Създадени по време на изпълнение (втория пример).</li>
</ol>
<p>Ключовата дума final важи за всякакви променливи:</p>
<pre>public class finalExample{
 public static void main(String[] args){
   final A obj = new A(3);
   obj.x = 5; // Няма проблем - x не е константа
   // obj = null;  &lt;&lt;&lt; невъзможно - obj е константа!
 }
}

final class A{
  public int x;
  public A(int x){
    this.x = x;
  }
}</pre>
<p>В случая виждаме, че полетата на самия обект не са непроменяеми. Не може да бъде променен само адреса на променливата, която &#8222;сочи&#8220; към този обект. Въпреки, че това е възможно, то въобще не се използва.</p>
<p>В Java съществуват още две понятия &#8211; константни методи и константни класове. Те също използват ключовата дума final.</p>
<p><strong>Константните методи</strong> не могат да бъдат предефинирани в клас-наследник. Например:</p>
<pre>class A{
  public A(){}
  final void func(){
    System.out.println("Final function!");
  }
}

class B extends A{
  public B(){ super(); }
  // <em>func() in B cannot override func() in A;</em>
  // <em>overridden method is final</em>
  // void func(){
  //  System.out.println("Predefined?");
  // }
}</pre>
<p><strong>Константните класове</strong> пък са такива класове, които не могат да бъдат наследявани:</p>
<pre>final class A{
  public A(){}
}

// <em>cannot inherit from final A</em>
//class B extends A{
//  public B(){ super(); }
//}</pre>
<p>В общи линии константни методи и константни класове се създават тогава, когато желаем сигурност на данните и искаме да сме уверени, че никой няма да създава обекти подобни на нашите, но с променена функционалност.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cphpvb.net/java/4354-final-in-java/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Garbage Collection и метод finalize()</title>
		<link>http://www.cphpvb.net/java/4346-garbage-collection-and-finalize/</link>
		<comments>http://www.cphpvb.net/java/4346-garbage-collection-and-finalize/#comments</comments>
		<pubDate>Thu, 15 Oct 2009 19:21:14 +0000</pubDate>
		<dc:creator>Филип Петров</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.cphpvb.net/?p=4346</guid>
		<description><![CDATA[В програмният език C++ съществува понятието &#8222;деструктор&#8220; &#8211; метод който &#8222;почиства&#8220; преди даден обект да бъде унищожен. Такъв метод е често използван там, защото в C++ програмистът сам контролира кога един обект да бъде изтрит (чрез &#8222;обратния&#8220; на new оператор delete).
В Java програмистът няма контрол над унищожението на обектите. Цялата тежест над това е прехвърлена [...]]]></description>
			<content:encoded><![CDATA[<p>В програмният език C++ съществува понятието &#8222;деструктор&#8220; &#8211; метод който &#8222;почиства&#8220; преди даден обект да бъде унищожен. Такъв метод е често използван там, защото в C++ програмистът сам контролира кога един обект да бъде изтрит (чрез &#8222;обратния&#8220; на new оператор delete).</p>
<p>В Java програмистът няма контрол над унищожението на обектите. Цялата тежест над това е прехвърлена върху т.нар. Garbage Collector. Въпреки това има аналогия на &#8222;почистващ метод&#8220; (деструктор) &#8211; това е метод &#8222;finalize()&#8220;. Именно той се извиква в момента, в който даден обект е изтрит от Garbage Collector.</p>
<p>За съжаление програмистите, които преминават от C++ към Java ще забележат бързо, че Garbage Collector не се извиква често. При по-малките програми даже е възможно да не бъде извикан въобще. Ето ви един пример:<span id="more-4346"></span></p>
<pre>public class GCExample{
 public static void main(String[] args){
   User[] u = new User[200];
   for (int i=0; i&lt;u.length; i++){
     u[i] = new User();
   }
   System.out.println("Users online: "+User.count);
   System.out.println("Last id: "+User.lastid);
   // "Изтриваме" 10ти елемент
   u[10] = null;
   System.out.println("Users online: "+User.count);
   System.out.println("Last id: "+User.lastid);
 }
}

class User{
  public static int count = 0;
  public static int lastid = 0;
  public int id;
  public User(){
    this.count++;
    this.lastid++;
    this.id = lastid;
  }
  protected void finalize(){
    count--;
  }
}</pre>
<p>Освен ако нямате завиден късмет Garbage Collector да се е включил, то резултатът ще бъде следния:</p>
<pre>Users online: 200
Last id: 200
Users online: 200
Last id: 200</pre>
<p>Виждате, че finalize() методът не е извикан въобще. Това е защото Garbage Collector не се е стартирал все още. Честно казано при тази програма той не би трябвало да се включи въобще. Опитайте чрез добавянето на следния код:</p>
<pre>   while(User.count == 200){}
   System.out.println("Garbage Collection was triggered!");</pre>
<p>Програмата почти 100% сигурно ще зацикли и никога няма да спре. Ако обаче ние собственоръчно предизвикаме Garbage Collector да се включи, то ще видим, че всичко е наред:</p>
<pre>public class GCExample{
 public static void main(String[] args){
   User[] u = new User[200];
   for (int i=0; i&lt;u.length; i++){
     u[i] = new User();
   }
   System.out.println("Users online: "+User.count);
   System.out.println("Last id: "+User.lastid);
   // "Изтриваме" 10ти елемент
   u[10] = null;
<strong>   System.gc();
   System.runFinalization();</strong>
   System.out.println("Users online: "+User.count);
   System.out.println("Last id: "+User.lastid);
 }
}</pre>
<p>Тук резултатът категорично сигурно ще бъде:</p>
<pre>Users online: 200
Last id: 200
Users online: 199
Last id: 200</pre>
<p>Метод System.gc() извиква Garbage Collector, а System.runFinalization() го кара да почисти абсолютно всичко. Метод finalize() се е изпълнил и е намалил променливата usersonline с единица &#8211; точно това, което желаехме. Сега трябва да отговорим на въпроса &#8222;защо Garbage Collector не се включи в предишния пример&#8220; и по-скоро &#8222;кога въобще се включва Garbage Collector сам&#8220;.</p>
<p>Отговорът е, че Garbage Collector се включва само и единствено тогава, когато е нужно освобождаване на памет. С други думи &#8211; ако JVM изпита недостиг на памет, то тя си освобождава такава, като почиства &#8222;боклука&#8220;. Ето един пример, с който ще демонстрираме това:</p>
<pre>public class GCExample{
 public static void main(String[] args){
   while (GCExample.continueFlag){
     new GCExample();
   }
   // "приспиваме програмата за 2 секунди
   try{
     Thread.sleep(2000);
   }
   catch(Exception e){}
   System.out.println(GCExample.ObjectsCreated);
   System.out.println(GCExample.ObjectsDeleted);
 }
}

class GCExample{
  public static boolean continueFlag = true;
  public static int ObjectsCreated = 0;
  public static int ObjectsDeleted = 0;
  public GCExample(){
    this.ObjectsCreated++;
  }
  protected void finalize(){
    continueFlag = false;
    ObjectsDeleted++;
  }
}</pre>
<p>Едно примерно изпълнение би върнало следния резултат:</p>
<pre>18580
1917</pre>
<p>Виждаме, че общо са се създали 18580 обекта и чак тогава се е включил Garbage Collector. Към нито един от тези създадени обекти няма насочена променлива, но въпреки това Garbage Collector е &#8222;събрал&#8220; само 1917 от тях. Нарочно &#8222;приспахме&#8220; програмата за 2 секунди, за да сме сигурни, че той си е свършил работата и не сме го прекъснали с край на програмата ни. Нека все пак демонстрираме, че System.runFinalization() работи коректно:</p>
<pre>public class GCExample{
 public static void main(String[] args){
   while (GCExample.continueFlag){
     new GCExample();
   }
   try{
     Thread.sleep(2000);
   }
   catch(Exception e){}

<strong>   System.gc();
   System.runFinalization();</strong>

   System.out.println(GCExample.ObjectsCreated);
   System.out.println(GCExample.ObjectsDeleted);
 }
}</pre>
<p>Резултатът тук вече очаквано ще бъде нещо подобно на:</p>
<pre>18688
18688</pre>
<p>Първоначално бихме си казали, че &#8222;щом желаем да изтрием обект, то можем винаги да си извикваме сами System.gc() и System.runFinalization()&#8220;. Това обаче е лоша практика, понеже &#8222;събирането на боклука&#8220; всъщност е доста тежка процедура, която отнема доста ресурси &#8211; повече от процедурата на създаване на обект:</p>
<pre>public class GCExample{
 public static void main(String[] args){
   long startCreateObjectsTime = System.currentTimeMillis();
   while (GCExample.continueFlag){
     new GCExample();
   }
   long endCreateObjectsTime = System.currentTimeMillis();

   long startGCTime = System.currentTimeMillis();
   System.gc();
   System.runFinalization();
   long endGCTime = System.currentTimeMillis();

   System.out.println(endCreateObjectsTime - startCreateObjectsTime);
   System.out.println(endGCTime - startGCTime);
 }
}</pre>
<p>Примерен резултат е:</p>
<pre>15
94</pre>
<p>С други думи събирането на боклука е отнело в пъти повече време отколкото създаването на обектите, които биват изтрити. Затова е редно да избягваме честото ръчно пускане на Garbage Collection. Всъщност именно концепцията за Garbage Collection и липсата на възможност за контрол над изтриване на обектите е една от най-честите критики към езика Java.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cphpvb.net/java/4346-garbage-collection-and-finalize/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Статични вложени член класове</title>
		<link>http://www.cphpvb.net/java/4336-static-nested-classes/</link>
		<comments>http://www.cphpvb.net/java/4336-static-nested-classes/#comments</comments>
		<pubDate>Thu, 15 Oct 2009 16:47:35 +0000</pubDate>
		<dc:creator>Филип Петров</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.cphpvb.net/?p=4336</guid>
		<description><![CDATA[Вече се запознахме с не-статични вложени член класове. За дефинирането на понятието &#8222;статичен вложен член клас&#8220; е много подходяща аналогията с вече познатите статични методи. Нека си припомним главните характеристики на статичните методи:

Те са общовалидни за всички инстанции на класа;
Могат да бъдат извиквани без инстанция;
Могат да променят и достъпват всички статични полета на класа;
Не могат [...]]]></description>
			<content:encoded><![CDATA[<p>Вече се запознахме с <a href="http://www.cphpvb.net/java/4314-sub-packages-and-nested-classes/" target="_blank">не-статични вложени член класове</a>. За дефинирането на понятието &#8222;статичен вложен член клас&#8220; е много подходяща аналогията с вече познатите статични методи. Нека си припомним главните характеристики на статичните методи:</p>
<ol>
<li>Те са общовалидни за всички инстанции на класа;</li>
<li>Могат да бъдат извиквани без инстанция;</li>
<li>Могат да променят и достъпват всички статични полета на класа;</li>
<li>Не могат да променят или достъпват не-статични полета и методи на класа.</li>
</ol>
<p>Абсолютно същите принципи са валидни и за статичните класове. Когато един вложен клас е статичен то той:</p>
<ol>
<li>Е общовалиден за всички инстанции на външния клас;</li>
<li>Има достъп само до статичните полета и методи на своя външен клас;</li>
<li>Може да бъде инициализиран без да е нужна инстанция на външния клас;</li>
<li>Няма достъп до не-статичните полета и методи на своя външен клас.</li>
</ol>
<p>Нека дадем един пример за клас, който има два вложени класа &#8211; статичен и не-статичен:<span id="more-4336"></span></p>
<pre>public class innerClassesExample{
 public static void main(String[] args){
   // Инстанция на външен клас
   MessagesClass m = new MessagesClass("testing outer class");
   // Извикване на не-статичен метод от външен клас
   m.getMsg();
   // Извикване на статичен метод от външен клас чрез инстанция
   m.defaultMessage();
   // Извикване на статичен метод от външен клас без инстанция
   MessagesClass.defaultMessage();
   // Инстанция на вложен не-статичен клас
   MessagesClass.InnerClass m2 =
          m.new InnerClass("testing inner non-static class");
   // Извикване на не-статичен метод от вложен не-статичен клас
   m2.innerNonStaticMethod();
   // Инстанция на вложен статичен клас:
   MessagesClass.InnerStaticClass m3 =
          new MessagesClass.InnerStaticClass("testing inner static class");
   // Извикване на не-статичен метод от вложен статичен клас:
   m3.innerStaticClassNonStaticMethod();
   // Извикване на статичен метод от вложен статичен клас:
   m3.innerStaticClassStaticMethod();
   // Извикване на статичен метод от вложен статичен клас без инстанция:
   MessagesClass.InnerStaticClass.innerStaticClassStaticMethod();
 }
}

class MessagesClass{
  private String msg;
  public MessagesClass(String msg){
    this.msg=msg;
  }
  void getMsg(){
    System.out.println(this.msg);
  }
  static void defaultMessage(){
    System.out.println("Hello World");
  }

  class InnerClass{
    String innerNonStaticClassMsg;
    public InnerClass(String msg){
      this.innerNonStaticClassMsg = msg;
    }
    public void innerNonStaticMethod(){
      System.out.println("Innerclass.msg: "+this.innerNonStaticClassMsg);
      System.out.println("Innerclass calls Outerclass.msg: "+msg);
      System.out.print("Innerclass calls Outerclass.getMsg(): ");
      getMsg();
      System.out.print("Innerclass calls Outerclass.defaultMessage(): ");
      defaultMessage();
    }
  }

  static class InnerStaticClass{
    String innerStaticClassMsg;
    public InnerStaticClass(String msg){
      this.innerStaticClassMsg = msg;
    }
    public void innerStaticClassNonStaticMethod(){
      System.out.println("InnerStaticClass.msg: "+this.innerStaticClassMsg);
<em>      // System.out.println("InnerStaticClass calls Outerclass.msg: "+msg); &lt;&lt;&lt; No
      // System.out.print("InnerStaticClass calls Outerclass.getMsg(): "); &lt;&lt;&lt; No
      // getMsg(); &lt;&lt;&lt; No</em>
      System.out.print("InnerStaticClass calls Outerclass.defaultMessage(): ");
      defaultMessage();
    }
    public static void innerStaticClassStaticMethod(){
<em>      // System.out.println("InnerStaticClass.msg: "+this.innerStaticClassMsg); &lt;&lt;&lt; No
      // System.out.println("InnerStaticClass calls Outerclass.msg: "+msg); &lt;&lt;&lt; No
      // System.out.print("InnerStaticClass calls Outerclass.getMsg(): "); &lt;&lt;&lt; No
      // getMsg(); &lt;&lt;&lt; No</em>
      System.out.print("InnerStaticClass calls Outerclass.defaultMessage(): ");
      defaultMessage();
    }
  }
}</pre>
<p>С този пример всъщност демонстрирахме друго важно свойство на вложените класове &#8211; те имат достъп до private променливите на външните им класове. Вложените не-статични класове не могат да имат статични методи (за разлика от вложените статични класове).</p>
<p>Нека се опитаме да направим една аналогия за двата типа вложени член класове:</p>
<ul>
<li> Ако един вложен клас е не е статичен, то можем да си го представим като подпрограма, която зависи пряко от инстанцията на главната програма;</li>
<li>Ако един вложен клас е статичен, то можем да си го представим отново като подпрограма, която може да бъде изпълнена и самостоятелно, независимо от инстанциите на главната програма.</li>
</ul>
<p>Най-често в практиката ако създаваме не-статични вложени член класове, те ще са private, т.е. за вътрешно ползване от външния им клас. Обратно &#8211; по-често ако пишем вложени статични класове, то те ще са публични.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cphpvb.net/java/4336-static-nested-classes/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Анонимни класове в Java</title>
		<link>http://www.cphpvb.net/java/4318-anonymous-classes-java/</link>
		<comments>http://www.cphpvb.net/java/4318-anonymous-classes-java/#comments</comments>
		<pubDate>Thu, 15 Oct 2009 04:18:31 +0000</pubDate>
		<dc:creator>Филип Петров</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.cphpvb.net/?p=4318</guid>
		<description><![CDATA[&#8222;Анонимните&#8220; класове са специален вид локални класове. Специалното при тях е, че те нямат специални имена и на тях винаги правим само една инстанция.
Нека вземем за пример изчисляването на дължината на хипутенизата на правоъгълен триъгълник. &#8222;Не-анонимно&#8220; би било извикването на програмата:
    double a = 3;
    double b = 4;
 [...]]]></description>
			<content:encoded><![CDATA[<p>&#8222;Анонимните&#8220; класове са специален вид <a href="http://www.cphpvb.net/java/4319-local-classes-java/">локални класове</a>. Специалното при тях е, че те нямат специални имена и на тях винаги правим само една инстанция.</p>
<p>Нека вземем за пример изчисляването на дължината на хипутенизата на правоъгълен триъгълник. &#8222;Не-анонимно&#8220; би било извикването на програмата:</p>
<pre>    double a = 3;
    double b = 4;
    double c = Math.sqrt(a*a + b*b);
    System.out.println(c);</pre>
<p>Защо обаче трябва да пазим променливата &#8222;c&#8220; ако не я използваме повече в програмата след това? Ако тя не ни е нужна, то ние по-често изписваме:<span id="more-4318"></span></p>
<pre>    double a = 3;
    double b = 4;
    System.out.println(Math.sqrt(a*a + b*b));</pre>
<p>Така променливата &#8222;хипутенуза&#8220; се създава в момента на извикването на System.out.println. Ние не сме дали име на стойността, която предаваме като параметър, тоест ние не можем да я използваме по-нататък. Това е съвсем нормално ако не се нуждаем от тази стойност отново. Това е пример за анонимни локални променливи.</p>
<p>Анонимните локални класове действат по същия принцип. Всъщност ние вече неведнъж ги демонстрирахме, но неявно. Например когато дефинирахме буферирани потоци към файлове:</p>
<pre>    BufferedReader in = new BufferedReader(<strong>new FileReader("text.txt")</strong>);</pre>
<p>Ние на практика не сме подали никакво име на файловия поток. Така той се използва само и единствено от създадения буфер.</p>
<p>Ето и как самите ние можем да създадем анонимен клас:</p>
<pre>public class AnonymousClassExample{
  public static void main(String[] args){
    Dog sparky = new Dog("Sparky");
    sparky.speak();
    sparky.play();

<strong>    Dog terry = new Dog("Terry"){
      public void speak(){
        System.out.println("This dog do not want to speak");
      }
    };</strong>
    terry.speak();
    terry.play();
  }
}

interface AnimalsActions{
  public void speak();
  public void play();
}

abstract class Animal{
  private String name;
  public Animal(String name){
    this.name = name;
  }
  abstract void speak();
  abstract void play();
}

class Dog extends Animal implements AnimalsActions{
  public Dog(String name){
    super(name);
  }
  public void speak(){
    System.out.println("Woof!");
  }
  public void play(){
    System.out.println("The dog rolled over...");
  }
}</pre>
<p>Виждате, че при декларирането на второто куче ние предефинирахме метод &#8222;speak&#8220;. Чисто функционално това е все едно, че сме наследили клас Dog в нов локален клас и сме предефинирали негов метод. Тъй като на този нов локален клас не сме дали формално ново име, то той се нарича &#8222;анонимен клас&#8220;.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cphpvb.net/java/4318-anonymous-classes-java/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Локални класове в Java</title>
		<link>http://www.cphpvb.net/java/4319-local-classes-java/</link>
		<comments>http://www.cphpvb.net/java/4319-local-classes-java/#comments</comments>
		<pubDate>Wed, 14 Oct 2009 17:34:33 +0000</pubDate>
		<dc:creator>Филип Петров</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.cphpvb.net/?p=4319</guid>
		<description><![CDATA[&#8222;Локални&#8220; класове са тези класове, които са дефинирани вътре в програмен блок. Досега знаем как да влагаме класове като членове на даден клас. Когато говорим за локални класове, то се има в предвид клас, който е създаден в тялото на метод. На пръв поглед това е доста странно и нетипично място за дефиниране на клас.
Първото [...]]]></description>
			<content:encoded><![CDATA[<p>&#8222;Локални&#8220; класове са тези класове, които са дефинирани вътре в програмен блок. Досега знаем как да влагаме класове като членове на даден клас. Когато говорим за локални класове, то се има в предвид клас, който е създаден в тялото на метод. На пръв поглед това е доста странно и нетипично място за дефиниране на клас.</p>
<p>Първото и може би най-важно нещо, което трябва да знаем за локалните класове е, че те са валидни само и единствено в програмния блок, в който са дефинирани. Можете веднага да си направите аналогия с &#8222;локални променливи&#8220;.<span id="more-4319"></span></p>
<p>Ето един пример за локален клас, вложен в метод на клас &#8222;потребители&#8220;:</p>
<pre>public class myfirstprogram{
  public static void main(String[] args){
    User ivan = new User("ivan", 8);
    System.out.println(ivan.getUser()+" has pass "+ivan.getPass());
  }
}

class User{
  private String username;
  private String password;
  private int id;
  private static int userscount = 0;
  public User(String username, int len){
    System.out.println("\nNew user entering...");
    this.username = username;
    this.setId();
    this.setPass(len);
  }
  private void setId(){
    userscount++;
    this.id = userscount;
  }
  private void setPass(int len){
    // Дефинираме локален клас "генератор на пароли"
<strong>    class PassGenerator{
      StringBuffer strb;
      public PassGenerator(int len){
        strb = new StringBuffer(len);
        for (int i=0; i&lt;len; i++){
          switch((int)Math.round(Math.random()*2)){
            case 0:
              this.strb.append((char)((int)'0' + (int)(Math.round(Math.random()*9))));
              break;
            case 1:
              this.strb.append((char)((int)'a' + (int)(Math.round(Math.random()*25))));
              break;
            case 2:
              this.strb.append((char)((int)'A' + (int)(Math.round(Math.random()*25))));
              break;
          }
        }
      }
      public String getpass(){
        return strb.toString();
      }
    }</strong>
    // Правим локална инстанция на този локален клас
    PassGenerator p = new PassGenerator(len);
    this.password = p.getpass();
  }
  protected String getPass(){
    return this.password;
  }
  public int getId(){
    return this.id;
  }
  public String getUser(){
    return this.username;
  }
  public static void usersOnline(){
    System.out.println("Users online: "+userscount);
  }
}</pre>
<p>Не можем да правим инстанция на клас &#8222;PassGenerator&#8220; некъде другаде освен вътре в метод &#8222;setPass&#8220;, където този локален клас е деклариран. По този начин казваме, че &#8222;PassGenerator е локален клас за метод setPass&#8220;.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cphpvb.net/java/4319-local-classes-java/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Под-пакети и не-статични вложени член класове</title>
		<link>http://www.cphpvb.net/java/4314-sub-packages-and-nested-classes/</link>
		<comments>http://www.cphpvb.net/java/4314-sub-packages-and-nested-classes/#comments</comments>
		<pubDate>Wed, 14 Oct 2009 12:39:56 +0000</pubDate>
		<dc:creator>Филип Петров</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.cphpvb.net/?p=4314</guid>
		<description><![CDATA[В статията класове и пакети на Java показахме примери за създаване на един пакет и няколко класа вътре в него. Също така по-късно показахме как е възможно едни класове да наследяват други. Що се отнася до пакетите &#8211; беше споменато, но не докрай обяснено, че те следват йерархичната структура на директории и под-директории. Сега ще [...]]]></description>
			<content:encoded><![CDATA[<p>В статията <a href="http://www.cphpvb.net/java/4224-classes-and-packages/" target="_blank">класове и пакети на Java</a> показахме примери за създаване на един пакет и няколко класа вътре в него. Също така по-късно показахме как е възможно едни класове да <a href="http://www.cphpvb.net/java/4249-inheritance-in-java/" target="_blank">наследяват</a> други. Що се отнася до пакетите &#8211; беше споменато, но не докрай обяснено, че те следват йерархичната структура на директории и под-директории. Сега ще обясним всичко по-подробно, но първо ще се спрем върху няколко основни неща свързани с организацията при изпълнението на програми.</p>
<p>Всяка виртуална машина на Java идва с определен набор (всъщност доста голям) от готови пакети с класове. Такива пакети са java.io (съдържат класове работещи с входно-изходни устройства), java.net (класове свързани с мрежови потоци) и т.н. Забелязвате веднага, че &#8222;системните&#8220; за Java класове започват винаги с думата &#8222;java&#8220; последвана от точка и конкретизиране на името на друг пакет (io, net, &#8230;). Тук вече имаме наличие на йерархия &#8211; системния пакет java има свой под-пакет io, който пък притежава клас FileInputStream. За да достигнем до този клас ние указваме пълният път до него &#8211; java.io.FileInputStream.<span id="more-4314"></span></p>
<p>Когато пишем наша собствена програма ние обикновено я слагаме извън системната директория на JDK, например в &#8222;c:\myprogram&#8220;. Нека за пример в тази директория сме сложили един клас HelloWorld.class, който показва тривиалното съобщение на екрана. Ако изпълните този клас от текущата директория, програмата ще стартира:</p>
<p><em>Файл HelloWorld.java намиращ се в c:\myprogram</em>:</p>
<pre>package myprogram;

public class HelloWorld{
 public static void main(String[] args){
  System.out.println("Hello World");
 }
}</pre>
<p>Поредица от команди и тяхното изпълнение:</p>
<pre>C:\&gt; cd myprogram
C:\myprogram&gt; dir
<em>14.10.2009 a.  13:52    &lt;:DIR&gt;:          .
14.10.2009 a.  13:52    &lt;:DIR&gt;:          ..
14.10.2009 a.  13:44               133 HelloWorld.java
               1 File(s)           133 bytes
               2 Dir(s)  213 201 666 048 bytes free
</em>C:\myprogram&gt; javac HelloWorld.java
C:\myprogram&gt; cd ..
C:\&gt; java myprogram.HelloWorld
<em>Hello World</em>
C:\&gt;</pre>
<p>С програмата javac ние компилирахме файл HelloWorld.java и създадохме байткод HelloWorld.class. Чрез програмата java ние заредихме main метода на този клас. Виждате, че достъпваме класа чрез пътя до него &#8211; &#8222;myprogram.HelloWorld&#8220;.</p>
<p>Ето какво ще стане обаче ако се намираме в друга директория, различна от c:\:</p>
<pre>C:\&gt; cd temp
C:\temp&gt; java myprogram.HelloWorld
<em>Exception in thread "main" java.lang.NoClassDefFoundError: myprogram/HelloWorld
Caused by: java.lang.ClassNotFoundException: myprogram.HelloWorld
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClassInternal(Unknown Source)
Could not find the main class: myprogram.HelloWorld.  Program will exit.
</em>C:\temp&gt;</pre>
<p>Явно е, че виртуалната машина не може да намери нашият клас. Логично е да се досетим защо се получава така &#8211; в директория temp, в която се намираме, няма поддиректория myprogram и няма файл HelloWorld.class. Веднага можете да си направите аналогия със стандартните програми с разширение &#8222;.exe&#8220; &#8211; можете да стартирате изпълнимия файл от неговата собствена директория.</p>
<p>Когато говорим за пакети обаче аналогията с изпълнимите .exe файлове не е достатъчна. Освен, че изпълняваме класове от някой пакет като програми (т.е. класове с main метод), ние често използваме класовете и като библиотеки. Става въпрос за абсолютно същия принцип, по който вие създавахте обект java.util.Scanner или още повече &#8211; правихте import &#8222;java.util.*&#8220; и вмъквахте множество от класове от един пакет. Естествено е, че бихме желали да можем да правим същото и с наши класове и пакети и не желаем да сме ограничени да работим само и единствено в една главна директория. Например може да пожелаем да вмъкнем клас SayHello от директория c:\myprogram2 в нашия клас HelloWorld от директория c:\myprogram.</p>
<p>За щастие java ни дава такава функционалност чрез environment variable &#8222;CLASSPATH&#8220;. По подразбиране в тази променлива е записана само &#8222;.&#8220;, т.е. &#8222;текущата директория&#8220;. Именно затова ние можехме да извикаме myprogram.HelloWorld от c:\ &#8211; само от такава &#8222;текуща директория&#8220; може да се &#8222;види&#8220; този пакет и класа в него. Ето един пример как можем да вмъкнем един пакет от &#8222;чужда&#8220; директория в нашата програма:</p>
<p><em>Файл SayHello.java в директория c:\myprogram2:</em></p>
<pre>package myprogram2;
public class SayHello{
  String message;
  public SayHello(String message){
    this.message = message;
  }
  public void speak(){
    System.out.println(this.message);
  }
}</pre>
<p><em>Файл HelloWorld.java в директория c:\myprogram:</em></p>
<pre>package myprogram;
<strong>import myprogram2.SayHello;</strong>
public class HelloWorld{
 public static void main(String[] args){
   SayHello msg = new SayHello("Hola amigos");
   msg.speak();
 }
}</pre>
<p>Ето какво би се случило ако се опитаме не да изпълним, а дори само да компилираме програмата HelloWorld.java:</p>
<pre>C:\&gt;cd c:\myprogram2
C:\myprogram2&gt;javac SayHello.java
C:\myprogram2&gt;cd c:\myprogram
C:\myprogram&gt;javac HelloWorld.java
<em>HelloWorld.java:2: package myprogram2 does not exist
import myprogram2.SayHello;
                 ^
HelloWorld.java:5: cannot find symbol
symbol  : class SayHello
location: class myprogram.HelloWorld
   SayHello msg = new SayHello("Hola amigos");
   ^
HelloWorld.java:5: cannot find symbol
symbol  : class SayHello
location: class myprogram.HelloWorld
   SayHello msg = new SayHello("Hola amigos");
                      ^
3 errors</em>
C:\myprogram&gt;</pre>
<p>Случи се точно това, което предположихме &#8211; пакет myprogram2 не е видим от програмата в пакет myprogram. За да се справим с този проблем трябва да добавим базовата директория за пакета myprogram2 (в случая това е c:\, защото именно тя &#8222;вижда&#8220; директорията/пакета myprogram2) в променливата CLASSPATH:</p>
<pre><strong>C:\myprogram&gt;set CLASSPATH=.;C:\;</strong>
C:\myprogram&gt;javac HelloWorld.java
C:\myprogram&gt;cd ..
C:\&gt;java myprogram.HelloWorld
<em>Hola amigos</em>
C:\&gt;</pre>
<p>Сега вече е време да поговорим и за &#8222;<strong>под-пакети</strong>&#8222;. Те не са нищо по-различно от под-директории в директориите на някой пакет. Ето един пример:</p>
<p><em>Файл SayHello.java в директория C:\myprogram2\myprogram2sub</em>:</p>
<pre>package myprogram2.myprogram2sub;
public class SayHello{
  String message;
  public SayHello(String message){
    this.message = message;
  }
  public void speak(){
    System.out.println("Subpackage says: "+this.message);
  }
}</pre>
<p><em>Файл SayHello.java в директория c:\myprogram2:</em></p>
<pre>package myprogram2;
public class SayHello{
  String message;
  public SayHello(String message){
    this.message = message;
  }
  public void speak(){
    System.out.println(this.message);
  }
}</pre>
<p><em>Файл HelloWorld.java в c:\myprogram:</em></p>
<pre>package myprogram;
import myprogram2.*;
public class HelloWorld{
 public static void main(String[] args){
   SayHello msg = new SayHello("Hola amigos");
   msg.speak();
   myprogram2.myprogram2sub.SayHello msg2 =
		new myprogram2.myprogram2sub.SayHello("abc");
   msg2.speak();
 }
}</pre>
<p>Изпълнение:</p>
<pre>C:\&gt;cd myprogram2
C:\myprogram2&gt;javac SayHello.java
C:\myprogram2&gt;cd myprogram2sub
C:\myprogram2\myprogram2sub&gt;javac SayHello.java
C:\myprogram2\myprogram2sub&gt;cd c:\myprogram
C:\myprogram&gt;javac HelloWorld.java
C:\myprogram&gt;cd ..
C:\&gt;java myprogram.HelloWorld
<em>Hola amigos
Subpackage says: abc</em>
C:\&gt;</pre>
<p>Виждате, че при първото създаване на обект &#8222;SayHello msg =&#8230;&#8220; се създаде обекта от клас myprogram2.SayHello &#8211; това е така, защото ние вмъкнахме всички класове от пакет myprogram2 чрез &#8222;import myprogram2.*&#8220;. Така компилаторът не се &#8222;обърква&#8220; от факта, че имаме два класа с едно и също име. Вторият клас обаче сме длъжни да го достъпваме чрез пълният път myprogram2.myprogram2sub.SayHello. В противен случай ще се получи грешка и компилаторът няма да знае кой от двата класа да &#8222;избере&#8220;. Ето един пример &#8211; модифицираме файла HelloWorld.java в:</p>
<pre>package myprogram;
<strong>import myprogram2.*;
import myprogram2.myprogram2sub.*;</strong>
public class HelloWorld{
 public static void main(String[] args){
<strong>   SayHello msg = new SayHello("Hola amigos");</strong>
   msg.speak();
<strong>   SayHello msg2 = new SayHello("abc");</strong>
   msg2.speak();
 }
}</pre>
<p>При компилация ще се получи грешка:</p>
<pre>C:\myprogram&gt; javac HelloWorld.java
<em>HelloWorld.java:6: reference to SayHello is ambiguous,
both class myprogram2.myprogram2sub.SayHello in
myprogram2.myprogram2sub and class myprogram2.SayHello in
myprogram2 match SayHello msg = new SayHello("Hola amigos");
...</em></pre>
<p>За изход от такива ситуации е нужно винаги да достъпваме класовете чрез пълният път до техните пакети. Принципно за да сме сигурни, че няма да попадаме в такива ситуации е нужно да спазваме следните две правила:</p>
<ul>
<li>Давайте уникални имена на пакетите, които пишете;</li>
<li>Давайте уникални имена на класовете, които пишете .</li>
</ul>
<p>Йерархията на пакети и под-пакети всъщност много наподобява тази на класове и &#8222;<strong>вложени класове</strong>&#8222;. В езика Java ни е дадена възможност да правим следните конструкции:</p>
<pre>class ВъншенКлас {
    ...
    class ВложенКлас {
        ...
    }
}</pre>
<p>В предишната статия споменахме, че има два вида класове &#8211; публични и стандартни. Публичните класове можеха да се виждат от всички класове, включително от класове в &#8222;чужди&#8220; пакети (в горните примери направихме именно това &#8211; клас SayHello беше публичен в пакет myprogram2 и по този начин клас HelloWorld от пакет myprogram го виждаше). Когато един клас не е публичен (т.е. видимост по подразбиране) казахме, че се вижда само и единствено от други класове в текущия клас.</p>
<p>В случая на вложените класове имаме на първо място логическо групиране на класовете:</p>
<pre>public class HelloWorld{
 public static void main(String[] args){
<strong>   // Създаваме инстанция на външен клас:
   MessagesClass m = new MessagesClass("Hello World", 3);
   m.speak();
   // Създаваме инстанция на вложен клас на клас m:
   MessagesClass.SpeakerClass s = m.new SpeakerClass("Hola");
   s.speak();</strong>
 }
}

class MessagesClass{
  String msg;
  int times;
  public MessagesClass(String msg, int times){
    this.msg=msg;
    this.times = times;
  }
  public void speak(){
    <strong>SpeakerClass s = new SpeakerClass(this.msg);</strong>
    for(int i=0; i&lt;this.times; i++){
         s.speak();
    }
  }

<strong>  class SpeakerClass{
    String msg;
    public SpeakerClass(String msg){
      this.msg=msg;
    }
    public void speak(){
      System.out.println(this.msg);
    }
  }
}</strong></pre>
<p>В този пример на вложения клас не бяхме сложили модификатор за достъп, т.е. по подразбиране той е видим от текущия пакет и само от него могат да се правят инстанции. Капсулация на данни се получава тогава, когато поставим модификатор за достъп private:</p>
<pre>public class HelloWorld{
 public static void main(String[] args){
   // Създаваме инстанция на външен клас:
   MessagesClass m = new MessagesClass("Hello World", 3);
   m.speak();
<strong>   // Вече не е възможно да правим инстанция:
   //MessagesClass.SpeakerClass s = m.new SpeakerClass("Hola");
   //s.speak();</strong>
 }
}

class MessagesClass{
  String msg;
  int times;
  public MessagesClass(String msg, int times){
    this.msg=msg;
    this.times = times;
  }

  public void speak(){
    SpeakerClass s = new SpeakerClass(this.msg);
    for(int i=0; i&lt;this.times; i++){
         s.speak();
    }
  }

  <strong>private </strong>class SpeakerClass{
    String msg;
    public SpeakerClass(String msg){
      this.msg=msg;
    }
    public void speak(){
      System.out.println(this.msg);
    }
  }
}</pre>
<p>Така вложеният клас се вижда само и единствено от неговия външен клас. Това е и най-честият случай &#8211; НЕ правим инстанции на вложени класове, а ги използваме само вътре в техните външни класове. Поради тази причина често се казва, че такива вложени класове са &#8222;помагащи класове&#8220;. Впрочем не е проблем да имаме вложени абстрактни класове, както и наследяване на вложени класове вътре във външен клас.</p>
<p>Виждате, че вложените класове могат да бъдат освен public и default (както външните) така и private и protected. Именно затова казваме, че чрез тяхната функционалност се засилва <a href="http://www.cphpvb.net/java/4289-%D0%BA%D0%B0%D0%BF%D1%81%D1%83%D0%BB%D0%B0%D1%86%D0%B8%D1%8F-%D0%BD%D0%B0-%D0%B4%D0%B0%D0%BD%D0%BD%D0%B8/" target="_blank">капсулацията на данни</a>.</p>
<p>И за да усложним още повече нещата ще кажем, че представеният пример е само един от четирите вида вложени класове &#8211; нарича се &#8222;вложен член клас&#8220;. Съществуват още &#8222;статичен вложен член клас&#8220;, &#8222;локален клас&#8220; и &#8222;анонимен клас&#8220;. Ще ги разгледаме поотделно в следваща статия.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cphpvb.net/java/4314-sub-packages-and-nested-classes/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Капсулация на данни</title>
		<link>http://www.cphpvb.net/java/4289-%d0%ba%d0%b0%d0%bf%d1%81%d1%83%d0%bb%d0%b0%d1%86%d0%b8%d1%8f-%d0%bd%d0%b0-%d0%b4%d0%b0%d0%bd%d0%bd%d0%b8/</link>
		<comments>http://www.cphpvb.net/java/4289-%d0%ba%d0%b0%d0%bf%d1%81%d1%83%d0%bb%d0%b0%d1%86%d0%b8%d1%8f-%d0%bd%d0%b0-%d0%b4%d0%b0%d0%bd%d0%bd%d0%b8/#comments</comments>
		<pubDate>Sun, 11 Oct 2009 18:43:10 +0000</pubDate>
		<dc:creator>Филип Петров</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.cphpvb.net/?p=4289</guid>
		<description><![CDATA[Досега ние неосъзнато сме използвали капсулация на данни в процедурното програмиране. Представете си например функцията &#8222;indexOf()&#8220; на клас String. Не са много учениците, пък и специалистите, които са се интересували как точно е реализиран този метод. В общи линии ние се интересуваме какви са входните му параметри и връщаната от него стойност. Самият алгоритъм, който [...]]]></description>
			<content:encoded><![CDATA[<p>Досега ние неосъзнато сме използвали капсулация на данни в процедурното програмиране. Представете си например функцията &#8222;indexOf()&#8220; на клас String. Не са много учениците, пък и специалистите, които са се интересували как точно е реализиран този метод. В общи линии ние се интересуваме какви са входните му параметри и връщаната от него стойност. Самият алгоритъм, който извършва работата остава скрит за вас. Още повече &#8211; алгоритъмът може след време да бъде променен (подобрен), но това няма да промени нашите програми и те ще продължат да функционират (вземете например алгоритмите за сортиране на масив &#8211; можете да използвате най-различни, които се различават само и единствено по скорост на изпълнение, но резултатът е един и същи). Именно такъв вид капсулация на данни има във всички методи, които сте писали.<span id="more-4289"></span></p>
<p>Тенденцията за &#8222;скриване на алгоритъма&#8220; от &#8222;външния свят&#8220; естествено се пренася и при обектно-ориентираното програмиране. Дотук във всички дадени примери използвахме ключова дума &#8222;public&#8220; при дефиниране на класове, техни полета и методи. Ключовата дума &#8222;public&#8220; се превежда като &#8222;публичен&#8220;, а в програмирането има значение на &#8222;достъпен от всеки&#8220;. Такива данни казваме, че не са &#8222;капсулирани&#8220;, т.е. &#8222;не са скрити/защитени&#8220;. За да капсулираме данни в даден клас, то ние използваме ключови думи &#8222;private&#8220; или &#8222;protected&#8220;. Наричат се още &#8222;модификатори за достъп&#8220;.</p>
<p>Нека да вземем следния пример &#8211; клас за автомобили:</p>
<pre>public class myfirstprogram{
  public static void main(String[] args){
    Car c1 = new Car("BMW", "5", 220);
    System.out.println("The "+c1.manufacturer+" "+c1.model+
                       " car has max speed of "+c1.getSpeed());
  }
}

class Car{
  public int speed;
  public String manufacturer;
  public String model;
  public Car(String manufacturer, String model, int speed){
    this.setSpeed(speed);
    this.manufacturer = manufacturer;
    this.model = model;
  }
  public int getSpeed(){
    return this.speed;
  }
  public void setSpeed(int speed){
    while (speed&lt;0){
      System.out.println("Speed must be positive integer!");
      try{
        java.util.Scanner s = new java.util.Scanner(System.in);
        System.out.print("Enter the correct value: ");
        speed = s.nextInt();
      }
      catch (java.util.InputMismatchException e){
        speed = -1;
      }
      finally{
        try{
          while(System.in.available() &gt; 0) System.in.skip(System.in.available());
        }
        catch(java.io.IOException e){}
      }
    }
    this.speed = speed;
  }
}</pre>
<p>Както виждате в метод &#8222;setSpeed(int speed)&#8220; сме вмъкнали функционалност, която гарантира, че винаги ще въвеждаме скорост на автомобила по-голяма от 0. Да, но полето &#8222;speed&#8220; в клас Car е публично. Това означава, че в главната програма ние можем лесно да направим следното:</p>
<pre>    c1.speed = -200;</pre>
<p>Така на практика ние &#8222;прескачаме&#8220; защитата, която сме направили в метод &#8222;setSpeed()&#8220; и вкарваме некоректни стойности. Когато пишем програма ние не трябва да позволяваме подобни неща. Затова ние можем да защитим данните от полетата, като ги направим например private:</p>
<pre>public class myfirstprogram{
  public static void main(String[] args){
    Car c1 = new Car("BMW", "5", 220);
    System.out.println("The "+c1.manufacturer+" "+c1.model+
                       " car has max speed of "+c1.getSpeed());
<strong>    // c1.speed = -200; // Вече НЕ работи</strong>
  }
}

class Car{
  <strong>private </strong>int speed;
  public String manufacturer;
  public String model;
  public Car(String manufacturer, String model, int speed){
    this.setSpeed(speed);
    this.manufacturer = manufacturer;
    this.model = model;
  }
  public int getSpeed(){
    return this.speed;
  }
  public void setSpeed(int speed){
    while (speed&lt;0){
      System.out.println("Speed must be positive integer!");
      try{
        java.util.Scanner s = new java.util.Scanner(System.in);
        System.out.print("Enter the correct value: ");
        speed = s.nextInt();
      }
      catch (java.util.InputMismatchException e){
        speed = -1;
      }
      finally{
        try{
          while(System.in.available() &gt; 0) System.in.skip(System.in.available());
        }
        catch(java.io.IOException e){}
      }
    }
    this.speed = speed;
  }
}</pre>
<p>От този пример се вижда ясно и каква е концепцията на т.нар. &#8222;get&#8220; и &#8222;set&#8220; методи. Вместо да добиваме директен достъп до полетата на даден клас, ние ги правим &#8222;private&#8220;, а достъпът до тях осъществаваме чрез вътрешни за класа методи. По този начин алгоритмите в методите гарантират, че данните са коректни!</p>
<p>Това поведение се прехвърля не само при създаването на инстанция на клас, но и при наследяването:</p>
<pre>public class usersexample{
  public static void main(String[] args){
    User pesho = new UserWithInfo("batman", "Petar Petrov");
    pesho.getId();
    pesho.getInfo();
    User.usersOnline();
    User ivan = new UserWithInfo("dexter", "Ivan Ivanov");
    ivan.getId();
    ivan.getInfo();
    User.usersOnline();
  }
}

class User{
<strong>  private String username;
  private int id;
  private static int userscount = 0;</strong>
  public User(String username){
    System.out.println("\nNew user entering...");
    this.username = username;
    this.setId();
  }
<strong>  private void setId(){
    userscount++;
    this.id = userscount;
  }</strong>
  public static void usersOnline(){
    System.out.println("Users online: "+userscount);
  }
  public void getId(){
    System.out.println("User id:"+this.id);
  }
  public void getInfo(){
    System.out.println("Username: "+this.username);
  }
}

class UserWithInfo extends User{
  private String name;
  public UserWithInfo(String username, String name){
    super(username);
<strong>    // id = 15; // НЯМА ДОСТЪП до private поле!
    // super.userscount = 10; // Също няма достъп!
    // super.setId(); // и пак няма достъп!</strong>
    this.name = name;
  }
  // Предефинираме метод
  public void getInfo(){
    super.getInfo();
    System.out.println("Name: "+this.name);
  }
}</pre>
<p>Виждате, че наследникът на класа няма достъп до private полетата и методите на супер класа (родителския клас). По този начин казваме, че супер класа е капсулирал своите данни &#8211; в примера той не предоставя възможност да бъдат променяни идентификационните номера на потребителите или пък да бъде подменяна стойността на броят на влезлите потребители в системата.</p>
<p>Модификатор за достъп &#8222;protected&#8220; има малко по-различно значение от това, което познаваме в C++. Ако направите едно поле &#8222;protected&#8220;, то ще бъде видимо не само от наследниците на класа (както е в C++), но и от всички други класове в текущия пакет (package). Ако нашата програма се разпростира само в един пакет (каквито програми сме писали досега), то public и protected ще имат едно и също действие. Разликата идва тогава, когато желаем да свържем класове от различни пакети. Независим клас от чужд пакет не може да достъпи protected данни. Това обаче не важи за наследник от чужд пакет на класа с protected данни (наследниците, независимо от пакета, на който принадлежат, виждат protected данните на своите родителски класове). Аналогичен на C++ protected модификатор (видимост само и единствено от наследниците) е имало в миналото чрез &#8222;private protected&#8220; модификатор в Java, но от Sun са го премахнали в съвременните версии на езика.</p>
<p>Почти аналогично на &#8222;protected&#8220; модификатор на достъп е &#8222;липсата на модификатор на достъп&#8220; (т.нар. &#8222;default&#8220;). Когато пред поле или метод не сложите нито една от ключовите думи private, protected или public, то полето/метода получават ниво на достъп default. Това е почти същото както protected достъп, т.е. всички класове и наследници в пакета могат да достъпват тези данни. Разликата е, че когато клас от друг пакет е наследник, то той няма достъп до тези данни. С други думи &#8222;protected&#8220; данните на един клас са видими за текущия пакет, но са напълно скрити (private) за всички други пакети (независимо дали класът от тях който се опитва да ги достъпи е наследник или не).</p>
<p>Колкото до декларациите на класове имаме две възможности &#8211; това са default (без модификатор) и public. Когато един клас е публичен (public), то той е видим от чужди пакети. Когато един клас се декларира без модификатор, то само и единствено класове в текущия пакет могат да правят негови инстанции или да го наследяват. Важно за Java правило е, че ако искате да направите един клас публичен, то той трябва да стои в самостоятелен .java файл (тоест за него ще се компилира отделен .class файл).</p>
<p>В Java НЕ можем да правим множествено наследяване и <a href="http://www.cphpvb.net/pik-3/232-%D0%BF%D1%80%D0%B8%D1%8F%D1%82%D0%B5%D0%BB%D1%81%D0%BA%D0%B8-%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%B8/">приятелски функции</a>, които са налични в C++. Това все пак не се смята за голям недостатък от общността, тъй като се счита, че такавива функционалности почти не се използват в реални програми.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cphpvb.net/java/4289-%d0%ba%d0%b0%d0%bf%d1%81%d1%83%d0%bb%d0%b0%d1%86%d0%b8%d1%8f-%d0%bd%d0%b0-%d0%b4%d0%b0%d0%bd%d0%bd%d0%b8/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Полиморфизъм</title>
		<link>http://www.cphpvb.net/java/4256-polymorphism/</link>
		<comments>http://www.cphpvb.net/java/4256-polymorphism/#comments</comments>
		<pubDate>Fri, 09 Oct 2009 15:49:45 +0000</pubDate>
		<dc:creator>Филип Петров</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.cphpvb.net/?p=4256</guid>
		<description><![CDATA[Понятието &#8222;полиморфизъм&#8220; идва от биологията. То описва &#8222;съществуване на морфологично различни индивиди в границите на един вид&#8220; (wikipedia). Това например са пчелите и търтеите в пчелния кошер &#8211; те са от един вид, но имат съвсем различни функции. В обектно-ориентираното програмиране под &#8222;полиморфизъм&#8220; се разбира същото &#8211; различни обекти (индивиди) от един и същи вид [...]]]></description>
			<content:encoded><![CDATA[<p>Понятието &#8222;полиморфизъм&#8220; идва от биологията. То описва &#8222;съществуване на морфологично различни индивиди в границите на един вид&#8220; (<a href="http://en.wikipedia.org/wiki/Polymorphism_%28biology%29" target="_blank">wikipedia</a>). Това например са пчелите и търтеите в пчелния кошер &#8211; те са от един вид, но имат съвсем различни функции. В обектно-ориентираното програмиране под &#8222;полиморфизъм&#8220; се разбира същото &#8211; различни обекти (индивиди) от един и същи вид (базов клас) извършват различни дейности. Тук се разбира, че индивидите наследяват характерните черти на вида, но променят някои от неговите функционалности.</p>
<p>Досега се запознахме с  предефинирането на методи. При тях знаем, че можем да извикваме различни методи с едно и също име. Това се наричаше &#8222;предефиниране на метод&#8220;:<span id="more-4256"></span></p>
<pre>public class polyexample{
  public static void main(String[] args){
    ClassExample c = new ClassExample(5);
    int imultiplyer = 2;
    double dmultiplyer = 2.5;
    int x = c.multiply(imultiplyer);
    double d = c.multiply(dmultiplyer);
    System.out.println("x = "+x+", d = "+d);
  }
}

class ClassExample{
  public int x;
  public ClassExample(){
    x = 0;
  }
  public ClassExample(int x){
    this.x = x;
  }
<strong>  public int multiply(int x){
    return this.x*x;
  }
  public double multiply(double x){
    return (double)this.x * x;
  }</strong>
}</pre>
<p>В литературата това се среща по-често просто като понятие &#8222;предефиниране на метод&#8220; и много автори не го свързват въобще с понятието &#8222;полиморфизъм&#8220;. Въпреки, че тук нямаме конкретно обект от даден клас и нямаме наследяване, все пак можем да направим доста добра аналогия с понятието полиморфизъм. Ако приемем, че &#8222;методът&#8220; е &#8222;вид&#8220;, а &#8222;извикването на метод с аргументи&#8220; е &#8222;индивид&#8220;, то имаме типичното свойство на полиморфизма &#8211; различните индивиди действат по различен начин. Затова ще си позволим да въведем понятието &#8222;<strong>статичен полиморфизъм</strong>&#8220; като синоним на &#8222;предефиниране на метод&#8220;. Също така можем да го приемем и за полиморфизъм в процедурното програмиране.</p>
<p><strong>Динамичен полиморфизъм</strong> се получава тогава, когато предефинираме методи на базов клас в негов наследник. Казваме също, че по този начин променяме съществуващата реализация на метод с нова. Ето един пример &#8211; създаваме наследник и предефинираме метод print():</p>
<pre>public class polyexample{
  public static void main(String[] args){
    ClassExample c = new ClassExample(5);
    c.print();
    SubClassExample c2 = new SubClassExample(5);
    c2.print();
  }
}

class ClassExample{
  public int x;
  public ClassExample(){
    x = 0;
  }
  public ClassExample(int x){
    this.x = x;
  }
<strong>  public void print(){
    System.out.println(this.x);
  }</strong>
}

class SubClassExample extends ClassExample{
  public int y;
  public SubClassExample(){
    super();
    y = 0;
  }
  public SubClassExample(int x){
    super(x);
    y = 0;
  }
<strong>  public void print(){
    System.out.println(this.x+" "+this.y);
  }</strong>
}</pre>
<p><strong>Динамично свързване на метод</strong> е третият, най-пълен тип полиморфизъм. Това е свойството на метод да върши различни неща в зависимост от обекта с който работи. Динамичното свързване на метод всъщност се свързва и с общото понятие &#8222;полиморфизъм&#8220; в обектно-ориентираното програмиране. В известен смисъл то &#8222;надгражда&#8220; динамичния полиморфизъм. Ето един класически пример:</p>
<pre>public class polyexample{
  public static void main(String[] args){
    Animal a;
    Dog kuche = new Dog("Sharo");
    Cat kotka = new Cat("Pisana");
<strong>    a = kuche;
    a.speak();
    a = kotka;
    a.speak();</strong>
  }
}

abstract class Animal{
  public String name;
  public Animal(String name){
    this.name = name;
  }
  abstract void speak();
}

class Dog extends Animal{
  public Dog(String name){
    super(name);
  }
  public void speak(){
    System.out.println("Бау, бау!");
  }
}

class Cat extends Animal{
  public Cat(String name){
    super(name);
  }
  public void speak(){
    System.out.println("Мяу!");
  }
}</pre>
<p>Виждате, че всеки път извиквахме метод &#8222;speak()&#8220; чрез променливата &#8222;a&#8220;, която е от абстрактен тип &#8222;Animal&#8220;. По време на компилация не се знае точно кой метод &#8222;speak()&#8220; ще бъде извикан &#8211; поради тази причина се казва, че имаме &#8222;динамично свързване на метод&#8220; по време на изпълнение. Това е и пълното разбиране за понятието &#8222;полиморфизъм в обектно-ориентираното програмиране&#8220;.</p>
<p> Тук е мястото да вмъкнем аналогията с програмирането на C++ където имаше понятие &#8222;<a href="http://www.cphpvb.net/pik-3/282-%D0%B2%D0%B8%D1%80%D1%82%D1%83%D0%B0%D0%BB%D0%BD%D0%B8-%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%B8/">виртуални функции</a>&#8222;, тоест такива, които могат да бъдат предефинирани. В Java всички методи са виртуални и могат да бъдат предефинирани. В C++ наричахме такова предефиниране като &#8222;полиморфизъм по време на изпълнение&#8220; или както го дефинирахме тук &#8211; динамично свързване на метод.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cphpvb.net/java/4256-polymorphism/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Абстрактни класове и интерфейси</title>
		<link>http://www.cphpvb.net/java/4284-abstract-classes-interfaces/</link>
		<comments>http://www.cphpvb.net/java/4284-abstract-classes-interfaces/#comments</comments>
		<pubDate>Fri, 09 Oct 2009 15:47:04 +0000</pubDate>
		<dc:creator>Филип Петров</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.cphpvb.net/?p=4284</guid>
		<description><![CDATA[Абстрактни са тези методи в клас, чиято реализация не е имплементирана. С други думи ние сме декларирали, че такъв метод трябва да има, но той все още не е описан как ще работи. Класове, които съдържат в себе си абстрактни методи наричаме &#8222;абстрактни класове&#8220;.
В статията за класове и обекти дефинирахме &#8222;класовете&#8220; като &#8222;понятия&#8220;. Когато говорим [...]]]></description>
			<content:encoded><![CDATA[<p>Абстрактни са тези методи в клас, чиято реализация не е имплементирана. С други думи ние сме декларирали, че такъв метод трябва да има, но той все още не е описан как ще работи. Класове, които съдържат в себе си абстрактни методи наричаме &#8222;абстрактни класове&#8220;.</p>
<p>В статията за <a href="http://www.cphpvb.net/java/4224-classes-and-packages/" target="_blank">класове и обекти</a> дефинирахме &#8222;класовете&#8220; като &#8222;понятия&#8220;. Когато говорим за &#8222;абстрактни класове&#8220;, то би трябвало да разбираме &#8222;абстрактни понятия&#8220;. По-доброто определение за такива класове обаче е &#8222;шаблони&#8220;. В действителност когато декларираме методи, чиято реализация още не е изпълнена, то ние даваме нещо като шаблон на наследниците на тези класове, който те просто трябва да &#8222;попълнят&#8220;. Не е възможно да се правят инстанции на абстрактни класове (противоречи и с обикновената логика, защото какво би трябвало да означава &#8222;абстрактен реален обект&#8220;?).<span id="more-4284"></span></p>
<p>Нека дадем един класически пример за абстрактен клас. Ще създадем клас &#8222;геометрична фигура&#8220;, в която ще има абстрактен метод, наречен &#8222;лице&#8220;:</p>
<pre>abstract class Figure{
  double dim1;
  double dim2;
  public Figure(double dim1, double dim2){
    this.dim1 = dim1;
    this.dim2 = dim2;
  }
  abstract double getArea();
}</pre>
<p>Ние не можем да кажем колко ще бъде лицето на фигурата и колко ще бъде нейния периметър, защото все още не знаем точно каква ще е тя &#8211; правоъгълник или успоредник при който dim1 и dim2 са страните, триъгълник при който dim1 е основа, а dim2 е височина към нея и т.н. Когато наследим клас Figure обаче можем да дадем имплементация на този метод:</p>
<pre>class Rectangle extends Figure{
  public Rectangle(double a, double b) {
    super(a,b);
  }
  public double getArea(){
    return this.dim1 * this.dim2;
  }
}

class Triangle extends Figure{
  public Triangle(double a, double b) {
    super(a,b);
  }
  public double getArea(){
    return (this.dim1 * this.dim2)/2;
  }
}</pre>
<p>Още по-голямо ниво на абстракция е понятието &#8222;интерфейс&#8220;. Интерфейсът съдържа само и единствено абстрактни методи и евентуално константи (за константи ще говорим по-късно). Чрез интерфейсите ние даваме само и единствено обща насоченост как трябва да изглежда даден клас, който го &#8222;имплементира&#8220;. Нека преработим горния пример да работи по същия начин, но чрез интерфейс:</p>
<pre>interface Figure{
  public double getArea();
}

class Rectangle implements Figure{
  double dim1;
  double dim2;
  public Rectangle(double a, double b) {
    this.dim1 = a;
    this.dim2 = b;
  }
  public double getArea(){
    return this.dim1 * this.dim2;
  }
}

class Triangle implements Figure{
  double dim1;
  double dim2;
  public Triangle(double a, double b) {
    this.dim1 = a;
    this.dim2 = b;
  }
  public double getArea(){
    return (this.dim1 * this.dim2)/2;
  }
}</pre>
<p>Границата на това кога да използваме абстрактен клас и кога интерфейс зависи от логиката на приложението. Ако решим да включим нова геометрична фигура &#8222;окръжност&#8220;, то може би интерфейсът ще бъде по-подходящ &#8211; за лицето на окръжност не ни трябват две размерности, а ни трябва само един радиус (тоест наследяването на показания абстрактен клас &#8222;Figure&#8220; би било нелогично, защото той съдържа две полета). Когато обаче искаме да създадем шаблон на фигура, която е задължително с две размерности, то шаблона на абстрактния клас би бил по-подходящ. С други думи отговорът на въпроса &#8222;интерфейс или абстрактен клас да използвам&#8220; е &#8222;зависи доколко абстрактно е понятието, което ще реализираме&#8220;. Попринцип ще спазваме правилото &#8222;конкретизираме и даваме колкото се може повече насоки&#8220;.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cphpvb.net/java/4284-abstract-classes-interfaces/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Наследяване в Java</title>
		<link>http://www.cphpvb.net/java/4249-inheritance-in-java/</link>
		<comments>http://www.cphpvb.net/java/4249-inheritance-in-java/#comments</comments>
		<pubDate>Thu, 08 Oct 2009 18:43:34 +0000</pubDate>
		<dc:creator>Филип Петров</dc:creator>
				<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://www.cphpvb.net/?p=4249</guid>
		<description><![CDATA[Както всеки обектно-ориентиран език за програмиране, така и Java позволява наследяване. Това е възможността един клас да наследи цялата функционалност на друг клас и да я разшири. Класът, който е наследяван се нарича &#8222;родителски клас&#8220;, а класа който наследява се нарича &#8222;наследник&#8220;. Освен да добавят нова функционалност, наследниците също могат да изменят съществуваща.
Нека вземем един [...]]]></description>
			<content:encoded><![CDATA[<p>Както всеки обектно-ориентиран език за програмиране, така и Java позволява наследяване. Това е възможността един клас да наследи цялата функционалност на друг клас и да я разшири. Класът, който е наследяван се нарича &#8222;родителски клас&#8220;, а класа който наследява се нарича &#8222;наследник&#8220;. Освен да добавят нова функционалност, наследниците също могат да изменят съществуваща.</p>
<p>Нека вземем един примерен клас за деца с метод, в който децата играят играта &#8222;камък &#8211; ножица &#8211; хартия&#8220;. В главната програма ще пуснем децата да изиграят 10 поредни игри:<span id="more-4249"></span></p>
<p><em>Файл kid.java:</em></p>
<pre>package kids;
import java.util.Scanner;
class Kid{
  public String name;
  public Kid(){
    Scanner s = new Scanner(System.in);
    System.out.print("Enter the kid name: ");
    this.name = s.next();
  }
  public Kid(String name){
    this.name = name;
  }
  public static void introduce(){
    System.out.println("Hello, nice to meet you!");
  }
  public int getRand(){
    int r = (int)Math.floor(Math.random()*3);
    return r;
  }
  public void playRPS(Kid oponent){
    int i = this.getRand();
    int j = oponent.getRand();
    String[] rps = {"leafs", "scissors", "rocks"};
    if (i == j){
      System.out.println(this.name+" and "+oponent.name+
                         " both played "+rps[i]);
      System.out.println("The game is tie");
    }
    else{
      System.out.println(this.name+" played "+rps[i]+
                         " and "+oponent.name+" played "+rps[j]);
      switch(i){
        case 0:
          if (j == 1) System.out.println(oponent.name+" wins!");
          else System.out.println(this.name+" wins!");
          break;
        case 1:
          if (j == 2) System.out.println(oponent.name+" wins!");
          else System.out.println(this.name+" wins!");
          break;
        case 2:
          if (j == 0) System.out.println(oponent.name+" wins!");
          else System.out.println(this.name+" wins!");
          break;
      }
    }
  }
}</pre>
<p><em>Файл kidsplay.java:</em></p>
<pre>package kids;

public class kidsplay{
  public static void main(String[] args){
    Kid ivancho = new Kid("Ivancho");
    Kid pencho = new Kid("Pencho");
    for (int i=0; i&lt;10; i++){
      ivancho.playRPS(pencho);
      System.out.println();
    }
  }
}</pre>
<p>Нека сега създадем клас &#8222;момче&#8220;, което има същите свойства, каквито и клас &#8222;дете&#8220;, но добавя и нова функция:</p>
<p><em>Файл Boy.java:</em></p>
<pre>package kids;
class Boy extends Kid{
  public String sex = "male";
  public Boy(){
    // извикване на конструктора на базовия клас
    super();
  }
  public Boy(String name){
    // извикване на конструктора на базовия клас
    super(name);
  }
  public void proposeGame(){
    System.out.println(this.name+" said: Let's go play football!");
  }
}</pre>
<p>Виждате, че можем да извикаме конструктора на базовият клас (също познат специално в Java и като &#8222;сурер клас&#8220;) чрез извикване на метод &#8222;super()&#8220;. Това все пак е нужно, защото всеки клас трябва да има конструктор (в случая конструктора на Boy изниква конструктора на Kid).</p>
<p>Можем да наследим класа и втори път:<br />
<em>Файл Girl.java:</em></p>
<pre>package kids;
class Girl extends Kid{
  public String sex = "female";
  public Girl(){
    super();
  }
  public Girl(String name){
    super(name);
  }
  public void proposeGame(){
    System.out.println(this.name+" said: Let's play fast and loose!");
  }
}</pre>
<p>Нека сега променим главната програма така, че да работи вече с класове Boy и Girl:</p>
<p><em>Файл kidsplay.java:</em></p>
<pre>package kids;

public class kidsplay{
  public static void main(String[] args){
    Boy ivancho = new Boy("Ivancho");
    Girl mariika = new Girl("Maria");
    for (int i=0; i&lt;10; i++){
      ivancho.playRPS(mariika);
      System.out.println();
    }
    ivancho.proposeGame();
    mariika.proposeGame();
  }
}</pre>
<p>Виждате, че всички функции от базовия клас бяха наследени от наследника му. Освен това трябва да обърнете внимание на метод playRPS(). Той очакваше като входен параметър обект от тип Kid, а ние му &#8222;изпращаме&#8220; обект от тип Girl. Въпреки това програмата ще работи. Това е и първият &#8222;зачатък&#8220; на полиморфизъм. По-подробно ще го разгледаме в следваща статия.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cphpvb.net/java/4249-inheritance-in-java/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
