Разница между клонированием и копированием
Клонированием называют поверхностное копирование (то, что реализуется защищенным по умолчанию методом clone любого потомка Object). Однако копирование может быть и полным.
То есть клонирование — просто частный случай копирования.
Остальные ответы
Операция клонирования (Edit • Clone) отличается от дублирования (копирования) двумя особенностями. Во-первых, клон наследует свойства контура и заполнения исходного объекта и сохраняет связь с этими свойствами: изменение свойств исходного объекта вызывает соответствующие изменения у клона. Во-вторых, операция клонирования «одноступенчатая»: нельзя создать клон от клона.
Чем отличается копирование от клонирования java
При работе с объектами классов надо учитывать, что они все представляют ссылочные типы, то есть указывают на какой-то объект, расположенный в памяти. Чтобы понять возможные трудности, с которыми мы можем столкнуться, рассмотрим пример:
public class Program < public static void main(String[] args) < Person tom = new Person("Tom", 23); tom.display(); // Person Tom Person bob = tom; bob.setName("Bob"); tom.display(); // Person Bob >> class Person < private String name; private int age; Person(String name, int age)< this.name=name; this.age=age; >void setName(String name) < this.name = name; >void setAge(int age) < this.age = age; >void display() < System.out.printf("Person Name: %s \n", name); >>
Здесь создаем два объекта Person и один присваиваем другому. Но, несмотря на то, что мы изменяем только объект bob, вместе с ним изменяется и объект tom. Потому что после присвоения они указывают на одну и ту же область в памяти, где собственно данные об объекте Person и его полях и хранятся.

Чтобы избежать этой проблемы, необходимо создать отдельный объект для переменной bob, например, с помощью метода clone :
class Person implements Cloneable < private String name; private int age; Person(String name, int age)< this.name=name; this.age=age; >void setName(String name) < this.name = name; >void setAge(int age) < this.age = age; >void display() < System.out.printf("Person %s \n", name); >public Person clone() throws CloneNotSupportedException < return (Person) super.clone(); >>
Для реализации клонирования класс Person должен применить интерфейс Cloneable , который определяет метод clone . Реализация этого метода просто возвращает вызов метода clone для родительского класса — то есть класса Object с преобразованием к типу Person.
Кроме того, на случай если класс не поддерживает клонирование, метод должен выбрасывать исключение CloneNotSupportedException , что определяется с помощью оператора throws .
Затем с помощью вызова этого метода мы можем осуществить копирование:
try < Person tom = new Person("Tom", 23); Person bob = tom.clone(); bob.setName("Bob"); tom.display(); // Person Tom >catch(CloneNotSupportedException ex)
Однако данный способ осуществляет неполное копирование и подойдет, если клонируемый объект не содержит сложных объектов. Например, пусть класс Book имеет следующее определение:
class Book implements Cloneable < private String name; private Author author; public void setName(String n)< name=n;>public String getName() < return name;>public void setAuthor(String n) < author.setName(n);>public String getAuthor() < return author.getName();>Book(String name, String author) < this.name = name; this.author = new Author(author); >public String toString() < return "Книга '" + name + "' (автор " + author + ")"; >public Book clone() throws CloneNotSupportedException < return (Book) super.clone(); >> class Author < private String name; public void setName(String n)< name=n;>public String getName() < return name;>public Author(String name) < this.name=name; >>
Если мы попробуем изменить автора книги, нас постигнет неудача:
try < Book book = new Book("War and Peace", "Leo Tolstoy"); Book book2 = book.clone(); book2.setAuthor("Ivan Turgenev"); System.out.println(book.getAuthor()); >catch(CloneNotSupportedException ex)
В этом случае, хотя переменные book и book2 будут указывать на разные объекты в памяти, но эти объекты при этом будут указывать на один объект Author.

