687 просмотров
От 30 января

Вопросы про Go на собеседовании

1. Какие типы данных используются в Go?

Go работает со следующими типами: - Method (метод); - Boolean (логический тип); - Numeric (численный); - String (строковый); - Array (массив); - Slice (срез); - Struct (структура); - Pointer (указатель); - Function (функция); - Interface (интерфейс); - Map (карта); - Channel (канал).

2. Как проверить тип переменной в среде выполнения?

Лучшим способом проверки типа переменной во время выполнения является Type Switch. Он анализирует переменные по типу, а не значению. Например: package main import "fmt" func do(i interface{}) { switch v := i.(type) { case int: fmt.Printf("Double %v is %v\n", v, v*2) case string: fmt.Printf("%q is %v bytes long\n", v, len(v)) default: fmt.Printf("I don't know type %T!\n", v) } } func main() { do(21) do("hello") do(true) } Также можно это сделать с помощью рефлексии: package main import ( "fmt" "reflect" ) func main() { x := 42 fmt.Println("Тип переменной x:", reflect.TypeOf(x)) }

3. Что такое рефлексия в Go?

Рефлексия в Go реализована в пакете reflect и представляет собой механизм, позволяющий коду исследовать значения, типы и структуры во время выполнения, без заранее известной информации о них. Рефлексия полезна в ситуациях, когда нам нужно работать с данными неизвестного типа, например, при сериализации/десериализации данных, реализации ORM систем и так далее. С помощью рефлексии мы можем, например, определить тип переменной, прочитать и изменить её значения, вызвать методы динамически. Это делает код более гибким, но следует использовать рефлексию осторожно, так как она может привести к сложному и трудночитаемому коду, а также снизить производительность. Простые примеры: Определение типа переменной: package main import ( "fmt" "reflect" ) func main() { x := 42 fmt.Println("Тип переменной x:", reflect.TypeOf(x)) } В примере мы используем функцию reflect.TypeOf(), чтобы определить тип переменной x. Программа выведет int, так как x — целое число. Чтение и изменение значений: package main import ( "fmt" "reflect" ) func main() { x := 42 v := reflect.ValueOf(&x).Elem() // Получаем reflect.Value fmt.Println("Исходное значение x:", x) v.SetInt(43) // Изменяем значение x fmt.Println("Новое значение x:", x) } Здесь мы используем reflect.ValueOf() для получения reflect.Value переменной x, а затем изменяем её значение с помощью SetInt(). Динамический вызов методов: package main import ( "fmt" "reflect" ) type MyStruct struct { Field int } func (m *MyStruct) UpdateField(val int) { m.Field = val } func main() { x := MyStruct{Field: 10} // Получаем reflect.Value структуры v := reflect.ValueOf(&x) // Получаем метод по имени method := v.MethodByName("UpdateField") // Вызываем метод с аргументами method.Call([]reflect.Value{reflect.ValueOf(20)}) fmt.Println("Обновленное значение поля:", x.Field) } В этом примере мы создаем экземпляр структуры MyStruct, получаем метод UpdateField с помощью MethodByName и вызываем его динамически с помощью Call. Метод обновляет значение поля структуры.

4. Является ли Go объектно-ориентированным языком?

Да и нет. Хотя в Go есть типы и методы, и он допускает объектно-ориентированный стиль программирования, в нем нет иерархии типов. Концепция «интерфейс» в Go предоставляет другой подход, считающийся простым в использовании и в некотором роде более общим. Есть также способы встраивать типы в другие типы, чтобы обеспечить нечто подобное, но не идентичное подклассам. Более того, методы в Go более общие, чем в C++ или Java: они могут быть определены для любого типа данных, даже для встроенных, таких как простые "unboxed" (представляющие значения) целые числа. Они не ограничиваются структурами (классами). Кроме того, отсутствие иерархии типов делает "объекты" в Go намного легче, чем в таких языках, как C++ или Java.

5. Как в Go реализовано наследование?

Как такового наследования в Go нет, но при этом у нас есть структуры - это специальные типы, в которые мы можем включать другие типы, в том числе такие же структуры. Можно сказать, что Go предпочитает наследованию композицию.

6. Как в Go реализована инкапсуляция?

Инкапсуляция в Go - это возможность задавать переменным, функциям и методам первую букву названия в верхнем или нижнем регистре. Соответственно нижний регистр будет значить, что переменная, функция или метод доступна только в рамках пакета. Тогда как верхний регистр даст доступ к переменной, функции или методу за рамками пакета.

7. Как в Go реализован полиморфизм?

Полиморфизм в Go реализован с помощью интерфейсов. Основная идея заключается в том, что мы можем объявить интерфейсы (контракты на определённое поведение) для наших типов. При этом, для типов мы должны реализовать методы, удовлетворяющие этим интерфейсам. Таким образом, мы сможем работать со всем набором типов, у которых реализовали интерфейсы, как с единым интерфейсным типом.

8. Какие механизмы синхронизации доступны в Go?

В Go примитивы синхронизации — это инструменты из пакета sync (и не только), которые помогают нам гарантировать, что множество горутин может безопасно взаимодействовать с общими данными или координировать свою работу. sync.Mutex: основной примитив блокировки для исключения одновременного доступа к данным. Мьютексы позволяют только одной горутине получить доступ к общему ресурсу в определенный момент времени. sync.RWMutex: разрешает множественное чтение или одну операцию записи в текущий момент времени. sync.WaitGroup: используется для ожидания завершения группы горутин перед продолжением выполнения основной программы. sync.Once: гарантирует, что функция будет вызвана только один раз, несмотря на количество вызовов. sync.Cond: предоставляет механизм для блокирования горутины, пока не будет выполнено некоторое условие. Не так давно Расс Кокс отменил предложение удалить данные тип в будущей версии Go. Подобную роль играют: Каналы. Каналы в Go хоть и не являются примитивами синхронизации в традиционном понимании, они играют ключевую роль в управлении горутинами, позволяют обеспечить безопасный обмен данными между ними. Каналы обеспечивают синхронизацию и блокируют выполнение до тех пор, пока данные не будут переданы или приняты. Атомарные операции: Golang предоставляет атомарные операции для безопасного выполнения операций чтения и записи разделяемых данных.

9. Что такое атомарная операция?

Атомарная операция выполняется за один шаг относительно других потоков или, в контексте Go, других горутин. Это означает, что атомарную операцию нельзя прервать в середине ее работы. Стандартная библиотека Go содержит пакет atomic, который в некоторых простых случаях может помочь избежать использования мьютекса. С помощью него мы получаем доступ к атомарным счетчикам из нескольких горутин, не имея проблем с синхронизацией и не беспокоясь о race condition.

10. Что такое горутина? Как ее остановить?

Горутина  —  это функция или метод, которые выполняются конкурентно с любыми другими горутинами, используя специальный поток. Потоки горутин более легковесны, чем стандартные потоки, и большинство программ Go одновременно используют тысячи горутин. Для создания горутины перед объявлением функции нужно добавить ключевое слово go. go f(x, y, z) Остановить горутину можно отправкой сигнала в специальный канал. При этом горутины могут отвечать на такие сигналы, только если им сказано выполнять проверку. Поэтому нужно будет включить проверки в подходящие места, например в начало цикла for. package main func main() { quit := make(chan bool) go func() { for { select { case <-quit: return default: // … } } }() // … quit <- true }

11. Что такое канал и какие виды каналов есть в Go?

Каналы — это инструменты коммуникации между горутинами. Технически это конвейер/труба, откуда можно считывать или помещать данные. То есть одна горутина может отправить данные в канал, а другая — считать помещенные в этот канал данные. Для создания канала в Go есть ключевое слово chan. Канал может передавать данные только одного типа. package main import "fmt" func main() { var c chan int fmt.Println(c) } При простом определении переменной канала она имеет значение nil, то есть по сути канал неинициализирован. Для инициализации применяется функция make(). В зависимости от определения емкости канала он может быть буферизированным или небуферизированным. Для создания небуферизированного канала вызывается функция make() без указания емкости канала: var intCh chan int = make(chan int) Буферизированные каналы также создаются с помощью функции make(), только в качестве второго аргумента в функцию передается емкость канала. Если канал пуст, то получатель ждет, пока в канале появится хотя бы один элемент. chanBuf := make(chan bool, 3) С каналом можно произвести 4 действия: - создать канал - записать данные в канал - вычесть что-то из канала - закрыть канал Однонаправленные каналы: в Go можно определить канал, как доступный только для отправки данных или только для получения данных. Канал может быть возвращаемым значением функции. Однако, следует внимательно подходить к операциям записи и чтения в возвращаемом канале.

12. Как работают буферизованные и небуферизованные каналы?

Буферизованные каналы позволяют вам быстро помещать задания в очередь, чтобы вы могли работать с большим количеством запросов и обрабатывать их позже. Кроме того, буферизованные каналы можно использовать в качестве семафоров, ограничивая пропускную способность вашего приложения. Суть: все входящие запросы перенаправляются на канал, который обрабатывает их по очереди. Завершая обработку запроса, канал отправляет исходному, вызвавшему сообщение о готовности обработать новый запрос. Таким образом, ёмкость буфера канала ограничивает количество одновременных запросов, которые он может хранить. Вот так выглядит код, который реализует данный метод: package main import ( "fmt" ) func main() { numbers := make(chan int, 5) // канал numbers не может хранить более пяти целых чисел — это буферный канал с емкостью 5 counter := 10 for i := 0; i < counter; i++ { select { // здесь происходит обработка case numbers <- i * i: fmt.Println("About to process", i) default: fmt.Print("No space for ", i, " ") } // мы начинаем помещать данные в numbers, однако когда канал заполнен, он перестанет сохранять данные и будет выполняться ветка default } fmt.Println() for { select { case num := <-numbers: fmt.Print("*", num, " ") default: fmt.Println("Nothing left to read!") return } } } Аналогично, мы пытаемся считывать данные из numbers, используя цикл for. Когда все данные из канала считаны, выполнится ветка default и программа завершится с помощью оператора return. При выполнении кода выше мы получаем такой вывод: $ go run bufChannel.go About to process 0 . . . About to process 4 No space for 5 No space for 6 No space for 7 No space for 8 No space for 9 *0 *1 *4 *9 *16 Nothing left to read! В общем: Буферизированный канал заблокирует горутину только в том случае, если весь буфер забит. И происходит попытка еще одной записи. Как только будет выполнено чтение из канала - горутина разблокируется. В случае, если горутина всего одна (только функция main) и канал её заблокирует — программа выпадет с ошибкой, так как все горутины блокированы и выполнять нечего. Небуферизированный канал заблокирует горутину до момента, пока с него ничего не прочитают.

13. Можно ли в Go закрыть канал со стороны читателя?

Закрытие канала обычно выполняется отправителем, а не получателем. Это связано с тем, что закрытие канала со стороны получателя может привести к панике при попытке отправителя записать в уже закрытый канал. Однако, в некоторых случаях, получатель может определить, что данные больше не нужны, и хочет уведомить отправителя о прекращении отправки. В таком случае, обычно используется дополнительный канал, называемый каналом управления или сигнальным каналом, который получатель может использовать для отправки сигнала об остановке. После получения сигнала, отправитель может корректно закрыть основной канал данных. Простой пример: func main() { dataCh := make(chan int) stopCh := make(chan struct{}) go func() { for { select { case data, ok := <-dataCh: if !ok { // Канал закрыт, прекращаем обработку return } // Обработка данных fmt.Println(data) case <-stopCh: // Получен сигнал остановки, закрываем канал dataCh close(dataCh) return } } }() // Отправка данных в канал dataCh <- 1 dataCh <- 2 // Отправка сигнала остановки stopCh <- struct{}{} } stopCh используется для уведомления горутины о необходимости закрыть канал dataCh. Это безопасный способ обеспечить корректное управление жизненным циклом канала.

14. Что такое пакеты в Go?

Поддержите проект и получите доступ ко всему контенту всего за 290

15. Как работает управление памятью в Go?

Поддержите проект и получите доступ ко всему контенту всего за 290

16. Что такое глобальная переменная?

Поддержите проект и получите доступ ко всему контенту всего за 290

17. Что такое heap и stack?

Поддержите проект и получите доступ ко всему контенту всего за 290

18. Что делает runtime.newobject()?

Поддержите проект и получите доступ ко всему контенту всего за 290

19. Что такое {} с необъявленным оператором в Go функции?

Поддержите проект и получите доступ ко всему контенту всего за 290

20. Как выполнить ряд условий в одном операторе switch case?

Поддержите проект и получите доступ ко всему контенту всего за 290

21. Что такое строки в Go?

Поддержите проект и получите доступ ко всему контенту всего за 290

22. Как можно оперировать строками?

Поддержите проект и получите доступ ко всему контенту всего за 290

23. Как узнать длину строки?

Поддержите проект и получите доступ ко всему контенту всего за 290

24. Какие численные типы есть в Go?

Поддержите проект и получите доступ ко всему контенту всего за 290

25. Расскажите про числовые константы в Go

Поддержите проект и получите доступ ко всему контенту всего за 290

26. Чем отличается int от uint?

Поддержите проект и получите доступ ко всему контенту всего за 290

27. Что такое обычный int и какие есть нюансы его реализации?

Поддержите проект и получите доступ ко всему контенту всего за 290

28. Как преобразовать строку в int и наоборот?

Поддержите проект и получите доступ ко всему контенту всего за 290

29. Сколько в памяти занимают int32 и int64?

Поддержите проект и получите доступ ко всему контенту всего за 290

30. Какие предельные значения int32 и int64?

Поддержите проект и получите доступ ко всему контенту всего за 290

31. Какой результат у деления int на 0 и float на 0?

Поддержите проект и получите доступ ко всему контенту всего за 290

32. Что такое iota?

Поддержите проект и получите доступ ко всему контенту всего за 290

33. Что такое слайс и чем он отличается от массива?

Поддержите проект и получите доступ ко всему контенту всего за 290

34. Как работает базовая функция append для Go?

Поддержите проект и получите доступ ко всему контенту всего за 290

35. Какой размер массива выделяется под слайс при его расширении за рамки емкости?

Поддержите проект и получите доступ ко всему контенту всего за 290

36. Как реализована map (карта) в Go?

Поддержите проект и получите доступ ко всему контенту всего за 290

37. Почему нельзя брать ссылку на значение, хранящееся по ключу в map?

Поддержите проект и получите доступ ко всему контенту всего за 290

38. Что такое эвакуация, и когда она происходит?

Поддержите проект и получите доступ ко всему контенту всего за 290

39. Какие есть особенности синтаксиса получения и записи значений в map?

Поддержите проект и получите доступ ко всему контенту всего за 290

40. Как происходит поиск по ключу в map?

Поддержите проект и получите доступ ко всему контенту всего за 290

41. Что такое интерфейсы в Go?

Поддержите проект и получите доступ ко всему контенту всего за 290

42. Приведите пример реализации интерфейсов

Поддержите проект и получите доступ ко всему контенту всего за 290

43. Что такое пустой интерфейс?

Поддержите проект и получите доступ ко всему контенту всего за 290

44. Что такое nil интерфейс?

Поддержите проект и получите доступ ко всему контенту всего за 290

45. Как преобразовать интерфейс к другому типу?

Поддержите проект и получите доступ ко всему контенту всего за 290

46. Как определить тип интерфейса?

Поддержите проект и получите доступ ко всему контенту всего за 290

47. Зачем используется ключевое слово defer в Go?

Поддержите проект и получите доступ ко всему контенту всего за 290

48. Порядок возврата при использовании несколько функций с defer?

Поддержите проект и получите доступ ко всему контенту всего за 290

49. Как передаются значения в функции, перед которыми указано defer?

Поддержите проект и получите доступ ко всему контенту всего за 290

50. Что такое замыкания функций?

Поддержите проект и получите доступ ко всему контенту всего за 290

51. Что такое lock-free структуры данных?

Поддержите проект и получите доступ ко всему контенту всего за 290

52. Как устроен сетевой ввод-вывод в Go?

Поддержите проект и получите доступ ко всему контенту всего за 290

53. Что такое дженерики (обобщения)?

Поддержите проект и получите доступ ко всему контенту всего за 290

54. Для чего нужна функция recover()?

Поддержите проект и получите доступ ко всему контенту всего за 290
Хотите стать частью сообщества Девстанции?
Вступайте в наш чат в Telegram

Также в этой категории

Задачник
  21 задача

Топ 20 задач с собеседований по Go

Алгоритмические задачи для собеседований Go-разработчиков

2635 просмотров
От 25 февраля

Вам может быть интересно

Шпаргалка
  7 вопросов

Коллекция полезных команд для Docker

Большая шпаргалка по всем командам Docker

268 просмотров
От 12 октября 2023
Викторина
  20 вопросов

Викторина по Django - Junior/Middle

Базовые вопросы с собеседования по Django

18 просмотров
От 2 июня 2023
Викторина
  26 вопросов

Викторина по Python - Junior

Вопросы на собеседовании по Python для Junior-позиции

74 просмотра
От 30 мая 2023
Шпаргалка
  60 вопросов

60 вопросов про базы данных и SQL

Вопросы и ответы с собеседования по базам данных и SQL

418 просмотров
От 20 февраля
Шпаргалка
  11 вопросов

Теория шардинга баз данных

О распределении данных между серверами

321 просмотр
От 10 октября 2023
Шпаргалка
  34 вопроса

Шпаргалка по вопросам о Python

Вопросы для собеседования Python-разработчика

714 просмотров
От 30 января

Топ тредов

Gravatar for 9tokio
Tokio:
то что раньше было бесплатным теперь платное - вот это я понимаю

Последнее сообщение:
Логотип Девстанции
Девстанция:
Спасибо за поддержку проекта :) Повышение качества контента - один из важнейших приоритетов. Этому м...
3 сообщения
275 просмотров

Логотип Девстанции
Девстанция:
Поиск людей для совместной разработки IT-стартапов

Последнее сообщение:
В этом треде пока нет сообщений
0 сообщений
142 просмотра

Логотип Девстанции
Девстанция:
Какой язык программирования выбрать в качестве первого?

Последнее сообщение:
Gravatar for 2kokke
Kokke:
Python или JS - универсально. Но по уму надо бы с чего-то строгого начинать и достаточно низкоуровне...
1 сообщение
177 просмотров

Все категории