Java基础(33)反射、枚举

1. 反射机制

1. 反射机制的概述

	Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
    对于任意一个对象,都能够调用它的任意一个方法和属性;
    这种动态获取类的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
    要想解剖一个类, 必须先要获取到该类的字节码文件对象。
    而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象

(1)可以通过new对象去调用属性和方法,但是私有的属性和方法外界无法调用
(2)我们可以通过反射机制,去调用类中的任何一个属性和方法(包括私有)

2. 获取一个类的字节码文件对象的三种方式
我们要使用反射机制,那么就要先获取一个类的字节码文件对象(Class字节码)

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

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

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

public class TestDemo01 {
    public static void main(String[] args) throws ClassNotFoundException {
        //方式1:通过Object类中的getClass()方法来获取一个类的字节码文件对象
        Student student = new Student();
        Class<? extends Student> aClass = student.getClass();
        System.out.println(aClass);    //class demo01.Student

        //方式2:通过Class类中的静态方法forName()来获取一个类的字节码文件对象
        //权限限定名:包名+类名 即可确定一个类的唯一性
        Class<?> aClass1 = Class.forName("demo01.Student");
        System.out.println(aClass1);    //class demo01.Student

        //方式3:每个类都有一个静态的.class属性,来获取一个类的字节码文件对象
        Class<Student> aClass2 = Student.class;
        System.out.println(aClass2);    //class demo01.Student

        //一个类的字节码文件对象是相同的
        System.out.println(aClass == aClass1);    //true
        System.out.println(aClass2 == aClass1);   //true
        System.out.println(aClass == aClass2);    //true

    }
}

public class Student {
}

3. 通过反射获取构造方法(Constructor)并使用
(1)以前的方式:new Student()
(2)现在的方式:Student.class --> Class中的getConstructor() --> Constructor构造方法对象 --> newInstance()

A:获取所有构造方法
	public Constructor<?>[] getConstructors() 获取所有的构造方法不包含私有的
	public Constructor<?>[] getDeclaredConstructors() 获取所有的构造方法 包括私有的
B:获取单个构造方法
	public Constructor<T> getConstructor(Class<?>... parameterTypes) 获取单个的构造方法 不包含私有的
	public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 获取单个的构造方法包含私有的
C:创建新对象的方法
	T newInstance(Object... initargs) :使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例 
D:取消语法检查
	T setAccessible(boolean b):
		参数:
			true:可使用私有
			false:不可使用私有
/**
 * 通过反射获取构造方法
 */
public class TestDemo01 {
    public static void main(String[] args) throws Exception {
        //通过反射先获取到该类的字节码文件对象
        Class<?> aClass = Class.forName("demo02.Student");

        //1.获取该类中的所有构造方法对象,私有的获取不到
        Constructor<?>[] constructors = aClass.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
            /*
            public demo02.Student()
            public demo02.Student(java.lang.String)
            */
        }

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

        //2.获取该类中的所有构造方法对象,包括私有的
        Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor);
            /*
            public demo02.Student()
            public demo02.Student(java.lang.String)
            private demo02.Student(java.lang.String,int)
             */
        }

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

        //3.获取一个构造方法对象(无参构造)
        Constructor<?> constructor = aClass.getConstructor();
        System.out.println(constructor);   //public demo02.Student()

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

        //4.获取一个构造方法对象(一个参数构造),参数就是:构造方法的参数的字节码类型
        Constructor<?> constructor2 = aClass.getConstructor(String.class);
        System.out.println(constructor2);    //public demo02.Student(java.lang.String)

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

        //5.获取一个构造方法对象(私有,多个参数构造),参数就是:构造方法的参数的字节码类型
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class, int.class);
        System.out.println(declaredConstructor);    //private demo02.Student(java.lang.String,int)

    }
}

===============================================================================================

/**
 * 通过反射获取构造方法并创建新的对象
 */
