一.类加载
1.类的加载概述
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通、过加载,连接,初始化三步来实现对这个类进行初始化。
- 加载:就是指将class文件读入内存,并为之创建一个Class对象。任何类被使用时系统都会简历一个Class对象。
- 连接
- 验证:是否有正确的内部结构,并和其他类协调一致
- 准备:负责为类的静态成员分配内存,并设置默认初始化值
- 解析:把类中的符号引用转换为直接引用
- 初始化:就是我们以前讲过的初始化步骤
2.类的加载时机
(1)创建类的实例
(2)访问类的静态变量,或者为静态变量赋值
(3)调用类的静态方法
(4)使用反射来强制创建某个类或接口对应的字节码文件对象
(5)初始化某个类的子类
(6)直接使用java.exe来运行某个主类
二.类加载器
1.类加载的概述
负责将字节码文件加载到内存中,并为之生成对应的Class对象
2.分类
- Bootstrap ClassLoader 根类加载器
- Extension ClassLoader 扩展类加载器
- Sysetm ClassLoader 系统类加载器
3.类加载的作用
- 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包和类路径。
三.反射
1.反射概述
- **Java反射机制就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;**这种动态获取类的信息以及动态调用对象的方法的功能称为java语言的反射机制。
- 要想解刨一个类,必须有、先要获取到该类的的字节码文件对象,而解刨使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象。
2.获取一个类对象的字节码文件对象的方法
a:Object类的getClass()方法
b:静态属性class
c:Class类中静态方法forName(),public static Class forName(String className):
className:这个表示的是一个类对应的全类名(就是需要加上包名)
3.通过反射获取构造方法
获取所有构造方法
public Constructor<?>[] getConstructors() 获取所有的构造方法不包含私有的
public Constructor<?>[] getDeclaredConstructors() 获取所有的构造方法 包括私有的
获取单个构造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes) 获取单个的构造方法 不包含私有的
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 获取单个的构造方法包含私有的
案例演示
//测试
package com.westmo3.demo4;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class MyDemo9 {
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
//获取Teacher类的字节码文件对象
Class<Teacher> teacherClass = Teacher.class;//第一种方式
//Class<? extends Teacher> aClass = new Teacher().getClass();//第二种方式
//Class<?> aClass = Class.forName(" com.westmo3.demo4.Teacher");//第三种方式
Constructor<?>[] constructors = teacherClass.getConstructors();//获取该类中所有非私有构造方法对象的数组
Constructor<?>[] declaredConstructors = teacherClass.getDeclaredConstructors();//获取该类中所有构造方法对象的数组
Constructor<Teacher> constructor1 = teacherClass.getConstructor();//获取单个的空参构造方法对象
System.out.println(constructor1);
//获取两个参数的私有的构造方法对象
Constructor<Teacher> declaredConstructor1 = teacherClass.getDeclaredConstructor(String.class, int.class);
System.out.println(declaredConstructor1);
//我们可以使用私有构造来创建对象,以前是不行的
declaredConstructor1.setAccessible(true);//取消权限的语法检测
Teacher teacher = declaredConstructor1.newInstance("张三", 12);
System.out.println(teacher);
}
}
//Teacher类
package com.westmo3.demo4;
public class Teacher {
public Teacher() {
System.out.println("公有空参被调用");
}
public Teacher(String name){
System.out.println("公有有参被调用");
}
private Teacher(String name,int age){
System.out.println("私有有参被调用");
}
}
4.通过反射获取成员变量并使用
获取所有成员变量
public Field[] getFields() 获取所有的成员变量包含从父类继承过来的
public Field[] getDeclaredFields() 获取所有的成员变量 包含私有的 也包含从父类继承过来的成员变量
获取单个成员变量
public Field getField(String name)
public Field getDeclaredField(String name)
案例演示
//测试用例
package com.westmo3.demo4;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class MyDemo10 {
public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<? extends Teacher> aClass = new Teacher().getClass();//获取类的字节码文件对象
Field[] fields = aClass.getFields();// 获取所有非私有成员(public 修饰的)变量包含从父类继承过来的
for (Field field : fields) {
System.out.println(field);
//输出:public java.lang.String com.westmo3.demo4.Teacher.name
}
Field[] declaredFields = aClass.getDeclaredFields();//获取所有的成员变量包含私有的和父类继承过来的
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
Field field = aClass.getField("name");//获取单个public成员变量
Teacher teacher = aClass.getConstructor().newInstance();//利用反射空参构造创建对象的简便方法
field.set(teacher,"张三");//给成员变量赋值
Field field1 = aClass.getDeclaredField("age");
field1.setAccessible(true);
field1.set(teacher,13);
Field field2 = aClass.getDeclaredField("age");
field2.setAccessible(true);
field2.set(teacher,'男');//给私有成员变量赋值
}
}
//Teacher类
package com.westmo3.demo4;
public class Teacher {
public Teacher() {
}
public String name;
int age;
private char sex;
}
5.通过反射获取成员方法并使用
获取所有成员方法
public Method[] getMethods() //获取所有的公共的成员方法不包含私有的 包含从父类继承过来的过来的公共方法
public Method[] getDeclaredMethods()//获取自己的所有成员方法 包含私有的
获取单个成员方法
//参数1: 方法名称 参数2:方法行参的class 对象
public Method getMethod(String name,Class<?>... parameterTypes) //获取单个的方法 不包含私有的
public Method getDeclaredMethod(String name,Class<?>... parameterTypes) 获取单个方法包括私有的
案例演示
//测试
package com.westmo3.demo4;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class MyTest1 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<?> aClass = Class.forName("com.westmo3.demo4.Worker");
Method[] methods = aClass.getMethods();//获取所有的公共成员方法包含从父类继承过来的
for (Method method : methods) {
System.out.println(method);
}
Method[] declaredMethods = aClass.getDeclaredMethods();//获取所有的成员方法
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
Worker worker = (Worker) aClass.getConstructor().newInstance();
//获取单个方法不包含私有
Method method = aClass.getMethod("test1", String.class);
Object w = method.invoke(worker, "张三");
Method test3 = aClass.getDeclaredMethod("test3", String.class, int.class);
test3.setAccessible(true);
Object invoke = test3.invoke(worker, "张三", 13);
}
}
//Worker类
package com.westmo3.demo4;
public class Worker {
public Worker() {
}
public void test1(String name){
System.out.println(name);
}
public void test2(String name,int age){
System.out.println("公有"+name+"="+age);
}
private void test3(String name,int age){
System.out.println("私有"+name+age);
}
}
6.反射应用
(1)通过反射运行配置文件内容
配置文件 peizhi.txt
className=com.练习.反射应用.运行配置文件内容.Cat
methodName=eat
Cat类
package com.练习.反射应用.运行配置文件内容;
public class Cat {
public void eat(){
System.out.println("猫吃鱼");
}
}
测试类
package com.练习.反射应用.运行配置文件内容;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
public class MyTest {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//以前的方法
//new Cat().eat();
//new Dog().eat();
//利用反射来读取配置文件实现
Properties properties = new Properties();
properties.load(new FileReader("peizhi.txt"));
//获取该类的字节码文件对象
String className1 = properties.getProperty("className");
Class<?> className = Class.forName(className1);
//利用反射创建该类的对象
Object o = className.getConstructor().newInstance();
//调用猫类中的方法执行
Method methodName = className.getMethod(properties.getProperty("methodName"));
Object invoke = methodName.invoke(o);
}
}
(2)通过反射越过泛型检查
package com.练习.反射应用.越过泛型检查;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
public class MyDemo {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//给定一个Arraylist<Integer>对象,但要想给集合中添加一个字符串数据该怎么实现?
ArrayList<Integer> arrayList = new ArrayList<Integer>();
arrayList.add(100);
arrayList.add(200);
arrayList.add(300);
//泛型只在编译器有效
Class<? extends ArrayList> aClass = arrayList.getClass();//获取该类的字节码文件对象
Method add = aClass.getMethod("add", Object.class);
add.invoke(arrayList,"张三");
System.out.println(arrayList);
}
}
四.动态代理
1.为什么会有动态代理?
我们要想对下面这个类中的方法进行增强,但是不想修改下面的代码,我们就可以使用动态代理实现
public class UserDaoImpl implements UserDao {
@Override
public void insert() {
System.out.println("添加一个用户");
}
@Override
public void delete() {
System.out.println("删除用户");
}
@Override
public void update() {
System.out.println("修改用户");
}
@Override
public void query() {
System.out.println("查询用户");
}
}
2.动态代理概述
定义:为其它对象提供一种代理以控制对这个对象的访问控制;在某些情况下,客户不想或者不能直接引用另一个对象,这时候代理对象可以在客户端和目标对象之间起到中介的作用。
3.动态代理的优势
Java动态代理的优势是实现无侵入式的代码扩展,也就是方法的增强;让你可以在不用修改源码的情况下,增强一些方法;在方法的前后你可以做你任何想做的事情(甚至不去执行这个方法就可以)。
4.使用
在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,
通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理。
我们有更强大的代理cglib,Proxy类中的方法创建动态代理类对象
(1)public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
最终会调用InvocationHandler的方法
(2)InvocationHandler Object invoke(Object proxy,Method method,Object[] args)
newProxyInstance方法的参数:
ClassLoader:类加载器
它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。
Class[]:字节码数组
它是用于让代理对象和被代理对象有相同方法。固定写法。
InvocationHandler:用于提供增强的代码
它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。
new InvocationHandler() {
/**
* 作用:执行被代理对象的任何接口方法都会经过该方法
* 方法参数的含义
* @param proxy 代理对象的引用
* @param method 当前执行的方法
* @param args 当前执行方法所需的参数
* @return 和被代理对象方法有相同的返回值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
}
}
5.案例演示
User类
package com.westmo3.demo4.动态代理;
public interface User {
void delete();
void insert();
void update();
}
class UserDao implements User{
@Override
public void delete() {
System.out.println("删除用户");
}
@Override
public void insert() {
System.out.println("添加用户");
}
@Override
public void update() {
System.out.println("修改用户");
}
}
获取代理对象的类
package com.westmo3.demo4.动态代理;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyUtils {
//获取代理对象的方法
public static User getProxy(User user){
//newProxyInstance()的三个参数:1.类加载器,负责加载代理对象的字节码
//2.这个目标对象所实现的所有接口的class类型的数组
//3.InvocationHandler 接口,每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,
// 将对方法调用进行编码并将其指派到它的调用处理程序的 invoke 方法。
User o = (User) Proxy.newProxyInstance(user.getClass().getClassLoader(),
user.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//执行目标对象的任何方法都会经过该方法
System.out.println("增强代码1");
Object invoke = method.invoke(user);//让目标对象中的方法执行
System.out.println("增强代码2");
//给其中的某个方法增强
/*Object invoke=null;
if (method.getName().equals("delete")) {
System.out.println("增强代码1");
invoke = method.invoke(user);
System.out.println("增强代码2");
}*/
return invoke;
}
});
return o;
}
}
测试类
package com.westmo3.demo4.动态代理;
public class MyTest {
public static void main(String[] args) {
User userDao = new UserDao();
User proxy = ProxyUtils.getProxy(userDao);
proxy.delete();
proxy.insert();
proxy.update();
}
}