Как сделать нечитабельный код java

Добавил пользователь Валентин П.
Обновлено: 10.09.2024

Привет! Счастливого Пришествия: D Я Саймон Мэйпл ( @sjmaple) , Технический Евангелист для ZeroTurnaround. Вы знаете, ребята JRebel ! В результате мы напишем такой продукт, как JRebel, который взаимодействует с байт-кодом, чаще, чем вы себе представляете. Мы узнали о нем много вещей, которыми хотели бы поделиться.

Давайте начнем с самого начала … Java был языком, предназначенным для работы на виртуальной машине, поэтому его нужно было скомпилировать только один раз, чтобы он везде работал (да, да, один раз напиши, повсюду тестируй). В результате JVM, который вы устанавливаете на свою систему, будет встроенным, что позволит коду, работающему на нем, быть независимым от платформы. Java-байт-код является промежуточным представлением Java-кода, который вы пишете как источник, и является результатом компиляции вашего кода. Таким образом, ваши файлы классов являются байт-кодом.

Чтобы быть более кратким, байт-код Java — это кодовый набор, используемый виртуальной машиной Java, который JIT-компилируется в собственный код во время выполнения.

Вы когда-нибудь играли с ассемблером или машинным кодом? Байт-код в некотором роде похож, но многие люди в отрасли на самом деле не играют с ним больше, больше из-за необходимости отсутствия. Тем не менее, важно понимать, что происходит, и полезно, если вы хотите кого-то обмануть в пабе.

1

Хорошо, в байт-коде мы фактически увидели бы коды операций (iconst_1 и iconst_2) и инструкцию (iadd), а не push и add, но последовательность действий та же. Фактические инструкции имеют длину один байт, следовательно, байт-код. В результате получается 256 возможных кодов операций, но только 200 или около того используются. К кодам операций добавляется префикс с типом, за которым следует имя операции. Итак, то, что мы видели ранее с iconst и iadd, это константы целочисленного типа и инструкция добавления для целочисленных типов.

Это все очень хорошо, но как насчет чтения файлов классов. Как правило, все, что вы обычно видите в файле класса при открытии в выбранном вами редакторе, это набор смайликов и несколько квадратов, точек и других странных символов, верно? Ответ в javap, утилите кода, которую вы фактически получаете с вашим JDK. Давайте посмотрим на пример кода, чтобы увидеть javap в действии.

Абсолютно не понимаю, как это делается. Что такое магия?

Термин, который вы ищете, это " obfuscate". Если вы просто выполните поиск Google для "php obfuscators", вы найдете программное обеспечение, в которое вы можете скопировать и вставить свой код, и он будет запутывать его для вас.

Выполняя что-то подобное, вы намеренно заставляете свой код работать дольше, не добавляя никаких преимуществ. Так как каждый может декодировать этот код обратно к оригиналу, он действительно не имеет никакой цели, кроме как просто обнаруживать некоторые возможности php и быть в состоянии сказать: "Посмотрите Ма! Я сейчас хакер"

Если вы действительно хотите, чтобы другие не читали ваш код, вам нужно что-то вроде Zend-кодера, что сделает ваш код нечитаемым и все еще запущенным как php.

Вы ищете что-то, чтобы запутать код.

Как сделать код нечитаемым? Это легко: назначьте его одному из программистов, с которыми я работаю здесь.

ну, просто сделайте обратное на исходном тексте, то есть в этом порядке, gzdeflate, str_rot13, base64_encode.

Это не очень хороший способ достижения "защищенного" кода, если это то, что вы собираетесь делать, потому что, предоставляя шаги для деобфискирования вашего кода, вы по сути позволяете любому, у кого есть доступ к коду, преобразовать его обратно в открытый текст. Как правило, обфускаторы кода делают ваш код нечитаемым, заменяя значащие имена переменных такими вещами, как $a , $b и т.д., А также удаляя комментарии, пробелы и любые другие удобства, которые мы обычно используем для чтения кода.