public class TestDemo02 {
    public static void main(String[] args) throws Exception {
        //通过反射先获取到该类的字节码文件对象
        Class<?> aClass = Class.forName("demo02.Student");

        //1.获取一个构造方法对象(无参构造),并创建新对象
        Constructor<?> constructor = aClass.getConstructor();
        System.out.println(constructor);   //public demo02.Student()
        Object o = constructor.newInstance();
        System.out.println(o);   //无参构造方法执行了
                                 //demo02.Student@eed1f14

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

        //2.获取一个构造方法对象(一个参数构造),并创建新对象
        Constructor<?> constructor2 = aClass.getConstructor(String.class);
        System.out.println(constructor2);    //public demo02.Student(java.lang.String)
        Object o2 = constructor2.newInstance("张三");
        System.out.println(o2);    //有一个参数的构造方法执行了
                                   //demo02.Student@7229724f

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

        //3.获取一个构造方法对象(私有,多个参数构造),并创建新对象
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class, int.class);
        System.out.println(declaredConstructor);    //private demo02.Student(java.lang.String,int)
        //取消语法检查
        declaredConstructor.setAccessible(true);
        Object o3 = declaredConstructor.newInstance("李四", 44);
        System.out.println(o3);     //私有多个参数的构造方法执行了
                                    //demo02.Student@4c873330
    }
}

===============================================================================================

public class Student {
    //定义构造方法
    public Student(){
        System.out.println("无参构造方法执行了");
    }

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

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

4. 通过反射获取成员变量(Field)并使用
(1)以前的方式:new 一个对象 —> 对象.成员变量名
(2)现在的方式:Student.class —> Class 类中的getField() —> Field 对象 —> 通过Field 对象中的set() get()方法给字段设置值,以及获取字段的值

A:获取所有成员变量
	public Field[] getFields() 获取所有的成员变量包含从父类继承过来的
	public Field[] getDeclaredFields() 获取所有的成员变量 包含私有的 也包含从父类继承过来的成员变量
B:获取单个成员变量
	public Field getField(String name)
	public Field getDeclaredField(String name)
C:为成员变量赋值
	void set(Object obj, Object value):将指定对象变量上此 Field 对象表示的字段设置为指定的新值 
		参数1:该类的对象,参数2:设置的值
D:获取成员变量的值
	Object get(Object obj):返回指定对象上此 Field 表示的字段的值 
		参数:该类的对象
/**
 * 通过反射获取成员变量
 */
public class TestDemo01 {
    public static void main(String[] args) throws Exception {
        //使用反射,先获取该类的字节码文件对象
        Class<?> aClass = Class.forName("demo03.Student");

        //1.获取Student类中所有成员,不包括私有的
        Field[] fields = aClass.getFields();
        for (Field field : fields) {
            System.out.println(field);

            //public java.lang.String demo03.Student.name
            //public int demo03.Student.age
        }

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

        //2.获取Student类中所有成员,包括私有的
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);

            //public java.lang.String demo03.Student.name
            //public int demo03.Student.age
            //private char demo03.Student.sex
        }

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

        //3.获取Student类中单个成员,不包括私有的
        Field fieldName = aClass.getField("name");
        System.out.println(fieldName);    //public java.lang.String demo03.Student.name
        Field fieldAge = aClass.getField("age");
        System.out.println(fieldAge);     //public int demo03.Student.age

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

        //4.获取Student类中单个成员,包括私有的
        Field fieldSex = aClass.getDeclaredField("sex");
        System.out.println(fieldSex);     //private char demo03.Student.sex

    }
}

===============================================================================================

/**
 * 通过反射获取成员变量并赋值
 */
