<?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; ПИК-3 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>Mon, 30 Jan 2012 16:44:20 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Четири нововъведения от Java 7</title>
		<link>http://www.cphpvb.net/java/7875-four-new-features-in-java-7/</link>
		<comments>http://www.cphpvb.net/java/7875-four-new-features-in-java-7/#comments</comments>
		<pubDate>Mon, 21 Nov 2011 16:41:18 +0000</pubDate>
		<dc:creator>Филип Петров</dc:creator>
				<category><![CDATA[ПИК-3 Java]]></category>

		<guid isPermaLink="false">http://www.cphpvb.net/?p=7875</guid>
		<description><![CDATA[В един от последните примери, които пуснах в сайта получих уместен коментар, в който се каза, че &#8222;Eclipse иска int или enum, a ние подаваме String&#8220; при използване на оператор &#8222;switch&#8220;. Объркването се получава поради това, че подаването на String към оператор switch е едно от нововъведенията в Java 7! Естествено нововъведенията са много повече, [...]]]></description>
			<content:encoded><![CDATA[<p>В <a title="Пример за отваряне на сокети в Java" href="http://www.cphpvb.net/java/7850-socket-and-serversocket/" target="_blank">един от последните примери</a>, които пуснах в сайта получих уместен коментар, в който се каза, че &#8222;Eclipse иска int или enum, a ние подаваме String&#8220; при използване на оператор &#8222;switch&#8220;. Объркването се получава поради това, че подаването на String към оператор switch е едно от нововъведенията в Java 7!</p>
<p>Естествено нововъведенията са много повече, но на този етап ще акцентирам върху четири от основните:<span id="more-7875"></span></p>
<p><strong>1. String като параметър на &#8222;switch&#8220;</strong>:</p>
<p>Вече е възможно да правите:</p>
<pre>String str=...;
...
switch(str){
   case "...":
        ...
}</pre>
<p>Мисля, че не е нужно да го коментирам подробно. Еквивалентно е на поредица от &#8222;if-else-if-else&#8220; команди, при които във всеки &#8222;if&#8220; се извиква оператор &#8222;equals&#8220; на обекта от тип String.</p>
<p><strong>2. Инстанции на обекти с тип по подразбиране</strong>:</p>
<p>Досега пишехме (а аз продължавам да пиша по навик) следното:</p>
<pre>LinkedList&lt;String&gt; list = new LinkedList&lt;String&gt;();</pre>
<p>Вече няма нужда да подавате типа на обектите в списъците от дясната страна. Тоест горното ще е еквивалентно на:</p>
<pre>LinkedList&lt;String&gt; list = new LinkedList&lt;&gt;();</pre>
<p><strong>3. Няколко изключения в един catch блок</strong>:</p>
<p>Когато в един try блок могат да се генерират различни изключения ние пишехме по един catch блок за всяко, например:</p>
<pre>try{
   ...
}
catch(ArrayOutOfBoundsException e){
   ...
}
catch(IOException e){
   ...
}</pre>
<p>А сега можем да правим следното:</p>
<pre>try{
   FileReader f = new FileReader("file.txt");
   ...
}
catch(ArrayOutOfBoundsException | IOException e){
   ...
}</pre>
<p><strong>4. Заделяне на ресурси за try блок</strong>:</p>
<p>На последно тук, но според мен първо по значение е заделянето на ресурси за try блок, които автоматично се освобождават при неговото затваряне. Разгледайте например това:</p>
<pre>FileReader in = null;
try{
   in = new FileReader("file.txt");
   ...
}
catch(IOException e){
   ...
}
finally{
   try{
      if(in!=null) in.close()
   }
   catch(IOException e){
      ...
   }
}</pre>
<p>Това, което се правеше във finally блока изглеждаше много &#8222;паразитно&#8220; и не без основание. В новия си синтаксис в Java 7 можем да напишем много по-компактен код:</p>
<pre>try(FileReader in = new FileReader("File.txt")){
   ...
}
catch(IOException e){
   ...
}</pre>
<p>Така заделеният ресурс ще бъде затворен автоматично след излизане от try блока, независимо дали е генерирано изключение или не.</p>
<p>Приятно програмиране.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cphpvb.net/java/7875-four-new-features-in-java-7/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Задача от упражнение 5, 2011г.</title>
		<link>http://www.cphpvb.net/java/7862-exercise-5/</link>
		<comments>http://www.cphpvb.net/java/7862-exercise-5/#comments</comments>
		<pubDate>Sun, 13 Nov 2011 12:11:39 +0000</pubDate>
		<dc:creator>Филип Петров</dc:creator>
				<category><![CDATA[ПИК-3 Java]]></category>

		<guid isPermaLink="false">http://www.cphpvb.net/?p=7862</guid>
		<description><![CDATA[Да се направят конзолни приложения за чат-сървър и чат-клиент. Примерно решение: chatserver/Server.java: package chatserver; import java.io.IOException; import java.io.DataOutputStream; import java.net.Socket; import java.util.ArrayList; public class Server{ private static int port = 1234; private static ArrayList&#60;Socket&#62; clients = new ArrayList&#60;Socket&#62;(10); public static void main(String[] args){ ServerListener s = null; try{ s = new ServerListener(port); } catch(IOException e){ [...]]]></description>
			<content:encoded><![CDATA[<p>Да се направят конзолни приложения за чат-сървър и чат-клиент.</p>
<p>Примерно решение:<span id="more-7862"></span></p>
<p><strong>chatserver/Server.java</strong>:</p>
<pre>package chatserver;
import java.io.IOException;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.ArrayList;
public class Server{
  private static int port = 1234;
  private static ArrayList&lt;Socket&gt; clients = new ArrayList&lt;Socket&gt;(10);
  public static void main(String[] args){
    ServerListener s = null;
    try{
      s = new ServerListener(port);
    }
    catch(IOException e){
      System.out.println("Не може да се отвори ServerSocket на порт: "+port);
      return;
    }
    while(true){
      try{
        Socket clientSocket = s.acceptConnection();
        clients.add(clientSocket);
        new ServerThread(clientSocket);
      }
      catch(IOException e){
        System.out.println("Неуспешно свързване на клиент...");
      }
    }
  }
  static void sendToAll(String message){
    synchronized(clients){
      for(int i=0; i&lt;clients.size(); i++){
        try{
          DataOutputStream cos = new DataOutputStream(clients.get(i).getOutputStream());
          cos.writeUTF(message);
          cos.flush();
        }
        catch(IOException e){
          System.out.println("Не мога да изпратя съобщение...");
        }
      }
    }
  }
  static void removeClient(Socket c){
    synchronized(clients){
      System.out.println("Премахвам клиент "+c);
      clients.remove(clients.indexOf(c));
      try{
        c.close();
      }
      catch(IOException e){
        System.out.println("Не мога да затворя връзка с клиент");
      }
    }
  }
}</pre>
<p><strong>chatserver/ServerListener.java</strong>:</p>
<pre>package chatserver;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerListener{
  private ServerSocket servSock;
  public ServerListener(int port) throws IOException{
    servSock = new ServerSocket(port);
    System.out.println("Сървърът е стартиран и слуша на порт: "+port);
  }
  public Socket acceptConnection() throws IOException{
    Socket s = this.servSock.accept();
    System.out.println("Нов клиент "+s.toString());
    return s;
  }
}</pre>
<p><strong>chatserver/ServerThread.java</strong>:</p>
<pre>package chatserver;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
public class ServerThread extends Thread{
  private Socket sock;
  DataOutputStream os;
  public ServerThread(Socket sock){
    this.sock = sock;
    this.start();
  }
  public void run(){
    DataInputStream in;
    String message;
    try{
      in = new DataInputStream(this.sock.getInputStream());
      while(true){
        message = in.readUTF();
        if(message.equals("BYE")){
          break;
        }
        else{
          System.out.println(message);
          Server.sendToAll(message);
        }
      }
    }
    catch(IOException e){
      System.out.println("Проблем с предаване на данни до "+this.sock.toString());
    }
    finally{
      Server.removeClient(this.sock);
    }
  }
}</pre>
<p><strong>chatclient/Client.java</strong>:</p>
<pre>package chatclient;
import java.util.Scanner;
import java.io.IOException;
public class Client{
  public static void main(String[] args){
    String host = "localhost";
    int port = 1234;
    String name = "Петър";
    Scanner keyboardIn = new Scanner(System.in);
    ClientThread c = new ClientThread(host, port);
    String messageOut;
    while(true){
      messageOut = keyboardIn.nextLine();
      if(messageOut.equalsIgnoreCase("exit")){
        c.sendMessage("BYE");
        c.interrupt();
        break;
      }
      else c.sendMessage(name+" каза: "+messageOut);
    }
    try{
      c.close();
      System.out.println("Успешен изход от системата");
    }
    catch(Exception e){
      System.out.println("Проблем при прекратяването на връзка");
    }
  }
}</pre>
<p><strong>chatclient/ClientThread.java</strong>:</p>
<pre>package chatclient;
import java.net.Socket;
import java.io.DataOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
public class ClientThread extends Thread{
  private Socket socket;
  private DataOutputStream out;
  private DataInputStream in;
  public ClientThread(String host, int port){
    try {
      this.socket = new Socket(host, port);
      System.out.println("Свързах се с "+socket);
      this.in = new DataInputStream(this.socket.getInputStream());
      this.out = new DataOutputStream(this.socket.getOutputStream());
      // Стартиране на нишката, която очаква получаване на съобщения
      this.start();
    }
    catch(IOException e){
      System.out.println("Не мога да се свържа със сървъра");
    }
  }
  public void run(){
    try{
      while(true){
        System.out.println(this.in.readUTF());
      }
    }
    catch(IOException e){
      System.out.println("Прекратяване на нишката поради невъзможност за четене");
      this.interrupt();
    }
  }
  void sendMessage(String message){
    try{
      this.out.writeUTF(message);
      this.out.flush();
    }
    catch(IOException e){
      System.out.println("Не мога да изпратя съобщението");
    }
  }
  void close() throws IOException{
    if(this.in!=null) in.close();
    if(this.out!=null) out.close();
    if(this.socket!=null) socket.close();
  }
}</pre>
<p><strong>Допълнителна задача</strong>: Реализирайте функционалност за предаване на лични съобщения между различни потребители. За целта запазете имената на клиентите и на сървъра и когато например някой клиент подаде съобщение от типа &#8222;/msg Petar abcdefg&#8220;, то на потребител с име &#8222;Petar&#8220; ще бъде подадено съобщението &#8222;abcdefg&#8220; (вместо да бъде разпратен целия String на всички).</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cphpvb.net/java/7862-exercise-5/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Задача от упражнение 3, 2011г.</title>
		<link>http://www.cphpvb.net/java/7857-excersice-3/</link>
		<comments>http://www.cphpvb.net/java/7857-excersice-3/#comments</comments>
		<pubDate>Tue, 08 Nov 2011 19:44:30 +0000</pubDate>
		<dc:creator>Филип Петров</dc:creator>
				<category><![CDATA[ПИК-3 Java]]></category>

		<guid isPermaLink="false">http://www.cphpvb.net/?p=7857</guid>
		<description><![CDATA[Задача 1: Съставете клас за работа с комплексни числа. Да се съставят методи реализиращи операциите сбор, разлика, умножение и деление на комплексни числа. Реализирайте методи за отпечатване на комплексното число и за връщане на комплесно спрегнатото число. Примерно решение: package complexnum; import java.lang.ArithmeticException; public class ComplexNumber{ private double re; private double im; public ComplexNumber(){ this.re [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Задача 1</strong>: Съставете клас за работа с комплексни числа. Да се съставят методи реализиращи операциите сбор, разлика, умножение и деление на комплексни числа. Реализирайте методи за отпечатване на комплексното число и за връщане на комплесно спрегнатото число.</p>
<p>Примерно решение:<span id="more-7857"></span></p>
<pre>package complexnum;
import java.lang.ArithmeticException;

public class ComplexNumber{
  private double re;
  private double im;
  public ComplexNumber(){
    this.re = 0;
    this.im = 0;
  }
  public ComplexNumber(ComplexNumber input) {
    this.re = input.getRe();
    this.im = input.getIm();
  }
  public ComplexNumber(double re, double im){
    this.re = re;
    this.im = im;
  }
  public double getRe(){
    return this.re;
  }
  public double getIm(){
    return this.im;
  }
  public void setRe(double re){
    this.re = re;
  }
  public void setIm(double im){
    this.im = im;
  }
  public String toString(){
    String result="";
    if (this.im==0){
      result += this.re;
    }
    else if (this.re==0){
      result += this.im+"i";
    }
    else if (this.im&amp;gt0){
      result += this.re+"+"+this.im+"i";
    }
    else if (this.im&lt;0){
      result += this.re+""+this.im+"i";
    }
    return result;
  }
  public void print(){
    System.out.println(this.toString());
  }
  public ComplexNumber getConjugate() {
    return new ComplexNumber(this.re, this.im * (-1));
  }
  public ComplexNumber add(ComplexNumber op) {
    ComplexNumber result = new ComplexNumber();
    result.setRe(this.re + op.getRe());
    result.setIm(this.im + op.getIm());
    return result;
  }

  public ComplexNumber substract(ComplexNumber op) {
    ComplexNumber result = new ComplexNumber();
    result.setRe(this.re - op.getRe());
    result.setIm(this.im - op.getIm());
    return result;
  }

  public ComplexNumber multiply(ComplexNumber op) {
    ComplexNumber result = new ComplexNumber();
    result.setRe(this.re * op.getRe() - this.im * op.getIm());
    result.setIm(this.re * op.getIm() + this.im * op.getRe());
    return result;
  }

  public ComplexNumber divide(ComplexNumber op) throws ArithmeticException {
    if(op.getRe()==0 &amp;&amp; op.getIm()==0){
      throw new ArithmeticException("Division by zero");
    }
    ComplexNumber result = new ComplexNumber(this);
    result = result.multiply(op.getConjugate());
    double opNormSq = op.getRe()*op.getRe()+op.getIm()*op.getIm();
    result.setRe(result.getRe() / opNormSq);
    result.setIm(result.getIm() / opNormSq);
    return result;
  }
}</pre>
<p><strong>Задача 2</strong>. Напишете клас с main метод, в който прочитате израз от вида (a+bi)OP(c+di), където OP={*,/,+,-}, а {a,b,c,d} са цели числа. Като изход изведете резултата от извършената операция между две комплексни числа.</p>
<p>Примерен вход: (1-2i)-(-2+3i)<br />
Очакван изход: 3.0-5.0i</p>
<p>Примерно решение:</p>
<pre>package complexnum;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.Scanner;

public class program{
  public static void main(String[] args){
    Scanner keyboardIn = new Scanner(System.in);
    System.out.print("Въведете израз: ");
    String expression = keyboardIn.nextLine();
    Pattern p = Pattern.compile("^\\(([+-]?[0-9]+)?([+-]?[0-9]+i)?\\)([*/+-])\\(([+-]?[0-9]+)?([+-]?[0-9]+i)?\\)$");
    Matcher m = p.matcher(expression);
    if (m.find()){
      try{
        double a=0, b=0, c=0, d=0;
        if (m.group(1)!=null) a = Double.parseDouble(m.group(1));
        if (m.group(2)!=null) b = Double.parseDouble(m.group(2).substring(0,m.group(2).length()-1));
        if (m.group(4)!=null) c = Double.parseDouble(m.group(4));
        if (m.group(5)!=null) d = Double.parseDouble(m.group(5).substring(0,m.group(5).length()-1));

        ComplexNumber c1 = new ComplexNumber(a, b);
        ComplexNumber c2 = new ComplexNumber(c, d);
        switch(m.group(3)){
          case "*":
            (c1.multiply(c2)).print();
            break;
          case "/":
            (c1.divide(c2)).print();
            break;
          case "+":
            (c1.add(c2)).print();
            break;
          case "-":
            (c1.substract(c2)).print();
            break;
        }
      }
      catch(java.util.InputMismatchException e){
        e.printStackTrace();
      }
      catch(ArithmeticException e){
        System.out.println("Не може да делите на нула!");
      }
    }
  }
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.cphpvb.net/java/7857-excersice-3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Пример за приложения използващи сокети</title>
		<link>http://www.cphpvb.net/java/7850-socket-and-serversocket/</link>
		<comments>http://www.cphpvb.net/java/7850-socket-and-serversocket/#comments</comments>
		<pubDate>Mon, 31 Oct 2011 17:41:03 +0000</pubDate>
		<dc:creator>Филип Петров</dc:creator>
				<category><![CDATA[ПИК-3 Java]]></category>

		<guid isPermaLink="false">http://www.cphpvb.net/?p=7850</guid>
		<description><![CDATA[В представеното приложение е реализирана възможно най-базовата комуникация между клиент и сървър, при която се разменят поредица от текстови съобщения. Обърнете внимание, че не се използват нишки, т.е. за една &#8222;сесия&#8220; сървърът може да комуникира само с един клиент. Server.java: import java.io.PrintWriter; import java.net.Socket; import java.net.ServerSocket; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException; import java.util.Scanner; public [...]]]></description>
			<content:encoded><![CDATA[<p>В представеното приложение е реализирана възможно най-базовата комуникация между клиент и сървър, при която се разменят поредица от текстови съобщения. Обърнете внимание, че не се използват нишки, т.е. за една &#8222;сесия&#8220; сървърът може да комуникира само с един клиент.<span id="more-7850"></span></p>
<p><strong>Server.java</strong>:</p>
<pre>import java.io.PrintWriter;
import java.net.Socket;
import java.net.ServerSocket;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.Scanner;

public class Server{
  public static void main(String args[]) {
    ServerSocket serverSocket = null;
    Socket connection = null;
    Scanner socketIn = null;
    PrintWriter socketOut = null;
    int port = 1234;

    // Безкраен цикъл - сървъра ще приема връзки
    // докато програмата не бъде спряна насилствено
    while(true){
      try {
        serverSocket = new ServerSocket(port);
        System.out.println("Сървъра очаква свързване...");
        connection = serverSocket.accept();
        System.out.println("Свърза се клиент:"
              +connection.getInetAddress().getHostName()
        );

        System.out.println("Подавам съобщение на клиента...");
        socketOut = new PrintWriter(connection.getOutputStream(), true);
        socketOut.println("Възможните команди са \"hi\" и \"exit\"");

        System.out.println("Сървъра очаква подаване на команда...");
        socketIn = new Scanner(new BufferedReader(
                          new InputStreamReader(connection.getInputStream()))
                       );

        String command = null;
        loop: do{
          socketOut.flush();
          command = socketIn.nextLine();
          switch(command){
            case "hi":
              System.out.println("Клиентът подаде команда \"hi\"");
              socketOut.println("OK");
              break;
            case "exit":
              System.out.println("Клиентът подаде команда \"exit\"");
              socketOut.println("OK");
              break loop;
            default:
              System.out.println("Получих непозната команда");
              socketOut.println("Не познавам тази команда");
              break;
          }
        }
        while(!command.equalsIgnoreCase("exit"));
        System.out.println("Затварям връзката с "
                             +connection.getInetAddress().getHostName()
                          );
      }
      catch(IOException e) {
        e.printStackTrace();
      }
      finally{
        try{
          if (socketIn!=null) socketIn.close();
          if (socketOut!=null) socketOut.close();
          if (connection!=null) connection.close();
          if (serverSocket!=null) serverSocket.close();
        }
        catch(IOException e){
          System.err.println("Не може да бъде затворен сокет");
        }
      }
    }
  }
}</pre>
<p>&nbsp;</p>
<p><strong>Client.java</strong>:</p>
<pre>import java.io.PrintWriter;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Scanner;
import java.net.Socket;
import java.io.IOException;
import java.net.ConnectException;

public class Client{
   public static void main(String args[]){
     Socket connection = null;
     Scanner socketIn = null;
     PrintWriter socketOut = null;
     Scanner keyboardIn = new Scanner(System.in);
     int port = 1234;
     String host = "localhost";

     try{
       System.out.println("Очаквам свързване със сървъра");
       try{
         connection = new Socket(host, port);
       }
       catch(ConnectException e){
         System.err.println("Не мога да осъществя връзка със сървъра");
         return;
       }
       System.out.println("Свързването със сървъра беше успешно!");

       System.out.println("Приемам първоначално съобщение от сървъра...");
       socketIn = new Scanner(new BufferedReader(
                         new InputStreamReader(connection.getInputStream()))
                      );
       System.out.println("Съобщението е: "+socketIn.nextLine());

       String command = null;
       socketOut = new PrintWriter(connection.getOutputStream(), true);
       do{
         socketOut.flush();
         System.out.print("Въведете команда: ");
         command = keyboardIn.nextLine();
         socketOut.println(command.toLowerCase());

         System.out.println("Изчакване на отговор от сървъра...");
         String answer = socketIn.nextLine();
         System.out.println("Сървърът отговори: "+answer);
       }
       while (!command.equalsIgnoreCase("exit"));
       System.out.println("Затваряне на връзката...");

     }
     catch(IOException e) {
       e.printStackTrace();
     }
     finally{
       try{
         if(socketIn!=null) socketIn.close();
         if(socketOut!=null) socketOut.close();
         if(connection!=null) connection.close();
       }
       catch(IOException e){
         System.err.println("Не може да се затвори сокета");
       }
     }
   }
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.cphpvb.net/java/7850-socket-and-serversocket/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Метод hashCode()</title>
		<link>http://www.cphpvb.net/java/6542-hashcode/</link>
		<comments>http://www.cphpvb.net/java/6542-hashcode/#comments</comments>
		<pubDate>Wed, 08 Dec 2010 18:51:19 +0000</pubDate>
		<dc:creator>Филип Петров</dc:creator>
				<category><![CDATA[ПИК-3 Java]]></category>

		<guid isPermaLink="false">http://www.cphpvb.net/?p=6542</guid>
		<description><![CDATA[Хеширането е важна част от езика за програмиране Java. То ни позволява при определени случаи ефективно и бързо да сравняваме обекти подобно на стандартния метод equals(). Когато на обект от даден клас извикаме метод &#8222;hashCode()&#8220;, то като резултат получаваме цяло число (int). Идеята е следната: &#8222;Ако два обекта са еднакви, то извикването на техните методи [...]]]></description>
			<content:encoded><![CDATA[<p>Хеширането е важна част от езика за програмиране Java. То ни позволява при определени случаи ефективно и бързо да сравняваме обекти подобно на стандартния метод equals(). Когато на обект от даден клас извикаме метод &#8222;hashCode()&#8220;, то като резултат получаваме цяло число (int). Идеята е следната:</p>
<p>&#8222;<em>Ако два обекта са еднакви, то извикването на техните методи hashCode() трябва да връщат равни числа</em>&#8220; (1)<span id="more-6542"></span></p>
<p>Ето един пример:</p>
<pre>public class hashCodeExample{
   public static void main(String[] args){
      String s1 = "abcd";
      String s2 = "abcd";
      String s3 = "aBcd";
      System.out.println(s1+" has hash code: "+s1.hashCode());
      System.out.println(s2+" has hash code: "+s2.hashCode());
      System.out.println(s3+" has hash code: "+s3.hashCode());
   }
}</pre>
<p>Резултатът ще бъде:</p>
<pre>abcd has hash code: 2987074
abcd has hash code: 2987074
aBcd has hash code: 2956322
</pre>
<p>Виждате, че обектите, които са еднакви имат и еднакви хеш кодове. Това обаче не винаги ще бъде вярно, когато самите ние създаваме класове. Нека дадем следния пример:</p>
<pre>public class hashCodeExample{
   public static void main(String[] args){
      Person p1 = new Person("Ivan", 23);
      Person p2 = new Person("Ivan", 23);
      Person p3 = new Person("Petar", 23);
      System.out.println(p1.name+" age "+p1.age+" hash code: "+p1.hashCode());
      System.out.println(p2.name+" age "+p2.age+" hash code: "+p2.hashCode());
      System.out.println(p3.name+" age "+p3.age+" hash code: "+p3.hashCode());
   }
}

class Person{
   public String name;
   public int age;
   public Person(String name, int age){
      this.name = name;
      this.age = age;
   }
}</pre>
<p>Резултатът ще бъде:</p>
<pre><strong>Ivan age 23 hash code: 9555723
Ivan age 23 hash code: 8414877</strong>
Petar age 23 hash code: 15613422
</pre>
<p>Виждаме, че клас Person не отговаря на условието (1). Следователно ние трябва да се погрижим да предефинираме метод hashCode() така, че два еднакви обекта да връщат един и същи хеш код. Това може да стане например така:</p>
<pre>public class hashCodeExample{
   public static void main(String[] args){
      Person p1 = new Person("Ivan", 23);
      Person p2 = new Person("Ivan", 23);
      Person p3 = new Person("Petar", 23);
      System.out.println(p1.name+" age "+p1.age+" hash code: "+p1.hashCode());
      System.out.println(p2.name+" age "+p2.age+" hash code: "+p2.hashCode());
      System.out.println(p3.name+" age "+p3.age+" hash code: "+p3.hashCode());
   }
}

class Person{
   public String name;
   public int age;
   public Person(String name, int age){
      this.name = name;
      this.age = age;
   }
   public int hashCode(){
      return (this.name.hashCode()+this.age);
   }
}</pre>
<p>Резултатът вече ще е валиден:</p>
<pre>Ivan age 23 hash code: 2291281
Ivan age 23 hash code: 2291281
Petar age 23 hash code: 77005191
</pre>
<p>Виждаме, че за да направим обектите си валидни, то елементарно трябва да предефинираме методът hashCode(). Въпросът, който остава е &#8211; проблем ли е ако ние не го правим? Всички програми в досегашните ни примери работеха напълно коректно и нямаха проблеми. Тогава защо ни е да предефинираме метод hashCode()? За да отговорим на този въпрос трябва да разгледаме два варианта:</p>
<ol>
<li>Обектите са еднакви, но връщат различни хеш кодове: няма абсолютно никакъв проблем да работим с такива обекти, но ще такъв ще се появи тогава, когато ги добавяме към HashSet или HashMap (тези колекции ще разгледаме по-късно в следваща статия). Тези колекции използват функцията hashCode() и очакват правилото за нея да е спазено. Следователно резултатите в този случай стават непредвидими!;</li>
<li>Обектите са различни, но връщат еднакви хеш кодове: това принципно не е проблем &#8211; не нарушава дефиницията на hashCode(). Единственото негативно в този случай ще е, че HashSet и HьshMap ще работят по-бавно когато има много обекти с много повторения на хеш кода. Затова е добре хеш кодовете да са максимално различни при различните обекти.</li>
</ol>
<p>С дотук казаното можем да заключим, че предефинирането на метод hashCode() е препоръчително за всеки клас. Освен това трябва да се стремим стойностите да са максимално различни. Последното впрочем е трудна задача в определени случаи. Дефинирането на добър hashCode() винаги е предизвикателство. В книгата &#8222;<a href="http://www.linuxtopia.org/online_books/programming_books/thinking_in_java/TIJ313_029.htm" target="_blank">Thinking in Java</a>&#8220; се дава следната &#8222;рецепта&#8220; &#8211; резултатът от функцията започва от числото 17 и за всяка променлива от класа го умножаваме по 37 и добавяме хеш кодът или стойността на променливата превърната в int (при примитивен тип данни) към него. Ето пример:</p>
<pre>class Person{
  public String firstname;
  public String lastname;
  public int age;
  public Person(String firstname, String lastname, int age){
    this.firstname = firstname;
    this.lastname = lastname;
    this.age = age;
  }
<strong>  public int hashCode(){
    int result = 17;
    result = result*37 + this.firstname.hashCode();
    result = result*37 + this.lastname.hashCode();
    result = result*37 + this.age;
    return result;
  }</strong>
}</pre>
<p>Ще забележите, че по този начин е възможно числата да станат отрицателни, защото прехвърлят размера на типа данни. Това не е проблем &#8211; хеш кодът може да е отрицателен. Спазването на горната рецепта е доказало практическата си ефективност. Препоръчва се такъв метод hashCode() да се предефинира във всеки клас.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.cphpvb.net/java/6542-hashcode/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<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[ПИК-3 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[ПИК-3 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[ПИК-3 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[ПИК-3 Java]]></category>

		<guid isPermaLink="false">http://www.cphpvb.net/?p=4486</guid>
		<description><![CDATA[1. Да се създаде клас &#8222;точка&#8220; с член променливи &#8222;координати по осите x и y&#8220;. Създайте необходимите конструктори и get методи. Защитете координатите така, че да не могат да бъдат променяни след като бъдат инициализирани. Създайте метод отпечатващ координатите на точката. Създайте статичен метод, който сравнява координатите на две точки и показва дали те съвпадат: [...]]]></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[ПИК-3 Java]]></category>

		<guid isPermaLink="false">http://www.cphpvb.net/?p=4459</guid>
		<description><![CDATA[1. Дефинирайте клас, описващ дати от календара. а) Дефинирайте конструктор по подразбиране и такъв с параметри за задаване на дата. б) Дефинирайте за него подходящите get и set методи. в) Направете необходимите проверки за валидност на данните (ако не са валидни да се генерира изключение): calException.java: package example; // Клас дефиниращ изключение "невалидна дата" public [...]]]></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>
<pre>// Намира средния успех на потока
potokstats getSrUsp(){
  if(firstFreeSlot == 0){
    System.err.println("No students in this group yet");
    return null;
  }
  double srusp = 0;
  for (int i=0; i &lt; this.firstFreeSlot; i++){
    srusp += this.list[i].getGPA();
  }
  srusp /= this.firstFreeSlot;
  return new potokstats(this.potokN, this.specN, srusp);
}</pre>
<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>2</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[ПИК-3 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>3</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[ПИК-3 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{ Car c [...]]]></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[ПИК-3 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[ПИК-3 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(); ChangeThread [...]]]></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[ПИК-3 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[ПИК-3 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 обаче е възможно константата само да се декларира и да бъде инициализирана по-късно: final [...]]]></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[ПИК-3 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[ПИК-3 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[ПИК-3 Java]]></category>

		<guid isPermaLink="false">http://www.cphpvb.net/?p=4318</guid>
		<description><![CDATA[&#8222;Анонимните&#8220; класове са специален вид локални класове. Специалното при тях е, че те нямат специални имена и на тях винаги правим само една инстанция. Нека вземем за пример изчисляването на дължината на хипутенизата на правоъгълен триъгълник. &#8222;Не-анонимно&#8220; би било извикването на програмата: double a = 3; double b = 4; double c = Math.sqrt(a*a + [...]]]></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[ПИК-3 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>
	</channel>
</rss>

