类加载、反射和枚举

一、类加载

1.1 类加载

当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类进行初始化。如果不出现意外情况,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载或者类初始化

类的加载

  • 就是指将class文件读入内存,并为之创建一个java.lang.Class对象
  • 任何类被使用时,系统都会为之建立一个iava.lang.Class对象
  • 分类

    • Bootstrap class loader:虚拟机的内置类加载器,通常表示为null ,并且没有父null
    • Platform class loader:平台类加载器,负责加载JDK中一些特殊的模块
    • System class loader:系统类加载器,负责加载用户类路径上所指定的类库
  • 类加载器的继承关系

    • System的父加载器为Platform
    • Platform的父加载器为Bootstrap
  • 代码演示

public class ClassLoaderDemo1 {
    public static void main(String[] args) {
        //获取系统类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();

        //获取系统类加载器的父加载器 --- 平台类加载器
        ClassLoader classLoader1 = systemClassLoader.getParent();

        //获取平台类加载器的父加载器 --- 启动类加载器
        ClassLoader classLoader2 = classLoader1.getParent();

        System.out.println("系统类加载器" + systemClassLoader);
        System.out.println("平台类加载器" + classLoader1);
        System.out.println("启动类加载器" + classLoader2);

    }
}

类的连接

  • 验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致
  • 准备阶段:负责为类的类变量分配内存,并设置默认初始化值
  • 解析阶段:将类的二进制数据中的符号引用替换为直接引用

类的初始化

  • 在该阶段,主要就是对类变量进行初始化

类的初始化步骤

  • 假如类还未被加载和连接,则程序先加载并连接该类
  • 假如该类的直接父类还未被初始化,则先初始化其直接父类
  • 假如类中有初始化语句,则系统依次执行这些初始化语句

注意:在执行第2个步骤的时候,系统对直接父类的初始化步骤也遵循初始化步骤1-3

类加载的过程

  • 类加载时机:

    • 创建类的实例
    • 调用类的类方法
    • 访问类或者接口的类变量,或者为该类变量赋值
    • 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
    • 初始化某个类的子类
    • 直接使用iava.exe命令来运行某个主类
  • 类加载过程

    1. 加载
    • 通过包名 + 类名,获取这个类,准备用流进行传输

    • 在这个类加载到内存中

    • 加载完毕创建一个class对象
      类加载、反射和枚举_第1张图片
      2.链接

      • 验证

        确保Class文件字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身安全
        (文件中的信息是否符合虚拟机规范有没有安全隐患)
        类加载、反射和枚举_第2张图片

      • 准备

        负责为类的类变量(被static修饰的变量)分配内存,并设置默认初始化值
        (初始化静态变量)
        类加载、反射和枚举_第3张图片

      • 解析

        将类的二进制数据流中的符号引用替换为直接引用
        (本类中如果用到了其他类,此时就需要找到对应的类)
        类加载、反射和枚举_第4张图片

    3.初始化

    根据程序员通过程序制定的主观计划去初始化类变量和其他资源
    (静态变量赋值以及初始化其他资源)
    类加载、反射和枚举_第5张图片

  • 小结

    • 当一个类被使用的时候,才会加载到内存
    • 类加载的过程: 加载、验证、准备、解析、初始化

1.2 类加载器

类加载器的作用

  • 负责将.class文件加载到内存中,并为之生成对应的java.lang.Class对象
  • 虽然我们不用过分关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行
    类加载、反射和枚举_第6张图片

JVM的类加载机制

  • 全盘负责:就是当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入

  • 父类委托:就是当一个类加载器负责加载某个Class时,先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类

  • 缓存机制:保证所有加载过的Class都会被缓存,当程序需要使用某个Class对象时,类加载器先从缓存区中搜索该CIass,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存储到缓存区

ClassLoader:是负责加载类的对象

Java运行时具有以下内置类加载器

  • Bootstrap classloader: 它是虚拟机的内置类加载器,通常表示为null,并且没有父null
  • Platform classloader: 平台类加载器可以看到所有平台类,平台类包括由平台类加载器或其祖先定义的Java SE平台API.其实现类和JDK特定的运行时类
  • System class loader:它也被称为应用程序类加载器,与平台类加载器不同。系统类加载器通常用于定义应用程序类路径模块路径和JDK特定工具上的类
  • 类加载器的继承关系: System的父加载器为Platform,而Platform的父加载器为Bootstrap

