反射
    /*
    反射 : JAVA有着一个非常突出的动态相关机制:Reflection。

    注意 :
        反射发生在程序运行期间

    Java反射机制,可以实现以下功能:
        ①在运行时判断任意一个对象所属的类;
        ②在运行时构造任意一个类的对象;
        ③在运行时判断任意一个类所具有的成员变量和方法;
        ④在运行时调用任意一个对象的方法;
        ⑤生成动态代理;

    反射的源头 : Class对象
        Class : 类类实例表示正在运行的Java应用程序中的类和接口。

        获取反射源头Class类型对象的方式 :
            1.类名.class
            2.对象.getClass()
            3.Class.forName("权限定名") --> 推荐

        特点 :
            Class对象在类加载到内存之后就已经存在,独一份的,不会改变的
 */

public class Class001_Reflect {
            public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, IOException {
                Properties pro = new Properties();
                pro.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("classname.properties"));
                Person p = (Person) Class.forName(pro.getProperty("classname")).newInstance();
                p.smile();
        
                System.out.println(p.getClass().getName());
        
                Class<String> cls1 = String.class;
                System.out.println(cls1.toString());
        
                Class cls2 = "abc".getClass();
                System.out.println(cls1==cls2);
            }
        }
        
        class Person{
            void smile(){
                System.out.println("微笑");
            }
        }
        
        class Student extends Person{
            void smile(){
                System.out.println("开怀大笑...");
            }
        }
        
        class Teacher extends Person{
            void smile(){
                System.out.println("智者的微笑...");
            }
        }

    /*
    Class类常用方法

 */

public class Class002_Reflect {
            public static void main(String[] args) {
                Class<String> cls = String.class;
                //String getName() 返回此 类对象表示的实体名称(类,接口,数组类,基本类型或void),作为 String 。
                //String getSimpleName() 返回源代码中给出的基础类的简单名称。
                System.out.println(cls.getName());
                System.out.println(cls.getSimpleName());
        
                //int getModifiers() 返回此类或接口的Java语言修饰符,以整数编码。
                System.out.println(cls.getModifiers());
                System.out.println(Modifier.toString(cls.getModifiers()));
        
                //boolean isInterface() 确定指定的 类对象是否表示接口类型。
                //boolean isPrimitive() 确定指定的 类对象是否表示基本类型。
                //boolean isEnum() 当且仅当此类在源代码中声明为枚举时返回true。
        
                //获取基本数据类型的Class对象
                Class<Integer> cls1 = int.class;
                Class<Integer> cls2 = Integer.class;
                Class<Integer> cls3 = Integer.TYPE;
        
                System.out.println(cls1);
                System.out.println(cls2);
                System.out.println(cls1==cls2);
                System.out.println(cls1==cls3);
        
                //类<? super T> getSuperclass() 返回 类表示此所表示的实体(类,接口,基本类型或void)的直接超类 类 。
                Class superClass = cls.getSuperclass();
                System.out.println(superClass);
                System.out.println(superClass==Object.class);
        
                //类<?>[] getInterfaces() 返回由此对象表示的类或接口直接实现的接口。
                Class[] arr = cls.getInterfaces();
                System.out.println(Arrays.toString(arr));
            }
        }

    /*
    反射创建对象
        1.Class--->newInstance 创建Class对象所有表示类型的实例--->默认调用调用空构造
        2.a)先获取一个构造器
            构造器<T> getConstructor(类<?>... parameterTypes) 返回一个 构造器对象,该对象反映此 类对象所表示的类的指定公共构造函数。
            构造器<?>[] getConstructors() 返回一个包含 构造器对象的数组, 构造器对象反映了此 类对象所表示的类的所有公共构造函数。
            以上两个方法只能获取公共的
            构造器<T> getDeclaredConstructor(类<?>... parameterTypes) 返回一个 构造器对象,该对象反映此 类对象所表示的类或接口的指定构造函数。
            构造器<?>[] getDeclaredConstructors() 返回 构造器对象的数组, 构造器对象反映由此 类对象表示的类声明的所有构造函数。

          b)Constructor-->newInstance 创建对象并调用当前构造器为对象初始化信息
 */

