Java中的反射和枚举

1.反射

1)反射的定义:

1.1)Java中的反射机制是指在运行状态中,对于任意的一个类,都可以知道这个类的所有属性和方法;

对于任意一个对象,都可以调用它的任意的方法和属性,那么既然可以拿到,我们就可以修改部分类型信息,这种动态获取信息以及动态调用对象的方法的功能称之为Java语言的反射机制;

1.2)所有有关于反射相关的类和相关的包都在java.lang.reflect包下面

2)反射中涉及到的一些常见的类-----重要

反射中在哪里用到,介绍一下(面试--框架)

三)主要用途:

1)在我们日常的第三方应用开发过程中,通常会遇到某个类的成员变量,方法或者属性是私有的或只是系统应用对应开放,这时候我们就可以通过反射获取所需的私有的成员或者方法

2)反射最重要的就是开发各种应用型框架,比如在Spring中,我们将所有的类Bean交给Spring容器进行管理,无论是XML配置Bean还是注解配置,当我们从容器中获取Bean来进行依赖注入的时候,容器会读取配置,而配置中给的就是类的信息,spring根据这些信息,需要创建那些bean,spring就动态的创建这些类

1)Class类:代表类的实体,在运行的java程序中表示类和接口

2)Field类:代表类的成员变量/类的属性

3)Method类:代表类的方法

4)Constructor类,它是代表类的构造方法

Java被编译之后,生成了.class文件(此时在磁盘上面),我们要把这个.class运行到JVM的时候,需要通过JAVA命令来进行运行,此时JVM就会要去解读.class文件,被编译后的java文件.class最终也被JVM解析成一个类,这个对象就是java.lang.Class,当程序在运行的时候,每一个类就变成了Class对象的一个实例。这样当程序正在运行的时候,我们通过java的反射机制应用到这个实例,就可以去获得甚至去改变这个类的属性和动作,使这个类称为一个动态的类;

package com.example.demo;

import lombok.Data;

@Data
public class Student {
    private int userID;
    public int age;
    private String username;
    private String password;
    public Student(String username,String password){
        this.username=username;
        this.password=password;
    }
    public Student(){
        System.out.println("我是不带有参数的构造方法");
    }
    public Student(int age){
        this.age=age;
        System.out.println("只带有一个参数的构造方法"+age);
    }
//这是私有的构造方法只能在当前类中进行创建实例
    private Student(String username,String password,int age){
        this.age=age;
        this.username=username;
        this.password=password;
        
    }
    public void run(String message){
        System.out.println(message+"我是run方法");
    }
    public void start(){
        System.out.println("我是start()方法");
    }
}
1)获取当前的Class对象(通过反射来获取类对象),此时有三种方式

1.1)通过Class.forName("类的全路径名"),是一个静态方法,必须知道类的全路径名字

1.2)通过类名.class来进行获取到class对象

1.3)先创建一个对象实例,再来通过这个对象实例的getClass方法来获取到当前的类对象

1)通过Class.forName(里面是路径)方法

2)通过类名.Class返回一个实例

3)先类创建实例,再通过引用.getClass()来进行返回

//1.根据Class.forName("类的路径名字");
        Class<?> Student1=Class.forName("com.example.demo.Student");
        //2.通过类名.class方法来获取到类对象
        Class<?> Student2=Student.class;
        Student student=new Student();
        //3.通过类实例.getClass()方法来获取到类对象
        Class<?> Student3=student.getClass();
        System.out.println(Student1==Student2);
        System.out.println(Student2 == Student3);

2.创建对象

2.1)先获取到类对象

2.2)通过类对象的newInstance方法来获取对象实例

//1.先进行获取到类的类对象
Class<?> C1=Class.forName("Student");
//2.通过反射创建这个类的实例
Student lijiawei= (Student) C1.newInstance();//最重要进行强制类型转换,这样就可以实例化一个对象 System.out.println(lijiawei);
//3.访问一些信息
System.out.println(lijiawei);
打印结果:我是这个类中的不带有参数的构造方法
Student{age=18, name='bit'}

要不就直接通过类对象调用newInstance()创建这个对象的实例

要不就先获取到构造方法的实例,通过调用constructer.newInstance方法传入相关的构造参数来进行创建实例

3.获取类中的构造器的方法:

下面的获取构造器的方法都是通过类对象来进行调用的

1)getConstructor(参数1.class,参数2.class...)获得该类中与参数类型相匹配的公有的构造方法

2)getConstructors()获取该类的所有的所有的构造方法

3)getDeclaredConstructor(参数1.class,参数2.class)获取该类中与参数类型相匹配的构造方法,即可以获取到公有构造方法,也可以获取到私有构造方法

