类的加载器和反射
Posted lzw123-
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了类的加载器和反射相关的知识,希望对你有一定的参考价值。
类的加载
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
加载
就是指将class文件读入内存,并为之创建一个Class对象。
任何类被使用时系统都会建立一个Class对象
连接
验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 将类的二进制数据中的符号引用替换为直接引用
类的初始化时机
1. 创建类的实例
2. 类的静态变量,或者为静态变量赋值
3. 类的静态方法
4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
5. 初始化某个类的子类
6. 直接使用java.exe命令来运行某个主类
类加载器:
负责将.class文件加载到内存中,并为之生成对应的Class对象。
类加载器的组成
1. Bootstrap ClassLoader 根类加载器
也被称为引导类加载器,负责Java核心类的加载
比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
2. Extension ClassLoader 扩展类加载器
负责JRE的扩展目录中jar包的加载。
在JDK中JRE的lib目录下ext目录
3. System ClassLoader 系统类加载器
负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径
反射
Class类
获取Class对象的三种方式
//1通过Object类中的getObject()方法 Person p=new Person(); Class c=p.getClass(); System.out.println(c); //2通过类名获取,类名.class 获取到字节码文件对象 Class c1=Person.class; System.out.println(c1); System.out.println(c==c1);//true System.out.println(c.equals(c1));//true //3通过Class类的静态方法forName(包名.类名)获取 Class c2 = Class.forName("com.oracle.DEMO.Person");// 包名.类名 System.out.println(c2);
Person类:
package com.oracle.DEMO; public class Person { public String name; private int age; public Person(){ System.out.println("公共构造方法"); } public Person(String name,int age){ this.name=name; this.age=age; System.out.println("公共有参构造"); } private Person(int age,String name){ this.name=name; this.age=age; System.out.println("私有有参构造"); } public void eat(){ System.out.println("公共方法"); } public void sleep(String name){ this.name=name; System.out.println(name+"公共有参方法"); } private void playGames(){ System.out.println("私有有参方法"); } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } }
通过反射获取构造方法并使用
在反射机制中,把类中的成员(构造方法、成员方法、成员变量)都封装成了对应的类进行表示。其中,构造方法使用类Constructor表示。可通过Class类中提供的方法获取构造方法:
返回一个构造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes) 获取public修饰, 指定参数类型所对应的构造方法
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 获取指定参数类型所对应的构造方法(包含私有的)
返回多个构造方法
public Constructor<?>[] getConstructors() 获取所有的public 修饰的构造方法
public Constructor<?>[] getDeclaredConstructors() 获取所有的构造方法(包含私有的)
获取构造方法:
//获取构造方法 public static void method2() throws Exception{ //获取字节码文件对象 Class c = Class.forName("com.oracle.DEMO.Person"); /*Constructor[] cons=c.getConstructors(); //h=获取非私有构造方法数组 for(Constructor con:cons){ System.out.println(con); }*/ /* //获取空参构造 Constructor con=c.getConstructor(); // System.out.println(con); //使用空参构造创建对象 Object obj = con.newInstance(); Person p=(Person)obj; p.eat(); // System.out.println(obj); */ /*//获取有参构造并调用 Constructor con1 = c.getConstructor(String.class,int.class ); Object obj = con1.newInstance("张三",13); System.out.println(obj);*/ //针对空参构造有一个快捷创建对象的方式 //1Person类中必须有空参构造方法 //2.空参构造必须是public修饰’ Object obj = c.newInstance(); System.out.println(obj); }
通过反射方式,获取构造方法(包括私有),创建对象
public static void method3() throws Exception{ Class c = Class.forName("com.oracle.DEMO.Person"); /*//获得所有构造方法,包括私有 Constructor[] con = c.getDeclaredConstructors(); for(Constructor co:con){ System.out.println(co); }*/ //获取私有构造方法并使用 Constructor con = c.getDeclaredConstructor(int.class,String.class); con.setAccessible(true);//取消Java检查,通过反射获取私有构造---暴力反射,不推荐使用 //破坏了程序的封装性和安全性,好比抢银行 Object obj = con.newInstance(13,"王五"); System.out.println(obj); }
通过反射,创建对象,获取指定的成员变量,进行赋值与获取值操作
返回一个成员变量
public Field getField(String name) 获取指定的 public修饰的变量
public Field getDeclaredField(String name) 获取指定的任意变量
返回多个成员变量
public Field[] getFields() 获取所有public 修饰的变量
public Field[] getDeclaredFields() 获取所有的 变量 (包含私有)
public static void method4() throws Exception{ Class c = Class.forName("com.oracle.DEMO.Person"); Object obj = c.newInstance(); //获取公共成员变量并使用 /*Field field=c.getField("name"); field.set(obj,"张三");*/ // System.out.println(obj); //获取私有成员并使用 Field f=c.getDeclaredField("age"); f.setAccessible(true); f.set(obj, 18); Field f1=c.getDeclaredField("name"); f1.setAccessible(true); f1.set(obj, "小王"); System.out.println(f.get(obj)); System.out.println(f1.get(obj)); }
获取成员方法并使用:
public static void method5() throws Exception{ //获取字节码文件对象 Class c = Class.forName("com.oracle.DEMO.Person"); Object obj = c.newInstance(); //获取空参的成员方法 并运行 Method method=c.getMethod("eat"); method.invoke(obj); //获取有参成员方法并运行 Method method1=c.getMethod("sleep",String.class); method1.invoke(obj,"张三"); }
泛型擦除
程序编译后产生的.class文件中是没有泛型约束的,这种现象我们称为泛型的擦除。
package com.oracle.DEMO; import java.lang.reflect.Method; import java.util.ArrayList; public class demo02 { public static void main(String[] args)throws Exception { //泛型擦除,泛型不进class文件 ArrayList<String> arr=new ArrayList<String>(); arr.add("1"); Class c = arr.getClass(); Method method1=c.getMethod("add",Object.class); method1.invoke(arr, 1.5); System.out.println(arr); } }
反射配置文件
测试类:
package com.oracle.DeGai; import java.io.FileReader; import java.lang.reflect.Method; import java.util.Properties; public class Test { public static void main(String[] args) throws Exception { // new Person().eat(); //反射配置文件实现 //把我们要运行的类和方法,以键值对的形式写在文本中 //运行哪个类的方法,只需要改配置文件即可 //1准备配置文件,键值对 //2.IO读取配置文件,Reader //3.将文件中的键值对保存在Properties集合中,键值对,就i是类和方法名 //4.通过反射获取指定类的class文件对象 //5.通过class文件对象获取指定方法 //6.运行方法 FileReader fr=new FileReader("src/config.properties"); Properties pro=new Properties(); pro.load(fr); String className=pro.getProperty("className"); String methodName=pro.getProperty("methodName"); //获取字节码文件对象 Class c = Class.forName(className); Object obj = c.newInstance(); Method method=c.getMethod(methodName); method.invoke(obj); } }
配置文件内容例子:
className=com.oracle.DeGai.Student
methodName=study
以上是关于类的加载器和反射的主要内容,如果未能解决你的问题,请参考以下文章