Java 反射
关于java.lang.Class类的理解
- 类的加载过程
代码经过javac.exe命令之后 会生成一个或多个字节码文件(.class结尾)
接着我们使用java.exe命令对某个字节码进行解释运行 相当于某个字节码文件加载到内存中 此过程叫做类的加载 加载到内存中的类 我们叫做运行时类 此运行时类 即叫做Class的一个实例
- 换句话说 Class实例对应着一个运行时类
我们来看一下获取Class实例的几种方式
package com.zyk;
public class ReflectionTest {
public static void main(String[] args) throws ClassNotFoundException {
//调用运行时类的属性 .class
Class clazz1=Person.class;
System.out.println(clazz1);
//调用运行时类的对象
Person person=new Person();
Class clazz2=person.getClass();
System.out.println(clazz2);
//调用Class的静态方法
Class clazz3=Class.forName("com.zyk.Person");
System.out.println(clazz3);
//使用类的加载器
ClassLoader classLoader= ReflectionTest.class.getClassLoader();
Class clazz4=classLoader.loadClass("com.zyk.Person");
System.out.println(clazz4);
}
}
结果
class com.zyk.Person
class com.zyk.Person
class com.zyk.Person
class com.zyk.Person
了解类的加载器 ClassLoader
Java把加载动作放到JVM外部实现,以便让应用程序决定如何获取所需的类,JVM提供了3种类加载器,启动类加载器(Bootstrap ClassLoader)、扩展类加载器(Extension ClassLoader)、应用程序类加载器(Application ClassLoader)
我们看一个代码案例
package com.zyk;
public class ClassLoaderTest {
public static void main(String[] args) {
//对于自定义类 使用应用类加载器加载
ClassLoader classLoader1 = ClassLoaderTest.class.getClassLoader();
System.out.println(classLoader1);
//调用getParent 获取扩展类加载器
ClassLoader classLoader2 = classLoader1.getParent();
System.out.println(classLoader2);
//获取启动类加载器 只不过显示不出来
//启动类加载器主要负责加载Java的核心类库 比如String
ClassLoader classLoader3 = classLoader2.getParent();
System.out.println(classLoader3);
}
}
结果
sun.misc.Launcher$AppClassLoader@18b4aac2//应用类加载器
sun.misc.Launcher$ExtClassLoader@1540e19d//扩展类加载器
null//启动加载器
通过反射创建运行时类的对象
package com.zyk;
public class NewInstanceTest {
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
Class<Person> personClass = Person.class;
Person person = personClass.newInstance();
System.out.println(person);
}
}
结果
Person{id=0, name='null'}
注意
运行时类必须提供空参的构造器 后期框架中 便于利用反射创建运行时类的对象
体会反射的动态性
package com.zyk;
import java.util.Random;
public class Test {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
for (int i = 0; i <10 ; i++) {
//生成随机数
int num=new Random().nextInt(3);
String path="";
switch (num){
case 0:
path="com.zyk.Person";
break;
case 1:
path="java.util.Date";
break;
case 2:
path="java.lang.Object";
break;
}
Object o=getInstance(path);
System.out.println(o);
}
}
public static Object getInstance(String path) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class clazz = Class.forName(path);
Object obj= clazz.newInstance();
return obj;
}
}
结果
Person{id=0, name='null'}
Person{id=0, name='null'}
Fri Apr 10 14:51:55 CST 2020
Fri Apr 10 14:51:55 CST 2020
java.lang.Object@135fbaa4
Person{id=0, name='null'}
java.lang.Object@45ee12a7
java.lang.Object@330bedb4
Fri Apr 10 14:51:55 CST 2020
java.lang.Object@2503dbd3
通过上面的例子 我们看出 只有在运行的时候 我们才能决定要去创建哪个对象 这就是反射的动态性的体现