反射与枚举

反射与枚举

反射概述
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取类的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类, 必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象。可以通过反射机制,去调用类中的任何一个属性和方法。
new 对象 调用方法或属性私有的属性和功能你外界调用不到。要使用反射机制,那么就要先获取一个类的字节码文件对象(Class 字节码文件类型)。

获取一个类的字节码文件对象的三种方式:
方式1:就是通过 Object类中的 getClass()方法

Student student = new Student(); //Student.class----->字节码文件对象
        Class<? extends Student> aClass = student.getClass();
        Class<? extends Student> aClass1 = student.getClass();
        System.out.println(aClass==aClass1);

方式2:通过Class 中的静态方法 forName()方法来获取一个类的字节码文件对象

Class<?> aClass2 = Class.forName("org.westos.demo.Student");
        Class<?> aClass3 = Class.forName("org.westos.demo.Student");
        System.out.println(aClass2 == aClass3);

        Class<?> aClass4 = Class.forName("org.westos.demo2.Student");
        System.out.println(aClass2 == aClass4);

方式3:针对每个类,都有一个静态的.class 属性 来获取一个 类的字节码文件对象。

Class<Student> studentClass = Student.class;
        Class<Object> objectClass = Object.class;
        Class<String> stringClass1 = String.class;
        Class<String> stringClass2 = String.class;
        System.out.println(stringClass1==stringClass2);

通过反射机制,来剖析一个类的构成,一个类的构成包括:构造方法,成员变量 ,成员方法。
通过反射来剖析这个类的三个构成部分,那么这三个构成,也会看做一种类型:
构造方法 Constructor
成员变量 Field
成员方法 Method

通过反射,来调用构造方法执行,创建出该类对象

通过反射获取无参构造方法并使用

获取所有构造方法
public Constructor<?>[] getConstructors() 获取所有的构造方法不包含私有的
public Constructor<?>[] getDeclaredConstructors() 获取所有的构造方法 包括私有的
获取单个构造方法
public Constructor getConstructor(Class<?>… parameterTypes) 获取单个的构造方法 不包含私有的
public Constructor getDeclaredConstructor(Class<?>… parameterTypes) 获取单个的构造方法包含私有的

public class Student {
    //定义构造方法

    public Student() {
        System.out.println("空参构造执行了");
    }

    public Student(String name) {
        System.out.println("一个参数的构造执行了");
    }

    private Student(String name,int age) {
        System.out.println("私有的构造执行了");
    }
}

 //1.先获取该类的字节码文件对象
        Class<?> aClass = Class.forName("demo.Student"); //Student.class
        //2.可以获取Student类的构造方法对象

        //获取该类中所有的构造方法对象,私有的构造方法对象获取不到
        Constructor<?>[] constructors = aClass.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }
        
        //获取该类中所有的构造方法对象,包括私有的构造方法
        Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor);
        }
        System.out.println("===================================");
        //获取单个的构造方法对象
        //获取空参的构造方法对象
        Constructor<?> constructor = aClass.getConstructor();
        System.out.println(constructor);
        System.out.println("===========================");
        //获取一个参数的构造方法对象 参数就是,你构造方法的参数类型的字节码类型
        Constructor<?> constructor1 = aClass.getConstructor(String.class);
        System.out.println(constructor1);
        System.out.println("===========================");
        //获取私有的构造方法对象 getDeclaredConstructor()
        Constructor<?> constructor2= aClass.getDeclaredConstructor(String.class,int.class);
        System.out.println(constructor2);
    }
}

通过反射来创建一个类的对象

public class MyTest2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
       

        //我们通过反射的方式来创建一个类的对象。
        Class<?> aClass = Class.forName("demo.Student");
        //获取空参的构造方法对象
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
        //通过构造方法对象中的方法,创建出Student类对象
        Student o = (Student) declaredConstructor.newInstance();
        System.out.println(o);

        System.out.println("============================");
      
        //使用反射的方式,通过有参构造来创建对象
        Constructor<?> constructor = aClass.getConstructor(String.class);
        //调用构造方法对象中的方法来创建Student类的对象
        Object obj = constructor.newInstance("李四");
        System.out.println(obj);

        System.out.println("=============================");
        //通过私有构造方法来创建对象,以前的方式,创建不了
       // Student student = new Student("王五", 25);

        //反射这种方式,即便你是私有构造,也能创建出该类的对象
        //获取私有的构造方法对象
        Constructor<?> declaredConstructor1 = aClass.getDeclaredConstructor(String.class, int.class);
        //取消语法检查不然会报错 因为私有的外界不能直接访问
        declaredConstructor1.setAccessible(true);//值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
        Object obj2 = declaredConstructor1.newInstance("赵六", 26);
        Student stu= (Student) obj2;
        System.out.println(stu);

      //  Student.class----->Class 中的getConstructor()----->Constructor构造方法对象----> newInstance()
    }
}

