类加载器

类加载

当程序要使用某个类时, 如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类进行初始化。如果不出现意外情况,JVM将会连续完成这三个步骤,所以有时候也把这三个步骤统称为类加载或者类初始化

类加载的三个步骤

类的加载

  1. - 就是指将class文件读入内存,并为之创建一个java.lang.Class对象
  2. - 任何类被使用时,系统都会为之建立一个java.lang.Class对象

类的连接

  1. - 验证阶段:用于检验被加载的类是否有正确的内部结构,冰河其他类协调一致
  2. - 准备阶段:负责为类的类变量分配内存,并设置默认初始化值
  3. - 解析阶段:将类的二进制数据中的符号引用替换为直接引用

类的初始化

  1. - 在该阶段,主要就是对类变量进行初始化

类初始化的三个步骤

  1. - 假如类还未被加载和连接,则程序加载并连接该类
  2. - 假如该类的直接父类还未被初始化,则先初始化其直接父类
  3. - 假如类中有初始化语句,则系统依次执行这些初始化语句
  • 注意:因为执行第二部初始化父类,所以系统也会对其直接父类执行类初始化三步骤
  • 所以JVM每次执行都是执行的Java.lang.Object

类初始化的时机

  1. - 创建类的实例
  2. - 调用类的类方法
  3. - 访问类或者接口的类变量,或者为该类变量赋值
  4. - 使用反射方式来强制创建某个类或接口对的Java.lang.Class对象
  5. - 初始化其个类的子类
  6. - 直接使用java.exe命令来运行某个主类

类加载器

类加载器的工作机制

虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制

类加载器(ClassLoader)

classloader顾名思义,即是类加载

常用的类加载器

image.png

  1. 1. **引导类加载器(bootstrap class loader):**

它用来加载 Java 的核心库(jre/lib/rt.jar),是用原生C++代码来实现的,并不继承自java.lang.ClassLoader。加载扩展类和应用程序类加载器,并指定他们的父类加载器,在java中获取不到。

  1. 2. **扩展类加载器(extensions class loader):**