ClassLoader中的两个方法

  • static ClassLoader getSystemClassLoader0: 返回用于委派的系统类加载器

  • ClassLoader getParent0: 返回父类加载器进行委派

  • 代码演示

package com.svt.classloader;

public class ClassLoaderDemo01 {
    /*
            ClassLoader 中的两个方法
            static ClassLoader getSystemClassLoader (): 返回用于委派的系统类加载器
            CLassLoader getParent ():返回父类加载器进行委派
         */
    public static void main(String[] args) {
        //static ClassLoader getSystemClassLoader (): 返回用于委派的系统类加载器
        System.out.println("-------------返回用于委派的系统类加载器------------");
        ClassLoader c = ClassLoader.getSystemClassLoader();
        System.out.println(c);//AppClassLoader

        //CLassLoader getParent ():返回父类加载器进行委派
        System.out.println("-------------返回父类加载器进行委派------------");
        ClassLoader c2 = c.getParent();
        System.out.println(c2);//ExtClassLoader 平台类加载器

        //Bootstrap classloader: 它是虚拟机的内置类加载器,通常表示为null,并且没有父null
        ClassLoader c3 = c2.getParent();
        System.out.println(c3);//null
    }
}

1.3 双亲委派机制

  • 如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式
    类加载、反射和枚举_第7张图片

类加载、反射和枚举_第8张图片

类加载、反射和枚举_第9张图片

1.4 ClassLoader 中的两个方法

  • 方法介绍

    方法名 说明
    public static ClassLoader getSystemClassLoader() 获取系统类加载器
    public InputStream getResourceAsStream(String name) 加载某一个资源文件
  • 示例代码

package com.svt.classloader;

import sun.net.spi.nameservice.dns.DNSNameService;

public class ClassLoaderDemo02 {
    public static void main(String[] args) {
        ClassLoader classLoader = Object.class.getClassLoader();
        System.out.println("Object类的类加载器是"+classLoader);//null
        System.out.println("--------------------------------");
        ClassLoader classLoader1 = DNSNameService.class.getClassLoader();
        System.out.println("DNSNameService类的类加载器是"+classLoader1);//ExtClassLoader
        System.out.println("--------------------------------");
        ClassLoader classLoader2 = ClassLoaderDemo02.class.getClassLoader();
        System.out.println("ClassLoaderDemo02类的类加载器是"+classLoader2);//AppClassLoader
        System.out.println("--------------------------------");
        /**
         * 在程序开发中,类的加载几乎是由上述3种类加载器相互配合执行的,同时我们还可以自定义类加载器。
         * 需要注意的是,lava虚拟机对class文件采用的是按需加载的方式,也就是说当需要使用该类时才会将它的class文件加载到内存生成class对象,
         * 而且加载某个类的class文件时,lava虚拟机采用的是双亲委派模式,即把加载类的请求交由父加载器处理,它一种任务委派模式。
         */

        while (classLoader2!=null){
            //不是父类加载器的时候打印
            System.out.println(classLoader2);
            classLoader2 = classLoader2.getParent();//获取父类加载器
            //第二次被赋予了extclassloader
        }

        /**
         * 使用双亲委派机制的好处:
         * 1、可以避免类的重复加载,当父类加载器已经加载了该类时,就没有必要子ClassLoader再加载一次
         * 2、考虑到安全因素,java核心api中定义类型不会被随意替换,假设通过网络传递一个名为java.lang.Object的类,
         * 通过双亲委托模式传递到启动类加载器,而启动类加载器在核心ava API发现这个名字的类,发现该类已被加载,
         * 并不会重新加载网络传递的过来的iava.lang.Obiect,而直接返回已加载过的Obiet.class,这样便可以防止核心API库被随意算改。
         */

    }
}

二、反射

2.1 反射的概述

  • 反射机制

    是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
    对于任意一个对象,都能够调用它的任意属性和方法;
    这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。

2.2 获取Class类对象的三种方式

三种方式分类

  • 类名.class属性
  • 对象名.getClass()方法
  • Class.forName(全类名)方法
    类加载、反射和枚举_第10张图片
  • 代码示例
    学生类
package com.svt.myreflect.myreflect1;

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}

测试类

package com.svt.myreflect.myreflect1;