使用反射的方式,调用空参构造来创建对象

public class Teacher {
    public Teacher(){
        System.out.println("空参构造调用了");
    }
}
public class MyTest {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //1.以前的方式,借助空参构造创建该类对象
        //Teacher teacher = new Teacher();
        //2.使用反射的方式,调用空参构造来创建对象的方式1
        Class<?> aClass = Class.forName("org.westos.demo3.Teacher");
        //3.获取空参构造方法对象
        Constructor<?> constructor = aClass.getConstructor();
        //4.调用构造方法对象中的方法来创建该类对象
        Teacher teacher = (Teacher) constructor.newInstance();
        System.out.println(teacher);

        System.out.println("===========================");
        //方式2:如果你只是想要通过空参构造来创建对象,你可以使用Class类里面的newInstance()方法直接创建对象。
        Teacher teacher1 = (Teacher) aClass.newInstance();



    }
}

如果你通过反射来剖析一个类的构成,对于类中的成员变量 使用Field类型来描述成员变量

public class Dog {
    public String name;
    public int age;
    private char sex;
    private Dog(){}

}

        //1.你要使用反射,第一步先获取该类的字节码文件对象。
        Class<?> aClass = Class.forName("org.westos.demo4.Dog");
        //2.获取Dog类中的所有字段对象,但是私有的字段对象获取不到
        Field[] fields = aClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        System.out.println("==================================");
        //获取Dog类中的所有字段对象,包括私有的字段对象
        Field[] fields2 = aClass.getDeclaredFields();
        for (Field field : fields2) {
            System.out.println(field);
        }

        System.out.println("=============================");
        //获取单个的非私有的字段对象,你要获取哪个字段对象,你名字传入
        Field fieldName = aClass.getField("name");
        System.out.println(fieldName);

        Field fieldAge = aClass.getField("age");
        System.out.println(fieldAge);

        System.out.println("====================");
        //获取单个的私有字段或非私有字段的对象
        Field fieldSex= aClass.getDeclaredField("sex");
        System.out.println(fieldSex);



    }
}

public class MyTest2 {
    public static void main(String[] args) throws Exception {
        //我们通过反射的方式,给类中的成员变量设置值,以及获取值。
        //1.你要使用反射,第一步先获取该类的字节码文件对象。
        Class<?> aClass = Class.forName("org.westos.demo4.Dog");
        //2.给name字段设置,我们就要获取到name字段对象
        Field fieldName = aClass.getField("name");
        //调用Field类中的方法来设置值
        //通过反射,创建一个类的对象
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Object obj = declaredConstructor.newInstance();
        //set()方法要两个参数,第一个参数,你该类的对象,第二个就是你要设置的具体的值
        fieldName.set(obj,"小黑");
        //获取name字段的值
        Object o = fieldName.get(obj);
        System.out.println(o);

        System.out.println("==============================");
        Field fieldAge = aClass.getField("age");
        fieldAge.setInt(obj,100);
        int anInt = fieldAge.getInt(obj);
        System.out.println(anInt);

        System.out.println("===================================");
        //给私有字段设置值
        Field fieldSex = aClass.getDeclaredField("sex");
        //对于私有的,我们可以取消语法检查
        fieldSex.setAccessible(true);
        fieldSex.setChar(obj,'女');

        //获取
        char aChar = fieldSex.getChar(obj);
        System.out.println(aChar);

        //以前的方式--->new 一个对象--->对象.成员变量名
        //反射的方式  Dog.class---->Class 类中的getField()--->Field 对象----通过Field 对象中的set() get()方法给字段设置值,以及获取字段的值
    }
}

通过反射获取无参无返回值成员以及有参有返回值成员方法并使用

public class Student {
    private Student() {
    }
    public void hehe(){
        System.out.println("呵呵");
    }

    public void haha(String name) {
        System.out.println(name+"哈哈");
    }

    public void test(String name,int age) {
        System.out.println(name + "test"+age);
    }

    public String test2(String name, int age) {
        System.out.println(name + "test2" + age);
        return "abc";
    }

