黑马程序员——反射
来源:互联网 发布:软件脱壳是什么意思 编辑:程序博客网 时间:2024/06/03 17:32
------- android培训、java培训、期待与您交流! ----------
相关概念
Class类
1)反射的基石:Class类。Java程序中的各个Java类属于同一类事物,描述这类事物的Java类名就是Class;
2)如何得到各个字节码对应的实例对象(Class类型),三种方式:
a、类名.class:如System.class
b、对象.getClass():如new Date().getClass()
c、Class.forName(“类名”) :如Class.forName(“java.util.Date”)
3)做反射时,一般用Class.forName(“类名”) 。因为在写源程序时,还不知道类的名字,在运行时,传递给我的字符串包含了一个类的名字,再将字符串传入方法中;
4)通过Class类可以得到一个类的方方面面的信息,且只要是在源程序中出现的类型;
5)8个基本类型也有对应的类型对象,void也有对应的void.Classs,这是9个预定义的Class对象;
a、方法isPrimitive()查看是不是基本类型的字节码;
b、Integer.TYPE方法指Integer所包装的基本类型的字节码;
int[].class == Integer.class;为false,而int.class ==Integer.TYPE;为true;说明TYPE是Class类和别的所有类的统称;
c、数组类型的Class实例对象:Class.isArray()。如int[].class.isArray();返回true;一个int[]数组的类型打印为:class [I。
字节码
什么是字节码?
1)当我们在源程序里用到一个类时,首先要从硬盘上将这个类的二进制代码编译成的class以后,把这些二进制代码加载到内存里面,才可以用这个类创建一个对象;
2)即首先要将类的字节码加载到内存里,再用这些字节码,复制出一个个对象来;
3)若内存里面有N份字节码,那每一份字节码就是一个Class的实例对象;
4)如Person.class就是代表Person类的字节码,Person p = new Person;就是字节码创建出来的对象,可以用p.getClass()可以得到p属于哪个字节码,即类名。
5)小结:
a、可以指定一个类的字节码的完成名称,forName也是得到一个类的字节码;
b、得到一个类的字节码分2种情况:
字节码已经加载,现在待在虚拟机里;
2、字节码还没加载,则用类加载器去加载,再将字节码缓存,forName返回刚才加载进来字节码。
反射
黑马学员冯伟立听完反射后的一句话总结:“反射就是把Java类中的各种成分映射成相应的Java类”。很透彻、精辟。
1)反射就是把Java类中各种成分映射成相应的Java类。如一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量、方法、构造方法、包等信息也用一个个的Java类来表示;表示Java类的Class类显然要提供一系列的方法,来获得其中的变量、方法、构造方法、修饰符、包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Constructor、Package等;
2)一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象。反射的要点就是:通过得到的这些实例对象来实现一些功能;
3)不管是什么方法,都可以都可以用一个类型Method的实例来表示;Method代表类型、类别,而其对象代表一个具体的方法;得到一个Method对象,即得到了一个类的一个方法;
4)通过查看Jdk源代码,看出反射比较占用时间和性能,导致程序性能严重下降。
知识点与代码
成员变量反射
b、扫描一个对象上的所有字段,且字节码用“==”比较更专业;
方法的反射Method类
1)Method类代表某个类中的一个成员方法:得到类中的某一个方法:
Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class);
2)调用方法:
a、通常方式:System.out.println(str.charAt(1));
b、反射方式:System.out.println(charAt.invoke(str, 1));
3)JDK1.4和JDK1.5的invoke方法的区别:
a、JDK1.5:public Object invoke(Object, Object... args)
b、JDK1.4:public Object invoke(Object obj, Object[] args),即按JDK1.4的语法,需要将一个数组作为参数传递给invoke方法时,数组中的每个元素分别对应一个参数。
4)静态方法调用时,不需要对象,则传入null。
Constructor类
1)Constructor类代表某个类中的一个构造方法。得到某个类所有的构造方法:
Constructor[] constructors = Class.forName("java.lang.String").getConstructor();
2)得到某一个构造方法:
Constructor constructor = Constructor[] constructors = Class.forName("java.lang.String").getConstructor(StringBuffer.class);
3)创建实例对象:
a、通常方式:String str = new String(new StringBuffer("abc");
b、反射方式:String str = (String)constructor.newInstance(new StringBuffer("abc"));
4)JDK1.5新特性,接收可变参数列表(若没有新特性,可通过传入数组来实现可变参数):
String.class.getConstructor(Class<?> ... parameterTypes);
5)newInstance();方法:
注意2重点:a、获得方法时,需要类型;b、调用方法时也要传同样类型的对象newInstance返回的是Object;
6)Constructor c = String.class.getConstructor(StringBuffer.class); String str = (String)c.newInstance(new StringBuffer("abc"));
a、在运行时才知道你对应的是哪个构造方法;要区别清楚编译时、运行时,错误错在哪个阶段,一定要搞清楚;
b、第一个StringBuffer表示选择哪个构造方法,第二个StringBuffer表示用这个构造时,还得传一个StringBuffer对象进去;编译时只知道你是个构造方法,但不知道是哪个类的构造方法,只有在运行才知道具体的构造方法,所以要强制转型(这里是给编译器强制转型的);
7)Class类也有个newInstance方法,它和上面的Constructor类的newInstance方法有什么区别呢?
相比之下,Class类的newInstance方法直接省略了中间步骤,封装了过程,底层调用了Constructor的newInstance方法。
main方法与反射
小结
1)每一个数组的父类都是Object,下面代码很重要:
Method mainMethod =
Class.forName(startingClassName).getMethod("main",String[].class);
//如果不加Object[],Java没把你当成一个数组,而是认为收到3个参数
mainMethod.invoke(null, new Object[]{new String[]{"111","222","333"}});
Method mainMethod =
Class.forName(startingClassName).getMethod("main",String[].class);
//mainMethod.invoke();
mainMethod.invoke(null,(Object){new String[]{"111","222","333"});
数组与Object及其反射
反射的基础代码
ArrayList、HashSet比较及hashCode分析
1)ArrayList是在数组位置中依次放入对象的引用,而不是放入对象;
2)而HashSet每次放的时候,是先判断集合中有没有,有就存放失败(而不是覆盖),对于其元素比较的是Hashcode值,会被HashSet本身的equals判断为不等;
a、若想通过特别的方式比较的元素相等,就要复写equals方法和hashCode方法;
b、如:HashSet中的两个元素new ReflectionPoint(3, 3);和new ReflectionPoint(3, 3);,通过HashSet本身的equals方法比较返回为相等;就可以通过复写equals方法和hashCode方法实现相等。
面试题
hashCode方法的作用?
1)以前这个题面试概率低,现在高,现在单位面试时也开始越来越深入面试底层;
2)存储的集合是哈希算法的集合,hashCode才有价值;
3)为了让“相等”的对象放在哈希值相同的区域,则如果equals方法比较是相同,也应该让它们的hashCode也要相同;
4)如果这个对象不需要存在哈希集合中,那么也不需要hashCode;
5)下面2个图的内容很重要;
6)下面的图3也可以帮助理解hashCode与equals方法:
pt1的计算hashCode的字段被改了,就被存到了另外的哈希值区域,这样去remove的时候就找不到这个对象返回false(日积月累,内存越来越泄露)。
图1:hashCode方法、equals方法以及HashSet关系。
图2:详解hashCode方法与HashSet类。
Java中有内存泄露吗?
1)概念:内存泄露,有个对象或东西在内存中,但是一直都不再用了,结果它还没有被释放掉,就是内存泄露;
2)下图中造成内存泄露:pt1的计算hashCode的字段被改了,就被存到了另外的哈希值区域,这样去remove的时候就找不到这个对象返回false(日积月累,内存越来越泄露)。
图3:内存泄露实例,同时理解对象通过hashCode存储。
反射技术开发框架原理
分析
1)房子就是框架(提前做好了),锁是一个工具类;我用了两个东西:房子和锁都是别人做的,但是用法不一样;门窗被房子调用,锁被门窗调用;
2)框架和工具类的区别:
a、都是别人写的,一个是别人调用你(框架),一个是你调用别人(锁);struts就是一个像房子的半成品,所以单位招人要求用框架,因为利用框架做事效率高;
b、使用别人写的类有两种使用方式:你去调用别人的类;别人的类调用你的类(但是都是你在使用别人的类);
3)反射在前面内容的应用:调用某个类的main方法,到底调用哪个类的?开始是不知道的,在程序运行时才知道;也就是:我先写好了调用你的类,但是你还没有被写出来,这也是反射的一个好处。
反射的作用:实现框架功能
1)框架:
我做房子卖给用户住,由用户自己安装门窗和空调,我做的房子就是框架;用户需要使用我的框架,把门窗插入我提供的框架中;框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类;
2)框架要解决的核心问题:
a、我在写框架(房子)时,你这个用户可能还在上小学,还不会写程序呢。我写的框架程序怎样能调用到你以后写的类(门窗)呢?
b、因为在写程序时无法知道要调用的类名,所以在程序中无法直接new某个类的实例对象了,而要用反射方式来做;
3)综合案例:
a、先直接用new语句来创建ArrayList和HashSet的实例对象,演示用eclipse自动生成ReflectPoint类的equals和hashCode方法,比较两个集合的运行结果差异;
b、然后改为采用配置文件加反射的方式创建ArrayList和HashSet的实例对象,比较观察运行结果差异;
c、引入eclipse对资源文件的管理方式的讲解。
用类加载方法管理资源和配置文件
分析
1)在实际开发中,一般是将.class打包成jar包,再给别人,而不是给源文件;所以不存在给别人配置文件做法;
2)InputStream is = new FileInputStream("config.properties");,一般也不会像这样使用相对路径,而是用绝对路径;
a、而使用相对路径也是使用getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties")的方式;
b、其中“cn/”,cn前面不能加斜杠;但是这种方式没有OutputStream,因为这里只读;未来要学的技术的配置文件都是放在classPath指定的目录下(回头把classPath搞的很清楚):用的就是类加载文件的方式;
3)InputStream is = new FileInputStream("d:\\config.properties");,但也会出现小问题,d:\\是去配的、运算出来的,不是写死的,所以用方法getRealPath();
4)如:金山词霸 / 内部,则可以得到其绝对位置,再拼上后面的文件路径;
5)用自己的类去加载的时候(类提供的简便方法),不是用类加载器时,可以用相对路径,也可以用绝对路径;
a、资源跟包有关系,用相对路径;
b、资源跟包没关系,用绝对路径;
c、但是底层都是用getClassLoader;
6)代码演示见下面代码。
得到资源配置文件两种方式
1)InputStream is = new FileInputStream("config.properties");这种方式,可以得到一个FileInputStream或者FileOutputStream;具体操作是:
在eclipse中,window——Preferences————General——Open mode下面打勾或者不打勾;如果要存,还要一定得到配置文件的完整路径(一定要放在程序的内部)。
2)还有个方法得到资源文件的方式,最常用的,但是不能替代上面的方法:
a、类加载器把.class文件加载进来,那么它也可以把普通资源文件加载,在指定目录下逐一查找要加载的文件;
b、eclipse有个功能,会自动把窗口包下的文件编译,并且其下的像配置文件也就是存在包的目录下;那么有的文件不放在classPath路径下,则可以放在eclipse的源目录下。
代码
JavaBean
内省(IntroSpector)和JavaBean
void setAge(int age);
6)eclipse中快捷封装方法操作:快速封装方法:选中代码——右键——重构Refactor——抽取方法Extract Method——输入方法名——Ok。
JavaBean复杂内省操作
5)以后经常要写的代码:从集合中找到需要的元素,但是上面的Set、getProperty方法更简便;而麻烦方式也是很有价值:我们做开发,可能永远不是最好最棒的,只要在规定时间内,拿出结果;生存是第一,然后再在能力内追求最好。
代码
面试
小结
配置文件
1)右键工程名——new——File(config.properties);
2)客户没有运行(javac)程序的时候,别人就不用改Java源程序,只用改这个properties文件
3)Properties在HashMap的基础上扩展了功能,可以把自己内存里面的键值对存放到硬盘上文件里去,也可以在初始化的时候把键值对加载进来;
4)容器有时得一个个填入值,而Properties可以从文件里面搞到一堆键值对。
关闭资源习惯
1)良好的习惯关闭资源(输入输出流等),不然马上就有小小的内存泄露;2)两个资源的区别:不是输入流对象不被释放,而是这个对象关联的系统资源没有被释放;
3)关闭的本对象还在,但是在给操作系统说:把操作系统的资源关掉,如在eclipse窗口下写java;
4)Java资源即使关了(资源被gc管理),eclipse窗口却还在(eclipse被操作系统管理),所以先把对象关联的物理资源关掉。
总结
面试
Class.forName作用
问题:Class.forName的作用?
回答:返回字节码,而返回的方式有两种:
1)字节码已经加载,现在它待在虚拟机里;
2)字节码还没加载,则用类加载器去加载,再将字节码缓存,forName返回刚才加载进来字节码。
面向对象设计
1)画圆:人发出信号,其实是圆自己在调用draw方法,circle.draw();
2)人关门,司机刹车——变量在谁身上,方法就在谁身上(专家设计模式)。
- 黑马程序员—反射
- 黑马程序员—反射
- 黑马程序员—反射
- 黑马程序员—反射
- 黑马程序员—反射
- 黑马程序员—反射
- 黑马程序员—反射
- 黑马程序员—反射
- 黑马程序员——反射
- 黑马程序员——反射
- 黑马程序员——反射
- 黑马程序员——反射
- 黑马程序员——反射
- 黑马程序员——反射
- 黑马程序员——反射
- 黑马程序员——反射
- 黑马程序员——反射
- 黑马程序员——反射
- perl函数说明(chown)
- block使用小结、在arc中使用block、如何防止循环引用
- java enum(枚举)使用详解 + 总结
- 软件体系结构--适配器模式
- php数组删除指定数值
- 黑马程序员——反射
- phonegap发送短信
- Angular $Watch
- 虚函数
- perl函数说明(chr)
- IOS中scrollsToTop问题小结
- Spine使用
- 通过ssh用rsync无密码传输文件
- 解析XML文件