Скорее всего, конкретно вы уже заходили на GitHub, сохранить свой код, посмотреть на чужой. А теперь посмотрите на собственные старые проекты, написанные годом ранее — вы удивитесь, насколько лучше стали ваши навыки с того момента.

Большинство младших программистов (и даже некоторые более опытные) не знают, как отличить хороший код от плохого. Когда программа заработает — они сразу идут дальше. К тому же, если они работают в одиночку, то никогда даже не узнают, что пишут плохой код.

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

В руководстве пойдет речь о некоторых признаках, выдающих плохие практики в разработке программного обеспечения. Давайте же разберемся!

Подобное случается с каждым программистом, стоит только перестать стараться писать чистый код.

Предположим, внутри класса или функции вы написали десять строк кода. Все в порядке. Теперь, в другом классе или функции того же проекта, вам нужно заново выполнить похожую задачу. Какова мгновенная реакция? Вместо написания отдельной функции для выполнения схожей задачи, вы скопируете десять строк кода и вставите их в новое место, словно заплатку.

Конечно, ведь всегда проще скопировать-вставить, чем написать функцию. Но вопрос состоит в том, когда подобный метод применим, а когда — только навредит кодовой базе?

Каждый раз, когда блок кода пригодился вам дважды — пишите с ним функцию. Подобный подход характеризуется рядом существенных преимуществ:

  1. Экономия времени, ведь функции изначально предназначены для подобного, верно?
  2. Чистый, структурированный код, который легко понять.
  3. Потенциал повторного применения функции в других проектах.
  4. Легкость удаления мусорного кода.
  5. Удобство в отладке.

Постоянство — качество хорошего программиста: старайтесь следовать шаблону, ведь вам придется написать еще очень много функций.

Например, если в одном и том же проекте хранятся две одинаковые функции с разным поведением, то следующим программистам будет очень трудно читать ваш код. Предположим, что вы написали функции проверки некоторых данных профиля. Первая функция проверяет дату рождения пользователя, а вторая — его фотографию.

Проверка заключается в том, что при совпадении входных данных с данными о пользователе булевы функции возвращают true , а при несовпадении — возвращают false .

Теперь представьте, что первая булева функция возвращает true при совпадении даты рождения с образцом. Тогда вы, очевидно, подумаете, что вторая функция тоже вернет true , если поданное на вход изображение совпадет с фотографией пользователя.
Но нет, вторая булева функция возвращает false , если входные данные совпадают с образцом! Поверьте, вы бы ни за что не допустили такой очевидной ошибки, но некоторые эгоистичные и безответственные программисты продолжают поступать так каждый день.

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

Все ненавидят жесткий кодинг, “хардкод”. Хорошо, что его появление легко заметить и предотвратить. Проанализируйте следующий фрагмент кода:

Действительная необходимость в жестком кодинге случается крайне редко, так как все изменчиво, каждый аспект программы при желании можно изменить. В любой момент просто инициализируйте новую переменную, присвойте ей значение. Например:

Динамические приложения всегда лучше статических. Когда нужно менять какую-либо часть статического решения, то приходится каждый раз обновлять приложение.

Большинство проектов подключаются к облачным сервисам. Если ваши проекты синхронизированы с какими-либо из них, то лучше сразу получать данные из облака согласно его стандарту.

Определение достаточно абстрактное, так что же имеется в виду?
Во-первых, не стоит писать “спагетти-код” — слишком конкретные и длинные условия со множеством проверок и категорий.

Тем не менее, второй совет — противоположность первого:
НЕ пишите сложные условия ради экономии в пару строк. Если вам придется написать две лишние строки, но код станет понятнее, то пишите эти строки с уверенностью. Проанализируйте следующие два примера:

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

Непонятно, почему до сих пор далеко не каждый разработчик на земле применяет встроенные инструменты и функции.

Если вам действительно нужно тратить свое время лишь на то, чтобы работодатель нанял побольше сотрудников для переработки вашего кода, то займитесь чем-нибудь другим. Пожалуйста, не наказывайте проект и разработчиков, в дальнейшем читающих ваш код.

