黑马程序员,看Java基础视频笔记:反射

反射      

反射就是把Java中各种成分映射成相应的Java类,它通过字节码文件对象,将类的字段,方法,构造器等映射成相应的类,并进行各自的操作。例如,一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示java类的class类显然要提供一系列的方法来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应的实例对象来表示,它们是Field、Method、Contructor、Package等等。简单说:反射技术可以对一个类进行解剖。

反射的好处:

反射属于JDk1.5版本之后出现的新特性,它的出现大大的增强了程序的扩展性。

反射的基本步骤:

1、获得Class对象,就是获取到指定的名称的字节码文件对象。
2、实例化对象,获得类的属性、方法或构造函数。
3、访问属性、调用方法、调用构造函数创建对象。

获取class类对象的三种方式:

1、通过每个对象都具备的方法getClass来获取。弊端:必须要创建该类对象,才可以调用getClass方法。

2、每一个数据类型(基本数据类型和引用数据类型)都有一个静态的属性class。弊端:必须要先明确该类。

前两种方式不利于程序的扩展,因为都需要在程序使用具体的类来完成。

3、使用的Class类中的方法,静态的forName方法。指定什么类名,就获取什么类字节码文件对象,这种方式的扩展性最强,只要将类名的字符串传入即可。

// 1. 根据给定的类名来获得                用于类加载
String classname = "cn.itcast.reflect.Person";// 来自配置文件
Class clazz = Class.forName(classname);// 此对象代表Person.class

 // 2. 如果拿到了对象,不知道是什么类型   用于获得对象的类型
Object obj = new Person();
Class clazz1 = obj.getClass();// 获得对象具体的类型

 // 3. 如果是明确地获得某个类的Class对象  用于传参
Class clazz2 = Person.class;  


反射的用法:

1)、我们要获得java类的各个组成部分,首先需要获得类的Class对象,获得Class对象的三种方式:
        Class.forName(classname)        用于做类加载
        obj.getClass()                                用于获得对象的类型
        类名.class                            用于获得指定的类型,传参用

2)、反射类的成员方法:Method类
        Class clazz = Person.class;
        Method method = clazz.getMethod(methodName, new Class[]{paramClazz1, paramClazz2});
        method.invoke();
       

3)、反射类的构造函数:Constructor类

        Constructor类代表某个类中的一个构造方法。

        Constructor con = clazz.getConstructor(new Class[]{paramClazz1, paramClazz2,...})
        con.newInstance(params...)

4)、反射类的属性,也称成员变量:Field类

       Field类代表某个类中的一个成员变量。getField只能提供获取到可见的值,而getDeclaredField只要声明过的,都可以获取到。

        Field field = clazz.getField(fieldName);
        field.setAccessible(true);

        field.setObject(value);

暴力反射

暴力反射可以获取私有变量,使用setAccessible方法,它就好比有两扇门上面的两把锁,我有第一把锁的钥匙,但是没有第二把锁的钥匙,我要进第二个门怎么办?这时候只能用工具把锁强行拆掉,我才能进去。而这个工具就是setAccessible(),类里面的私有变量,通过普通的getField反射无法获得,只有通过getDeclaredField()获得,然后利用setAccessible方法访问,这个获取和访问的过程就是暴力访问。

    Field fieldX = ReflectPoint.class.getDeclaredField("x");
    fieldX.setAccessible(true);


创建指定类的对象:

创建对象的两种方式(其实就是对象在进行实例化时的初始化方式):

1,调用空参数的构造函数:使用了Class类中的newInstance()方法。
2,调用带参数的构造函数:先要获取指定参数列表的构造函数对象,然后通过该构造函数的对象的newInstance(实际参数) 进行对象的初始化。

注意:

第二种方式,必须要先明确具体的构造函数的参数类型,不便于扩展。所以一般情况下,被反射的类,内部通常都会提供一个公有的空参数的构造函数。

 //第一种方式创建对象:该实例化对象的方法调用就是指定类中的空参数构造函数,给创建对象进行初始化。当指定类中没有空参数构造函数时,

Object obj = clazz.newInstance();


        //第二种方式创建对象:既然类中没有空参数的构造函数,那么只有获取指定参数的构造函数,用该函数来进行实例化。
                
          public static void method_2() throws Exception {                <pre name="code" class="java">                //获取一个带参数的构造器。
Class clazz = Class.forName("cn.itcast.bean.Person"); Constructor constructor = clazz.getConstructor(String.class,int.class); //想要对对象进行初始化,使用构造器的方法newInstance(); Object obj = constructor.newInstance("zhagnsan",30);
 
 
                //获取所有构造器。
                Constructor[] constructors = clazz.getConstructors();//只包含公共的
                constructors = clazz.getDeclaredConstructors();//包含私有的
                for(Constructor con : constructors) {
                        System.out.println(con);
                }
        }