И в этом случае нам необходимо выполнить полное копирование. Для этого надо определить метод клонирования у класса Author:
class Author implements Cloneable < // остальной код класса public Author clone() throws CloneNotSupportedException< return (Author) super.clone(); >>
И затем исправим метод clone в классе Book следующим образом:
public Book clone() throws CloneNotSupportedException
Какой вариант лучше: клонирование или копирование конструкторов?
Вот как я начал писать эту статью. Я читал это утверждение много раз: «Клонирование становится трудным, когда у объекта есть ссылки на изменяемые конечные поля». И каждый раз, когда я гуглю об этом, понимаю, что именно это означает, и как часть процесса забываю об этом тоже. Поэтому я подумал, что я буду вести блог, чтобы это послужило моей непосредственной ссылкой.
Клонирование объекта, которое я мог вспомнить из моего курса ООП в аспирантуре, – это создание аналогичной копии объекта, которая в основном должна соответствовать следующим правилам:
- x.clone ()! = x
- x.clone (). getClass () == x.getClass ()
- x.clone (). равно (х)
Обратите внимание, что условие (1) всегда должно выполняться во всех случаях. Хотя условия (2) и (3) не являются абсолютными требованиями, хорошо спроектировать метод клонирования таким образом, чтобы он оставался в силе. Прежде чем продолжить обсуждение, вот сигнатура метода клона в классе Object:
protected native Object clone() throws CloneNotSupportedException;
Итак, как вы заметили, защищенный модификатор, мы не можем вызвать метод clone () непосредственно для любого объекта. Мы должны переопределить этот метод как открытый метод и обеспечить реализацию для него в нашем классе, чтобы получить к нему доступ. Если никакой конкретной реализации не требуется, мы можем просто вернуть super.clone (). Поскольку ковариантные возвраты возможны после Java 5, мы можем изменить возвращаемое значение clone, чтобы вернуть объект нашего класса. Итак, если мы пишем наш класс работника, вот как метод clone () хотел бы:
public Employee clone() throws CloneNotSupportedException < return (Employee) super .clone();
Но обратите внимание, что метод clone в классе Object проверяет, реализует ли наш класс интерфейс Cloneable. Если он не реализует его, он создает исключение CloneNotSupportedException. В противном случае он создает новую копию. Но обратите внимание, что метод clone никогда не вызывает конструктор для создания копии объекта. Поэтому, если вы хотите отслеживать количество экземпляров, создаваемых для класса, увеличивая статический счетчик внутри конструктора, это не сработает, так как конструктор никогда не вызывается. Вместо этого метод clone выполняет полевое копирование свойств экземпляра из памяти объекта и возвращает его вызывающей стороне. Поэтому класс должен реализовать интерфейс маркера Cloneable, если он должен предоставить возможность клонировать его без получения исключения CloneNotSupportedException. Но обратите внимание, что код, который вызывает clone (), должен обрабатывать это исключение. В противном случае это приведет к ошибке компилятора. Да, это болевая точка, и за это критикуют.
Давайте рассмотрим пример: Case (1) :
Java Core. Вопросы к собеседованию, ч. 3

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