В 99 из 100 случаев ваши функции с течением времени станут менее эффективными в плане времени выполнения и требуемой памяти. Не смотря на это, многие программисты даже не знают, что есть хорошая встроенная функция, но уже пишут свои.

Вот почему большинство старших программистов советуют начинающим и младшим специалистам поискать в Интернете лишние пять минут, прежде чем написать собственную функцию.

Самое приятное, что даже если вы не найдете встроенных функций, то узнаете, как решили задачу другие разработчики. Всегда помните, что написание кода — лишь одна часть работы программиста, нельзя забывать про важность чтения публично доступных наработок.

Об опыте программиста можно судить по тому, как он называет функции и классы в своих проектах.

Давайте рассмотрим простой пример: getABC() .

Вы догадались, что делает метод? Никто не догадался. Следовательно, следующий программист, прочитавший код, наверняка проклянет вас. Потому что ему, вероятно, придется вчитываться в каждую строчку функции, долго и постепенно понимая, что она делает.

Посмотрите на следующее название: getUserNameFromDB() . Идентификатор метода явно сообщает о предполагаемом результате выполнения.

Некоторые программисты так сокращают название, что становится трудно расшифровать его полную версию. handleBtnClick , getConfig или parseInfo легко понять. Но hndleBtnClk , getCnfg уже слишком коротки. Отбрасывая скуку, рассмотрим ситуацию из жизни:

Один из преподавателей курса снизил оценку за неправильное написание названия функции в лабораторной работе по Java.
Он поступил так, чтобы студенты в будущем запомнили важность именования.

Локальная переменная существует не просто так, верно? Пожалуйста, избегайте глобальных переменных всякий раз, когда задача решается через локальные переменные.

Почему? Много причин для превалирования локальных переменных над глобальными. Поговорим о некоторых основных:

  1. Меньше ошибок и неожиданных результатов.
  2. Когда вы получаете ошибки, то их легко найти.
  3. Серьезное упрощение рефакторинга кода.
  4. Другим программистам в сотни раз сложнее читать ваш код, когда в нем повсеместно применены глобальные переменные.

Если везде только глобальные переменные, значит, вы плохой программист. Извините за обидное, но правдивое высказывание.

Иногда программистам нравится писать код сложным способом. Поверьте, подобное не сделает вас лучшим программистом на свете, несмотря на то, что многие старшие разработчики занимаются каким-либо хобби в области компьютерных наук, где они нестандартным образом все усложняют.

Однажды программист ответил, что запутанный код делает компанию зависимой от него — работа ему обеспечена.

Но это очень неправильная концепция, крайне вредная идея. Гораздо лучше руководствоваться следующим:
“Загадочность” — плохо. “Легкость” — хорошо.

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

Кроме того, игнорирование комментариев или отсутствие документации затрудняет понимание вашего кода сильнее, чем что-либо еще. Переусложненный код, как и последствия интеллектуального хобби, куда легче понять, если они детально задокументированы.

Код весьма плох, когда функции выполняются исключительно в строгом порядке. Рассмотрим пример:

Откуда другим программистам знать, что перед процедурой begin() надо вызвать процедуру start() ?

Функции работают независимо друг от друга.

Решением “на каждый раз” послужит специальный метод-шаблон. Рассмотрим пример на языке программирования JavaScript:

Когда все же необходимо вызывать функции по порядку, то пишется одна внешняя функция start() , со множеством внутренних, вроде begin() , preparing() и end() . Кроме того, внешней функции start() присваивается идентификатор, явно отражающий ее функционал.

Начинающие программисты часто пишут лишние вложенные циклы. Тем более, когда менталитет “достаточно выполнить работу и запустить код” превращает программиста в неэффективного лентяя. Правда, производительность приложения — скорее дело привычки:

Когда команда не ценит хороший код и не заботится о качестве, то программисты не заботятся о временной сложности алгоритма и сложности алгоритма по памяти

Если вы разбираетесь в оценке сложности алгоритмов и пишете эффективный код — он даст вам силы, а если не обращаете внимание на детали реализации, то не сможете изменить шаблоны, остановившись в развитии профессиональных навыков.

