第7章 Java高级技术

发布时间:2026/7/10 12:21:33
第7章 Java高级技术 一、枚举枚举是Java中的一种特殊类型一般用来做信息的标记和分类 使用标记来区分春、夏、秋、冬四个季节1、传统的枚举方法2、枚举的枚举方法3、枚举的特点1、每一个枚举项其实就是该枚举的一个对象2、通过枚举类名去访问指定的枚举项3、所有枚举类都是Enum的子类4、枚举也是类可以定义成员变量5、枚举类的第一行上必须是枚举项最后一个枚举项后的分号是可以省略的但是如果枚举类有其他的东西这个分号就不能省略建议不要省略6、枚举类可以有构造器但必须是private的它默认也是private7、枚举类也可以有抽象方法但是枚举项必须重写该方法4、代码示例package com.itheima.my_enum; ​ import java.lang.annotation.ElementType; ​ public class EnumTest { /* 枚举介绍: Java中的一种特殊的类型, 常用于信息的标记和分类 ​ 1. 常量 2. 枚举 (入参严谨, 提示性更好代码优雅) */ public static void main(String[] args) { useSeason(Season.WINTER); ​ System.out.println(Season.SPRING.ordinal());//枚举项是该类的对象可以调用成员方法 System.out.println(Season.SUMMER.ordinal());//获取枚举项的序号 System.out.println(Season.AUTUMN.ordinal()); System.out.println(Season.WINTER.ordinal()); } ​ //常量标记不严谨 public static void useSeason(Season season) { switch (season) { case SPRING: System.out.println(春季); break; case SUMMER: System.out.println(夏季); break; case AUTUMN: System.out.println(秋季); break; case WINTER: System.out.println(冬季); break; } } } ​ //每一个枚举项都是该类的对象 enum Season { SPRING(春季), SUMMER(夏季), AUTUMN(秋季), WINTER(冬季);//对象调用构造方法 ​ //枚举一般没有乱七八糟的东西只有几个枚举项 String name;//可以定义成员变量 private Season(String name){ this.name name; } }5、枚举的使用场景用来做信息的标记和分类---代码可读性更高入参约束严谨代码优雅二、类加载器将类的字节码载入方法区中1、加载时机加载时机用到就加载包括创建对象、调用类的静态成员、初始化继承体系、使用反射加载类的字节码2、加载过程1加载通过包名类名获取这个类准备用流进行传输将类加在到内存中加载完毕创建一个class对象2链接验证验证类是否符合JVM规范安全性检查准备为static变量分配空间设置默认值解析将常量池中的符号引用解析为直接引用3初始化根据程序员程序编码制定的主观计划去初始化类变量和其他资源三、类加载器的分类1、Bootstrap class loader启动类加载器虚拟机的内置类加载器通常表示为nullC实现获取到的只能是null2、Platform class loader平台类加载器负责加载JDK中一些特殊的模块负责加载lib\modules内部的类JDK9之前是Extension Class Loader扩展类加载器 负责加载jre\lib\ext目录下的类3、Application class loader应用程序类加载器负责加载自己写的类自定义类加载器上级为Application 目前不做讲解public Classloader getclassLoader() 获取该类的类加载器对象逻辑上父类应用程序类--扩展类--启动类代码示例package com.itheima.classloader; ​ import sun.net.spi.nameservice.dns.DNSNameService; ​ public class ClassLoaderDemo { public static void main(String[] args) { // 1. 获取BootStrapClassLoader (启动类\引导类加载器) ClassLoader classLoader1 String.class.getClassLoader(); System.out.println(classLoader1); ​ // 2. 获取PlatformClassLoader (平台类加载器 jdk\lib\modules) ClassLoader classLoader2 DNSNameService.class.getClassLoader(); System.out.println(classLoader2); ​ // 3. 获取ApplicationClassLoader (应用程序类加载器) ClassLoader classLoader3 ClassLoaderDemo.class.getClassLoader(); System.out.println(classLoader3); ​ // 测试类加载器的上下级关系 System.out.println(classLoader2.getParent());//获取上级加载器 System.out.println(classLoader2.getParent().getParent()); } }四、双亲委派模式如果一个类加载器收到了类加载请求它并不会自己先去加载而是把这个请求委托给父类的加载器去执行如果父类加载器还存在其父类加载器则进一步向上委托依次递归请求最终将到达顶层的启动类加载器如果父类加载器可以完成类加载任务就成功返回倘若父类加载器无法完成此加载任务子加载器才会尝试自己去加载这就是双亲委派模式双亲委派模式图解五、反射介绍-获取字节码对象框架技术的灵魂是在运行状态中对于任意一个类都能够知道这个类的所有属性和方法对于任意一个对象都能够调用它的任意属性和方法这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制学习路径获取类的字节码对象反射类中的构造方法随后创建对象反射类中的成员变量完成赋值和获取反射类中的成员方法调用成员方法获取字节码对象的三种方式1. Class.forName(全类名);//Class不是一个class类而是一个对象。第一种方式比较灵活 2. 类名.class//第二种方式比较简单 3. 对象.getClass();//第三种方式可以获取对象的。来自Object类所以所有对象都可以调用该方法代码示例package com.itheima.reflect; ​ import com.itheima.pojo.Student; ​ public class ReflectDemo1 { /* 反射获取类的字节码对象 ​ 1. Class.forName(全类名); 2. 类名.class 3. 对象.getClass(); */ public static void main(String[] args) throws ClassNotFoundException { // 1. Class.forName(全类名); Class? class1 Class.forName(com.itheima.pojo.Student); ​ // 2. 类名.class ClassStudent class2 Student.class; ​ // 3. 对象.getClass(); Student stu new Student(); Class? extends Student class3 stu.getClass(); ​ //证实字节码对象的唯一性 System.out.println(class1 class2); System.out.println(class2 class3); System.out.println(class3 class1); } }六、反射获取构造方法创建对象反射类中的构造方法1. Constructor?[] getConstructors() 返回所有公共构造方法对象的数组获取多个返回公共权限的构造方法 2. Constructor?[] getDeclaredConstructors() 返回所有构造方法对象的数组暴力反射获取 3. ConstructorT getConstructor(Class?... parameterTypes) 返回单个公共构造方法对象返回指定的Constructor对象 4. ConstructorT getDeclaredConstructor(Class?... parameterTypes) 返回单个构造方法对象暴力反射获取 1. T newInstance(Object...initargs) 根据指定的构造方法创建对象 2. setAccessible(boolean flag) 设置为true,表示取消访问检查package com.itheima.reflect; ​ import com.itheima.pojo.Student; ​ import java.lang.reflect.Constructor; ​ public class ReflectDemo2 { /* 反射类中的构造方法 ​ 1. Constructor?[] getConstructors() 返回所有公共构造方法对象的数组 2. Constructor?[] getDeclaredConstructors() 返回所有构造方法对象的数组 3. ConstructorT getConstructor(Class?... parameterTypes) 返回单个公共构造方法对象 4. ConstructorT getDeclaredConstructor(Class?... parameterTypes) 返回单个构造方法对象 ​ --------------------------------------------------------------------------------------------------- 创建对象的方法 1. T newInstance(Object...initargs) 根据指定的构造方法创建对象 2. setAccessible(boolean flag) 设置为true,表示取消访问检查 --------------------------------------------------------------------------------------------------- */ public static void main(String[] args) throws Exception { // 1. 获取类的字节码对象 ClassStudent studentClass Student.class; // 2. 反射构造方法对象 ConstructorStudent constructor studentClass.getDeclaredConstructor(String.class, int.class);//反射String和int类型的构造方法 //取消访问检查 constructor.setAccessible(true); // 3. 通过构造方法对象, 完成实例化 Student stu constructor.newInstance(张三, 23); ​ System.out.println(stu); } ​ private static void method(ClassStudent studentClass) {//暴力反射获取 Constructor?[] constructors studentClass.getDeclaredConstructors(); for (Constructor? c : constructors) { System.out.println(c); } } ​ }七、反射操作类中成员变量反射类中的成员变量1. Field[] getFields() 返回所有公共成员变量对象的数组 2. Field[] getDeclaredFields() 返回所有成员变量对象的数组 3. Field getField(String name) 返回单个公共成员变量对象 4. Field getDeclaredField(String name) 返回单个成员变量对象 Field类的设置和获取方法 1. void set(Object obj, Object value) 赋值 2. Object get(Object obj) 获取值代码示例package com.itheima.reflect; import com.itheima.pojo.Student; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; public class ReflectDemo3 { /* 反射类中的成员变量 1. Field[] getFields() 返回所有公共成员变量对象的数组 2. Field[] getDeclaredFields() 返回所有成员变量对象的数组 3. Field getField(String name) 返回单个公共成员变量对象 4. Field getDeclaredField(String name) 返回单个成员变量对象 --------------------------------------------------------------------------------------------------- Field类的设置和获取方法 1. void set(Object obj, Object value) 赋值 2. Object get(Object obj) 获取值 --------------------------------------------------------------------------------------------------- */ public static void main(String[] args) throws Exception { //拿到Students的字节码对象 ClassStudent studentClass Student.class; // 反射类的空参构造方法并创建对象 Student stu1 studentClass.getConstructor().newInstance(); Student stu2 studentClass.getConstructor().newInstance(); // 获取成员变量的对象 Field nameField studentClass.getDeclaredField(name); nameField.setAccessible(true);//取消访问检查 Field ageField studentClass.getDeclaredField(age); ageField.setAccessible(true);//取消访问检查 // 通过成员变量对象, 完成赋值和获取操作 nameField.set(stu1, 张三); nameField.set(stu2, 李四); ageField.set(stu1, 23); ageField.set(stu2, 24); System.out.println(nameField.get(stu1)); System.out.println(nameField.get(stu2)); System.out.println(ageField.get(stu1)); System.out.println(ageField.get(stu2)); } }八、反射操作类中的成员方法开发中禁止暴力反射操作反射类中的成员方法1. Method[] getMethods() 返回所有公共成员方法对象的数组包括继承的 2. Method[] getDeclaredMethods() 返回所有成员方法对象的数组, 不包括继承的 3. Method getMethod(String name, Class?... parameterTypes) 返回单个公共成员方法对象 4. Method getDeclaredMethod(String name, Class?... parameterTypes) 返回单个成员方法对象 Method类的执行方法 Object invoke(Object obj, Object... args) 运行方法代码示例package com.itheima.reflect; import com.itheima.pojo.Student; import java.lang.reflect.Method; public class ReflectDemo4 { /* 反射类中的成员方法 1. Method[] getMethods() 返回所有公共成员方法对象的数组包括继承的 2. Method[] getDeclaredMethods() 返回所有成员方法对象的数组, 不包括继承的 3. Method getMethod(String name, Class?... parameterTypes) 返回单个公共成员方法对象 4. Method getDeclaredMethod(String name, Class?... parameterTypes) 返回单个成员方法对象 --------------------------------------------------------------------------------------------------- Method类的执行方法 Object invoke(Object obj, Object... args) 运行方法 --------------------------------------------------------------------------------------------------- */ public static void main(String[] args) throws Exception { ClassStudent studentClass Student.class; //反射构造方法并创造实例 Student stu studentClass.getConstructor().newInstance(); // 反射指定的成员方法对象 Method eat1 studentClass.getMethod(eat); Method eat2 studentClass.getMethod(eat, int.class); // 让成员方法执行 eat1.invoke(stu); eat2.invoke(stu, 3); } }九、反射练习1、泛型Integer添加字符串package com.itheima.test; import java.lang.reflect.Method; import java.util.ArrayList; public class ReflectTest1 { /* 需求: 请向一个泛型为 Integer 的集合添加一个 String 字符串 思路: Java 中的泛型是假的只在编译的时候有效 */ public static void main(String[] args) throws Exception { ArrayListInteger list new ArrayList(); list.add(1); list.add(2); list.add(3); Class? extends ArrayList listClass list.getClass(); Method addMethod listClass.getMethod(add, Object.class); addMethod.invoke(list, 张三); System.out.println(list); } }2、反射的灵活性改动配置文件的全类名和方法名就可以让程序有不一样的效果package com.itheima.test; import java.io.FileInputStream; import java.lang.reflect.Method; import java.util.Properties; public class ReflectTest2 { /* 编写三个任意的Javabean类Student类Teacher类Worker类。 属性均为nameage 方法要求 Student里面写学习study和吃饭eat的方法方法无参无返回 Teacher里面写上课teach和吃饭eat的方法方法无参无返回 Worker里面写工作work和睡觉sleep的方法方法无参无返回 本地新建配置文件properties 属性 classNameStudent类的全类名 methodNamestudy 在测试类中读取properties文件中的两个属性值 利用反射创建对象学生类的对象并调用方法 修改配置文件不修改代码运行老师类中的上课方法 修改配置文件不修改代码运行工人类中的工作方法 */ public static void main(String[] args) throws Exception { // 1. 加载配置文件 FileInputStream fis new FileInputStream(day06\\config.properties); // 2. 创建Properties集合, 加载数据 Properties prop new Properties(); prop.load(fis); fis.close(); // 3. 取出数据 String className prop.getProperty(className); String methodName prop.getProperty(methodName); // 4. 获取类的字节码对象 Class? aClass Class.forName(className); // 5. 反射内部的构造方法对象, 并实例化 Object obj aClass.getConstructor().newInstance(); // 6. 反射内部的成员方法对象 Method method aClass.getMethod(methodName); // 7. 让方法执行 method.invoke(obj); } }//配置文件 classNamecom.itheima.test.Worker methodNamework十、注解Annotation表示注解是JDK1.5的新特性。注解的主要作用对程序进行标注。理解注释是给人看的注解是给虚拟机看的通过注解可以给类增加额外的信息。编译器或JVM可以根据注解来完成对应的功能1、JDK中常见注解1.override表示方法的重写2.Deprecated表示修饰的方法已过时3.SuppressWarnings(al1)压制警告package com.itheima.anno; SuppressWarnings(all)//压制警告 public class JDKAnnoDemo { public static void main(String[] args) { method(); int a 10; int b 20; int max a b ? a : b; } Deprecated//已过时 public static void method() { } }2、自定义注解自定义注解单独存在意义不大一般会跟反射结合起来使用格式//自定义注解格式 public interface 注解名称{ public 属性类型 属性名() default 默认值; } //举例 public interface Anno { String show() default show...; }自定义注解并定义属性package com.itheima.anno; public interface MyAnno1 { String show1(); String[] show2(); String show3() default C; }属性类型基本数据类型StringClass注解枚举以上类型的一维数组public interface MyAnno { public static final int numl 100; public static final String num2 abc; public static final MyAnno num3 null; public static final Class num4 String.class; public static final int[l num5 {}; public abstract String showl() default showl; public abstract int show2() default 132; public abstract MyAnno2 show3() default MyAnno2; public abstract Class show4()default String.class; public abstract int[] show5()default {12,3}; }自定义注解的使用常用位置1.类2.方法注意在使用注解时如果注解的属性没有给出默认值需要手动给出Anno(name张三)如果数组中只有一个属性值在使用时{}是可以省略的特殊属性属性名如果是value()就不用写value 了package com.itheima.anno; public interface MyAnno2 { String value(); }package com.itheima.anno; MyAnno1(show1 A, show2 {B, C, D}) MyAnno2(A) public class JDKAnnoDemo2 { MyAnno1(show1 B, show2 Q) public void method() { } }3、自定义注解实战需求自定义一个注解Test用于指定类的方法上如果类中的某个的方法上使用了该注解就执行该方法元注解就是用在注解上注解Target 指定了注解能在哪里使用Target用来标识注解使用的位置如果没有使用该注解标识则自定义的注解可以使用在任意位置Retention 可以理解为保留时间生命周期Retention用来标识注解的生命周期有效范围可使用的值定义在RetentionPolicy枚举类中常用值如下SOURCE注解只作用在源码阶段生成的字节码文件中不存在CLASS注解作用在源码阶段字节码文件阶段运行阶段不存在默认值RUNTIME注解作用在源码阶段字节码文件阶段运行阶段代码范例//自定义注解 package com.itheima.anno; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; //如果不指定生命周期默认生命周期为在字节码文件中 Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface Test { }//配合反射技术使含有该注解的方法自动运行一个方法 package com.itheima.anno; ​ import java.lang.reflect.Method; ​ public class ScanTest { public static void main(String[] args) throws Exception { ClassMethodDemo methodDemoClass MethodDemo.class; ​ //字节码反射构造方法创建对象 MethodDemo instance methodDemoClass.getConstructor().newInstance(); ​ // 获取该类中所有成员方法的对象 Method[] methods methodDemoClass.getMethods(); ​ // 遍历数组获取每一个成员方法对象 for (Method method : methods) { // 判断该方法的头顶上, 是否存在Test注解 if (method.isAnnotationPresent(Test.class)) {//方法用于判断是否有这个注解 // 方法执行 method.invoke(instance); } } } }//带有该注解的方法自动运行该注解所代表的方法 package com.itheima.anno; ​ public class MethodDemo { ​ Test public void print() { System.out.println(print); } ​ ​ public void show() { System.out.println(show); } ​ Test public void method() { System.out.println(method); } ​ }十一、动态代理已有的创建对象的方式Dog d new Dog();//new Animal a new Dog();//多态 Dog.class.getconstructor().newInstance();//反射在运行时动态生成代理类无需手动为每个目标类编写代理类java.lang.reflect.Proxy类提供了使用动态代理方式创建对象的APIstatic Object newProxyInstance(ClassLoader loader,Class?[]interfaces,InvocationHandler h)参数介绍参数一用于指定用哪个类加载器应用程序类加载器去加载生成的实现类参数二指定接口这些接口用于指定生成的实现类中有什么也就是有哪些方法参数三用来指定生成的实现类中不同方法的实现方案代码案例package com.itheima.proxy; ​ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.List; ​ SuppressWarnings(all) public class ProxyDemo { /* Proxy类: static Object newProxyInstance(ClassLoader loader, Class?[] interfaces, InvocationHandler h) ​ 参数一: 用于指定用哪个类加载器,去加载生成的实现类 参数二: 指定接口,这些接口用于指定生成的实现类中有什么,也就是有哪些方法 参数三: 用来指定生成的实现类中不同方法的实现方案 ​ 动态代理: 对象的另外一种创建方式, 这种方式可以在不修改源码的情况下, 对方法进行增强 */ public static void main(String[] args) { ​ ArrayListString list new ArrayList(); ​ //创建代理对象 ListString newList (ListString) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{List.class}, new InvocationHandler() { ​ // proxy : 生成的代理对象, 一般放在这里不用 // method : 代理的方法 // args : 调用方法的时候, 传入的实际参数 Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { ​ String name method.getName(); ​ if (add.equals(name)) { // 计算add方法的耗时 long begin System.currentTimeMillis(); Object result method.invoke(list, args); long end System.currentTimeMillis(); System.out.println(耗时: (end - begin)); return result; ​ } return null; } }); ​ newList.add(张三); newList.add(李四); newList.add(王五); ​ System.out.println(list); ​ ​ } }总结动态代理是对象的另外一种创建方式这种方式可以在不修改源码的情况下增强方法代理对象可以在原有功能的基础上进行增强动态代理在运行时动态的生成代理类无需手动为每个目标类编写代理类使用Proxy中的newProxyInstance()方法创建代理对象