Злоупотреблять! 10 распространенных вопросов на собеседовании по Java

Java опрос

Однажды Сяо Ван сказал мне, что он пошел в компанию, чтобы пройти собеседование на должность Java, и интервьюер пытал его и плакал. Из 10 вопросов на собеседовании по Java Сяо Ван не ответил правильно ни на один из них.

Он сказал мне расстроенно: «Брат, расскажи мне о моей ситуации, ты хочешь это услышать? Я лажу с девушкой, которая старше меня на два года, и я не профессионал. Я изначально планировал изменить к более высокой зарплате в Национальный день, чтобы подтвердить отношения. У меня нет большого опыта, и мои навыки средние. Раньше я работал в аутсорсинговой компании, и было внутреннее направление от Стороны А, поэтому я опрометчиво ушел из работа на аутсорсинге. Я не ожидал, что на собеседовании будут злоупотреблять. Я волновался, что моя девушка будет из-за меня. Нет работы и расстаться со мной».

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

Пользуясь случаем, я поделюсь этими 10 вопросами интервью, с которыми столкнулся Сяо Ван, надеясь помочь другим друзьям.

Первый вопрос, каков результат печати следующего кода?

public class Test {
    public static void main(String[] args) {
        System.out.println(Math.min(Double.MIN_VALUE, 0.0d));
    }
}

Причина, по которой Сяо Ван не ответил на этот вопрос правильно, заключается в том, что он считает, что Double.MIN_VALUE — это то же самое, что и Integer.MIN_VALUE, это отрицательное число и должно быть меньше 0,0d.

Но на самом деле Double.MIN_VALUE и Double.MAX_VALUE являются положительными числами, а значение Double.MIN_VALUE равно2^(-1074), если вы напечатаете Double.MIN_VALUE напрямую, результат вывода будет4.9E-324.

Таким образом, правильный ответ на этот вопрос состоит в том, чтобы вывести0.0.

Второй вопрос: выполните оператор return в блоке try или операторе catch илиSystem.exit()Что произойдет, будет ли выполняться оператор finally?

Причина, по которой Сяо Ван не ответил на этот вопрос правильно, заключается в том, что согласно его стереотипу оператор finally выполняется несмотря ни на что.

Но на самом деле, когда оператор return выполняется в блоке try или операторе catch, выполняется оператор finally; в блоке try или операторе catchSystem.exit(), оператор finally не будет выполняться.

public class Test1 {
    public static void main(String[] args) {
        returnTryExec();
        returnCatchExec();
        exitTryExec();
        exitCatchExec();
    }

    public static int returnTryExec() {
        try {
            return 0;
        } catch (Exception e) {
        } finally {
            System.out.println("finally returnTryExec");
            return -1;
        }
    }

    public static int returnCatchExec() {
        try { } catch (Exception e) {
            return 0;
        } finally {
            System.out.println("finally returnCatchExec");
            return -1;
        }
    }

    public static void exitTryExec() {
        try {
            System.exit(0);
        } catch (Exception e) {
        } finally {
            System.out.println("finally exitTryExec");
        }
    }

    public static void exitCatchExec() {
        try { } catch (Exception e) {
            System.exit(0);
        } finally {
            System.out.println("finally exitCatchExec");
        }
    }
}

Результат выполнения программы следующий:

finally returnTryExec
finally returnCatchExec

Третий вопрос: можно ли переопределить частные или статические методы?

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

Два переопределенных метода имеют одинаковое имя и одинаковое количество параметров метода, однако один метод находится в родительском классе, а другой — в подклассе.

class LaoWang{
    public void write() {
        System.out.println("老王写了一本《基督山伯爵》");
    }
}
class XiaoWang extends LaoWang {
    @Override
    public void write() {
        System.out.println("小王写了一本《茶花女》");
    }
}
public class OverridingTest {
    public static void main(String[] args) {
        LaoWang wang = new XiaoWang();
        wang.write();
    }
}

Родительский класс LaoWang имеетwrite()Метод (без параметров), тело метода — написать книгу «Граф Монте-Кристо»; подкласс XiaoWang переписывает родительский классwrite()Метод (без параметров), но суть метода в написании книги "Травиата".

В основном методе мы объявляем переменную wang типа LaoWang. Во время компиляции компилятор проверяет, содержит ли класс LaoWangwrite()метод, обнаружил, что класс LaoWang имеет, поэтому компиляция прошла. Во время работы создается новый объект XiaoWang и назначается wang.В это время виртуальная машина Java знает, что wang ссылается на объект XiaoWang, поэтому она вызывает подкласс XiaoWang.write()метод вместо родительского класса LaoWangwrite()метод, так что вывод "Сяо Ван написал книгу "Травиата".

Метод private невидим для подклассов, он виден только в объявленном в данный момент классе, ключевое слово private удовлетворяет высочайшему уровню требований к инкапсуляции. Кроме того, приватные методы в Java связаны статической привязкой во время компиляции и не зависят от типа объекта, содержащегося в конкретной ссылочной переменной.

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

