侧边栏壁纸
博主头像
快乐江湖的博客博主等级

更多内容请点击CSDN关注“快乐江湖”

  • 累计撰写 127 篇文章
  • 累计创建 33 个标签
  • 累计收到 2 条评论

目 录CONTENT

文章目录

第十六章:Java之反射和枚举

快乐江湖
2023-04-21 / 0 评论 / 0 点赞 / 17 阅读 / 24503 字

专栏目录首页:【专栏必读】Java基础教程和数据结构内容导航和学习说明

一:反射

(1)反射的定义

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

  • 在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统应用开放,这时候就可以利用Java的反射机制通过反射来获取所需的私有成员或是方法
  • 反射最重要的用途就是开发各种通用框架,比如在spring中,我们将所有的类Bean交给spring容器管理,无论是XML配置Bean还是注解配置,当我们从容器中获取Bean来依赖注入时,容器会读取配置,而配置中给的就是类的信息,spring根据这些信息,需要创建那些Bean,spring就动态的创建这些类

Java程序中许多对象在运行时会出现两种类型:运行时类型编译时类型,利用反射就能判断出该对象和类属于哪些类

  • 例如Person p = new Student()中,p在编译时类型为Person而运行时类型为Student

(2)反射相关类及方法

A:类

类名用途
Class代表类的实体,在运行的Java应用程序中表示类和接口
Field代表类的成员变量/类的属性
Method代表类的方法
Constructor代表类的构造方法

B:方法

①:Class类中的相关方法

类名用途
getClassLoader()获得类加载器
getDeclaredClasses()返回一个数组,数组中包含该类中所有类和接口类的对象(包括私有)
forName(String className)根据类名返回类的对象
newInstance()创建类的实例
getName()获得类的完整路径名字

②:Field类中的相关方法

类名用途
getField(String name)获得某个公有的属性对象
getFields()获得所有公有的属性对象
getDeclaredField(String name)获得某个属性对象
getDeclaredFields()获得所有属性对象

③:获得类中注解相关方法

类名用途
getAnnotation(Class annotationClass)返回该类中与参数类型匹配的公有注解对象
getAnnotations()返回该类所有的公有注解对象
getDeclareAnnotation(Class annotationClass)返回该类中与参数类型匹配的所有注解对象
getDeclaredAnnotations()返回该类所有的注解对象

④:获得类中构造器相关方法

类名用途
getConstructor(Class...<?> parameter Types)获得该类中与参数类型匹配的公有构造方法
getConstructors()获得该类的所有公有构造方法
getDeclaredConstructor(Class...<?> parameter Types)获得该类中与参数类型匹配的构造方法
getDeclaredConstructors()获得该类所有构造方法

⑤:获得类中方法的相关方法

类名用途
getMethod(String name, Class...<?> parameter Types)获得该类某个公有的方法
getMethods()获得该类所有公有的方法
getDeclaredMethod(String name, Class...<?> parameter Types)获得该类某个方法
getDeclaredMethods()获得该类所有方法

(3)反射使用

以如下Student类为例

class Student{
    private String name = "zhangsan";
    public int age = 19;
    public Student(){
        System.out.println("调用无参构造方法");
    }

    private Student(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("调用含参构造方法(私有)");
    }
    private void eat(){
        System.out.println("调用私有eat方法");
    }
    private void sleep(){
        System.out.println("调用私有sleep方法");
    }
    private void function(String str){
        System.out.println(str);
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

A:获取Class对象

获取Class对象:使用反射之前首先需要拿到当前待反射的类的Class对象,然后通过Class对象的核心方法,达到反射目的。主要有以下三种方法

  • 第一种:使用Class.forName("类的全路径名")
  • 第二种(推荐):使用.class方法
  • 第三种(较多使用):使用类对象的getClass()方法

如下

public class TestDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        //第一种:通过Class对象的forName静态方法获取,但可能抛出ClassNotFoundException异常,需要进行捕获
        try {
            Class<?> c1 = Class.forName("Student");
        }catch(ClassNotFoundException e){
            e.printStackTrace();
        }
        //第二种:通过类名.class方式获取
        Class<?> c2 = Student.class;

        //第三种:通过getClass获取
        Student student = new Student();
        Class<?> c3 = student.getClass();
    }
}

B:反射使用示例

反射使用示例如下,开始学习时可能会感觉语法繁琐,但是熟悉之后还是感觉比较容易的。从下面的例子中,大家可以体会到反射的作用非常强大

