反射
反射在程序运行期间动态获取类和操纵类的一种技术。通过反射机制,可以在运行时动态地创建对象、调用方法、访问和修改属性,以及获取类的信息。
invoke 方法的执行流程如下:
- 查找方法:当通过 java.lang.reflect.Method 对象调用 invoke 方法时,Java 虚拟机(JVM)首先确认该方法是否存在并可以访问。这包括检查方法的访问权限、方法签名是否匹配等。
- 安全检查:如果方法是私有的或受保护的,还需要进行访问权限的安全检查。如果当前调用者没有足够的权限访问这个方法,将抛出 IllegalAccessException。
- 参数转换和适配:invoke 方法接受一个对象实例和一组参数,需要将这些参数转换成对应方法签名所需要的类型,并且进行必要的类型检查和装箱拆箱操作。
- 方法调用:对于非私有方法,Java 反射实际上是通过 JNI(Java Native Interface,Java 本地接口)调用到 JVM 内部的 native 方法,例如 java.lang.reflect.Method.invoke0()。这个 native 方法负责完成真正的动态方法调用。对于 Java 方法,JVM 会通过方法表、虚方法表(vtable)进行查找和调用;对于非虚方法或者静态方法,JVM 会直接调用相应的方法实现。
- 异常处理:在执行方法的过程中,如果出现任何异常,JVM 会捕获并将异常包装成 InvocationTargetException 抛出,应用程序可以通过这个异常获取到原始异常信息。
- 返回结果:如果方法正常执行完毕,invoke 方法会返回方法的执行结果,或者如果方法返回类型是 void,则不返回任何值。
无实例化
// 1.反射得到对象
Class<?> clazz = Class.forName("User");
// 2.得到方法
Method method = clazz.getDeclaredMethod("publicMethod");
// 得到私有方法
Method privateMethod = clazz.getDeclaredMethod("privateMethod");
// 设置私有方法可访问
privateMethod.setAccessible(true);
// 执行私有方法
privateMethod.invoke(clazz.getDeclaredConstructor().newInstance());
实例化
// 1.反射得到对象
Class<?> clazz = Class.forName("User");
// 2.得到私有方法
Method privateMethod = clazz.getDeclaredMethod("privateMethod");
// 3.设置私有方法可访问
privateMethod.setAccessible(true);
// 4.得到实例
Object user = clazz.getDeclaredConstructor().newInstance();
// 5.执行私有方法
privateMethod.invoke(user);
克隆
在编程中,克隆是指创建一个与原始对象相同的新对象。这个新对象通常具有与原始对象相同的属性和方法,但是它们是两个不同的对象,它们在内存中的位置不同。 在 Java 中,可以通过实现 Cloneable 接口和重写 clone() 方法来实现对象的克隆
浅克隆
-
克隆出来的新对象与原始对象共享引用类型的属性。也就是说,新对象中的引用类型属性指向的是原始对象中相同的引用类型属性。如果修改了新对象中的引用类型属性,原始对象中的相应属性也会被修改。在 Java 中,可以通过实现 Cloneable 接口和重写 clone() 方法来实现浅克隆。
public class CloneDemo {
public static void main(String[] args) {
Person p1 = new Person();
p1.setName("张三");
p1.setAge(18);
Address address = new Address();
address.setCity("北京");
p1.setAddress(address);
// 克隆 p1 对象
Person p2 = p1.clone();
System.out.println(p1 == p2); // false
System.out.println(p1.getAddress() == p2.getAddress()); // true
}
}
class Person implements Cloneable {
private String name;
private int age;
private Address address; // 引用类型
@Override
public Person clone() {
Person person = null;
try {
person = (Person) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return person;
}
// 忽略 getter 和 setter 方法
}
class Address {
private String city;
// 忽略 getter 和 setter 方法
}
深克隆
- 克隆出来的新对象与原始对象不共享引用类型的属性。也就是说,新对象中的引用类型属性指向的是新的对象,而不是原始对象中相同的引用类型属性。如果修改了新对象中的引用类型属性,原始对象中的相应属性不会被修改。
import lombok.Getter;
import lombok.Setter;
public class CloneDemo {
public static void main(String[] args) {
Person p1 = new Person();
p1.setName("张三");
p1.setAge(18);
// 引用类型
Address address = new Address();
address.setCity("北京");
p1.setAddress(address);
// 克隆 p1 对象
Person p2 = p1.clone();
// 对比引用类型的地址值是否相同
System.out.println(p1.getAddress() == p2.getAddress()); // false
}
}
@Getter
@Setter
class Person implements Cloneable {
private String name;
private int age;
private Address address; // 引用类型
@Override
public Person clone() {
Person person = null;
try {
person = (Person) super.clone();
// 克隆引用类型
person.setAddress(person.getAddress().clone());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return person;
}
}
@Getter
@Setter
class Address implements Cloneable {
private String city;
@Override
public Address clone() {
Address address = null;
try {
address = (Address) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return address;
}
}
杂记
下面代码相等
import lombok.Builder;
@Builder
public class Student {
private String name;
private int age;
}
Student stu = Student.builder()
.name("磊哥")
.age(18)
.build();
Student stu = GenericBuilder.of(Student::new)
.with(Student::setName, "磊哥")
.with(Student::setAge, 18)
.build();