Java中的反射和枚举

1.反射

1)反射的定义:

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

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

2)反射中涉及到的一些常见的类(背)

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

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

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

3)Method类:代表类的方法

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

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

class Student{
    public int age=18;
    private String name="bit";
    public Student()
    {
        System.out.println("我是这个类中的不带有参数的构造方法");
    }
    private Student(String name,int age)
    {
        this.name=name;
        this.age=age;
        System.out.println("我是这个类中一个私有的构造方法");
    }
    public void start(String str)
    {
        System.out.println("我是这个类中的带参数的普通方法");
    }
    private void run()
    {
        System.out.println("我是这个类中的不带有参数的私有方法");
    }

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

1)获取当前的Class对象(通过反射来获取类对象),此时有三种方式

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

Class<?> C1=Class.forName("Student");//报名.类名
Class<?> C2= Student.class;
Student student=new Student();
Class<?> C3=student.getClass();
System.out.println(C1==C2);
System.out.println(C2==C3);不管是用哪种方式来获取Class对象,这里面的打印结果都是false,说明类对象只有一个

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

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

2.创建对象

类对象的引用.newInstance() 

Class<?> C1=Class.forName("Student");
Student lijiawei= (Student) C1.newInstance();//最重要进行强制类型转换,这样就可以实例化一个对象
 System.out.println(lijiawei);

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

1)getConstructor(Class......<?> parameterTypes)获得该类中与参数类型相匹配的公有的构造方法

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

3)getDeclaredConstructor(Class<?> parameterTypes)获取该类中与参数类型相匹配的构造方法//即可以获取到公有,也可以获取到私有,如果在其中获取到了私有的构造方法,我们还是需要调用一下构造器引用.setAccessible()方法,将里面的参数设置成true

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

//先获取到类对象

Class<?> S1=Class.forName("Student");
//获取到构造器的引用
Constructor<Student> constructor= (Constructor<Student>) 
//接下来进行传参S1.getDeclaredConstructor(String.class,int.class);
 //调用构造方法为私有的时候才会设置这个参数constructor.setAccessible(true);
//创建一个新的对象Student student= constructor.newInstance("lijiaxin",13); System.out.println(student);

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

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

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

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

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

//1获取到Class对象
   Class<?> C1=Class.forName("Student");
//2获取到一个对象
    //可以直接通过类对象的引用.newInstance来新创建一个对象
   Student student= (Student) C1.newInstance();
    //可以直接通过构造器来进行生成一个对象
  Field field= C1.getDeclaredField("name");
  field.setAccessible(true);
//改变私有的属性字段
  field.set(student,"张三");
  System.out.println(student);

 5.反射类中的方法

1)获取某个类中共有的方法:getMethod(),里面的参数是方法名,参数类型,class
2)获取到该类中所有的方法:getMethods()
3)获取到该类中的某一个方法getDeclaredMethod(String name,Class....<?> parameterTypes)
4)获取到该类中的所有方法:getDeclaredMethods();
Class<Student> studentClass= (Class<Student>) Class.forName("Student");
Student student=studentClass.newInstance();
  Method method1= studentClass.getDeclaredMethod("run");
  Method method2= studentClass.getMethod("start",String.class);
  method1.setAccessible(true);
  method2.invoke(student,"我是私有的run方法");

我是这个类中的不带有参数的构造方法
我是这个类中的带参数的普通方法
我是私有的run方法

2.枚举 

1)背景以及定义:

枚举是在JDK1.5之后进行引入的,目的用途是将一组常量组织起来,在这之前通常使用定义常量的方式例如:

public static final int RED=1;

public static final int GREEN=2;

public static final int BLACK=3;

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

public enum TestEnum{

RED,BLACK,GREEN;

public static void main(String[] args]{

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

System.out.println(BLACK);

    }

}

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

public enum TestEnum{

    RED,BLACK,GREEN;

    public static void main(String[] args){

    TestEnum testEnum=TestEnum.BLACK;
    switch (testEnum){
        case RED:
            System.out.println("red");
            break;
        case BLACK:
            System.out.println("black");
            break;
        case GREEN:
            System.out.println("green");
            break;
    }

    }

}

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

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

2)ordinal()获取到枚举成员的索引位置

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

4)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]);
        }
   //把字符串变成对应的枚举对象
       TestEnum testEnum= TestEnum.valueOf("RED");
        System.out.println(testEnum);
        System.out.println(RED.compareTo(BLACK));
        System.out.println(BLACK.compareTo(WHITE));
    }
}

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

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

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)//此时的自己构造方法默认是私有的
    {//super()
        this.color=color;
        this.origin=origin;
    }

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)//此时的自己构造方法默认是私有的
    {
        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);
    }
}
这段代码时会报错的,他的报错的意思是没有这样的构造方法,但是在我们所写的代码中,是存在这样的构造方法的,那怎么会没有呢?

原因是我们自己所写的枚举类默认是继承于上面的一个抽象类的,而这个抽象类的构造方式是含有两个参数的,所以在我们自己所写的枚举类型中,我们应该先要帮助父类进行构造

// TestEnum testEnum= (TestEnum) constructor.newInstance("BLUE",23);

构造方法里面再写"hhh",89;那么这时还是会报错的

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

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

2)如何设置一个线程安全的单例模式呢?

把构造方法设置成私有的,在类里面之创建一个实例,并进行返回;通过公开方法来获取到一个实例;

1)设计饿汉模式和懒汉模式,里面的字段属性一定要是private static 防止在类外被访问到,除了获取实例的方法之外设置成public之外;

public class Singleton {
    Object object = new Object();
    private Singleton() {
    };
    private volatile static Singleton singleton = null;
    public static Singleton getInstance() {
        if (singleton == null) {
            synchronized (Object.class) {
                if (singleton == null) {
                    return new Singleton();
                }
            }
        }
        return singleton;
    }
 class Singleton{
     private Singleton(){};
     private static final Singleton singleton=new Singleton();
     public  static Singleton getInstance()
     {
         return singleton;
     }
}

2)通过静态内部类来实现一个单例模式

class Singleton{
    private Singleton(){};
    private static class User{
        private static final Singleton singleton=new Singleton();
    }
    public static Singleton getInstance(){
        return User.singleton;
    }
}
class TestEnum{
    public static void main(String[] args) {
        Singleton singleton=Singleton.getInstance();
        Singleton singleton1=Singleton.getInstance();
        System.out.println(singleton1==singleton);
    }
}

3)通过枚举来实现一个单例模式

public enum TestEnum{
    RED;
    public  static TestEnum getInstance()
     {
        return RED;
    }

    public static void main(String[] args) {
        TestEnum testEnum1=TestEnum.getInstance();
        TestEnum testEnum=TestEnum.getInstance();
        System.out.println(testEnum==testEnum1);
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_61518137/article/details/125128653