public class MyReflectDemo01 {
    public static void main(String[] args) throws ClassNotFoundException {
        /**
         * 利用反射获取class对象
         *
         * 获取class对象的三种方式:
         * 1. Class.forName("全类名");/源代码阶段
         * 2.类名.class/加载阶段
         * 3.对象.getclass();/运行阶段
         *
         * 开始前要先确定哪个类 先将类写好
         *
         */

        //1. Class.forName("全类名");/源代码阶段
        //全类名:包名+类名 进入类->选中右击类名->Copy Reference
        //粘贴地点:要粘贴在注释内 或者字符串内 其余地方不会显示全类名
        System.out.println("-------------Class.forName(\"全类名\")------------");
        Class clazz1 = Class.forName("com.svt.myreflect.myreflect1.Student");
        //打印
        System.out.println(clazz1);

        //2.类名.class/加载阶段
        //一般更多的当作参数进行传递
        System.out.println("-------------类名.class------------");
        Class clazz2 = Student.class;
        System.out.println(clazz2);

        //3.对象.getclass();/运行阶段
        //局限性 当我们已经有了这个类的对象时 才能进行使用
        System.out.println("-------------对象.getclass()------------");
        Student student = new Student();
        Class clazz3 = student.getClass();
        System.out.println(clazz3);

        System.out.println("-------------比较地址值------------");
        System.out.println(clazz1==clazz2);
        System.out.println(clazz2==clazz3);
        //获取的字节码文件是一样的 所以可以用来做锁文件
    }
}

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

Class类获取构造方法对象的方法

  • 方法介绍

    方法名 说明
    Constructor[] getConstructors() 返回所有公共构造方法对象的数组
    Constructor[] getDeclaredConstructors() 返回所有构造方法对象的数组
    Constructor getConstructor(Class… parameterTypes) 返回单个公共构造方法对象
    Constructor getDeclaredConstructor(Class… parameterTypes) 返回单个构造方法对象

Constructor类用于创建对象的方法

  • 方法介绍

    方法名 说明
    T newInstance(Object…initargs) 根据指定的构造方法创建对象
    setAccessible(boolean flag) 设置为true,表示取消访问检查
  • 示例代码
    学生类
package com.svt.myreflect.myreflect2;

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name) {
        this.name = name;
    }

    protected Student(int age){
        this.age=age;
    }

    private Student(String name,int age) {
        this.name = name;
        this.age=age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}

测试类

package com.svt.myreflect.myreflect2;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Parameter;

public class MyReflectDemo02 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        /**
         * 利用反射获取构造方法
         *
         * class类中用于获取构造方法的方法
         * Constructor[] getConstructors(): 返回所有公共构造方法对象的数组
         * Constructor[] getDeclaredConstructors(): 返回所有构造方法对象的数组
         * Constructor getConstructor(Class... parameterTypes): 返回单个公共构造方法对象
         * Constructor getDeclaredConstructor(Class... parameterTypes): 返回单个构造方法对象
         *
         * Constructor类中用于创建对象的方法
         * T newInstance(Object...initargs): 根据指定的构造方法创建对象
         * setAccessible(boolean flag): 设置为true,表示取消访问检查
         */

        //1.获取class字节码文件对象
        Class clazz = Class.forName("com.svt.myreflect.myreflect2.Student");

        //2.获取构造方法
        //Constructor[] getConstructors(): 返回所有公共构造方法对象的数组
        System.out.println("-------------返回所有公共构造方法对象的数组------------");
        Constructor[] cons = clazz.getConstructors();
        for (Constructor con : cons) {
            System.out.println(con);
        }

        //Constructor[] getDeclaredConstructors(): 返回所有构造方法对象的数组
        System.out.println("-------------返回所有构造方法对象的数组------------");
        Constructor[] cons2 = clazz.getDeclaredConstructors();
        for (Constructor con : cons2) {
            System.out.println(con);
        }

        System.out.println("-------------返回public修饰的空参构造------------");
        Constructor con3 = clazz.getDeclaredConstructor();
        System.out.println(con3);


        //Constructor getDeclaredConstructor(Class... parameterTypes): 返回单个构造方法对象
        System.out.println("-------------返回单个构造方法对象------------");
        //括号内参数要和构造方法内参数保持一致 传递参数的字节码文件
        Constructor con4 = clazz.getDeclaredConstructor(String.class);
        System.out.println(con4);
        Constructor con5 = clazz.getDeclaredConstructor(int.class);
        System.out.println(con5);
        System.out.println("-------------返回多个构造方法对象------------");
        Constructor con6 = clazz.getDeclaredConstructor(String.class,int.class);
        System.out.println(con6);
        //获取权限修饰符
        System.out.println("-------------获取权限修饰符------------");
        int modifiers = con6.getModifiers();
        System.out.println(modifiers);
        //获取所有参数
        System.out.println("-------------获取所有参数------------");
        Parameter[] parameters = con6.getParameters();
        for (Parameter parameter : parameters) {
            System.out.println(parameter);
        }

        //这种方式只能获取公共的
        //Constructor getConstructor(Class... parameterTypes): 返回单个公共构造方法对象
        System.out.println("-------------返回单个公共构造方法对象------------");
        Constructor con7 = clazz.getConstructor(String.class);//根据传递的参数改变传出结果
        System.out.println(con7);

        //getDeclaredConstructor只是让你看到这个构造 还无法创建对象
        //这时需要调用一个方法
        //setAccessible(boolean flag): 设置为true,表示取消访问检查
        con6.setAccessible(true);//暴力反射:临时取消权限校验
        //此时就能创建对象不论公共私有
        //T newInstance(Object...initargs): 根据指定的构造方法创建对象
        System.out.println("-------------根据指定的构造方法创建对象------------");
        Student stu = (Student) con6.newInstance("圆", 27);
        System.out.println(stu);


    }
}