Глубокое копирование и поверхностное копирование
- Если класс имеет только члены примитивных типов, то будет создана совершенно новая копия объекта и возвращена ссылка на этот объект.
- Если класс содержит не только члены примитивных типов, а и любого другого типа класса, тогда копируются ссылки на объекты этих классов. Следовательно, оба объекта будут иметь одинаковые ссылки.
- Нет необходимости копировать отдельно примитивные данные;
- Все классы-члены в оригинальном классе должны поддерживать клонирование. Для каждого члена класса должен вызываться super.clone() при переопределении метода clone() ;
- Если какой-либо член класса не поддерживает клонирование, то в методе клонирования необходимо создать новый экземпляр этого класса и скопировать каждый его член со всеми атрибутами в новый объект класса, по одному.
Что такое синхронизация? Блокировка на уровне объекта и блокировка на уровне класса?
Синхронизация относится к многопоточности. Синхронизированный блок кода может исполняться одновременно только одним потоком. Java позволяет обрабатывать одновременно несколько потоков. Это может привести к тому, что два или более потока хотят получить доступ к одному и тому же полю. Синхронизация позволяет избежать ошибок памяти, возникающих при неправильном использовании ресурсов памяти. Когда метод объявлен как синхронизированный, нить удерживает его монитор. Если другая нить пытается в это время получить доступ к синхронизированному методу, то поток блокируется, и ждет освобождения монитора. Синхронизация в Java осуществляется специальным ключевым словом synchronized. Вы можете помечать таким образом отдельные блоки или методы в вашем классе. Ключевое слово synchronized не может быть использовано вместе с переменными или атрибутами класса. Блокировка на уровне объекта – механизм, когда вы хотите синхронизировать non-static метод или non-static блок кода таким образом, что только один поток сможет выполнить блок кода в данном экземпляре класса. Это нужно всегда делать, чтобы сделать экземпляр класса потокобезопасным. Блокировка на уровне класса предотвращает вход нескольких потоков в синхронизированный блок для всех доступных экземпляров класса. Например, если есть 100 экземпляров класса DemoClass, то только 1 поток сможет выполнить demoMethod () используя одну из переменных в определенный момент времени. Это должно быть всегда сделано, что бы обеспечить безопасность статического потока. Узнайте больше о синхронизации здесь.
Какая разница между sleep() и wait()?
Sleep() — это метод, который используется, чтобы задержать процесс на несколько секунд. В случае с wait() , нить находится в состоянии ожидания, пока мы не вызовем метод notify() или notifyAll() . Основное различие заключается в том, что wait() снимает блокировку монитора, в то время как sleep() не освобождает блокировку. Wait() используется для многопотоковых приложений, sleep() используют просто для паузы выполнения нити. Thread.sleep() ставит текущий поток в «Not Runnable» состояние на определенное количество времени. Нить сохраняет состояние монитора, которое было до вызова данного метода. Если же другая нить вызывает t.interrupt() , нить которая «заснула» — проснется. Обратите внимание, что sleep() является статическим методом, что означает, что он всегда влияет на текущий поток (тот, который выполняет метод sleep() ). Распространенной ошибкой является вызов t.sleep() , где t является другим потоком; даже тогда, когда текущая нить, которая вызвала метод sleep() , не является t нитью. Object.wait() посылает текущий поток в «Not Runnable» состояние на некоторое время, так же как и sleep() , но все же с некоторым нюансом. Wait() вызывается для объекта, а не для нити; мы называем этот объект “lock object”. Перед вызовом lock.wait() , текущая нить должна быть синхронизирована с “lock object”; wait() после этого снимает эту блокировку, и добавляет нить в ”wait list” связанный с этой блокировкой. Позже, другая нить может быть синхронизирована с тем же самым lock object и вызвать метод lock.notify() . Этот метод «разбудит» оригинальную нить, которая все еще ждет. В принципе, wait() / notify() можно сравнить с sleep() / interrupt() , только активной нити не нужен прямой указатель на спящую нить, нужно только знать общий lock object. Читайте детальную разницу здесь.
Можно ли присвоить null к this ссылочной переменной?
Нет, нельзя. В Java левая часть оператора присваивания должен быть переменной. «This» — это специальное ключевое слово, которое всегда дает текущий экземпляр класса. Это не любая переменная. Точно также, null нельзя присвоить переменной, используя ключевое слово “super” или любое другое подобное.
Какая разница между && и &?
- & оценивает обе стороны операции;
- && оценивает левую сторону операции. Если она true, он продолжает оценить правую сторону.
Как переопределить equals() и hachCode() методы?
- Всегда используйте одинаковые атрибуты объектов при генерации hashCode() и equals() ;
- Симметричность. Т.е. если для каких-либо объектов x и y x.equals(y) возвращает true, то и y.equals(x) должен возвращать true;
- Рефлексивность. Для любого объекта x x.equals(x) должен возвращать true;
- Постоянство. Для любых объектов x и y x.equals(y) возвращает одно и то же, если информация, используемая в сравнениях, не меняется;
- Транзитивность. Для любых объектов x , y и z , если x.equals(y) вернет true и y.equals(z) вернет true, то и x.equals(z) должен вернуть true;
- Всякий раз, когда метод вызывается у одного и того же объекта во время выполнения приложения, он должен возвращать одно и то же число, если используемая информация не изменяется. hashCode может возвращать разные значения для идентичных объектов в различных экземплярах приложения;
- Если два объекта равны, согласно equals , то их hashCode должны возвращать одинаковые значения;
- Обратное требование необязательно. Два неравных объекта могут возвращать одинаковый hashCode. Однако для повышения производительности, лучше, чтобы разные объекты возвращали разные коды.
Расскажите про модификаторы доступа

