Простые задачи на 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
    Конференции для программистов и сочувствующих. 18+

    Comments 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 можно посмотреть что будет, если там будет кложур, принимащий одну переменую
                      +2
                      Прям ваще.
                      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
                                а мы с Лёшей сейчас это обсудим!
                                  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 конференций. Но не для всех, да :-)
                                                +2
                                                вон, 23derevo их тоже не любит :-)
                                                  +2
                                                  я их обожаю!
                                    +1
                                    Ну да, я сначала тоже с циклами начал экспериментировать. Но там слишком много вариантов получается — и чтобы их грокнуть, чтобы построить какую-то вменяемую модель, нужно было лезть в дизасм или исходники JVM. А мне лень — я хотел простое объяснение для конкретного примера. Потому что общая идея что ссылка живет не до завершения метода, а насколько JVM мозгов хватит — она давно известна, как раз из дискуссий по ранней финализации, на которую Алексей ссылается. И вопрос был лишь в том, чем два отличаются от трех. И — неожиданно — оказалось, что два отличаются от трех ровно на один.
                                  +1
                                  Ты, кстати, осчастливишь нас своим присутствием, наконец?
                                    0
                                    присоединяюсь к вопросу
                                      +1
                                      Увы, но нет. Зато придут люди из моей команды
                                        +2
                                        Пичалька.
                                          +1
                                          Грустняшка.
                                            0
                                            пиняшка!
                                            0
                                            Кто именно и с каким докладом?
                                              0
                                              Послушать, Володя, просто послушать )
                                  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.
                                                В теории оба варианта должны сработать)
                                                  0
                                                  Ты крут!

                                                Only users with full accounts can post comments. Log in, please.