它用来加载 Java 的扩展库(jre/ext/*.jar)。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。

  1. 3. **系统类加载器(system class loader):**

它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过
ClassLoader.getSystemClassLoader()来获取它。

  1. 4. **自定义类加载器(custom class loader):**

除了系统提供的类加载器以外,开发人员可以通过继承 java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求。
image.png

反射

反射是框架设计的灵魂

(使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码))

反射概述

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
反射就是把java类中的各种成分映射成一个个的Java对象
例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。(其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述)
实际上,我们创建的每一个类也都是对象,即类本身是java.lang.Class类的实例对象。这个实例对象称之为类对象,也就是Class对象。那么,Class对象又是什么对象呢?
熟悉一下加载的时候:Class对象的由来是将xxx.class文件读入内存,并为之创建一个Class对象。Class的对象就是一个一个xxx.class文件
如图是类的正常加载过程:反射的原理在与class对象。
Java学习笔记XXXX -- 反射 - 图3

获取Class类的对象

我们想通过反射区使用一个类,首先我们要获取到对该类的字节码文件对象,也就是类型为Class类型的对象

三种方式获取Class类型的对象

  1. - 使用类的class属性来获取该类对于的Class对象。例如:Student.class将返回Student类对应的Class对象
  2. - 调用对象的getClass()方法,返回该对象所属类对应的Class对象
  3. - 该方法是Object类中的方法,所有的Java对象都可以调用该方法
  4. - 使用Class类中的静态方法forName(String className),该方法需要传入字符串参数,该字符串参数的值是某个类的全路径,也就是完成包名的路径

/三种方式获取Class类型的对象/
// 使用类的class属性来获取该类对于的Class对象。
// 例如:Student.class将返回Student类对应的Class对象
Class<Student> sc1 = Student.class;
System.out.println(sc1); // class com_1.Student
Class<Student> sc2 = Student.class;
System.out.println(sc1 == sc2); // true
// 调用对象的getClass()方法,返回该对象所属类对应的Class对象
Student s = new Student();
// 该方法是Object类中的方法,所有的Java对象都可以调用该方法
Class<? extends Student> sc3 = s.getClass();
System.out.println(sc1 == sc3); // true
//使用Class类中的静态方法forName(String className),
//该方法需要传入字符串参数,该字符串参数的值是完成包名的路径
Class<?> sc4 = Class.forName(“com_1.Student”);
System.out.println(sc1 == sc4); //true

获取Class类反射对象的 字段|构造方法|方法

  1. - 字段:就是成员变量(Field
  2. - 构造方法(Construstor
  3. - 方法(Method

总结:
所有的都有指定获取以及所有获取

  1. - getFields()/getField(Sting s)
  2. - getConstructors()/getConstructor(String s)
  3. - getMethods/getMethod(String s)

所有封装(private)修饰的成员变量,构造方法,方法都需要通过
getDeclaredxx获取该私有的,以及setAccessible(boolean b)设置通过访问

  1. - getDeclaredxxs()/getDeclaredxx(String s)
  2. - setAccessible(true)

使用成员变量

  1. - 需要使用Class获取的class文件创建Class对象
  2. - 再通过Class的创造器获取需要反射的成员对象
  3. - 在使用创建的Class对象中的set(obj,s)
  4. - 将需要设置的成员对象,以及参数传入

使用成员方法

  1. - 需要使用Class获取的class文件创建Class对象
  2. - 再通过Class的创造器获取需要反射的成员方法
  3. - 在使用创建的Class对象中的invoke(obj, args)
  4. - 将需要调用的成员方法传入。args是成员方法中需要的参数

反射获取构造方法并使用

Class类中用于获取构造方法的方法

Class类的类表示正在运行的Java应用程序中的类和接口。

  1. - getConstructors()

返回包含Constructor对象的数组,Constructor对象反射由此表示的类的所有公共构造函数

  1. - getConstructor(类<?>... parameterTypes)

返回一个 Constructor对象,该对象反映 Constructor对象表示的类的指定的公共构造函数。

  1. - getDeclaredConstructor(类<?>... parameterTypes)

返回一个 Constructor对象,该对象反映 Constructor对象表示的类或接口的指定类构造函数。

  1. - 参数:要获取的构造方法的参数的个数,数据类型对应的字节码文件对象

Constructor类中用于创建对象的方法

  1. - Constructor 提供了一个类的单个构造函数的信息和访问。
  2. - newInstance(Object... initargs)

使用此 Constructor对象表示的构造函数,使用指定的初始化参数来创建和初始化构造函数的声明类的新实例。

利用反射机制创建对象基本步骤

与传统的通过new 来获取对象的方式不同反射机制,反射会先拿到Student的“类对象”,然后通过类对象获取“构造器对象”再通过构造器对象创建一个对象,具体步骤:

1.获取类对象 Class class = Class.forName(“包名.类”);
2.获取构造器对象 Constructor con = class.getConstructor(形参.class);
3 获取对象 类名 对象 =con.newInstance(实参);
[

](https://blog.csdn.net/lililuni/article/details/83449088)

反射个人总结:

  1. 1. 通过Class类获取类的字节码对象--三种方法
  2. - Class**<**?**> **sc = Class.**forName(**"com_1.Student"**)**;
  3. 2. 使用获取到的字节码对象获取该对象的构造方法 -- 两种方法
  4. - Constructor**<**?**> **con = sc.getConstructor**()**;
  5. 3. 使用构造方法中的方法获取无参构造方法的数据 -- newInstance()
  6. - Object obj = con.newInstance**()**;

反射获取构造方法并使用练习

练习1: 通过反射实现如下操作

  1. - Student s = new Student("XX",30,"XX");
  2. - Sout(s);
  3. - 基本数据类型(int.class)也是可以得到对应的Class类型的

代码实现:
//创建一个学生类
public class Student {
private String name;
int age;
public String address;
public Student() {}
private Student(String name) {this.name = name;}
public Student(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address; }
public String getName() {return name;}
private void method() {System.out.println(“私有方法”);}
public void method1() {System.out.println(“公共方法”);}
public void method2(String s) {System.out.println(s);}
public String method3(String s, int i) {return s + “,” + i;}
@Override
_public String toString() {
return “Student{“ +
“name=’” + name + ‘\’’ +
“, age=” + age +
“, address=’” + address + ‘\’’ +
‘}’; }
public void setName(String _name
) {this.name = name;}
public int getAge() {return age;}
public void setAge(int age) {this.age = age;}
public String getAddress() {return address;}
public void setAddress(String address) {this.address = address;}}

// 通过Class类获取Student.class对象
Class<?> c = Class.forName(“com_01.Student”);
// 使用Class的获取单个构造类Constructor对象
// 参数是getConstructor(形参.class); int.class基本数据类型也可以得到class对象
Constructor<?> con = c.getConstructor(String.class, int.class, String.class);
// 使用Constructor的newInstance(参数)方法
Object o = con.newInstance(“李畅”, 23, “濮阳”);
System.out.println(o);
练习2: 通过反射实现如下操作

  1. - Student s = new Student("XX");
  2. - Sout(s);
  3. - 使用Construstor中的setAccessible(boolean flag): 值为true。则可以通过访问检查

代码实现:
// 私有构造方法访问
// private Student(String name) {this.name = name;}
// 使用Class类中getDeclaredConstructor(形参.class),指定构造函数
Constructor<?> con = c.getDeclaredConstructor(String.class);
// 使用构造器访问class文件私有构造方法报错
// 使用Constructor中的setAccessible(boolean flag)暴力反射
con.setAccessible(true);// 设置访问为true
Object 李畅 = con.newInstance(“李畅”);
System.out.println(李畅);

反射获取成员变量并使用

成员变量=字段=Field

反射拆解class文件的方法都是对应的,有获取公共的也有获取所有的
Field[] getFields()
返回包含一个数组 Field对象反射由此表示的类或接口的所有可访问的公共字段类对象。
Field[] getDeclaredFields()
返回的数组 Field对象反映此表示的类或接口声明的所有字段 类对象。
Field getField(String name)
返回一个 Field对象,它反映此表示的类或接口的指定公共成员字段 类对象。
Field getDeclaredField(String name)
返回一个 Field对象,它反映此表示的类或接口的指定已声明字段 类对象。

Field提供有关类或接口的单个字段的信息和动态访问
void set(Object obj, Object value)
将指定对象参数上的此 Field对象表示的字段设置为指定的新值。
代码实现:
// 访问所有格公共成员变量
Field[] fields = c.getFields();
for (Field f:fields){
// public java.lang.String com_01.Student.address
System.out.println(f); }
// 获取所有的成员变量—setAccessible
Field[] dfs = c.getDeclaredFields();
for (Field f: dfs){
//private java.lang.String com_01.Student.name
//int com_01.Student.age
//public java.lang.String com_01.Student.address
System.out.println(f); }
// 获取单个成员变量
// 使用Filed类中的set指定反射的对象,并使用getField指定的字段赋值
fieldAddress.set(obj,”濮阳”);

反射获取成员变量并使用练习

练习1: 通过反射实现如下操作

  1. - Student s = new Student();
  2. - s.name = "xx";
  3. - s.age =30;
  4. - s.address = "xx";
  5. - Sout(s);

代码实现:
// 通过Class类获取Student.class对象
Class<?> c = Class.forName(“com_01.Student”);
// 反射方法创建对象
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
// 使用Fields获取获取成员变量
// name成员变量是私有所以使用getDeclaredField()
Field name = c.getDeclaredField(“name”);
// 私有变量访问通过检查setAccessible
name.setAccessible(true);
// 使用Filed给反射的对象,以及指定的字段赋值
name.set(obj,”李畅”);
//Student{name=’李畅’, age=0, address=’null’}
// int age = 30;
Field age = c.getDeclaredField(“age”);
age.setAccessible(true);
age.set(obj,30);
// Student{name=’李畅’, age=30, address=’null’}
Field address = c.getDeclaredField(“address”);
address.setAccessible(true);
address.set(obj,”濮阳”);
//Student{name=’李畅’, age=30, address=’濮阳’}
System.out.println(obj);

反射获取成员方法并使用

获取指定的公共成员方法
Method[] getMethod(String name, 类<?>… parameterTypes)
返回一个 方法对象,它反映此表示的类或接口的指定公共成员方法 类对象。
Method getDeclaredMethod(String name, 类<?>… parameterTypes)
返回一个 方法对象,它反映此表示的类或接口的指定声明的方法 类对象。

获取所有的公共成员方法
Method[] getMethods()
返回包含一个数组 方法对象反射由此表示的类或接口的所有公共方法 类对象,包括那些由类或接口和那些从超类和超接口继承的声明。
Method[] getDeclaredMethods()
返回包含一个数组 方法对象反射的类或接口的所有声明的方法,通过此表示 类对象,包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。

Method类 方法提供有关类和接口上单一方法的信息和访问权限
Object invoke(Object obj, Object… args)
在具有指定参数的 方法对象上调用此 方法对象表示的底层方法。
Object: invoke 返回值类型
obj: 调用该方法的对象
args: 方法需要的参数
代码实现:
// 通过Class类获取Student.class对象
Class<?> c = Class.forName(“com_01.Student”);
// 获取成员所有公共方法
Method[] methods = c.getMethods();
for (Method m: methods){ System.out.println(m);}
// 获取成员私有方法
Method[] dm = c.getDeclaredMethods();
for (Method m: dm){ System.out.println(m);}
// 获取单个指定公共方法无参
Method method1 = c.getMethod(“method1”);
method1.setAccessible(true);
//public void com_01.Student.method1()
System.out.println(method1);
// 获取单个指定公共方法带参
Method method2 = c.getMethod(“method2”, String.class);
// public void com_01.Student.method2(java.lang.String)
// 获取单个指定公共方法带返回值的方法
Method method3 = c.getMethod(“method3”, String.class, int.class);
// public java.lang.String com_01.Student.method3(java.lang.String,int)
// 获取指定的私有方法
// 私有通过getDeclaredMethod(),并且设置通过权限setAccessible
Method method = c.getDeclaredMethod(“method”);
method.setAccessible(true);
// private void com_01.Student.method()
System.out.println(method);
// 调用成员变量方法,获取单个指定的无参公共方法
Method method1 = c.getMethod(“method1”);
method1.setAccessible(true);
// 使用获取的公共方法
// 先通过Class构造器构造对象
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
// Object invoke(Object obj, Object… args)
// Object: invoke 返回值类型 obj: 调用该方法的对象 args: 方法需要的参数
// private void method() 没有返回值void,无参
method1.invoke(obj); // 调用方法
// 获取单个指定的无参公共方法
// 调用成员变量方法,获取单个指定的带参公共方法
// 获取单个指定的带参公共方法
Method method2 = c.getMethod(“method2”, String.class);
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
method2.invoke(obj,”带参方法参数”); // 调用带参方法
// 调用成员变量方法,获取单个指定的带参带返回值公共方法
Method method3 = c.getMethod(“method3”, String.class, int.class);
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
method3.invoke(obj,”带返回值”,23);
System.out.println(method3.invoke(obj,”带返回值”,23));

反射获取成员方法并使用练习

练习1: 通过反射实现如下操作

  1. - Student s = new Student();
  2. - s.method();
  3. - s.method1();
  4. - s.method2();
  5. - Sout(s.method3(););

代码实现:
// 通过Class类获取Student.class对象
Class<?> c = Class.forName(“com_01.Student”);
// 通过Class获取的class文件创建反射对象
Constructor<?> con = c.getConstructor();
Object obj = con.newInstance();
Method method1 = c.getMethod(“method1”);
method1.invoke(obj); // 公共方法
Method method2 = c.getMethod(“method2”, String.class);
method2.invoke(obj,”李畅”); // 李畅
Method method3 = c.getMethod(“method3”, String.class, int.class);
Object 李畅 = method3.invoke(obj, “李畅”, 23);
System.out.println(李畅); // 李畅,23
// 私有方法需要通过getDeclaredMethod获取,并且设置通过访问权限
Method method = c.getDeclaredMethod(“method”);
method.setAccessible(true);
method.invoke(obj); // 私有方法

反射练习

练习1 : 我有一个ArrayList集合,现在我想在这个集合中添加一个字符串数据,如何实现?
代码实现:
// 创建集合
ArrayList<Integer> al = new ArrayList<>();
/al.add(10);
al.add(20);
al.add(“hello”);
/
// 使用反射获取集合的Class类的对象
Class<? extends ArrayList> c = al.getClass();
// 使用Class的getMethod()获取ArrayList的add()方法
Method add = c.getMethod(“add”, Object.class);
// public boolean java.util.ArrayList.add(java.lang.Object)
// 可以看出该集合的参数是Object类型,可以直接将ArrayList传入obj这个对象
// 通过反射方法给ArrayList添加集合,
// 反射可以消除固定的泛型类型,并且越过访问检查(setAccessible)
add.invoke(al, “hello”);
add.invoke(al, “world”);
add.invoke(al, “java”);
add.invoke(al,1);
System.out.println(al); // [hello, world, java, 1]
练习2: 通过配置文件运行类中的方法 — spring的properties文件
Java学习笔记XXXX -- 反射 - 图4
代码实现:
// 创建一个学生类
public class Student {
public void study(){ System.out.println(“爱生活,爱Java”); }}
——————————————————-
// 创建一个教师类
public class Teacher {
public void teacher(){ System.out.println(“教你做人”); } }
——————————————————-
// 配置一个类properties文件(class.txt)
className=com_02.Student
methodName=study
——————————————————-
/ 配置文件:
class.txt
className = xxx.xxx(配置的带包名的类)
methodName = method(配置的类的方法)
/
// 使用Properties类—将Map以字节传输
// Properties类则是properties文件和程序的中间桥梁
// 不论是从properties文件读取信息还是写入信息到properties文件都要经由Properties类。
Properties prop = new Properties();
// 使用字符流InputStreamReader-FileReader读入文档
FileReader fr = new FileReader(“E:\IdeaProjects\test01\class.txt”);
// 获得文件的输入流再使用Properties类的load()方法加载到Properties对象中
prop.load(fr);
fr.close(); // 关闭字符流
/ 因为Properties类是键值对传输,配置文件也是以key=value写入配置
className=com_02.Student
methodName=study
/
// 使用Properties类的getProperty获取键
String className = prop.getProperty(“className”);
String methodName= prop.getProperty(“methodName”);
// 通过反射使用,将每个属性拆解成一个class的对象
Class<?> c = Class.forName(className);// 指向的是com_02.Student
Constructor<?> con = c.getConstructor(); // 构造器获取对象
Object obj = con.newInstance(); // 创建对象
// 使用Class类的getMethod方法获取方法
Method method = c.getMethod(methodName); // 指向的是study
method.invoke(obj); // 爱生活,爱Java