目
录
一.类加载器
1.类的加载过程概述
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过 加载,连接,初始化三步 来实现对这个类的初始化。
- 加载 就是指将class文件读入内存,并为之创建一个Class对象。 (任何类被使用时系统都会建立一个Class对象。)
- 连接 包含验证,准备,解析等过程。验证,是否有正确的内部结构,并和其他类协调一致。准备,负责为类的静态成员分配内存,并设置默认初始化值。解析,把类中的符号引用转换为直接引用。
- 初始化 就是我们以前学过的初始化步骤。
2.类的加载时机
- 创建类的实例 new Student()
- 访问类的静态变量,或者为静态变量赋值 Math.PI Math.class
- 调用类的静态方法 Math.abs()
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
- 初始化某个类的子类
- 直接使用java.exe命令来运行某个主类
3.类加载器的概述
类加载器负责将.class文件加载到内在中,并为之 生成对应的Class对象。
4.类加载器的分类及各类类加载器的作用
- Bootstrap ClassLoader 根类加载器 , 也被称为引导类加载器,负责Java核心类的加载 。比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
- Extension ClassLoader 扩展类加载器 , 负责JRE的扩展目录中 jar包的加载 。在JDK中JRE的lib目录下ext目录
- Sysetm ClassLoader 系统类加载器 , 负责在JVM启动时 加载来自java命令的class文件 ,以及classpath环境变量所指定的jar包和类路径
5.类加载器的双亲委派机制
工作原理:
- 如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行;
- 如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达项层的启 动类加载器;
- 如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派机制。
为什么要采用双亲委派机制?
- 避免核心API被篡改
- 避免类的重复加载
说通俗点, 如果可以随意的就去执行自己写的String了,那么有人进行恶意攻击, 给你发送一个String类,项目就会直接崩溃! 所以这也是一种保护。
二.反射机制
1.反射概述
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取类的信息以及动态调用对象的方法的功能称为java语言的反射机制。要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象。反射就是在运行状态中的一种动态调用方法或者属性的一种机制.就是 获取字节码文件对象,然后剖析该类中存在哪些构造方法(Constructor),哪些成员变量(Field),哪些成员方法(Method)
2.获取class文件对象的三种方式
- Object类的 getClass() 方法
- 静态 属性class
- Class类中静态方法 forName() 补:public static Class forName(String className), 其中的className表示的是一个类对应的全类名(就是需要加上包名)
public class MyTest {
public static void main(String[] args) throws ClassNotFoundException {
//方式1:getClass方法
Student student = new Student();
Student student2 = new Student();
Class<? extends Student> aClass = student.getClass();
Class<? extends Student> aClass1 = student2.getClass();
//方式2:每个类,都有一个 class属性,通过他就可以获取
Class<Student> studentClass = Student.class;
//方式3:Class类中的一个静态方法forName
Class<?> aClass2 = Class.forName("org.xingyun.demo2.Student");
}
}
package org.xingyun.demo2;
public class Student {
}
三.通过反射获取构造方法,成员变量和成员方法
1.获取构造方法
之前我们创建一个类的对象,通过new调用构造方法来创建,因为只要能调用到这个类的构造方法,就能创建该类的对象。现在采用反射的方式来创建一个类的对象, 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。 通过这个类的字节码文件对象,就能够获取到这个类的构造方法对象。 获取构造方法对象的方法如下:
- public Constructor<?>[] getConstructors() //获取所有的 公共的构造方法 对象不包含私有的
- public Constructor<?>[] getDeclaredConstructors() //获取 所有的构造方法 对象,包括私有的
- public Constructor<T> getConstructor(Class<?>... parameterTypes) //获取 单个的公共的构造方法 对象, 不包含私有的
- public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) //获取 单个的构造方法 对象,包含私有的
public class MyTest {
public static void main(String[] args) throws ClassNotFoundException {
//获取字节码文件对象(方式3)
Class<?> aClass = Class.forName("org.xingyun.demo3.Student");
//获取所有的公共的构造方法对象
Constructor<?>[] constructors = aClass.getConstructors();
//循环输出
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
//获取所有的构造方法对象包括私有的构造方法对象
Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
//获取空参的构造方法对象
Constructor<?> constructor = aClass.getConstructor();
System.out.println(constructor);
Constructor<?> constructor2 = aClass.getConstructor(String.class);
System.out.println(constructor2);
Constructor<?> constructor3 = aClass.getConstructor(String.class, int.class);
System.out.println(constructor3);
//获取私有的构造方法对象
Constructor<?> constructor4 = aClass.getDeclaredConstructor(String.class, int.class, double.class);
System.out.println(constructor4);
}
}
2.获取成员变量
- public Field[] getFields() //获取所有 公共的成员变量 对象包含从父类继承过来的
- public Field[] getDeclaredFields() //获取 所有的成员变量 对象,包含私有的 也包含从父类继承过来的成员变量
- public Field getField(String name) //获取 单个公共的成员变量 对象
- public Field getDeclaredField(String name) //获取 单个成员变量 对象(包含私有成员变量)
- set(); //给成员变量设置值,set后面可以跟很多数据类型,因此可以设置不同数据类型的数据,例如:setInt()等等.
- get(); //获取成员变量的值
- setAccessible(true); //取消私有权限检查(私有成员方法必须取消的哈)
public class MyTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
//获取字节码文件对象,org.xingyun.demo5为路径名
Class<?> aClass = Class.forName("org.xingyun.demo5.Teacher");
//获取所有的成员变量对象,包含私有的
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
//获取所有公共的成员变量对象
Field[] fields = aClass.getFields();
for (Field field : fields) {
System.out.println(field);
}
//获取单个公共的成员变量对象
Field nameField = aClass.getField("name");
System.out.println(nameField);
//获取单个成员变量对象(包含私有成员变量)
Field sal = aClass.getDeclaredField("sal");
System.out.println(sal);
//给姓名字段设置值
nameField.set(teacher, "张三");
//获取字段对象的值
String nameValue = (String) nameField.get(teacher);
System.out.println(nameValue);
//给年龄字段设置值
Field ageField = aClass.getField("age");
ageField.setInt(teacher, 20);
//取出年龄字段值
int anInt = ageField.getInt(teacher);
System.out.println(anInt);
Field salField = aClass.getDeclaredField("sal");
salField.setAccessible(true); //取消私有权限的检查
salField.setDouble(teacher, 3.25);
double aDouble = salField.getDouble(teacher);
System.out.println(aDouble);
}
}
public class Teacher {
public String name;
public int age;
private double sal;
}
3.获取成员方法
- public Method[] getMethods() //获取所有的 公共的成员方法 不包含私有的 包含从父类继承过来的过来的公共方法
- public Method[] getDeclaredMethods() //获取自己的 所有成员方法 包含私有的
- public Method getMethod(String name,Class<?>... parameterTypes) //获取 单个的公共方法 不包含私有的(参数1: 方法名称 参数2:方法行参的class 对象)
- public Method getDeclaredMethod(String name,Class<?>... parameterTypes) //获取 单个成员方法 包括私有的
- dogClass.newInstance(); //通过空参构造创建对象
- show.invoke(dog1); //调用方法执行,show为方法名,dog1为创建的对象
public class MyTest {
public static void main(String[] args) throws NoSuchMethodException {
//获取字节码文件对象
Class<Dog> dogClass = Dog.class;
//获取所有的公共的成员方法对象,包括父类的。
Method[] methods = dogClass.getMethods();
for (Method method : methods) {
System.out.println(method);
}
//获取所有的成员方法对象包括私有的
Method[] methods2 = dogClass.getDeclaredMethods();
for (Method method : methods2) {
System.out.println(method);
}
//获取单个公共的成员方法对象
Method show = dogClass.getMethod("show"); //传入需要获取的方法的方法名
//获取单个公共的带参的成员方法(在后面获取数据类型的类的字节码文件对象)
Method haha = dogClass.getMethod("haha", String.class, int.class);
//获取单个带参的成员方法(在后面获取数据类型的类的字节码文件对象),可包含私有成员方法
Method test = dogClass.getDeclaredMethod("test", String.class);
//通过空参构造创建对象,用下面这个也可以
Dog dog1 = dogClass.newInstance();
Method show = dogClass.getMethod("show");//传入方法名
//调用方法执行
show.invoke(dog1);
Method haha = dogClass.getMethod("haha", String.class, int.class);
Double o = (Double) haha.invoke(dog1, "小黑", 2);
System.out.println(o);
Method test = dogClass.getDeclaredMethod("test", String.class);
test.setAccessible(true); //取消私有权限检查
test.invoke(dog1, "abc");
}
}
public class Dog {
public void show() {
System.out.println("我是小白");
}
public double haha(String name, int age) {
System.out.println("哈哈" + name + "===" + age);
return 3.25;
}
private void test(String name) {
System.out.println("私有的方法调用了" + name);
}
}
四.动态代理
1.动态代理概述
代理形象点就是, 本来应该自己做的事情,却请了别人来做,被请的人那就是代理对象。而 动态代理便是在程序运行过程中产生的这个对象 。在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理。我们有更强大的代理cglib,Proxy类中的静态方法获取一个代理对象 , 最终调用InvocationHandler的方法来实现。
2.动态代理的特点,作用和分类
特点 | 字节码随用随创建,随用随加载 |
作用 | 不修改源码的基础上对方法增强 |
涉及的类 | Proxy |
提供者 | JDK官方 |
分类 |
|
3.如何创建代理对象
强大的代理cglib,使用Proxy类中的 newProxyInstance方法来创建代理对象 。需要注意的是 被代理类最少实现一个接口, 如果没有则不能使用。
public static Object newProxyInstance(ClassLoader loader,Class<?>[]interfaces,InvocationHandler h) 创建代理对象
newProxyInstance方法中的参数:
- ClassLoader: 类加载器. 它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。
- Class[]:字节码数组, 代理对象需要实现的接口, 它是用于让代理对象和被代理对象有相同方法。属于固定写法。
- InvocationHandler: 用于提供增强的代码, 它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。一般此接口的实现类都是谁用谁写。
InvocationHandler Object invoke(Object proxy,Method method,Object[] args) 同时绑定的一个方法, 执行被代理对象的任何接口方法都会经过该方法
new InvocationHandler() {
/**
* 作用:执行被代理对象的任何接口方法都会经过该方法
* 方法参数的含义
* @param proxy 代理对象的引用
* @param method 当前执行的方法
* @param args 当前执行方法所需的参数
* @return 和被代理对象方法有相同的返回值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
}
}
InvocationHandler 方法调用的实际处理者,代理对象的方法调用都会转发到这里。Proxy.newProxyInstance()会返回一个实现了指定接口的代理对象,对该对象的所有方法调用都会转发给InvocationHandler.invoke()方法。
4.动态代理的神奇之处
- 代理对象是在程序运行时产生的,而不是编译期;
- 对代理对象的所有接口方法调用都会转发到InvocationHandler.invoke()方法,在invoke()方法里我们可以加入任何逻辑,比如修改方法参数,加入日志功能、安全检查功能等;之后我们通过某种方式执行真正的方法体
(小编也在努力学习更多哟!以后再慢慢分享的啦!)
希望对友友们有所帮助!!!!