小结

  • 获取class对象
    三种方式: Class.forName(“全类名”), 类名.class, 对象名.getClass()
  • 获取里面的构造方法对象
    getConstructor (Class... parameterTypes) getDeclaredConstructor (Class… parameterTypes)
  • 如果是public的,直接创建对象
    newInstance(Object… initargs)
  • 如果是非public的,需要临时取消检查,然后再创建对象
    setAccessible(boolean) 暴力反射

2.4 反射获取成员变量并使用

Class类获取成员变量对象的方法

  • 方法分类

    方法名 说明
    Field[] getFields() 返回所有公共成员变量对象的数组
    Field[] getDeclaredFields() 返回所有成员变量对象的数组
    Field getField(String name) 返回单个公共成员变量对象
    Field getDeclaredField(String name) 返回单个成员变量对象

Field类用于给成员变量赋值的方法

  • 方法介绍

    方法名 说明
    void set(Object obj, Object value) 赋值
    Object get(Object obj) 获取值
  • 示例代码
    学生类
package com.svt.myreflect.myreflect3;

public class Student {
    private String name;
    private int age;
    public String gender;

    public Student() {
    }

    public Student(String name, int age, String gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    /**
     * 获取
     * @return gender
     */
    public String getGender() {
        return gender;
    }

    /**
     * 设置
     * @param gender
     */
    public void setGender(String gender) {
        this.gender = gender;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + ", gender = " + gender + "}";
    }
}

测试类

package com.svt.myreflect.myreflect3;

import java.lang.reflect.Field;

public class MyReflectDemo03 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        /**
         * 利用反射获取成员变量
         * Class类中用于获取成员变量的方法
         * Fieldll getFields(): 返回所有公共成员变量对象的数组
         * Field[] getDeclaredFields(): 返回所有成员变量对象的数组
         * Field getField(String name): 返回单个公共成员变量对象
         * Field getDeclaredField(String name): 返回单个成员变量对象
         *
         * Field类中用于创建对象的方法
         * void set(Object obj, Object value): 赋值
         * Object get(Object obj) 获取值。
         */

        //1.获取class字节码文件的对象
        Class clazz = Class.forName("com.svt.myreflect.myreflect3.Student");
        //2.获取成员变量
        //Fieldll getFields(): 返回所有公共成员变量对象的数组
        System.out.println("-------------返回所有公共成员变量对象的数组------------");
        Field[] fields1 = clazz.getFields();
        for (Field field : fields1) {
            System.out.println(field);
        }
        //Field[] getDeclaredFields(): 返回所有成员变量对象的数组
        System.out.println("-------------返回所有成员变量对象的数组------------");
        Field[] fields2 = clazz.getDeclaredFields();
        for (Field field : fields2) {
            System.out.println(field);
        }

        //Field getField(String name): 返回单个公共成员变量对象
        System.out.println("-------------返回单个公共成员变量对象------------");
        Field gender1 = clazz.getField("gender");
        System.out.println(gender1);

