package com.day14.json;

import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.Properties;
import java.util.Random;

/**
 * Author: Json
 * Date: 2021/10/6
 **/
public class JsonTest {
    public static void main(String[] args) {
        System.out.println("加油把");
        //java 反射机制

    }

    @Test
    public void test1(){
        //反射之前 的操作
        //1.创建对象
        Person person=new Person("JSON",12);

        //2. 调用内部属性 方法
        person.show();

        //在person类的外部不能调用私有的方法或属性

    }

    @Test
    public void test2() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        //反射之后
       Class c=  Person.class;
       // 1.通过反射 创建Person类的对象
       Constructor constructor=c.getConstructor(String.class,int.class);

       Object object= constructor.newInstance("Json",123);
       System.out.println(object.toString());
       //强转
        Person person =(Person) object;
        System.out.println(person.getName());
        //2.通过反射 调用对象的指定的方法和属性
        //调用更改类里的属性
        Field age=c.getDeclaredField("age");
        age.set(person,12);
        System.out.println(person.toString());

        //调用类中方法
        Method show=c.getDeclaredMethod("show");
        show.invoke(person);

        //通过反射是可以调用Person类中的私有结构的
        //如果 私有方法 私有构造器 私有属性
        //调用私有的构造器
        Constructor constructor1= c.getDeclaredConstructor(String.class);
        //激活私有方法权限调用 保持当前属性是可访问的
          constructor1.setAccessible(true);
        Person person1=(Person) constructor1.newInstance("whL");
        System.out.println(person1.getName());

        //调用修改私有属性
        Field name=c.getDeclaredField("name");
        name.setAccessible(true);
        name.set(person1,"hahah");
        System.out.println(person1);

        //调用私有方法
        Method eat=c.getDeclaredMethod("eat", String.class);
        eat.setAccessible(true);
        String naa= (String) eat.invoke(person1,"21312321");
        System.out.println(naa);


    }

    // 通过直接new的方式或反射的方式都可以调用公共的结构 开发中用哪个?
    //建议 直接new的方式
    //什么时候会使用反射的方式? 反射的特征 动态性
    //比如 api 接口请求地址 就是利用反射实现的
    //根据url来的路径 来new相对应的对象
    // 反射机制与面向对象中的封装性是不是矛盾的?如何看待两个技术
    // 不矛盾
    @Test
    public void  test3() throws ClassNotFoundException {
        //class实例的理解
        //调用 class
        //方式一
        Class<Person> c=Person.class;

        //方式二  通过运行时对象 调用
        Person person=new Person();
        Class person1=person.getClass();

        //方式三  通过class 静态方法 获取 class
        Class.forName("com.day14.json.Person");
    }

    //读取配置文件
    @Test
    public void test4() throws IOException {
        //默认在当前module下的配置文件
        Properties properties=new Properties();
//         FileInputStream fis=new FileInputStream("jdbc.properties");
//         properties.load(fis);

         //classLoader 类的加载器 可以一层一层找 类的加载器
        // 根据加载器的位置 找相对应的配置文件
         //默认在src下的配置文件
        ClassLoader classLoader=ClassLoaderTest.class.getClassLoader();
        InputStream is=classLoader.getResourceAsStream("jdbc1.properties");
        properties.load(is);

    }


    //体验反射的动态性
    @Test
    public void test5(){
        int num=new Random().nextInt(3);
        //根据运行 造相对应的类
        String classPath;
        switch (num){
            case 0:
                classPath= "java.util.Date";
                break;
            case 1:
                classPath="java.lang.Object";
                break;
            case 2:
                classPath="com.day14.json.Person";
                break;
            default:
                throw new IllegalStateException("Unexpected value: " + num);
        }

        try {
            Object object=new JsonTest().getInstance(classPath);
            System.out.println(object);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Object getInstance(String classPath) throws Exception {
        Class c=Class.forName(classPath);
        return  c.newInstance();
    }

    //获取当前运行时类的属性结构
    @Test
    public void test6(){
        Class c= com.day14.json1.Person.class;
        //获取属性结构
        //只能获取 当前运行时类及其父类中的公共的(Public)属性结构
        Field[] fields = c.getFields();
        for (Field f:fields){
            System.out.println(f);
        }
        System.out.println("*****************");
        //只能获取 当前运行时类属性结构  (不包含父类)
        Field[] declaredFields = c.getDeclaredFields();
        for (Field f:declaredFields){
            System.out.println(f);
        }
    }

    //权限修饰符   数据类型 变量名
    @Test
    public void test7(){
        Class c= com.day14.json1.Person.class;
        Field[] declaredFields = c.getDeclaredFields();
        for (Field f:declaredFields){
            //权限修饰符
            System.out.println(f.getModifiers()); //返回int
            System.out.println(Modifier.toString(f.getModifiers()));  //转成字符串
            // 数据类型
            System.out.println(f.getType());
            //变量名
            System.out.println(f.getName());
        }
    }


    //获取运行时类的方法结构
    @Test
    public void test8(){
        Class c= com.day14.json1.Person.class;
        //获取当前运行时类及其父类的所有的公共方法
        Method[] Methods = c.getMethods();
        for (Method m:Methods){
            System.out.println(m);
        }
        System.out.println("******************");
        //获取当前运行时类的所有的方法(不包含父类)
        Method[] declaredMethods = c.getDeclaredMethods();
        for (Method m:declaredMethods){
            System.out.println(m);
        }
    }


    //获取运行时类的泛型的父类 和普通父类
    @Test
    public void test9(){
       Class c= com.day14.json1.Person.class;
      // 普通父类 c.getSuperclass();
        Type genericSuperclass = c.getGenericSuperclass();  //泛型的父类
        System.out.println(genericSuperclass);

        //获取父类的泛型的类型
        ParameterizedType parameterizedType= (ParameterizedType) genericSuperclass;
        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
        System.out.println(actualTypeArguments[0].getTypeName());
    }

    //获取运行时类的接口
    //用的时候找

    //获取运行时类的包
    //用的时候找

    //获取运行时类的注解
    @Test
    public void test10(){
        //通过反射读取注释内容后 去做一些事情
        Class c= com.day14.json1.Person.class;
        Annotation[] annotations = c.getAnnotations();

        for (Annotation annotation:annotations){
            System.out.println(annotation);
        }
    }

    //调用运行时类的指定的结构 属性 方法 构造器
    @Test
    public void test11() throws NoSuchFieldException, IllegalAccessException, InstantiationException {
        //通过反射读取注释内容后 去做一些事情
        Class c= com.day14.json1.Person.class;
        Field id = c.getField("id");
        System.out.println(id);
        //声明对象
        //Person person = (Person) c.newInstance();
        //设置 对象中的 属性的值
      //  id.set(person,2123);

        //获取对象中的 属性的值
//        int id1= (int) id.get(person);
//        System.out.println(id1);

        //如果获取私有的
//        Field name = c.getDeclaredField("name");
//        //设置属性可访问的权限
//        name.setAccessible(true);
//        name.set(person,"json");
//        name.get(person);

    }




}