反射原理

131 篇文章 1 订阅

一、反射原理的概念

反射:将类中的各个成分映射成不同的对象

   反射原理举例:

java.lang.Class:将java中的任何一个类都可以统一使用

   反射的深层原理:

    如果对于一个类,在编译期间不知道其中的结构,而希望在运行期间去探索、得知内部的结构,则需要使用反射原理

    如果要使用反射原理去探索,则需要先加载这个类

获取Class对象的方式

    Class.forName("类的全局限定名")

    类名.class

    调用对象实例中的getClass方法

    通过类加载器获取

注意:

     方法一不执行静态块和动态构造块

     方法二执行静态快、不执行动态构造块

     方法三需要创建对象,静态快和动态快都会执行

     四种方式得到的Class对象都是同一个对象,因为类只会被加载一次,因此要判断是否为某一类型,用==比较即可

 

java反射原理中有哪些核心类?分别有什么作用?

通配符和泛型

   无限定通配符?

   使用一个问号表示类型参数,虽然我们可以在用到的使用再指定具体的类型,但是如果在用到的时候还不能确定具体的类型,就需要依靠通配符来解决。即引入了通配符,使得泛型变成协变的。

上边界限定通配符? extends ……      

    List<? extends Number> list1 = new ArrayList<Number>();    

    List<? extends Number> list2 = new ArrayList<Integer>();    

    List<? extends Number> list3 = new ArrayList<Long>();

下边界限定通配符? super ……      

    List<? super Integer> list4 = new ArrayList<Integer>();    

    List<? super Integer> list5 = new ArrayList<Number>();

    T、E、V、K等

      以List或Map为例,其中的泛型参数定义为<E>或者<K,V>,类中的方法中甚至将E、K、V等作为参数类型使用。此处和?最大的区别在于,这个类型其实是一个确定的类型,调用时指定什么类型就是什么类型,例如List<String>,编译期间会将E自动擦除,替换成String,类中所有出现E的地方,也替换成String。

      这一类参数相当于实现了类中一些类型的同统一(包括参数、返回值等)

/***
 * extends
 *
 * super
 */
public class Demo02 {

    public static void main(String[] args) {
//        List<Integer> list1=new ArrayList<Integer>();
//        List<Double> list2=new ArrayList<Double>();

        //? extends Number:类型可以是Number类型或其子类型
        List<? extends Number> list1 = new ArrayList<Number>();
        List<? extends Number> list2 = new ArrayList<Integer>();
        List<? extends Number> list3 = new ArrayList<Double>();
        //List<? extends Number> list4=new ArrayList<String>();

        //? super Integer:Integer类型本身或其父类
        List<? super Number> list5 = new ArrayList<Number>();
        List<? super Integer> list6 = new ArrayList<Number>();


    }
}

   

import java.util.LinkedList;
import java.util.List;

/**
 * T/E/V/K:泛型参数,
 */
public class Demo03 {
    public static void main(String[] args) {
        MyStack<Integer> myStack = new MyStack<>();
        myStack.add(1);
        myStack.add(2);
        myStack.add(3);
        myStack.print();

        MyStack<String> myStack2 = new MyStack<>();
        myStack2.add("1");
        myStack2.add("2");
        myStack2.add("3");
        myStack2.print();

    }
}

/**
 * 表示一个栈
 */
class MyStack<E> {
    /**
     * E:表示的是一个泛型,类中使用的泛型要在类上声明出来
     * 泛型参数在运行期间是不存在的,在编译成字节码的时候,会使用具体的类型来擦除泛型参数
     * 泛型参数其实是一种固定的数据类型
     */
    private List<E> list = new LinkedList<>();

    //    private List<F> list2 = new LinkedList<>();
    public void add(E o) {

        //泛型参数不能当做类去使用,也不能直接实例化
        //E e=new E();

        list.add(o);
    }

    /**
     * 该方法实现的操作:打印出当前集合中的元素值
     * <p>
     * 如果元素是整数则,每个+5
     * 如果是字符串,则前面加上str前缀
     */
    public void print() {
        for (E ele : list) {
            if (ele.getClass() == Integer.class) {
               int num=(Integer) ele;
               System.out.println(num+5);
            }
            if (ele.getClass() == String.class) {
                String str=(String) ele;
                System.out.println("str"+str);
            }
        }
    }

    /**
     * 如果泛型参数只是某个方法用到,可以不用在类中声明这个泛型参数
     * <p>
     * 可以直接在方法中声明这个泛型参数
     *
     * @param param1
     */
    public <T, R, S> S method(T param1, R param2) {
        return null;
    }


}

java.lang.Class:将java中的任何一个类都可以统一使用Class类型来表示,表示的是一个类的完整信息,所有反射操作的开始都是在这里开始的。

import com.sy.reflect.entity.Emp;