Классы Java, поля, конструкторы и методы могут иметь один из четырех различных модификаторов доступа: private Если метод или переменная помечены как private, то только код внутри одного класса может получить доступ к переменной, или вызвать метод. Код внутри подклассов не может получить доступ к переменной или методу, также как и не может получить доступ из любого другого класса. Модификатор доступа private чаще всего используется для конструкторов, методов и переменных. default Модификатор доступа default объявляется в том случае, если модификатор не указан вообще. Данный модификатор означает, что доступ к полям, конструкторам и методам данного класса может получить код внутри самого класса, код внутри классов в том же пакете. Подклассы не могут получить доступ к методам и переменным – членам суперкласса, если они объявлены как default, если подкласс не находится в том же пакете, что и суперкласс. protected Модификатор protected работает так же, как и default, за исключением того, что подклассы так же могут получить доступ к защищенным методам и переменным суперкласса. Данное утверждение является верным, даже если подкласс не находится в том же пакете, что и суперкласс. public Модификатор доступа public означает, что весь код может получить доступ к классу, его переменным, конструкторам или методам, независимо от того, где расположен этот код.
Что такое сборщик мусора? Можем ли мы вызвать его?
Сбор мусора является функцией автоматического управления памятью во многих современных языках программирования, таких как Java и языков в NET.Framework. Языки, которые используют сбор мусора, часто интерпретируют сбор мусора в виртуальной машине, такой как JVM. Сбор мусора имеет две цели: любая неиспользованная памяти должна быть освобождены, и память не должна быть освобождены, если программа еще будет ее использовать. Можете ли вы запустить сбор мусора вручную? Нет, System.gc() предоставляет вам настолько большой доступ, насколько можно. Лучшим вариантом является вызов метода System.gc() , который намекнет сборщику мусора про необходимость запуска. Нет никакого способа запустить его немедленно, так как сборщик мусора является недетерминированным. Кроме того, согласно документации, OutOfMemoryError не будет проброшена, если виртуальной машине не удалось освободить память после полной сборки мусора. Узнайте больше о сборщике мусора здесь.
Что означает ключевое слово native? Объясните в деталях
- Вы должны вызвать библиотеку из Java, которая написана на другом языке.
- Вам нужен доступ к системным или аппаратным ресурсам, к которым можно получить доступ только используя другой язык (как правило, С). На самом деле, многие системные функции, которые взаимодействуют с реальным компьютером (например диски или сетевые данные) могут быть вызваны только native методом.
- JNI/JNA могут дестабилизировать JVM, особенно если вы попытаетесь сделать что-то сложное. Если ваш native метод делает что-то неправильно, есть вероятность аварийного завершения JVM. Также, неприятные вещи могут произойти, если ваш native метод вызывается из нескольких нитей. И так далее.
- Программу с native кодом сложнее дэбажить.
- Native код требует отдельного построения фрэймворков, что может создать проблемы с переносом на другие платформы.
Что такое сериализация?
- Удаление поля;
- Перемещение класса вверх или вниз по иерархии;
- Изменение non-static поля на static или non-transient на transient;
- Изменение объявленного типа примитивный данных;
- Изменение метода WriteObject или ReadObject так, что они больше не пишут или не читают поля по умолчанию;
- Изменение класса Serializable на Externalizable или наоборот;
- Изменение класса enum на non-enum или наоборот;
- Удаление Serializable или Externalizable ;
- Добавление writeReplace или readResolve метода к классу.
- Добавление полей;
- Добавление/удаление классов;
- Добавление методов WriteObject/ReadObject [методы defaultReadObject или defaultWriteObject должны быть вызваны в начале];
- Удаление методов WriteObject/ReadObject ;
- Добавление java.io.Serializable ;
- Изменение доступа к полю;
- Изменение static поля на non-static или transient на non-transient.