Reflection
反射是被视为动态语言(在运行时代码可以根据某些条件改变自身结构)的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。通过这个对象看到类的结构
关于java.lang.Class类的理解
字节码
Java中引入了虚拟机的概念,即在机器和编译程序之间加入了一层抽象的虚拟的机器。这台虚拟的机器在任何平台上都提供给编译程序一个的共同的接口
编译程序只需要面向虚拟机,生成虚拟机能够理解的代码,然后由解释器来将虚拟机代码转换为特定系统的机器码执行。在Java中, 这种供虚拟机理解的代码叫做字节码(即扩展名为.class的文件), 它不面向任何特定的处理器,只面向虛拟机。每一种平台的解释器是不同的,但是实现的虚拟机是相同的。Java源程序经过编译器编译后变成字节码,字节码由虚拟机解释执行,虚拟机将每一要执行的字节码送给解释器,解释器将其翻译成特定机器上的机器码,然后在特定的机器上运行。这也就是解释了Java的编译与解释并存的特点
Java源代码—->编译器—>JVM可执行的字节码(即虚批指令—->jvm中解释—机器可执行的二进制机器码—->程序运行
采用字节码的好处:
Java语言通过字节码的方式,在一定程度 上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以Java程序运行时比较高效,而且,由于字节码并不专对一种特定的机器,因此,Java程序无须重新编译便可在多种不同的计算机上运行(跨平台)
类的加载过程:
程序经过**javac.exe**命令以后,会生成一个或多个**字节码文件(.class文件)**。接着使用**java.exe**命令对某个字节码文件进行解释运行。相当于**将某个字节码文件加载到内存中。此过程就称为类的加载**。**加载到内存中的类,称为运行时类**,**此运行时类,就作为Class的一个实例**<br />2.Class的实例就对应着一个运行时类<br />3.加载到内存中的运行时类,会缓存一定的时间。在此时间之内,可以通过不同的方式来获取此运行时类
获取Class类的实例
法一 调用运行时类的属性 .class
public void test(){
Class<User> clazz = User.class;
System.out.println(clazz);
}
法二 通过运行时类的对象
public void test2() {
User user = new User();
Class clazz = user.getClass();
System.out.println(clazz);
}
法三 调用 Class 的静态方法 forname(String classPath)
public void test3() throws ClassNotFoundException {
Class clazz = Class.forName("User");
System.out.println(clazz);
}
法四 使用类的加载器:ClassLoader
public void test4() throws ClassNotFoundException {
ClassLoader classLoader = reflectionTest.class.getClassLoader();
Class clazz4 = classLoader.loadClass("User");
System.out.println(clazz4);
}
注:只要数组的元素类型与维度一样,就是同一个Class
类的加载
类加载的作用
将class文件字节码内容加载到内存中,并将这些静态数据转换成方 法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为 方法区中类数据的访问入口
类缓存
标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器 中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象
ClassLoader
类加载器作用是用来把类(class)装载进内存的
ClassLoader加载配置文件
public void test2() throws Exception {
Properties pros = new Properties();
//此时的文件默认在当前的module下。
//读取配置文件的方式一:
// FileInputStream fis = new FileInputStream("jdbc.properties");
// FileInputStream fis = new FileInputStream("src/jdbc.properties");
// pros.load(fis);
//读取配置文件的方式二:使用ClassLoader
//配置文件默认识别为:当前module的src下
ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("jdbc.properties");
pros.load(is);
String user = pros.getProperty("user");
String password = pros.getProperty("password");
System.out.println("user = " + user + ",password = " + password);
}
通过反射创建运行时类的对象
#调用 newInstance()方法 创建对应的运行时类的对象
public void test3() throws Exception {
Class<User> clazz=User.class;
User instance = clazz.newInstance();
System.out.println(instance);
}
//获取运行时类的父类
@Test
public void test2(){
Class clazz = Person.class;
Class superclass = clazz.getSuperclass();
System.out.println(superclass);
}
//获取运行时类的带泛型的父类
@Test
public void test3(){
Class clazz = Person.class;
Type genericSuperclass = clazz.getGenericSuperclass();
System.out.println(genericSuperclass);
}
//获取运行时类的带泛型的父类的泛型 功能性代码 模板固定
@Test
public void test4(){
Class clazz = Person.class;
Type genericSuperclass = clazz.getGenericSuperclass();
//带参数的类型
ParameterizedType paramType = (ParameterizedType) genericSuperclass;
//获取泛型类型
Type[] actualTypeArguments = paramType.getActualTypeArguments();
// System.out.println(actualTypeArguments[0].getTypeName());
System.out.println(((Class)actualTypeArguments[0]).getName());
}
调用运行时类中的指定的属性
public void testField1() throws Exception {
Class clazz = Person.class;
//创建运行时类的对象
Person p = (Person) clazz.newInstance();
//1. getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
Field name = clazz.getDeclaredField("name");
//2.保证当前属性是可访问的
name.setAccessible(true);
//3.获取、设置指定对象的此属性值
name.set(p,"Tom");
System.out.println(name.get(p));
}
调用运行时类中的指定的方法
public void testMethod() throws Exception {
Class clazz = Person.class;
//创建运行时类的对象
Person p = (Person) clazz.newInstance();
//1.获取指定的某个方法
//getDeclaredMethod():参数1 :指明获取的方法的名称
参数2:指明获取的方法的形参列表
Method show = clazz.getDeclaredMethod("show", String.class);
show.setAccessible(true);
//3.调用方法的invoke():参数1:方法的调用者
参数2:给方法形参赋值的实参
//invoke()的返回值即为对应类中调用的方法的返回值
Object returnValue = show.invoke(p,"CHN"); //String nation = p.show("CHN");
System.out.println(returnValue);
//调用静态方法
Method showDesc = clazz.getDeclaredMethod("showDesc");
showDesc.setAccessible(true);
//如果调用的运行时类中的方法没有返回值,则此invoke()返回null
// Object returnVal = showDesc.invoke(null);
Object returnVal = showDesc.invoke(Person.class);
System.out.println(returnVal);
}