n 内部类:定义在一个类内部的类,就称为内部类
u 内部类分为3种
² 成员内部类:相当于成员变量
² 局部内部类:定义在方法内部的,相当于局部变量(极少用到,了解即可)
² 匿名内部类:(常用)
Iterator it = new Iterator() {//直接定义类,并同时创建该类对象 @Override publicboolean hasNext() { returnfalse; } @Override public Object next() { returnnull; } @Override publicvoid remove() {
} }; |
u 使用要点:
² 内部类的构造方法不可以在其他类里直接调用!
² 内部类如果有静态成员变量,那么内部类也必须是静态的!
² 非静态内部类Car.Engine en = new Car().new Engine();
或者可以这样写: Car c = new Car(); Car.Engine en = c.new Engine();² 静态内部类 Car.Engine en = new Car.Engine();
² 外部类在使用内部类的时候,可以直接实例化 Engine en = new Engine();
n 抽象类: 使用abstract关键字修饰的类,称之为抽象类
u 使用要点:
² 有抽象方法的类只能定义成抽象类(要使用abstract修饰)
² 抽象类不能实例化,不能生成对象
² 可以定义构造方法但不可以调用
² 抽象类只能被继承
² 继承抽象类的子类必须实现抽象类的抽象方法
u 抽象类的意义:
² 使一个类更加具有模板意义,强制规定了子类的行为
² 把设计工作和具体的代码实现工作做了分离
n 接口: 类的模板,用来做设计或规范,比抽象类更抽象
u 飞机、篮球、子弹、石头,这些事物之间有什么共同点?是否能用继承体现他们的关系?
n 这些事物之间没有特别明显的共同属性,也很难抽象出父类(很难归纳为一个类别),但是它们都具有一些相同的行为能力:那就是飞行
u 因此这里我们不再使用抽象类,而使用接口来体现它们的关系,抽象出它们的共同行为
publicinterface Flyable{ //接口中的方法都是抽象的,没有实现 publicabstractvoid fly(); }
class Plane implements/*实现*/ Flyable { @Override publicvoid fly() {
} }
class Stone implements Flyable { @Override publicvoid fly() {
} }
class Bullet implements Flyable{ @Override publicvoid fly() {
} }
|
n 接口的特点:
u 接口中的方法全部都是抽象的,不能有方法实现代码!
² 接口中的方法,默认可以省略public 和 abstract关键字,系统会自动加上
u 对于实现了接口的类,必须实现接口中的所有抽象方法。
u 接口中的属性,全部都是静态常量。
² 默认可以省略final关键字,系统会自动加上
u 接口是可以多继承的!比如接口A可以同时继承接口B和接口C
n 常用类:
u 基本数据类型的包装类
u byteàByte、booleanà Boolean、char à Character、
shortà Short、intà Integer、longà Long、floatà Float、doubleà Doublen String类和StringBuffer类
u 掌握String类和StringBuffer的区别
u StringBuffer是带缓冲的可变长度字符串,如果一个字符串在内存中频繁的被修改,为了能够提高性能,这时我们就会考虑使用StringBuffer来存储字符串内容。
u StringBuffer类同时也给我们提供了很多实用处理字符串的方法
//计算字符串长度 publicsynchronizedint length() //把字符串两边空格去掉再计算长度 publicsynchronizedvoid trimToSize() //获得指定下标位置上的一个字符 publicsynchronizedchar charAt(int index) //设置指定下标位置上的字符,将字符内容更改为ch的值 publicsynchronizedvoid setCharAt(int index, char ch) //向字符串末尾追加内容 publicsynchronized StringBuffer append(String str) //截取字符串的一部分,从指定下标开始,到指定下标结束 publicsynchronized String substring(int start, int end) //计算指定内容在整个字符串中出现的下标位置 publicint indexOf(String str) //反转逆序整个字符串 publicsynchronized StringBuffer reverse() ...... |
u
//计算a的绝对值 publicstaticint abs(int a) publicstaticint abs(float a) ...... //计算a和b的最大值 publicstaticint max(int a, int b) publicstaticint max(double a, double b) ...... //计算a和b的最小值 publicstaticint min(int a, int b) //生产一个0至1的随机小数 publicstaticdouble random() ...... |
Math类
u System类
字段摘要 | |
static PrintStream | err “标准”错误输出流。 |
static InputStream | in “标准”输入流。 |
static PrintStream | out “标准”输出流。 |
方法摘要 | |
static long | currentTimeMillis() 返回以毫秒为单位的当前时间。 |
static void | exit(int status) 终止当前正在运行的 Java 虚拟机。 |
static void | gc() 运行垃圾回收器。 |
static String | getenv(String name) 获取指定的环境变量值。 |
static int | identityHashCode(Object x) 返回给定对象的哈希码,该代码与默认的方法 hashCode() 返回的代码一样,无论给定对象的类是否重写 hashCode()。 |
static long | nanoTime() 返回最准确的可用系统计时器的当前值,以毫微秒为单位。 |
n 集合类: 一组数据的集合,类似于数组,功能比数组强大
u 集合的特点
² 集合的长度是可变的
² 集合中存储的元素都是对象
² 所有的List集合类都实现了一个叫做Collection的接口
u List集合类的结构图:
u 掌握接口List和Set集合有什么不同
² List接口和Set接口都继承了Collection接口
² 接口定义了不同集合类的作用以及存储数据的方式
² 其中Set所有集合中的数据对象都是没有顺序且不可重复的
² 其中Vector集合是线程安全的
u 掌握ArrayList和LinkedList的区别
² ArrayList是简单的顺序结构,而LinkedList是链表结构,二者各有优缺点。顺序结构由于有下标的存在访问元素时速度比较快!但插入或删除元素会使得整个集合的元素都需要挪动,所以插入删除效率比较低!
² 链表结构由于采用了指针(也叫引用)链接对象,例如:
a对象保存了一个b对象的引用,b对象保存了一个c对象的引用,这时对象a、b、c就构成了一个链表结构
a |
b |
c |
a |
b |
c |
d |
当插入元素d时,不需要挪动元素,而只需要修改指针(引用)即可
因此链表结构在做插入删除操作时,速度较快,但由于没有下标,访问元素时,必须从第一个元素开始,挨个查找下一个元素,所以效率是比较低的!
结论:ArrayList访问效率高,插入删除效率低
LinkedList访问效率低,插入删除效率高
u 掌握Collection和Collections有什么不同
Collection是一个接口,规定了所有集合的方法,相当于所有集合的一个总规范
Collections是一个类,里面提供了各种操作集合的工具方法
u Collections类的sort方法
要求集合中所有元素对象都必须实现了Comparable接口
u Collection的父接口Iterable
这个接口规定了所有实现Collection接口的集合,都必须要有一个itrerator()方法,这个方法返回一个Iterator(迭代器)类型的对象
u Map集合类
u
key |
value |
key
|
value
|
key
|
value
|
Map集合的特点
² 以Entry(键值对)的方式来存储对象
² Map集合的结构
² 没有顺序
key
|
value
|
u HashMap和HashTable区别
² HashMap和HashTable都实现了Map接口,但不同的是,HashTable是一个线程安全的Collection
² HashMap允许将null作为一个entry(键值对)的key或者value,而Hashtable不允许null。
u 泛型: 类型检查,防止出现类型转换错误,并且减少了手动类型转换的次数
ArrayList<Int> list = new ArrayList<Int>(); list.add(new Int(45)); list.add(new Int(99)); list.add(new Int(22)); Collections.<Int>sort(list);
Collections.<Int>sort(list, new Comparator<Object>() { @Override publicint compare(Object o1, Object o2) { return 0; } });
print(list);
privatevoid print(Collection<? extends Object> list) { for(Object o : list) {
} }
class Int implements Comparable<Object>{ intvalue; public Int(int value) { this.value = value; }
@Override publicint compareTo(Object o) { if(o instanceof Int) { Int i = (Int)o; if(this.value > i.value) { return 10; } elseif(this.value == i.value) { return 0; } } return -10; }
public String toString() { returnthis.value+""; } } |
声明类型 |
声明类型 |
u HashMap的深度解析
² 在使用集合时,JAVA规范中要求,凡是要存入集合中的元素,都要重写对象的equals方法,请问这是为什么?(面试题)
key-value
|
key-value
|
key-value
|
key-value
|
key-value |
在解答整个问题之前,要先了解HashMap 的存储结构
第一、查看源码我们可以得知HashMap本质仍然是一个数组
第二、在使用put方法存放每一个Entry(键值对)的时候,HashMap会根据key(键)的hashcode值进行计算,得出一个下标,然后将其存放到相应的数组位置上。
关于hashcode()方法,由于是一个本地方法,所以我们无法知道它是如何实现的,但不管怎样,至少我们知道,key-value的存放位置是跟key 的hashcode值有关。
那么当两个key的hashcode值一样时,键值对又该如何存放呢?
我们再查看一下源代码,会发现在HashMap当中有一个内部类Entry
对于每一个Entry,实际上它本身是一个链表结构的,它的next属性保存另外一个对象的引用。JAVA为什么要这样设计呢?
原来,在存放Entry 的时候,当一个Entry的key的hash码相同,则在数组的同一个位置上,则会以链表的形式存储
key-value |
key-value
|
key-value
|
key-value
|
key-value
|
key-value
|
当key的hash码一样时,会存放到数组的 同一位置,并以链表形式存储
key-value
|
在获取元素的时候,HashMap同样会去计算传入key的hash码,首先定位到数组的位置上,然后利用传入key的equals方法来比较链表上的每一个key,比较结果相同时,才会将value值取出来。
如果没有重写equals方法,那会造成什么严重后果呢?
每个对象都有自己的hashcode值,通过hashcode()方法获得,由于没有重写equls方法,定位到数组的位置后,key的比较无法相等,因此就无法把数据取出来!
从这里我们也可以看出,当使用HashMap存储数据时,为了能够进一步的提高HashMap的效率,我们希望数组的每一个位置上元素都不要重复,否则每次获取元素时,都要对链表上的每一个key进行equals比较,而链表的访问效率是比较低的,所以链表的长度越小越好。
这也就提出了一个新的问题?如何才能使得元素在数组上存放的时候,位置不冲突?说白了,怎么能把这些元素分的越散越好?这就要重新去考虑我们的散列算法(hash算法)。
关于HashMap 更多的信息,由于篇幅有限不再做更多介绍,有兴趣自行百度。
u 迭代器
next() |
next() |
def
|
abc |
一个带游标的数组
next()方法会返回游标的下一个元素“abc”同时 游标会向后移动一个位置 游标 |
再次调用next()方法返回“def”
hasNext()返回是否还有下一个元素可访问
迭代器中的remove方法删除迭代器刚刚返回的 元素,并且不能同时调用两次!
使用迭代器进行for循环
for(Iterator it = list.iterator(); it.hasNext();) { Student stu = (Student)it.next();
} 当然也可以加上泛型,省去类型转换的麻烦 for(Iterator<Student> it = list.iterator(); it.hasNext();) { Student stu = it.next();
}
|
n 异常处理: java中提供了一种处理程序错误的机制,叫做异常处理
u 考虑这样一个问题,当我们打算写这样一个程序,将D:/a.txt 复制到E:/a.txt
if(D:/a.txt这个文件存在) { if(E盘剩余空间足够复制a.txt文件) { if(文件读取一半时出现错误,比如a.txt被修改或删除了) { 停止复制工作,提示复制失败 } else { if(如果E:/a.txt文件不存在) { 在E盘下新建a.txt文件 } copy("D:/a.txt","E:/a.txt");//执行复制动作 } } else { 提示E盘剩余空间不足 } } else { 提示a.txt文件不存在 }
|
我们的代码会这样写:
可以看到,我们真正复制文件的代码只有黑色字体部分的少量代码
而大部分代码逻辑在做条件判断,目的就是为了程序出错时, 可以做出一些相应的处理,并能给出一些错误的提示信息程序员在写代码的时候,要考虑的错误处理比功能处理还要多,这显然有些本末倒置了,为了能够在写程序时,考虑问题更专注于程序的功能,而不是错误处理,因此JAVA设计出了异常处理机制。
所以,利用异常处理机制我们可以把程序修改成下面的样子:
try { if(如果E:/a.txt文件不存在) { 在E盘下新建a.txt文件 } copy("D:/a.txt","E:/a.txt");//执行复制动作
} catch (发现文件不存在) { 提示a.txt文件不存在 } catch (发现E盘剩余空间不够) { 提示E盘剩余空间不足 } catch (文件复制一半时出现错误) { 停止复制工作,提示复制失败 } |
这里指明出现了什么样的异常状况 |
JAVA允许我们以这种方法来判断异常的发生 这样我们就可以把所有的异常状况集中起来做处理了!
|
在这里程序员可以专注于文件复制功能的实现,而暂时先不去关心错误如何处理
|
u Exception类的继承结构
Throwable |
Error |
Exception
|
RuntimeExcepton
|
RuntimeException,叫做运行时异常,由于这种异常编译阶段通常难以检查出来,这类异常也称为不检查异常(unchecked)编译器不会强制要求程序人员做try-catch的异常处理 例如: //下标越界异常 IndexOutOfBoundsException//空指针异常 NullPointerException//类型转换异常 ClassCastException//算术异常 ArithmeticException |
若干子类...... |
除运行时异常以外的其它异常类,在编译期间会检查出来的异常,我们称之为检查异常,对于这类异常,在程序员写代码的时候,如果使用了有可能产生这类异常的方法,则编译器会强制要求程序人员必须try-catch处理异常 例如: //类找不到异常 ClassNotFoundException//IO流使用异常 IOException//数据库交互异常 SQLException//算术异常 ArithmeticException |
其它子类...... |
对于ERROR这个类我们不必太关心,因为ERROR代表了非程序代码的错误,例如虚拟机内存不足而导致的程序终止,程序员不需要对这类错误做任何处理。
在JAVA的世界中,一切都是对象,所以毫无例外的,异常的产生也是以对象的形式存在,例如当发生数组下标越界的异常时,程序会自动生成一个IndexOutOfBoundsException类的实例对象, 并把这个对象以 throw的形式抛出!
在任何的程序设计语言中,都会有意外错误的情况发生,在一个小的程序中,可以用比较简单的方法处理异常,但是在一个大的系统中,每一个方法都要有处理异常状况的代码,这样必然使得程序变得非常复杂和庞大。所以,JAVA才设计出了这样的办法:
当一个方法内出现错误时,错误不在方法内做处理,而是发出一个错误信息,把它传给调用的人,例如:方法1()----> 方法2()----> 方法3()
如果方法3()里出现了任何错误,由于方法3的逻辑代码已经非常复杂,因此方法3不想对任何错误做处理,那么它发出一条消息给调用它的方法2,假设方法2也比较复杂,为了减少自己的工作量,它也可以不做处理,而把这个错误消息再一次发送给调用它的方法1,如果方法1仍然不做处理继续把消息向上发送,最终就由虚拟机来接管了。
虚拟机是所有代码的执行者,因此它处理错误的方式比较粗暴!把错误的消息打印出来之后,就直接把程序终止了。
当然如果不想让某个错误影响到整个系统的运行,在方法内应该拦截这个错误消息,并且可以做出相应的错误处理。
当然,如果你犯懒的话,拦截了消息,不做处理也是可以的。例如:
try{ inta = 3/0; } catch (Exception e) { //不做任何处理 } //不影响下面代码执行 ...... ......
|
在JAVA中,错误消息都是以对象的形式自动向上级抛出的,当然你也可以定义自己的消息类型,手动抛出去。在JAVA中这种错误消息我们称之为“异常”,如果一个异常在被抛出时没有任何捕获的动作,抛给上级后,上级也不做处理,最终就会抛给虚拟机,虚拟机不论错误的大小,一律都会打印错误信息并终止程序。
n 数据库复习(MySQL)
u 视图
² 创建一个虚拟表,虚拟表的数据由指定的select语句查询而得。
² 由于该表实际中没有数据,只是一个概念表,所以称之为视图
² 视图可以大大的简化查询语句,使得sql语句变得相对简单,例如在分布式系统中,程序的执行效率往往还会受到服务器之间的网速的影响。
因此,减少服务器之间通信时的数据包大小,就变得尤为重要了。 服务器 |
远程数据库 |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX巨复杂的sql语句 |
发送到数据库时间: 0.04秒 |
使用视图之后
远程数据库 |
服务器 |
select * from view_a |
发送到数据库时间: 0.01秒 |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 事先在数据库里定义好的视图 |
这种好处,只有大数据量,高并发时,才会体现的出来
它跟存储过程有着同样的好处。
u 存储过程
² 它让我们以编程的方式来定义一系列SQL语句
² 在存储过程当中,我们可以定义变量,运算。可以接受参数,可以返回结果,与自定义函数类似
² 而它的执行效率要高于使用JDBC操作数据库,所以很多时候,如果程序对操作数据库的执行速度要求很高的话,就应该尽量多的使用存储过程来代替大量的JDBC代码。
l 练习:使用存储过程做分页查询
u 索引
² select * from student where city = 'beijing' and age = ......
u 事务
² 事务的隔离级别:
l Read Uncommitted(读取未提交内容)脏读
l Read Committed(读取提交内容)
l Repeatable Read(可重读)
可重复读隔离级别是最严格的隔离级别。在该隔离级别下,一个事务的影响完全与其他并发事务隔离,脏读、不可重复的读、幻像读现象都不会发生。当使用可重复读隔离级别时,在事务执行期间会锁定该事务以任何方式引用的所有行。因此,如果在同一个事务中发出同一个SELECT语句两次或更多次,那么产生的结果数据集总是相同的。因此,使用可重复读隔离级别的事务可以多次检索同一行集,并对它们执行任意操作,直到提交或回滚操作终止该事务。但是,在事务存在期间,不允许其他事务的执行影响到这个事务正在访问的任何行。为了确保这种行为不会发生,锁定该事务所引用的每一行-- 而不是仅锁定被实际检索或修改的那些行。因此,如果一个事务扫描了1000行,但只检索10行,那么它所扫描的1000行(而不仅是被检索的10行)都会被锁定。
l Serializable(可串行化)
u mysql 的存储引擎
² MyIsam 查询效率比较高,但不支持事务
² InnoDB 支持事务,查询效率略低,默认以聚簇索引方式存储
nJDBC(Java Data Base Connectivity)
u JDBC是一种技术规范,定义了连接数据库的方式。
Oracle |
JAVA |
Mysql |
SQLServer |
JAVA连接不同的数据库,方法肯定是不一样的,每个数据库都提供了自己特有的函数方法,因此需要使用不同的JAVA-API来实现连接数据库。
例如连接Mysql的方法叫 connectMysql() 连接Oracle的方法叫connectOracle()
但是,这样做的话需要程序员去记住每一种方法的原理和特点以及使用方式。显然非常的麻烦
为了能让连接数据库变的更简单,JAVA定义了一个规范,规范中声明:
JAVA本身不提供任何连接数据库的代码,所有数据库企业自己来提供一套连接自己数据库的JAVA代码。但是!这套JAVA代码必须要遵循一定的规范,例如:所有的类都应该叫什么名字,每个类应该有什么方法。当所有的API都遵循了这个规范,达到了统一。程序人员在使用JAVA代码连接不同的数据库时,才能变得简单和易用。
因此JAVA本身提供了这样一套接口类和一些相关的辅助类,而没有任何连接数据库的代码实现,实现类都由数据库厂商自己来提供。
我们把这些实现代码也称之为驱动程序
这样,当我们再次使用JDBC连接数据库时,就变得简单多了
Oracle |
SQLServer |
JAVA |
Oracle提供的驱动程序 |
Mysql提供的驱动程序 |
SQLServer提供的驱动程序 |
JDBC接口 |
JAVA声明的规范 |
Mysql |
调用同一个方法,使用不同的驱动,就可以连接不同的数据库,这样用起来是不是感觉爽多了呢? JDBC就像是一个万能转换器一样,帮我们把不同的实现代码屏蔽掉了,我们只需要了解一个使用接口就可以了,这里再一次体现出了封装的思想!
u JDBC编程的步骤:
² 加载驱动 xxx.jar
² 实例化驱动类并注册给DriverMananger
Driver d = new com.mysql.jdbc.Driver(); DriverManager.registerDriver(d); |
² 自动注册驱动对象,这是推荐的写法
|
² 获得数据库连接(三个参数:1、数据库连接字符串 2、用户名 3、密码)
Connection conn = DriverManager.getConnection( "jdbc:mysql://localhost:3306/test", "root", "root"); |
² 创建执行语句对象
Statement stmt = conn.createStatement(); |
² 执行SQL语句获得结果集对象
ResultSet rs = stmt.executeQuery("select * from student"); |
² 遍历并展示结果集
while(rs.next()) {
} |
² 关闭所有数据库连接资源
rs.close(); stmt.close(); conn.close(); |
u JDBC的异常处理
u 事务
conn.setAutoCommit(false); //关闭事务的自动提交 conn.commit();//手动提交 conn.rollback();//catch到异常时,事务进行回滚 |
u 批量处理
Statement stmt = null; stmt = conn.createStatement(); stmt.addBatch("insert into student values(null,'测试888',20)"); stmt.addBatch("update student set age = 199 where name = 'lisi'"); stmt.addBatch("update student set age = 204 where name = '测试52'"); int[] rscount = stmt.executeBatch(); for (int i = 0; i < rscount.length; i++) {
} stmt.close(); conn.close();
|
u PreparedStatement
预编译所带来的好处是巨大的,它不仅提高了SQL语句的执行效率,还有效的防止了SQL注入
u JDBC调用存储过程
n 文件IO流java提供的一套读写文件的API
u 首先认识一下File类
² 这个类位于java.io包中
² 它是一个文件和目录路径名的抽象表示形式。
² 将文件读取到内存当中,或者在指定目录生成文件,都需要借助File类
u 什么是流?
数据源A |
数据源B
|
数据源(数据源可以是一个文件,也可以是内存里的一个数据块等)A传输数据到数据源B。需要在两个数据源之间建立一个管道,如同流水一般,数据从数据源A通过管道流入数据源B。这个建立出来的管道,我们称之为流。数据从一个地方流向另一个地方的时候,都需要用到流
u 从流的功能来划分:节点流 和 处理流
u 从流的方向上来划分:输入流 和 输出流
u 从流读取数据的大小上来划分:字节流 和 字符流
u 四大抽象类:InputStream(字节输入流)、OutputStream(字节输出流)、Reader(字符输入流)、Writer(字符输出流)
² FileInputStream、FileOutputStream
² 读写文件的输入和输出字节流
² FileReader、FileWriter
² 读写文件的输入和输出字符流
² BufferedInputStream、BufferedOutputStream
² 带缓冲的字符输入、输出流,是普通字节流的一种包装流,将它套接在普通字节流之上来使用。例如:
FileInputStream fis=newFileInputStream();
BufferedInputStream bis = newBufferedInputStream(fis);
fis字节输入流 |
带缓冲的字节输入流 |
² BufferedReader、BufferedWriter
² 带缓冲的字符流同理
² DataInputStream、DataOutputStream
² 数据处理流,允许以平台无关的方式将基本数据类型写入到磁盘中。
² ObjectInputStream、ObjectOutputStream
² 对象流能够将对象直接存储到磁盘中
² 对象的存储也称之为对象的序列化(串行化)要想实现对象的序列化,该对象的类必须实现Serializable接口
n 反射机制(重点!)
execute("System.out.println('sdfasdf')");
java所有的代码都是编译好之后才能执行的。没有动态执行语句的功能,因此它还称不上是一种动态语言。为了弥补这一缺陷,JAVA设计了反射这一概念
利用反射机制,JAVA在运行期间可以加载一个编译期间未知的class,获取它的构造,生成对象,给属性设值、调用其方法。而不需要将代码写死。
更直白的说: 即使我不知道类的名字,也可以动态加载这个类,并生成这个类的对象,并调用它的方法,而在我的代码当中并不会出现如下的语句:
Student stu = new Student();
stu.study();
因此相当于我做到了动态的执行代码的效果!
n 动态代理(重点!)
它的重要性无法形容,应用之广也是我们不能想象的
它从根本上解决了面向切面编程的难点。让我们可以拦截任意的方法
这里介绍的是JDK动态代理,它要求委托对象的类必须实现接口
除此以外还有Cglib的动态代理,是不需要实现接口的
代理对象proxy 方法a () 方法b (args...) 方法c (args...)
自定义的处理器 Handler |
委托对象atm 方法a () 方法b (args...) 方法c (args...)
|
MyHandler //利用反射机制执行请求的方法 invoke(proxy,method,args)
ATM atm |
JDK的动态代理要求委托对象的类必须实现一个接口,在生成代理对象时,会利用该接口来动态生成一个代理对象,它与委托对象有着一模一样的方法,同时需要我们自定义一个执行处理器(InvocationHandler)
在代理对象接收到方法调用的请求时,会执行处理器的invoke方法,这样的话,我们只要在处理器invoke方法前后加上自己想要的代码,就可以做到方法的拦截了。
了解更多详情请登录超人学院网站http://www.crxy.cn或者每周日晚八点半相约免费公开课 具体详情请联系QQ2435014406