您的位置:首页 > 职场人生

黑马程序员——Java中的反射技术

2015-06-21 16:11 375 查看
 ------- android培训java培训、期待与您交流!
----------
反射的基石——Class类
Java程序中的各个Java类属于同一类事物,描述这类事物的Java类名就是Class
Class描述了哪些方面的信息呢?
类的名字
类的访问属性 类所属于的包名
字段名称的列表 方法名称的列表等,

使用Class类
那么怎么给Class类赋值呢?

其中的什么是字节码呢?
将一个类编译成class文件后放在硬盘上,就会生成一些二进制代码,要把这些二进制文件加载到内存中来,然后才可以创建一个一个的对象,首先要把字节码加载到类成员中来,在用字节码赋值出一个一个对象来

每一个字节码就是一个Class类的实例对象
Person p1=new Person();

Class cls1=Person.class; //字节码1;其中Person.class就表示Person类在内存中的那个字节码
Class cls2=字节码2;

Class.forName()的作用?就是返回字节码
返回的方式有两种:
1,这份字节码曾经被加载过,已经待在java虚拟机中了,就可以直接返回了
2,java虚拟机中还没有这份字节码,则用类加载器去加载,把加载进来的字节码,缓存到虚拟机中,以后要得到这个字节码就不用在加载了

得到字节码的方式有三种:
1,类名.class
例如:System.class
2,对象.getClass()
例如:new Date().getClass(); //有一个对象,这个对象一定是字节码创建出来的
3,Class.forName(“类名”)
例如:Class.forName(“java.util.Date”) //使用静态方法去加载

在反射中主要用到的是第三种

isPrimitive()
判断是否是原始类型

数组类型的Class实例对象
Class.isArray() 判断是否是数组
总之,只要是源程序中出现的类型,都有各自的Class实例对象,例如,int[]
void
什么是反射?
反射就是把Java类中的各种成分映射成相应的java类。例如:一个java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个java类来表示,

一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象后,得到这些实例对象后有什么用呢?怎么用呢?这正是学习和应用反射的要点。

使用反射技术,应该导入的包是java.reflect.*;
反射会导致程序的性能严重下降
Constructor类:
Constructor类代表某个类中的构造方法
得到某个类所有的构造方法:
例子:Constructor constructors[]=Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);

得到某一个构造方法:
例子:Constructor constructor=Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);
也可写成
Constructor constructor=String.class.getConstructor(StringBuffer.class);
StringBuffer.class //获得方法时要用到的类型

创建实例对象:
通常方式:String str=new String(new StringBuffer(“abc”));
反射方式:String str=(String)constructor.newInstance(new StringBuffer(“abc”));
new StringBuffer(“abc”)//调用获得的方法时,要用到上面相同类型的实例对象

Class.newInstance()方法:
例子:String obj=(String)Class.forName(“java.lang.String”).newInstance();
该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象
该方法内部用到了缓存机制来保存默认构造方法的实例对象

使用反射的构造方法的简单示例:
import java.lang.reflect.*;
publicclass reflect {

publicstaticvoid main(String[] args)throws
Exception

{

Constructor constructor=String.class.getConstructor(StringBuffer.class);

String str=(String)constructor.newInstance(new StringBuffer("abc"));

System.out.println(str.charAt(1));

}
}
Field类
Field类代表某个类中的一个成员变量
生成构造方法:
Alt+Shift+s
——Generate Constructor using Fileds….
定义一个类
publicclass reflectClass {

private int
x;

public
inty;

public reflectClass(int x,
int y) {

super();

this.x = x;

this.y = y;

}
}

总结:
Field类代表一个字节码里面的一个变量,不代表某个对象身上的变量

Field fieldY=pt1.getClass().getField("y");
因为y变量在类中是public类型的,所以在获取变量的时候可以用getField("y")

Field fieldX=pt1.getClass().getDeclaredField("x");

因为x变量在类中的是private类型的,所以在获取变量的时候必须要用getDeclaredField("x")
fieldX.setAccessible(true);//这个就是所谓的暴力反射,让变量的值强制可以去访问

例子:将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的“b”改成“a”

