Страницы

Поиск по вопросам

воскресенье, 12 мая 2019 г.

Когда может понадобиться самому загружать классы в java?

Помогите пожалуйста разобраться с динамической загрузкой классов в java. Как я понимаю, при старте программы загружаются классы из rt.jar, потом загружается главный класс, а все остальные пользовательские классы загружаются по мере необходимости (например класс A загружается только когда в программе создается объект класса A). Первый мой вопрос заключается в следующем: что собственно значит "JVM загружает класс"? Имеется в виду что в этот момент происходит компиляция класса? Так же мне интересно, зачем JVM загружает класс, когда используется его статический метод? Я провел небольшой эксперимент: написал класс, в котором есть один статический метод и несколько нестатических.
public class StExmp { static{ c = 5; } private int a; public int getA() { return a; }
public void setA(int a) { this.a = a; }
public int getB() { return b; }
public void setB(int b) { this.b = b; }
private int b; private static int c;
public StExmp(int a, int b){ this.a = a; this.b = b; c = 5; }
public static void show(){ System.out.println(c); } }
Вызываю статический метод в главном классе StExmp.show();, потом запускаю программу с флагом -verbose:class и вижу что когда программа дошла до вызова этого метода, она загрузила весь класс. А почему нельзя загружать только статические члены класса? Ведь получилось что из-за вызова одного маленького метода пришлось загрузить весь класс, хотя он больше никак и не используется. И главный вопрос: Когда может потребоваться самому загружать классы? Например с помощью ClassLoader.loadClass()? Вот этого я совсем не могу понять. Ведь если мы загружаем какой то класс, значит мы собираемся его как то использовать? Почему тогда нельзя просто использовать его в программе (например, создать объект) JVM же сама его загрузит. А когда это может потребоваться делать методом ClassLoader.loadClass()?


Ответ

Ну и топик. Про загрузку классов можно много чего написать. Тема интересная, правда, уже 100 лет теорией не занимался. В контексте треда, пожалуй, отвечу на вопросы конкретные. Если в чём-то не прав, поправляйте, критика приветствуется.
Первый мой вопрос заключается в следующем: что собственно значит "JVM загружает класс"?
У вас есть скомпилированные байт коды классов, ClassLoader их грузить по мере необходимости.
Зачем нам держать в памяти класс, если он не используется? Класс будет загружен только в момент использования. Точно так же, если на класс не осталось никаких ссылок, то ClassLoader может выгрузить класс из памяти при проходе GC.
Имеется в виду что в этот момент происходит компиляция класса?
Ваши классы уже скомпилированы javac. ClassLoader в память его грузит. Это если в 2-х словах, на самом деле там происходит верификация байт-кода и т.п.
Так же мне интересно, зачем JVM загружает класс, когда используется его статический метод?
Статически метод, константы - это метаданные класса. Не представляю, как их можно загрузить в отрыве без класс.
А почему нельзя загружать только статические члены класса? Ведь получилось что из-за вызова одного маленького метода пришлось загрузить весь класс, хотя он больше никак и не используется.
Предположим, есть у вас:
public class StExmp { public static void show(){ System.out.println(c); } }
Ок, давайте методом рассуждения выведем необходимость грузить класс. Вы хотите, чтобы метод show был загружен без класса. Но ведь в коде вы потом вызываете метод как StExmp.show()? Если класс не загружен, как вы себе представляете вызов метода? Ну хорошо, предположим загрузчик метод добавить в какую-то общую таблицу виртуальных методов. А потом вы создадите класс:
public class StExmp2 { public static void show(){ System.out.println(c); } }
Метод show загрузчик так же добавит его в общую таблицу статических методов? Проблему уже видите? Как потом при вызове StExmp2.show() понять какой из этих методов вызвать?
Когда может потребоваться самому загружать классы? Например с помощью ClassLoader.loadClass()? Вот этого я совсем не могу понять. Ведь если мы загружаем какой то класс, значит мы собираемся его как то использовать? Почему тогда нельзя просто использовать его в программе (например, создать объект) JVM же сама его загрузит. А когда это может потребоваться делать методом ClassLoader.loadClass()?
Например, у вас high-load проект. Приложение должно работать непрерывно. Но вам понадобилось заменить реализацию какого-то метода. Не перезапускать же всё приложение? Если оно стетйтлесс, то ещё ладно, но если там в памяти много данных/кэш и т.п.?
Можно заменить налету. Когда это надо? Ну, скажем, вы хотите поправить какой-то критический баг, оптимизировали метод и т.п.
Тут можно много чего придумать. К примеру у вас игра, в которой есть возможность добавлять кастомных npc. Вы просто пишите новые класс, который в рантайме подтягивается. Или просто неизвестно какой класс будет использоваться в итоге, решение принимаете в рантайме и грузите необходимый класс.
Бывает случаи, когда классы хранятся в базе (да-да, бывает такое). Их иначе и не загрузить вовсе.