        //Field getDeclaredField(String name): 返回单个成员变量对象
        System.out.println("-------------返回单个成员变量对象------------");
        Field gender2 = clazz.getDeclaredField("name");
        System.out.println(gender2);

        //获取权限修饰符
        System.out.println("-------------获取权限修饰符------------");
        int modifiers = gender2.getModifiers();
        System.out.println(modifiers);

        //获取成员变量名
        System.out.println("-------------获取成员变量名------------");
        String name = gender2.getName();
        System.out.println(name);

        //获取数据类型
        System.out.println("-------------获取数据类型------------");
        Class<?> type = gender2.getType();
        System.out.println(type);

        //获取成员变量记录的值
        System.out.println("-------------获取成员变量记录的值------------");
        Student student = new Student("圆", 27, "男");
        gender2.setAccessible(true);
        Object value = gender2.get(student);
        System.out.println(value);

        //修改对象里面记录的值
        System.out.println("-------------修改对象里面记录的值------------");
        gender2.set(student,"wonwoo");
        System.out.println(student);
    }
}

2.5 反射获取成员方法并使用

Class类获取成员方法对象的方法

  • 方法分类

    方法名 说明
    Method[] getMethods() 返回所有公共成员方法对象的数组,包括继承的
    Method[] getDeclaredMethods() 返回所有成员方法对象的数组,不包括继承的
    Method getMethod(String name, Class… parameterTypes) 返回单个公共成员方法对象
    Method getDeclaredMethod(String name, Class… parameterTypes) 返回单个成员方法对象

Method类用于执行方法的方法

  • 方法介绍

    方法名 说明
    Object invoke(Object obj, Object… args) 运行方法

    参数一: 用obj对象调用该方法

    参数二: 调用方法的传递的参数(如果没有就不写)

    返回值: 方法的返回值(如果没有就不写)

  • 示例代码
    学生类

package com.svt.myreflect.myreflect4;

import java.io.IOException;

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public void sleep(){
        System.out.println("睡觉");
    }

    private String eat(String something) throws IOException,NullPointerException,ClassCastException {
        System.out.println("在吃"+something);
        return "刘师傅给我做";
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}

测试类

package com.svt.myreflect.myreflect4;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public class MyReflectDemo04 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        /**
         * 利用反射获取成员方法
         *
         * Class类中用于获取成员方法的方法
         * Methodll getMethods(): 返回所有公共成员方法对象的数组,包括继承的
         * Methodl] getDeclaredMethods(): 返回所有成员方法对象的数组,不包括继承的
         * Method getMethod(String name,Class... parameterTypes): 返回单个公共成员方法对象
         * Method getDeclaredMethod(String name,Class... parameterTypes): 返回单个成员方法对象
         *
         * Method类中用于创建对象的方法
         * Object invoke(Object obj, Object... args): 运行方法
         * 参数一:用obj对象调用该方法
         * 参数二:调用方法的传递的参数(如果没有就不写)
         * 返回值:方法的返回值(如果没有就不写)
         *
         * 获取方法的修饰符
         * 获取方法的名字
         * 获取方法的形参
         * 获取方法的返回值
         * 获取方法的抛出的异常
         */
        //1.获取class字节码文件的对象
        Class clazz = Class.forName("com.svt.myreflect.myreflect4.Student");

        //2.获取里面所有的方法对象

        //Methodll getMethods(): 返回所有公共成员方法对象的数组,包括继承的(包含父类中所有的公共方法)
        System.out.println("-------------返回所有公共成员方法对象的数组,包括继承的------------");
        Method[] methods1 = clazz.getMethods();
        for (Method method : methods1) {
            System.out.println(method);
        }

        //Methodl] getDeclaredMethods(): 返回所有成员方法对象的数组,不包括继承的(不能获取父类的,但能获取本类中私有的方法)
        System.out.println("-------------返回所有成员方法对象的数组,不包括继承的------------");
        Method[] methods2 = clazz.getDeclaredMethods();
        for (Method method : methods2) {
            System.out.println(method);
        }

        //Method getMethod(String name,Class... parameterTypes): 返回单个公共成员方法对象
        System.out.println("-------------返回单个公共成员方法对象------------");

        //Method getDeclaredMethod(String name,Class... parameterTypes): 返回单个成员方法对象
        System.out.println("-------------返回单个成员方法对象------------");
        Method eat = clazz.getDeclaredMethod("eat", String.class);
        System.out.println(eat);

        //获取权限修饰符
        System.out.println("-------------获取权限修饰符------------");
        int modifiers = eat.getModifiers();
        System.out.println(modifiers);

        //获取方法的名字
        System.out.println("-------------获取成员变量名------------");
        String name = eat.getName();
        System.out.println(name);

        //获取方法形参
        System.out.println("-------------获取方法形参------------");
        Parameter[] parameters = eat.getParameters();
        for (Parameter parameter : parameters) {
            System.out.println(parameter);
        }
        //获取方法的抛出的异常
        System.out.println("-------------获取方法的抛出的异常------------");
        Class[] exceptionTypes = eat.getExceptionTypes();
        for (Class exceptionType : exceptionTypes) {
            System.out.println(exceptionType);
        }

        //Object invoke(Object obj, Object... args): 运行方法
        //参数一:用obj对象调用该方法
        Student s = new Student();
        eat.setAccessible(true);
        //参数二:调用方法的传递的参数(如果没有就不写)
        //s:方法的调用者
        //"满汉全席":调用方法的时候传递的实际参数
        Object re = eat.invoke(s, "满汉全席");

        //返回值:方法的返回值(如果没有就不写)
        //获取方法的返回值
        System.out.println("-------------获取方法的返回值------------");
        System.out.println(re);
    }
}

