Rozdíl mezi statickou a nestatickou vnořenou třídou v Javě

Vnořená třída, která není statická, má přístup ke všem proměnným a metodám (i private) nadřazené třídy. Tato vnořená třída ale nelze vytvořit bez instance nadřazené (zaobalující) třídy.

Třída s vnořenou nestatickou třídou

public class ClassWithInner {

	private String message;
	
	public InnerClass getInnerClass() {
		return new InnerClass();
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

	public class InnerClass {
		
		private int number;
		
		public void getMessage() {
			System.out.println(message);
			setMessage("new message");
			System.out.println(ClassWithInner.this.getMessage());
		}

		public int getNumber() {
			return number;
		}

		public void setNumber(int number) {
			this.number = number;
		}
	}
}

Vytvoření v main metodě

public static void main(String[] args) {

	// nelze, neexistuje instance ClassWithInner
	// ClassWithInner.InnerClass innerClass = new ClassWithInner.InnerClass();
	
	ClassWithInner classWithInner = new ClassWithInner();
	InnerClass innerClass = classWithInner.getInnerClass();
	innerClass.printMessage();
}

Vnořená statická třída nepotřebuje k vytvoření instanci zaobalující třídy. Na rozdíl od nestatické, ale nemá přístup k proměnným obalující třídy.

Třída s vnořenou statickou třídou

public class ClassWithInnerStatic {

	private String message;
	
