-
java 代码在计算机中经历的阶段
- 编写源代码:首先,你需要使用文本编辑器或集成开发环境(IDE)编写Java源代码文件。源代码是以.java文件扩展名保存的文本文件,包含了Java编程语言的语法和逻辑。
- 编译源代码:一旦源代码编写完成,你需要使用Java编译器(javac命令)将源代码编译成字节码文件。编译器将检查语法错误、类型检查等,并将源代码翻译成平台无关的字节码。
- 字节码文件:编译完成后,生成的字节码文件以.class文件扩展名保存。这些文件包含了Java虚拟机(JVM)可以理解和执行的指令。
- 类加载:在运行Java程序之前,JVM需要将字节码加载到内存中。类加载器负责加载字节码文件,并将其转换为运行时的类对象。类加载器按需加载类,确保类在首次使用之前被加载。
- 字节码验证:在加载字节码文件后,JVM会对字节码进行验证,以确保其符合Java虚拟机的安全和规范要求。验证过程包括类型检查、访问权限验证、指令验证等。
- 解释和执行:一旦字节码通过验证,JVM将使用解释器或即时编译器(JIT编译器)来执行字节码指令。解释器逐条解释字节码指令并执行,而即时编译器将热点代码编译成本地机器码以提高执行性能。
- 运行时优化:在执行过程中,JVM还会进行运行时优化。它使用各种技术,如方法内联、垃圾回收、逃逸分析等,来改善程序的性能和内存管理。
- 程序输出:一旦程序执行完成,它可以产生输出,如控制台输出或写入文件。这些输出是根据程序的逻辑和代码来生成的。
- 总体来说,Java代码的执行经历了编写源代码、编译、类加载、验证、解释/编译执行以及运行时优化等阶段,最终产生程序的输出结果。这个过程是由Java虚拟机负责管理和执行的
-
Java反射的原理
-
简述:将类的各个组成部分封装为其他对象,这就是反射机制
-
获取Class对象:Java反射的入口是通过获取类的Class对象。Class对象包含了类的完整结构信息,包括类的名称、父类、接口、字段、方法等。
Class<?> clazz = ClassName.class; // 或者 Class<?> clazz = Class.forName(“package.ClassName”); -
类加载器:在Java中,类的加载是由类加载器(ClassLoader)负责的。类加载器根据类的名称查找并加载对应的字节码文件,并将其转换为一个或多个Class对象。类加载器可以从文件系统、网络、JAR文件等位置加载类。
-
Class对象的创建:当类加载器加载类的字节码文件后,会将其转换为一个Class对象。该Class对象包含了类的所有信息,并被存储在方法区(Method Area)中。
-
类信息的存储:在方法区中,JVM会存储类的结构信息,包括字段、方法、构造函数、注解等。这些信息可以通过Class对象的方法进行访问和操作。
-
访问和操作类的成员:通过Class对象,可以获取类的字段、方法、构造函数等成员对象,并进行调用和操作。Java反射提供了一系列的方法,如getMethods()、getField()、getMethod()等,用于获取和操作类的成员。
-
动态创建对象和调用方法:使用Class对象,可以动态地创建类的实例,并调用类的方法。通过newInstance()方法可以创建对象,通过invoke()方法可以调用方法。
Person.java: package com.test; public class Person { private String name; private int age; public String a; protected String b; String c; private String d; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", a='" + a + '\'' + ", b='" + b + '\'' + ", c='" + c + '\'' + ", d='" + d + '\'' + '}'; } public void eat(){ System.out.println("eat..."); } public void eat(String food){ System.out.println("eat..."+food); } } ReflectDemo1.java: public class ReflectDemo1 { /** 获取Class对象的方式: 1. Class.forName("全类名"):将字节码文件加载进内存,返回Class对象 多用于配置文件,将类名定义在配置文件中。读取文件,加载类 2. 类名.class:通过类名的属性class获取 多用于参数的传递 3. 对象.getClass():getClass()方法在Object类中定义着 多用于对象的获取字节码的方式 结论:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一 */ public static void main(String[] args) throws Exception { //1.Class.forName("全类名") Class cls1 = Class.forName("com.test.Person"); System.out.println(cls1); //2.类名.class Class cls2 = Person.class; System.out.println(cls2); //3.对象.getClass() Person p = new Person(); Class cls3 = p.getClass(); System.out.println(cls3); //== 比较三个对象 System.out.println(cls1 == cls2);//true System.out.println(cls1 == cls3);//true Class c = Student.class; System.out.println(c == cls1); } }
-
-
Class对象功能:
-
获取功能:
-
获取成员变量们
-
Field[] getFields() :获取所有public修饰的成员变量
-
Field getField(String name) 获取指定名称的 public修饰的成员变量
-
Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
-
Field getDeclaredField(String name)
-
-
获取构造方法们
-
Constructor<?>[] getConstructors()
-
Constructor getConstructor(类<?>… parameterTypes)
-
Constructor getDeclaredConstructor(类<?>… parameterTypes)
-
Constructor<?>[] getDeclaredConstructors()
-
-
获取成员方法们:
-
Method[] getMethods()
-
Method getMethod(String name, 类<?>… parameterTypes)
-
Method[] getDeclaredMethods()
-
Method getDeclaredMethod(String name, 类<?>… parameterTypes)
-
-
获取全类名
- String getName()
public class ReflectDemo2 { public static void main(String[] args) throws Exception { //0.获取Person的Class对象 Class personClass = Person.class; /* 1. 获取成员变量们 * Field[] getFields() * Field getField(String name) * Field[] getDeclaredFields() * Field getDeclaredField(String name) */ //1.Field[] getFields()获取所有public修饰的成员变量 Field[] fields = personClass.getFields(); for (Field field : fields) { System.out.println(field); } System.out.println("------------"); //2.Field getField(String name) Field a = personClass.getField("a"); //获取成员变量a 的值 Person p = new Person(); Object value = a.get(p); System.out.println(value); //设置a的值 a.set(p,"张三"); System.out.println(p); System.out.println("==================="); //Field[] getDeclaredFields():获取所有的成员变量,不考虑修饰符 Field[] declaredFields = personClass.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println(declaredField); } //Field getDeclaredField(String name) Field d = personClass.getDeclaredField("d"); //忽略访问权限修饰符的安全检查 d.setAccessible(true);//暴力反射 Object value2 = d.get(p); System.out.println(value2); //Constructor<T> getConstructor(类<?>... parameterTypes) Constructor constructor = personClass.getConstructor(String.class, int.class); System.out.println(constructor); //创建对象 Object person = constructor.newInstance("张三", 23); System.out.println(person); System.out.println("----------"); Constructor constructor1 = personClass.getConstructor(); System.out.println(constructor1); //创建对象 Object person1 = constructor1.newInstance(); System.out.println(person1); Object o = personClass.newInstance(); System.out.println(o); //constructor1.setAccessible(true); //获取指定名称的方法 Method eat_method = personClass.getMethod("eat"); Person p = new Person(); //执行方法 eat_method.invoke(p); Method eat_method2 = personClass.getMethod("eat", String.class); //执行方法 eat_method2.invoke(p,"饭"); System.out.println("-----------------"); //获取所有public修饰的方法 Method[] methods = personClass.getMethods(); for (Method method : methods) { System.out.println(method); String name = method.getName(); System.out.println(name); //method.setAccessible(true); } //获取类名 String className = personClass.getName(); System.out.println(className);//com.test.Person } }
-
-
Invoke机制:
invoke() 是 Java 中的一个反射工具方法,用于调用指定方法,并传递相应的参数。该方法可以在程序运行时动态调用指定对象的方法,并执行相应的操作。
invoke() 方法的语法如下:
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
其中,obj 参数是要调用方法的对象,args 参数是要传递给方法的参数列表,该方法返回一个 Object 类型的值,表示方法的返回值。
需要注意的是,使用 invoke() 方法调用方法时,需要注意方法的访问修饰符。如果要调用的方法是 private 或 protected 访问修饰符的,需要首先使用 setAccessible(true) 方法将其设置为可访问,否则将抛出 IllegalAccessException 异常。
下面是一个示例代码,演示如何使用 invoke() 方法调用指定对象的方法:
public class MyClass {
public void doSomething(String arg1, int arg2) {
// 执行某些操作
}
}
public class Main {
public static void main(String[] args) throws Exception {
// 创建 MyClass 的实例
MyClass myClass = new MyClass();
// 获取 doSomething 方法的 Method
Method method = MyClass.class.getMethod("doSomething", String.class, int.class);
// 调用 doSomething 方法
method.invoke(myClass, "hello", 123);
}
}
在上面的代码中,我们首先创建了 MyClass 的实例 myClass,然后使用 getMethod() 方法获取了 doSomething() 方法的 Method 对象 method。最后,我们使用 invoke() 方法调用了 doSomething() 方法,并传递了两个参数 “hello” 和 123。需要注意的是,doSomething() 方法必须是 public 访问修饰符的,否则会抛出 IllegalAccessException 异常。
实际案例:
pro.properties
className=cn.test.Student
methodName=sleep
Student.java
package com.test
public class Student {
public void sleep(){
System.out.println("sleep...");
}
}
ReflectTest.java
public class ReflectTest {
public static void main(String[] args) throws Exception {
Properties properties = new Properties();
Classloader classloader = ReflectTest.class.getClassloader();
InputStream is = classloader.getResourceAsStream("pro.properties");
pro.load(is);
// 获取配置文件中定义的数据;
String className = properties.getProperty("className");
String methodName = properties.getProperty("methodName");
// 获取到类的class对象
Class class1 = Class.forName(className);
// 创建对象并指定类的实例
Obejct obj = class1.newInstance();
// 获取方法的对象
Method method1 = class1.getMethod();
// 执行方法
method1.invoke(obj);
}
}