2.7 反射应用案例一

  • 需求:对于任意一个对象,都可以把对象所有的字段名和值,保存在文件中去
  • 代码实现
    学生类
package com.svt.myreflect.Test1;

public class Student {
    private String name;
    private int age;
    private char gender;
    private double height;
    private String hobby;

    public Student() {
    }

    public Student(String name, int age, char gender, double height, String hobby) {
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.height = height;
        this.hobby = hobby;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    /**
     * 获取
     * @return gender
     */
    public char getGender() {
        return gender;
    }

    /**
     * 设置
     * @param gender
     */
    public void setGender(char gender) {
        this.gender = gender;
    }

    /**
     * 获取
     * @return height
     */
    public double getHeight() {
        return height;
    }

    /**
     * 设置
     * @param height
     */
    public void setHeight(double height) {
        this.height = height;
    }

    /**
     * 获取
     * @return hobby
     */
    public String getHobby() {
        return hobby;
    }

    /**
     * 设置
     * @param hobby
     */
    public void setHobby(String hobby) {
        this.hobby = hobby;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + ", gender = " + gender + ", height = " + height + ", hobby = " + hobby + "}";
    }
}

老师类

package com.svt.myreflect.Test1;

public class Teacher {
    private String name;
    private double salary;

    public Teacher() {
    }

    public Teacher(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return salary
     */
    public double getSalary() {
        return salary;
    }

    /**
     * 设置
     * @param salary
     */
    public void setSalary(double salary) {
        this.salary = salary;
    }

    public String toString() {
        return "Teacher{name = " + name + ", salary = " + salary + "}";
    }
}

测试类

package com.svt.myreflect.Test1;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;

public class MyReflectTest01 {
    public static void main(String[] args) throws IllegalAccessException, IOException {
     /*
        对于任意一个对象,都可以把对象所有的字段名和值,保存在文件中去
     */
        Student s = new Student("小吴", 20, '女', 163, "睡觉");
        //Teacher t = new Teacher("小圆", 10000);

        saveObject(s);
        //saveObject(t);
    }

    //把对象里面所有的成员变量名和值保存到本地文件中
    private static void saveObject(Object obj) throws IllegalAccessException, IOException {
        //1.获取字节码文件的对象
        Class clazz = obj.getClass();

        //创建IO流
        //路径->右击->CopyPath
        //BufferedWriter bw = new BufferedWriter(new FileWriter("F:\\DELL\\项目\\dellTestProject\\myreflect\\src\\com\\svt\\myreflect\\Test1\\a.txt"));


        //2.获取所有的成员变量
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            //获取成员变量的名字
            String name = field.getName();
            //获取成员变量的值
            Object value = field.get(obj);
           /* //写出数据
            bw.write(name+"="+value);
            //换行
            bw.newLine();*/
            System.out.println(name+"="+value);

        }
       // bw.close();
    }
}

2.8 反射应用案例二