	public InnerStaticClass getInnerStaticClass() {
		return new InnerStaticClass();
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

	public static class InnerStaticClass {
		
		int number;
		
		public void printMessage() {
			// nelze: Cannot make a static reference to the non-static method getMessage() from the type ClassWithInnerStatic
			// System.out.println(getMessage());
		}

		public int getNumber() {
			return number;
		}

		public void setNumber(int number) {
			this.number = number;
		}
	}
}

Vytvoření v main metodě

public static void main(String[] args) {
	
	// třída lze vytvoři i bez instance ClassWithInnerStatic
	ClassWithInnerStatic.InnerStaticClass innerStaticClass = new ClassWithInnerStatic.InnerStaticClass();
	innerStaticClass.setNumber(123);
	System.out.println(innerStaticClass.getNumber());
}

Vytvoření volitelně dlouhého řetězce znaků v Javě

Občas je potřeba vytvořit řetězec znaků o volitelné délce. Pro tento případ lze využít třídy Arrays a její metody fill() a toho, že řetězec v Javě lze vytvořit z pole znaků.

char c = '=';
String headerText = " A W E S O M E   P R O G R A M ";

char[] charArray = new char[headerText.length()];
Arrays.fill(charArray, c);
String headerLine = new String(charArray);

System.out.println(headerLine);
System.out.println(headerText);
System.out.println(headerLine);

Výsledek

===============================
 A W E S O M E   P R O G R A M 
===============================

Obdobně, ale na jediné řádce a pomocí metody replace().

String headerLine = new String(new char[headerText.length()]).replace('\0', c);

Zdroj: stackoverflow.com/…/create-a-string-with-n-characters

Setřízení seznamu řetězců dle locale v Javě

Pro seřazení seznamu se v Javě používá třída java.util.Collections a její metoda sort(). Tato metoda je přetížená a má možnost jako paramer mít objekt implementující Comparator, což je rozhraní s metodou compare(objekt_1, objekt_2). Jako Comparator lze tedy použít i instanci třídy Collator, která rozhraní Comparator implementuje. Výhodou Collatoru je to, že umožňuje řetězce porovnávat dle locale. Pokud budeme chtít správně porovnávat a řadit řetězce s českou diakritikou, je třeba použít Collator a české locale.

import java.text.Collator;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;

public class AppStringCompare {
	public static void main(String[] args) {
		List<String> list = Arrays.asList("zelí", "mrkev", "česnek", "petržel", "řepa");
		Collator collator = Collator.getInstance(new Locale("cs", "CZ"));
		Collections.sort(list, collator);
		System.out.println(Arrays.toString(list.toArray()));
	}
}

Výsledek

[česnek, mrkev, petržel, řepa, zelí]

Posílání jednoduchých emailů přes Google SMTP

Pokud máte účet na gmail.com, můžete použít Google SMTP pro posílání emailů z vaší aplikace. Pozor ale na to, že počet takto zaslaných emailů je omezen (maximálně 20 za hodinu). Pro zkoušení to ale většinou stačí.

Funguje to tak, že Google SMTP server přijme vaši zprávu a předá ji dalšímu serveru pomocí SMTP protokolu (Simple Mail Transfer Protocol). Tak to pokračuje do té doby, dokud se zpráva nedostane na server, kde je vaše poštovní schránka. Pro přístup k poštovní schránce se pak používají protokoly POP3 nebo IMAP.

Následující příklad ukazuje poslání jednoduchého textového emailu přes Google SMTP server. Jak poslat html email ukážu v nějakém další příspěvku.

import java.util.Properties;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

public class GoogleMail {
	public static void main(String args[]) throws AddressException, MessagingException {
		System.out.println("Started");
		
		// Set up properties
		Properties mailServerProperties = System.getProperties();
		mailServerProperties.put("mail.smtp.port", "587");
		mailServerProperties.put("mail.smtp.auth", "true");
		mailServerProperties.put("mail.smtp.starttls.enable", "true");

		// Get session
		Session mailSession = Session.getDefaultInstance(mailServerProperties);

		// Create message
		MimeMessage message = new MimeMessage(mailSession);
		message.addRecipient(Message.RecipientType.TO, new InternetAddress("email@email.cz"));
		message.setSubject("Pokusný email", "UTF-8");
		message.setText("Ahoj, toto je pokusný email.", "UTF-8");

		// Send
		Transport transport = mailSession.getTransport("smtp");
		transport.connect("smtp.gmail.com", "user", "password");
		transport.sendMessage(message, message.getAllRecipients());
		transport.close();

		System.out.println("Finished");
	}
}

Pro zaslání emailu se musí nejdříve vytvořit session, které předáme properties. Dále se vytvoří email, který by měl minimálně obsahovat TO adresu, SUBJECT a TEXT. Zároveň se dá nastavit kódování. Já jsem použil UTF-8. Nakonec se provede samotné poslání emailu (připojení na server, odeslání, ukončení připojení). Pro připojení k serveru je třeba uvést platného uživatele (pokud máte email jmeno.prijmeni@gmail.com tak jako uživatele uveďte jmeno.prijmeni) a heslo.

Pokud dostanete chybu AuthenticationFailedException: 534-5.7.14, řešení naleznete v tomto příspěvku.

Pokud vše proběhne v pořádku a přesto vám nedojde email na emailovou adresu zadanou v TO (zde je to na email@email.cz), zkontrolujte si, zda neskončil ve spamu. Z vlastní zkušenosti mohu říci, že pokud máte email na seznam.cz, zprávy zprávy se zobrazí v emailové schránce téměř okamžitě. Pokud používáte email na centrum.cz, můžete čekat i několik desítek vteřin.


Zdroj:

BufferedReader, BufferedWriter a try with resources

BufferedReader načítatá textová data do paměti (buffer) a umožňuje efektivnější čtení po řádcích. Obdobně to platí pro BufferedWriter, který zase zapisuje po blocích a ne znak po znaku. Blok try-with-resources zjednodušuje práci se zdroji a zpracování výjimek a je vhodný například právě pro načítání ze a zápis do souborů. Zdroje (zde to představuje streamy pro čtení a zápis) definované v části try budou po skončení bloku (a nebo v případě vyhození výjimky) korektně uzavřeny.

V následujícím příkladu čtu ze souboru input.txt, který je uložen v kódování Windows-1250 a hned načtené řádky ukládám do souboru output.txt. Pro ukládání používám kódování UTF-8. Soubor input.txt se nachází v adresáři s projektem (v adresáři, kde je i složka src).

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;

public class App {

