一、类的加载
1.概述
当程序需要使用某个类时,如果类还未被加载到内存中,系统会通过加载、连接、初始化散步来实现对 这个类进行初始化
加载
就是指将class文件读入内存,并为之创建一个Class对象。
任何类被使用时系统都会建立一个Class对象。
连接
验证 : 是否有正确的内部结构,并和其他类协调一致
准备 : 负责为类的静态成员分配内存,并设置默认初始化值
解析: 把类中的符号引用转换为直接引用
初始化 就是我们以前讲过的初始化步骤
2.类的加载时机
a.创建类的实例
b.访问类的静态变量,或为静态变量赋值
c.调用静态方法
d.使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
e.初始化某个类的子类
f.直接使用java.exe命令来运行某个主类
3.类加载器的概述和分类
A:类加载器的概述
负责将.class文件加载到内在中,并为之生成对应的Class对象。
B:类加载器的分类
1.Bootstrap ClassLoader 根类加载器
2.Extension ClassLoader 扩展类加载器
3.Sysetm ClassLoader 系统类加载器
C:类加载器的作用
1.Bootstrap ClassLoader 根类加载器
也被称为引导类加载器,负责Java核心类的加载
比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
2.Extension ClassLoader 扩展类加载器
负责JRE的扩展目录中jar包的加载。
在JDK中JRE的lib目录下ext目录
3.Sysetm ClassLoader 系统类加载器
负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径
二、反射
1.反射概述
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意一个方法和属性;
这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
通过一个类的字节码文件对象(Class)反着去剖析这个类的构成
2.获取一个类的字节码文件对象的方式
1.通过Object类的getClass()方法
Student s = new Student();
//Class 类的实例表示正在运行的java应用程序中的类和接口
Class sc = s.getClass();
2.任何一个类都有一个.class属性,通过这个属性就可以获取到该类的字节码文件对象
Class<Student> scl = Student.Class;
sc==scl;//true
3.Class.forName();
全路径:一个类带有包名 全类名
ctrl+alt+空格(强制提示)
Class<?> tc = Class.forName("java.lang.Thread");
3.类的构成
构造方法:Constructor
成员变量: Field
成员方法: Method
4.通过反射获取构造方法并创建该类对象
构造方法:Constructor
A.通过反射获取构造方法
getConstructors();
getDeclaredConstructors();
getConstructor();//可变参数 需要构造方法参数类型的字节码文件作为参数(如String.class) 无参就是获取无参构造 无法获取私有构造
getDeclaredConstructor();//可获取私有构造
B.通过反射获取构造方法并创建该类对象
Student s = new Student();
Class sc = s.getClass();
1.无参构造 构造方法对象中的newInstance() 创建该类对象
Object o = sc.getConstructor().newInstance();
Student s = (Student)sc.getConstructor().newInstance();
2.有参构造
Constructor<?> c = sc.getDeclaredConstructor(String.class,int.class);
c.setAccessible(true);//取消权限检查 可以使用私有构造方法
Object o = c.newInstance("lisi",25);
Student s = (Student)c.newInstance("lisi",25);
反射方式获取一个类的对象:
获取 该类字节码对象(3种方式)》字节码对象 获取 构造方法对象》构造方法对象 取消 权限检查==》构造方法对象 创建 该类对象
如果指向用空参构造创建一个类的对象,那么反射的方式有两种
1.Dog.class.getDeclaredConstructor().newInstance();
2.Dog.class.newInstance();
5、通过反射获取成员变量并赋值
Field 字段类型/成员变量类型
A.通过反射获取成员变量
Class<Student> sc = Student.class;
Field[] fs = sc.getFields();//获取所有公共成员变量对象的数组
Field[] dfs = sc.getDeclaredFields();//获取所有成员变量对象的数组 包括私有
Field n = sc.getField("name");//获取单个公共成员变量对象
Field n = sc.getDeclaredField("money");//获取单个成员变量对象 包括私有
B.通过反射获取成员变量并赋值
Class<Student> sc = Student.class;
Field n = sc.getField("name");//成员变量名称
Constructor<> scc = sc.getConstructor();
scc.setAccessible(true);
Student s = scc.newInstance();
n.setAccessible(true);
n.set(s,"张三");//需要一个该类对象和成员变量值 给哪个对象的那个成员变量赋值
Object o = n.get(s);//需要set()方法的该类对象s 返回该成员变量对象
System.out.println(o);//打印对象 即是设置的成员变量的值
通过反射方法设置成员变量的值:
获取该类字节码对象==》字节码对象 获取 成员变量对象 ==》成员变量对象设置具体该类对象的成员变量值
6、通过反射获取成员方法并使用
Method 成员方法对象
A.通过反射获取成员方法
Class<Student> sc = Student.class;
Constructor<Student> scc = sc.getDeclaredConstructor();
scc.setAccessible(true);
Student s = scc.newInstance();
Method[] ms = sc.getMethods();//获取所有公共成员方法对象,包括父类的公共方法
Method[] dms = sc.getDeclaredMethods();//获取所有的私有成员方法对象,不包括父类
sc.getMethod();//需要成员方法名和成员方法参数类型的class类型(可变参数)
Method show = sc.getMethod("show",String.class,int.class);
sc.getDeclaredMethod();
B.通过反射获取成员方法并使用
Class<Student> sc = Student.class;
Constructor<Student> scc = sc.getDeclaredConstructor();
scc.setAccessible(true);
Student s = scc.newInstance();
Method show = sc.getDeclaredMethod("show");
show.invoke(s);//参1:类对象 参2(可变参数):成员方法需要的具体参数
Method sho = sc.getDeclaredMethod("sho",String.class,int.class);
sho.invoke(s,"张三",23);
Method how = sc.getDeclaredMethod("how",double.class);
how.setAccessible(true);
Object o = how.invoke(s,3.2);//有返回值的成员方法 会返回该方法返回值
System.out.println(o);//该方法返回值
反射机制读取配置文件
//peizhi.properties
classname=org.westos.demo.Dog
methodname=sleep
//Dog类
public class Dog {
public void eat() {
System.out.println("狗吃骨头");
}
public void sleep() {
System.out.println("狗睡觉");
}
}
//读取配置文件
Properties properties = new Properties();
properties.load(new FileReader("peizhi.properties"));
Class<?> classname = Class.forName(properties.getProperty("classname"));
Object o = classname.getDeclaredConstructor().newInstance();
Method eat = classname.getMethod(properties.getProperty("methodname"));
eat.invoke(o);
反射机制越过泛型检查
// 我给你ArrayList<Integer> 的一个对象,我想在这个集合中添加一个字符串数据,如何实现呢?
ArrayList<Integer> list = new ArrayList<>();
list.add(100);
//list.add("abc"); 报错
//泛型:泛型只在编译期,运行期就擦除了。
Class<? extends ArrayList> aClass = list.getClass();
//获取add方法对象
Method add = aClass.getDeclaredMethod("add", Object.class);
add.invoke(list,"abc");
System.out.println(list);
反射
动态代理:在不修改某个类的情况下对该类中的方法进行增强的一种手段
A:动态代理概述
代理:本来应该自己做的事情,却请了别人来做,被请的人就是代理对象。
举例:春季回家买票让人代买
动态代理:在程序运行过程中产生的这个对象
而程序运行过程中产生对象其实就是我们刚才反射讲解的内容,所以,动态代理其实就是通过反射来生成一个代理
在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,必须要有接口,没有接口使用cglib 可以代理
通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理。
我们有更强大的代理cglib,Proxy类中的方法创建动态代理类对象
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
最终会调用InvocationHandler的方法
InvocationHandler Object invoke(Object proxy,Method method,Object[] args)
被代理对象(目标对象)
代理对象==运行期生成
动态生成代理人的类 Proxy java.lang.reflect
static Object newProxyInstance(Classloader loader,Class<?>[]interfaces,InvocationHandler h); //返回一个指定接口的代理类实例
1.Classloader loader类加载器,负责加载代理对象的字节码文件,根被代理对象使用的是同一个类加载器,固定写法 被代理对象.getClass().getClassLoader()
2.Class<?>[]interfaces 被代理对象实现的所有接口的class数组 固定写法 被代理对象.getClass().getInterfaces()
3.InvocationHandler h 用于提供增强的代码 new InvocationHandler(){增强代码}
new InvocationHandler(){
@Override
/**proxy 代理对象
* method 方法对象
* args 方法上的参数数组
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//System.out.println("权限校验");
//Object invoke = method.invoke(userDao);
//System.out.println("记录日志");
Object invoke=null;
if(method.getName().equals("update")){
System.out.println("权限校验");
invoke= method.invoke(userDao);
System.out.println("记录日志");
}
return invoke;
}
}
Proxy.newProxyInstance();