类加载器 反射 动态代理 JDK1.5以及1.7的新特性
A:类的加载概述 当程序要使用某个类时,如果该类还未被加载到内存中, 则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。 加载 就是指将class文件读入内存,并为之创建一个Class对象。 任何类被使用时系统都会建立一个Class对象。 连接 验证 : 是否有正确的内部结构,并和其他类协调一致 准备 : 负责为类的静态成员分配内存,并设置默认初始化值 解析: 把类中的符号引用转换为直接引用 初始化 就是我们以前讲过的初始化步骤 B:类的加载时机 创建类的实例 new Student() 访问类的静态变量,或者为静态变量赋值 Math.PI Math.class 调用类的静态方法 Math.abs() 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象 初始化某个类的子类 直接使用java.exe命令来运行某个主类
A:类加载器的概述 负责将.class文件加载到内在中,并为之生成对应的Class对象。 B:类加载器的分类 Bootstrap ClassLoader 根类加载器 Extension ClassLoader 扩展类加载器 Sysetm ClassLoader 系统类加载器 C:类加载器的作用 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)如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的 加载器去执行; 2)如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达项层的启 动类加载器; 3)如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子 加载器才会尝试自己去加载,这就是双亲委派机制。 2. 为什么要采用双亲委派机制 因为如果随意的就去执行我自己写的String了,那么你说你的一个很完美的项目,我要是恶意攻击 你给你发送一个String类,那么你的项目岂不是直接就崩溃了?这也是一种保护啊! 1)避免核心API被篡改 2)避免类的重复加载
A:反射概述 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法; 对于任意一个对象,都能够调用它的任意一个方法和属性; 这种动态获取类的信息以及动态调用对象的方法的功能称为java语言的反射机制。 要想解剖一个类,必须先要获取到该类的字节码文件对象。 而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象 B:获取class文件对象的三种方式 a:Object类的getClass()方法 b:静态属性class c:Class类中静态方法forName() C:案例演示: 获取class文件对象的三种方式 反射: 就是在运行状态中的一种动态调用方法或者属性的一种机制. - 就是获取字节码文件对象,然后剖析该类中存在哪些构造方法,哪些成员变量,哪些成员方法 - - 类的成员 - 成员变量 Field - 构造方法 Constructor - 成员方法 Method - - 如何获取一个类对应的字节码文件对象: - - a: 第一种通过Object类中的getClass方法 - b: 通过静态属性(class属性) - c: 通过Class类中的一个静态方法: - public static Class forName(String className): - className: 这个表示的是一个类对应的全类名(就是需要加上包名)
A:获取所有构造方法 public Constructor<?>[] getConstructors() 获取所有的构造方法不包含私有的 public Constructor<?>[] getDeclaredConstructors() 获取所有的构造方法 包括私有的 B:获取单个构造方法 public Constructor<T> getConstructor(Class<?>... parameterTypes) 获取单个的构造方法 不包含私有的 public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 获取单个的构造方法包含私有的 C:案例演示: 通过反射获取无参构造方法并使用
A:案例演示: 通过反射获取带参构造方法并使用
A:案例演示: 通过反射获取私有构造方法并使用 // 获取字节码文件对象 Class clazz = Class.forName("com.click369.Student") ; Constructor con = clazz.getDeclaredConstructor(String.class , int.class) ; // 值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。 con.setAccessible(true) ; 取消语法检查不然会报错 因为私有的外界不能直接访问 Object obj = con.newInstance("张三" , 23) ; System.out.println(obj);
A:获取所有成员变量 public Field[] getFields() 获取所有的成员变量包含从父类继承过来的 public Field[] getDeclaredFields() 获取所有的成员变量 包含私有的 也包含从父类继承过来的成员变量 B:获取单个成员变量 public Field getField(String name) public Field getDeclaredField(String name) C:案例演示: 通过反射获取成员变量并使用
A:获取所有成员方法 public Method[] getMethods() //获取所有的公共的成员方法不包含私有的 包含从父类继承过来的过来的公共方法 public Method[] getDeclaredMethods()//获取自己的所有成员方法 包含私有的 B:获取单个成员方法 //参数1: 方法名称 参数2:方法行参的class 对象 public Method getMethod(String name,Class<?>... parameterTypes) //获取单个的方法 不包含私有的 public Method getDeclaredMethod(String name,Class<?>... parameterTypes) 获取单个方法包括私有的 C:案例演示: 通过反射获取无参无返回值成员方法并使用
A:案例演示: 通过反射获取带参带返回值成员方法并使用
A:案例演示: 通过反射运行配置文件内容
A:案例演示: 我给你ArrayList<Integer>的一个对象,我想在这个集合中添加一个字符串数据,如何实现呢?
A:案例演示 public void setProperty(Object obj, String propertyName, Object value){}, 此方法可将obj对象中名为propertyName的属性的值设置为value。
A:案例演示: 用户的增删改查
A:动态代理概述 代理:本来应该自己做的事情,却请了别人来做,被请的人就是代理对象。 举例:春季回家买票让人代买 动态代理:在程序运行过程中产生的这个对象 而程序运行过程中产生对象其实就是我们刚才反射讲解的内容,所以,动态代理其实就是通过反射来生成一个代理 在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口, 通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理。 我们有更强大的代理cglib,Proxy类中的方法创建动态代理类对象 public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 最终会调用InvocationHandler的方法 InvocationHandler Object invoke(Object proxy,Method method,Object[] args) B:案例演示: 动态代理的实现 我们可以通过Proxy类中的静态方法获取一个代理对象: - - public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
loader: 类加载器 interfaces: 接口对应的一个Class数组 InvocationHandler: 这个其实就是要代理对象所做的事情的一个类的封装 注意:JDK给我们提供的动态代理,只能对接口进行代理.
动态代理: * 特点:字节码随用随创建,随用随加载 * 作用:不修改源码的基础上对方法增强 * 分类: * 基于接口的动态代理 * 基于子类的动态代理 * 基于接口的动态代理: * 涉及的类:Proxy * 提供者:JDK官方 * 如何创建代理对象: * 使用Proxy类中的newProxyInstance方法 * 创建代理对象的要求: * 被代理类最少实现一个接口,如果没有则不能使用 * 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 { } } - InvocationHandler 方法调用的实际处理者,代理对象的方法调用都会转发到这里。 Proxy.newProxyInstance()会返回一个实现了指定接口的代理对象,对该对象的所有方法调用都会转发给InvocationHandler.invoke()方法。 动态代理神奇的地方就是: 1.代理对象是在程序运行时产生的,而不是编译期; 2.对代理对象的所有接口方法调用都会转发到InvocationHandler.invoke()方法,在invoke()方法里我们可以加入任何逻辑,比如修改方法参数,加入日志功能、安全检查功能等;之后我们通过某种方式执行真正的方法体
A: JDK1.5的新特性: 自动拆装箱 , 泛型 , 增强for , 可变参数 , 枚举 B:枚举概述: 就是一个类只能存在几个固定的对象,那么这个就是枚举.我们就可以使用这些对象可以表示一些固定的值. 举例:一周只有7天,一年只有12个月等。 C:案例演示: 自己实现枚举类
A:案例演示: 通过enum实现枚举类
A:案例演示 定义枚举类要用关键字enum 所有枚举类都是Enum的子类,但是不要显式的写出来 枚举类的第一行上必须是枚举项,最后一个枚举项后的分号是可以省略的,但是如果枚举类有其他的东西,这个分号就不能省略。建议不要省略 枚举类可以有构造器,但必须是private的,它默认的也是private的。枚举项的用法比较特殊:枚举(“”); 枚举类也可以有抽象方法,但是枚举项必须重写该方法 枚举在switch语句中的使用
A:枚举类的常见方法 int ordinal() 返回枚举项的序号 int compareTo(E o) 比较两个枚举项的 返回的是两个枚举项序号的 差值 String name() 获取枚举项的名称 String toString()获取枚举项的名称 <T> T valueOf(Class<T> type,String name) 用来获取指定的枚举项 参数1:枚举类对应的字节码对象 参数2 枚举项的名称 values() 获取所有的枚举项 此方法虽然在JDK文档中查找不到,但每个枚举类都具有该方法,它遍历枚举类的所有枚举值非常方便 B:案例演示: 枚举类的常见方法 public static void main(String[] args) { // 测试 Direction front = Direction.FRONT ; Direction behind = Direction.BEHIND; Direction left = Direction.LEFT ; Direction right = Direction.RIGHT ; System.out.println(front.ordinal()); System.out.println(behind.ordinal()); System.out.println(left.ordinal()); System.out.println(right.ordinal()); System.out.println("----------------------------------"); System.out.println(front.compareTo(right)); System.out.println("----------------------------------"); System.out.println(front.name()); System.out.println("----------------------------------"); System.out.println(front.toString()); System.out.println(front); System.out.println("----------------------------------"); // <T> T valueOf(Class<T> type,String name): 用来获取指定的枚举项 // type: 表示的是对应的枚举的字节码文件对象 // name: 就是枚举项的名称 Direction direction = Direction.valueOf(Direction.class, "RIGHT") ; System.out.println(direction); System.out.println("----------------------------------"); Direction[] directions = Direction.values() ; for(Direction d : directions){ System.out.println(d); } }
A:二进制字面量 JDK7开始,终于可以用二进制来表示整数(byte,short,int和long)。 使用二进制字面量的好处是,可以使代码更容易被理解。语法非常简单,只要在二进制数值前面加 0b或者0B int x = 0b110110 B:数字字面量可以出现下划线 为了增强对数值的阅读性,如我们经常把数据用逗号分隔一样。JDK7提供了_对数据分隔。 举例: int x = 100_1000; 注意事项: 不能出现在进制标识和数值之间 不能出现在数值开头和结尾 不能出现在小数点旁边 C:switch 语句可以用字符串 D:泛型简化