——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
一.类的加载
概念
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
加载
就是指将class文件读入内存,并为之创建一个Class对象。
任何类被使用时系统都会建立一个Class对象。
连接
验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 将类的二进制数据中的符号引用替换为直接引用
初始化
class类的初始化时机
创建类的实例
访问类的静态变量,或者为静态变量赋值
调用类的静态方法
使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
初始化某个类的子类
直接使用java.exe命令来运行某个主类
类的加载器
概念
负责将.class文件加载到内存中,并为之生成对应的Class对象。
虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行。
类的加载器的组成
Bootstrap ClassLoader 根类加载器
Java核心类的加载器,在JDK中JRE的lib目录下rt.jar文件中
Extension ClassLoader 扩展类加载器
第三方扩展类的加载器,在JDK中JRE的lib目录下ext目录,我们自己定义的类也可以放到该目录中
Sysetm ClassLoader 系统类加载器
负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径
,我们自己定义的类加载时都用此加载器
二.反射
概念
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
获取class文件对象的三种方式
1.调用Object类的getClass()方法(任何类都会继承此方法),该方法不是静态的,所以只能通过对象调用
2.任何的数据类型(包括基本类型)都有一个静态的class属性,比如:Student.class,int.class
3.调用Class类的静态方法forName(String 全名限定的类名):(常用)
通过Class对象获取和调用构造方法
批量获取构造方法:
Constructor[] getConstructors() :获取所有的”公有构造方法”
Constructor[] getDeclaredConstructors() :获取全部(包括私有)的构造方法;
获取某个构造方法
Constructor getConstructor(Class … parameterTypes) :获取指定的”公有构造方法”;
Constructor getDeclaredConstructor(Class … parameterTypes) :获取指定的构造方法(包括私有的);
调用某个构造方法:
Constructor的 Object newInstance(Object… initargs) :调用指定构造方法,并实例化此类的对象;
暴力访问:如果私有成员,需要设置暴力访问;
Constructor的setAccessible(true):不进行权限检查;
通过Class对象获取成员属性并对成员属性赋值
批量获取成员属性:
Field[] getFields():获取所有”公有属性”;
Field[] getDeclaredFields() :获取所有成员属性(包括私有):
获取单个成员属性:
Field getField(String name) :获取指定的”公有属性”;
Field getDeclaredField(String name) :获取指定的属性,包括私有的;
为成员属性赋值:
Field的 void set(Object obj, Object value)
注意:1.一定要先实例化此类对象;2.访问私有属性前,要设置暴力访问
通过Class对象获取成员方法并调用
批量获取成员方法:
Method[] getMethods():获取所有”公有方法”(包括继承的)
Method[] getDeclaredMethods() :获取所有成员方法(包括私有):
获取单个成员方法:
Method getMethod(String name, Class… parameterTypes) :获取指定的”公有方法”;
Method getDeclaredMethod(String name, Class… parameterTypes) :获取指定的方法,包括私有的;
调用方法:
Method:
Object invoke(Object obj, Object… args) :调用Method所代表的方法:
返回值:此Method对象调用所代表的方法,所获取的返回值;
形参:
obj:方法所属对象;
args:Method所代表的那个方法的实参列表;
注意:1.要先创建此类的对象;2.调用私有方法前,要先设置暴力访问。
三.反射应用案例
通过反射运行配置文件内容
要求:
通过反射运行配置文件中的内容,达到只需修改配置文件即可进行程序版本更新的效果
代码实现:
public class Demo {
public static void main(String[] args) throws Exception{
//获取要操作的类的全名限定类名
String classPath = getValue("className");
//获取Class对象
Class stuClass = Class.forName(classPath);
//创建Student对象
Object obj = stuClass.getConstructor().newInstance();
//获取并调用方法
Method met = stuClass.getMethod(getValue("methodName"));
met.invoke(obj);
}
public static String getValue(String key) throws IOException{
//创建配置文件对象
Properties pro = new Properties();
//创建输入流
FileReader in = new FileReader("properties.ini");//该指定的配置文件中存储了类名和方法名信息,可以更改此处的数据进行程序版本的更新
//通过输入流将指定配置文件中的内容存储到对象中
pro.load(in);
//关闭资源
in.close();
//将传入的参数做键到配置文件中查找对应的值并将其返回
return pro.getProperty(key);
}
}
class Student1 {
public void show1(){
System.out.println("第一个版本打印");
}
}
class Student2 {
public void show2(){
System.out.println("第二个版本打印");
}
}
通过反射越过泛型检查
要求:
定义一个具有String泛型的集合,成功的向该集合中添加一个int类型数据
原理分析:
Java中泛型只存在于编译器,当java文件被编译成class文件后,会将泛型丢弃,所以我们可以通过反射将不是指定泛型类型的数据成功添加到集合中去。
代码实现:
public class Demo {
public static void main(String[] args) throws Exception{
//定义一个String泛型的集合
ArrayList<String> list = new ArrayList<>();
//向集合中添加元素
list.add("aaa");
list.add("bbb");
//list.add(1);
//获取Class对象
Class listClass = list.getClass();
//获取add方法
Method add = listClass.getMethod("add", Object.class);
//调用方法
add.invoke(list, 1);
//遍历集合,看是否添加成功
for(Object o : list){
System.out.println(o);
}
}
}