Простые задачи на Java. Слабо решить все?



    Привет! Мы тут собрали тусовку одних из самых крутых русскоязычных Java-практиков и попросили их дать по задаче, чтобы вы сломали зубы, мозг и бились об стену, пытаясь понять, как это работает. Собственно, мы поспорили на бутылку Хеннеси, что за 12 часов после публикации никто не пришлёт все правильные ответы. Я уверен, что кто-то сможет. Поэтому если вы это сделаете первым – с меня бутылка.

    Первая задача простая, она от телезрителя Николая Гарбузова, специалиста по скалкам, любящего рекурсию, паттерн-матчинг и магию компиляции:
    Скомпилируется ли следующий аспект AJC компилятором?
    Если да — то что он выведет на консоль при компиляции?

    public aspect QuizAspect {
        public static int count(int i) {
            return i++;
        }
    
        before (int n) : execution(public int QuizAspect.count(int)) 
                && args(n) && if(QuizAspect.count(1)>1) {
            System.out.println("QuizAspect " + n);
        }
    }
    


    Пока просто, правда?

    Вторая задача от Владимира Ситникова (NetCracker), грязного извращенца во всём, что касается регулярных выражений. Этот нехороший человек даже анонс своего доклада на JPoint написал с их помощью.

    В чём подвох удалять Java-комментарии таким выражением? Укажите 3 причины, почему так делать нельзя. (считаем, что исходник написан нормальными символами) —
    Pattern.compile("/\\*(?:[^*]|\\*[^/])*\\*/")
    


    И сразу вторая задача — можно ли написать «hello world» на java без единого пробела?


    Третью задачу прислал телезритель Николай Алименков из клуба анонимных разработчиков. 10 лет он пилит свои масштабируемые системы, а к нам пришёл отдохнуть, поэтому задача одна из самых простых:

    Есть 2 Spring контекста:

    1. a.xml с бином

    <util:list id="myList">        
        <value>3</value>        
        <value>4</value>    
    </util:list>


    2. b.xml с бином

    <util:list id="myList">
        <value>6</value>
    </util:list>
    


    Что напечатает такой фрагмент кода:

    System.out.println(new ClassPathXmlApplicationContext("a.xml", "b.xml").getBean("myList"));
    


    И как можно заставить его бросить ошибку, не изменяя логику работы кода?


    Четвёртая задача предоставлена Никитой Сальников-Тарновским, конкретно упоровшимся по хардкору и написавшим инструмент для поиска утечек памяти – Plumbr. Писал не один, конечно, но прочитать полученный код может только он. Долбанный оптимизатор.

    Ниже приведены 2 программы. Каждая из них пытается аллоцировать суммарно памяти больше размера хипа. Но одна из них выкидывает java.lang.OutOfMemoryError, а вторая нет. Почему?

    public class OOM1 {
        private static final int SIZE = (int) (Runtime.getRuntime().maxMemory() * 0.55);
    
        public static void main(String[] args) {
            {
                byte[] bytes = new byte[SIZE];
                System.out.println(bytes.length);
            }
    
            byte[] bytes1 = new byte[SIZE];
            System.out.println(bytes1.length);
    
            System.out.println("I allocated memory successfully");
        }
    }
    
    public class OOM2 {
        private static final int SIZE = (int) (Runtime.getRuntime().maxMemory() * 0.35);
    
        public static void main(String[] args) {
            {
                byte[] bytes = new byte[SIZE];
                System.out.println(bytes.length);
            }
    
            byte[] bytes1 = new byte[SIZE];
            System.out.println(bytes1.length);
    
            byte[] bytes2 = new byte[SIZE];
            System.out.println(bytes2.length);
    
        System.out.println("I allocated memory successfully");
        }
    }
    



    Предпоследняя задача от Баруха Садогурского из JFrog и тусовки Bintray/Artifactory

    Что выведет этот код?

    def back = 'back'
    def quotes = ["I'll be $back",
    "I'll be ${-> back}",
    "I'll be ${back}",
    "I'll be "+back]
    println quotes
    back = 'bach'
    println quotes
    


    Последняя – от Евгения Борисова, тренера офицеров израильской армии по Java. Spring:

    Есть два бина:
    @Component
    public class Няня {
        public void closeAll() {
            while (ребёнокГрязный()) {
                купайРебёнка();
            }
        }
    }
    
    @Component
    public class Уборщица {
        public void closeAll() {
            while (посудаГрязная()) {
                мойПосуду();
            }
        }
    }
    

    Как сделать, чтобы при закрытия контекста, оба метода closeAll работали параллельно (и какие есть варианты)?


    Ачивки и решения


    • Небольшой сувенир первому, кто запостит три правильных решения под спойлер в комментарии.
    • Бутылка Хеннеси – тому, кто сможет за 12 часов правильно ответить на все вопросы за один раз. Если таких будет несколько – первому, приславшему правильные ответы.
    • Бесплатные билеты для вас и друга на конференцию JPoint в понедельник тому, кто сможет поправить или существенно доуточнить ответы тех, кто задавал задачи.


    Ответы, пожалуйста, в комментарии под спойлер. Если вы не можете комментировать, то делайте #jpoint в Facebook или ВКонтакте.

    Вот ответы на первые четыре вопроса, а после JPoint мы опубликуем и ответы на вопросы jbaruch и EvgenyBorisov.

    P.S.: подсказки можно поискать в анонсах соответствующих докладов на сайте JPoint.
    JUG.ru Group 1 299,48
    Конференции для взрослых. Java, .NET, JS и др. 18+
    Поделиться публикацией
    Комментарии 57
    • 0
      Правильные ответы 23derevo выложит не все.
      Поскольку и задачка по Груви от меня и задачка по Спрингу от Борисова являются неотъемлемой частью наших докладов, за правильными ответами на них придется посетить наши доклады.
      • +2
        Т.е на них ответы сюда не писать лучше? А то некрасиво получится)
        • 0
          Пишите все, но под спойлер!
          • +2
            Барух что то слишком простую задачку приложил) jbaruch, самые крутые на JPoint оставил?
            • +2
              Ты тут не умничай!
              • +1
                Простую и не на Java. Несносный groovyист.
                • 0
                  И не говори. Что за люди?!
            • +1
              Пиши под спойлер, и мы никому не скажем, что ты прав :)
              • 0
                Ну а чего писать ) И так же ясно что
                Заголовок спойлера
                только кложур заэвалуэйтится повторно :)

                Даже у какого то телезрителя Алименкова задача сложнее! (
                • +1
                  Возникает закономерный вопрос
                  А сколько там кложуров?
                  • 0
                    some groovy
                    Кложур там только в списке только в 1 месте, AST proof

                    А почему так, вкратце описано на относительно новом няшном груви-сайте

                    Ну в а сорцах, в groovy.lang.GString#writeTo можно посмотреть что будет, если там будет кложур, принимащий одну переменую
                  • 0
                    Да сразу понятно же чем троллить будешь! В этом и беда)
                    • 0
                      Ну, это тебе понятно. Ты не забывай, это идет на jpoint, не на какой нибудь g8conf.
            • 0
              Все, но ваши — после конфы :)
            • +4
              Без единого разрыва
              package\u0020ru.nullpointer.jugtest;
              public
              class
              Hello{
              public
              static
              void
              main(String
              args[]){
              System.out.println("Hello\u0020world");
              }
              }
              

              • +1
                Вторая задача от Владимира Ситникова (NetCracker)
                Регулярка не учитывает /* */ в стрингах

                Написать Hello world без пробелов можно с помощью
                System.out.println("Hello"+(char)32+"World");
                • 0
                  Ваш Hello world не компилируется.
                  • 0
                    Естественно имелось в виду это
                    package
                    cz;
                    public
                    class
                    NoSpace
                    {
                    public
                    static
                    void
                    main(String[]
                    args)
                    {
                    System.out.println("Hello"+(char)32+"World");
                    }
                    }
                    

                • +2
                  Регулярки - зло
                  package ru.nullpointer.jugtest;
                  
                  import java.util.regex.Pattern;
                  
                  public class CommentCleaner {
                  
                      public static void main(String args[]) {
                          String source = "/***/";
                          Pattern p = Pattern.compile("/\\*(?:[^*]|\\*[^/])*\\*/");
                  
                          String result = p.matcher(source).replaceAll("");
                          if (!result.isEmpty()) {
                              throw new OHSHIException();
                          }
                      }
                  
                      static class OHSHIException extends RuntimeException {
                  
                          OHSHIException() {
                              super("OH SHI--");
                          }
                      };
                  }
                  
                  

                  • +7
                    Не могу удержаться -- задача №4
                    Очевидно, что java переиспользует слоты переменных на стеке (в регистрах), когда можно понять, что переменная дальше по коду функции не используется. В обоих случаях аллоцированные массивы перестают быть нужными сразу после вычисления их длины для передачи в println() — поэтому, в идеале, в обоих случаях максимальное необходимое количество памяти не превышает размера одного массива. Почему в одном случае все-таки вылетает исключение?
                    Я могу предположить, что есть некоторое количество слотов на стеке, ниже которого нет смысла оптимизировать (тем более, что main вызывается лишь однажды, поэтому о реальной оптимизации C1/C2 речь, скорее всего, не идет — почти наверняка тут мы работаем в интерпретируемом режиме). Методом научного тыка можно обнаружить, что вот такой код
                    public static void main( final String[] args ) {
                    		{
                    			final byte[] bytes = new byte[SIZE];
                    			System.out.println( bytes.length );
                    		}
                    
                    		final byte[] a = {}; // <=== !!!
                    
                    		final byte[] bytes = new byte[SIZE];
                    		System.out.println( bytes.length );
                    
                    		System.out.println( "I allocated memory successfully" );
                    	}
                    

                    выполняется без ошибок. В то время, как вот такой
                    public static void main( final String[] args ) {
                    		final byte[] a = {};
                    		{
                    			final byte[] bytes = new byte[SIZE];
                    			System.out.println( bytes.length );
                    		}
                    
                    
                    
                    		final byte[] bytes = new byte[SIZE];
                    		System.out.println( bytes.length );
                    
                    		System.out.println( "I allocated memory successfully" );
                    	}
                    

                    Падает с OOM.

                    Отсюда можно предположить, что на моей версии джавы (1.6.0_45), во видимому, два последних (то есть последних используемых) слота на стеке java не трогает, но если их становится больше, то делаются какие-то попытки переиспользовать предыдущие.

                    P.S. Любопытно, что вот такой код
                    public static void main( final String[] args ) {
                    		{
                    			final byte[] bytes = new byte[SIZE];
                    			System.out.println( bytes.length );
                    		}                       
                    		int a = 0;   // <== constant
                    
                    		final byte[] bytes = new byte[SIZE];
                    		System.out.println( bytes.length );
                    
                    		System.out.println( "I allocated memory successfully" );
                    	}
                    

                    Падает с OOM, но если заменить a= 0; на a=args.length — то выполняется успешно. Опять же, можно предположить, что константная переменная просто заменяется на свое значение, без реального размещения ее на стеке, а переменная с хоть сколь либо динамическим значением все-таки размещается — и вытесняет со стека размещенный там byte[]
                    • 0
                      грязный, грязный извращенец!
                      • +1
                        Ну интересно же было разобраться! А потом уж что, сидеть на этом знании как Кащей над злате? ;)
                        • +1
                          К слову, если добавить достаточно циклов, чтобы ворвался компилятор, тоже много чего интересного начинается ;)
                            • 0
                              а мы с Лёшей сейчас это обсудим!
                              • 0
                                Конечно, реклама для лохов, но Лёшу зачем с «будущим Java» вместе поставили? Придётся, видимо, Лёшей пожертвовать в угоду будущему.
                                • 0
                                  когда программа хорошая (а она хорошая!), все время кто-то интересный тебе идет параллельно с кем-то другим, тоже интересным.

                                  Скажи лучше, есть ли слоты, в которые тебе не на что пойти?
                                  • 0
                                    На 13:50-14:40 я бы всем пожертвовал. Можно про профилирование послушать, но опционально. Скорее всего пойду в экспертную зону №1, может, там полезнее будет.

                                    На 12:50-13:40 в принципе можно бы было всё пропустить, если б было что-то из вечерних докладов в параллели (Куксенко или Чуйко, например: оба интересны и тоже одновременно идут). Катехизис прекрасен, я не спорю, но я смотрел его на ютюбе и Алексей пишет, что будет почти то же самое. А «Сжимай меня полностью», как я понимаю, новый доклад (поправьте, если ошибаюсь), его жалко. Возможно, Фолькера послушаю. Ну или уж Алексея уважу, если места хватит :-)

                                    И да, на 10:30-11:20 я бы тоже пропустил, если б в параллель шёл почти любой из остальных докладов :-)
                                    • 0
                                      ты как-то очень сложно все расписал: "… я бы пропустил, если б… ". Я в итоге ничего не понял :)
                                      • 0
                                        легче написать на что ты хочешь пойти. Потому что по комментарию похоже, что ты готов не пойти никуда.
                                        • +3
                                          Ага, а деньги я печатаю, и мне ничего не стоит прилететь из Новосиба в Москву и оплатить участие ради того, чтобы пойти никуда =)

                                          Если интересно, то вот куда я хочу в порядке убывания приоритета:

                                          • Круглый стол. Будущее Java-платформы
                                          • Сжимай меня полностью
                                          • CompletableFuture уже здесь
                                          • Тайны — в наших головах, а не в JVM
                                          • Где моя память, чувак?!
                                          • Круглый стол. HighLoad
                                          • Железные счётчики на страже производительности

                                          Если бы эти семь не пересекались, я был бы счастлив. Дальше так:

                                          • Лучший отладчик — сделанный своими руками
                                          • Катехизис java.lang.String
                                          • Packed Objects, Object Layout & Value Types — a Survey
                                          • Непрерывное профилирование Java-приложений в ходе эксплуатации
                                          • Выражаемся регулярно
                                          • Круглый стол. Рефакторинги и технический долг
                                          • Круглый стол. Рабочие инструменты Java-разработчика

                                          Твои доклады в топ-14 не попали, извиняй =)
                                          • +2
                                            всё ок, мне достаточно того, что они у меня получаются лучшими докладами всех jpoint и joker конференций. Но не для всех, да :-)
                              • +1
                                Ну да, я сначала тоже с циклами начал экспериментировать. Но там слишком много вариантов получается — и чтобы их грокнуть, чтобы построить какую-то вменяемую модель, нужно было лезть в дизасм или исходники JVM. А мне лень — я хотел простое объяснение для конкретного примера. Потому что общая идея что ссылка живет не до завершения метода, а насколько JVM мозгов хватит — она давно известна, как раз из дискуссий по ранней финализации, на которую Алексей ссылается. И вопрос был лишь в том, чем два отличаются от трех. И — неожиданно — оказалось, что два отличаются от трех ровно на один.
                            • +1
                              Ты, кстати, осчастливишь нас своим присутствием, наконец?
                      • 0
                        Задача №2
                        Регулярка не учитывает контекст — например, применив ее сюда
                        String stringLiteral = " /* this is not a comment but a string literal */ ";
                        

                        будем немного расстроены результатом.

                        P.S. Вместо пробела можно использовать unicode escape типа \uXXXX, например.
                        • +1
                          ASPECTJ адок
                          Скомпилируется то нормально, только при вызове count очевидно получим StackOverflow, дергаем же метод снова прямо из аспекта.
                          • +10
                            У Вас по Java только 1 задача, остальные имеют к языку лишь опосредованное отношение.
                            • +2
                              К языку да, к платформе — нет.

                              Но вообще сейчас разбавим, ага!
                            • +6
                              Предпоследняя задача
                              В java не скомпилится.
                              image
                              • 0
                                данунеможетбыть!
                                • +3
                                  мояджавасъелаейнорм
                              • 0
                                Третья о спринге
                                Если не хочется проставлять
                                context.setAllowBeanDefinitionOverriding(false);
                                

                                Всегда можно написать свой BeanPostProcessor, который будет разруливать такие ситуации
                                • +1
                                  Задача №4
                                  Обе программы выдадут и выдают OOM в JDK 1.8.0.25 32-bit. Получается условие задачи заведомо неверное, так как автор считает, что одна из программ не выбросит OOM, хотя на практике обе бросают.
                                  • 0
                                    окей, тогда почему обе бросают?
                                    • +1
                                      Давайте считать, что для 32-битной JVM предполагается запускать её с опцией -server.

                                      >"C:\Program Files (x86)\Java\jdk1.8.0_31\bin\java.exe" -server OOM1
                                      358862028
                                      Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
                                              at OOM1.main(OOM1.java:9)
                                      
                                      >"C:\Program Files (x86)\Java\jdk1.8.0_31\bin\java.exe" -server OOM2
                                      228366745
                                      228366745
                                      228366745
                                      I allocated memory successfully
                                    • +2
                                      Последняя – от Евгения Борисова
                                      Первый вариант — заимплементить SmartLifecycle.stop(Runnable) и назначить обоим одинаковые фазы.
                                      Второй — использовать ApplicationEventMulticaster (например, SimpleApplicationEventMulticaster) с соответствующим taskExecutor (например, ThreadPoolExecutor) и повесить обоих на ContextCloseEvent.
                                      В теории оба варианта должны сработать)

                                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                    Самое читаемое