Как покрыть тестами конструктор класса в java?

Есть код, который по алгоритму Эвклида находит наибольший общий делитель.
import java.util.Scanner;
/** * Created by user on 24.11.2015. * По данным двум числам 1 public static int Euclid(int a, int b) { //конструктор if (a == 0 || b == 0) { //Рассматривается случай, когда одно из if (a == 0) { //делимых равно нулю return b; } else { return a; } } else { if (a > b) { return Euclid(a % b, b); //рекурсивно вызовется } // алгоритм, если будет остаток от деления большего if (b > a) { // числа на меньшее и наоборот return Euclid(a, b % a); } else return Euclid(a % b, b);
} } public static void main(String[] args) {
Scanner sc1 = new Scanner(System.in); //ввод с клавиатуры Scanner sc2 = new Scanner(System.in); int a = sc1.nextInt(); int b = sc2.nextInt(); System.out.println(Euclid(a,b)); } }
Несмотря на то, что программа работает, я решил по практиковаться на ней в разработке через тестирование.
Пишу тест:
import org.junit.Test;
import static org.junit.Assert.*; public class EuclidTest {
@Test public void testEuclid() throws Exception {
int result = new Euclid(234, 45); //в этой строке ошибка компиляции assertEquals(9, result, 1e-9); } }
В строке, где я объявляю result мне показывает ошибку компиляции.
Когда я переписал класс, заменив конструктор методом с другим названием всё прошло как по маслу. Связи с этим вопросы:
Можно ли покрыть конструктор тестами так, чтобы не вызвать ошибку компиляции? Как это сделать?


Ответ

public static int Euclid(int a, int b) { //конструктор
Дело в том, что это у вас не конструктор, а статический метод, возвращающий int. Конструктор был бы такой (он возвращает объект класса Euclid):
public Euclid(int a, int b) { //конструктор
Вообще, конструктор здесь не нужен никоим образом. Вам не нужно хранить какое-то состояние, так что и объекты создавать незачем.
Переименуйте ваш метод euclid с маленькой буквы, как положено по стандарту именования и пишите вот такой тест:
import org.junit.Test;
import static org.junit.Assert.*; public class EuclidTest {
@Test public void testEuclid() throws Exception { assertEquals(9, euclid(234, 45), 1e-9); } }

Создание ссылок TextView на Android

помогите с созданием ссылок TextView пожалуйста, перепробовал много вариантов никакой не помогает, выходит ошибка java.lang.NullPointerException
пробовал эти варианты:
1) добавить слушателя
texttest.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View view) { Uri adress= Uri.parse("www.google.ru"); Intent browser= new Intent(Intent.ACTION_VIEW, adress); startActivity(browser); }
});
2)
TextView clickableTextLink = (TextView)findViewById(R.id.textView_About); clickableTextLink.setMovementMethod(LinkMovementMethod.getInstance());
3) Этот способ работает, но не получается создать ссылку вида
Google


Ответ

Сложный способ позволяющий не только сделать ссылки кликабельными, но и отловить нажатие на них:
Берём обычный TextView
Создаём класс для обработки нажатий на ссылки в тексте:
public class MakeLinksClicable { private final static String LOG = MakeLinksClicable.class.getSimpleName();
public static class CustomerTextClick extends ClickableSpan { String mUrl;
public CustomerTextClick(String url) { mUrl = url; }
@Override public void onClick(View widget) { //Тут можно как-то обработать нажатие на ссылку //Сейчас же мы просто открываем браузер с ней Log.i(LOG, "url clicked: " + this.mUrl);
Intent i = new Intent(Intent.ACTION_VIEW); i.setData(Uri.parse(mUrl)); widget.getContext().startActivity(i); } }
public static SpannableStringBuilder reformatText(CharSequence text) { int end = text.length(); Spannable sp = (Spannable) text; URLSpan[] urls = sp.getSpans(0, end, URLSpan.class); SpannableStringBuilder style = new SpannableStringBuilder(text); for (URLSpan url : urls) { style.removeSpan(url); MakeLinksClicable.CustomerTextClick click = new MakeLinksClicable.CustomerTextClick(url.getURL()); style.setSpan(click, sp.getSpanStart(url), sp.getSpanEnd(url), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); }
return style; } } Присваиваиваем TextView текст, указав ему, что он в HTML формате и кликабельный и ловим клики на ссылки с помощью класса из предыдущего пункта:
TextView textView = ... //находим TextView //Экранируем кавычки в атрибуте html тега слэшем: String textWithLink = "Google"; //Указываем с помощью Html.fromHtml, что у нас не просто текст: textView.setText(Html.fromHtml(textWithLink, null, null)); ////Указываем что разрешаем ссылки кликать: textView.setLinksClickable(true); textView.setMovementMethod(LinkMovementMethod.getInstance()); //Научаемся отлавливать клики пропустив текст через наш класс из пред. пункта. CharSequence text = textView.getText(); if (text instanceof Spannable) { textView.setText(MakeLinksClicable.reformatText(text)); }
Итого:
Ссылка в TextView окрасится дефолтным цветом выделения ссылок, а при нажатии на неё в логи выведется адрес и запустится браузер с этой ссылкой.
UPD_0:
Чтобы дополнить вопрос - нажмите "править" - см. скриншот.

Если заявленная в вопросе проблема решена - вопрос надо отметить верным, нажав на "галочку" слева от тела вопроса. Если в процессе решения родился новый вопрос - задавайте новый вопрос отдельным постом на сайте. Смысл сайта: один вопрос - один ответ.

Объявление двумерного массива java

Друзья, помогите, пожалуйста, с таким вопросом. Встретил такой кусок кода в Java
public String saveComputingResult(int firstNumber, int secondNumber, String operation) { String[][] arr = new String[2][0]; //** arr[0] = new String[1]; //** arr[1] = new String[1]; //** arr[0][0] = getOperation(operation); arr[1][0] = Integer.toString(getComputingResult(firstNumber, secondNumber, operation)); return arr[0][0] + " - " + arr[1][0];
Кто может пояснить выделенные комментарием //** строки? Объявляется двумерный массив типа String, имеет две строки и....0!!! столбцов. Это как? Что это означает? В Шилдте и Эккеле таких примеров нету, есть только примеры где размер массива указывается для первого массива, так как это обязательно, а второй [] просто остается пустым, но что означает если там 0 внутри? И что означают эти строки при этом??
arr[0] = new String[1]; arr[1] = new String[1];


Ответ

Объявляется массив состоящий из двух массивов нулевой длины. То есть каждый элемент массива имеет указатель на физический адрес, где хранится массив нулевой длины.
При объявлении:
String[][] arr = new String[2][];
каждому элементу массива присвоено значение null вместо указателя на адрес, то есть:
arr[0]=null; arr[1]=null;
В вашем же случае:
String[][] arr = new String[2][0];
тоже самое что:
String[][] arr = new String[2][]; arr[0] = new String[0]; arr[1] = new String[0];
Массивы нулевой длины объявлены и хранятся в памяти, а arr[0] и arr[1] присвоены указатели на адреса в памяти.

Как загрузить данные из БД, показывая процесс ProgressDialog'ом

Мне нужно загрузить данные из базы данных в ArrayList, вo втором потоке, сопровождая выгрузку DialogFragment'ом. При помощи чего это можно сделать?

Хотел сделать через IntentServise, но он не дружит с List'ами (насколько я понял). Проповал запустить выгрузку во втором потоке, остановивши первый, но ProgressDialog спит с основным потоком. Даже пробовал сделать все в основном потоке, но ProgressDialog не успевает запуститься и зависает с основным потоком...


Ответ

Делается это через AsyncTask, примерная схема такая:
Создаем класс наследующий от AsyncTask В конструкторе класса создаем ProgressDialog В методе onPreExecute() показываем диалог progressDialog.show(), проводим другие мероприятия - подготовку у чтению БД и проч. В методе doInBackground() организуем чтение данных из БД в цикле и в ходе чтения вызываем this.publishProgress(), который будет вызывать onProgressUpdate() (см. ниже) В методе onProgressUpdate() увеличиваем счетчик progressDialog.setProgress() В методе onPostExecute() закрываем счетчик progressDialog.dismiss()

Когда выбирать реляционную БД, а когда не реляционную?

Пишу сайт. Подразумевается большое количество записей разного размера. Подумал о том, что при большом объёме записей БД будет долго обрабатывать запрос, поэтому надо её раскинуть на несколько серверов. но вычитал, что реляционные БД плохо масштабируются и для больших объемов данных используют не реляционные.

подВопрос: Как поступить: пока не заморачиваться над этим и потом, при необходимости, перенести данные в не реляционную БД ИЛИ выбрать реляционную/не реляционную БД ?


Ответ

Подумал о том, что при большом объёме записей БД будет долго обрабатывать запрос
Планирование не может начинаться с "подумал", боттлнеки всегда бывают в иных местах. Реляционки спокойно работают с миллионами записей.
поэтому надо её раскинуть на несколько серверов. но вычитал, что реляционные БД плохо масштабируются
Тут у меня опять претензия в ту же степь. Вы не знаете, что именно вы вычитали. Они действительно плохо масштабируются из-за того, что (по крайней мере у большинства) есть только одна модель master-slaves, которая упирается в мастер по скорости записи (поправьте, если есть популярные движки с многочисленными мастерами). В любом случае в базе данных не должно быть тяжелых подсчетов - если вы считаете количество записей в БД, то это должно рассчитываться внутри БД и кэшироваться на уровне приложения, если вы рассчитываете аналитику, то тут БД уже ничего считать не должна.
В общем, у меня большие сомнения, что вам нужна масштабируемая система. Что до нереляционок, то их нельзя выбрать просто потому что "лучше масштабируются" - их только основных типов четыре штуки под свои задачи (вряд ли вы будете использовать key-value или графовую БД). Что до масштабирования, то есть row-column (т.е. хранение данных практически как в SQL) Cassandra, которая шардит данные по узлам и имеет практически линейную масштабируемость, т.е. добавление сервера в кластер из N серверов обеспечивает практически 100 * (1 / N + 1) процентов прироста производительности. Если есть знание кассандры, то делать проект на чем-то другом я не вижу смысла (единственная претензия - отсутствие готовых утилит для миграций, но это, надеюсь, изменится), классические реляционки как концепция по факту уже умерли - они, конечно, останутся, но в узком применении, и ближайшие N лет их популярность будет снижаться.

правильная сортировка массива по значению

есть массив
[0] => Array ( [title] => ITEM1 [id] => 1 [voites] => 0 )
[1] => Array ( [title] => ITEM2 [id] => 2 [voites] => 0 )
[2] => Array ( [title] => ITEM3 [id] => 3 [voites] => 4 // высший рейтинг ) [3] => Array ( [title] => ITEM4 [id] => 4 [voites] => 0 )
нужно отсортировать по полю voites... и нужно, чтобы не нарушался порядок.. например, обычная отсортировка выводит сначала элемент с наибольшим значение в voites(что и логично), но остальные, у которых это поле равно 0, выводит с конца... сортирую через usort()... не получается написать к нему cmp функцию
public function srtCMP($a, $b){ return $b["voites"] - $a["voites"]; }
повторюсь: нужно, чтобы не нарушался порядок... элементы у которых высший рейтинг должны подниматься вверх, а остальные оставить как есть
вот так:
[0] => Array ( [title] => ITEM3 [id] => 3 [voites] => 4 // высший рейтинг ) [1] => Array ( [title] => ITEM1 [id] => 1 [voites] => 0 )
[2] => Array ( [title] => ITEM2 [id] => 2 [voites] => 0 ) [3] => Array ( [title] => ITEM4 [id] => 4 [voites] => 0 )


Ответ

По сути, имеется две сортировки: по убыванию voites и (при их равенстве) - по возрастанию ключа элемента. Для применения функции usort() надо временно запомнить этот ключ внутри элемента. Например, так:
$common = Array( "0" => Array ( "title" => ITEM1, "id" => 1, "voites" => 0 ),
"1" => Array ( "title" => ITEM2, "id" => 2, "voites" => 0 ),
"2" => Array ( "title" => ITEM3, "id" => 3, "voites" => 4 // высший рейтинг ), "3" => Array ( "title" => ITEM4, "id" => 4, "voites" => 0 ) );
function cmp($a, $b) { if ($a["voites"] == $b["voites"]) { if (end($a) == end($b)) { return 0; } return (end($a) < end($b)) ? -1 : 1; } return ($a > $b) ? -1 : 1; }
function add_key(&$item, $key){ array_push($item,$key); }
function delete_key(&$item, $key){ array_pop($item); }
array_walk($common, 'add_key'); usort($common, 'cmp'); array_walk($common, 'delete_key'); var_dump($common);
Результат:
array (size=4) 0 => array (size=3) 'title' => string 'ITEM3' (length=5) 'id' => int 3 'voites' => int 4 1 => array (size=3) 'title' => string 'ITEM1' (length=5) 'id' => int 1 'voites' => int 0 2 => array (size=3) 'title' => string 'ITEM2' (length=5) 'id' => int 2 'voites' => int 0 3 => array (size=3) 'title' => string 'ITEM4' (length=5) 'id' => int 4 'voites' => int 0