public class TestDemo {
    //创建类的实例
    public void reflectNewInstance() {
        try {
            Class<?> c = Class.forName("Student");
            Student student = (Student) c.newInstance();
            System.out.println(student);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException((e));
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    //反射私有构造方法
    public void reflectPrivateConstructor() {
        try {
            Class<?> c = Class.forName("Student");
            //拿到带有两个参数的私有构造方法
            Constructor<?> constructor = c.getDeclaredConstructor(String.class, int.class);
            //只有设置为true后下一步才可以生效
            constructor.setAccessible(true);
            //使用该构造方法构造对象
            Student student = (Student) constructor.newInstance("李四", 18);
            System.out.println(student);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    //反射私有属性
    public void reflectPrivateField() {
        try {
            Class<?> c = Class.forName("Student");
            Student student = (Student) c.newInstance();

            //获取私有属性name
            Field field = c.getDeclaredField("name");
            //只有设置为true后下一步才可以生效
            field.setAccessible(true);
            //修改该属性
            field.set(student, "王麻子");
            System.out.println(student);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        }
    }

    //反射私有方法
    public void reflectPrivateMethod() {
        try {
            Class<?> c = Class.forName("Student");
            Student student = (Student) c.newInstance();

            //获取私有方法function
            Method method = c.getDeclaredMethod("function", String.class);
            //只有设置为true后下一步才可以生效
            method.setAccessible(true);
            //传参给该私有方法
            method.invoke(student, "通过反射给该方法传参");
            System.out.println(student);

        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        TestDemo testDemo = new TestDemo();
        testDemo.reflectNewInstance(); // 利用反射创建对象
        System.out.println("----------------------------------");
        testDemo.reflectPrivateConstructor(); //利用反射调用私有构造方法并创建对象
        System.out.println("----------------------------------");
        testDemo.reflectPrivateField(); //利用反射获取私有属性并修改
        System.out.println("----------------------------------");
        testDemo.reflectPrivateMethod();  //利用反射调用私有方法并传参
        System.out.println("----------------------------------");
    }
}

(4)反射优缺点

优点

  • 对于任意一个类都能知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法
  • 提高灵活性、增强扩展能力、降低耦合性、提高自适应能力
  • 可以应用在很多流行框架中

缺点

  • 反射可能会导致效率降低
  • 反射会使维护变得困难,使代码复杂

二:枚举

(1)枚举基本概念

枚举:Java枚举是一个特殊的类,一般表示一组常量,使用关键字enum来定义,常量间使用逗号分隔

  • 注意:自定义的枚举类默认继承java.lang.Enum
public enum Color {
    RED, BLACK, GREEN;
}

(2)枚举使用

A:在switch语句中

public enum Color {
    RED, BLACK, GREEN, WHITE;
}
public class TestDemo {
    public static void main(String[] args) {
        Color color = Color.BLACK;
        switch(color){
            case RED:
                System.out.println("红色");
                break;
            case BLACK:
                System.out.println("黑色");
                break;
            case GREEN:
                System.out.println("绿色");
                break;
            case WHITE:
                System.out.println("白色");
                break;
            default:
                System.out.println("未知颜色");
                break;
        }
    }
}

B:常用方法

Enum类常用方法如下表

方法名称描述
values()以数组形式返回枚举类型的所有成员
ordinal()获取枚举成员的索引位置
valueOf()将普通字符串转化为枚举实例
compareTo()比较两个枚举成员在定义时的顺序

如下例

public enum Color {
    RED, BLACK, GREEN, WHITE;
}
public class TestDemo {
    public static void main(String[] args) {
        //以数组形式返回枚举类型的所有成员,然后遍历输出枚举成员和其序号
        Color[] enums = Color.values();
        for(int i = 0; i < enums.length; i++){
            System.out.println(enums[i] + "序号" + enums[i].ordinal());
        }
        //通过返回枚举实例
        System.out.println("======================================");
        Color green = Color.valueOf("GREEN");
        System.out.println(green);

        //比较枚举成员顺序大小(序号大小)
        System.out.println("======================================");
        Color red = Color.valueOf("RED");
        Color black = Color.valueOf("BLACK");
        System.out.println(red.compareTo(black) < 0 ? "RED<BLACK" : "RED>BLACK");
    }
}

C:关于构造方法

  • 需要注意,枚举构造方法默认为private
public enum Color {
    RED("red", 7),
    BLACK("black",3),
    GREEN("green", 11),
    WHITE("white", 0);

    public final String color;
    public final int ordinal;

    Color(String color, int ordinal) {
        this.color = color;
        this.ordinal = ordinal;
    }
}
0

评论区