黑马程序员——java基础---反射

------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

反射

一、概述 

反射:就是把java类中的各种成分映射成相应的java类。

一个已经写完并且可以使用的应用程序,它的源代码已经固定不可以被改变我们只要拿来使用就可以,但是后期如果我们想要扩展其功能时,这时该怎么做呢?就如我们的电脑一样,后期我们可能会有鼠标、键盘等,所以电脑给我们预留了usb接口,只要符合这个接口规则的设备,电脑就可以通过加载驱动等操作来使用。

一般情况时,我们可以提供一个配置文件,来供以后实现此程序的类扩展功能。对外提供配置文件,让后期出现的子类直接将类名字配置到配置文件中即可。该应用程序直接读取配置文件中的内容。并查找和给定名称相同的类文件。此时,只要我们修改配置文件就可以扩展源程序的功能,大大提高了程序的扩展性。

二、反射的基石——Class类

Java中Class类代表着一类事物,它的实例就是类对象;其中Class类对应着java中的类而它的示例就对应着各个类在内存中的字节码;当一个类被类加载器加载到内存中就会占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以他们在空间中的内容也是不同的,而这一个个的空间可分别用一个个的对象来表示,这些对象显然具有相同的类型,这个类型就是Class 。

Java程序中的各个java类属于同一类事物,描述这类事物的java类名就是Class。即:反射的基础class类。

1、Class类介绍

java.lang.Class类十分特殊,用来表示java中类型(class/interface/enum/annotation/primitive type/void)本身。

·Class类的对象包含了某个被加载类的结构。一个被加载的类对应一个Class对象。

·当一个class被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM 便自动产生一个Class 对象。

·Class类是Reflection的根源。

·针对任何您想动态加载、运行的类,唯有先获得相应的Class 对象

2、Class类的对象获取的三种方式

1、对象.getClass();

2、Class.forName(“类名”);(最常被使用)

3、类名.class;

static Class<?> forName(String className) 返回与带有给定字符串名的类或接口相关联的 Class 对象。

static Class<?> forName(String name, boolean initialize, ClassLoader loader) 使用给定的类加载器,返回与带有给定字符串名的类或接口相关联的 Class对象。

3、九个预定义Class实例对象

基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字void。

即:只要在源程序中出现的类型都有各自的Class实例对象。

4、反射机制

· 可以在运行时加载、探知、使用编译期间完全未知的类 。

· 程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个已加载的类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。

·加载完类之后,在堆内存中,就产生了一个 Class类型的对象(一个类只有一个  Class  对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子 看到类 的结构,所以,我们形象的称之为:反射。 

5、Class中的常用方法

Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。故:不可以new对象。

Boolean isPrimitive();  判定指定的 Class 对象是否表示一个基本类型。

Field[]getFields(String name);获取此类的公共成员字段。     

Method[]getMethods();获取此类的公共成员方法

StringgetName()   以字符串形式返回此构造方法的名称

PackagegetPackage();获取此类的包。

Constructor<T>getConstructor(Class<?>...parameterTypes)   获取此类的构造函数

public T newInstance()  创建此 Class 对象所表示的类的一个新实例。

String  getName()        :获得包名+类名

String  getSimpleName() :获得类名

不论一个类被创建多少的实例,但是一个类只对应一个Class对象

三、Constructor类通过反射获得构造器信息的常用方法

Constructor类属于java.lang.reflect.Constructor包中

Constructor<TgetConstructor(Class<?>... parameterTypes)  只能获得public的一个Constructor

Constructor<?>[] getConstructors()  获得public的所有Constructor

Constructor<TgetDeclaredConstructor(Class<?>... parameterTypes) 获得所有的一个Constructor(包含私有成员)

Constructor<?>[] getDeclaredConstructors()  获得所有的Constructor(包含私有成员)

注:当获取预操作的类的属性和方法以后,若想使用该方法或属性,则必须使用方法newInstance创建实例后才可以操作

代码示例:






四、Method类通过反射获取方法信息的常用方法

Method所属于java.lang.reflect.Method包中

Object invoke(Object obj, Object... args)   激活此功能

Method    getMethod("setUname",String.class)  只能获得public的一个Method

Method[]  getMethods("setUname",String.class)  获得public的所有Method

Method    getDeclaredMethod()  获得所有的一个Method(包含私有成员)

Method[] getDeclaredMethods()  获得所有的Method(包含私有成员)

代码示例:



用反射的方式调用主函数

用反射方式执行某个main方法:

        首先要明确为何要用反射:在写源程序时,并不知道使用者传入的类名是什么,但是虽然传入的类名不知道,而知道的是这个类中的方法有main这个方法。所以可以通过反射的方式,通过使用者传入的类名(可定义字符串型变量作为传入类名的入口,通过这个变量代表类名),内部通过传入的类名获取其main方法,然后执行相应的内容。

此时会出现下面的问题:

        启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。所以,在给main方法传递参数时,不能使用代码mainMethod.invoke(null,new String[]{“xxx”}),javac只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5的语法解释,因此会出现参数类型不对的问题。

解决办法:

mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});

mainMethod.invoke(null,(Object)new String[]{"xxx"});

这两种方式编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了。

因为主函数是String类型的数组。所以我们可以用的方法为:类名.main(new String[]{“111”,“111”,“111”});

代码示例:

String ClassName = mainName;

Method mainMetod=  Class.forname(ClassName).getMethod(main,string[].class);

mainMethod.invoke(null,newstring[]{“111””222””333”}拆包);

(这个对象必须改成new String[](new string[]{“111””222””333”}),或者(String)new string[]{“111””222””333”}后,参数类型才可以)