public class TestDemo02 {
    public static void main(String[] args) throws Exception {
        //使用反射,先获取该类的字节码文件对象
        Class<?> aClass = Class.forName("demo03.Student");
        //1.通过反射创建一个类的对象
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Object obj = declaredConstructor.newInstance();

        /*
            给name设置值
         */
        //2.先获取到name字段的对象
        Field fieldName = aClass.getField("name");
        //3.使用set()方法设置值 参数1:该类的对象,参数2:设置的值
        fieldName.set(obj,"张三");
        //4.使用get()方法获取name的值  参数:该类的对象
        Object o = fieldName.get(obj);
        System.out.println(o);   //张三


        /*
            给age设置值
         */
        Field fieldAge = aClass.getField("age");
        fieldAge.set(obj,33);
        Object o2 = fieldAge.get(obj);
        System.out.println(o2);    //33
        
        /*
            给私有的sex设置值
         */
        Field fieldSex = aClass.getDeclaredField("sex");
        fieldSex.setAccessible(true);
        fieldSex.set(obj,'男');
        System.out.println(fieldSex.get(obj));    //男
    }
}

===============================================================================================

public class Student {
    public String name;
    public int age;
    private char sex;

    private Student() {

    }
}

5. 通过反射获取成员方法(Method)并使用
(1)以前的方式:new 一个对象 —> 对象.方法名
(2)现在的方式:Student.class —> Class 类中的getMethod() —> Method 对象 —> 通过Method对象中的invoke()方法使用成员方法

A:获取所有成员方法
	public Method[] getMethods() //获取所有的公共的成员方法不包含私有的 包含从父类继承过来的过来的公共方法
	public Method[] getDeclaredMethods()//获取自己的所有成员方法 包含私有的
B:获取单个成员方法
	public Method getMethod(String name,Class<?>... parameterTypes) //获取单个的方法 不包含私有的
						参数1: 方法名称  参数2:方法行参的class 对象
	public Method getDeclaredMethod(String name,Class<?>... parameterTypes) 获取单个方法包括私有的
C:执行方法
	Object invoke(Object obj, Object... args):对带有指定参数的指定对象调用由此 Method 对象表示的底层方法
			参数1:调用方法的对象  参数2...:参数的值
/**
 * 通过反射获取成员方法
 */
public class TestDemo01 {
    public static void main(String[] args) throws Exception {
        Class<?> aClass = Class.forName("demo04.Student");

        //1.获取类中所有的成员方法对象,获取到非私有的方法对象,包括从父类继承下来的方法对象也能够获取到
        Method[] methods = aClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);
            /*
                public java.lang.String demo04.Student.test(java.lang.String,int)
                public void demo04.Student.hehe()
                public void demo04.Student.haha(java.lang.String)
                public void demo04.Student.heihei(java.lang.String,int)
                public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
                public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
                public final void java.lang.Object.wait() throws java.lang.InterruptedException
                public boolean java.lang.Object.equals(java.lang.Object)
                public java.lang.String java.lang.Object.toString()
                public native int java.lang.Object.hashCode()
                public final native java.lang.Class java.lang.Object.getClass()
                public final native void java.lang.Object.notify()
                public final native void java.lang.Object.notifyAll()
             */
        }

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

        //2.获取所有的方法对象,包括私有的,但是不获取父类的方法对象
        Method[] declaredMethods = aClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
            /*
                public java.lang.String demo04.Student.test(java.lang.String,int)
                public void demo04.Student.hehe()
                public void demo04.Student.haha(java.lang.String)
                public void demo04.Student.heihei(java.lang.String,int)
                private java.lang.String demo04.Student.test2(java.lang.String,int)
             */
        }

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

        //3.获取单个的非私有方法对象
        Method heheMethod = aClass.getMethod("hehe");
        System.out.println(heheMethod);   //public void demo04.Student.hehe()

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

        //4.获取有参的方法对象 getMethod()方法的参数1:方法名 参数2:你方法上形参的数据类型的Class类型
        Method hahaMethod = aClass.getMethod("haha", String.class);
        System.out.println(hahaMethod);    //public void demo04.Student.haha(java.lang.String)

        Method heiheiMethod = aClass.getMethod("heihei", String.class, int.class);
        System.out.println(heiheiMethod);    //public void demo04.Student.heihei(java.lang.String,int)

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

        //5.获取单个的私有方法对象
        Method test2Method = aClass.getDeclaredMethod("test2", String.class, int.class);
        System.out.println(test2Method);     //private java.lang.String demo04.Student.test2(java.lang.String,int)

    }
}