自定义的类:
package itheima;
publicclass reflectClass {

private int
x;

public
int y;

public String
str1="ball";

public String
str2="basketball";

public String
str3="itcast";

public reflectClass(int x,
int y) {

super();

this.x = x;

this.y = y;

}

public
String toString()

{

return
str1+"::"+str2+"::"+str3;

}
}
主函数类:
import java.lang.reflect.*;
publicclass reflect {

publicstaticvoid main(String[] args)throws
Exception

{
reflectClass
pt1=new
reflectClass(3, 5);

changeStringValue(pt1);

System.out.println(pt1);

}

private static
void
changeStringValue(Object obj)throws Exception {

Field[] fields=obj.getClass().getFields();

for(Field field:fields)

if(field.getType()==String.class)
//这里应该用==,因为是同一个字节码

{

String oldValue=(String)field.get(obj);

String newValue=oldValue.replace('b',
'a');

field.set(obj, newValue);

}

}
}

总结:
(1)Field[] fields=obj.getClass().getFields();
这句话是要获取类中所有的成员变量
(2)使用增加for循环遍历所有的成员变量for(Field
field:fields)
(3)if(field.getType()==String.class)这是应该使用==,因为是同一个字节码,这句话是用来判断变量的类型是否是String类型,如果是String类型的变量,就将其中的‘b’替换成‘a’。
(4)String oldValue=(String)field.get(obj);获取field对象中的值
(5)String newValue=oldValue.replace('b',
'a');将b替换成a
(6)field.set(obj, newValue);将对象身上的值更改,obj---pt1
对obj对象身上的field进行更改值,将其值设置为newValue

Method类:
Method类代表某个类中的一个成员方法
得到类中的某个方法:
例子:
Method charAt=Class.forName(“java.lang.String”).getMethod(“charAt”,int.class);
调用的方法:
通常方式:System.out.println(str.charAt()1);
反射方式:System.out.println(chatAt.invoke(str,1));
如果传递给Method对象的invoke()方法的一个参数为null,这有着什么样的意义呢? 说明该Method对象对应的是一个静态方法!!
用反射的方式得到字节码中的方法,在拿这个方法去作用到某个对象 invoke是调用前面的方法 invoke表示方法对象上的方法

用反射方式执行某个类中的main方法
String start=args[0];

Method method=Class.forName(start).getMethod("main",String.class);

method.invoke(null,(Object)new String[]{"111","222","333"});

class TestArguments
{

publicstaticvoid main(String[] args)

{

for(String arg:args)

System.out.println(arg);

}
}
数组的反射:
具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象
代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class
基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用,非基本类型的一维数组,即可以当作Object类型使用,又可以当作Object[]类型使用
Arrays.asList()方法处理int[]和String[]时的差异
Array工具类用于完成对数组的反射操作

