Java八股(对象)
创建对象
使用 new 关键字创建
使用new关键字:这是最常见、最基础的创建对象方式。通过调用类的构造器来实例化对象。
这种创建对象的方式要求我们必须知道对象所属类的名称。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class Person { private String name; public Person() {} public Person(String name) { this.name = name; } public void sayHello() { System.out.println("Hello, " + name); } }
public class Main { public static void main(String[] args) { Person person1 = new Person(); Person person2 = new Person("Alice"); person2.sayHello(); } }
|
使用Class类的newInstance()方法
这是利用了Java的反射API,在运行的时候动态的创建对象,在这种方式下我们不需要知道具体的类。
应用场景:框架设计(如 Spring 的 IOC 容器)、动态代理等。
使用Constructor类的newInstance()方法
同样是通过反射机制,可以使用Constructor类的newInstance()方法创建对象。
1 2
| Constructor<MyClass> constructor = MyClass.class.getConstructor(); MyClass obj = constructor.newInstance();
|
使用clone()方法:
通过实现 Cloneable 接口并重写 Object 类的 clone() 方法,可以基于一个现有对象(原型)创建一个新的副本对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class Person implements Cloneable { private String name; @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
public class Main { public static void main(String[] args) { Person original = new Person("Charlie"); try { Person copy = (Person) original.clone(); copy.sayHello(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } } }
|
Object.clone() 默认是浅拷贝,对于引用类型的字段,复制的是引用地址,而不是引用的对象本身。如果需要深拷贝,必须在 clone() 方法中手动对引用对象进行克隆。
使用反序列化
通过 ObjectInputStream 从一个字节流(通常是文件或网络)中重建一个对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import java.io.*;
public class Person implements Serializable { private String name; }
public class Main { public static void main(String[] args) { Person personToSave = new Person("David"); try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.dat"))) { oos.writeObject(personToSave); } catch (IOException e) { e.printStackTrace(); } try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.dat"))) { Person restoredPerson = (Person) ois.readObject(); restoredPerson.sayHello(); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } } }
|
特点是不会调用类的任何构造器,类必须实现 java.io.Serializable 接口。
使用工厂模式
这是一种设计模式,不直接使用 new,而是通过一个方法来返回对象实例。 getInstance()、valueOf() 等都是常见的工厂方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class Person { private String name; private Person(String name) { this.name = name; } public static Person createPerson(String name) { return new Person(name); } }
public class Main { public static void main(String[] args) { Person person = Person.createPerson("Eva"); person.sayHello(); } }
|
优点是将对象的创建与使用分离,降低耦合,还可以隐藏创建对象的复杂逻辑(如池化技术、缓存)。
Java 标准库中的例子:Integer.valueOf(int),Calendar.getInstance()。
New出的对象什么时候回收?
由Java的垃圾回收器(Garbage Collector)负责回收,它会周期性地检测不再被引用的对象,并将其回收释放内存。
垃圾回收器算法:
- 引用计数法:某个对象的引用计数为0时,表示该对象不再被引用,可以被回收。
- 可达性分析算法:从根对象(如方法区中的类静态属性、方法中的局部变量等)出发,通过对象之间的引用链进行遍历,如果存在一条引用链到达某个对象,则说明该对象是可达的,反之不可达,不可达的对象将被回收。
- 终结器(Finalizer):如果对象重写了
finalize()方法,垃圾回收器会在回收该对象之前调用finalize()方法,对象可以在finalize()方法中进行一些清理操作。然而,终结器机制的使用不被推荐,因为它的执行时间是不确定的,可能会导致不可预测的性能问题。
私有对象
私有对象通常指的是类中被声明为 private 的成员变量或方法。由于 private 访问修饰符的限制,这些成员只能在其所在的类内部被访问。
可以通过下面两种方式来间接获取私有对象。
通过公共访问器方法访问私有对象
如果类的设计者遵循良好的编程规范,通常会为私有成员变量提供公共的访问器方法(即 getter 方法),通过调用这些方法可以安全地获取私有对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class MyClass { private String privateField = "私有字段的值";
public String getPrivateField() { return privateField; } }
public class Main { public static void main(String[] args) { MyClass obj = new MyClass(); String value = obj.getPrivateField(); System.out.println(value); } }
|
使用反射机制访问私有对象
反射机制允许在运行时检查和修改类、方法、字段等信息,通过反射可以绕过 private 访问修饰符的限制来获取私有对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import java.lang.reflect.Field;
class MyClass { private String privateField = "私有字段的值"; }
public class Main { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException { MyClass obj = new MyClass(); Class<?> clazz = obj.getClass(); Field privateField = clazz.getDeclaredField("privateField"); privateField.setAccessible(true); String value = (String) privateField.get(obj); System.out.println(value); } }
|