- Последовательность символов произвольной длины (не класс)
- Не null-terminated
- Строки не изменяемы
- Все строковые литералы кэшируются (можно, потому что строки не изменяются)
String hello = "Hello"
String str = new String(...)
int length()
char charAt()
char[] toCharArray()
Прочитали огрооомную строку, и у нас есть ссылка на маленькую подстроку, поэтому большая строка всегда хранится в памяти.
- Оператор
==
дляString
сравнивает ссылки, а не содержимое строки (в общем-то, как и для любых ссылочных типов) equals
принимаетObject
, поэтому в теории можно сравнивать апельсин и крокодила.- Есть регулярные выражения
// Регулярки
String str = "a, b, c, d, e"
String items[] = str.split(", *");
// items[] -> {"a", "b", "c", "d", "e"}
Есть ОДИН перегруженный оператор (это исключение), оператор +
. Это конкатенация строк.
String str = ""
for (...) {
str = str + " ";
}
// Это чудо работает за квадрат, потому что нужно каждый раз копировать строку и выделять память
Альтернатива - StringBuilder
. Это просто список, куда мы складываем строки, а потом в конце просто делаем toString()
StringBuilder buf = new StringBuilder();
buf.append("Hello");
buf.append("World");
buf.append("!");
String result = buf.toString();
Не стоит использовать String concat(String str)
, всегда пользуемся оператором +
. Потому что +
- это просто синтаксический сахар над StringBilder
.
То есть в случае a + b
создаётся StringBuilder
, туда складываются a
и b
, а потом делается toString()
. Это становится разумно, если у нас есть цепочка a + b + c + ...
.
/*modifiers*/ class Example {
/* class contents : fields and contents */
}
class Example {
/* modifiers */ int number;
/* modifiers */ String text = "hello";
}
class Example {
/* modifiers */ Example(int number) { // constructor
...
}
}
Публичные классы - это классы, которые можно использовать из других пакетов
- Всё инициализируется либо явно, либо по умолчанию
- Возможна перегрузка методов
- Если не объявлен ни один конструктор, автоматически создаётся конструктор по умолчанию без параметров
- Декструкторов нет(сборка мусора автоматически)
- Есть метод
void finilize()
- он будет вызван при удалении объекта. Но вызывается он ровно ОДИН раз (иначе можно было бы создать вечный объект). Возможно сборщик мусора не приходит никогда. - Любой код, который написан в
finalize()
- может выполнится, а может и нет, поэтому при компиляции прилетаетwarning
Example e = null;
// e.getNumber() -> NullPointerException
e = new Example(3);
// e.getNumber() -> 3
class Derived extends Example {
/* derived class contents */
Derived() {
this(10); // вызываем другой конструктор.
}
Derived(int number) {
super(number); // Вызов родительского конструктора.
}
}
// Если в родительском класс есть конструктор по умолчанию - он
// будет вызван. Иначе нужно вызывать самому
- Нет множественного наследования
- Аналог в плюсах - все методы в классе чистые виртуальные.
- Класс может реализовывать несколько интерфейсов.
- Если не реализовать в наследнике интерфейса какой-то метод, то класс будет абстрактным.
- В интерфейсах нет конструктора
- Можно заводить статические константы, которые будут иметь модификаторы
public static final
(их нельзя изменить) - Можно создавать
private
методы (начиная с Java 9) - Можно создавать
static
methods вместе с телом метода - Можно задавать имплементацию по умолчанию, добавив моификатор
default
(начиная с Java 8)
interface ExampleInterface {
int getNumber();
}
abstract class Example {...} // нельзя создать экземпляр класса
// Отличия:
// 1. Нет полей (ну точнее это константы)
// 2. Нет конструктора
// 3. Есть множественное наследование
Могут использоваться не только перед методами и полями, но и перед классами.
public
- доступ для всехprotected
- доступ в пределах пакета и дочерних классовprivate
- доступ в пределах класса- Доступ по умолчанию - доступ в пределах пакета (не рекомендуется)
Ещё есть такие модификаторы
final
- Можно писать у полей, методов и классов.
- Для классов - нельзя иметь наследников
- Для методов - нельзя перегрузить в наследниках (+ метод становится non-virtual)
- Для полей - либо константа времени компиляции, либо константа времени выполнения
- Почему стоит делать когда-то
final
: потому что иначе может нарушиться инвариант, который вы считаете, что сохраняется.
public class BlankFinal {
private final int = 0; // константа времени компиляции
public static final int N = 10; // константа времени компиляции
private final int j; // константа времени выполнения
}
class A {
A() {
...
foo(); // foo() обязан быть final
...
}
}
void f() {
int i;
i++; // не компилирутеся, обязательно нужно инициализировать.
}
public class StaticTest {
static int i;
int j, h;
static {
i = 25;
System.out.println("Hello");
} // статическая секция инициализации
{
j = 8;
h = 3;
} // обычная секция инициализации
// Обычная секция вызывается перед конструктором. Нужно, например, если есть общий для всех конструкторов код
// Со статической секцией интереснее - она выпоняется ОДИН раз, примерно в тот момент, когда класс загружается в память.
// Но откладывается на как можно поздний момент.
// В момент загрузки класса в память статическая инициализация на самом деле ещё не произошла.
// Она произойдёт тогда, когда мы попытаемся обратиться к содержимому класса (точнее только если то, к чему мы обращаемся зависит от того, выполнилась статическая инициализация или нет)
}
class X {
static {
System.out.println("Hello world");
System.exit(1);
// Это когда-то работало... без main написали Hello world.
// Но сейчас, к счастью, нет.
}
}
Причины ошибок:
- Ошибки программирования
- Неверное использование
API
- Доступ к внешним ресурсам
- Системные сбои
Всякое:
- Все исключения - полноценный объект
- Все исключения наследуются от класса
Throwable
- От него наследуется
Error
иException
Exception
- то, в чём так или иначе виновата наша программаError
- то, в чём виновата виртуальная машина
- Если исключение не обрабатывается в классе, а вылетает дальше - указываем в
throws
- Исключения - это долго
public static int parseInt(String s, int radix) throws /* exceptions */ {
...
}
try {
n = Integer.parseInt(s);
/*
* Если исключение, то
* Java машина останавливается и бежит вверх по стеку, ищем следующий
* catch, если подошло - супер, иначе - бежим дальше
* если не нашлось - пишем матерное сообщение в консоль
*/
} catch (NumberFormatException e) {
System.out.print("Bad number, try again");
}
try {
...
} catch (IOException | NumberFOrmatException e) { // e имеет тип LCA
...
}
InputStream is = new FileInputStream("a.txt");
try {
readFromInputStream(is);
} finally {
is.close();
}
Стратегии обработки:
- Писать пустой
catch
- плохо - Запись в лог - плохо (потому что это
SideEffect
) - Содержательная обработка, например повтор операции
Документация по try
-with-resources.
try (Input is = new FileInputStream("a.in")) {
readFromInputStream(is);
}
// close будет вызван автоматически
// можно перечислить несколько ресурсов через ;
// На самом деле это просто синтаксический сахар!
В блоке try (...)
создаваемые ресурсы должны имплементировать интерфейс AutoCloseable
или Closeable
(так у них появится метод close()
).
В блоке try (...)
можно создавать несколько ресурсов:
try (ZipFile zf = new ZipFile(zipFileName);
BufferedWriter writer = newBufferedWriter(outputFilePath, charset))
{
...
}
/*
Вызов close() будет происходить в порядке обратном созданию объектов, т.е. в данном примере порядок будет следующий:
1. writer.close();
2. zf.close();
*/
Как рассахаривается try-with-resources
:
try {
InputStream is = new FileInputStream("filename.txt");
Throwable t1 = null;
try {
// process file
} catch (Throwable t2) {
t1 = t2;
throw t1;
} finally {
if (is != null) {
if (t1 != null) {
try {
is.close();
} catch (Throwable closeException) {
t1.addSupressed(closeException);
}
} else {
is.close();
}
}
}
} catch (...) {
}