1 类加载器
1.1 类的加载
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
- 加载,就是指将class文件读入内存,并为之创建一个Class对象(字节码对象)
- 连接,验证是否有正确的内部结构,并和其他类协调一致。准备 负责为类的静态成员分配内存,并设置默认初始化值。解析将类的二进制数据中的符号引用替换为直接引用。
- 初始化,就是我们以前讲过的初始化步骤。
1.2 类的初始化时机
- 创建类的实例
- 类的静态变量,或者为静态变量赋值
- 类的静态方法
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
- 初始化某个类的子类
- 直接使用java.exe命令来运行某个主类
1.3 类的加载器
负责将.class文件加载到内在中,并为之生成对应的Class对象。
1.3.1 类的加载器组成
- Bootstrap ClassLoader 根类加载器,也被称为引导类加载器,负责Java核心类的加载。比如System,String等。在JDK中JRE的lib目录下rt.jar文件中。
- Extension ClassLoader 扩展类加载器,负责JRE的扩展目录中jar包的加载。在JDK中JRE的lib目录下ext目录。
- System ClassLoader 系统类加载器,负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。
2 反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象。
2.1 获取一个类的class文件对象的3中方法
- 对象获取;
- 类名获取;
- Class 类的静态方法获取;
package demo;
public class Person {
public String name;
private int age;
static {
System.out.println("静态代码块");
}
public Person() {
}
public Person( String name,int age) {
this.name = name;
this.age = age;
}
private Person(int age, String name) {
this.name = name;
this.age = age;
}
public void eat(){
System.out.println("吃饭");
}
public void sleep(String s,int a,double d){
System.out.println("在睡觉"+s+" "+a+" "+d);
}
public void playGame(){
System.out.println("人在打游戏");
}
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 + '}';
}
}
public static void main(String[] args)throws ClassNotFoundException {
//1.对象获取
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);//T
System.out.println(c.equals(c1));//T
//3.Class的静态方法forName(字符串的类名)
Class c2=Class.forName("demo.Person");
System.out.println(c2);
}
前两种你必须明确Person类型.
后面是指定这种类型的字符串就行.这种扩展更强.我不需要知道你的类.我只提供字符串,按照配置文件加载就可以了
2.2 反射获取class文件中的构造方法,运行构造方法(创建对象)
2.2.1 反射获取空参数的构造函数
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* 1.获取class文件对象
* 2.从class文件对象,获取需要的成员
*/
public class ReflactDemo1 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class c = Class.forName("demo.Person");
//getConstructors()获取class文件对象中所有的公共的构造方法
/* Constructor[] cons = c.getConstructors();
for(Constructor con:cons){
System.out.println(con);
}*/
//获取指定的构造函数,空参数的构造函数
Constructor con = c.getConstructor();
//运行空参数的构造方法
Object obj = con.newInstance();
System.out.println(obj);
}
}
2.2.2 反射获取有参数的构造方法
public class ReflactDemo2 {
public static void main(String[] args)throws Exception {
Class c=Class.forName("demo.Person");
//获取带有String 和 int 构造方法
//Constructor<T> getConstructor(Class<?>...parameterTypes)
Constructor con= c.getConstructor(String.class,int.class);
//运行构造方法
Object obj= con.newInstance("mark",1);
System.out.println(obj);
}
}
2.2.3 反射获取构造方法并运行的快速方式
- 前提:被反射的类,必须具有空参数的构造方法;
- 构造方法必须 public;
public static void main(String[] args) throws Exception {
Class c = Class.forName("demo.Person");
// Class类中定义方法,T newInstance()
//直接创建被反射类的对象实例
Object obj = c.newInstance();
System.out.println(obj);
}
2.2.4 反射获取私有构造方法并运行
package demo;
import java.lang.reflect.Constructor;
public class ReflactDemo4 {
public static void main(String[] args) throws Exception {
Class c = Class.forName("demo.Person");
// Constructor[] getDeclaredConstructors() 获取所有的构造方法,包括私有
/*Constructor[] cons = c.getDeclaredConstructors();
for(Constructor con:cons){
System.out.println(con);
}*/
//Constructor getDeclaredConstructor(Class...c)获取到指定参数列表的构造方法
Constructor con= c.getDeclaredConstructor(int.class,String.class);
//Constructor类,父类AccessibleObject,定义方法setAccessible(boolean b)
con.setAccessible(true);
Object obj= con.newInstance(18,"John");
System.out.println(obj);
}
}
2.2.5 反射获取成员变量并改值
import java.lang.reflect.Field;
public class ReflactDemo5 {
public static void main(String[] args) throws Exception {
Class c = Class.forName("demo.Person");
Object obj = c.newInstance();
//获取成员变量Class类的方法getFields() class文件中的所有公共的成员变量
//返回值 Field[],Field类描述成员变量对象的类
/*Field[] fields = c.getFields();
for(Field f:fields){
System.out.println(f);
}*/
//获取指定的成员变量
Field field= c.getField("name");
System.out.println(field);
//Field 类的方法 void set(Object obj,Object value)
//Object obj必须有对象类的支持,Object value 修改后的值
field.set(obj,"KKKKK");
System.out.println(obj);
}
}
2.2.6 反射获取空参数成员方法并运行
package demo;
import java.lang.reflect.Method;
public class ReflactDemo6 {
public static void main(String[] args)throws Exception {
Class c= Class.forName("demo.Person");
Object obj=c.newInstance();
//Methods[] getMethods 获取的是class 文件中的所有公共成员方法,包括继承的
//Method 类是描述成员方法的对象
/* Method[] methods=c.getMethods();
for(Method m:methods){
System.out.println(m);
}*/
//获取指定的方法eat运行
//Method getMethod(String methodName,Class...c)
Method method= c.getMethod("eat");
//System.out.println(method);
//运行eat
method.invoke(obj);
}
}
2.2.7 反射获取有参数成员方法并运行
public class ReflectDemo7 {
public static void main(String[] args) throws Exception {
Class c = Class.forName("demo.Person");
Object obj = c.newInstance();
Method method = c.getMethod("sleep", String.class, int.class, double.class);
//System.out.println(method);
method.invoke(obj,"休眠",100,889.99);
}
}
3 反射泛型擦除
import java.lang.reflect.Method;
import java.util.ArrayList;
/**
* 定义集合类,泛型String
* 要求向集合添加Integer类型
* 反射方式,获取出ArrayList类class文件对象
* 通过class文件对象,调用add方法
* 伪泛型:编译后的class文件,没有泛型的
*/
public class ReflectTest {
public static void main(String[] args) throws Exception {
ArrayList<String> array=new ArrayList<String>();
array.add("a");
//反射方式,获取出集合ArrayList类的class文件对象
Class c= array.getClass();
Method method= c.getMethod("add",Object.class);
// System.out.println(method);
method.invoke(array,150);
method.invoke(array,1500);
method.invoke(array,15000);
System.out.println(array);
}
}
4 反射通过配置文件
package demo3;
public class Person {
public void eat(){
System.out.println("人在吃饭");
}
}
import java.io.FileReader;
import java.lang.reflect.Method;
import java.util.Properties;
/**
* 调用Person方法,调用Student方法,调用Worker方法
* 类不清楚,方法也不清楚
* 通过配置文件实现此功能
* 运行的类名和方法名字,以键值对的形式写在文本
* 实现步骤:
* 1、 准备配置文件
* 2、 IO流读取配置文件,Reader
* 3、 文件中的键值对存储到集合中 Properties
* 4、 反射获取指定类的class 文件对象
* 5、 class 文件对象,获取指定的方法
*
*/
public class Test {
public static void main(String[] args) throws Exception {
FileReader r=new FileReader("config.properties");
Properties pro = new Properties();
pro.load(r);
r.close();
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);
}
}