public class Class003_Constructor {
            public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
                //Class对象
                Class<Emp> cls = Emp.class;
        
                //创建对象
                //1.Class--->newInstance
                Emp emp = cls.newInstance();
                System.out.println(emp);
        
                //2)获取构造器
                Constructor[] cons = cls.getConstructors();
                Arrays.stream(cons).forEach(System.out::println);
        
                Constructor<Emp> con = cls.getDeclaredConstructor(int.class,String.class);
                System.out.println(con);
                System.out.println(Modifier.toString(con.getModifiers()));
                System.out.println(con.getName());
                System.out.println(con.getParameterCount());
        
                //创建对象调用指定构造器
                con.setAccessible(true);  //忽略权限
                Emp emp2 = con.newInstance(1001,"zhangsan");
                System.out.println(emp2);
            }
        }

    /*
    反射操作成员 :
        成员变量
            1)获取属性
                字段 getField(String name) 返回 字段对象,该对象反映此 类对象表示的类或接口的指定公共成员字段。
                字段[] getFields() 返回一个包含 字段对象的数组, 字段对象反映此 类对象所表示的类或接口的所有可访问公共字段。
                字段 getDeclaredField(String name) 返回 字段对象,该对象反映此 类对象表示的类或接口的指定声明字段。
                字段[] getDeclaredFields() 返回 字段对象的数组, 字段对象反映由此 类对象表示的类或接口声明的所有字段。

            2)使用属性
                设置属性值
                    void set(Object obj, Object value) 将指定对象参数上此 字段对象表示的字段设置为指定的新值。
                获取属性值
                    Object get(Object obj) 返回指定对象上此 字段表示的字段的值。
        成员方法
            1)获取方法
                方法 getMethod(String name, 类<?>... parameterTypes) 返回 方法对象,该对象反映此 类对象表示的类或接口的指定公共成员方法。
                方法[] getMethods() 返回一个包含 方法对象的数组, 方法对象反映此 类对象所表示的类或接口的所有公共方法,包括由类或接口声明的那些以及从超类和超接口继承的那些。
                方法 getDeclaredMethod(String name, 类<?>... parameterTypes) 返回 方法对象,该对象反映此 类对象表示的类或接口的指定声明方法。
                方法[] getDeclaredMethods() 返回一个包含 方法对象的数组, 方法对象反映此 类对象表示的类或接口的所有已声明方法,包括public,protected,default(package)访问和私有方法,但不包括继承的方法。

            2)调用方法
                Object invoke(Object obj, Object... args) 在具有指定参数的指定对象上调用此 方法对象表示的基础方法。
 */

public class Class004_Member {
            public static void main(String[] args) throws Exception {
                Emp emp = new Emp(1,"lucy",8888);
                Class<Emp> cls = Emp.class;
                testMethod(cls,emp);
            }
        
            public static void testMethod(Class<Emp> cls,Emp emp) throws Exception {
                //1)获取方法
                Method[] arr = cls.getMethods();
                Arrays.stream(arr).forEach(System.out::println);
        
                Method method = cls.getMethod("getName");
                System.out.println(method);
        
                Method method2 = cls.getDeclaredMethod("testStatic",int.class,boolean.class);
        
                //2)执行方法
                String name = (String) method.invoke(emp);
                System.out.println(name);
        
                method2.setAccessible(true);
                Object returnValue = method2.invoke(null,100,false);
                System.out.println(returnValue);
            }
        
            public static void testField(Class<Emp> cls,Emp emp) throws NoSuchFieldException, IllegalAccessException {
                //1)获取属性
                Field field = cls.getDeclaredField("name");
        
                //私有的属性忽略权限使用
                field.setAccessible(true);
        
                //2)操作属性值
                field.set(emp,"卢西");
        
                Object value = field.get(emp);
                System.out.println(value);
            }
        }