五、Field类通过反射获取属性信息的常用方法

Field类所属于java.lang.reflect.Field包中

void set(Object obj,Object value); 通过反射直接设置此属性的值。

Objectget(Object obj);  通过反射获取指定对象上此Field 表示的字段的值

public void setAccessible(boolean flag)  若为真则取消语言访问检查,也称为暴力访问。

Field   getField() 只能获得public的一个field

Field[]getFields()  只获得public的所有field。

Field  getDeclaredField()  只能获得一个field(包含私有成员)

Field[] getDeclaredFields() 获得所有的field(包含私有成员)

代码示例:



六、数组的反射

数组类型:只有数组的维数和类型不同时Class对象才不同。

1、具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。数组字节码的名字:有[和数组对应类型的缩写,如int[]数组的名称为:[I

2Object[]String[]没有父子关系,ObjectString有父子关系,所以new Object[]{aaa,bb}不能强制转换成new String[]{aaa,bb}; Object x =abc”能强制转换成String x =abc”。

3、如何得到某个数组中的某个元素的类型,

        我们无法直接得到某个数组的具体类型,只能先得到其中某个元素的类型,从而就可以确定此数组的类型。

eg:Object[] a = new Object[](“a”,1);用一个元素a[0].getClass().getName();即可的到数组类型

4、基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。

数组与Object的关系及其反射类型

String类型的数组的父类全是Object。而其他类型数组int父类不是Object

只有数组的维数和类型不同时Class对象才不同。

方法Arrays.asList(Object obj):将数组转换成List集合;1.4版本是接受Object类型的数组,1.4版本以后是(T…a)其中Object类型的数组可以直接接收;若是int类型的数组则无法接收;

数组类型:只有数组的维数和类型不同时Class对象才不同。

数组的反射应用

代码实例://不知道数组类名的时候的打印方法

 Public static voidprintObject(Object obj)

{

Class clazz = obj.getClass();

If(clazz.isArray) //是数组就拆包

{

Int len = Array.getLength(obj);

For(int i=0;i<len;i++)

System,out.println(Array.get(obj,i));

}

Else

System,out.println(obj);

}    

七、ArrayList_HashSet的比较及Hashcode分析

ArrayList:可重复的有序数组;依据方法equals();

HashSet:  不可重复没有顺序哈希;依据方法先hashCode()和后equals();

注意:当一个对象被存储进HashSet集合中以后,就不能修改这个对象了,如若修改则这个对象的哈希就会改变,它在该集合中的存储位置就会改变,这是就找不到这个对象了,也就无法对其进行删除或者其他操作这种情况称作:内存泄露

八、反射技术开发框架的原理

框架以及框架要解决的核心问题:

              例如:开发商卖房子给住户,用户自己安装门窗和空调,则房子就是框架,用户需要使用框架房子,将门窗安装到开发商提供的房子框架中。框架和工具类有区别:工具类被用户调用,而框架是调用用户提供的类。

              但是我开发框架的时候还没有用户我怎么去调用他的类呢?这时就用反射;将用户的类反射到配置文件中,而框架只需要读取配置文件即可。      

九、反射的效率问题

•setAccessible

启用和禁用访问安全检查的开关,值为true则指示反射的对象在使用时应该取消Java语言访问检查。值为false则指示反射的对象应该实施 Java 语言访问检查。并不是为true就能访问为false就不能访问。禁止安全检查,可以提高反射的运行速度。

反射的开发效率很高,但是运行效率很低,一把比正常代码慢30倍左右,禁止安全检查时则会提高4倍运行效率 。System.currentTimeMillis();

反射的优点:大大加强了代码的扩展性。 反射的缺点:效率不高,比较消耗资源。

十、反射与正常使用类的区别

一般在我们正常使用类的时候一般都是new一个对象,new的意思是:先知道类的名称根据被new的类的名称找寻该类的字节码文件,并加载进内存,然后创建该字节码文件对象,接着创建该字节文件的对应的Person对象;反射也是遵循着个思路就是表现形式不同。

代码示例:

假设与操作的类名“Person”

原来new对象的时候:先根据被new的类的名称找寻该类的字节码文件,并加载进内存,并创建该字节码文件对象,并接着创建该字节文件的对应的Person对象.                  

 Personp = new Person();

然而现在:

Stringname = "Person";  //找寻该名称类字节码文件,并加载进内存,并产生Class对象。

Classclazz = Class.forName(name);

/如何产生该类的对象呢?实质是调用空参数的构造方法

Objectobj  = clazz.newInstance();    

              没有空参数的构造函数的方法:

原来:

Person p = new Person("小强",39);

现在:

String name ="Person";//找寻该名称类文件,并加载进内存,并产生Class对象。

Classclazz = Class.forName(name); //获取到了指定的构造函数对象。

Constructorconstructor = clazz.getConstructor(String.class,int.class);//通过该构造器对象的newInstance方法进行对象的初始化。

Objectobj = constructor.newInstance("小明",38);

(由原来的一行代码变成了现在的三四行代码,有人会说这不更麻烦吗?)

2、在我们正常使用类的时候只要new一个此类的实例化对象即可,使用这种方法时,我们必须要先知道这个类的名字然后才去new对象。但是当我们不知道这个类名字的时候,或者说我们在写程序的时候还不知道要调用的类存不存在,而且我们写完的源代码已经封装完毕不允许被改变这时怎么办呢?这时就要用到反射机制。

反射应用小练习:用反射读取配置文件




配置文件自己创建,可以写上内容为 :

CPU1=shengka

CPU2=wangka

这样以后不用更改源代码,二只是改动配置文件就可以运行新加入的类了。

------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值