	public static void main(String[] args) throws UnsupportedEncodingException, FileNotFoundException, IOException {
		
		try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("input.txt"), "Cp1250"))) {
			try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("output.txt"), "UTF-8"))) {
				
				String line = null;
				while ((line = br.readLine()) != null) {
					System.out.println(line);
					bw.write(line);
					bw.newLine();
				}
			}
		}
	}
}

Pravděpodobně jste si všimli, pro někoho trochu divoké, konstrukce BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("input.txt"), "Cp1250")). Při práci se streamy se jednotlivé streamy zabalují do sebe a tím se výslednému streamu přidává funkcionalita. V tomto příkladu FileInputStream dokáže číst ze souboru ale pouze ve výchozím kódování. Pokud je kódování jiné, výstup vypadá třeba takto:

�lu�ou�k� k�� �p�l ��belsk� �dy.

Pokud ale tento stream zabalíme do InputStreamReader, může určit i kódování souboru. Stále ale načítáme znaky jednotlivě, což není příliš efektivní a určitě by bylo lepší je načítat po blocí (po řádcích). V tom případě použijeme BufferedReader, kterému předáme jako parametr InputStreamReader. Obdobné je i pro zapisování BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("output.txt"), "UTF-8")). Zde se ale zase používají streamy obsahující v názvu Writer nebo Output.

Samotné čtení a zápis se děje v této části:

String line = null;
while ((line = br.readLine()) != null) {
	System.out.println(line);
	bw.write(line);
	bw.newLine();
}

Do proměnné line postupně načítáme řádky. V případě že BufferedReader#readLine() vrátí null znamená to, že jsem na konci souboru. Pro zápis se používá metoda BufferedWriter#write(). Pokud chceme zapsat konec rádky, používá se metoda BufferedWriter#newLine(). Po skončení práce se streamy by se na nich měla zavolat metoda close(). To platí i v případě, že máme otevřený stream a dojde k výjimce. Používám ale blok try-with-resources takže to udělá za mě.


Zdroje:

Proč nepoužívat double pro přesné výpočty

Čísla s desetinnou čárkou jsou v počítači reprezentována podle standardu IEEE 754. Tato reprezentace ale není přesná což může vést k následujícím situacím.

double x = 0.5;
double y = 0.4;
double z = x - y;

System.out.println("Hodnota z: \t" + z);

Výsledek

Hodnota z: 0.09999999999999998

Další příklad

double mixer = 1099.00;
double mixerPoSleve = mixer * 0.9;
double trouba = 5999.00;
double troubaPoSleve = trouba * 0.9;
double celkovaCena = troubaPoSleve + mixerPoSleve;

System.out.println("mixér po slevě: \t" + mixerPoSleve);
System.out.println("trouba po slevě: \t" + troubaPoSleve);
System.out.println("celková cena: \t\t" + celkovaCena);

Výsledek

mixér po slevě: 989.1
trouba po slevě: 5399.1
celková cena: 6388.200000000001

Řešením je použít datový typ BigDecimal. Zde je s použitím BigDecimal upraven první příklad.

BigDecimal x = new BigDecimal("0.5");
BigDecimal y = new BigDecimal("0.4");
BigDecimal z = x.subtract(y);

System.out.println("Hodnota z: \t" + z);

Výsledek

Hodnota z: 0.1

Prohlížení .jar souborů

Soubor .jar obsahuje zkomprimované soubory typu class a další zdroje. Používá se pro zabalení všech souborů potřebných pro běh Java aplikace. Tento soubor je zkomprimovaný a lze jej bez problémů otevřít například v nástroji 7-Zip. Obsahuje ale soubory .class, což jsou soubory obsahující bytecode.

java decompiler 01

Pokud si chcete takový soubor prohlédnout v čitelné (pro programátora) formě, můžete použít například Java Decompiler. Po jeho otevření stačí vybrat .jar (pro otevření celé Java aplikace) nebo .class soubor a Java Decompiler je dekompiluje a zobrazí jejich obsah. Na podtržení třídy se lze prokliknout.

java decompiler 02

Enum: od jednoduchého po víceparametrický

Enum je výčtový typ. Představuje omezený výčet hodnot. Enum nemusí představovat pouze název, ale též hodnoty.

Jednoduchý bezparametrický enum.

public enum Country {	
  	CZ, SK, EN
}

Volání

System.out.println(Country.CZ.name());

Výsledek

CZ

Enum s jením parametrem

public enum Country {	
	CZ(1), 
	SK(2), 
	EN(3);
	
	private int code;
	
	Country(int code) {
		this.code = code;
	}
	
	public int getCode() {
		return code;
	}
}

Volání

Country sk = Country.SK;
System.out.println(sk.name());
System.out.println(sk.getCode());

Výsledek

SK
2

Enum s několika paramatery

public enum Country {	
	CZ(1, "Česko", "Česká republika"), 
	SK(2, "Slovensko", "Slovenská republika"), 
	EN(3, "Velká Britanie", "Spojené království Velké Británie a Severního Irska");
	
	private int code;
	private String popis;
	private String popisDlouhy;
	
	Country(int code, String popis, String popisDlouhy) {
		this.code = code;
		this.popis = popis;
		this.popisDlouhy = popisDlouhy;
	}
	
	public int getCode() {
		return code;
	}

	public String getPopis() {
		return popis;
	}

	public String getPopisDlouhy() {
		return popisDlouhy;
	}
}

Volání

Country en = Country.EN;
System.out.println(en.name());
System.out.println(en.getCode());
System.out.println(en.getPopis());
System.out.println(en.getPopisDlouhy());

Výsledek

EN
3
Velká Britanie
Spojené království Velké Británie a Severního Irska

Zásobník v Javě

Třída Stack (zásobník) je datová stuktura, která funguje na principu LIFO (Last In First Out). Jedná se o kolekci objektů, kdy poslední vložený objekt je vybírán jako první (a první vložený jako poslední).

Vytvoříme si zásobník obsahující čtyři objekty.

Stack<Integer> stack = new Stack<Integer>();
stack.push(5);
stack.push(7);
stack.push(2);
stack.push(7);

Jedná se o kolekci, takže je možné ji procházet.

for (Integer i : stack) {
	System.out.println(i);
}

Pro přidání objektu na konec zásobníku se používá metoda push(). Pro získání posledně přidaného objektu se používají metody peek() a pop().

peek()

System.out.println(stack.peek());
System.out.println(stack.peek());

Výsledek (pořád vrací poslení objekt)

7
7
7
7
7

pop()

System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack.pop());

Výsledek

7
2
7
5
Exception in thread "main" java.util.EmptyStackException

Čtvrté volání metody pop() vrátí poslední objekt (první přidaný) a zásobník je prázdný. Pokud zavoláme pop() ještě jednou (na prázdném zásobníku) dostaneme chybu EmptyStackException.

Vytvoření seznamu z množiny v Javě

Datový typ Set (množina) je kolekce prvků, kdy každý z prvků se v množině může vyskytovat maximálně jednou. Set se používá jako struktura, která odfiltruje duplicity. Následně je ale často potřeba převést Set na seznam (List). Díky tomu, že Set implementuje rozhraní Collection, je možné použít konstruktor new ArrayList<>(set).

Set set = new HashSet<>();
set.add(7L);
set.add(2L);
set.add(19L);
set.add(7L);
set.add(7L);
set.add(2L);

List list = new ArrayList<>(set);

System.out.println(Arrays.toString(list.toArray()));

Výsledek

[2, 19, 7]

V případě, že Set je prázdný, vytvoří se prázdný List.

Set set = new HashSet<>();
List list = new ArrayList<>(set);

System.out.println(Arrays.toString(list.toArray()));

Výsledek

[]