4)如果在其中获取到了私有的构造方法,需要调用一下构造器引用.setAccessible()方法,将里面的参数设置成true

5)getDeclaredConstructors() 获取该类的所有构造方法

   public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
         //现在我们想获取到Student类的私有构造方法
            //1.先获取到类对象
            Class<?> ClassStudent=Class.forName("Demo.Student");
            //2.获取到构造方法,调用类对象的getConstructor方法返回一个构造器
            Constructor<Student> constructor= (Constructor<Student>) ClassStudent.getConstructor(String.class,int.class);//里面传入构造方法具体的参数类型.class
            //3.通过构造器调用构造方法,传入相应的参数,创建对应的实例
            Student student=constructor.newInstance("李佳伟",90);
            System.out.println(student);
            //上面的代码直接运行会报错,因为我们调用的是私有的构造方法,为了体现封装的安全性,我们应该再加上一条语句

        }

现在这么写就好了:

  public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
         //现在我们想获取到Student类的私有构造方法
            //1.先获取到类对象
            Class<?> ClassStudent=Class.forName("Demo.Student");
            //2.获取到构造方法
            Constructor<Student> constructor= (Constructor<Student>) ClassStudent.getDeclaredConstructor(String.class,int.class);//里面传入构造方法具体的参数类型.class
            //3.确认调用私有构造方法,传入相应的参数
            constructor.setAccessible(true);//可以使用构造方法
            //4.通过构造器来进行创建实例
            Student student=constructor.newInstance("李佳伟",90);
            System.out.println(student);
            //上面的代码直接运行会报错,因为我们调用的是私有的构造方法,为了体现封装的安全性,我们应该再加上一条语句

        }

4.反射私有的属性(可以获取私有的,获取公开的)

1)getField(String name) 获得某一个共有的属性对象

2)getFields()获取到所有的共有的属性对象

3)getDeclaredField(String name)获取到某一个属性对象,返回值是一个Field对象

4)getDeclaredFields()获取到所有的属性对象

  //现在我们想获取到Student类的私有构造方法
            //1.先获取到类对象
            Class<?> ClassStudent=Class.forName("Demo.Student");
            //2.获取到构造方法构造对象
            Constructor<?> constructor=ClassStudent.getDeclaredConstructor(String.class,int.class);
            constructor.setAccessible(true);
            Student student=(Student)constructor.newInstance("李佳伟",12);
            //3.获取到类里面的字段
            Field filed1=ClassStudent.getDeclaredField("name");//里面填写字段名
            Field filed2=ClassStudent.getDeclaredField("age");
            filed1.setAccessible(true);
            filed2.setAccessible(true);
            //4.进行修改私有字段和属性
            filed1.set(ClassStudent,"张志超");
            filed2.set(ClassStudent,81);//第一个参数填写这个类对象,第二个参数写要修改成为的具体的字段名
            System.out.println(filed1);
            System.out.println(filed2);

 5.反射类中的方法
下面的所有方法都是通过类对象来进行调用下面的方法
1)获取某个类中共有的方法:getMethod()
里面的参数是方法名,参数类型.class,最终返回的是Method对象
2)获取到该类中所有的方法:getMethods()
3)获取到该类中的某一个方法
getDeclaredMethod(String name(方法名字),方法的参数1.class,方法的参数2.class.....)
4)获取到该类中的所有方法:getDeclaredMethods();

method.invoke(对象名,字段值)我们最重要调用这个方法来进行调用我们获取到的方法
   //1.先进行获取到类对象
            Class<?> classStudent=Student.class;
            //2.获取到私有的构造方法
            Constructor<?> constructor= classStudent.getDeclaredConstructor(String.class,int.class);
            //3.创建具体的对象
            constructor.setAccessible(true);
            Student student=(Student)constructor.newInstance("张中军",90);
            //4.获取到对应的方法
            Method method= classStudent.getDeclaredMethod("start",String.class);
            method.setAccessible(true);
            //5.调用对应的获取到的方法
            method.invoke(student,"sb");
//第一个参数是对应的对象,第二个参数是方法中要传递的参数

2.枚举 

1)背景以及定义:

访问枚举类型:

在类内:直接写枚举名字

在类外:直接写TestEnum.枚举名字

枚举是在JDK1.5之后进行引入的,目的用途是将一组常量组织起来,

在这之前表示一组常量通常使用定义常量的方式例如:

public static final int RED=1;

public static final int GREEN=2;

public static final int BLACK=3;

但常量枚举有一个不好的地方,例如假设现在正好有一个数字1,但是他可能会被误认为是RED,现在我们可以直接使用枚举来进行组织,这样一来就拥有了枚举类型而不是一个普通的数字1;