    private String show(String name, int age) {
        System.out.println(name + "show" + age);
        return "AAA";
    }
}
public class MyTest {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
      
        //对应类中的成员方法Java提供Method这个类来描述

        //1.获取该类的字节码文件对象
        Class<?> aClass = Class.forName("org.westos.demo.Student");
        //2.获取类中所有的成员方法对象,获取到非私有的方法对象,包括从父类继承下来的方法对象也能够获取到
        Method[] methods = aClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }

        System.out.println("==================================");
        //获取所有的方法对象,包括私有的,但是不获取父类的方法对象
        Method[] declaredMethods = aClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }

        System.out.println("=================================");
        //获取单个的非私有方法对象
        Method heheMethod = aClass.getMethod("hehe");
        System.out.println(heheMethod);
        System.out.println("========================");
        //获取有参的方法对象 getMethod()方法的参数1:方法名 参数2:你方法上形参的数据类型的Class类型
        Method haha = aClass.getMethod("haha", String.class);
        System.out.println(haha);
        System.out.println("==========================");
        Method test = aClass.getMethod("test", String.class, int.class);
        System.out.println(test);
        System.out.println("============================");
        //获取单个的私有方法对象
        Method show = aClass.getDeclaredMethod("show", String.class, int.class);
        System.out.println(show);
    }
}

使用反射的方式,调用方法执行

  //现在,我们使用反射的方式,调用方法执行
        Class<?> aClass = Class.forName("org.westos.demo.Student");
        //获取hehe方法对象
        Method heheMethod = aClass.getMethod("hehe");
        //Object invoke(Object obj, Object... args)
        //          对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Object obj = declaredConstructor.newInstance();
        //invoke(obj); //参数1,就是该类的对象
        Object invoke = heheMethod.invoke(obj);

        System.out.println("============================");
        //调用有参方法执行
        Method hahaMethod = aClass.getMethod("haha", String.class);
        hahaMethod.invoke(obj,"老师");

        System.out.println("=========================");
        Method testMethod = aClass.getMethod("test",  String.class, int.class);
        testMethod.invoke(obj,"王五",25);

        System.out.println("==========================");
        Method test2Method = aClass.getMethod("test2", String.class, int.class);
        //返回的就是test2()调用完毕之后的返回值
        Object invoke1 = test2Method.invoke(obj, "赵六", 26);
        System.out.println(invoke1);

        System.out.println("===========================");
        //调用私有的方法执行
        Method showMethod= aClass.getDeclaredMethod("show", String.class, int.class);
        //取消私有的检查
        showMethod.setAccessible(true);
        //如果这个方法,有返回值,我们也可以获取
        Object invoke2 = showMethod.invoke(obj, "ccc", 30);
        System.out.println(invoke2);
    }
}

通过反射运行配置文件内容

在开发过程中中途需要改变需求

public class Cat {
    public void eat(){
        System.out.println("猫吃鱼");
    }
}
public class Dog {
    public void eat() {
        System.out.println("狗吃肉");
    }
}

public class MyTest {
    public static void main(String[] args) throws Exception {
      
        Properties properties = new Properties();
        properties.load(new FileReader("MyConfig.properties"));
        Class<?> myClass = Class.forName(properties.getProperty("className"));
        Object obj = myClass.getDeclaredConstructor().newInstance();
        //调用猫类中的eat方法执行
        Method methodName = myClass.getDeclaredMethod(properties.getProperty("methodName"));
        Object invoke = methodName.invoke(obj);
    }
}

通过反射越过泛型检查

泛型,只在编译期有效,运行期就擦除了。

public class MyTest {
    public static void main(String[] args) throws Exception {
      
        ArrayList<String> list = new ArrayList<>();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");
        //list.add(200);
        //我们通过反射机制,越过泛型检测
        Class<? extends ArrayList> aClass = list.getClass();
        //获取add方法对象
        Method addMethod = aClass.getDeclaredMethod("add", Object.class);
        //调用add方法执行。
        addMethod.invoke(list,200);
        addMethod.invoke(list, true);

        System.out.println(list);

    }
}

通过反射写一个通用的设置某个对象的某个属性为指定的值

public class Teacher {
    private Teacher() {
    }