===============================================================================================

/**
 * 通过反射获取成员方法并使用
 */
public class TestDemo02 {
    public static void main(String[] args) throws Exception {
        Class<?> aClass = Class.forName("demo04.Student");

        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Object obj = declaredConstructor.newInstance();

        //获取hehe方法对象
        Method heheMethod = aClass.getMethod("hehe");
        //执行hehe方法:Object invoke(Object obj, Object... args):对带有指定参数的指定对象调用由此 Method 对象表示的底层方法
        heheMethod.invoke(obj);   //无参,无返回值,公有的hehe方法执行了

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

        //有参的haha方法
        Method hahaMethod = aClass.getMethod("haha", String.class);
        hahaMethod.invoke(obj,"你好:");    //你好:有一个参数,无返回值,公有的haha方法执行了

        Method heiheiMethod = aClass.getMethod("heihei", String.class, int.class);
        heiheiMethod.invoke(obj,"张三:",33);    //张三:有两个参数,无返回值,公有的heihei方法执行了33

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

        //有参有返回值的test方法
        Method testMethod = aClass.getMethod("test", String.class, int.class);
        //返回的就是test()调用完毕后的返回值
        Object invoke = testMethod.invoke(obj, "李四:", 44);   //李四:有两个参数,有返回值,公有的test方法执行了44
        System.out.println(invoke);     //test

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

        //私有的,有参,有返回值的方法
        Method test2Method = aClass.getDeclaredMethod("test2", String.class, int.class);
        test2Method.setAccessible(true);    //王五:有两个参数,有返回值,私有的test2方法执行了55
        Object invoke2 = test2Method.invoke(obj, "王五:", 55);
        System.out.println(invoke2);    //test2

    }
}

===============================================================================================

public class Student {
    private Student(){

    }

    public void hehe(){
        System.out.println("无参,无返回值,公有的hehe方法执行了");
    }

    public void haha(String name){
        System.out.println(name + "有一个参数,无返回值,公有的haha方法执行了");
    }

    public void heihei(String name,int age){
        System.out.println(name + "有两个参数,无返回值,公有的heihei方法执行了" + age);
    }

    public String test(String name,int age){
        System.out.println(name + "有两个参数,有返回值,公有的test方法执行了" + age);
        return "test";
    }

    private String test2(String name,int age){
        System.out.println(name + "有两个参数,有返回值,私有的test2方法执行了" + age);
        return "test2";
    }

}

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

/**
 * 通过反射运行配置文件内容
 */
public class TestDemo01 {
    public static void main(String[] args) throws Exception {
        Properties properties = new Properties();
        properties.load(new FileReader("MyConfig.properties"));

        Class<?> aClass = Class.forName(properties.getProperty("className"));

        Object obj = aClass.getDeclaredConstructor().newInstance();

        //调用Cat类中的eat方法
        Method methodName = aClass.getDeclaredMethod(properties.getProperty("methodName"));
        methodName.invoke(obj);
    }
}

===============================================================================================

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

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

===============================================================================================

MyConfig.properties:
		className = demo05.Cat
		methodName = eat

7. 通过反射越过泛型检查

/**
 * 通过反射越过泛型检查
 */
