集合列表ListArrayList利用空参创建的集合在底层创建一个默认长度为0的数组添加第一个元素时底层会创建一个新的长度为10的数组存满时会扩容1.5倍一次存多个元素1.5倍还不够则新创建的数组长度以实际为准LinkedList底层数据结构是双链表查询慢首位操作速度是极快的复杂度分析操作ArrayListLinkedList查询O(1)O(n)头部插入/删除O(n)O(1)尾部插入O(1)*O(1)中间插入/删除O(n)O(n)空间开销O(n)O(n)ArrayList尾部插入在扩容时为O(n)遍历方式增强for循环for-each)for (ElementType element : list) { // 处理element } ​传统forfor (int i 0; i list.size(); i) { ElementType element list.get(i); // 处理element } ​iterator迭代器IteratorElementType iterator list.iterator(); while (iterator.hasNext()) { ElementType element iterator.next(); // 处理element } ​stream流jdk8list.stream().forEach(element - { // 处理element }); ​parallelStream并行流list.parallelStream().forEach(element - { // 处理element });Iterator在底层创建一个内部类对象表示ArrayList的迭代器多次调用这个方法相当于创建多个迭代器对象如何避免并发修改异常 在使用迭代器或者增强for遍历集合过程中不要使用集合的方法去添加或者删除元素平衡二叉树规则任意节点左右子树高度差不超过1右旋左左一次右旋当前根节点左子树有节点插入导致二叉树不平衡左右先局部左旋再整体右旋右右一次左旋右左先局部右旋再整体左旋红黑树红黑树是一种自平衡的二叉查找树不是高度平衡的而是通过红黑规则实现的红黑规则每一个节点是红色或者黑色根节点是黑色的如果一个节点没有子节点或者父节点则该节点相应的指针值为Nil这些Nil视为叶节点每个叶节点Nil是黑色的如果一个节点是红色的那么他的子节点必须是黑色的不能出现两个红色节点相连的情况对每一个节点从该节点到其所有后代叶节点的简单路径上均包含相同数目的黑色节点Set特点无序存取顺序不一致不重复集合中的元素不重复无索引没有带索引的方法不能通过普通的for循环遍历也不能通过索引获取元素HashSet无序、不重复、无索引底层原理创建一个默认长度16加载因子0.75的数组数组名为table根据元素的哈希值跟数组的长度计算出存入的位置int index (数组长度-1) 哈希值;判断当前元素是否为null如果为null直接存入如果位置不为null表示有元素则调用equals方法比较属性值一样不存 不一样存入数组形成链表 JDK8以前新元素存入数组老元素在新元素下面JDK8以后新元素直接挂在老元素下面当链表长度超过8而且数组长度大于等于64时自动转换为红黑树如果集合中存储的是自定义对象必须重写hashCode和equals方法LinkedHashSet有序、不重复、无索引有序指保证存储和取出的元素顺序一致底层原理底层数据结构是哈希表每个元素额外多了一个双链表机制记录存储的顺序TreeSet可排序、不重复、无索引可排序按照元素的默认规则由小到大排序底层原理基于红黑树的数据结构实现排序增删改查性能都比较好两种排序方式自然排序Javabean类实现Comparable接口指定比较规则public int compareTo(Student o) { return this.getAge()-o.getAge(); // this:表示当前元素 // o:表示已经在红黑树存在的元素 // 返回值 // 负数表示当前要添加的元素是小的存左边 // 正数表示当前要添加的元素是大的存右边 // 0表示当前要添加的元素已经存在不存 }比较器排序创建TreeSet对象时传递比较强Comparator指定规则使用原则默认第一种如果第一种不能满足当前需求就使用第二种TreeSetStudent set new TreeSet(new ComparatorStudent() { ​ Override public int compare(Student o1, Student o2) { int io1.getAge()-o2.getAge(); return i; } });用lambda表达式简化TreeSetStudent set new TreeSet((s,p)-s.getAge()-p.getAge());复杂度分析操作HashSetTreeSetLinkedHashSet查询 (contains)O(1) 平均 O(n) 最坏O(log n)O(1) 平均插入 (add)O(1) 平均 O(n) 最坏O(log n)O(1) 平均删除 (remove)O(1) 平均 O(n) 最坏O(log n)O(1) 平均空间复杂度O(n)O(n)O(n)各Set特点HashSet:基于哈希表实现无序存储性能最优TreeSet:基于红黑树实现保持元素排序有序存储LinkedHashSet:哈希表 链表实现保持插入顺序遍历方式增强for循环for-eachfor (ElementType element : set) { // 处理element } ​Iterator迭代器IteratorElementType iterator set.iterator(); while (iterator.hasNext()) { ElementType element iterator.next(); // 处理element } ​Stream APIJava 8set.stream().forEach(element - { // 处理element }); ​parallelStream并行流set.parallelStream().forEach(element - { // 处理element }); ​MapMap集合也叫键值对集合Map集合键不可以重复值可以重复遍历方式增强for循环for-eachfor (Map.EntryK, V entry : map.entrySet()) { K key entry.getKey(); V value entry.getValue(); // 处理key和value } ​Iterator迭代器IteratorMap.EntryK, V iterator map.entrySet().iterator(); while (iterator.hasNext()) { Map.EntryK, V entry iterator.next(); K key entry.getKey(); V value entry.getValue(); // 处理key和value } ​forEach方法Java 8map.forEach((key, value) - { // 处理key和value });lambdamap.forEach((k,v)- System.out.println(k v));Stream APIJava 8map.entrySet().stream().forEach(entry - { K key entry.getKey(); V value entry.getValue(); // 处理key和value });遍历键或值单独遍历// 遍历键 for (K key : map.keySet()) { // 处理key } // 遍历值 for (V value : map.values()) { // 处理value }HashMapSet系列集合的底层就是基于Map实现的只是Set集合中的元素只要键数据不要值而已HashMap底层原理和HashSet是一样的Map实现查询(get)插入(put)删除(remove)空间复杂度HashMapO(1) 平均 O(n) 最坏O(1) 平均 O(n) 最坏O(1) 平均 O(n) 最坏O(n)TreeMapO(log n)O(log n)O(log n)O(n)LinkedHashMapO(1) 平均O(1) 平均O(1) 平均O(n)HashtableO(1) 平均 O(n) 最坏O(1) 平均 O(n) 最坏O(1) 平均 O(n) 最坏O(n)ConcurrentHashMapO(1) 平均 O(n) 最坏O(1) 平均 O(n) 最坏O(1) 平均 O(n) 最坏O(n)特点说明HashMap: 基于哈希表实现无序存储性能最优TreeMap: 基于红黑树实现按键排序存储LinkedHashMap: 哈希表链表实现保持插入顺序Hashtable: 线程安全的哈希表实现性能相对较低ConcurrentHashMap: 线程安全的现代哈希表实现支持高并发NavigableMap 常用方法键查找方法lowerKey(K key) - 返回严格小于给定键的最大键floorKey(K key) - 返回小于或等于给定键的最大键ceilingKey(K key) - 返回大于或等于给定键的最小键higherKey(K key) - 返回严格大于给定键的最小键Entry 查找方法lowerEntry(K key) - 返回严格小于给定键的最大键值对floorEntry(K key) - 返回小于或等于给定键的最大键值对ceilingEntry(K key) - 返回大于或等于给定键的最小键值对higherEntry(K key) - 返回严格大于给定键的最小键值对边界元素方法firstEntry() - 返回第一个最小的键值对lastEntry() - 返回最后一个最大的键值对pollFirstEntry() - 获取并移除第一个键值对pollLastEntry() - 获取并移除最后一个键值对导航和子集方法descendingIterator() - 返回降序键迭代器descendingMap() - 返回逆序视图的 NavigableMapheadMap(K toKey) - 返回严格小于指定键的部分视图tilMap(K fromKey) - 返回大于等于指定键的部分视图Stream流Stream流是一种可以用于操作集合或者数组数据的一套API示例ListString list2 list.stream() .filter(s - s.startsWith(张)) .filter(s - s.length() 3) .collect(Collectors.toList());获取stream流listListString s list.stream();mapStreamString s1 map.keySet().stream(); StreamInteger s2 map.values().stream(); StreamMap.EntryString, Integer s3 map.entrySet().stream();arrrayStreamInteger s1 Arrays.stream(arr); StreamInteger s2 Arrays.stream(arr); StreamInteger s3 Stream.of(arr); StreamString s4 Stream.of(张三, 李四, 王五);Stream流常用方法Stream流的终结方法Collection工具类字符集ASCII标准ASCII字符集使用一个字节存储一个字符首位是0GBK汉字编码字符集一个中文编码成两个字节的形式存储汉字的第一个字节必须是1Unicode字符集统一码也叫万国码Unicode是国际组织制定的可以容纳世界上所有文字、符号的字符集UTF-32每4个字节表示一个字符UTF-8是Unicode字符集的一种编码方式采取可变长度编码方案共分4个长度区1个字节、2个字节、3个字节、4个字节英文字符、数字等占一个字节汉字占用3个字节IO流I指input称为输入流负责把数据读到内存中O指Outout称为输出流负责写数据出去读文本适合用字符流字节流适合数据的转移比如文件复制字节流文件字节输入流FileInputStream filenew FileInputStream(1.text); byte []buf file.readAllBytes(); int len; while((lenfile.read(buf))!-1){ String strnew String(buf, 0, len); System.out.print(str); }文件字节输出流FileOutputStream filenew FileOutputStream(out.txt,true); byte[] bytes 马超.getBytes(StandardCharsets.UTF_8); file.write(bytes); file.write(\r\n.getBytes(StandardCharsets.UTF_8)); file.close();文件复制字节流非常适合文件复制public static void copyFile(String src,String dest) throws IOException { //1.创建一个文件字节输入流管道与源文件连通 FileInputStream fis new FileInputStream(src); FileOutputStream fos new FileOutputStream(dest); byte[] buffer new byte[1024]; int len; while((len fis.read(buffer))!-1){ fos.write(buffer,0,len); } fos.close(); ​ }资源释放一般用于在程序执行完成后用于资源的释放操作字符流文件字符输入流文件字符输出流缓冲流作用提高字节输入读取数据的性能原理缓冲字节输入流自带8KB的缓冲池缓冲字节输出流自带8KB缓冲池缓冲字节流缓冲字符输入流try ( Reader fr new FileReader(src); BufferedReader br new BufferedReader(fr) ) { String line; while((linebr.readLine())!null){ System.out.println(line); } }catch(Exception e){ e.printStackTrace(); }缓冲字符输出流try ( Reader fr new FileReader(src); BufferedReader br new BufferedReader(fr); Writer fwnew FileWriter(dest,true); BufferedWriter bwnew BufferedWriter(fw) ) { String line; while((linebr.readLine())!null){ bw.write(line); bw.newLine(); } }catch(Exception e){ e.printStackTrace(); }字符输入转换流解决不同编码时字符读取文本乱码问题解决思路先获取文件的原始字节流再将其按真实的字符集编码转化成字符输入流这样字符输入流中的字符就不乱码了打印流作用更方便、更高效的打印数据出去能实现打印啥就是啥PrintStream ps new PrintStream(3.text); ps.println(hello); ps.println(97);数据输出流允许把数据和其类型一并写出去IO框架多线程线程是一个程序内部的一条执行流程多线程是指从软硬件上实现的多条执行流程技术创建方式继承Thread类定义一个子类MyThread继承线程类重写run()方法创建MyThread对象调用线程对象的start()方法启动线程启动后还是执行run方法的public class Test{ public static void main(String[] args) { MyThread t1new MyThread(); t1.start(); ​ } ​ } class MyThread extends Thread{ public void run(){ for(int i0;i10;i){ System.out.println(Thread.currentThread().getName() i); } } }实现Runnable接口定义一个线程任务类MyThread实现Runnable接口重写run()方法创建MyRunnable任务对象把MyRunnable任务对象交给Thread处理​ public class Test{ public static void main(String[] args) { MyRunnable mr new MyRunnable(); Thread t1 new Thread(mr,线程1); t1.start(); } ​ } class MyRunnable implements Runnable{ public void run(){ for (int i 0; i 10; i) { System.out.println(Thread.currentThread().getName()--i); } } }或者使用匿名内部类方式public static void main(String[] args) { //方式1 Runnable r new Runnable() { Override public void run() { for (int i 0; i 5; i) { System.out.println(Thread.currentThread().getName() i); } } }; Thread tnew Thread(r); t.start(); //方式2 new Thread(() -{ for (int i 0; i 5; i) { System.out.println(Thread.currentThread().getName() i); } }).start(); }实现Callable接口优点可以返回线程执行完毕后的结果定义一个类实现Callable接口重写call方法把Callable类型的对象封装成FutureTask线程任务对象调用Thread对象的start()方法线程执行完毕后通过FutureTask对象的get方法获取线程任务执行的结果public class Test{ public static void main(String[] args) { CallableString mc new MyCallable(100); FutureTaskString ft new FutureTask(mc); Thread tnew Thread(ft); t.start(); ​ ​ try { System.out.println(ft.get()); } catch (Exception e) { e.printStackTrace(); } } ​ } class MyCallable implements CallableString { private int n; public MyCallable(int n) { this.n n; } Override public String call() throws Exception { ​ int sum0; for(int i0;in;i){ sumi; } return 0-n的和sum; } }线程安全问题多个线程同时访问一个共享资源且存在修改时会发生线程安全问题解决方法同步代码块把访问共享资源的核心代码上锁以此保证线程安全原理每次指允许一个线程加锁后进入执行完毕后自动解锁其它线程才可以进来执行使用规范建议使用共享资源作为锁对象对于实列方法建议使用this作为锁对象对于静态方法建议使用字节码类名.class对象作为锁对象public void drawMoney(double money){ String nammeThread.currentThread().getName(); synchronized (this) { if(this.moneymoney){ System.out.println(namme取钱成功取了money元); this.money-money; System.out.println(余额为this.money); }else{ System.out.println(namme取钱失败余额不足); } } }同步方法作用把共享资源的核心方法上锁以此保护线程安全public synchronized void drawMoney(double money){ String nammeThread.currentThread().getName(); if(this.moneymoney){ System.out.println(namme取钱成功取了money元); this.money-money; System.out.println(余额为this.money); }else{ System.out.println(namme取钱失败余额不足); } }lock锁public void drawMoney(double money){ try { String nammeThread.currentThread().getName(); lock.lock(); if(this.moneymoney){ System.out.println(namme取钱成功取了money元); this.money-money; System.out.println(余额为this.money); }else{ System.out.println(namme取钱失败余额不足); } } finally { lock.unlock(); } }线程池线程池就是一个可以复用的技术不使用线程池的问题创建新线程的开销是很大的并且请求过多时肯定会产生大量的线程出来这样会严重影响系统的性能。创建线程池对象使用ExecutorServixe的实现类ThreadPoolExecutor自创一个线程池对象什么时候创建临时线程新任务提交时发现核心线程都在忙任务队列也满了并且还可以创建线程此时才会创建临时线程核心线程和临时线程都很忙任务队列也满了新任务过来才会开始拒绝任务public static void main(String[] args) { ThreadPoolExecutor pool new ThreadPoolExecutor(3,5, 10,SECONDS,new ArrayBlockingQueue(3), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy()); Runnable rnew MyRunnable(); pool.execute(r);//提交任务创建第1个线程 pool.execute(r);//提交任务创建第2个线程 pool.execute(r);//提交任务创建第3个线程 pool.execute(r);//复用线程 pool.execute(r);//复用线程 pool.execute(r);//创建临时线程 pool.execute(r);//创建临时线程 pool.execute(r);//创建临时线程 pool.execute(r);//任务拒绝策略 ​ }使用Executors线程池的工具类调用方法返回不同特点的线程池对象线程池的核心线程数量和最大线程数量的配置需要根据具体的应用场景、任务类型CPU密集型或I/O密集型以及硬件资源来决定。CPU密集型任务 核心线程数CPU核心数 1 最大线程数与核心线程数相同或稍大一些 这种情况下线程数不宜过多否则会因上下文切换导致性能下降。I/O密集型任务 核心线程数CPU核心数 * 2 最大线程数可以根据任务的具体情况适当增加通常可以设置为几十到上百。 I/O操作会让线程等待因此可以配置更多的线程来提高CPU利用率。并发并行正在运行的程序是一个独立的进程线程是属于进程的一个进程可以同时运行很多个线程进程中的多个线程其实是并发和并行执行的并发并发是指在同一时间段内多个任务交替执行给人一种同时进行的感觉。它通过任务切换实现尽管在单核CPU上同一时刻只有一个任务运行但快速切换使得多个任务看似并行。并行并行是指多个任务在同一时刻真正同时执行通常需要多核处理器或多台计算机的支持。与并发不同并行任务不仅在逻辑上同时进行物理上也在同一时间执行。网络编程计算机网络中连接和通信数据的规则被称为网络协议UDP协议用户数据协议面向无连接不可靠传输的通信协议TCP协议面向连接、可靠通信目的保证数据在不可靠信道上实现可靠传输。三次握手建立连接四次挥手断开连接UDP通信一发一收public class Server { public static void main(String[] args) throws Exception { //完成UDP通信一发一收服务端开发 System.out.println(服务端启动...); //1.创建服务器Socket对象 DatagramSocket socket new DatagramSocket(8080); //2.创建数据包用于接收客户端发送的数据 byte[] bys new byte[1024*64]; DatagramPacket dp new DatagramPacket(bys, bys.length); //3.接受数据将数据封装到数据包中 socket.receive(dp); String datanew String(bys,0,dp.getLength()); System.out.println(客户端说data); } } ​public class Client { public static void main(String[] args) throws Exception { //完成UDP通信一发一收客户端开发 System.out.println(客户端启动...); //1.创建发送端对象 DatagramSocket socket new DatagramSocket(); //2.创建数据包对象封装要发送的数据 byte[] bytes 你好我是客户端.getBytes(); DatagramPacket dp new DatagramPacket(bytes,bytes.length, InetAddress.getLocalHost(),8080); //3.发送数据 socket.send(dp); ​ } }多发多收public class Client { public static void main(String[] args) throws Exception { //完成UDP通信一发一收客户端开发 System.out.println(客户端启动...); //1.创建发送端对象 DatagramSocket socket new DatagramSocket(); Scanner sc new Scanner(System.in); while (true) { System.out.println(请输入要发送的数据(exit)终止:); String msgsc.nextLine(); if(exit.equals(msg)){ break; } //2.创建数据包对象封装要发送的数据 byte[] bytes msg.getBytes(); DatagramPacket dp new DatagramPacket(bytes,bytes.length, InetAddress.getLocalHost(),8080); //3.发送数据 socket.send(dp); } socket.close(); ​ } }public class Server { public static void main(String[] args) throws Exception { //完成UDP通信一发一收服务端开发 System.out.println(服务端启动...); //1.创建服务器Socket对象 DatagramSocket socket new DatagramSocket(8080); //2.创建数据包用于接收客户端发送的数据 byte[] bys new byte[1024*64]; DatagramPacket dp new DatagramPacket(bys, bys.length); while (true) { ​ //3.接受数据将数据封装到数据包中 socket.receive(dp); String datanew String(bys,0,dp.getLength()); System.out.println(客户端说data); ​ String ipdp.getAddress().getHostAddress(); int portdp.getPort(); System.out.println(客户端的ipip 客户端的端口port); System.out.println(------------------------------------); } } }TCP通信一发一收public class ClientDemo { public static void main(String[] args) throws Exception { //实现TCP通信一发一收客户端开发 System.out.println(客户端启动...); //1.获取Socket管道对象请求与服务端的Socket连接 Socket socket new Socket(127.0.0.1, 9999); //2.从Socket通信管道中得到一个字节输出流 OutputStream os socket.getOutputStream(); //3.特殊数据流 DataOutputStream dos new DataOutputStream(os); dos.writeInt(1); dos.writeUTF(你好我是客户端); //4.关闭流和Socket dos.close(); } }public class ServerDemo { public static void main(String[] args) throws Exception { //实现TCP通信下一发一收服务端开发 System.out.println(服务端启动...); //1.创建服务端ServerSocket对象 ServerSocket ss new ServerSocket(9999); //2.调用accept方法阻塞等待客户端连接一旦有客户端连接accept方法就会返回一个Socket对象 Socket socket ss.accept(); //3.获取输入流读取客户端发送的数据 InputStream is socket.getInputStream(); //4.把字节输入流包装成特殊数据输入流 DataInputStream dis new DataInputStream(is); //5.读取数据 int iddis.readInt(); String msgdis.readUTF(); System.out.println(idid,msgmsg); //6.客户端的ip和端口 System.out.println(客户端的ipsocket.getInetAddress().getHostAddress()); System.out.println(客户端的端口socket.getPort()); } }多发多收public class ClientDemo2 { public static void main(String[] args) throws Exception { //实现TCP通信一发一收客户端开发 System.out.println(客户端启动...); //1.获取Socket管道对象请求与服务端的Socket连接 Socket socket new Socket(127.0.0.1, 9999); //2.从Socket通信管道中得到一个字节输出流 OutputStream os socket.getOutputStream(); //3.特殊数据流 DataOutputStream dos new DataOutputStream(os); Scanner sc new Scanner(System.in); while (true) { System.out.println(请输入要发送的内容(exit退出)); String msgsc.nextLine(); if(exit.equals(msg)){ System.out.println(客户端退出...); socket.close(); break; } dos.writeUTF(msg); } } }public class ServerDemo2 { public static void main(String[] args) throws Exception { //实现TCP通信下一发一收服务端开发 System.out.println(服务端启动...); //1.创建服务端ServerSocket对象 ServerSocket ss new ServerSocket(9999); //2.调用accept方法阻塞等待客户端连接一旦有客户端连接accept方法就会返回一个Socket对象 Socket socket ss.accept(); //3.获取输入流读取客户端发送的数据 InputStream is socket.getInputStream(); //4.把字节输入流包装成特殊数据输入流 DataInputStream dis new DataInputStream(is); while (true) { //5.读取数据 String msgdis.readUTF(); System.out.println(msgmsg); //6.客户端的ip和端口 System.out.println(客户端的ipsocket.getInetAddress().getHostAddress()); System.out.println(客户端的端口socket.getPort()); } } }public class ServerThread { public static void main(String[] args) throws Exception { //实现TCP通信下一发一收服务端开发 System.out.println(服务端启动...); //1.创建服务端ServerSocket对象 ServerSocket ss new ServerSocket(9999); while(true){ Socket socket ss.accept(); new ServerReader(socket).start(); } } }public class ServerReader extends Thread{ private Socket Socket; public ServerReader(Socket socket) { this.Socket socket; } Override public void run() { try { DataInputStream dis new DataInputStream(Socket.getInputStream()); while (true) { String msg dis.readUTF(); System.out.println(msg msg); System.out.println(客户端的ip Socket.getInetAddress().getHostAddress()); System.out.println(客户端的端口 Socket.getPort()); } } catch (Exception e) { e.printStackTrace(); System.out.println(客户端断开连接Socket.getInetAddress().getHostAddress()); } } }反射加载类并允许以编程的方式解剖类中的各种成分成员变量、方法、构造器等基本作用可以得到一个类的全部成分然后操作可以破坏封装性可以绕过泛型约束最重要的用途适合做Java的框架基本上主流的框架都会基于反射设计一些通用的功能public static void saveObject(Object obj) throws Exception { PrintStream ps new PrintStream(new FileOutputStream(5.text, true)); Class cobj.getClass(); String simpleName c.getSimpleName(); ps.println(simpleName); Field[] fields c.getDeclaredFields(); for (Field field : fields) { String fieldName field.getName(); field.setAccessible(true); Object fieldValue field.get(obj); ps.println(fieldNamefieldValue); } }注解注解是Java代码里的特殊标记作用是让其它程序根据注解信息来决定怎么执行该程序。自定义注解注解的解析元注解Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface MyTest { int count() default 1; }public class AnnotationDemo { public static void main(String[] args) throws Exception { Class c AnnotationDemo.class; AnnotationDemo adnew AnnotationDemo(); Method[] methodsc.getMethods(); for (Method method : methods) { if(method.isAnnotationPresent(MyTest.class)){ MyTest myTestmethod.getDeclaredAnnotation(MyTest.class); int count myTest.count(); for (int i 0; i count; i) { method.invoke(ad); } } } } MyTest(count 5) public void test1(){ System.out.println(test1方法执行了); } public void test2(){ System.out.println(test2方法执行了); } MyTest public void test3(){ System.out.println(test3方法执行了); } public void test4(){ System.out.println(test4方法执行了); } }动态代理public interface StarService { void sing(String name); String dance(); }Data AllArgsConstructor NoArgsConstructor public class Star implements StarService{ private String name; public void sing(String name) { System.out.println(this.name 正在唱歌); } public String dance() { System.out.println(this.name 正在跳舞); return 谢谢; } }public class ProxyUtil { public static StarService createProxy(Star star){ StarService proxy(StarService) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(), star.getClass().getInterfaces(), new InvocationHandler() { Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String name method.getName(); if(name.equals(sing)){ System.out.println(准备话筒收钱20w); } if(name.equals(dance)){ System.out.println(准备场地收钱100w); } return method.invoke(star, args); } }); return proxy; } }public class ProxyTest { public static void main(String[] args) { Star star new Star(洛天依); StarService proxy ProxyUtil.createProxy(star); proxy.sing(霜雪千年); System.out.println(proxy.dance()); } }加强版工具包SuppressWarnings(unchecked) public class UserUtil { public static T T createProxy(T t){ T proxy(T) Proxy.newProxyInstance(UserUtil.class.getClassLoader(), t.getClass().getInterfaces(), new InvocationHandler() { Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long start System.currentTimeMillis(); Object result method.invoke(t, args); long end System.currentTimeMillis(); System.out.println(耗时(end-start)/1000秒); return result; } }); return proxy; } }