    private String name;
    private int age;
}
public class MyTest {
    public static void main(String[] args) throws Exception {
        //通过反射写一个通用的设置某个对象的某个属性为指定的值
      
        Constructor<?> declaredConstructor = Class.forName("org.westos.demo4.Teacher").getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Object obj = declaredConstructor.newInstance();
        MyUtils.setProperty(obj,"name","张三");

        Object name = MyUtils.getProperty(obj, "name");
        System.out.println(name);

        System.out.println("=======================");

        MyUtils.setProperty(obj, "age", 30);
        Object r = MyUtils.getProperty(obj, "age");
        Integer num= (Integer) r;
        System.out.println(num);
    }
}

动态代理的概述和实现

在不改动原有代码的基础上,对功能进行增强。我们可以使用一种更加灵活的方式 叫做动态代理

​ 在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理。
​ 我们有更强大的代理cglib,Proxy类中的方法创建动态代理类对象
​ public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
​ 最终会调用InvocationHandler的方法
​ InvocationHandler Object invoke(Object proxy,Method method,Object[] args)

public interface UserDao {
    public abstract void add();

    public abstract void delete();

    public abstract void upate();

    public abstract void query();
}
public class UserDaoImpl implements UserDao {
    @Override
    public void add() {
        
        System.out.println("增加一个用户");
     
    }

    @Override
    public void delete() {
      
        System.out.println("删除一个用户");
       
    }

    @Override
    public void upate() {
       
        System.out.println("修改一个用户");
        
    }

    @Override
    public void query() {
        
        System.out.println("查询一个用户");
       
    }
}

public class UserDaoStaticProxy implements UserDao{
    private UserDaoImpl userDao=new UserDaoImpl();
    @Override
    public void add() {
        System.out.println("权限的校验");
        userDao.add();
    }

    @Override
    public void delete() {
        System.out.println("权限的校验");
        userDao.delete();
    }

    @Override
    public void upate() {
        System.out.println("权限的校验");
        userDao.upate();
    }

    @Override
    public void query() {
        System.out.println("权限的校验");
        userDao.query();
    }
}
public class BookDao {
    public void add(){
        System.out.println("权限校验");
        System.out.println("增加图书");
    }
}

public class MyTest2 {
    public static void main(String[] args) {
        // userDao 目标对象
        UserDao userDao = new UserDaoImpl();
    
        //代理对象:Java提供了一个Proxy类,可以帮你生成一个代理对象。
        //Proxy 提供用于创建动态代理类和实例的静态方法
        //创建一个代理对象的方法
       /* static Object newProxyInstance (ClassLoader loader, Class < ?>[]interfaces, InvocationHandler h)
        返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
*/


        //获取出代理对象
        UserDao proxy = ProxyUtils.getProxy(userDao);
        proxy.add();
        System.out.println("=====================");
        proxy.delete();
        System.out.println("=====================");
        proxy.upate();
        System.out.println("=====================");
        proxy.query();
        //注意:JDK的动态代理,必须要有一个接口。

        //如果一个普通的类,并没有一个接口,你想要对这个类中的功能,进行增强,可以使用CGLIB代理
    }
}

public class ProxyUtils {
    //传过来的是被代理对象(目标对象)
    public static UserDao getProxy(UserDao dao){
       // Proxy 提供用于创建动态代理类和实例的静态方法
        /* static Object newProxyInstance (ClassLoader loader, Class < ?>[]interfaces, InvocationHandler h)
        返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。*/
        //参数1:ClassLoader loader 类加载器 加载代理对象,跟目标对象用的是同一个加载器 固定写法
        //参数2:接口对应的一个Class数组 代理对象需要实现的接口 固定写法
         //  InvocationHandler 是代理对象的调用处理程序 实现的接口。需要我们自己编写的那些增强的代码

        UserDao obj= (UserDao) Proxy.newProxyInstance(dao.getClass().getClassLoader(), dao.getClass().getInterfaces(), new InvocationHandler() {
             //Object proxy 代理对象
              //Method method 被代理接口中的方法对象
              //Object[] args 被代理接口中方法对象的参数数组
            //当我们调用接口中的方法的时候,都会经过invoke()方法
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //System.out.println("invoke方法调用了");
              /*  System.out.println("权限校验");
                Object result = method.invoke(dao);//调用目标对象的方法执行。
                System.out.println("日志的记录");*/
               //对某个方法进行增加
                Object result=null;
                if (method.getName().equals("add")) {
                    System.out.println("权限校验");
                    result = method.invoke(dao);//调用目标对象的方法执行。
                    System.out.println("日志的记录");
                }else{
                    result = method.invoke(dao);//调用目标对象的方法执行。
                }

                return result;
            }
        });
        return obj;//返回代理对象
    }
}

