ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

枚举、反射

2026/9/24 13:53:38 拓冰建站 浏览量
枚举、反射 一、 枚举1.1 枚举的概述枚举是 Java 中一种特殊的类它可以定义固定数量的枚举实例例如: 性别、交通信号灯、季节等等1.2 为什么要使用枚举假设我们要定义一个人类人类中包含姓名和性别。通常会将性别定义成字符串类型不使用枚举存在的问题可以给性别传入任意的字符串导致性别是非法的数据不安全。解决固定数量的实例即一个方法接受的参数是固定范围之内的时候那么即可使用枚举类型1.3 格式enum 枚举名 {第一行都是罗列枚举实例,这些枚举实例直接写大写名字即可。}1.4 案例1.定义枚举enum Aimal { Cat, Dog, Zi; }2.将A中的String类型改为type枚举类型public class A { public Aimal b; }3.使用是只能传入枚举中的固定值Test public void creat(){ A anew A(); a.bB.Cat; System.out.println(a.b); }枚举中添加成员变量和成员方法枚举的本质是一个类所以枚举中还可以有成员变量成员方法等。enum Aimal { Cat(猫), Dog(狗), Zi(猪); private String s; Aimal(String s) { this.ss; System.out.println(); } public String getS() { return s; } }Test public void creat(){ A anew A(); a.bAimal.Cat; System.out.println(a.b.getS()); }二、反射2.1 类加载类在内存中的生命周期加载--使用--卸载2.1.1类加载过程当程序主动使用某个类时如果该类还未被加载到内存中系统会通过加载、连接、初始化三个步骤来对该类进行初始化如果没有意外JVM将会连续完成这三个步骤所以有时也把这三个步骤统称为类加载。三个阶段1加载load就是指将类型的class字节码数据读入内存2连接link①验证校验合法性等②准备准备对应的内存方法区创建Class对象为类变量赋默认值为静态常量赋初始值。③解析把字节码中的符号引用替换为对应的直接地址引用3初始化initialize类初始化即执行clinit类初始化方法会给类的静态变量赋初始值2.1.2 类初始化1运行主方法所在的类要先完成类初始化再执行main方法public class C { static { System.out.println(执行C); } //运行主方法所在的类要先完成类初始化再执行main方法 public static void main(String[] args) { } }2第一次使用某个类型就是在new它的对象此时这个类没有初始化的话先完成类初始化再做实例初始化public class C { /* 第一次使用某个类型就是在new它的对象 此时这个类没有初始化的话 先完成类初始化再做实例初始化*/ public static void main(String[] args) { new A(); } } public class A { static { System.out.println(执行A); } }3调用某个类的静态成员类变量和类方法此时这个类没有初始化的话先完成类初始化public class C { /* 调用某个类的静态成员类变量和类方法 此时这个类没有初始化的话 先完成类初始化*/ public static void main(String[] args) { System.out.println(A.s); } } public class A { public static int s20; static { System.out.println(执行A); } }4子类初始化时发现它的父类还没有初始化的话那么先初始化父类class D{ static { System.out.println(执行A); } } public class C extends D{ static { System.out.println(执行c); } /* 子类初始化时 发现它的父类还没有初始化的话 那么先初始化父类*/ public static void main(String[] args) { new C(); } }5通过反射操作某个类时如果这个类没有初始化也会导致该类先初始化**注意什么情况下类不会被加载到内存中①、访问静态常量class D{ public static final int s10; static { System.out.println(执行A); } } public class C { /* 访问静态常量 类不会被加载到内存中 */ public static void main(String[] args) { System.out.println(D.s); } }②、通过子类去调用父类的静态资源(子类不会被加载、父类会被加载)public class A { public static int s10; public static void setS(){ } static { System.out.println(执行A); } }class E extends A{ static { System.out.println(执行E); } } public class C { /* 通过子类去调用父类的静态资源(子类不会被加载、父类会被加载) */ public static void main(String[] args) { E.setS(); } }③、使用对应类型数组(该类不会被加载)class D{ public static final int s10; static { System.out.println(执行A); } } public class C { /* 使用对应类型数组(该类不会被加载) */ public static void main(String[] args) { D[] ds new D[10]; } }2.1.3 类加载器想要更好的解决java.lang.ClassNotFoundException或java.lang.NoClassDefError 这类问题类加载器分为:1引导类加载器Bootstrap Classloader又称为根类加载器它负责加载jre/lib中的核心库它本身不是Java代码实现的也不是ClassLoader的子类获取它的对象时往往返回null2扩展类加载器Extension ClassLoader它负责加载jre/lib/ext扩展库它是ClassLoader的子类3应用程序类加载器Application Classloader它负责加载项目的classpath路径下的类它是ClassLoader的子类4自定义类加载器当你的程序需要加载“特定”目录下的类可以自定义类加载器当你的程序的字节码文件需要加密时那么往往会提供一个自定义类加载器对其进行解码后面会见到的自定义类加载器tomcat中2.1.4 类加载器的双亲委托模式原理: 加载字节码文件时先由下至上(当前类加载器是否加载过没有则找上一层是否加载过)都没有加载则由上至下尝试进行加载都没有加载到则报错2.2java.lang.Class类2.2.1 概念Java反射机制是在运行状态中对于任意一个类都能够知道这个类的所有属性和方法对于任意一个对象都能够调用它的任意一个方法和属性这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。字节码文件.Class①、创建对象②、了解该类属于那个父类、实现了那些接口...③、知道该类有那些成员变量、成员方法④、操作成员变量、调用成员方法目的: 程序灵活、可扩展性更强2.2.2 获取Class对象的四种方式1类型名.class要求编译期间已知类型2对象.getClass()获取对象的运行时类型3Class.forName(类型全名称)通常需要配置文件配置配合使用可以获取编译期间未知的类型4ClassLoader的类加载器对象.loadClass(类型全名称)可以用系统类加载对象或自定义加载器对象加载指定路径下的类型//获取Class对象的四种方式 //第一种方式获取Class: 类型名.class ClassB bClass B.class; //第二种方式获取Class: 对象.getClass() B b new B(); Class? extends B aClass b.getClass(); //第三种方式获取Class: Class.forName(类的全限定名) Class? aClass1 Class.forName(reflex.TestReflex);*/ ClassD dClass D.class;2.3反射概念反射是一种机制/功能利用该机制/功能可以在程序运行过程中对类进行解剖并操作类中的构造方法成员方法成员属性。2.4反射的应用2.4.1 获取类型的详细信息可以获取包、修饰符、类型名、父类包括泛型父类、父接口包括泛型父接口、成员属性、构造器、方法、注解类上的、方法上的、属性上的2.4.1.1 获取包信息Package pkg clazz.getPackage();2.4.1.2 获取修饰符int mod clazz.getModifiers();修饰符定义在Modifier类中该类里面有很多常量值每一个常量对应一种修饰符2.4.1.3 获取类名String name clazz.getName();2.4.1.4 获取父类的字节码对象Class superclass clazz.getSuperclass();2.4.1.5 获取该类实现的所有接口Class[] interfaces clazz.getInterfaces();2.4.1.6 获取该类的所有属性Field[] declaredFields clazz.getDeclaredFields();2.4.1.7 获取该类的所有构造函数Method[] declaredConstructors clazz.getDeclaredConstructors();2.4.1.8 获取该类的所有方法Method[] declaredMethods clazz.getDeclaredMethods();2.4.2 创建任意引用类型的对象(重点)两种方式1、直接通过Class对象来实例化要求必须有无参构造2、通过获取构造器对象来进行实例化package reflect; public class D { private String id; private String name; private Integer age; private D(String id, String name, Integer age) { this.id id; this.name name; this.age age; } public D() { } public String getId() { return id; } public void setId(String id) { this.id id; } public String getName() { return name; } public void setName(String name) { this.name name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age age; } Override public String toString() { return D{ id id \ , name name \ , age age }; } private void showMsg(String name){ System.out.println(name name); } }方式一的步骤1获取该类型的Class对象2创建对象ClassD dClass D.class; // 创建对象 (使用字节码文件newInstance创建对象时使用无参数的构造函数) D d dClass.newInstance(); System.out.println(d);方式二的步骤ConstructorT getConstructor(Class?... parameterTypes) 返回一个 Constructor对象该对象反映 Constructor对象表示的类的指定的公共类函数。 Constructor?[] getConstructors() 返回包含一个数组 Constructor对象反射由此表示的类的所有公共构造 类对象。 ConstructorT getDeclaredConstructor(Class?... parameterTypes) 返回一个 Constructor对象该对象反映 Constructor对象表示的类或接口的指定类函数。 Constructor?[] getDeclaredConstructors() 返回一个反映 Constructor对象表示的类声明的所有 Constructor对象的数组 类 。1获取该类型的Class对象2获取构造器对象3创建对象ClassD dClass D.class; ConstructorD constructor dClass.getDeclaredConstructor(String.class, String.class, Integer.class); constructor.setAccessible(true); D ss constructor.newInstance(10012, ss, 18); System.out.println(ss);2.3.3 操作任意类型的属性(重点)反射操作字段 ①、获取字节码对象 ②、获取字段(成员变量)对象 Field getDeclaredField(String name) 返回一个 Field对象它反映此表示的类或接口的指定已声明字段 类对象。 Field[] getDeclaredFields() 返回的数组 Field对象反映此表示的类或接口声明的所有字段 类对象。 Field getField(String name) 返回一个 Field对象它反映此表示的类或接口的指定公共成员字段 类对象。 Field[] getFields() 返回包含一个数组 Field对象反射由此表示的类或接口的所有可访问的公共字段 类对象 ③、给字段取值、赋值 Object get(Object obj) void set(Object obj, Object value)1获取该类型的Class对象ClassD dClass D.class;2获取属性对象Field id dClass.getDeclaredField(id); Field name dClass.getDeclaredField(name); Field age dClass.getDeclaredField(age);3设置属性可访问id.setAccessible(true); name.setAccessible(true); age.setAccessible(true);4创建实例对象如果操作的是非静态属性需要创建实例对象D d dClass.newInstance();4设置属性值id.set(d,1003); name.set(d,王琪); age.set(d,20);5获取属性值System.out.println(id.get(d)); System.out.println(name.get(d)); System.out.println(age.get(d));System.out.println(d);如果操作静态变量那么实例对象可以省略用null表示当然一般不会使用反射操作静态变量Test public void invokeFiled() throws NoSuchFieldException, InstantiationException, IllegalAccessException { ClassD dClass D.class; Field id dClass.getDeclaredField(id); Field name dClass.getDeclaredField(name); Field age dClass.getDeclaredField(age); D d dClass.newInstance(); id.setAccessible(true); id.set(d,1003); name.setAccessible(true); name.set(d,王琪); age.setAccessible(true); age.set(d,20); System.out.println(id.get(d)); System.out.println(name.get(d)); System.out.println(age.get(d)); System.out.println(d); System.out.println(); Field[] fields dClass.getDeclaredFields(); Arrays.stream(fields) .forEach(f-{ System.out.println(f.getName():f.getModifiers()); }); }2.4.4 调用任意类型的方法反射调用方法 ①、获取字节码对象 ②、获取字节码对象中的方法对象 Method getDeclaredMethod(String name, Class?... parameterTypes) 返回一个 方法对象它反映此表示的类或接口的指定声明的方法 类对象。 Method[] getDeclaredMethods() 返回包含一个数组 方法对象反射的类或接口的所有声明的方法通过此表示 类对象包括公共保护默认包访问和私有方法但不包括继承的方法。 Method getMethod(String name, Class?... parameterTypes) 返回一个 方法对象它反映此表示的类或接口的指定公共成员方法 类对象。 Method[] getMethods() 返回包含一个数组 方法对象反射由此表示的类或接口的所有公共方法 类对象包括那些由类或接口和那些从超类和超接口继承的声明。 ③、调用方法 Object invoke(Object obj, Object... args) 在具有指定参数的 方法对象上调用此 方法对象表示的底层方法。1获取该类型的Class对象ClassD dClass D.class;2获取方法对象Method showMsg dClass.getDeclaredMethod(showMsg, String.class);3创建实例对象D d dClass.newInstance();4调用方法Object zs showMsg.invoke(d, zs); System.out.println(zs);如果方法的权限修饰符修饰的范围不可见也可以调用setAccessible(true)如果方法是静态方法实例对象也可以省略用null代替Test public void invokeMethod() throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { ClassD dClass D.class; Method showMsg dClass.getDeclaredMethod(showMsg, String.class); D d dClass.newInstance(); showMsg.setAccessible(true); Object zs showMsg.invoke(d, zs); System.out.println(zs); System.out.println(); Method[] methods dClass.getMethods(); Arrays.stream(methods) .forEach(e-{ System.out.println(e.getName()-Arrays.toString(e.getParameterTypes())); }); }