:Java之反射和枚举

Posted 快乐江湖

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了: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;
    

以上是关于:Java之反射和枚举的主要内容,如果未能解决你的问题,请参考以下文章

java反射机制可以调用到私有方法,是否就破坏了JAVA的卦装性呢。

总结Java中反射+枚举+Lambda表达式

枚举实现单例避免被反射破坏的原因

Java数据结构之枚举

java语言基础--枚举,注解,正则和反射

Java 之枚举类