Многие программисты даже не знают про “сложность алгоритма по памяти” или “memory complexity”. Если вы не знаете, пожалуйста, узнайте, ведь это поможет на собеседованиях.

Потребуется время, чтобы стать хорошим программистом, но чем дольше вы следите за качеством собственных проектов, тем чище и лучше становится ваш код. Не говорите себе, что со временем все само по себе изучится, но и ученым-ракетостроителем быть не требуется, чтобы написать хороший код.

Преимущества переформатирования

  • Отступ.
  • Стиль и написание функций.
  • Пустое пространство.
  • Использование заглавных букв и наименований.

Советы по форматированию кода

Убедитесь, что ваш код правильно прокомментирован. Используйте комментарии, чтобы передать намерение и смысл. Даже если вы единственный, кто читает этот код, это все равно полезно себе напоминать предназначение тех или иных блоков кода. Это облегчит работу, если вы вернетесь к своей кодовой базе, например, после отпуска.

Не используйте табуляции для пробелов в одной строке. Это снижает читабельность кода.

Добавьте TODO в свои блоки кода, чтобы следить за будущими реализациями. Таким образом, когда вы переформатируете свой код, это не создаст иллюзию того, что ваша работа завершена.

Не используйте переформатирование кода как быстрое исправление синтаксических ошибок.

Избегайте глубоких вложений. Это обычная практика среди некоторых разработчиков, но глубокая вложенность кода затрудняет читаемость и дает место ошибкам, которые нелегко обнаружить с первого взгляда.

Переформатирование кода в IntelliJ IDEA

Прежде чем мы исследуем различные способы форматирования кода в IntelliJ, начните с создания образца проекта Java в вашей среде IDE. После запуска IntelliJ вы должны увидеть такой экран: Нажмите Далее и завершите настройку инициализации проекта. Теперь, когда ваш проект настроен, пришло время добавить образец кода, который поможет вам понять переформатирование кода в IntelliJ. Это простой счетчик слов от Техасского университета:

Переформатирование фрагментов кода

Начнем с форматирования фрагментов кода. Для этого просто выделите часть кода, которую вы хотите отформатировать, и нажмите Code > Reformat Code . Как вы можете видеть, выделенная часть вашего кода (в данном случае строки 11–17) отформатирована правильно. Давайте посмотрим на “до” и “после” переформатирования нашего фрагмента кода. Если вы заметили на первом изображении, объявление и инициализация вашей переменной произошли в той же строке, что и try . Закрывающие фигурные скобки смешаны с блоком else . Но после выделения этой части вашего кода и использования опции переформатирования у вас получится более приятный для глаз блок кода. Теперь полный код вашего класса выглядит примерно так: Обратите внимание, что переформатирован был только выбранный вами блок кода.

Переформатирование целых файлов

Переформатирование модуля или каталога

Оптимизировать импорт : выберите этот вариант, если вы хотите добавить отсутствующие операторы импорта, а также избавиться от неиспользуемых операций импорта.

Переупорядочить записи : для реорганизации кода в соответствии с правилами расположения, указанными в настройках стиля кода.

Код очистки : для запуска очистки кода.

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

Примеры горячих клавиш для переформатирования

  • Переформатировать блок кода: Ctrl + Alt + Shift + L
  • Переформатировать файл: Ctrl + Alt + L
  • Добавить комментарий к строке: Ctrl + /
  • Добавить комментарий блока: Ctrl + Shift + /

Заключение

Уделять внимание форматам кода так же важно для разработчика, как и заставить его работать. Вы никогда не можете сказать, кто в конечном итоге будет работать с вашим кодом. Наличие легко читаемой, хорошо структурированной кодовой базы говорит о профессиональном разработчике. Благодаря IntelliJ IDEA форматирование кода не должно быть таким сложным, как написание самого кода. В следующий раз, когда будете писать код с использованием IntelliJ, воспользуйтесь этими советами. Так вы упростите себе жизнь во время отладки кода.

Читайте также: