欢迎来到我的博客

将来的你一定会感激现在拼搏的自己

JavaSE基础day24 枚举常用方法、反射


 

一. 枚举的定义特点以及常用方法

(一)  枚举类型中的常用方法

1. ordinal(): 获取枚举类型中的枚举序数,序数根据定义的枚举项,从0开始,返回值int

2. compareTo(E o) : 比较枚举项之间的顺序大小,方法调用枚举项的序数减去参数枚举项的序数

3. name() : 将枚举项转换成String类型

4. toString() : 将枚举项转换成String类型

5. static <T> T valueOf(Class<T> type,String name) 是Enum类的静态方法,获取指定枚举类中的指定名称的枚举值

6. static values() : 将一个枚举类型中的所有枚举项获取到,返回值类型枚举类型的数组

public enum MyEnum {
    ONE,TWO,THREE,FOUR;
}

 

复制代码
public class EnumMethod {
    public static void main(String[] args) {
     // 1.ordinal(): 获取枚举类型中的枚举序数,序数根据定义的枚举项,从0开始,返回值int
        System.out.println(MyEnum.ONE.ordinal());// 0
        System.out.println(MyEnum.THREE.ordinal());// 2

        // 2.compareTo(E o) : 比较枚举项之间的顺序大小,方法调用枚举项的序数减去参数枚举项的序数
        // 0 - 3
        System.out.println(MyEnum.ONE.compareTo(MyEnum.FOUR));// -3

       /* 3.name() : 将枚举项转换成String类型
        4.toString() : 将枚举项转换成String类型*/

        System.out.println(MyEnum.TWO.name());// TWO
        System.out.println(MyEnum.TWO.toString());// TWO

        //5.static <T> T valueOf(Class<T> type,String name) 是Enum类的静态方法,获取指定枚举类中的指定名称的枚举值
        MyEnum my1 = MyEnum.valueOf("THREE");
        System.out.println(my1);// THREE

        System.out.println("---------------------");
        MyEnum my2 = MyEnum.valueOf(MyEnum.class,"ONE");
        System.out.println(my2);// ONE

        // 6.static values() : 将一个枚举类型中的所有枚举项获取到,返回值类型枚举类型的数组
        MyEnum[] arr = MyEnum.values();
        for(MyEnum my : arr){
            System.out.println(my);
        }
    }
}
复制代码

 

 

二. 反射

(一)  虚拟机类加载机制

2.1 虚拟机类加载机制概述

虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被java虚拟机直接使用的java类型,这就是虚拟机的类加载机制.

 

2.2 类加载过程

当程序要使用某个类时,如果该类还未被加载到内存中,系统会通过加载,连接,初始化三步来实现对这个类的加载.

1. 加载

(1) 就是指将class文件读入内存,并为之创建一个Class对象.

任何类被使用时系统都会建立一个Class对象

2. 连接

(1) 验证是否有正确的内部结构,并和其他类协调一致

(2) 准备负责为类的静态成员分配内存,并设置默认初始化值

(3) 解析将类的二进制数据中的符号引用替换为直接引用

3. 初始化

        主要对类变量进行初始化

             a: 类还未被加载, 程序先加载并连接该类

             b: 如该类还有直接父类, 则先初始化其直接父类

             c: 有初始化语句,按顺序执行

 

 

2.3 类的初始化时机

什么时候这个类的字节码文件被加载?【什么时候创建出该类的字节码对象 Class对象】

1.创建类的实例

2.类的静态成员使用

3.类的静态方法调用

4.使用反射方式来强制创建某个类或接口对应的java.lang.Class对象

5.初始化某个类的子类

6.直接使用java.exe命令来运行某个主类

 

 

(二)  类加载器

概述: 类加载器是负责加载类的对象,将class文件加载到内存中,并为之生成对应的java.lang.Class对象.

 

2.1 类加载器的分类

1. Bootstrap ClassLoader 引导类加载器,通常表示为null

        也被称为根类加载器,负责jre\lib目录下核心类的加载,比如System,String等.

2. Extension ClassLoader 扩展类加载器

        负责JRE的扩展目录中jar包的加载,在JDK中JRE的lib目录下ext目录.

3. Application ClassLoader 系统类加载器

        负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径.

4. 自定义类加载器

        开发人员可以通过继承java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求.

 

类加载器之间的关系是子父类, 但是不是继承关系

-Bootstrap ClassLoader

  -Extension ClassLoader

      -Application ClassLoader

 

复制代码
import sun.net.spi.nameservice.dns.DNSNameService;

public class ClassLoaderTest {
    public static void main(String[] args) {
        System.out.println(System.class.getClassLoader());// null
        System.out.println(String.class.getClassLoader());// null
        System.out.println(DNSNameService.class.getClassLoader());// sun.misc.Launcher$ExtClassLoader@677327b6
        System.out.println(ClassLoaderTest.class.getClassLoader());// sun.misc.Launcher$AppClassLoader@18b4aac2

        System.out.println("------------------------------");
        ClassLoader cl = ClassLoaderTest.class.getClassLoader();
        while(cl != null){
            System.out.println(cl);
            cl = cl.getParent();
        }
    }
}
复制代码

 

2.2 双亲委派机制

1. 双亲委派机制是指当一个类加载器收到一个类加载请求时,该类加载器首先会把请求委派给父类加载器.每个类加载器都是如此,只有在父类加载器在自己的搜索范围内找不到指定类时,子类加载器才会尝试自己去加载.

2. 双亲委派模型工作过程:

        1)当Application ClassLoader 收到一个类加载请求时,他首先不会自己去尝试加载这个类,而是将这个请求委派给父类加载器Extension ClassLoader去完成.   

        2)当Extension ClassLoader收到一个类加载请求时,他首先也不会自己去尝试加载这个类,而是将请求委派给父类加载器Bootstrap ClassLoader去完成. 

        3)如果Bootstrap ClassLoader加载失败,就会让Extension ClassLoader尝试加载.

        4)如果Extension ClassLoader也加载失败,就会使用Application ClassLoader加载.

        5)如果Application ClassLoader也加载失败,就会使用自定义加载器去尝试加载.

        6)如果均加载失败,就会抛出ClassNotFoundException异常.

 

3.例子:

  当一个Hello.class这样的文件要被加载时.不考虑我们自定义类加载器,首先会在AppClassLoader中检查是否加载过,如果有那就无需再加载了.如果没有,那么会拿到父加载器,然后调用父加载器的loadClass方法.父类中同理会先检查自己是否已经加载过,如果没有再往上.注意这个过程,直到到达Bootstrap classLoader之前,都是没有哪个加载器自己选择加载的.如果父加载器无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException.

 

 

2.3 ClassLoader类

1. ClassLoader 叫做类加载器.虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流” 也就是.class字节码文件,这个动作放到java虚拟机外部去实现,以便让应用程序自己决定去如何获取所需要的类,实现这个动作的代模块称之为“类加载器”.把【.class】文件加载到jvm虚拟机当中,转换为一个Class的对象【类的字节码对象 类的类对象】

 

 

2.ClassLoader的方法:        

        ClassLoader getParent()

                返回父类加载器进行委派

复制代码
import sun.net.spi.nameservice.dns.DNSNameService;
public class ClassLoaderTest {
    public static void main(String[] args) {
        System.out.println(System.class.getClassLoader());// null
        System.out.println(String.class.getClassLoader());// null
        System.out.println(DNSNameService.class.getClassLoader());// sun.misc.Launcher$ExtClassLoader@677327b6
        System.out.println(ClassLoaderTest.class.getClassLoader());// sun.misc.Launcher$AppClassLoader@18b4aac2

        System.out.println("------------------------------");
        ClassLoader cl = ClassLoaderTest.class.getClassLoader();
        while(cl != null){
            System.out.println(cl);
            cl = cl.getParent();
        }
    }
}
复制代码

 

(三) 反射应用

3.1 反射机制的概述

   反射是指在运行时去获取一个类的变量和方法信息.然后通过获取到的信息来创建对象,调用方法的一种机制.由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展.

 

  举例说明 :

  1. 例如项目工程中, 有两个类,Student, Teacher, 需要使用这两个类

  2. 因为要使用上述两类, 这个两个类对应的.class字节码文件就会被类加载器从磁盘路径上加载进入到内存中

  3. 一个类型一旦进入到内存,证明需要使用,证明代码需要运行, 这种状态就是动态的效果

  4. 类加载器为这个正在运行的.class字节码文件, 创建出一个对应的Class对象, 对象中包含了class文件中所有代码内容(可以包含类中所有成员变量, 构造方法,方法...)

  5. 使用Class对象, 获取出类型中所有的需要的成员, 这种使用方式称为反射

 

 

3.2 获取Class类对象三种方式

1.Class类: Class类型的实例表示正在运行的java应用程序的类或者接口.

2.Class类的对象: 想获取和操作类中的内容,首先要获取类的字节码对象(Class类对象),每一个正在运行的类,都有对应的字节码对象,获取了类的字节码对象,就可以使用这个对象的所有方法,这些方法都定义在Class类型中.

 

3.三种获取Class类对象的方式:

        1)类名.class属性

        2)对象名.getClass()方法

        3)Class.forName(全类名)方法

       全类名 : com.ujiuye.demos.Demo01 包名 + 类名

复制代码
public class Demo01_CLass对象获取三种方式 {
    public static void main(String[] args) throws ClassNotFoundException {
        String s = "abc";
        // 1. 直接使用类型名称.class, 获取这个类型的字节码文件对象
        Class<Person> c = Person.class;
        System.out.println(c);

        // 2. object父类中getClass(): 获取这个类型的字节码文件对象
        Person p = new Person();
        Class c1 = p.getClass();
        System.out.println(c1);

        // 3. Class类中静态方法功能内容: Class.forName(提供带有完整包名的类名),
        // 获取这个类的字节码文件对象
        Class c2 = Class.forName("com.ujiuye.reflect.Person");
        System.out.println(c2);

        // 注意: 一个类型的.class字节码文件加载进入到内存中之后, 类加载器会为这个字节码文件自动生成
        // 一个字节码文件对象, 对象中包含有文件的一切内容和功能, 这个对象只有一个, 存在于堆内存中
        // 上述的三个方法都是获取这个对象的手段
        System.out.println(c == c1);// true
        System.out.println(c1 == c2);// true
    }
}
复制代码

 

3.3反射获取构造方法并使用

1. Class类获取构造方法对象:

方法分类:

Constructor<?>[] getConstructors()

    返回所有public公共构造方法对象的数组

   

Constructor<?>[] getDeclaredConstructors()

    返回所有构造方法对象的数组

   

Constructor getConstructor(Class<?>... parameterTypes)

    返回单个公共构造方法对象

   

Constructor getDeclaredConstructor(Class<?>...parameterTypes)

    返回单个构造方法对象

 

2. 注意:

        getConstructor(Class<?>... parameterTypes)

        getDeclaredConstructor(Class<?>...parameterTypes)

两方法的参数列表为可变参数,可变参数即参数的个数可以是任意个,0个,1个或者多个均可,任意的数据类型都有对应Class对象, 连基本数据数据类型也不例外 : int.class

 

3. Constructor类型:

        1)Constructor类表达的含义就是一个类当中的构造方法,一个对象就表达了一个构造方法

        2)构造方法对象应该具有的功能: 获取构造方法各种信息(构造方法修饰符、构造方法名称、构造方法的参数列表、构造方法的注解),最基本的一个功能就是,创建对象.

 

4. Constructor类用于创建对象的方法:

        T newInstance(Object...initargs) 根据指定的构造方法创建对象,参数为所运行构造方法需要的实际参数.

 

复制代码
public class Person {
    int i = 10;
    public Person(){}

    public Person(String name){
        System.out.println(name);
    }

    Person(String name, int age){
        System.out.println(name + "--" + age);
    }

    private Person(int age){
        System.out.println(age);
    }
}
复制代码

 

复制代码
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Demo02_ReflectConstructor {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        // 1. 获取到Person类型的字节码文件对象
        Class c = Class.forName("com.ujiuye.reflect.Person");
        // 2. 获取到Person中的所有的公共修饰的构造方法
        Constructor[] conArr  = c.getConstructors();
        for(Constructor con : conArr){
            System.out.println(con);
        }

        // 3.Constructor<?>[] getDeclaredConstructors():返回所有构造方法对象的数组
        Constructor[] conArrAll = c.getDeclaredConstructors();
        for(Constructor con : conArrAll){
            System.out.println(con + "-----");
        }

        // 4.Constructor getConstructor(Class<?>... parameterTypes)回单个公共构造方法对象
        // 想要获取到公共修饰的, 空参数构造方法
        Constructor con1 = c.getConstructor();
        System.out.println(con1);

        // 5.Constructor getDeclaredConstructor(Class<?>...parameterTypes)返回单个构造方法对象
        Constructor con2 = c.getDeclaredConstructor(int.class);
        System.out.println(con2);

        // 6. Constructor构造器类: 表示一个构造方法,构造方法可以运行,运行效果就是创建出一个指定类型对象
        // Constructor类中有一个方法功能: newInstance(Object...initargs)
        Person p = (Person)con1.newInstance();
        System.out.println(p.i);

        // 7. 获取到默认修饰的构造方法
        Constructor con3 = c.getDeclaredConstructor(String.class, int.class);
        Person p3 = (Person)con3.newInstance("张三",19);
        System.out.println(p3.i);
    }
}
复制代码

 

3.4反射获取第一个接口和所有接口

Class<?> string01 = person.getClass().getInterfaces()[0];

//获得person对象所实现的第一个接口

Class<?> string01 = person.getClass().getInterfaces();

//获得person对象所实现的所有接口

 

posted @   晴天般的微  阅读(183)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示

目录导航