  • 需求:
    利用反射和重载完成以下功能
    1)创建Student类,类中有属性name和age并封装属性
    2)重载Student的构造函数,一个是无参构造并,另一个是带两个参数的有参构造,要求在构造函数打印提示信息
    3)创建带main函数的NewInstanceTest类,利用Class类得到Student对象
    4)通过上述获取的Class对象分别调用Student有参函数和无参函数
  • 代码实现
    学生类
package com.svt.work.workdemo3;

//1.创建Student类,类中有属性name和age并封装属性
public class Student {
    private String name;
    private int age;


    //重载Student的构造函数,一个是无参构造并,
    public Student() {
        System.out.println("我是无参构造");
    }
    // 另一个是带两个参数的有参构造,要求在构造函数打印提示信息
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("我是有参构造");
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }

}

测试类

package com.svt.work.workdemo3;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class NewInstanceTest {
    /**
     * 利用反射和重载完成以下功能
     *
     * 1)创建Student类,类中有属性name和age并封装属性
     * 2)重载Student的构造函数,一个是无参构造并,另一个是带两个参数的有参构造,要求在构造函数打印提示信息
     * 3)创建带main函数的NewInstanceTest类,利用Class类得到Student对象
     * 4)通过上述获取的Class对象分别调用Student有参函数和无参函数
     */
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class clazz1 = Class.forName("com.svt.work.workdemo3.Student");

        //调用Student有参函数
        Constructor con = clazz1.getDeclaredConstructor(String.class,int.class);
        //利用Class类得到Student对象
        Object object = con.newInstance("小圆", 20);
        System.out.println(object);

        //调用Student无参函数
        Constructor con1 = clazz1.getDeclaredConstructor();
        //利用Class类得到Student对象
        Object object1 = con1.newInstance();
        System.out.println(object1);

    }
}

2.9 总结

类加载、反射和枚举_第11张图片

三、枚举

3.1 概述

为了间接的表示一些固定的值,Java就给我们提供了枚举
是指将变量的值一一列出来,变量的值只限于列举出来的值的范围内

3.2 定义格式

  • 格式

    public enum s {   
    	枚举项1,枚举项2,枚举项3;
    }
    注意: 定义枚举类要用关键字enum
    
  • 示例代码

    // 定义一个枚举类,用来表示春,夏,秋,冬这四个固定值
    public enum Season {
        SPRING,SUMMER,AUTUMN,WINTER;
    }
    

3.3 枚举的特点

  • 特点

    • 所有枚举类都是Enum的子类

    • 我们可以通过"枚举类名.枚举项名称"去访问指定的枚举项

    • 每一个枚举项其实就是该枚举的一个对象

    • 枚举也是一个类,也可以去定义成员变量

    • 枚举类的第一行上必须是枚举项,最后一个枚举项后的分号是可以省略的,但是如果枚举类有其他的东西,这个分号就不能省略。建议不要省略

    • 枚举类可以有构造器,但必须是private的,它默认的也是private的。

      枚举项的用法比较特殊:枚举(“”);

    • 枚举类也可以有抽象方法,但是枚举项必须重写该方法

  • 示例代码

    public enum Season {
    
        SPRING("春"){
    
            //如果枚举类中有抽象方法
            //那么在枚举项中必须要全部重写
            @Override
            public void show() {
                System.out.println(this.name);
            }
    
        },
    
        SUMMER("夏"){
            @Override
            public void show() {
                System.out.println(this.name);
            }
        },
    
        AUTUMN("秋"){
            @Override
            public void show() {
                System.out.println(this.name);
            }
        },
    
        WINTER("冬"){
            @Override
            public void show() {
                System.out.println(this.name);
            }
        };
    
        public String name;
    
        //空参构造
        //private Season(){}
      
        //有参构造
        private Season(String name){
            this.name = name;
        }
      
        //抽象方法
        public abstract void show();
    }
    
    public class EnumDemo {
        public static void main(String[] args) {
            /*
            1.所有枚举类都是Enum的子类
            2.我们可以通过"枚举类名.枚举项名称"去访问指定的枚举项
            3.每一个枚举项其实就是该枚举的一个对象
            4.枚举也是一个类,也可以去定义成员变量
            5.枚举类的第一行上必须是枚举项,最后一个枚举项后的分号是可以省略的,
              但是如果枚举类有其他的东西,这个分号就不能省略。建议不要省略
            6.枚举类可以有构造器,但必须是private的,它默认的也是private的。
              枚举项的用法比较特殊:枚举("");
            7.枚举类也可以有抽象方法,但是枚举项必须重写该方法
        */
      
            //第二个特点的演示
            //我们可以通过"枚举类名.枚举项名称"去访问指定的枚举项
            System.out.println(Season.SPRING);
            System.out.println(Season.SUMMER);
            System.out.println(Season.AUTUMN);
            System.out.println(Season.WINTER);
      
            //第三个特点的演示
            //每一个枚举项其实就是该枚举的一个对象
            Season spring = Season.SPRING;
        }
    }
    