/**
 * java.lang.Class:当一个类被虚拟机加载以后,会得到一个Class对象
 * Class对象是反射中其它操作的起源,通过Class对象可以得到类中的属性、方法、构造器等对象
 */
public class Demo01 {
    public static void main(String[] args) {
        try {
            //Class.forName("类的全局限定名")
            //全局限定名=类所在的包+类名
            //注意:不能使用具体的类型作为泛型,应该使用?
            //?:类型不确定的时候,使用?解决
            //通过字符串指定类名,虚拟机不知道最后会得到什么类型
            Class<?> clazz1 = Class.forName("com.sy.reflect.entity.Emp");
            System.out.println(clazz1);
            //类名.class
            Class<Emp> clazz2 = Emp.class;
            System.out.println(clazz2);
            //对象实例.getClass();
            //由于在编译期间还没产生对象,虚拟机也不知道会产生什么对象
            //所以类型不确定
//            Class<?> clazz3 = new Emp().getClass();
//            System.out.println(clazz3);

            //由于Class对象在第一次加载以后会进行缓存,所以
            //重复加载不会得到新的对象
            System.out.println(clazz1 == clazz2);
//            System.out.println(clazz3 == clazz3);
//            System.out.println(clazz1 == clazz3);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

   

import com.sy.reflect.entity.Emp;

import java.lang.reflect.Modifier;
import java.util.Arrays;

/**
 * Class对象的用法
 */
public class Demo04 {
    public static void main(String[] args) {
        try {
            Class<?> clazz = Class.forName("com.sy.reflect.entity.Emp");
            //获取访问修饰符
            //(1)获取的是访问修饰符对应的整数值
            System.out.println(clazz.getModifiers());
            //(2)可以将访问修饰符对应的数值转换为字符串
            System.out.println(Modifier.toString(clazz.getModifiers()));

            //获取类名
            //完整类名=包名+类名
            System.out.println(clazz.getName());
            //类名
            System.out.println(clazz.getSimpleName());

            //获得了Class对象以后,可以调用Class对象的newInstance方法来创建对象
            //权限足够的前提之下,调用的其实是类的无参构造方法
            Object emp = clazz.newInstance();
            System.out.println(emp);

            //判断clazz是否为基本数据类型
            System.out.println(Integer.class.isPrimitive());
            System.out.println(int.class.isPrimitive());
            System.out.println(void.class.isPrimitive());

            //判断是否为数组类型
            System.out.println(new int[]{1, 2, 3}.getClass().isArray());
            //isArray()方法不能用于集合
            System.out.println(Arrays.asList(1, 2, 3).getClass().isArray());


        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }
}

java.lang.reflect.Field:将类中的属性映射成该类型

import com.sy.reflect.entity.Emp;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

/**
 * java.lang.reflect.Field:表示类中的属性
 * 通过Field对象可以获取属性的信息,也可以对属性的值进行修改
 */
public class Demo06 {
    public static void main(String[] args) {
        try {
            Class<?> clazz = Class.forName("com.sy.reflect.entity.Emp");
            //获取所有的属性
            //clazz.getFields():只返回公有属性
            Field[] fields = clazz.getFields();
            for (Field field : fields) {
                System.out.println(field);
            }
            System.out.println("========================");
            //clazz.getDeclaredFields():返回所有的属性
            fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                System.out.println(field);
            }
            System.out.println("========================");
            //获取某一个属性
            //clazz.getField只能获取公有的属性
            Field field = clazz.getField("gender");
            System.out.println(field);

            //clazz.getDeclaredField可以返回任何一个属性
            field = clazz.getDeclaredField("name");
            System.out.println(field);
            System.out.println("========================");
            //Field对象的用法
            //获取访问修饰符
            System.out.println(Modifier.toString(field.getModifiers()));
            //获取属性的数据类型,返回的是Class<?>
            System.out.println(field.getType());
            //获取属性名
            System.out.println(field.getName());
            //也可以使用Filed对象对属性进行赋值操作
//            Emp emp=new Emp();
//            emp.name="";
            //emp.属性名=值;

            //第一个参数:要被修改属性的对象,第二个参数:要修改进去的属性值
            Object obj=clazz.newInstance();
            System.out.println(obj);
            //打破封装,访问私有属性
            field.setAccessible(true);
            field.set(obj,"Smith" );
            //用完后,还原封装
            field.setAccessible(false);
            System.out.println(obj);


            //Emp emp=new Emp();
            //emp.属性名="";
            //emp.setxxx();

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }

    }
}

java.lang.reflect.Construtor:将类中的构造器映射成该类型

import com.sy.reflect.entity.Emp;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;

/**
 * java.lang.reflect.Constructor:
 * 构造器对象,获取构造器对象以后,可以对类进行实例化操作
 */
public class Demo05 {
    public static void main(String[] args) {
        try {
            Class<?> clazz = Class.forName("com.sy.reflect.entity.Emp");
            //获取所有的构造器
            //getConstructors:获取所有的公有构造方法
            Constructor<?>[] constructors = clazz.getConstructors();
            for (Constructor constructor : constructors) {
                System.out.println(constructor);
            }
            System.out.println("=================================");
            //getDeclaredConstructors:获取所有的构造方法
            constructors = clazz.getDeclaredConstructors();
            for (Constructor constructor : constructors) {
                System.out.println(constructor);
            }
            System.out.println("=================================");
            //获取某一个构造器
            //getConstructor方法的参数应该要执行构造器的参数列表的类型
            Constructor<?> constructor = clazz.getConstructor();
            System.out.println(constructor);
            constructor = clazz.getConstructor(String.class, int.class, char.class, String.class, double.class);
            System.out.println(constructor);
            //getConstructor只能获取公有的构造方法
//            constructor = clazz.getConstructor(String.class);
//            System.out.println(constructor);

            //getDeclaredConstructor();获取任何修饰词修饰的构造方法
            constructor = clazz.getDeclaredConstructor(String.class);
            System.out.println(constructor);

            System.out.println("==================");
            //获取构造器的访问修饰符
            System.out.println(Modifier.toString(constructor.getModifiers()));
            //通过构造器进行实例化操作

            constructor = clazz.getConstructor(String.class, int.class, char.class, String.class, double.class);
            //通过newInstance进行实例化,方法参数就是构造器中要传入的属性的初始值
            //调用public构造
            Object obj = constructor.newInstance("Tom", 1001, 'F', "Java开发", 6000);
            System.out.println(obj);
            System.out.println("============================");
            //调用非public构造
            constructor = clazz.getDeclaredConstructor(String.class);
            //打破封装,使得原来权限不够的构造方法可以被调用
            constructor.setAccessible(true);
            obj = constructor.newInstance("Jack");
            //用完以后,再将封装还原
            constructor.setAccessible(false);
            System.out.println(obj);

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }


    }
}

java.lang.reflect.Method:将类中的成员方法映射成该类型

import com.sy.reflect.entity.Emp;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * java.lang.reflect.Method:用来表示成员方法
 * 可以用来获取方法信息,通过Method对象可以调用类中的方法
 */
public class Demo07 {
    public static void main(String[] args) {
        try {
            Class<?> clazz = Class.forName("com.sy.reflect.entity.Emp");
            //获取类中的所有方法
            //获取当前类的所有公有方法 clazz.getMethods();
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                System.out.println(method);
            }
            System.out.println("=========================");
            //获取当前类中无论何种修饰符的方法
            methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                System.out.println(method);
            }
            System.out.println("=========================");

//            getMethod(本类及父类中public的)
            //一个类中同名方法可能有多个(重载)
            //所以在指定方法名的同时要指定方法的参数列表
            Method method = clazz.getMethod("method01", int.class, String.class);
            System.out.println(method);
//             getDeclaredMethod(本类中无论何种访问修饰符)
            method = clazz.getDeclaredMethod("method01", Integer.class, String.class);
            System.out.println(method);
            System.out.println("=========================");
            //获取的方法的访问修饰符
            System.out.println(Modifier.toString(method.getModifiers()));
            //获取方法的返回类型
            Class<?> returnType = method.getReturnType();
            System.out.println(returnType.getName());
            System.out.println(returnType.getSimpleName());
            //获取方法名
            System.out.println(method.getName());
            //获取方法的参数类型
            Class<?>[] parameterTypes = method.getParameterTypes();
            for (Class<?> parameterType : parameterTypes) {
                System.out.println(parameterType);
            }

            //通过Method对象调用方法
//            Emp emp=new Emp();
//            emp.method01(, );
            //emp.方法名(参数1,参数2,参数3,……,参数N);
            Object obj = clazz.newInstance();
            //invoke返回的结果就是方法的返回值
            //打破封装后,调用私有方法
            method.setAccessible(true);
            Object result = method.invoke(obj, 3, "Hello");
            //封装还原
            method.setAccessible(false);
            System.out.println(result);
            System.out.println("==========================");
            //注意:void返回方法的invoke返回的值
            method = clazz.getDeclaredMethod("method03", int.class);
            System.out.println(method);
            result = method.invoke(obj, 1);
            //空参方法的invoke返回为null
            System.out.println(result);
            //需要区分是带返回类型的方法返回null还是方法本身就是void方法
            if (method.getReturnType() == void.class) {
                System.out.println("方法返回类型为void");
            } else {
                System.out.println("方法有指定返回类型");
            }


        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

类加载器的缓存机制:

    类装载器还使用了Cache(缓存)机制,如果缓存中有这个Class则直接返回它,如果没有,则从文件中读取并转换成Class对象,同时再将它Cache起来。这样做的目的是保证Class对象只被包装一次。这也就是为什么在修改了java代码之后,需要重新启动才会生效的原因。

    结合同步代码块中的类名.class全局锁               记忆。

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值