class LaoWang{
    public LaoWang() {
        write();
        read();
    }
    public void write() {
        System.out.println("老王写了一本《基督山伯爵》");
    }

    private void read() {
        System.out.println("老王在读《哈姆雷特》");
    }
}
class XiaoWang extends LaoWang {
    @Override
    public void write() {
        System.out.println("小王写了一本《茶花女》");
    }

    private void read() {
        System.out.println("小王在读《威尼斯商人》");
    }
}
public class PrivateOrrideTest {
    public static void main(String[] args) {
        LaoWang wang = new XiaoWang();
    }
}

Вывод программы следующий:

小王写了一本《茶花女》
老王在读《哈姆雷特》

В конструкторе родительского класса вызовитеwrite()а такжеread()метод,write()Метод является общедоступным и может быть переопределен, поэтому подклассwrite()метод,read()Метод является закрытым и не может быть переопределен, поэтому выполнение по-прежнему выполняется родительским классом.read()метод.

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

public class StaticOrrideTest {
    public static void main(String[] args) {
        Laozi zi = new Xiaozi();
        zi.write();
    }
}
class Laozi{
    public static void write() {
        System.out.println("老子写了一本《基督山伯爵》");
    }
}
class Xiaozi extends Laozi {
    public static void write() {
        System.out.println("小子写了一本《茶花女》");
    }
}

Вывод программы следующий:

老子写了一本《基督山伯爵》

Ссылочная переменная zi имеет тип Laozi, поэтомуzi.write()Выполняется в родительском классеwrite()метод.

Статические методы также называются методами класса, которые можно вызывать напрямую через имя класса, при вызове через объект IDE выдаст предупреждение.

Четвертый вопрос,1.0/0.0Каков результат? Будет ли выброшено исключение или я получу ошибку компиляции?

Причина, по которой Сяо Ван не ответил на этот вопрос правильно, заключается в том, что он не изучал подробно операцию деления типов double и int.

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

Когда число с плавающей запятой делится на 0, результатом является бесконечность или NaN.

System.out.println(1.0 / 0.0); // Infinity
System.out.println(0.0 / 0.0); // NaN

Бесконечность по-китайски означает бесконечность, а NaN по-китайски означает, что это не число (Not a Number).

При делении целого числа на 0 (10 / 0), будет выброшено исключение:

Exception in thread "main" java.lang.ArithmeticException: / by zero
	at com.itwanger.eleven.ArithmeticOperator.main(ArithmeticOperator.java:32)

Обычно, когда мы выполняем целочисленное деление, нам нужно сначала определить, равен ли делитель 0, чтобы программа не выдавала исключение.

Пятый вопрос, поддерживает ли Java множественное наследование?

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

Чтобы определить два интерфейса, Fly будет летать, а Run — работать.

public interface Fly {
    void fly();
}
public interface Run {
    void run();
}

Затем пусть класс реализует оба интерфейса.

public class Pig implements Fly,Run{
    @Override
    public void fly() {
        System.out.println("会飞的猪");
    }

    @Override
    public void run() {
        System.out.println("会跑的猪");
    }
}

Но когда дело доходит до множественного наследования, речь идет о расширении, а не о реализации.

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

Класс C наследует как класс A, так и класс B. Когда объект класса C вызывает переопределенный метод в классе A и классе B, он не знает, вызывать ли метод класса A или метод класса B.

Вопрос 6, что происходит, когда существующий ключ помещается в HashMap?

Причина, по которой Сяо Ван не ответил на этот вопрос правильно, заключается в том, что он не изучал глубоко, как работает HashMap.

Хэш, обычно переводится как «хэш», а напрямую переводится как «хеш», что это значит? То есть данные любой длины сопоставляются с фиксированной длиной данных (значением хеш-функции) с помощью алгоритма.

Чтобы быть более интуитивным, нужно смешать строку данных wang и вывести другую er данных фиксированной длины в качестве функции data wang. Обычно мы используем цепочку отпечатков пальцев для отображения определенного человека. Не стоит недооценивать отпечаток пальца размером с ваш палец. алгоритм тоже очень мощный, у вас есть).

Для любых двух разных блоков данных вероятность одинакового значения хеш-функции крайне мала, то есть для заданного блока данных крайне сложно найти блок данных с одинаковым значением хеш-функции. Более того, для блока данных, даже если изменить всего один его бит, изменение его хеш-значения будет очень большим — это и есть значение Hash!

Вы должны знать, что базовой структурой данных HashMap является массив.hash()метод определения индекса.

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

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

Вопрос 7, что напечатает следующий код?

public class Test {
    public static void main(String[] args) {
        char[] chars = new char[]{'\u0097'};
        String str = new String(chars);
        byte[] bytes = str.getBytes();
        System.out.println(Arrays.toString(bytes));
    }
}

