После прочтения Скрытые возможности C# я задался вопросом: какие скрытые возможности Java?
Вы (/ кто-то), вероятно, должны аккуратно суммировать ответы в теле вопроса, как вопрос C#.




Ключевое слово утверждать на уровне языка.
Проблема с assert в том, что его нужно включить во время выполнения.
Но если он отключен, его как бы там нет. Вы можете добавить в свой код столько утверждений, сколько хотите, и у вас не будет никакого снижения производительности, если они отключены.
Я считаю, что это хороший вещь об assert: его можно отключить без штрафных санкций.
проблема в том, что если вы случайно реализовали побочный эффект в утверждении, вам нужно включить утверждение, чтобы программа ur работала ...
Чтобы определить, включены ли утверждения, вы можете использовать {boolean assertsOn = false; assert assertsOn = true; if (assertsOn) {/ * сложная проверка * /}}. Это также позволяет регистрировать или генерировать исключение, если состояние assert не является ожидаемым.
Мне очень нравится переписанный Threading API из Java 1.6. Звонки отличные. По сути, это потоки с возвращаемым значением.
По сравнению со старыми вещами все просто
В основном есть ExecutorServices, которым вы можете отправлять Callables. Когда вы отправляете Callable в ExecutorService, вы получаете обратно Future, в котором есть блокирующий вызов для получения результата Callable (или вы можете спросить его, есть ли результат, неблокирующий).
ExecutorService - это способ запуска Callables (и Runnables). Это может быть один фоновый поток, он может поддерживаться пулом потоков, он может даже запускать задачи последовательно в текущем потоке. Зависит от реализации.
Также существует CompletionService, которому могут быть отправлены Callables, и результаты помещаются в очередь для использования по мере их завершения.
статический импорт для «улучшения» языка, чтобы вы могли делать приятные буквальные вещи безопасными для типов способами:
List<String> ls = List("a", "b", "c");
(также можно делать с картами, массивами, наборами).
http://gleichmann.wordpress.com/2008/01/13/building-your-own-literals-in-java-lists-and-arrays/
Продолжая:
List<Map<String, String>> data = List(Map( o("name", "michael"), o("sex", "male")));
Очень жаль, что такой функциональности нет непосредственно в API коллекций.
это не часть языка; автор в ссылке определяет метод «Список» для создания списка
Это метод вроде: ... public static
Разве это не шаблон проектирования Factory? Конечно, очень полезно, особенно с varargs.
статический импорт java.util.Arrays; List
@opsb Также необходимо учитывать, что asList возвращает список фиксированного размера, поддерживаемый массивом.
Я думаю, что еще одна «недооцененная» особенность java - это сама JVM. Вероятно, это лучшая доступная виртуальная машина. И он поддерживает множество интересных и полезных языков (Jython, JRuby, Scala, Groovy). Все эти языки могут легко и без проблем взаимодействовать друг с другом.
Если вы разрабатываете новый язык (как в случае со scala), у вас сразу становятся доступными все существующие библиотеки, и поэтому ваш язык «полезен» с самого начала.
Все эти языки используют оптимизацию HotSpot. ВМ очень хорошо отслеживается и поддается отладке.
Нет. На самом деле это не очень хорошая виртуальная машина. Он был разработан исключительно для работы с JAVA. Бестиповые динамические и функциональные языки с ним не работают. Если вы хотите использовать виртуальную машину, вам следует использовать .NET / Mono. Это было разработано для работы с КАЖДЫМ языком ...
На самом деле JVM предназначена исключительно для запуска байт-кодов Java. Вы можете скомпилировать большинство современных языков в байт-код Java. Почти единственное, чего не хватает в байт-коде Java, - это поддержка динамического языка, указатели и хвостовая рекурсия.
@ Hades32: на самом деле виртуальная машина .NET очень похожа на JVM. Он получил поддержку динамических языков относительно недавно (с DLR), и Java 7 тоже скоро получит эту поддержку. И классический "КАЖДЫЙ язык" .NET (C#, Visual Basic.NET, ...) - все они имеют практически одинаковый набор функций.
JVM не поддерживает универсальные шаблоны, в то время как виртуальная машина .NET поддерживает. JVM далеко не лучший.
Я бы сказал, что BEAM (для Erlang) - «лучшая» виртуальная машина. По крайней мере, для (большого) подмножества лучших виртуальных машин.
Хорошо, может быть, не самый лучший для большинства определений слова «лучший». Тем не менее я считаю это особенностью java. У него приличный JIT (Hotspot), есть много реализаций, он работает на самых разных системах ...
Чтобы не сбрасывать со счетов жалобы на конкретные языковые функции, которые напрямую не поддерживаются JVM ... но я склонен думать, что стабильность, кроссплатформенная согласованность и хорошая производительность намного важнее причин, по которым JVM получает дополнительные баллы. Я работаю с серверной Java уже много лет на многих платформах (включая AS / 400) и смог почти полностью забыть об этом - ошибки почти всегда присутствуют в коде, который я могу исправить, и это просто не вылетает.
Инициализация двойной скобки застал меня врасплох несколько месяцев назад, когда я впервые обнаружил его, никогда раньше о нем не слышал.
Тема обычно не так широко известны как способ хранения состояния каждого потока.
Поскольку JDK 1.5 Java имеет чрезвычайно хорошо реализованные и надежные инструменты параллелизма, помимо блокировок, они живут в java.util.concurrent, и особенно интересным примером является подпакет java.util.concurrent.atomic, который содержит потокобезопасные примитивы, которые реализуют операцию сравнивать и менять местами и могут отображаться на реальное собственное оборудование, поддерживаемое версии этих операций.
Жаль, что моя IDE (NetBeans) ужасно их форматирует при автоматическом форматировании.
Инициализация двойных скобок ... странно ... Я бы не стал слишком широко использовать эту конкретную идиому, поскольку она фактически создает анонимный подкласс объекта, что может вызвать запутанные проблемы с равенством / хэш-кодом. java.util.concurrent - действительно отличный пакет.
У меня есть учил Java, и я впервые сталкиваюсь с этим синтаксисом ... который показывает, что вы никогда не перестанете учиться = 8-)
Обратите внимание, что компилятор генерирует AnonymousInnerClass для реализации инициализации двойной скобки, поэтому он работает только для не финальных классов. Это досадно, поскольку буквальные инициализации часто используются как константы.
Обратите внимание, что если вы сохраняете ссылку на коллекцию, инициализированную с помощью этой идиомы «двойной скобки» (или если мы называем ее настоящим именем - анонимный класс с блоком инициализатора), вы неявно сохраняете ссылку на внешний объект, который может вызвать неприятную память утечки. Я бы рекомендовал вообще избегать этого.
«Инициализация с двойными скобками» - очень эвфемистическое название для создания анонимного внутреннего класса, приукрашивающего то, что на самом деле происходит, и заставляющего звучать так, как будто внутренние классы предназначены для использования таким образом. Я бы предпочел, чтобы этот шаблон оставался скрытым.
Должен признать, что впервые вижу двойные фигурные скобки в Java. Отличный совет !!!
Чтобы убедиться, что я понимаю, двойная скобка создает анонимный внутренний класс, а затем создает внутри него статический блок, который затем позволяет выполнять методы из статического контекста. Правильный?
На самом деле это не статический блок, а «блок инициализатора», который отличается, поскольку он запускается в другое время (см. Ссылку, которую я поместил в ответ, для получения более подробной информации)
Это, вероятно, нормально, если использовать его время от времени, но если вы поощряете всех делать это, ваш проект будет завален множеством ложных анонимных классов, прежде чем вы это узнаете. Например, обычно предпочтительнее использовать Arrays.asList () для инициализации хеш-набора.
Это также известно как «изобретение сумасшедшего боба». Хотя я не могу найти происхождение этого имени, Google находит несколько вариантов использования.
Хотя двойная скоба интересна - я бы сказал, что она того не стоит. По крайней мере, вы смешиваете данные с кодом - всегда плохая идея. Чтобы уменьшить размер, их пример будет иметь еще меньшую версию: String initial = new String [] {"XZ13s", "AB21 / X", "YYLEX", "AR2D"}; тогда просто используйте initial в цикле «добавляет». Это имеет дополнительное преимущество, заключающееся в том, что ваши данные извлечены на 95%, и их будет тривиально полностью исключить из вашего кода. Создание строковых массивов - это очень легкий синтаксис, который отлично подходит для того, чтобы ваш код и данные не стали слишком близкими.
Не нравится, мне немного ужасно создавать анонимный подкласс без всякой причины :)
«Инициализация двойной скобки» - это на самом деле три отдельных понятия. Они не связаны между собой, за исключением того, что имеют похожий синтаксис, поэтому я не уверен, почему мы изобрели термин, охватывающий все три. Разработчики должны понимать, что это конструкции кода, которые могут использоваться по-разному. Это: объявление анонимного класса: new Object () {}, блок инициализатора: class MyObject {int x; {х = 0; / * блок инициализатора не обязательно должен быть статическим * /}} и обозначение литерала массива: new Object [] {"One", "Two", "Three"}. Я бы не назвал ничего из этого антипаттерном, хотя использование анонимных классов должно быть ограничено.
Я часто использую его для Ожидания JMock. Я не видел в этом другой необходимости.
Я обнаружил, что инициализация двойных скобок очень полезна при создании некоторых сложных объектов JAXB. Например, у меня была задача создать XML-код SID (общая информация / модель данных) с помощью JAXB, и это стало в 10 раз более интуитивно понятным и простым с использованием инициализации с двойными скобками. Вот пример: pastebin.com/29wiyGhj
На самом деле это не особенность, но меня заставляет посмеяться, что goto - это зарезервированное слово, которое ничего не делает, кроме как побуждает javac ткнуть вас в глаз. Напомню, что вы сейчас находитесь в OO-land.
Есть ли какая-то связь между объектно-ориентированным программированием и отказом от "goto"?
Как новичок, я очень ценю программное обеспечение для мониторинга JConsole в Java 6, оно уже решило для меня несколько проблем, и я продолжаю находить для него новые применения.
По-видимому, JConsole уже была в Java 5, но я считаю, что сейчас она улучшена и, по крайней мере, на данный момент работает намного стабильнее.
JConsole в Java 5: JConsole в Java 5
JConsole в Java 6: JConsole в Java 6
И пока вы это делаете, внимательно посмотрите на другие инструменты из этой серии: Инструменты устранения неполадок Java 6
JConsole будет заменена на VisualVM в будущих версиях (возможно, 6u10?)
Конечно, JConsole заменят, вернее доработают, думаю уже с 6u7. Но многие по-прежнему используют старые версии suns JVM и, следовательно, нуждаются в JConsole. Я до сих пор не нашел ничего, подтверждающего теорию о том, что JVisualVM будет поддерживать более старые версии JDK.
Это не совсем скрыто, но отражение невероятно полезно и мощно. Замечательно использовать простой Class.forName ("..."). NewInstance (), где тип класса настраивается. Такую фабричную реализацию легко написать.
Я постоянно использую отражение, чтобы делать такие вещи, как
Это мощно, потому что позволяет нарушить все статические гарантии и инвариантные Java, javac и интерфейсы, которые вам дают. Используйте с особой осторожностью.
Как насчет ковариантные возвращаемые типы, которые используются с JDK 1.5? Это довольно плохо освещается, так как это несексуальное дополнение, но, как я понимаю, абсолютно необходимо для работы дженериков.
По сути, компилятор теперь позволяет подклассу сузить тип возвращаемого значения переопределенного метода до подкласса возвращаемого типа исходного метода. Итак, это разрешено:
class Souper {
Collection<String> values() {
...
}
}
class ThreadSafeSortedSub extends Souper {
@Override
ConcurrentSkipListSet<String> values() {
...
}
}
Вы можете вызвать метод подкласса values и получить отсортированный потокобезопасный Set из Strings без необходимости опускать для ConcurrentSkipListSet.
Можете ли вы привести пример использования?
Я часто этим пользуюсь. clone () - отличный пример. Он должен возвращать Object, что означает, что вам нужно будет сказать, например, (Список) list.clone (). Однако, если вы объявляете как List clone () {...}, тогда приведение не требуется.
Функторы - это круто. Они довольно близки к указателю на функцию, что, как обычно говорят, невозможно в Java.
Не невозможно, просто невозможно многословно.
Разве это не особенность упс, чем специфическая для java ??
Для большинства людей, которых я беру интервью у Java-разработчиков, позиции, помеченные блоками, очень удивительны. Вот пример:
// code goes here
getmeout:{
for (int i = 0; i < N; ++i) {
for (int j = i; j < N; ++j) {
for (int k = j; k < N; ++k) {
//do something here
break getmeout;
}
}
}
}
Кто сказал, что goto в java - это просто ключевое слово? :)
Я почти надеюсь, что люди не знают об этой функции. Намного предпочтительнее, чтобы они организовали свой код в более мелкие, более значимые методы и в большинстве случаев просто использовали return. В настоящий момент существует большой фурор вокруг добавления этой языковой конструкции в PHP6.
Что ж, я бы предпочел иметь возможность сделать это несколькими способами. Например, я видел людей, использующих System.exit (1); в коде сервлета и EJB, но это не означает, что мы должны удалить System.exit (); из JDK, верно?
Я согласен с тем, что это должно быть разрешено в языке, даже если оно не используется часто. Я не могу припомнить ни одного случая, когда я его использовал, но я видел обе стороны аргумента в пользу его использования. Лучше иметь его и не использовать, чем нуждаться в нем и не иметь.
Я нашел один случай, когда это было полезно, сделайте странный поток. В конце концов, я просто переделал все это, чтобы оно не понадобилось, и это было проще. Я решил придерживаться правила «психопат, который знает, где ты живешь».
Это должен быть блок? У меня создалось впечатление, что он просто помечает цикл for.
В некоторых случаях в конструкции вложенного цикла может быть полезно перейти к следующей итерации внешнего цикла. Это было бы разумным использованием этой функции.
в случае нескольких вложенных циклов, да. Но я бы лучше переписал код.
Я использовал это в stackoverflow.com/questions/288200/… Первый раз, когда я использовал это, казалось естественным ... :-)
Что особенно неизвестно многим программистам (и, вероятно, тоже), так это то, что вы действительно можете пометить ЛЮБОЙ старый блок и выйти из него. Это не обязательно должен быть цикл - вы можете просто определить произвольный блок, присвоить ему метку и использовать с ним break.
Что за сюрприз, я тоже понятия не имел. Я люблю спагетти, но не в коде, я не уверен, что буду использовать опасные знания в будущем
Хммм, вперед. Давно не виделись. Вдохновение наполняет ... Затмение, я иду!
Это вообще не goto, он может только вернуться к предыдущей итерации (то есть: вы не можете перейти вперед). Это тот же механизм, который возникает, когда итерация возвращает false. Сказать, что java имеет goto, все равно что сказать, что любой язык, компилятор которого создает инструкцию JUMP, имеет инструкцию goto.
Это отличная функция, которую я использую время от времени, чтобы избежать вложенных состояний if и сократить некоторые итерационные конструкции. Это не goto; просто потому, что вы (люди) не знаете, что это неправильно использовать. Всем «напуганным» стоит почитать какую-нибудь книгу по Java.
Я думаю, что единственный раз, когда я буду использовать эту конструкцию, будет переводить ассемблерный код на java в очень сжатые сроки.
Итак, вы проводите собеседование, основываясь на мелочах Java, а не на способности решать проблемы и продумывать проекты. Я позабочусь о том, чтобы никогда не брать интервью у вашей компании. :)
Итак, вы комментируете, расширяя точку зрения и / или делая некоторые далеко идущие обобщения и / или предположения. Я никогда больше не буду читать ваши комментарии. :)
Это всегда можно сделать без помеченного блока (извините за отсутствие пробелов): boolean breakSet = false; для (int i = 0; i
@Zombies: «Это вообще не goto, он может только вернуться к предыдущей итерации (то есть: вы не можете перейти вперед)». Какие? Вы вообще код смотрели? Что вы имеете в виду "не может прыгнуть вперед?"
Я знал, что Java 6 включает поддержку сценариев, но недавно обнаружил рукопись, который может интерпретировать и запускать JavaScript (и, как предполагается, другие языки сценариев, такие как Groovy) в интерактивном режиме, вроде как оболочка Python или irb в Ruby
Динамические прокси (добавлен в 1.3) позволяет вам определять новый тип во время выполнения, который соответствует интерфейсу. Пригодится на удивление много раз.
Динамические прокси-серверы - отличный повод написать интерфейс Foo и использовать его везде с классом FooImpl по умолчанию. Поначалу это может показаться уродливым («Почему бы просто не создать класс с именем Foo?»), Но преимущества с точки зрения будущей гибкости и возможности имитации для модульных тестов полезны. Хотя есть способы сделать это и для неинтерфейсов, они обычно требуют дополнительных вещей, таких как cblib.
Совместное объединение в вариации параметра типа:
public class Baz<T extends Foo & Bar> {}
Например, если вы хотите взять параметр, который является как Comparable, так и Collection:
public static <A, B extends Collection<A> & Comparable<B>>
boolean foo(B b1, B b2, A a) {
return (b1.compareTo(b2) == 0) || b1.contains(a) || b2.contains(a);
}
Этот надуманный метод возвращает истину, если две заданные коллекции равны или если одна из них содержит данный элемент, в противном случае - ложь. Следует отметить, что вы можете вызывать методы Comparable и Collection для аргументов b1 и b2.
Мое любимое использование для этого - когда вам нужен метод, который принимает добавляемую последовательность символов.
Можно ли поставить там «ИЛИ» вместо &?
@Grasper: Нет, OR (непересекающееся объединение) в этом контексте не предусмотрено. Но вы можете использовать непересекающийся тип данных объединения, например Eitherвместо. Этот тип является двойником парытип. Посмотрите на функциональную библиотеку Java или базовые библиотеки Scala для примера такого типа данных.
Вы должны сказать, что ДОЛЖНЫ расширять только один класс и несколько интерфейсов -> public class Baz <T extends Clazz & Interface1 & InterfaceI ... а не class Baz <T extends Clazz1 & ClazzI>
Я знаю, что это было добавлено в версии 1.5, но новый тип перечисления - отличная функция. Отсутствие необходимости использовать старый «шаблон int enum» очень помогло моему коду. Проверьте JLS 8.9 за сладкую подливку к картофелю!
Большинство «олдскульных» разработчиков Java никогда не удосужились начать использовать эту функцию, но я согласен, что это здорово.
final для переменных экземпляра:
Действительно полезно для многопоточного кода и значительно упрощает споры о состоянии и правильности экземпляра. Я нечасто видел это в контексте отрасли и часто не думал о Java-классах.
static {something;}:
Используется для инициализации статических членов (также я предпочитаю для этого статический метод (потому что у него есть имя). Не думал.
Да, статический метод также позволит вам хорошо справляться с исключениями, но когда вы используете статический блок, у вас нет другого выбора, кроме как поймать (и не выполнять никаких соответствующих действий).
На днях я был удивлен инициализаторами экземпляров. Я удалял некоторые методы с свернутым кодом и в итоге создал несколько инициализаторов экземпляров:
public class App {
public App(String name) { System.out.println(name + "'s constructor called"); }
static { System.out.println("static initializer called"); }
{ System.out.println("instance initializer called"); }
static { System.out.println("static initializer2 called"); }
{ System.out.println("instance initializer2 called"); }
public static void main( String[] args ) {
new App("one");
new App("two");
}
}
При выполнении метода main отобразится:
static initializer called
static initializer2 called
instance initializer called
instance initializer2 called
one's constructor called
instance initializer called
instance initializer2 called
two's constructor called
Думаю, это было бы полезно, если бы у вас было несколько конструкторов и вам нужен был общий код.
Они также предоставляют синтаксический сахар для инициализации ваших классов:
List<Integer> numbers = new ArrayList<Integer>(){{ add(1); add(2); }};
Map<String,String> codes = new HashMap<String,String>(){{
put("1","one");
put("2","two");
}};
Вероятно, было бы более разборчиво иметь метод, который вызывается всеми вашими конструкторами, или чтобы все конструкторы вызывали корневой this (...), а не super (...)
Полезно знать, что проверенные исключения, созданные в анонимных инициализаторах, должны быть объявлены конструкторами. Статические инициализаторы не могут генерировать проверенные исключения.
Преимущество этого метода по сравнению с явным методом, который необходимо вызвать, состоит в том, что если кто-то позже добавит конструктор, ему не нужно будет помнить о вызове init (); это будет сделано автоматически. Это может предотвратить ошибки будущих программистов.
Кроме того, в отличие от метода init (), он может инициализировать поля final.
Что, если вы расширите класс и создадите экземпляры различных дочерних элементов? Будет ли он по-прежнему вести себя так же?
Люди часто отказываются от сертификации (например, stackoverflow.com/questions/281100/281127#281127) и особенно ставят под сомнение их технические достоинства. Но если бы больше программистов изучали свой SCJP, многие не сочли бы это «скрытой особенностью». ;-)
Бьюсь об заклад, даже книга "Java за 24 часа" имеет эту "очевидную особенность". читать дальше ребята :) (
@Jonik Я учился на своем SCJP и помню, как видел это, но давно забыл об этом. Было приятно увидеть, что это опубликовано в этой ветке ... это не метод, который вы видите, используемый каждый день.
Важно отметить, что, поскольку блоки инициализатора экземпляра запускаются перед конструкторами, они также выполняются до вызова конструкторов суперкласса.
Новый Эффективная Java Джошуа Блоха - хороший ресурс.
Отличная книга, но в ней не так много скрытых функций.
Разрешение методов и конструкторов в перечислениях меня удивило. Например:
enum Cats {
FELIX(2), SHEEBA(3), RUFUS(7);
private int mAge;
Cats(int age) {
mAge = age;
}
public int getAge() {
return mAge;
}
}
У вас даже может быть «постоянное тело конкретного класса», которое позволяет определенному значению перечисления переопределять методы.
Дополнительная документация здесь.
Действительно потрясающая функция - делает перечисления OO и очень чисто решает множество проблем инициализации.
Из-за этого перечисления также отлично подходят для синглтонов.
Enum как синглтон? Enum только с одним значением?
Георгий: Синглтон - это один экземпляр объекта, а не объект с одним значением.
Это действительно удобно, если вам нужны счетчики относительно значения. Итак, в этом примере, возможно, вы могли бы инициализировать Cats с указанным значением возраста, а затем иметь FELIX (2 + age), таким образом, возраст Феликса будет относиться к экземпляру возраста Cats.
Перечисления настолько объектно-ориентированные, что вы даже можете добавить общедоступный void setAge (int age) {this.mAge = afe} ... и он работает !!
@Georgy: см. Также пункт 3 в книге Джошуа Блоха «Эффективная Java» (2-е изд.); «Хотя этот подход еще не получил широкого распространения, одноэлементный перечисляемый тип - лучший способ реализовать синглтон».
Мелкие придирки: mAge должен быть окончательным. Редко есть причина для незавершенных полей в перечислениях.
Это почти сводит на нет цель использования перечислений IMO.
@Jonik, хотя у этого есть преимущества в отношении инициализации экземпляра синглтона и доступа к нему, это мешает вам иметь класс синглтона, расширяющий другой класс (все перечисления неявно расширяют java.lang.enum) и, хотя обычно это не нужно / нежелательно, расширять.
К сожалению, перечисления не могут расширять другие перечисления, но они могут реализовывать интерфейсы, что иногда бывает полезно.
На самом деле не является частью языка Java, но дизассемблер javap, который поставляется с Sun JDK, широко не известен и не используется.
Самосвязанные дженерики:
class SelfBounded<T extends SelfBounded<T>> {
}
http://www.artima.com/weblogs/viewpost.jsp?thread=136394
см. также crtp справки: artima.com/weblogs/viewpost.jsp?thread=133275
JDK 1.6_07 + содержит приложение под названием VisualVM (bin / jvisualvm.exe), которое представляет собой красивый графический интерфейс поверх многих инструментов. Это кажется более всеобъемлющим, чем JConsole.
И у него есть плагин (это штука netbeans), который позволяет использовать плагины Jconsole. Довольно мило.
VisualVM - лучшая вещь после нарезанного хлеба! Жаль, что он не имеет всех функций при работе с JDK 1.5.
Я считаю VisualVM медленным или непригодным для использования в некоторых обстоятельствах. Коммерческий YourKit не имеет этой проблемы, но, к сожалению, не является бесплатным.
Более поздние версии Visual VM, начиная с JDK 1.6.0_22 и более поздних, значительно улучшены. Могу поспорить, что у JDK1.7 есть даже лучшая версия.
Параметры типа для универсальных методов можно указать явно следующим образом:
Collections.<String,Integer>emptyMap()
И, клянусь богом, это уродливо и сбивает с толку. И не имеет отношения к безопасности типов.
Мне это нравится. Это делает ваш код более явным и более понятным для бедняги, которым придется поддерживать его через год или два.
На самом деле это очень полезно в ситуациях, когда вы объявили статический универсальный метод, такой как public static <T> T foo(T t). Затем вы можете звонить на Class.<Type>foo(t);.
По какой-то причине это не работает со статически импортированными методами ... Интересно, почему.
Это особенно полезно при использовании троичных if с возвратом. Например return set1.equals(set2) ? new ArrayList<String>(set1) : Collections.<String>emptyList(). Это также полезно для некоторых вызовов методов, когда простой Collections.emptyMap () выдает ошибку компиляции.
Им потребовалось достаточно времени, чтобы добавить поддержку для этого,
Метод asList в java.util.Arrays позволяет хорошо комбинировать varargs, общие методы и автобоксинг:
List<Integer> ints = Arrays.asList(1,2,3);
вы хотите обернуть возвращенный список конструктором List, иначе ints будет фиксированного размера (поскольку он поддерживается массивом)
У Arrays.asList есть необычная особенность: вы можете использовать элементы set(), но не add() или remove(). Поэтому я обычно оборачиваю его в new ArrayList(...) или Collections.unmodifiableList(...), в зависимости от того, хочу ли я, чтобы список изменялся или нет.
Некоторые трюки с потоком управления, finally вокруг оператора return:
int getCount() {
try { return 1; }
finally { System.out.println("Bye!"); }
}
Правила для определенное назначение будут проверять, что последняя переменная всегда назначается с помощью простого анализа потока управления:
final int foo;
if (...)
foo = 1;
else
throw new Exception();
foo+1;
JVisualVM из каталога bin в дистрибутиве JDK. Мониторинг и даже профилирование любого java-приложения, даже того, которое вы не запускали с какими-либо специальными параметрами. Только в последних версиях Java 6SE JDK.
Если вы используете Spring для настройки ваших аннотированных компонентов JMX для работы с jconsole и jvisualvm, вы необходимость запускаете интерфейс управления вручную вместо того, чтобы полагаться на виртуальную машину, созданную по умолчанию. Если вы этого не сделаете, вы столкнетесь с проблемами из-за наличия нескольких конфликтующих «синглтонов», и ваши JMX-бины аккуратно окажутся в неправильном…
Передача управления в блоке finally отбрасывает любые исключения. Следующий код не генерирует исключение RuntimeException - он теряется.
public static void doSomething() {
try {
//Normally you would have code that doesn't explicitly appear
//to throw exceptions so it would be harder to see the problem.
throw new RuntimeException();
} finally {
return;
}
}
От http://jamesjava.blogspot.com/2006/03/dont-return-in-finally-clause.html
Это неприятно, но это также своего рода логическое следствие того, как в конечном итоге работает. Управление потоком try / catch / finally делает то, для чего предназначено, но только в определенных пределах. Точно так же вы должны быть осторожны, чтобы не вызвать исключение внутри блока catch / finally, иначе вы также выбросите исходное исключение. И если вы выполните System.exit () внутри блока try, блок finally не будет вызван. Если вы нарушите нормальный поток, вы нарушите нормальный поток ...
Не используйте finally {return; } но только наконец {код без возврата}. Это логично, поскольку finally также предназначено для выполнения при возникновении исключений, и единственное возможное значение возврата в качестве обработчика исключений должно заключаться в игнорировании исключения и, действительно, возврате.
Это больше похоже на «ловушку», чем на скрытую функцию, хотя есть способы использовать ее как единое целое, использование этого метода не будет хорошей идеей.
Eclipse выдаст предупреждение, если вы это сделаете. Я с Eclipse. Если вы хотите перехватить исключение ... перехватите исключение.
Это то, что вы платите за грязный код, который возвращается из середины метода. Но все же хороший пример.
@ Нил Коффи: Если вы, наконец, вызовете исключение внутри, но поймаете его, я полагаю, что это не проблема?
Добавление конструкции цикла for-each в версии 1.5. я
// For each Object, instantiated as foo, in myCollection
for(Object foo: myCollection) {
System.out.println(foo.toString());
}
И может использоваться во вложенных экземплярах:
for (Suit suit : suits)
for (Rank rank : ranks)
sortedDeck.add(new Card(suit, rank));
Конструкция for-each также применима к массивам, где она скрывает индексную переменную, а не итератор. Следующий метод возвращает сумму значений в массиве int:
// Returns the sum of the elements of a
int sum(int[] a) {
int result = 0;
for (int i : a)
result += i;
return result;
}
Я думаю, что использование i здесь очень сбивает с толку, поскольку большинство людей ожидают, что i будет индексом, а не элементом массива.
Итак ... int sum (int [] array) {int result = 0; for (int element: array) {результат + = элемент; } вернуть результат; }
Единственное, что меня сводит с ума по этому поводу, это то, что кажется, что было бы очень легко создать ключевое слово для доступа к значению счетчика циклов, но вы не можете. Если я хочу перебрать два массива и внести изменения во второй на основе значений в первом, я вынужден использовать старый синтаксис, потому что у меня нет смещения во втором массиве.
Жаль, что он также не работает с перечислениями, такими как те, что используются в JNDI. Вернемся к итераторам.
@extraneon: взгляните на Collections.list (Enumeration
Единственная возможная проблема с этим состоит в том, что он использует Iterator списка, и любое изменение списка в цикле вызовет ConcurrentModificationException.
@deepc К сожалению, это создает копию перечисления, а не представление List, поэтому я создал свой собственный адаптер Iterable, который оборачивается вокруг Enumeration.
«const» - это ключевое слово, но вы не можете его использовать.
int const = 1; // "not a statement"
const int i = 1; // "illegal start of expression"
Я предполагаю, что авторы компилятора думали, что он может быть использован в будущем, и им лучше оставить его зарезервированным.
Не совсем «особенность», но определенно «скрытая».
Не просто не-функция, а анти-функция! (Я также не упомянул, что goto то же самое - зарезервировано, но не реализовано).
Он зарезервирован для компилятора, чтобы создавать более точные сообщения об ошибках.
goto - это тоже ключевое слово, которое нельзя использовать. ;)
@ Питер Лоури: Я знаю, но это уже было опубликовано.
Не совсем особенность, но забавный трюк, который я недавно обнаружил на какой-то веб-странице:
class Example
{
public static void main(String[] args)
{
System.out.println("Hello World!");
http://Phi.Lho.free.fr
System.exit(0);
}
}
- допустимая программа на Java (хотя и выдает предупреждение). Если вы не понимаете почему, посмотрите ответ Грегори! ;-) Подсветка синтаксиса здесь тоже дает подсказку!
аккуратно, этикетка с комментарием :)
Мой любимый вариант: вывести все трассировки стека потоков в стандартный формат.
windows: CTRL-Break в окне java cmd / консоли
unix: kill -3 PID
Также ctrl- \ в Unix. Или используйте jstack из JDK.
Спасибо, вы только что научили меня, что на моей клавиатуре есть клавиша Break.
В Windows CTRL-BREAK работает только в том случае, если процесс запущен в текущем окне консоли. Вместо этого вы можете использовать JAVA_HOME / bin / jstack.exe. Просто укажите идентификатор процесса Windows.
Я думал, что это было убить -SIGQUIT
@Nick, да, SIGQUIT - это обычно сигнал №3.
Полномочия, которые вы можете получить над сборщиком мусора и то, как он управляет сборкой объектов, очень эффективны, особенно для долго работающих и чувствительных ко времени приложений. Он начинается со слабых, мягких и фантомных ссылок в пакете java.lang.ref. Взгляните на них, особенно для создания кешей (уже есть java.util.WeakHashMap). Теперь углубитесь в ReferenceQueue, и вы получите еще больший контроль. Наконец, возьмите документацию по самому сборщику мусора, и вы сможете контролировать, как часто он запускается, размеры различных областей сбора и типы используемых алгоритмов (для Java 5 см. http://java.sun.com/docs/hotspot/gc5.0/gc_tuning_5.html).
WeakHashMap - это нет, подходящий для создания кешей.
Использование ключевого слова это для доступа к полям / методам содержащего класса из внутреннего класса. В приведенном ниже довольно надуманном примере мы хотим использовать поле sortAscending класса контейнера из анонимного внутреннего класса. Использование ContainerClass.this.sortAscending вместо this.sortAscending делает свое дело.
import java.util.Comparator;
public class ContainerClass {
boolean sortAscending;
public Comparator createComparator(final boolean sortAscending){
Comparator comparator = new Comparator<Integer>() {
public int compare(Integer o1, Integer o2) {
if (sortAscending || ContainerClass.this.sortAscending) {
return o1 - o2;
} else {
return o2 - o1;
}
}
};
return comparator;
}
}
Это необходимо только в том случае, если вы затеняли имя (в вашем случае - имя параметра метода). Если бы вы назвали аргумент чем-то другим, вы могли бы напрямую обращаться к переменной-члену sortAscending класса Container без использования this.
По-прежнему полезно иметь ссылку на включающий класс, например. если вам нужно передать его какому-либо методу или конструктору.
Часто используется при разработке макета Android, чтобы получить контекст, скажем, от слушателя кнопки с помощью MyActivity.this.
Это не совсем «скрытые функции» и не очень полезные, но в некоторых случаях могут быть чрезвычайно интересными:
Класс sun.misc.Unsafe - позволит вам реализовать прямое управление памятью на Java (вы даже можете написать с ним самомодифицирующийся код Java, если будете много стараться):
public class UnsafeUtil {
public static Unsafe unsafe;
private static long fieldOffset;
private static UnsafeUtil instance = new UnsafeUtil();
private Object obj;
static {
try {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
unsafe = (Unsafe)f.get(null);
fieldOffset = unsafe.objectFieldOffset(UnsafeUtil.class.getDeclaredField("obj"));
} catch (Exception e) {
throw new RuntimeException(e);
}
};
}
это солнце. * API, который на самом деле не является частью языка Java как таковой
В Unsafe есть ряд других любопытных методов, таких как создание объекта без вызова конструктора, методы malloc / realloc / free style.
Мне особенно нравятся примитивные методы получения и установки в ячейках памяти, такие как putDouble (длинный адрес, double d).
Поскольку никто еще не сказал этого (я думаю), моя любимая функция - автоматический бокс!
public class Example
{
public static void main(String[] Args)
{
int a = 5;
Integer b = a; // Box!
System.out.println("A : " + a);
System.out.println("B : " + b);
}
}
Думаю, речь идет о скрытых функциях, а не о любимых функциях. Я предполагаю, что автобокс довольно хорошо известен всем, кто использует 1.5 или новее.
@Trick Использование valueOf явно не имеет значения, поскольку автобокс компилируется с вызовом valueOf. Это более эффективно с точки зрения памяти по сравнению с вызовом конструктора Integer или Long для небольших значений, поскольку valueOf использует кеш малых значений.
автобокс - одна из наиболее часто используемых функций. Не то чтобы это серьезная проблема, на самом деле, вы никогда этого не заметите, но вы найдете тысячи случаев, таких как boolean b = Boolean.valueOf (someString), во всех мыслимых проектах.
Как насчет файлов свойств в выбранной вами кодировке? Раньше при загрузке свойств вы предоставляли InputStream, а метод load() декодировал его как ISO-8859-1. Вы мог на самом деле храните файл в какой-то другой кодировке, но вам пришлось использовать такой отвратительный хак после загрузки, чтобы правильно декодировать данные:
String realProp = new String(prop.getBytes("ISO-8859-1"), "UTF-8");
Но, начиная с JDK 1.6, существует метод load(), который принимает Reader вместо InputStream, что означает, что вы можете использовать правильную кодировку с самого начала (есть также метод store(), который принимает Writer). Мне это кажется большим делом, но, похоже, он был прятан в JDK без всяких помпезностей. Я наткнулся на это только несколько недель назад, и быстрый поиск в Google обнаружил лишь одно случайное упоминание о нем.
Не взламывайте, пожалуйста - это испортит все escape-последовательности \uXXXX. Лучше использовать native2ascii для преобразования файла после редактирования и перед развертыванием или использовать читательский вариант метода load().
Если вы много занимаетесь разработкой JavaBean и работаете с поддержкой изменения свойств, вы обычно заканчиваете писать много сеттеров следующим образом:
public void setFoo(Foo aFoo){
Foo old = this.foo;
this.foo = aFoo;
changeSupport.firePropertyChange("foo", old, aFoo);
}
Недавно я наткнулся на блог, в котором предлагалось более краткое описание этого, что значительно упрощает написание кода:
public void setFoo(Foo aFoo){
changeSupport.firePropertyChange("foo", this.foo, this.foo = aFoo);
}
Это фактически упростило ситуацию до такой степени, что я смог настроить шаблон установщика в Eclipse, чтобы метод создавался автоматически.
Четко ли определен порядок выполнения аргументов в Java? В противном случае это может привести к беспорядку.
Он хорошо определен, но в целом не совсем понятен. Технически это сработает, но явно сбивает с толку.
Да - порядок или выполнение аргументов очень хорошо определено. Я не уверен, что согласен с тем, что это более запутанно, чем наличие трех строк мусорного кода в каждом сеттере в каждом отдельном JavaBean - гораздо лучше сосредоточиться на коде, который вы хотите написать, а не на этом типе шаблона!
Кома порядок выполнения очень четко определен. Всегда должно быть слева направо.
Я не понимаю, почему более короткую версию легче создать автоматически, чем более длинную.
Джейсон, честно говоря, генерация кода может сделать это в любом случае. Я думаю, это своего рода точка генерации кода ;-) Все же намного проще читать код с уменьшенным шаблоном (наличие геттера и сеттера вообще все еще является огромным количеством шаблонов, но это обсуждение в другой день)
Вы можете использовать для этого проект lombok.
Фабрика строковых параметризованных классов.
Class.forName( className ).newInstance();
Загрузите ресурс (файл свойств, xml, xslt, изображение и т. д.) Из файла jar развертывания.
this.getClass().getClassLoader().getResourceAsStream( ... ) ;
Что меня действительно удивило, так это механизм настраиваемой сериализации.
Хотя это методы частный!!, они являются "таинственно", вызываемыми JVM во время сериализации объекта.
private void writeObject(ObjectOutputStream out) throws IOException;
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;
Таким образом, вы можете создать свою собственную сериализацию, чтобы сделать ее более «любой» (безопасной, быстрой, редкой, простой и т. д.)
Это то, что действительно следует учитывать, если через узлы нужно передавать много информации. Механизм сериализации может быть изменен для отправки половины данных. Часто узкие места возникают не в платформе, но в количестве, отправленном по сети, вы можете сэкономить тысячи dll на оборудовании.
Вот статья. http://java.sun.com/developer/technicalArticles/Programming/serialization/
Это также полезно для настраиваемой сериализации для объектов с несериализуемыми членами.
Вы можете объявить класс в методе:
public Foo foo(String in) {
class FooFormat extends Format {
public Object parse(String s, ParsePosition pp) { // parse stuff }
}
return (Foo) new FooFormat().parse(in);
}
Вы также могли бы просто перейти на новый Format () {вместо объявления класса FooFormat
Анонимные классы переоценены. Попробуйте отладить один когда-нибудь или поддержать его в полевых условиях, и вы поймете, что я имею в виду. Если вы потеряете номера строк в сборке релиза, их очень трудно отследить.
и они называются локальными классами :) java.sun.com/docs/books/jls/second_edition/html/…
Я предпочитаю приручить анонимные классы, заставляя их обращаться к основному объекту для выполнения своей работы; анонимный класс - это просто адаптер, поэтому его легко понять.
@Donal - я часто использую это с кодом пользовательского интерфейса. Однако стоит сказать, что этот дизайн плох с точки зрения рефакторинга. Вы можете извлечь класс «верхнего уровня» и обнаружить, что его логика полностью реализована другим классом!
@oxbow: Я не совсем понимаю вашу точку зрения. Единственное, что есть в моем внутреннем классе, - это вызов (обычно непубличного) метода внешнего класса. Хотя да, теоретически это может быть довольно неожиданным, но на практике это довольно легко увидеть, поскольку это только вызов без обработки исключений.
API обработки аннотаций из Java 6 выглядит очень перспективным для генерации кода и проверки статического кода.
окончательную инициализацию можно отложить.
Он гарантирует, что даже при сложном потоке логики всегда будут установлены возвращаемые значения. Слишком легко пропустить регистр и случайно вернуть null. Это не делает невозможным возврат null, просто очевидно, что это сделано специально:
public Object getElementAt(int index) {
final Object element;
if (index == 0) {
element = "Result 1";
} else if (index == 1) {
element = "Result 2";
} else {
element = "Result 3";
}
return element;
}
Это удивительно. Можно ли справедливо сказать: «Значение конечной переменной может быть установлено один раз», независимо от того, где происходит установка?
Да, но более строго: «Значение последней переменной должно быть установлено один раз»
+1 Согласитесь, это еще один ценный инструмент для обнаружения ошибок во время компиляции, и программисты по некоторым причинам стесняются его использовать. Обратите внимание, что, поскольку начиная с Java 5, 'final' также имеет последствия для безопасности потоков, возможность установить конечную переменную во время конструктора неоценима.
Хотя для этого конкретного метода я бы просто использовал несколько возвратов. Фактически, в большинстве случаев, когда это применимо, я бы, вероятно, реорганизовал его в отдельный метод и использовал несколько возвратов.
Мне это нравится! Я всегда удалял последнее ключевое слово, потому что думал, что оно не сработает. Благодарность!
Обычно это немного запах кода. Как упоминалось выше, следует провести рефакторинг для использования защитного закрытия (с многократным возвратом). Если такой блок существует только как часть метода, он должен быть извлечен в новый метод с несколькими возвратами.
В нескольких комментариях упоминается множественный возврат - обратите внимание, что вы можете получить то же преимущество, что и с последней переменной, если опустите предложение return в конце. Вы можете быть уверены, что все случаи обработаны (и возвращены соответствующие значения), потому что в противном случае он не будет компилироваться.
Несколько лет назад, когда мне приходилось делать Java (1.4.x), я хотел метод eval (), а Suns javac был (был?) Написан на Java, поэтому нужно было просто связать tools.jar и использовать его с некоторым клеевым кодом вокруг Это.
Обработка Java делает изящный трюк с определением переменных, если вы не используете инициализатор по умолчанию.
{
int x;
if (whatever)
x=1;
if (x == 1)
...
}
Это приведет к ошибке во время компиляции, что у вас есть путь, в котором X не определен должным образом. Это помогло мне несколько раз, и я подумал об инициализации по умолчанию, например:
int x=0; String s=null;
быть плохим шаблоном, поскольку он блокирует эту полезную проверку.
Тем не менее, иногда трудно обойтись - мне приходилось возвращаться и редактировать = null, когда это имело смысл по умолчанию, но я больше никогда не вставлял его на первом проходе.
+1 Согласен - по какой-то причине некоторых людей "сбивает с толку" не указывать начальное значение для переменной, как будто они думают, что компилятор тайно собирается выбрать случайное число или что-то в этом роде. Но, как вы правильно сказали, это ценный инструмент для выявления определенных ошибок во время компиляции.
Это потому, что во вселенной C неинициализированные указатели были проклятием существования - хотя, если я правильно помню, во вселенной C++ указатели автоматически инициализировались нулевым значением, если они размещались в стеке.
Да, именно поэтому стоит отметить, что это уже не очень хорошая идея и даже несколько контрпродуктивна.
По возможности используйте final для дополнительных проверок.
Вы можете получить доступ к конечным локальным переменным и параметрам в блоках инициализации и методах локальных классов. Учти это:
final String foo = "42";
new Thread() {
public void run() {
dowhatever(foo);
}
}.start();
Немного похоже на закрытие, не так ли?
Я знал, что вы не можете получить доступ к нефиналам, но был удивлен, что вы может получили доступ к финалам.
Значение:
new URL("http://www.yahoo.com").equals(new URL("http://209.191.93.52"))
это true.
(Из Java Puzzlers)
Только если вы подключены к Интернету. Если он не может разрешить адрес, он вернет false, и поэтому класс URL разрывает контракт equals (). Лучше использовать класс URI в java.net.
И этого не должно быть, потому что HTTP-сервер может иметь виртуальный хостинг и может вести себя иначе.
Несколько человек написали об инициализаторах экземпляров, вот вам хорошее применение:
Map map = new HashMap() {{
put("a key", "a value");
put("another key", "another value");
}};
Это быстрый способ инициализировать карты, если вы просто делаете что-то быстрое и простое.
Или использовать его для создания прототипа рамы быстрого поворота:
JFrame frame = new JFrame();
JPanel panel = new JPanel();
panel.add( new JLabel("Hey there"){{
setBackground(Color.black);
setForeground( Color.white);
}});
panel.add( new JButton("Ok"){{
addActionListener( new ActionListener(){
public void actionPerformed( ActionEvent ae ){
System.out.println("Button pushed");
}
});
}});
frame.add( panel );
Конечно, можно злоупотреблять:
JFrame frame = new JFrame(){{
add( new JPanel(){{
add( new JLabel("Hey there"){{
setBackground(Color.black);
setForeground( Color.white);
}});
add( new JButton("Ok"){{
addActionListener( new ActionListener(){
public void actionPerformed( ActionEvent ae ){
System.out.println("Button pushed");
}
});
}});
}});
}};
Почему-то это как если бы ключевое слово "with" (фактически, функциональность) было добавлено в Java. Может быть очень удобно, недавно я ворчал, потому что инициализация массивов была недоступна для коллекций. Спасибо!
Однако есть один побочный эффект от его использования. Создаются анонимные объекты, что не всегда хорошо.
Для большого приложения с графическим интерфейсом, разработанного таким образом, вероятно, потребуется немного больше места для перманента. Хотя я не думаю, что нужно много места для хранения чего-то, что по сути является именем класса и инициализатором.
Должен сказать, что это лучшее использование статических инициализаторов, чем №1, но ничего себе. Неужели это действительно так много преимуществ по сравнению с обозначением рамки "f" и кнопок b? Читаемость, я бы сказал, что даже «короткие» названия лучше, чем это, если бы не только фактор «без сюрпризов».
Хотя, посмотрев на это побольше, я должен сказать, что меня странно привлекает естественная вложенность, которую это обеспечивает, даже версия "Abused".
Мне тоже нравится :) Это как JavaFX, но бесплатно
Ага, мне очень нравится "злоупотребленная" версия, я думаю, что она действительно очень ясна и читабельна, но, возможно, это только я.
Абсолютно согласен, иерархия кода, отражающая иерархию графического интерфейса, является огромным улучшением по сравнению с последовательностью вызовов методов.
Мне это не нравится, потому что это позволяет приложению запускаться даже медленнее, чем это делает Java. Он создает новый подкласс HashMap, который совершенно бесполезен.
"ссылочная прозрачность" - вот причина того, почему такое вложение кажется приятным, см. википедию. его также смутно шепелявые, даже "оскорбленные" версии, шепелявые, кажется, имеют высокую терпимость к глубокой композиции. github.com/technomancy/leiningen/blob/master/src/leiningen/…
Начиная с Java 1.5, Java теперь имеет гораздо более чистый синтаксис для написания функций переменной арности. Итак, вместо того, чтобы просто передавать массив, теперь вы можете сделать следующее
public void foo(String... bars) {
for (String bar: bars)
System.out.println(bar);
}
бары автоматически конвертируются в массив указанного типа. Не большая победа, но, тем не менее, победа.
Здесь важно то, что при вызове метода вы можете написать: foo ("first", "second", "third")
так что старый привет мир можно переписать; public static void main (String ... args) {System.out.println ("Hello World!"); }
@Karussell Может быть, но вроде бы аргументы неуместны: p
Javadoc - при правильном написании (к сожалению, не всегда в случае с некоторыми разработчиками) он дает вам четкое, связное описание того, что должен делать код, в отличие от того, что он делает на самом деле. Затем его можно превратить в удобный для просмотра набор HTML-документации. Если вы используете непрерывную интеграцию и т. д., Ее можно создавать регулярно, чтобы все разработчики могли видеть последние обновления.
SwingWorker для простого управления обратными вызовами пользовательского интерфейса из фоновых потоков.
Экземпляры одного и того же класса могут иметь доступ к закрытым членам других экземпляров:
class Thing {
private int x;
public int addThings(Thing t2) {
return this.x + t2.x; // Can access t2's private value!
}
}
Только совсем недавно я узнал об этом, и кто-то должен был указать мне на это в комментарии, оставленном в моем блоге.
Это удивительно, если у вас есть доступ к объектно-ориентированному языку, отличному от C++.
Это ужасно. Не делай этого. Другой человек имеет право заглядывать мне в кишечник не потому, что я человек ;-)
Но как бы вы сделали правильную реализацию hash / equals, если не можете получить доступ к его внутренностям?
это точно такая же логика, которая позволяет методу получать доступ к частной переменной экземпляра ... частные классы являются частью реализации, поэтому не должно быть никаких ограничений на доступ между внутренними классами и родительским, родительским и внутренним классами и внутренние классы к другим внутренним классам.
По-видимому, с некоторыми отладочными сборками есть опция, которая выгружает собственный (JIT) ассемблерный код из HotSpot: http://weblogs.java.net/blog/kohsuke/archive/2008/03/deep_dive_into.html
К сожалению, я не смог найти сборку по ссылке в этом посте, если кто-нибудь сможет найти более точный URL-адрес, я бы с удовольствием поигрался с ней.
Я голосую за java.util.concurrent с его параллельными коллекциями и гибкими исполнителями, позволяющими, среди прочего, пулы потоков, запланированные задачи и скоординированные задачи. DelayQueue - мой личный фаворит, где элементы становятся доступными после определенной задержки.
java.util.Timer и TimerTask можно безопасно остановить.
Кроме того, не совсем скрыто, а в другом пакете от других классов, связанных с датой и временем. java.util.concurrent.TimeUnit полезен при преобразовании между наносекундами, микросекундами, миллисекундами и секундами.
Он читается намного лучше, чем обычные someValue * 1000 или someValue / 1000.
Недавно обнаружил CountDownLatch и CyclicBarrier - полезно так!
Ключевое слово strictfp. (Я никогда не видел, чтобы он использовался в реальном приложении :)
Вы можете получить класс для примитивных типов, используя следующую нотацию: int.class, float.class и т. д. Очень полезно при рефлексии.
Конечные массивы могут использоваться для "возврата" значений из анонимных внутренних классов (предупреждение, бесполезный пример ниже):
final boolean[] result = new boolean[1];
SwingUtilities.invokeAndWait(new Runnable() {
public void run() { result[0] = true; }
});
использование последнего массива для возврата из такого анонимного внутреннего класса, вероятно, не рекомендуется для хорошей практики программирования ...
Ромен Гай? Например, в Ромен Гай? ... Во всяком случае, +1 для int.class. Я думал, что Integer.TYPE - единственный способ.
Более чем бесполезно. Код, следующий сразу за этим, скорее всего, будет выполнен до обратного вызова EDT. Следовательно, он не увидит истинной ценности.
Я сам использовал strictfp. Это было для программы, в которой потенциальное перемещение двойной между регистрами (80-битные) и RAM (64-битные) могло вызвать проблемы.
На самом деле для invokeAndWait это более полезно, чем для invokeLater.
Не видел, чтобы кто-нибудь упоминал instanceof, реализованный таким образом, что проверка на null не требуется.
Вместо:
if ( null != aObject && aObject instanceof String )
{
...
}
просто используйте:
if ( aObject instanceof String )
{
...
}
Обидно, что это такая малоизвестная особенность. Я видел много кода, похожего на первый блок кода.
Это потому, что Java имеет специальный (скрытый) нулевой тип, который вы не видите и не можете ссылаться.
Что еще хуже, проверка на null перед freeing или deleteing в C / C++. Такая фундаментальная концепция.
мне понравилось
Подключаемый модуль Java следующего поколения, найденный в Java 1.6 Update 10 и более поздних версиях, имеет несколько очень полезных функций:
Здесь задокументировано много других вещей: http://jdk6.dev.java.net/plugin2/
Больше из этого выпуска здесь: http://jdk6.dev.java.net/6u10ea.html
Вы можете создать строку в стиле sprintf, используя String.format ().
String w = "world";
String s = String.format("Hello %s %d", w, 3);
Конечно, вы также можете использовать специальные спецификаторы для изменения вывода.
Подробнее здесь: http://java.sun.com/j2se/1.5.0/docs/api/java/util/Formatter.html#syntax
со статическим импортом вы можете делать такие классные вещи, как:
List<String> myList = list("foo", "bar");
Set<String> mySet = set("foo", "bar");
Map<String, String> myMap = map(v("foo", "2"), v("bar", "3"));
вы даже можете сделать это с помощью дженериков. В Google Collections для этого есть хорошие утилиты.
Мне нравится статический импорт методов.
Например, создайте следующий класс утилиты:
package package.name;
public class util {
private static void doStuff1(){
//the end
}
private static String doStuff2(){
return "the end";
}
}
Тогда используйте это так.
import static package.name.util.*;
public class main{
public static void main(String[] args){
doStuff1(); // wee no more typing util.doStuff1()
System.out.print(doStuff2()); // or util.doStuff2()
}
}
Статический импорт работает с любым классом, даже Math ...
import static java.lang.Math.*;
import static java.lang.System.out;
public class HelloWorld {
public static void main(String[] args) {
out.println("Hello World!");
out.println("Considering a circle with a diameter of 5 cm, it has:");
out.println("A circumference of " + (PI * 5) + "cm");
out.println("And an area of " + (PI * pow(5,2)) + "sq. cm");
}
}
исправьте регистр буквы «S» в обоих методах doStuffX в вашем примере. Это могло сбивать с толку.
List.subList возвращает представление исходного списка
Документированная, но малоизвестная функция списков. Это позволяет вам работать с частями списка с изменениями, отраженными в исходном списке.
Список подсписок (int fromIndex, int toIndex)
"This method eliminates the need for explicit range operations (of the sort that commonly exist for arrays). Any operation that expects a list can be used as a range operation by passing a subList view instead of a whole list. For example, the following idiom removes a range of elements from a list:
list.subList(from, to).clear();Similar idioms may be constructed for indexOf and lastIndexOf, and all of the algorithms in the Collections class can be applied to a subList."
Будьте осторожны с подсписком, он будет хранить в памяти весь базовый список, даже если вы сохраняете только ссылку на подсписок. Это очень отличается от, скажем, «хвостовой» операции на функциональном языке.
И, в зависимости от реализации, некоторые операции с подсписком могут выполняться медленнее, чем с полным списком.
Когда люди видят list.subList(from, to).clear(), они приходят в восторг, это так здорово! За исключением того, что в других библиотеках это просто list.RemoveRange(from, to), который намного аккуратнее, прямолинейнее и удобнее.
Я лично обнаружил java.lang.Void очень поздно - улучшает читаемость кода в сочетании с дженериками, например Callable<Void>
не совсем. в какой-то момент вам нужно уточнить: Executors.newSingleThreadExecutor (). submit (new Callable
Void более конкретен, чем Object, поскольку Void может быть только нулевым, Object может быть любым.
Вы можете использовать перечисления для реализации интерфейса.
public interface Room {
public Room north();
public Room south();
public Room east();
public Room west();
}
public enum Rooms implements Room {
FIRST {
public Room north() {
return SECOND;
}
},
SECOND {
public Room south() {
return FIRST;
}
}
public Room north() { return null; }
public Room south() { return null; }
public Room east() { return null; }
public Room west() { return null; }
}
Обновлено: годы спустя ....
Я использую эту функцию здесь
public enum AffinityStrategies implements AffinityStrategy {
Используя интерфейс, разработчики могут определять свои собственные стратегии. Использование enum означает, что я могу определить коллекцию (из пяти) встроенных.
@ Ариан, это не безумие. ЭТО. ЯВЛЯЕТСЯ. ДЖАВАААААААААА!
ВААААА нет, я с Адрианом. Это не Java. Это безумие. Я очень хочу использовать это.
Типы пересечений позволяют (своего рода) выполнять перечисления с иерархией наследования. Вы не можете наследовать реализацию, но можете делегировать ее вспомогательному классу.
enum Foo1 implements Bar {}
enum Foo2 implements Bar {}
class HelperClass {
static <T extends Enum<T> & Bar> void fooBar(T the enum) {}
}
Это полезно, когда у вас есть несколько разных перечислений, реализующих какой-то шаблон. Например, несколько пар перечислений, которые связаны родительско-дочерними отношениями.
enum PrimaryColor {Red, Green, Blue;}
enum PastelColor {Pink, HotPink, Rockmelon, SkyBlue, BabyBlue;}
enum TransportMedium {Land, Sea, Air;}
enum Vehicle {Car, Truck, BigBoat, LittleBoat, JetFighter, HotAirBaloon;}
Вы можете написать общие методы, которые говорят: «Хорошо, учитывая значение перечисления, которое является родителем некоторых других значений перечисления, какой процент всех возможных дочерних перечислений дочернего типа имеет это конкретное родительское значение в качестве своего родителя?», И иметь все это безопасный тип и делается без литья. (например: «Море» составляет 33% всех возможных транспортных средств, а «Зеленый» - 20% всех возможных пастелей).
Код выглядит так. Это довольно неприятно, но есть способы исправить это. Обратите внимание, в частности, что сами «листовые» классы довольно аккуратны - общие классы имеют ужасно уродливые объявления, но вы пишете их только один раз. Как только универсальные классы есть, их легко использовать.
import java.util.EnumSet;
import javax.swing.JComponent;
public class zz extends JComponent {
public static void main(String[] args) {
System.out.println(PrimaryColor.Green + " " + ParentUtil.pctOf(PrimaryColor.Green) + "%");
System.out.println(TransportMedium.Air + " " + ParentUtil.pctOf(TransportMedium.Air) + "%");
}
}
class ParentUtil {
private ParentUtil(){}
static <P extends Enum<P> & Parent<P, C>, C extends Enum<C> & Child<P, C>> //
float pctOf(P parent) {
return (float) parent.getChildren().size() / //
(float) EnumSet.allOf(parent.getChildClass()).size() //
* 100f;
}
public static <P extends Enum<P> & Parent<P, C>, C extends Enum<C> & Child<P, C>> //
EnumSet<C> loadChildrenOf(P p) {
EnumSet<C> cc = EnumSet.noneOf(p.getChildClass());
for(C c: EnumSet.allOf(p.getChildClass())) {
if (c.getParent() == p) {
cc.add(c);
}
}
return cc;
}
}
interface Parent<P extends Enum<P> & Parent<P, C>, C extends Enum<C> & Child<P, C>> {
Class<C> getChildClass();
EnumSet<C> getChildren();
}
interface Child<P extends Enum<P> & Parent<P, C>, C extends Enum<C> & Child<P, C>> {
Class<P> getParentClass();
P getParent();
}
enum PrimaryColor implements Parent<PrimaryColor, PastelColor> {
Red, Green, Blue;
private EnumSet<PastelColor> children;
public Class<PastelColor> getChildClass() {
return PastelColor.class;
}
public EnumSet<PastelColor> getChildren() {
if (children == null) children=ParentUtil.loadChildrenOf(this);
return children;
}
}
enum PastelColor implements Child<PrimaryColor, PastelColor> {
Pink(PrimaryColor.Red), HotPink(PrimaryColor.Red), //
Rockmelon(PrimaryColor.Green), //
SkyBlue(PrimaryColor.Blue), BabyBlue(PrimaryColor.Blue);
final PrimaryColor parent;
private PastelColor(PrimaryColor parent) {
this.parent = parent;
}
public Class<PrimaryColor> getParentClass() {
return PrimaryColor.class;
}
public PrimaryColor getParent() {
return parent;
}
}
enum TransportMedium implements Parent<TransportMedium, Vehicle> {
Land, Sea, Air;
private EnumSet<Vehicle> children;
public Class<Vehicle> getChildClass() {
return Vehicle.class;
}
public EnumSet<Vehicle> getChildren() {
if (children == null) children=ParentUtil.loadChildrenOf(this);
return children;
}
}
enum Vehicle implements Child<TransportMedium, Vehicle> {
Car(TransportMedium.Land), Truck(TransportMedium.Land), //
BigBoat(TransportMedium.Sea), LittleBoat(TransportMedium.Sea), //
JetFighter(TransportMedium.Air), HotAirBaloon(TransportMedium.Air);
private final TransportMedium parent;
private Vehicle(TransportMedium parent) {
this.parent = parent;
}
public Class<TransportMedium> getParentClass() {
return TransportMedium.class;
}
public TransportMedium getParent() {
return parent;
}
}
Прочтите «Java-головоломки» Джошуа Блоха, и вы будете одновременно озарены и испуганы.
URL-адреса исходного кода. Например. вот некоторый законный исходный код Java:
http://google.com
(Да, это было в Java Puzzlers. Я засмеялся ...)
Люди иногда немного удивляются, когда понимают, что можно вызывать частные методы и получать доступ / изменять частные поля с помощью отражения ...
Рассмотрим следующий класс:
public class Foo {
private int bar;
public Foo() {
setBar(17);
}
private void setBar(int bar) {
this.bar=bar;
}
public int getBar() {
return bar;
}
public String toString() {
return "Foo[bar = "+bar+"]";
}
}
Запуск этой программы ...
import java.lang.reflect.*;
public class AccessibleExample {
public static void main(String[] args)
throws NoSuchMethodException,IllegalAccessException, InvocationTargetException, NoSuchFieldException {
Foo foo=new Foo();
System.out.println(foo);
Method method=Foo.class.getDeclaredMethod("setBar", int.class);
method.setAccessible(true);
method.invoke(foo, 42);
System.out.println(foo);
Field field=Foo.class.getDeclaredField("bar");
field.setAccessible(true);
field.set(foo, 23);
System.out.println(foo);
}
}
... даст следующий результат:
Foo[bar=17]
Foo[bar=42]
Foo[bar=23]
Однако вызов setAccessible может быть запрещен менеджером безопасности.
Частичная особенность, частично беспокойство: обработка строк в Java, чтобы он `` казался '' собственным типом (использование операторов на них, +, + =)
Уметь писать:
String s = "A";
s += " String"; // so s == "A String"
очень удобно, но это просто синтаксический сахар для (т.е. компилируется в):
String s = new String("A");
s = new StringBuffer(s).append(" String").toString();
например, создание экземпляра объекта и вызов 2 методов для простой конкатенации. Представьте себе, что вы создаете таким образом длинную строку внутри цикла !? И все методы StringBuffer объявляются синхронизированными. К счастью, в (я думаю) Java 5 они представили StringBuilder, который идентичен StringBuffer без синхронизации.
Цикл, такой как:
String s = "";
for (int i = 0 ; i < 1000 ; ++i)
s += " " + i; // Really an Object instantiation & 3 method invocations!
можно (нужно) переписать в вашем коде как:
StringBuilder buf = new StringBuilder(); // Empty buffer
for (int i = 0 ; i < 1000 ; ++i)
buf.append(' ').append(i); // Cut out the object instantiation & reduce to 2 method invocations
String s = buf.toString();
и будет работать примерно на 80% быстрее, чем исходный цикл! (до 180% в некоторых тестах, которые я проводил)
Литерал "A" на самом деле является java.lang.String, хотя его массив поддерживающих символов выделяется другим способом для динамически создаваемых строк.
Я знал это и испытал. Это происходит потому, что создание и уничтожение объектов - одна из самых дорогих вещей в любом объектно-ориентированном языке. Я слышал однажды ...
Текущие компиляторы будут использовать StringBuilder вместо StringBuffer для конкатенации строк.
Пожалуй, самая удивительная скрытая функция - это класс sun.misc.Unsafe.
http://www.docjar.com/html/api/ClassLib/Common/sun/misc/Unsafe.java.html
Вы можете;
BTW: неправильное использование этого класса убьет JVM. Я не знаю, какие JVM поддерживают этот класс, поэтому его нельзя переносить.
Это не скрытая функция Java, а скрытая функция некоторых конкретных реализаций JVM.
Верно, хотя я не встречал JSE, в котором его нет. Если кто-нибудь знает кого-нибудь, мне было бы интересно.
вы имеете в виду oracle.misc.Unsafe? :) Помню, когда я это обнаружил (заглянул внутрь реализации солнечного jvm AtomicInteger) и был совершенно очарован
Я не видел его под названием oracle. * Думаю, когда-нибудь они смогут переименовать его.
Недавно я использовал его для выделения блока размером 16 ГБ (чего нельзя сделать в Java), для заполнения которого потребовалось 30 секунд. Я действительно мог видеть основную память как новый диск. ;)
Вы можете определить анонимный подкласс и напрямую вызвать для него метод, даже если он не реализует интерфейсы.
new Object() {
void foo(String s) {
System.out.println(s);
}
}.foo("Hello");
@Vuntic - позволяет определить простой класс в том контексте, в котором он нужен.
@Chaos, а почему? Есть реальный пример, где это полезно?
@Wouter - в этом случае метод, который вызывается для анонимного объекта (start()), фактически не определен в подклассе ...
На самом деле, это очень полезная функция, если вы расширяете базовый класс, который выполняет некоторую необходимую настройку, вызывает метод, который вы пишете, а затем выполняет разборку. Начните с того, что вызовите метод в базовом классе (которому я бы, вероятно, не дал то же имя). Есть пример использования (не определение) здесь
Уловка оптимизации, которая упрощает обслуживание вашего кода и делает его менее уязвимым для ошибок параллелизма.
public class Slow {
/** Loop counter; initialized to 0. */
private long i;
public static void main( String args[] ) {
Slow slow = new Slow();
slow.run();
}
private void run() {
while( i++ < 10000000000L )
;
}
}
$ time java Slow
реальный 0m15.397s
$ time java Slow
реальный 0m20.012s
$ time java Slow
реальный 0m18.645s
Среднее: 18.018 с
public class Fast {
/** Loop counter; initialized to 0. */
private long i;
public static void main( String args[] ) {
Fast fast = new Fast();
fast.run();
}
private void run() {
long i = getI();
while( i++ < 10000000000L )
;
setI( i );
}
private long setI( long i ) {
this.i = i;
}
private long getI() {
return this.i;
}
}
$ time java Fast
реальный 0m12.003s
$ time java Fast
реальный 0m9.840s
$ time java Fast
реальный 0m9.686s
Среднее: 10,509 с
Для ссылки на переменную области класса требуется больше байт-кодов, чем на переменную области действия метода. Добавление вызова метода перед критическим циклом добавляет небольшие накладные расходы (и в любом случае вызов может быть встроен компилятором).
Еще одно преимущество этого метода (всегда использующего аксессоры) состоит в том, что он устраняет потенциальную ошибку в классе Медленный. Если бы второй поток постоянно сбрасывал значение я в 0 (например, вызывая slow.setI( 0 )), класс Медленный никогда не смог бы завершить свой цикл. Вызов метода доступа и использование локальной переменной исключает эту возможность.
Протестировано с использованием J2SE 1.6.0_13 в Linux 2.6.27-14.
но Fast - это не то же самое, что Slow: значение члена Fast.i НЕ изменяется после цикла. Если вы вызовете метод run () второй раз, Slow будет намного быстрее (увеличивает "i" только один раз), а Fast будет таким же медленным, как раньше, поскольку "Fast.i" по-прежнему равен нулю.
Вы правы, Карлос. Чтобы обеспечить одинаковое поведение Fast и Slow (в однопоточной среде), переменная экземпляра «i» должна быть обновлена в конце метода «run», что не окажет существенного влияния на производительность.
также получил "странный" результат с использованием System.currentTimeMillis () вокруг вызова для расчета времени выполнения: медленное быстрее, чем быстрое (медленное = 40,6 с, быстрое = 42,9 с) для 1.6.0_13-b03 на WindowsXP
Карлос: Попробуйте выполнить четыре прогона для обоих классов без запуска каких-либо потенциально интенсивных программ (например, антивирусной программы, обновления системы, браузера). Кроме того, выбрасывайте первую пробежку в обоих тестовых залпах. Этот Fast на ~ 2 секунды медленнее, чем Slow, заставляет меня поверить, что что-то мешает бегу. (То есть получение и установка переменной с помощью метода доступа не должно занимать 2 секунды.)
«Преждевременная оптимизация» - это фраза, используемая для описания ситуации, когда программист позволяет соображениям производительности влиять на дизайн фрагмента кода. Это может привести к тому, что дизайн будет не таким чистым, как мог бы, или к некорректному коду, потому что код усложняется оптимизацией, а программист отвлекается на оптимизацию. [ref: en.wikipedia.org/wiki/Optimization_%28computer_science%29]
@jdigital: Я не считаю это преждевременным. Когда методы синхронизированы, это защищает от следующей проблемы: stackoverflow.com/questions/2458217/…
Методы доступа к свойствам Java Bean действительно должны начинаться с нет с «get» и «set».
Даже Джош Блох ошибается в Effective Java.
Ну, конечно, нет, это скорее соглашение о коде. Многие фреймворки / API полагаются на отражение для доступа к вашим свойствам, поэтому отказ от использования get (is) / set просто вызывает проблемы.
Это доказывает мою точку зрения. Использование префиксов get / set взято из API Java Beans и является просто соглашением об именах по умолчанию, а не обязательным. Но авторы плохо спроектированных фреймворков / API, похоже, этого не знают. Они жестко кодируют соглашение об именах, которое классы должны использовать для работы с их фреймворком (а затем имеют наглость сказать, что их фреймворк поддерживает POJO; обязательное соглашение об именовании, по определению, делает их фреймворк не поддерживать POJO).
Я только что (заново) узнал сегодня, что $ - это допустимое имя метода или переменной в Java. В сочетании со статическим импортом он может сделать код немного более читаемым, в зависимости от вашего представления о читаемости:
http://garbagecollected.org/2008/04/06/dollarmaps/
Символ $ также используется для отличия внутренних классов от их классов в большинстве компиляторов.
Вы также можете использовать знаки £ и € в именах переменных. А также любые буквы UNICODE æ, ø, å и т. д.
Уже было упомянул, что последний массив может использоваться для передачи переменной из анонимных внутренних классов.
Другой, возможно, лучший и менее уродливый подход - использовать класс AtomicReference (или AtomicBoolean / AtomicInteger /…) из пакета java.util.concurrent.atomic.
Одним из преимуществ этого является то, что эти классы также предоставляют такие методы, как compareAndSet, которые могут быть полезны, если вы создаете несколько потоков, которые могут изменять одну и ту же переменную.
Еще один полезный родственный шаблон:
final AtomicBoolean dataMsgReceived = new AtomicBoolean(false);
final AtomicReference<Message> message = new AtomicReference<Message>();
withMessageHandler(new MessageHandler() {
public void handleMessage(Message msg) {
if (msg.isData()) {
synchronized (dataMsgReceived) {
message.set(msg);
dataMsgReceived.set(true);
dataMsgReceived.notifyAll();
}
}
}
}, new Interruptible() {
public void run() throws InterruptedException {
synchronized (dataMsgReceived) {
while (!dataMsgReceived.get()) {
dataMsgReceived.wait();
}
}
}
});
В этом конкретном примере мы могли бы просто подождать, пока сообщение не станет ненулевым, однако null часто может быть допустимым значением, и тогда вам нужно использовать отдельный флаг, чтобы завершить ожидание.
waitMessageHandler(…), приведенный выше, является еще одним полезным шаблоном: он где-то устанавливает обработчик, затем запускает выполнение Прерывистый, которое может вызвать исключение, а затем удаляет обработчик в блоке finally, например:
private final AtomicReference<MessageHandler> messageHandler = new AtomicReference<MessageHandler>();
public void withMessageHandler(MessageHandler handler, Interruptible logic) throws InterruptedException {
synchronized (messageHandler) {
try {
messageHandler.set(handler);
logic.run();
} finally {
messageHandler.set(null);
}
}
}
Здесь я предполагаю, что метод handleMessage(…)messageHandler (если он не нулевой) вызывается другим потоком при получении сообщения. messageHandler не должен быть просто типа MessageHandler: таким образом вы будете синхронизироваться с изменяющейся переменной, что явно является ошибкой.
Конечно, это не обязательно должно быть InterruptedException, это может быть что-то вроде IOException или что-то еще, что имеет смысл в конкретном фрагменте кода.
Запятая и массив. Это допустимый синтаксис: String s [] = {
«123»,
«234» ,
};
У меня был разрыв запятой в определенной версии javac, 1.6.0_13 или 17, я думаю
Большинство людей не знают, что они могут клонировать массив.
int[] arr = {1, 2, 3};
int[] arr2 = arr.clone();
Вы можете вызвать в clone на любой Object. Вам просто нужно быть осторожным, чтобы Object реализовал глубокий клон.
@Finbarr: Скорее наоборот. Это мелкий клон; внутренние объекты просто получают еще одну ссылку на них. Самый «простой» способ глубокого клонирования - сериализовать и десериализовать или действительно понять, что вы делаете копию.
@Finbar: нельзя вызывать clone для любого объекта, только для объектов из классов, которые сделали этот метод общедоступным, или из текущего класса.
Используйте StringBuilder вместо StringBuffer, если вам не нужно синхронизированное управление, включенное в StringBuilder. Это повысит производительность вашего приложения.
Улучшения для Java 7 были бы даже лучше, чем любые скрытые функции Java:
Не используйте этот бесконечный синтаксис при создании экземпляра:
Map<String, List<String>> anagrams = new HashMap<String, List<String>>();
// Can now be replaced with this:
Map<String, List<String>> anagrams = new HashMap<>();
Используйте String в переключателе вместо old-C int:
String s = "something";
switch(s) {
case "quux":
processQuux(s);
// fall-through
case "foo":
case "bar":
processFooOrBar(s);
break;
case "baz":
processBaz(s);
// fall-through
default:
processDefault(s);
break;
}
Этот старый код:
static void copy(String src, String dest) throws IOException {
InputStream in = new FileInputStream(src);
try {
OutputStream out = new FileOutputStream(dest);
try {
byte[] buf = new byte[8 * 1024];
int n;
while ((n = in.read(buf)) >= 0)
out.write(buf, 0, n);
} finally {
out.close();
}
} finally {
in.close();
}
}
теперь его можно заменить более простым кодом:
static void copy(String src, String dest) throws IOException {
try (InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dest)) {
byte[] buf = new byte[8192];
int n;
while ((n = in.read(buf)) >= 0)
out.write(buf, 0, n);
}
}
Вы имеете в виду StringBuilder вместо StringWriter? API для StringBuffer и StringBuilder одинаковы, однако использование StringWriter потребует некоторых изменений кода.
На самом деле он имел в виду StringBuilder, который не является поточно-ориентированным, но более быстрым. StringBuffer является потокобезопасным, но медленнее. Следует использовать только в том случае, если вам нужна потокобезопасность при создании строкового буфера.
Вы можете переключить (это) внутри определений методов классов перечисления. Заставил меня крикнуть "ах!" громко, когда я обнаружил, что это действительно работает.
Да, переключатель работает с выражениями перечисления в целом, а не только внутри классов перечисления.
На самом деле, что мне нравится в Java, так это то, как мало там скрытых уловок. Это очень очевидный язык. Настолько, что по прошествии 15 лет почти все, о которых я могу думать, уже перечислены на этих нескольких страницах.
Возможно, большинство людей знают, что Collections.synchronizedList () добавляет синхронизацию в список. Чего вы не можете знать, если не прочитаете документацию, так это того, что вы можете безопасно выполнять итерацию по элементам этого списка, синхронизируя сам объект списка.
CopyOnWriteArrayList может быть неизвестен некоторым, а Future представляет интересный способ абстрактного многопоточного доступа к результатам.
Вы можете подключаться к виртуальным машинам (локальным или удаленным), получать информацию об активности сборщика мусора, использовании памяти, файловых дескрипторах и даже размерах объектов с помощью различных API-интерфейсов управления, агентов и подключения.
Хотя TimeUnit, возможно, лучше, чем long, я предпочитаю класс Duration от Wicket.
О, я чуть не забыл эту маленькую жемчужину. Попробуйте это на любом запущенном java-процессе:
jmap -histo: live PID
Вы получите гистограмму живых объектов кучи в данной виртуальной машине. Бесценен как быстрый способ определить некоторые виды утечек памяти. Другой метод, который я использую для их предотвращения, - это создание и использование подклассов с ограниченным размером всех классов коллекций. Это приводит к быстрым сбоям в неконтролируемых коллекциях, которые легко идентифицировать.
Функция, с помощью которой вы можете отображать заставки для консольных приложений Java.
Используйте инструмент командной строки java или javaw с параметром -splash.
например:
java -splash:C:\myfolder\myimage.png -classpath myjarfile.jar com.my.package.MyClass
содержимое C:\myfolder\myimage.png будет отображаться в центре экрана всякий раз, когда вы выполняете класс "com.my.package.MyClass"
Это действительно скрыто ... не могу найти параметр в документации для команды java (java.exe). (но это в справочном сообщении или в javadoc SplashScreen)
При работе в Swing мне нравится скрытая функция Ctrl - Shift - F1.
Делает дамп дерева компонентов текущего окна. (Предполагая, что вы не связали это нажатие клавиши с чем-то другим.)
Скорее всего, ваш оконный менеджер что-то связано с этим ключом. Gnome не привязывается к нему, поэтому я предполагаю, что вы используете KDE, который привязывает его к «переключению на рабочий стол 13». Вы можете изменить его, перейдя в Панель управления, Региональные стандарты, Сочетания клавиш и удалив сопоставление для Shift-Ctrl-F1.
Меня удивляет, что интерфейс может расширять несколько интерфейсов, но класс может расширять только один класс.
Не совсем. Никогда не возникает проблем с тем, «какая реализация суперкласса должна быть вызвана», поскольку нет реализаций.
Как бы вы расширили 2 класса с одинаковым методом? Какой метод войдет в окончательную реализацию?
На самом деле возможно множественное наследование @archer. В этом случае, как вы указываете, мы можем просто отклонить это или использовать один из методов разрешения конфликтов имен.
Подстановочные знаки пути к классам начиная с Java 6.
java -classpath ./lib/* so.Main
Вместо
java -classpath ./lib/log4j.jar:./lib/commons-codec.jar:./lib/commons-httpclient.jar:./lib/commons-collections.jar:./lib/myApp.jar so.Main
См. http://java.sun.com/javase/6/docs/technotes/tools/windows/classpath.html
Хуки выключения. Это позволяет зарегистрировать поток, который будет создан немедленно, но запустится только после завершения JVM! Так что это своего рода «глобальный финализатор jvm», и вы можете делать полезные вещи в этом потоке (например, отключать ресурсы Java, такие как встроенный сервер hsqldb). Это работает с System.exit () или с CTRL-C / kill -15 (но, конечно, не с kill -9 в unix).
Более того, это довольно просто настроить.
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
endApp();
}
});;
Они великолепны! Вы также можете отменить регистрацию их (если вы сохраните ссылку), чтобы вы могли хорошо очистить ресурсы. Я использую их - в сочетании с обратными вызовами жизненного цикла Spring, особенно атрибутом destroy-method - для отключения подпроцессов рабочих.
Обратите внимание, что перехватчики завершения работы не выполняются, если вызывается Runtime.halt ().
Перехватчики завершения работы не вызываются в Windows, если javaw используется для запуска приложения и инициируется выход из системы Windows. Должно быть консольное окно. Вздох. Я пытался использовать это, чтобы отметить меня как «вне офиса» на нашем веб-сайте, и мне пришлось жить с минимизированным окном консоли.
Идентификаторы могут содержать символы иностранного языка, например умляуты:
вместо написания:
String title = "";
кто-то мог написать:
String Überschrift = "";
и вы можете получить к нему доступ как \u00dcberschrift = "OK";
На мой взгляд, это не лучший стиль. Вы знаете, что я имею в виду, если вам когда-либо приходилось работать с кодом с комментариями и идентификаторами на языке, который вы не понимаете. Код не должен быть локализован.
Проблема не столько в том, что в любом проекте реального размера будут разработчики, говорящие на разных языках ... Проблема в том, что в любом проекте реального размера должен представляет собой смесь Windows, а текстовые файлы Unx (включая OS X) и сочетание разных IDE с разными настройками. Объедините это с факт, что * .java не имеют метаданных, описывающих их кодировки файлов. ни хороших спецификаций (спецификации допускают любую кодировку символов), и у вас есть свой рецепт катастрофы. Программисты Java, использующие символы, отличные от ASCII, внутри идентификаторов String of in, заслуживают смерти.
Строки, отличные от ASCII, должны быть экстернализованы, а сценарии сборки и / или верификаторы перед фиксацией должны быть сконфигурированы так, чтобы сразу же отказываться при обнаружении такой бессмысленной бессмыслицы. Наши сценарии сборки Ant потерпят неудачу и поставят любого разработчика, пытающегося это сделать, на стену стыда.
Java 6 (от Sun) поставляется со встроенным интерпретатором JavaScrip.
http://java.sun.com/javase/6/docs/technotes/guides/scripting/programmer_guide/index.html#jsengine
Пример использования: java2s.com/Code/Java/JDK-6/…
Не читал об этом
Integer a = 1;
Integer b = 1;
Integer c = new Integer(1);
Integer d = new Integer(1);
Integer e = 128;
Integer f = 128;
assertTrue (a == b); // again: this is true!
assertFalse(e == f); // again: this is false!
assertFalse(c == d); // again: this is false!
узнайте больше об этом, выполнив поиск в пуле целых чисел Java (внутренний «кеш» от -128 до 127 для автобокса) или загляните в Integer.valueOf
Точный предел зависит от реализации (имеет только некоторый минимальный размер), поэтому на некоторых виртуальных машинах второй тест тоже может быть верным.
Верно ли говорить, что третий тест будет ложный независимо от ВМ?
@Pacerier, если честно. Точно сказать не могу. но это сработало в то время, когда я публиковал это ... просто не зависит от оператора == ...
Итак, они используют указатель для хранения небольших целочисленных значений, не так ли?
Это указано в Спецификации языка Java для диапазона [-127,127]. Что касается других целых чисел, я не уверен
Вы можете переопределить метод и вызвать его конструктор суперкласса (это может стать сюрпризом для программистов на C++).
Это может привести к множеству проблем. Учтите это: класс B расширяет класс A и переопределяет метод doSomething (), который вызывается конструктором A. Теперь конструкторы вызываются в базовом -> производном порядке, но конструктор A вызывает функцию doSomething () B. B инициализирован?
Вот мой список.
Моя любимая (и самая страшная) скрытая функция - это то, что вы можете генерировать проверенные исключения из методов, которые ничего не объявляют.
import java.rmi.RemoteException;
class Thrower {
public static void spit(final Throwable exception) {
class EvilThrower<T extends Throwable> {
@SuppressWarnings("unchecked")
private void sneakyThrow(Throwable exception) throws T {
throw (T) exception;
}
}
new EvilThrower<RuntimeException>().sneakyThrow(exception);
}
}
public class ThrowerSample {
public static void main( String[] args ) {
Thrower.spit(new RemoteException("go unchecked!"));
}
}
Также вам может быть интересно узнать, что вы можете выбросить 'null' ...
public static void main(String[] args) {
throw null;
}
Угадайте, что это печатает:
Long value = new Long(0);
System.out.println(value.equals(0));
И угадайте, что это вернет:
public int returnSomething() {
try {
throw new RuntimeException("foo!");
} finally {
return 0;
}
}
вышесказанное не должно удивлять хороших разработчиков.
В Java вы можете объявить массив следующими допустимыми способами:
String[] strings = new String[] { "foo", "bar" };
// the above is equivalent to the following:
String[] strings = { "foo", "bar" };
Итак, следующий код Java вполне допустим:
public class Foo {
public void doSomething(String[] arg) {}
public void example() {
String[] strings = { "foo", "bar" };
doSomething(strings);
}
}
Есть ли веская причина, по которой следующий код не должен быть действительным?
public class Foo {
public void doSomething(String[] arg) {}
public void example() {
doSomething({ "foo", "bar" });
}
}
Я думаю, что приведенный выше синтаксис мог бы заменить varargs, введенный в Java 5. И более согласованный с ранее разрешенными объявлениями массивов.
Веская причина в том, что компилятор не может определить тип массива. Но хороший список.
Разве doSomething ({"", ""}) не будет поддерживаться простыми замыканиями в Java 7?
относительно вашего комментария о "хорошие разработчики" и попробуй / наконец головоломке плохого стиля, которая-никогда-не-в-дикой природе ... Что ж, у хороших разработчиков есть IDE, которая будет предупреждать их в реальном времени, даже при неполном AST, что "такие операторы возврата (внутри блоков finally) могут маскировать выброшенное исключение" . Кто сейчас плохие разработчики, использующие второстепенные IDE? ;)
@ SyntaxT3rr0r: Хорошие разработчики - это те, кто знает больше, чем IDE, которую они используют, может обнаружить, поскольку большинство ошибок логики / кодирования не обнаруживаются IDE.
throw null должен дать вам NullPointerException во время выполнения.
Каждый файл класса начинается с шестнадцатеричного значения 0xCAFEBABE, чтобы идентифицировать его как действительный байт-код JVM.
Вы можете добавить проверки общих типов во время выполнения с помощью объекта Class<T>, это удобно, когда класс создается где-то в файле конфигурации и нет возможности добавить проверку времени компиляции для универсального типа класса. Вы не хотите, чтобы класс взорвался во время выполнения, если приложение неправильно настроено, и вы не хотите, чтобы все ваши классы были пронизаны экземплярами проверок.
public interface SomeInterface {
void doSomething(Object o);
}
public abstract class RuntimeCheckingTemplate<T> {
private Class<T> clazz;
protected RuntimeChecking(Class<T> clazz) {
this.clazz = clazz;
}
public void doSomething(Object o) {
if (clazz.isInstance(o)) {
doSomethingWithGeneric(clazz.cast(o));
} else {
// log it, do something by default, throw an exception, etc.
}
}
protected abstract void doSomethingWithGeneric(T t);
}
public class ClassThatWorksWithStrings extends RuntimeCheckingTemplate<String> {
public ClassThatWorksWithStrings() {
super(String.class);
}
protected abstract void doSomethingWithGeneric(T t) {
// Do something with the generic and know that a runtime exception won't occur
// because of a wrong type
}
}
Я был удивлен, когда впервые заметил тернарный оператор, равный простому выражению if-then-else:
minVal = (a < b) ? a : b;
IMHO это не скрыто и не удивительно, большинство языков, которые я знаю, имеют это (C, C++, C#, PHP, Perl, Javascript, ...)
Я знаю это сейчас, но несколько недель назад я не думал о таком операторе. Наверное, потому, что я не слишком опытен, но мой разум был несколько взорван: D
Я согласен с @dbermerlin. Это НЕ скрытая функция. Это очень распространено.
Я могу добавить объект сканера. Лучше всего подходит для разбора.
String input = "1 fish 2 fish red fish blue fish";
Scanner s = new Scanner(input).useDelimiter("\s*fish\s*");
System.out.println(s.nextInt());
System.out.println(s.nextInt());
System.out.println(s.next());
System.out.println(s.next());
s.close();
На мой взгляд, это не скрытая особенность. Сканер - это библиотека.
Вы можете определять и вызывать методы анонимных внутренних классов.
Ну, они не так уж и скрыты, но мало кто знает, что с их помощью можно определить новый метод в классе и вызвать его следующим образом:
(new Object() {
public String someMethod(){
return "some value";
}
}).someMethod();
Вероятно, это не очень распространено, потому что это тоже не очень полезно, вы можете вызвать метод Только, когда вы его определяете (или через отражение)
Немного похоже на шаблон модуля JavaScript;)
На самом деле это не скрытая функция, но я очень удивился, когда увидел этот скомпилированный штраф:
public int aMethod(){
http://www.google.com
return 1;
}
причина, по которой он компилируется, заключается в том, что строка http://www.google.com в части «http:» обрабатывается компилятором как метка, а остальная часть строки является комментарием.
Итак, если вы хотите написать какой-то причудливый код (или запутанный код), просто поместите туда много http-адресов. ;-)
но старше года. См. Этот ответ Дэвида Пламптона от 12 мая 2009 г .: stackoverflow.com/questions/15496/hidden-features-of-java/… (и он получил только 2 голоса за ...)
Это повторяющийся ответ
... и он работает максимум с одним http-адресом для каждого метода, поскольку метки должны быть уникальными.
Функция printf () в стиле C :)
System.out.printf("%d %f %.4f", 3,Math.E,Math.E);
Выход: 3 2,718282 2,7183
Двоичный поиск (и его возвращаемое значение)
int[] q = new int[] { 1,3,4,5};
int position = Arrays.binarySearch(q, 2);
Как и в C#, если «2» не найден в массиве, он возвращает отрицательное значение, но если вы возьмете дополнение 1 возвращаемого значения, вы фактически получите позицию, в которую можно вставить «2».
В приведенном выше примере позиция = -2, ~ позиция = 1, которая является позицией, в которую следует вставить 2 ... она также позволяет вам найти «ближайшее» совпадение в массиве.
Думаю, это здорово ... :)
printf не скрыт, как и работа binarySearch.
Ни одна из функций, упомянутых в предыдущих ответах, не является точно «скрытой». Большинство из них просто «относительно неизвестны» обычному программисту Java. По крайней мере, я думал, что это был вопрос ...
Не так уж и скрыто, но интересно.
У вас может быть «Hello, world» без основного метода (это вызывает мысль NoSuchMethodError)
Первоначально опубликовано RusselW на Самая странная языковая функция
public class WithoutMain {
static {
System.out.println("Look ma, no main!!");
System.exit(0);
}
}
$ java WithoutMain
Look ma, no main!!
Добавьте System.exit(0);, чтобы подавить это ужасное исключение ...
Это простое злоупотребление статической инициализацией, я бы ничего не стал рассматривать как особенность ...
Обратите внимание, что использование этих скрытых функций не всегда является хорошей идеей; часто они удивляют и сбивают с толку других, читающих ваш код.