3.4 枚举的方法

  • 方法介绍

    方法名 说明
    String name() 获取枚举项的名称
    int ordinal() 返回枚举项在枚举类中的索引值
    int compareTo(E o) 比较两个枚举项,返回的是索引值的差值
    String toString() 返回枚举常量的名称
    static T valueOf(Class type,String name) 获取指定枚举类中的指定名称的枚举值
    values() 获得所有的枚举项
  • 示例代码

public enum Season {
    SPRING,SUMMER,AUTUMN,WINTER;
}

public class EnumDemo {
    public static void main(String[] args) {
//        String name() 获取枚举项的名称
        String name = Season.SPRING.name();
        System.out.println(name);
        System.out.println("-----------------------------");

//        int ordinal() 返回枚举项在枚举类中的索引值
        int index1 = Season.SPRING.ordinal();
        int index2 = Season.SUMMER.ordinal();
        int index3 = Season.AUTUMN.ordinal();
        int index4 = Season.WINTER.ordinal();
        System.out.println(index1);
        System.out.println(index2);
        System.out.println(index3);
        System.out.println(index4);
        System.out.println("-----------------------------");

//        int compareTo(E o) 比较两个枚举项,返回的是索引值的差值
        int result = Season.SPRING.compareTo(Season.WINTER);
        System.out.println(result);//-3
        System.out.println("-----------------------------");

//        String toString()   返回枚举常量的名称
        String s = Season.SPRING.toString();
        System.out.println(s);
        System.out.println("-----------------------------");

//        static  T valueOf(Class type,String name)
//        获取指定枚举类中的指定名称的枚举值
        Season spring = Enum.valueOf(Season.class, "SPRING");
        System.out.println(spring);
        System.out.println(Season.SPRING == spring);
        System.out.println("-----------------------------");

//        values()       获得所有的枚举项
        Season[] values = Season.values();
        for (Season value : values) {
            System.out.println(value);
        }
    }
}

3.5 枚举应用案例

  • 代码示例
    BookType枚举类
package com.svt.enumdemo;

public enum BookType {
    MATH("数学"),PROGRAM("程序"),GAME("游戏");
    private String value;
    BookType(String value){
        this.value=value;
    }

    @Override
    public String toString() {
        return this.value;
    }
}

Book类

package com.svt.enumdemo;

public class Book {
    private String title;
    private String author;
    private double price;
    private BookType type;
    public Book() {
    }

    public Book(String title, String author, double price,BookType type) {
        this.title = title;
        this.author = author;
        this.price = price;
        this.type=type;
    }

    /**
     * 获取
     * @return title
     */
    public String getTitle() {
        return title;
    }

    /**
     * 设置
     * @param title
     */
    public void setTitle(String title) {
        this.title = title;
    }

    /**
     * 获取
     * @return author
     */
    public String getAuthor() {
        return author;
    }

    /**
     * 设置
     * @param author
     */
    public void setAuthor(String author) {
        this.author = author;
    }

    /**
     * 获取
     * @return price
     */
    public double getPrice() {
        return price;
    }

    /**
     * 设置
     * @param price
     */
    public void setPrice(double price) {
        this.price = price;
    }

    public String toString() {
        return "Book{title = " + title + ", author = " + author + ", price = " + price + ", type = " + type + "}";
    }
}

BookDemo类

package com.svt.enumdemo;

public class BookDemo {
    public static void main(String[] args) {
        System.out.println(new Book("Java从入门到放弃","小吴",88,BookType.PROGRAM));
    }
}

  • 程序运行结果
    类加载、反射和枚举_第12张图片

你可能感兴趣的:(Java基础阶段一,java,开发语言)