Причина, по которой Сяо Ван не ответил на этот вопрос правильно, заключается в том, что он не изучил некоторые знания о кодировании символов.

В этой программе мы создаем строковый объект из массива символов, а затем вызываем класс StringgetByte()получает массив байтов и выводит его на консоль.

Суть этого вопроса интервью заключается не в окончательном результате печати (результат неясен), а в кодировке символов. Обычно мы звонимgetBytes()метод, укажите кодировку, напримерstr.getBytes(StandardCharsets.UTF_8).

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

При использовании UTF_8 результат-62, -105, при использовании GB2312 результат такой63.

Вопрос 8, когда метод бросается в родительский классNullPointerExceptionможно ли использовать бросокRuntimeExceptionспособ переопределить это?

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

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

Кроме того, правила переопределения и перегрузки методов не совпадают. В Java методы private, static и final нельзя переопределить, но можно перегрузить.

Давайте сосредоточимся на правилах переопределения методов:

1) Сигнатуры методов должны быть одинаковыми, включая тип возвращаемого значения, количество параметров, типы параметров и порядок параметров.

2) Переопределенный метод не может генерировать исключение более высокого уровня, чем родительский класс. Например, если метод в родительском классе генерирует IOException, то переопределенный метод в дочернем классе не может генерировать Exception, он может быть подклассом IOException или не генерировать никаких исключений. Это правило применяется только к проверенным исключениям.

Проверяемые исключения должны быть явно перехвачены и обработаны в исходном коде.Непроверенные исключения — это так называемые исключения времени выполнения, такие как NullPointerException, ArrayIndexOutOfBoundsException и т.п., которые не применяются компилятором.

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

public class ExceptionDemo {
    public static void main(String[] args) {
        Super s = new Child();
        s.write();
    }
}
class Super{
    public void write() throws NullPointerException { }
}

class Child extends Super {
    @Override
    public void write() throws RuntimeException { }
}

RuntimeException и NullPointerException являются непроверяемыми исключениями, поэтому ответ на этот вопрос в порядке. Если это проверенное исключение, среда IDE предупредит вас.

Девятый вопрос, следующий код используетcompareTo()метод, есть проблема?

class Employee implements Comparable {
    private int id;

    @Override
    public int compareTo(Object o) {
        Employee emp = (Employee) o;
        return this.id - emp.id;
    }
}

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

Когда нам нужно отсортировать по определенным правилам, мы обычно реализуем интерфейс Comparable и реализуем метод compareTo, правила такие:

1) Если текущий объект меньше другого объекта, метод compareTo должен возвращать отрицательное число, если текущий объект больше другого объекта, он должен возвращать положительное число, если два объекта равны, он возвращает ноль.

2) Вообще говоря, метод compareTo должен быть совместим с методом equals.Если результат двух объектов, оцениваемых методом equals, истинен, то compareTo должен возвращать ноль.

Однако в JDK есть контрпример — BigDecimal.

BigDecimal bd1 = new BigDecimal("2.0");
BigDecimal bd2 = new BigDecimal("2.00");

System.out.println("equals: " + bd1.equals(bd2));
System.out.println("compareTo: " + bd1.compareTo(bd2));

Результат выглядит следующим образом:

equals: false
compareTo: 0

Это связано с тем, что JDK считает, что точность 2.0 и 2.00 не одинакова, поэтому она не может равняться, но значения действительно равны.

3) Вычитание нельзя использовать для сравнения целочисленных значений, потому что результат вычитания может переполниться. следует использоватьInteger.compareTo()сравнивать. Если вы хотите повысить производительность путем вычитания, вы должны убедиться, что два операнда являются положительными целыми числами или что разница между ними меньше, чем Integer.MAX_VALUE.

public class CompareDemo {
    public static void main(String[] args) {
        List<Employee> list = new ArrayList<>();
        list.add(new Employee(1));
        list.add(new Employee(Integer.MIN_VALUE));
        list.add(new Employee(Integer.MAX_VALUE));
        Collections.sort(list);
        System.out.println(list);
    }
}

class Employee implements Comparable {
    private int id;

    public Employee(int id) {
        this.id = id;
    }

    @Override
    public int compareTo(Object o) {
        Employee emp = (Employee) o;
        return this.id - emp.id;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                '}';
    }
}

Вывод программы следующий:

[Employee{id=1}, Employee{id=2147483647}, Employee{id=-2147483648}]

Сортировка нарушена. потому чтоInteger.MIN_VALUE - 1становится положительным числом2147483647.

Вопрос 10. В чем разница между StringBuffer и StringBuilder?

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

StringBuilder был представлен после JDK 1.5.Самая большая разница между ним и StringBuffer заключается в том, что его последовательность методов является асинхронной.

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

Сильный толчок:Самое вдохновляющее руководство по самостоятельному изучению компьютера на GitHub (обновленная версия)