反射指定类中的方法:

        //获取类中所有的方法。
        public static void method_1() throws Exception {
                Class clazz = Class.forName("cn.itcast.bean.Person");
                Method[] methods = clazz.getMethods();//获取的是该类中的公有方法和父类中的公有方法。
                methods = clazz.getDeclaredMethods();//获取本类中的方法,包含私有方法。
                for(Method method : methods) {
                        System.out.println(method);
                }
        }


        //获取指定方法;
        public static void method_2() throws Exception {
                Class clazz = Class.forName("cn.itcast.bean.Person");
                //获取指定名称的方法。
                Method method = clazz.getMethod("show", int.class,String.class);
                //想要运行指定方法,当然是方法对象最清楚,为了让方法运行,调用方法对象的invoke方法即可,但是方法运行必须要明确所属的对象和具体的实际参数。
                Object obj = clazz.newInstance();
                method.invoke(obj, 39,"hehehe");//执行一个方法
        }


        //想要运行私有方法。
        public static void method_3() throws Exception {
                Class clazz = Class.forName("cn.itcast.bean.Person");
                //想要获取私有方法。必须用getDeclearMethod();
                Method method = clazz.getDeclaredMethod("method", null);
                // 私有方法不能直接访问,因为权限不够。非要访问,可以通过暴力的方式。
                method.setAccessible(true);//一般很少用,因为私有就是隐藏起来,所以尽量不要访问。
        }


        //反射静态方法。
        public static void method_4() throws Exception {
                Class clazz = Class.forName("cn.itcast.bean.Person");
                Method method = clazz.getMethod("function",null);
                method.invoke(null,null);
        }

反射的小特点:

Java的泛型是通过编译器实现的,如果我们使用反射,就可以跳过JVM的编译过程,这样就可以不受泛型的限制,往集合中存储一些超出泛型范围的对象。

如,以下是,往泛型限定为Integer的集合中存入String数据,使用反射可以做到:

        public static void fanshes() throws Exception{
		ArrayList<Integer> list = new ArrayList<Integer>();
		Class clazz = list.getClass();
		Method method = clazz.getMethod("add",Object.class);
		System.out.println(method.getName());
		method.invoke(list, "zhangsan");
		method.invoke(list, 13);
		System.out.println(list);
	}


类加载器

常用的三种类加载器

1、bootstrap class loader,是顶层类加载器,用于加载JDK的核心类,是有本地语言写的,没有名字。
2、extension class loader ,用于加载jre/lib/ext目录下的类。
3、application class loader,用于加载自己写的类。

自定义类加载器

要创建用户自己的类加载器,只需要扩展java.lang.ClassLoader类,然后覆盖它的findClass(String name)方法即可,该方法根据参数指定类的名字,返回对应的Class对象的引用。

程序加载的过程

首先是bootstrap class loader把其他的class loader加载进内存,然后其他的class loader再加载其他的class。这三个class loader 之间不是继承关系,而是树形层次关系。在application class loader中有一个引用指向:extesion class loader ,这个引用是parent,所以可以通过getParent()方法获得这个引用指向的class loader的对象。继承是类与类之间的关系,而在这里是对象与对象之间的关系。

加载类的过程

加载一个类的过程是:首先 application class loader找它的parent class loader,看看上一层的class loader是不是已经加载了该类,如果已经加载了它就不会再加载一遍。优势:安全性好
当程序主动使用某个类时,如果该类还未被加载到内存中,系统会通过加载、连接、初始化三个步骤来对该类进行初始化,如果没有意外,JVM将会连续完成这个三个步骤,所以有时也把这三个步骤统称为类加载或类初始化。在类初始化阶段会对静态属性进行初始化。

类加载器的委托机制 

当一个类加载器要加载一个类时,该类加载器不会马上加载这个类,而是会调用它的父类加载器,如果父类加载器还有父类就继续向上调用直到

动态代理

JVM在运行期间会动态加载类的字节码,这些动态生成的通常被用作目标类的代理类,即动态代理类,动态代理类需要实现一个或多个接口,如果目标类没有实现接口,想要为目标类创建动态代理,就要使用CGLIB库,使用CGLIB库就可以动态创建一个类的子类,子类可以被当做一个类的动态代理类。

内省

JavaBean

    JavaBean是一个特殊的Java类,这个类中的方法的名称符合某种特定的规则

内省

    JDK中提供了对JavaBean进行操作的一些API,这套API就成为内省。

反射与内省的区别

 反射可以操作各种不同的java类,内省只是通过反射来操作JavaBean类。JavaBean类里面操作的都是成员变量,都是通过setXXX和getXXX方法来获取成员变量,这样的类用内省来操作会更简单。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值