JDK1.5的新特性回归以及自己实现枚举类

JDK1.5的新特性: 自动拆装箱 , 泛型 , 增强for , 可变参数 , 枚举
枚举概述: 就是一个类只能存在几个固定的对象,那么这个就是枚举.我们就可以使用这些对象可以表示一些固定的值.

public class Direction {
    public static final Direction BEFORE=new Direction();
    public static final Direction AFTER = new Direction();
    public static final Direction LEFT = new Direction();
    public static final Direction RIGHT = new Direction();

    private Direction(){}
}
public class MyTest {
    public static void main(String[] args) {
       
         //方向:前后左右
        Direction before = Direction.BEFORE;
        Direction after = Direction.AFTER;
        Direction left = Direction.LEFT;
        Direction right = Direction.RIGHT;
        System.out.println(before);
        System.out.println(after);
        System.out.println(left);
        System.out.println(right);
    }
}

自定义枚举类

public abstract class Direction {
    public static final Direction BEFORE=new Direction("前"){
        @Override
        public void show(String name) {
            System.out.println(name);
        }
    };
    public static final Direction AFTER = new Direction("后") {
        @Override
        public void show(String name) {
            System.out.println(name);
        }
    };
    public static final Direction LEFT = new Direction("左") {
        @Override
        public void show(String name) {
            System.out.println(name);
        }
    };
    public static final Direction RIGHT = new Direction("右") {
        @Override
        public void show(String name) {
            System.out.println(name);
        }
    };

   // private Direction(){}
    public  String name;
    private Direction(String name) {
        this.name=name;
    }
    //提供了一个抽象方法
    public abstract void show(String name);


}

public class MyTest {
    public static void main(String[] args) {
        Direction before = Direction.BEFORE;
        Direction after = Direction.AFTER;
        Direction left = Direction.LEFT;
        Direction right = Direction.RIGHT;
        before.show("前");
        after.show("后");
        left.show("左");
        right.show("右");
    }
}

JDK1.5之后引入了一个enum这个关键字,用来表示定义枚举。

public enum Direction {
    //枚举项:必须位于第一行 最后一个枚举项可以使用;分号结束
    BEFORE,AFTER,LEFT, RIGHT;
    String name;
    //枚举里面的构造方法,必须是私有修饰的,默认也是私有的
     private Direction() {
    }


}
public class MyTest {
    public static void main(String[] args) {
        //自定义枚举,写法太繁琐,JDK1.5之后引入了一个enum这个关键字,用来表示定义枚举。
        Direction after = Direction.AFTER;
        Direction before = Direction.BEFORE;
        Direction left = Direction.LEFT;
        Direction right = Direction.RIGHT;

        System.out.println(after.toString());
        System.out.println(before);
        System.out.println(left);
        System.out.println(right);

        //JDK1.5之后,switch语句支持枚举类型

        switch (Direction.RIGHT){
            case LEFT:
                System.out.println("左");
                break;
            case RIGHT:
                System.out.println("右");
                break;
            case BEFORE:
                System.out.println("前");
                break;
            case AFTER:
                System.out.println("后");
                break;
        }

        //使用枚举类型做判断
        if(Direction.RIGHT.toString().equals("RIGHT")){
            System.out.println("进来了");
        }
        //获取枚举项的编号
        System.out.println(before.ordinal());
        System.out.println(after.ordinal());
        System.out.println(before.toString());
        System.out.println(before.name());
        //JDK1.7 支持千位分隔符
        int num=100_201_411;

        
    }
}
// Direction 枚举类  extends Enum 但是你不要显式的写出来
public enum Direction {
    //枚举项
    BEFORE("前"),AFTER("后"),LEFT("左"), RIGHT("右");
    public String name;
    //注意:枚举里面的 构造方法默认是私有的
    private Direction(String name) {
        this.name = name;
    }

枚举类的常见方法

枚举类的常见方法
int ordinal() 返回枚举项的序号
int compareTo(E o) 比较两个枚举项的 返回的是两个枚举项序号的差值
String name() 获取枚举项的名称
String toString()获取枚举项的名称
T valueOf(Class type,String name) 用来获取指定的枚举项 参数1:枚举类对应的字节码对象 参数2 枚举项的名称
values() 获取所有的枚举项
此方法虽然在JDK文档中查找不到,但每个枚举类都具有该方法,它遍历枚举类的所有枚举值非常方便

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值