public class TestDemo01 {
    public static void main(String[] args) throws Exception {
        //泛型:只在编译期有效,运行期就擦除了
        ArrayList<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("王五");
//        list.add(111);   这样添加就会报错

        //但是可以通过泛型机制,越过泛型检测

        //1.获取字节码文件对象
        Class<? extends ArrayList> aClass = list.getClass();
        //2.获取add()方法
        Method addMethod = aClass.getDeclaredMethod("add", Object.class);
        //3.调用add()方法执行
        addMethod.invoke(list,111);
        addMethod.invoke(list,3.14);
        addMethod.invoke(list,true);

        System.out.println(list);   //[张三, 李四, 王五, 111, 3.14, true]

    }
}

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

public class TestDemo01 {
    public static void main(String[] args) throws Exception {
        Class<?> aClass = Class.forName("demo07.Teacher");
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Object obj = declaredConstructor.newInstance();

        //设置值
        MyUtils.setProperty(obj,"name","张三");
        MyUtils.setProperty(obj,"age",33);

        //获取值
        Object name = MyUtils.getProperty(obj, "name");
        System.out.println(name);   //张三

        Object age = MyUtils.getProperty(obj, "age");
        System.out.println(age);    //33

    }
}

===============================================================================================

public class MyUtils {

    /**
     * 设置属性的值
     *
     * @param obj  要给哪个对象设置值,就把哪个对象传进来
     * @param propertyName  成员变量名
     * @param value   给成员变量设置具体的值
     */
    public static void setProperty(Object obj,String propertyName,Object value) throws Exception {
        Class<?> aClass = obj.getClass();
        Field declaredField = aClass.getDeclaredField(propertyName);
        declaredField.setAccessible(true);
        declaredField.set(obj,value);
    }

    /**
     * 获取属性的值
     *
     * @param obj  要获取哪个对象设置值,就把哪个对象传进来
     * @param propertyName  成员变量名
     * @return   返回该值
     */
    public static Object getProperty(Object obj,String propertyName) throws Exception {
        Class<?> aClass = obj.getClass();
        Field declaredField = aClass.getDeclaredField(propertyName);
        declaredField.setAccessible(true);
        Object value = declaredField.get(obj);
        return value;
    }
}

===============================================================================================

public class Teacher {
    private Teacher(){

    }

    private String name;
    private int age;
}

2. 枚举

1. 枚举的定义注意事项
(1)定义枚举类要用关键字enum
(2)所有枚举类都是Enum的子类,但是不要显式的写出来
(3)枚举类的第一行上必须是枚举项,最后一个枚举项后的分号是可以省略的,但是如果枚举类有其他的东西,这个分号就不能省略。建议不要省略
(4)枚举类可以有构造器,但必须是private的,它默认的也是private的
(5)枚举类也可以有抽象方法,但是枚举项必须重写该方法

2. 枚举定义的代码示例

/**
	自定义枚举
*/
public class TestDemo01 {
    public static void main(String[] args) {
        Direction after = Direction.AFTER;
        Direction before = Direction.BEFORE;
        Direction left = Direction.LEFT;
        Direction right = Direction.RIGHT;

        System.out.println(after);   //demo01.Direction@4f023edb
        System.out.println(before);   //demo01.Direction@3a71f4dd
        System.out.println(left);   //demo01.Direction@7adf9f5f
        System.out.println(right);    //demo01.Direction@85ede7b
    }
}

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 TestDemo01 {
    public static void main(String[] args) {
        Direction after = Direction.AFTER;
        Direction before = Direction.BEFORE;
        Direction left = Direction.LEFT;
        Direction right = Direction.RIGHT;

        System.out.println(after);   //AFTER
        System.out.println(before);   //BEFORE
        System.out.println(left);    //LEFT
        System.out.println(right);   //RIGTH
    }
}

enum Direction{
    //枚举项:必须位于第一行 最后一个枚举项可以使用 ; 分号结束
    BEFORE,AFTER,RIGHT,LEFT;

    //枚举里面的构造方法,必须是私有修饰的,默认也是私有的

    Direction() {
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值