Скрытые возможности Java
После прочтения скрытых возможностей C# я задумался: каковы некоторые из скрытых возможностей Java?
100 ответов
Инициализация двойной скобки застала меня врасплох несколько месяцев назад, когда я впервые обнаружил это, никогда не слышал об этом раньше.
ThreadLocals обычно не так широко известны как способ хранения состояния для каждого потока.
Поскольку в Java JDK 1.5 реализованы чрезвычайно надежные и надежные инструменты параллелизма, помимо просто блокировок, они живут в java.util.concurrent, и особенно интересным примером является подпакет java.util.concurrent.atomic, который содержит поточно-ориентированные примитивы, которые реализуют сравнение. -и- операции подкачки и могут отображаться на реальные версии этих операций с аппаратной поддержкой.
Совместное объединение в дисперсии параметра типа:
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);
}
Этот придуманный метод возвращает значение true, если две заданные коллекции равны или если одна из них содержит данный элемент, в противном случае - false. Следует отметить, что вы можете вызывать методы как Comparable, так и Collection для аргументов b1 и b2.
Я был удивлен инициализаторами экземпляра на днях. Я удалял несколько свернутых в код методов и в итоге создал несколько инициализаторов экземпляров:
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");
}};
JDK 1.6_07+ содержит приложение под названием VisualVM (bin/jvisualvm.exe), которое представляет собой приятный графический интерфейс поверх многих инструментов. Это кажется более полным, чем JConsole.
Подстановочные знаки Classpath начиная с 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
Для большинства людей, у которых я беру интервью на должности разработчиков 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
в яве это просто ключевое слово?:)
Как насчет ковариантных типов возвращаемых данных, которые существуют с JDK 1.5? Это довольно плохо публикуется, так как это несексуальное дополнение, но, насколько я понимаю, абсолютно необходимо для работы дженериков.
По сути, компилятор теперь позволяет подклассу сужать возвращаемый тип переопределенного метода, чтобы он был подклассом возвращаемого типа исходного метода. Так что это разрешено:
class Souper {
Collection<String> values() {
...
}
}
class ThreadSafeSortedSub extends Souper {
@Override
ConcurrentSkipListSet<String> values() {
...
}
}
Вы можете позвонить в подкласс values
метод и получить отсортированный поток безопасно Set
из String
s без необходимости бросать на ConcurrentSkipListSet
,
Передача контроля в блоке 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
Никто не упомянул, что instanceof реализован таким образом, что проверка на нулевое значение не требуется.
Вместо:
if( null != aObject && aObject instanceof String )
{
...
}
просто используйте:
if( aObject instanceof String )
{
...
}
Разрешение методов и конструкторов в перечислениях удивило меня. Например:
enum Cats {
FELIX(2), SHEEBA(3), RUFUS(7);
private int mAge;
Cats(int age) {
mAge = age;
}
public int getAge() {
return mAge;
}
}
Вы даже можете иметь "постоянное тело определенного класса", которое позволяет определенному значению перечисления переопределять методы.
Больше документации здесь.
Параметры типа для универсальных методов могут быть указаны явно следующим образом:
Collections.<String,Integer>emptyMap()
Вы можете использовать перечисления для реализации интерфейса.
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 1.5, Java теперь имеет гораздо более чистый синтаксис для написания функций переменной арности. Итак, вместо того, чтобы просто передать массив, теперь вы можете сделать следующее
public void foo(String... bars) {
for (String bar: bars)
System.out.println(bar);
}
бары автоматически преобразуются в массив указанного типа. Не огромная победа, но, тем не менее, победа.
Мое любимое: выгрузить все трассы стека потоков в стандартный вывод.
Windows: CTRL-Разбить в вашем окне Java CMD / консоли
UNIX: kill -3 PID
Несколько человек написали об инициализаторах экземпляров, вот хорошее применение для этого:
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");
}
});
}});
}});
}};
Динамические прокси (добавлены в 1.3) позволяют вам определять новый тип во время выполнения, который соответствует интерфейсу. Это пригодилось удивительное количество раз.
окончательная инициализация может быть отложена.
Это гарантирует, что даже при сложном потоке логики возвращаемые значения всегда устанавливаются. Слишком легко пропустить случай и вернуть ноль случайно. Это не делает возврат нуля невозможным, просто очевидно, что это специально:
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;
}
Я думаю, что еще одна "упущенная" особенность Java - сама JVM. Это, вероятно, лучшая доступная виртуальная машина. И он поддерживает много интересных и полезных языков (Jython, JRuby, Scala, Groovy). Все эти языки могут легко и без проблем сотрудничать.
Если вы разрабатываете новый язык (как в случае с scala), вы сразу же получаете все имеющиеся библиотеки, и поэтому ваш язык "полезен" с самого начала.
Все эти языки используют оптимизацию HotSpot. ВМ очень хорошо контролирует и отлаживается.
Вы можете определить анонимный подкласс и напрямую вызвать метод для него, даже если он не реализует интерфейсы.
new Object() {
void foo(String s) {
System.out.println(s);
}
}.foo("Hello");
Метод asList в java.util.Arrays
допускает хорошее сочетание varargs, общих методов и автобокса:
List<Integer> ints = Arrays.asList(1,2,3);
Использование этого ключевого слова для доступа к полям / методам, содержащим класс из внутреннего класса. В приведенном ниже довольно надуманном примере мы хотим использовать поле 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;
}
}
Не совсем особенность, но забавный трюк, который я недавно обнаружил на одной веб-странице:
class Example
{
public static void main(String[] args)
{
System.out.println("Hello World!");
http://Phi.Lho.free.fr
System.exit(0);
}
}
является допустимой программой Java (хотя она генерирует предупреждение). Если вы не понимаете почему, посмотрите ответ Грегори!;-) Ну и подсветка синтаксиса здесь тоже подсказывает!
Это не совсем "скрытые функции" и не очень полезно, но может быть чрезвычайно интересно в некоторых случаях:
Класс 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);
}
};
}
При работе в Swing мне нравится скрытая функция Ctrl - Shift - F1.
Он сбрасывает дерево компонентов текущего окна.
(Предполагая, что вы не связали это нажатие клавиши с чем-то другим.)
Каждый файл класса начинается с шестнадцатеричного значения 0xCAFEBABE, чтобы идентифицировать его как действительный байт-код JVM.
( Объяснение)
Я голосую за java.util.concurrent с его параллельными коллекциями и гибкими исполнителями, позволяющими среди прочих пулов потоков, запланированных и скоординированных задач. DelayQueue - мой личный фаворит, где элементы становятся доступными после указанной задержки.
java.util.Timer и TimerTask можно смело положить в покой.
Кроме того, не совсем скрыто, но в другой упаковке, чем другие классы, связанные с датой и временем. java.util.concurrent.TimeUnit полезен при преобразовании между наносекундами, микросекундами, миллисекундами и секундами.
Он читается намного лучше, чем обычный someValue * 1000 или someValue / 1000.
На самом деле не является частью языка Java, но дизассемблер javap, поставляемый с Sun JDK, широко не известен и не используется.
Добавление конструкции для каждого цикла в 1.5. Я <3 это.
// 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;
}
Я лично обнаружил java.lang.Void
очень поздно - улучшает читабельность кода в сочетании с обобщениями, например Callable<Void>