public enum TestEnum{

RED,BLACK,GREEN;

public static void main(String[] args]{

System.out.println(RED);//但是在类外要通过类名.的方式

System.out.println(BLACK);

    }
}

此时打印出来的值就是RED,BLACK

Switch语句可以使用枚举类型

package com.example.demo;

public enum TestEnum {
    RED,BlACK,BLUE,GREEN;

    public static void main(String[] args) {
        TestEnum testEnum=RED;
        switch(testEnum){
            case RED:
                System.out.println("我是红色");
                break;
            case BlACK:
                System.out.println("我是黑色");
                break;
            case BLUE:
                System.out.println("我是蓝色");
                break;
            case GREEN:
                System.out.println("我是绿色");
                break;
            default:
                System.out.println("啥也不是");
                break;
        }
    }
}

2)Enum中的常见方法(自己写的枚举类,默认继承了一个抽象类Enum,public abstract class<E extends Enum<E>> implements Comparable<E>,Serializable

我们自己所写的枚举类默认情况下都是继承于一个抽象类Enum

1)TestEnum.values()以数组的形式返回枚举类型的所有成员

2)TestEnum.ordinal()获取到枚举成员的索引位置--------在Enum类里面

3)TestEnum.valueOf()将普通字符串转化成枚举实例

4)TestEnum.compareTo()比较两个枚举成员定义的顺序,默认是通过索引来进行比较的

public enum TestEnum{
    RED,BLACK,GREEN,WHITE;
    public static void main(String[] args) {
        TestEnum[] arr1=TestEnum.values();
        for(int i=0;i< arr1.length;i++)
        {
            System.out.println(arr1[i]+arr[i].ordinal());
        }
   //把字符串变成对应的枚举对象
       TestEnum testEnum= TestEnum.valueOf("RED");
        System.out.println(testEnum);RED
        System.out.println(RED.compareTo(BLACK));
        System.out.println(BLACK.compareTo(WHITE));
    }
}

1)枚举的构造方法默认是私有的

2)枚举类是不可以被继承的(TestEnum不可被继承) 

1)既然枚举的构造方法是私有的,那么我们是否可以通过反射来获取枚举对象的实例呢?

public enum TestEnum{
 RED("hello",1),BLACK("world",2),GREEN("l want",3),WHITE("kkkk",4);
 public String color;
 public int origin;
 private TestEnum(String color,int origin)
//此时的枚举自己构造方法默认是私有的,不能写成public,写或者不写都是私有的
    {
//这里面会默认帮助父类进行构造,会默认super(),默认执行这个构造方法
        this.color=color;
        this.origin=origin;
    }
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<Enum> S1= (Class<Enum>) Class.forName("TestEnum");
Constructor<Enum> constructor=S1.getDeclaredConstructor(String.class,int.class);
//枚举的构造方法是私有的,所以要把参数设置成true
constructor.setAccessible(true);
TestEnum testEnum= (TestEnum) constructor.newInstance("BLUE",23);
//构造方法里面再写"hhh",89;那么这时还是会报错的
 System.out.println("枚举对象是"+testEnum);
    }
}
这段代码时会报错的,他的报错的意思是没有这样的构造方法
但是在我们所写的代码中,是存在这样的构造方法的,那怎么会没有呢?

nosuchmethodException获取不到这个构造方法

1)原因是自己所写的枚举类默认是继承于ENUM一个抽象类的,而这个抽象类的构造方式是含有两个参数的

2)所以在我们自己所写的枚举类型中,我们应该先要帮助父类进行构造,但是枚举类已经提前帮助父类进行构造了

3)当我们进行反射获取的时候,就需要多写上父类的参数

Constructor<Enum>constructor=S1.getDeclaredConstructor(String.class,int.class,String.class,int.class);

TestEnum testEnum= (TestEnum) constructor.newInstance("BLUE",23,"bit",99)

其实本质上TestEnum是四个构造参数,两个参数是父类的

构造方法里面后面再写"hhh",89

那么这时还是会报错的,先帮助父类进行构造,newInstance会进行报错

1)在咱们的反射里面的newInstance方法里面会进行判断,如果是枚举类型,是不可以创建实例的

2)所以我们进行总结,枚举类型是非常安全的,我们无法通过反射来进行获取到枚举类型的实例对象,在类外是无法反射枚举对象的

3)我们定义的一个枚举,在第一次被真正用到的时候,会被虚拟机加载并初始化,而这个初始化过程是线程安全的,而我们知道,解决单例的并发问题,主要解决的就是初始化过程中的线程安全问题。

所以,由于枚举的以上特性,枚举实现的单例是天生线程安全的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值