Java是一门解释性语言,需要将程序源代码编译成字节码才能在JVM上运行。在Java程序中,当需要使用某个类时,如果该类还没有被加载到内存中,系统会通过类加载器将该类加载进来。类加载的时机有以下几种情况:
- 在程序启动时,系统会自动加载某些类,例如main方法所在的类,因为该类是程序执行的入口。
- 当使用new关键字创建对象时,会先检查该对象的类是否已经被加载,如果没有则先进行加载。
- 当使用静态方法、静态属性时,也会进行加载。
- 当使用反射方式操作某个类时,该类也会被加载。
在JVM中,类的加载过程分为三个阶段:
- 加载阶段:类加载器通过类的全限定名读取二进制字节流,并将其转换成JVM中的类模板。在加载阶段,JVM会执行一些校验操作,例如文件格式的校验、字节码校验等。
- 链接阶段:将类模板中的符号引用替换成直接引用,这个过程分为验证、准备、解析三个阶段。验证阶段主要进行语义分析,检查类之间的依赖关系是否合法,准备阶段主要为类变量分配内存并初始化,解析阶段主要将符号引用转换成直接引用。
- 初始化阶段:为类变量赋初始值,并执行类构造器()方法的过程。
类加载器的概述和分类
类加载器负责加载类,并将其转换成JVM中的类模板。Java中的类加载器分为以下三种:
- 启动类加载器:负责加载JRE/lib目录下的核心类库,如rt.jar、charsets.jar等。
- 扩展类加载器:负责加载JRE/lib/ext目录下的扩展类库。
- 应用程序类加载器:也称为系统类加载器,负责加载用户类路径上的类。
类加载器还有一个重要的特性,就是双亲委派机制。即当一个类加载器需要加载某个类时,它会先将该任务委派给其父类加载器进行处理,如果父类加载器无法加载该类,再由子类加载器进行加载。这样可以保证类的唯一性,避免出现多个版本的类。
反射概述
Java反射机制是指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;并且可以在运行时调用该类的任意方法和属性。Java反射机制提供了程序在运行时动态加载、探知和使用编译期间完全未知的类。
Java反射机制主要有以下两个核心类:Class类和java.lang.reflect包中的各种类型。Class类代表一个类,在运行时可以动态地获取类的信息。java.lang.reflect包中的各种类型则提供了访问类的属性和方法等信息的方法。
Class.forName
Class.forName()方法可以动态加载类,该方法返回指定类名的Class对象。例如,如果需要动态加载com.example.MyClass类,可以使用以下代码:
Class<?> clazz = Class.forName("com.example.MyClass");
通过反射获取带参构造方法并使用
通过反射获取带参构造方法可以使用getConstructor(Class<?>…parameterTypes)方法。例如,如果需要获取com.example.MyClass类的一个带一个String类型参数的构造方法,可以使用以下代码:
Constructor<?> constructor = Class.forName("com.example.MyClass").getConstructor(String.class);
Object obj = constructor.newInstance("Hello World");
通过反射获取成员变量并使用
通过反射获取成员变量可以使用getField(String name)方法。例如,如果需要获取com.example.MyClass类的一个名为"myField"的成员变量,可以使用以下代码:
Field field = Class.forName("com.example.MyClass").getField("myField");
Object obj = field.get(myObject);
通过反射获取方法并使用
通过反射获取方法可以使用getMethod(String name, Class<?>…parameterTypes)方法。例如,如果需要获取com.example.MyClass类的一个名为"myMethod",带一个String类型参数的方法,可以使用以下代码:
Method method = Class.forName("com.example.MyClass").getMethod("myMethod", String.class);
Object obj = method.invoke(myObject, "Hello World");
通过反射越过泛型检查
Java中的泛型是在编译时进行类型检查的,而反射可以在运行时动态地获取类型信息,因此可以通过反射越过泛型检查。例如,如果需要向一个List类型的集合中添加一个String类型的元素,可以使用以下代码:
List<Integer> list = new ArrayList<>();
Method method = list.getClass().getMethod("add", Object.class);
method.invoke(list, "Hello World");
动态代理的概述和实现
动态代理是一种常见的设计模式,它可以在运行时动态地生成一个代理对象,该代理对象可以替代原始对象进行一些额外的操作。Java中的动态代理机制是基于反射实现的。例如,如果需要生成一个com.example.MyInterface接口的代理对象,可以使用以下代码:
MyInterface proxy = (MyInterface) Proxy.newProxyInstance(MyInterface.class.getClassLoader(),
new Class[] { MyInterface.class },
new MyInvocationHandler());
模版(Template)设计模式概述和使用读取配置文件举例
模版(Template)设计模式是一种常见的设计模式,它将一些固定的流程和操作封装在一个抽象类中,具体的实现可以在子类中进行。例如,如果需要读取一个配置文件,可以使用以下代码:
public abstract class ConfigReader {
public void readConfig() {
loadConfig();
parseConfig();
validateConfig();
}
protected abstract void loadConfig();
protected abstract void parseConfig();
protected abstract void validateConfig();
}
public class FileConfigReader extends ConfigReader {
protected void loadConfig() {
// 读取文件
}
protected void parseConfig() {
// 解析文件内容
}
protected void validateConfig() {
// 验证文件格式
}
}
public class Main {
public static void main(String[] args) {
ConfigReader reader = new FileConfigReader();
reader.readConfig();
}
}
以上是Java反射机制的主要内容,掌握这些知识点可以让我们在开发过程中更加灵活地使用Java语言,提高代码的可扩展性和可维护性。