总结:
如果使用数组的反射,那么每个数组都属于同一个字节码,但是必须具有相同的数据类型,以及具有相同的维度
如果对象想要得到字节码,必须使用方法getClass();
想要获取对象的字节码的名字,必须使用方法getName();
因为是数组,所以在显示名字的时候,如果是int类型,则显示“[I”
这有个图:



在定义数组的时候,如果后面指定了值,前面就不能指定数组的个数
例如:
Int[] a1=new int[3]{1,2,3};
这种方式是错的
Int[] a1=new int[]{1,2,3};
这种方式是正确的

所有的类都有父类,除了Object类

Int[][] a3=new int[2][3];
Int[] a1=new int[3];
String[] a4=new String[3];

Object[] aObj3=a1;
这种方式不能用,因为基本类型的一维数组,不能转换成Object[]
数组的,因为这个数组中装的是int,不是Object

Object[] aObj4=a3; a3是二维数组,就相当于里面装入了int类型的一维数组,因为这个数组中装的是一维数组,一维数组是Object

Object[] aObj5=a4;
这个数组中装的是String类型 String也是Object类型
Arrays.asList()方法处理int[]和String[]时的差异:
String不属于基本数据类型
Int[] a1=new int[3]; 这个可以理解为,有一个数组,这个数组中装的是int类型,int是基本数据类型,不是Object
Arrays.asList(Object[] obj);

System.out.println(Arrays.asList(a1)); asList方法接收的是Object类型的数组,因为a1是int[]类型的数组,在向方法中传值的时候,Object[]这个数组认为和这个对象对不上,因为传入的值是int,基本数据类型,不能处理,jdk1.5版本可以处理
String[] a4=new String[3];
这个可以理解为,有一个数组这个数组中装的是String类型,String类型是Object
System.out.println(Arrays.asList(a4));
因为a4是String类型的数组,所以可以将数组中的值打印出来
思考题:怎么得到数组中元素的类型呢?
不能获取整个数组的类型,只能获取整个数组中某个元素的类型
Int[] a=new int[]{1,2,3};

Object a1=new Object[]{“a”,1,2}; //可以使用a[0].getClass().getName()来获取某个元素的类型

ArrayList—HashSet的比较及HashCode的分析
ArrayList和HashSet的区别:
ArrayList是一个有顺序的集合,有顺序的集合就相当于一个数组,在将对象放入集合中的时候,是将对象的引用放入进去,是按照依次的顺序先后放进去,如果一个对象有多个引用的时候,也是依次放入集合中的,可以明确的说取出第几个
(这里的顺序指的是位置的顺序)

HashSet是在向里面存入对象的引用的时候,先查看集合中有没有这个对象是否相等,如如果有这个对象,则不向里面存入

面试题:
HashCode方法的作用:
哈希算法来提高从集合中查找元素的效率,这种方式将集合分成若干个存储区域,每个对象可以计算出一个哈希码,可以将哈希吗分组,每组分对应某个存储区域,根据一个对象的哈希码就可以确定该对象应该存储在哪个区域了。
当有一个对象的时候,先算出它属于哪一个区域,假如属于第五个区域,那么就不依次去找,只在第五区域找是否有与它相等的对象即可,这样就提高了程序的效率

要想hashCode的方法有价值,前提是对象必须存储在哈希集合中才有价值

ReflectPoint pt1=new ReflectPoint(3,3);
ReflectPoint p2=new ReflectPoint(4,4);
ReflectPoint pt3=new ReflectPoint(3,3);
如果没有实现hashCode方法为什么长度就等于3呢?虽然pt1和pt3这两个对象用equals比较是相等的,但是算出来的hashCode值pt1和pt3都是按照自己的内存地址去算的,这两个本来相同的对象,分别存在了不同的区域中,所以被放入到了集合中
为了让相等的对象放在相同的区域中,那么就应该是如果两个对象的equals方法相等,那么两个对象的hashCode方法也应该相等
如果集合不存在HashSet中,那么就没有必要使用hashCode方法了

提示:当一个对象被存储进HashSet集合中以后,就不能拿修改这个对象中的那些参与计算哈希值的字段了,否则,对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为的参数去HashSet集合中检索对象,也讲返回找不到对象的结果,这也会导致无法从HashSet集合中单独删除当前对象,从而造成内存泄露

所谓的内存泄露是我这个对象不要用了,但还一直占用内存的空间,没有被释放,这就叫内存泄露

反射的作用——>实现框架功能

框架与框架要解决的核心问题

例子:我做房子卖给用户住,由用户自己安装门窗和空调,我做的房子就是框架,用户需要使用我的框架,把门窗插入进我提供的框架中。框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类

框架要解决的核心问题:

我在写框架(房子)时,你这个用户可能还在上小学,还不会写程序呢?我写的框架程序怎样调用到你以后写的类(门窗)呢?
因为在写程序时无法知道要被调用的类名,所以,在程序中无法直接new某个类得实例对象了,而要用反射方式来做。

框架和工具类的区别:
这两个类都是别人写的,一个是你调用人家,一个是人家调用你
框架是框架在调用用户的类
工具类是用户在调用工具类

例子:
config.properties类中:
className=java.util.HashSet

reflectSet类中:
package itheima;
import java.util.*;
import java.io.*;
import
java.lang.reflect.*;
publicclass reflectSet {

publicstaticvoid main(String[] args)
throws Exception{

InputStream in=new FileInputStream("config.properties");

Properties prop=new Properties();

prop.load(in);

in.close();

//getProperty()这个方法是获取config.properties文件中的属性

String className=prop.getProperty("className");

Collection a1=(Collection)Class.forName(className).newInstance();

reflectClass pt1=new reflectClass(3, 3);

reflectClass pt2=new reflectClass(5, 5);

reflectClass pt3=new reflectClass(3, 3);

a1.add(pt1);

a1.add(pt2);

a1.add(pt3);

a1.add(pt1);

System.out.println(a1.size());

}

}
 ------- android培训java培训、期待与您交流!
----------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: