Добрый день, уважаемый читатель! Вы могли подумать, что я ушёл в глубокое подполье, так как вестей от меня нет уже довольно давно. Около 10 дней я работал над одним увлекшим меня проектом – синтезатором речи.

Идея была простая: записать звуки русского языка (28 для гласных [включая ударные], 36 для согласных (включая мягкие)), и делать компоновку в соответствие с правилами русского языка. Благо исключений в нем не так много, как в английском или французском, например.


image


Рисунок 1. Записи звуков.

Я записал все звуки, назвав их следующим образом: номер символа в java + a (если ударный/мягкий) + i (если йотированный) + .au. При обращении к файлу было очень легко собрать необходимый путь.

Алгоритм простой. Будем запрашивать строку, разбивать ее на слова (массив строк) по пробельным символам(а также знакам препинания). Далее если в слове лишь одна гласная, то она будет ударной. Если гласных нет вообще, то просто озвучить символ. Если в слове больше одной гласной, то программа гуглит (проводит парсинг с помощью jsoup) правильное ударение на сайте где-ударение.рф. Вся информация об ударениях хранится в отдельном целочисленном массиве gdeudarenie[].

Когда проводились пробные прослушивания, стало очевидно, что Java не воспроизводит короткие файлы в формате .wav, и необходимо конвертировать мои записи в .au.

Далее было написано тело программы. Были учтены многие правила русского языка: ставит ударение, правила, связанные с йотированными гласными, мягкость согласных, и др. Для достижения совершенства необходимо расширить количество правил чтения, учесть все возможные.

Была проведена калибровка, чтобы между отдельными звуками не было пауз, а между словами – была (500 мс).

Конечный код, основной класс:
package opening;

import java.util.Scanner;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import java.net.URLEncoder;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import java.io.File;
import java.io.IOException;

public class Solver {
// проигрывать звук
public static void playMusic(File it) {
try {
Clip clip = AudioSystem.getClip();
clip.open(AudioSystem.getAudioInputStream(it));
clip.start();
// чем больше знаменатель этой дроби, тем быстрее программа произносит звуки
Thread.sleep(clip.getMicrosecondLength() / 800);
} 
catch (Exception e) {
}
}

Solver() throws Exception {
// читаем строку
Scanner in = new Scanner(System.in);
String s = in.nextLine();
s = s.toLowerCase();
// разобъем строку по пробельным (и не только) символам
String[] words = s.split("\\s|\\. |\\, |\\? |\\! |\\; |\\: |\\'|\\-|\\_|\\ — ");
in.close();

int slozhnost = 0;
int[] gdeudarenie = new int[words.length];

for (int i = 0; i < gdeudarenie.length; i++) {
gdeudarenie[i] = -1;
}

System.out.println("Я определяю, где находится ударение в словах.");
for (int i = 0; i < words.length; i++) {
slozhnost = words[i].length() - words[i].replaceAll("а|о|у|ы|э|я|ё|ю|и|е", "").length();
if ((slozhnost == 0) | (slozhnost == 1)) {
gdeudarenie[i] = -1;
} 
else if (words[i].contains("ё") == true) {
for (int ko = 0; ko < words[i].length(); ko++) {
char[] prom = words[i].toCharArray();
if (prom[ko] == 'ё') {
gdeudarenie[i] = ko;
}
}
} 
else {
// будем гуглить на "https://где-ударение.рф/"
String punycodeURL = java.net.IDN.toASCII("где-ударение");
String conn = "https://" + punycodeURL + ".";
punycodeURL = java.net.IDN.toASCII("рф");
conn = conn + punycodeURL + "/";
String shelping = "в-слове-" + words[i];
conn = conn + URLEncoder.encode(shelping, "UTF-8");
try {
Document doc = Jsoup.connect(conn).userAgent("Chrome/4.0.249.0 Safari/532.5").referrer("http://www.google.com").get();
Elements listNews = doc.select("div");
String justaword = listNews.select("b").get(2).text();
byte[] winData = justaword.getBytes("Cp1251");
for (int jo = 0; jo < justaword.length(); jo++) {
if (winData[jo] == 63) {
gdeudarenie[i] = jo - 1;
}
}
} catch (IOException e) {
System.out.println("Не могу понять, куда ставить ударение");
}
}
}
System.out.println("Я закончил упражнение.");
for (int i = 0; i < words.length; i++) {
int[] myagkiy = { 1000, 1000 };
// для запоминания гласных
int[] vowel = { 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000 };
int[] vowels = { 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000 };
int r = 0;
int q = 0;

try {
// пауза между словами 500 мс
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// если длина строки = 1, просто прочти файл
if (words[i].length() == 1) {
if ((((int) words[i].charAt(0) > 1039) & ((int) words[i].charAt(0) < 1077)& ((int) words[i].charAt(0) != 1066) & ((int) words[i].charAt(0) != 1068)& ((int) words[i].charAt(0) != 1098) & ((int) words[i].charAt(0) != 1100))| (((int) words[i].charAt(0) > 47) & ((int) words[i].charAt(0) < 60))| (((int) words[i].charAt(0) > 32) & ((int) words[i].charAt(0) < 35))| (((int) words[i].charAt(0) > 38) & ((int) words[i].charAt(0) < 42))| (((int) words[i].charAt(0) > 43) & ((int) words[i].charAt(0) < 47))| (((int) words[i].charAt(0) == 63)) | (((int) words[i].charAt(0) == 95))| (((int) words[i].charAt(0) == 8212))| (((int) words[i].charAt(0) > 1077) & ((int) words[i].charAt(0) < 1102))| ((int) words[i].charAt(0) == 1104) | ((int) words[i].charAt(0) == 1105)) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int) words[i].charAt(0)) + ".au";
playMusic(new File(filePath));
} 
else if (((int) words[i].charAt(0) == 1102) | ((int) words[i].charAt(0) == 1103)| ((int) words[i].charAt(0) == 1105) | ((int) words[i].charAt(0) == 1077)) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int) words[i].charAt(0)) + "ai.au";
playMusic(new File(filePath));
} 
else {			
System.out.println("Я отказываюсь это озвучивать!");
}
}
else {
// преобразуем слово в массив символов
char[] letters = words[i].toCharArray();
// запомним, где находятся мягкие знаки
for (int f = 0; f < letters.length; f++) {
if (letters[f] == 'ь') {
myagkiy[r] = f;
r++;
}
}
// запомним, где находятся смягчающие гласные
for (int f = 0; f < letters.length; f++) {
if ((letters[f] == 'я') | (letters[f] == 'ё') | (letters[f] == 'ю') | (letters[f] == 'и')| (letters[f] == 'е')) {
vowel[q] = f;
q++;
}
}
// запомним, где находятся остальные гласные
for (int f = 0; f < letters.length; f++) {
if ((letters[f] == 'а') | (letters[f] == 'о') | (letters[f] == 'э') | (letters[f] == 'ы')| (letters[f] == 'и')) {
vowels[q] = f;
q++;
}
}
for (int j = 0; j < letters.length; j++) {
if ((((int) words[i].charAt(j) > 47) & ((int) words[i].charAt(j) < 60))| (((int) words[i].charAt(j) > 32) & ((int) words[i].charAt(j) < 35))| (((int) words[i].charAt(j) > 38) & ((int) words[i].charAt(j) < 42))| (((int) words[i].charAt(j) > 43) & ((int) words[i].charAt(j) < 47))| (((int) words[i].charAt(j) == 63)) | (((int) words[i].charAt(j) == 95))| (((int) words[i].charAt(j) == 8212))| (((int) words[i].charAt(j) > 1039) & ((int) words[i].charAt(j) < 1104))| ((int) words[i].charAt(0) == 1105) | ((int) words[i].charAt(j) == 1105)) {
if ((words[i].length() - words[i].replaceAll("а|о|у|ы|э|я|ё|ю|и|е", "").length() == 0)|(words[i].length() - words[i].replaceAll("а|о|у|ы|э|я|ё|ю|и|е", "").length() == 1)) {
// под ударением (т.к. один слог всего)
if ((letters[j] == 'ё') & (gdeudarenie[i] == j)) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int) words[i].charAt(j)) + "ai.au";
playMusic(new File(filePath));
}
else if ((letters[j] == 'а') | (letters[j] == 'о') | (letters[j] == 'у') | (letters[j] == 'ы') | (letters[j] == 'э') | (letters[j] == 'и')) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int) words[i].charAt(j)) + "a.au";
playMusic(new File(filePath));
}
// на первом месте - йотированная и под ударением (т.к. один слог всего)
else if ((j == 0) & ((letters[j] == 'я') | (letters[j] == 'е') | (letters[j] == 'ю'))) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int) words[i].charAt(j)) + "ai.au";playMusic(new File(filePath));
}
// после ъ всегда йотированная гласная
else if (letters[j] == 'ъ') {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int) words[i].charAt(j + 1)) + "ai.au";
playMusic(new File(filePath));
}
// смягчаем согласные
else if ((j < letters.length - 1) & ((j + 1 == myagkiy[0]) | (j + 1 == myagkiy[1]))& ((letters[j] == 'п') | (letters[j] == 'б') | (letters[j] == 'л')| (letters[j] == 'м') | (letters[j] == 'н') | (letters[j] == 'р')| (letters[j] == 'в') | (letters[j] == 'ф') | (letters[j] == 'г')| (letters[j] == 'к') | (letters[j] == 'д') | (letters[j] == 'т')| (letters[j] == 'с') | (letters[j] == 'х') | (letters[j] == 'з'))) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int) words[i].charAt(j)) + "a.au";
playMusic(new File(filePath));
}
// йотированная гласная после ь
else if ((j > 0) & ((j - 1 == myagkiy[0]) | (j - 1 == myagkiy[1])) & ((letters[j] == 'я')| (letters[j] == 'ё') | (letters[j] == 'е') | (letters[j] == 'ю'))) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int) words[i].charAt(j)) + "ai.au";
playMusic(new File(filePath));
}
// смягчение согласных гласным
else if (((j + 1 == vowel[0]) | (j + 1 == vowel[1]) | (j + 1 == vowel[2])| (j + 1 == vowel[3]) | (j + 1 == vowel[4]) | (j + 1 == vowel[5])| (j + 1 == vowel[6]) | (j + 1 == vowel[7]) | (j + 1 == vowel[8])| (j + 1 == vowel[9]))& ((letters[j] == 'б') | (letters[j] == 'в') | (letters[j] == 'г')| (letters[j] == 'д') | (letters[j] == 'з') | (letters[j] == 'к')| (letters[j] == 'л') | (letters[j] == 'м') | (letters[j] == 'н')| (letters[j] == 'п') | (letters[j] == 'р') | (letters[j] == 'с')| (letters[j] == 'т') | (letters[j] == 'ф') | (letters[j] == 'х'))) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int) words[i].charAt(j)) + "a.au";
playMusic(new File(filePath));
}
// йотированная ударная гласная после гласной
else if ((gdeudarenie[i] == j) & (j > 0) & ((j - 1 == vowels[0]) | (j - 1 == vowels[1])| (j - 1 == vowels[2]) | (j - 1 == vowels[3])| (j - 1 == vowels[4] | (j - 1 == vowels[5]) | (j - 1 == vowels[6])| (j - 1 == vowels[7]) | (j - 1 == vowels[8]) | (j - 1 == vowels[9])))& ((letters[j] == 'я') | (letters[j] == 'ё') | (letters[j] == 'е')| (letters[j] == 'ю'))) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int) words[i].charAt(j)) + "ai.au";
playMusic(new File(filePath));
}
// йотированная безударная гласная после гласной
else if ((gdeudarenie[i] != j) & (j > 0) & ((j - 1 == vowels[0]) | (j - 1 == vowels[1])| (j - 1 == vowels[2]) | (j - 1 == vowels[3])| (j - 1 == vowels[4] | (j - 1 == vowels[5]) | (j - 1 == vowels[6])| (j - 1 == vowels[7]) | (j - 1 == vowels[8]) | (j - 1 == vowels[9])))& ((letters[j] == 'я') | (letters[j] == 'ё') | (letters[j] == 'е')| (letters[j] == 'ю'))) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int)words[i].charAt(j)) + "ai.au";
playMusic(new File(filePath));
}
// остальные случаи
else {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int) words[i].charAt(j)) + ".au";
playMusic(new File(filePath));
}
}
// для дву- и более сложных слов.
else {
// ударная йотированная на первом слоге
if ((letters[j] == 'ё') & (j == gdeudarenie[i]) & (j == 0)) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int) words[i].charAt(j)) + "ai.au";
playMusic(new File(filePath));
}
else if ((letters[j] == 'е') & (j == gdeudarenie[i]) & (j == 0)) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int) words[i].charAt(j)) + "ai.au";
playMusic(new File(filePath));
}
else if ((letters[j] == 'ю') & (j == gdeudarenie[i]) & (j == 0)) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int) words[i].charAt(j)) + "ai.au";
playMusic(new File(filePath));
}
else if ((letters[j] == 'я') & (j == gdeudarenie[i]) & (j == 0)) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int)words[i].charAt(j)) + "ai.au";
playMusic(new File(filePath));
}
else if ((letters[j] == 'ё') & (j == gdeudarenie[i]) & (j != 0)) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int) words[i].charAt(j)) + "a.au";
playMusic(new File(filePath));
}
// йотированная гласная после ь
else if ((j > 0) & (j - 1 == myagkiy[0]) & (letters[j] == 'я') & (gdeudarenie[i] != j)) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int)words[i].charAt(j)) + "i.au";
playMusic(new File(filePath));
} else if ((j > 0) & (j - 1 == myagkiy[1]) & (letters[j] == 'я') & (gdeudarenie[i] != j)) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int) words[i].charAt(j)) + "i.au";
playMusic(new File(filePath));
}
else if ((j > 0) & (j - 1 == myagkiy[0]) & (letters[j] == 'е') & (gdeudarenie[i] != j)) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int) words[i].charAt(j)) + "i.au";
playMusic(new File(filePath));
}
else if ((j > 0) & (j - 1 == myagkiy[1]) & (letters[j] == 'е') & (gdeudarenie[i] != j)) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int) words[i].charAt(j)) + "i.au";
playMusic(new File(filePath));
}
else if ((j > 0) & ((j - 1 == myagkiy[0]) | (j - 1 == myagkiy[1])) & (letters[j] == 'ю')& (gdeudarenie[i] != j)) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int) words[i].charAt(j)) + "i.au";
playMusic(new File(filePath));
}
else if ((j > 0) & ((j - 1 == myagkiy[0]) | (j - 1 == myagkiy[1]))& (letters[j] == 'ё')) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int) words[i].charAt(j)) + "ai.au";
playMusic(new File(filePath));
}
else if ((j > 0) & (j - 1 == myagkiy[0]) & (letters[j] == 'я') & (gdeudarenie[i] == j)) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int)words[i].charAt(j)) + "ai.au";
playMusic(new File(filePath));
}
else if ((j > 0) & (j - 1 == myagkiy[1]) & (letters[j] == 'я') & (gdeudarenie[i] == j)) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int)words[i].charAt(j)) + "ai.au";
playMusic(new File(filePath));
}
else if ((j > 0) & (j - 1 == myagkiy[0]) & (letters[j] == 'е') & (gdeudarenie[i] == j)) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int) words[i].charAt(j)) + "ai.au";
playMusic(new File(filePath));
}
else if ((j > 0) & (j - 1 == myagkiy[1]) & (letters[j] == 'е') & (gdeudarenie[i] == j)) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int) words[i].charAt(j)) + "ai.au";
playMusic(new File(filePath));
}
else if ((j > 0) & ((j - 1 == myagkiy[0]) | (j - 1 == myagkiy[1])) & (letters[j] == 'ю')& (gdeudarenie[i] == j)) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int) words[i].charAt(j)) + "ai.au";
playMusic(new File(filePath));
}
// йотированная ударная гласная после гласной
else if ((j > 0)& ((j - 1 == vowels[0]) | (j - 1 == vowels[1]) | (j - 1 == vowels[2])| (j - 1 == vowels[3])| (j - 1 == vowels[4] | (j - 1 == vowels[5]) | (j - 1 == vowels[6])| (j - 1 == vowels[7]) | (j - 1 == vowels[8])| (j - 1 == vowels[9])) & (gdeudarenie[i] == j))& ((letters[j] == 'я') | (letters[j] == 'ё') | (letters[j] == 'е')| (letters[j] == 'ю'))) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int)words[i].charAt(j)) + "ai.au";
playMusic(new File(filePath));
}
// йотированная безударная гласная после гласной
else if ((j > 0) & ((j - 1 == vowels[0]) | (j - 1 == vowels[1]) | (j - 1 == vowels[2])| (j - 1 == vowels[3])| (j - 1 == vowels[4] | (j - 1 == vowels[5]) | (j - 1 == vowels[6])| (j - 1 == vowels[7]) | (j - 1 == vowels[8]) | (j - 1 == vowels[9])))& ((letters[j] == 'я') | (letters[j] == 'ё') | (letters[j] == 'е')| (letters[j] == 'ю'))& (gdeudarenie[i] != j)) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int)words[i].charAt(j)) + "i.au";
playMusic(new File(filePath));
}
// ударение для остальных случаев
else if ((letters[j] != 'ё') & (j == gdeudarenie[i])) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int) words[i].charAt(j)) + "a.au";
playMusic(new File(filePath));
}
// безударная йотированная
else if ((j == 0) & ((letters[j] == 'я') | (letters[j] == 'е') | (letters[j] == 'ю'))& (gdeudarenie[i] != j)) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int) words[i].charAt(j)) + "i.au";
playMusic(new File(filePath));
}
// ъ
else if (letters[j] == 'ъ') {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int) words[i].charAt(j + 1)) + "ai.au";
playMusic(new File(filePath));
}
// смягчаем согласные
else if ((j < letters.length - 1) & ((j + 1 == myagkiy[0]) | (j + 1 == myagkiy[1]))& ((letters[j] == 'п') | (letters[j] == 'б') | (letters[j] == 'л')| (letters[j] == 'м') | (letters[j] == 'н') | (letters[j] == 'р')| (letters[j] == 'в') | (letters[j] == 'ф') | (letters[j] == 'г')| (letters[j] == 'к') | (letters[j] == 'д') | (letters[j] == 'т')| (letters[j] == 'с') | (letters[j] == 'х') | (letters[j] == 'з'))) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int) words[i].charAt(j)) + "a.au";
playMusic(new File(filePath));
}
// смягчение согласных гласным
else if (((j + 1 == vowel[0]) | (j + 1 == vowel[1]) | (j + 1 == vowel[2])| (j + 1 == vowel[3]) | (j + 1 == vowel[4]) | (j + 1 == vowel[5])| (j + 1 == vowel[6]) | (j + 1 == vowel[7]) | (j + 1 == vowel[8])
| (j + 1 == vowel[9]))& ((letters[j] == 'б') | (letters[j] == 'в') | (letters[j] == 'г')| (letters[j] == 'д') | (letters[j] == 'з') | (letters[j] == 'к')| (letters[j] == 'л') | (letters[j] == 'м') | (letters[j] == 'н')
| (letters[j] == 'п') | (letters[j] == 'р') | (letters[j] == 'с')| (letters[j] == 'т') | (letters[j] == 'ф') | (letters[j] == 'х'))) {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int) words[i].charAt(j)) + "a.au";
playMusic(new File(filePath));
}
// остальные случаи
else {
String filePath = "C:\\Users\\Admin\\Desktop\\SpeechGenerator\\"+ String.valueOf((int)words[i].charAt(j)) + ".au";
playMusic(new File(filePath));
}
}
} else {
System.out.println("Я отказываюсь это озвучивать!");
}
}
}
}
}
}


Класс запуска
package opening;


public class Beginning {
	public static void main (String[]args) {
			try {
				Solver a = new Solver();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
}
}


И в итоге получилось следующее:
1. Голос человека
2. Люблю программировать
3. Хабр — русскоязычный веб-сайт

Выводы


Как вы можете услышать, синтезированная речь немного отличима от живой человеческой. Почему? Полагаю, Google и другие синтезаторы речи используют целые слова при генерации фраз. Т.е. у них реально трудоустроен диктор, который начитывает 100-150 тыс. словесных форм в файлы. А потом синтезатор речи просто обращается на сервер к этому файлу.

Можно немного усовершенствовать синтезатор речи, если разбить слова не на отдельные звуки, а хотя бы на слоги. Но в этом случае надо также записать n-ное к-во слогов.

Спасибо, что прочли эту статью до конца! Надеюсь, парни из Google Inc увидят её.

P.S. Про стандарты форматирования в Java я знаю, табуляция была удалена для повышения читаемости текста.

Комментарии (0)