Меня учили, что все переменные и обьекты нужно инициализировать в onCreate() , ну в смысле глобальные (как правило).
Но уже несколько раз встречаю в коде опытных разработчиков когда например листы инициализируются прям там где обьявляются переменные
private OnVideoSelectedListener mCallback;
private AdapterList mAdapterList = null;
private ArrayList> mTempVideoData = new ArrayList<>();
private ArrayList> mVideoData = new ArrayList<>();
private String mNextPageToken = "";
private String mVideoIds = "";
private String mDuration = "00:00";
private boolean mIsStillLoading = true;
Не сколько это правильно и ничему ли это не мешает? Например быстродействию программы или еще что то?
Ответы
Ответ 1
Где правильно инициализировать обьекты?
Согласно документации, к инициализации объектов есть только одно требование: Пол
и переменные должны быть инициализированны до того как они будут использоваться. Так что ответ будет - в любом месте.
Меня учили, что все переменные и обьекты нужно инициализировать в
onCreate()...
Я так понимаю что вопрос больше про андроид. OnCreate() - это метод который предназначе
для инициализации Activity и он будет вызван как только вы запустите Activity с интентом
Это нормально и общепринято, инициализировать поля в onCreate методе. Я думаю что главна
идея сделать инициализацию Activity в onCreate это сократить время старта приложения. Когда ваше приложение запускается, андроид создает экземпляры всех классов (не уверен на счет всех) и если что-то долгое выполняется в конструкторе, то это замедлит старт приложения. Это также относится к выделению памяти для полей класса. Хотя конечно, это мизерное время.
Но стоит помннить, что если вы попытаетесь использовать Activity как
Activity activity = new Activity();
activity.someMethod()....
тогда, onCreate() метод вызыван не будет.
В общем, касательно вопроса где инициализировать, наиболее распростороненны два способа:
- там где переменная объявлена
- в конструкторе
оба правильные, я предпочитаю констркторы. В любом случае старайтесь следовать одному стилю.
Для полноты ответа отмечу, что вы также можете инициализировать поля в инициализационном блоке
class A {
private String myField;
{
myField = "...";
}
}
Ответ 2
Можно прострелить ногу при наследовании. Псевдокод:
class A {
private List items;
public void onCreate(){
items = new ArrayList<>();
}
public void onStart(){
showList();
}
public void showList(){
((ListView)findViewById(R.id.listview)).setAdapter(new FooAdapter(items));
}
public void setItems(List items){
this.items.clear();
this.items.addAll(items);
}
}
class B extends A {
@Override
public void onCreate(){
showList();
}
}
class C extends A {
@Override
public void onCreate(){
showList();
super.onCreate();
}
}
class D extends A {
@Override
public void onCreate(){
setItems((List)getIntent().getSerializable("list"));
super.onCreate();
}
}
Если инициализацию перенести в месте объявления (или в нестатическом блоке, или
конструкторе), поведение программы станет более предсказуемым и вариантов сломать базовый класс будет меньше.
Ответ 3
Нашел интересную статью, в которой описано пять советов по оптимизации кода.
Совет №1. Всегда, когда возможно, используйте локальные переменные вместо общедоступных полей класса
Ограничивая область видимости переменных, вы не только улучшите читаемость кода
уменьшите число потенциальных ошибок, но и сделаете его лучше подходящим для оптимизации.
В блоке неоптимизированного кода, который показан ниже, значение переменной v вычисляетс
во время исполнения приложения. Это происходит из-за того, что данная переменная доступн
за пределами метода m() и может быть изменена в любом участке кода. Поэтому значение переменной неизвестно на этапе компиляции. Компилятор не знает, изменит ли вызов метода some_global_call() значение этой переменной, или нет, так как переменную v, повторимся, может изменить любой код за пределами метода.
В оптимизированном варианте этого примера v – это локальная переменная. А значит
её значение может быть вычислено на этапе компиляции. Как результат – компилятор может поместить значение переменной в код, который он генерирует, что поможет избежать вычисления значения переменной во время выполнения.
Неоптимизированный код:
class A {
public int v = 0;
public int m(){
v = 42;
some_global_call();
return v*3;
}
}
Оптимизированный код:
class A {
public int m(){
int v = 42;
some_global_call();
return v*3;
}
}
Совет №2. Используйте ключевое слово final для того, чтобы подсказать компилятору то, что значение поля – константа
Ключевое слово final можно использовать для того, чтобы защитить код от случайног
изменения переменных, которые должны быть константами. Однако оно позволяет улучшить производительность, так как подсказывает компилятору, что перед ним именно константа.
Во фрагменте неоптимизированного кода значение v*v*v должно вычисляться во врем
выполнения программы, так как значение v может измениться. В оптимизированном вариант
использование ключевого слова final при объявлении переменной и присвоении ей значения, говорит компилятору о том, что значение переменной меняться не будет. Таким образом, вычисление значения можно произвести на этапе компиляции и в выходной код будет добавлено значение, а не команды для его вычисления во время выполнения программы.
Неоптимизированный код:
class A {
int v = 42;
public int m(){
return v*v*v;
}
}
Оптимизированный код:
class A {
final int v = 42;
public int m(){
return v*v*v;
}
}
Совет №3. Используйте ключевое слово final при объявлении классов и методов
Так как любой метод в Java может оказаться полиморфными, объявление метода или класс
с ключевым словом final указывает компилятору на то, что метод не переопределён ни в одном из подклассов.
В неоптимизированном варианте кода перед вызовом функции m() нужно произвести е
разрешение. В оптимизированном коде, из-за использования при объявлении метода m() ключевог
слова final, компилятор знает, какая именно версия метода будет вызвана. Поэтому он может избежать поиска метода и заменить вызов метода m() его содержимым, встроив его в необходимое место программы. В результате получаем увеличение производительности.
Неоптимизированный код:
class A {
public int m(){
return 42;
}
public int f(){
int sum = 0;
for (int i = 0; i < 1000; i++)
sum += m(); // m must be resolved before making a call
return sum;
}
}
Оптимизированный код:
class A {
public final int m(){
return 42;
}
public int f(){
int sum = 0;
for (int i = 0; i < 1000; i++)
sum += m();
return sum;
}
}
Совет №4. Избегайте вызывать маленькие методы через JNI
Существуют веские причины использования JNI-вызовов. Например, если у вас есть ко
или библиотеки на C/C++, которые вы хотите повторно использовать в Java-приложениях
Возможно, вы создаёте кросс-платформенное приложение, или ваша цель – увеличение производительност
за счет использования низкоуровневых механизмов. Однако, важно свести количество JNI-вызово
к минимуму, так как каждый из них создаёт значительную нагрузку на систему. Когда JNI используют для оптимизации производительности, эта дополнительная нагрузка может свести на нет ожидаемую выгоду. В частности, частые вызовы коротких, не производящих значительной вычислительной работы JNI-методов, способны производительность ухудшить. А если такие вызовы поместить в цикл, то ненужная нагрузка на систему лишь увеличится.
Пример кода:
class A {
public final int factorial(int x){
int f = 1;
for (int i =2; i <= x; i++)
f *= i;
return f;
}
public int compute (){
int sum = 0;
for (int i = 0; i < 1000; i++)
sum += factorial(i % 5);
// if we used the JNI version of factorial() here
// it would be noticeably slower, because it is in a loop
// and the loop amplifies the overhead of the JNI call
return sum;
}
}
Совет №5. Используйте стандартные библиотеки вместо реализации той же функциональности в собственном коде
Стандартные библиотеки Java серьёзно оптимизированы. Если использовать везде, гд
это возможно, внутренние механизмы Java, это позволит достичь наилучшей производительности
Стандартные решения могут работать значительно быстрее, чем «самописные» реализации. Попытка избежать дополнительной нагрузки на систему за счёт отказа от вызова стандартной библиотечной функции может, на самом деле, ухудшить производительность.
В неоптимизированном варианте кода показана попытка избежать вызова стандартной функци
Math.abs() за счёт собственной реализации алгоритма получения абсолютного значения числа. Однако, код, в котором вызывается библиотечная функция, работает быстрее за счёт того, что вызов заменяется оптимизированной внутренней реализацией в ART во время компиляции.
Неоптимизированный код:
class A {
public static final int abs(int a){
int b;
if (a < 0)
b = a;
else
b = -a;
return b;
}
}
Оптимизированный код:
class A {
public static final int abs (int a){
return Math.abs(a);
}
}
P.S. Надеюсь хоть чем-то да помог.
Ответ 4
Ваш вопрос довольно абстрактный. По существу могу отметить что критически это н
на что не влияет, коллеции инициализируют сразу чтобы потом не забыть, так как даж
методы неинициализированой коллекции компилятор пропустит, а потом будут вылетать NPE
Фактически инициализация этих переменных произойдет до начала работы метода onCreate
и увеличит время между вызовом активити(например из другого активити) и началом работы метода onCreate. Но эта разница ничтожно мала. Чтобы ощутить разницу, вам нужно инициализировать сотни тысяч глобальных переменных. Так что если не придираться к словам, это ничему не мешает. В он onCreate же делается инициализация потому что там вы уже можете получить доступ к различным данным(например которые были переданы через Extra и т.д...)
Комментариев нет:
Отправить комментарий