您的位置:首页 > 编程语言 > Java开发

Java反射篇详细~讲解底层原理

2019-02-18 15:53 134 查看

#反射(Java程序中的各个java类属于同一类事物,描述这类事物的java类名就是Class)

#Class类代表java类,它的各个实例对象分别对应什么?

对应各个类在内存中的字节码,例如Person类的字节码,ArrayList类的字节码等等
一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同类的字节码是不同的,这一个个空间分别用一个对象来表示,这些对象具备相同的类型。

##三种方式得到class类型

  1. 类名.class,例如System.class;
  2. 对象.getClass(),例如new Date.getClass();
  3. class.forName(“包名+类名”),例如Class.forName(“java.util.Date”);

###九个预定义的class实例对象(除了八个基本类型还有一个void也有class类型)
Class.isPrimitive方法查看程序中出现的类型都有各自的Class实例对象,到底是什么类型
int.class == Integer.Type(true)
数组类型的class对象实例通过isArray()查看
##反射概念理解:
反射就是把java类中的各种成分映射成相应的java类。

例如一个java类中用一个class类的对象来表示。一个类中的组成部分:成员变量、方法、构造方法、包等等信息也用一个个java类来表示,就像一个汽车是一个类,汽车中的发动机,变速箱等等也是一个个类。表示java类的class类显然要提供一系列方法,来获取其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,他们是Field、Method、Contructor、Package等等。
构造方法的反射应用(Constructor)
Constructor类代表某个类中的一个构造方法
得到某个类所有的构造方法:

例子:Constructor[] constructor =
Class.forName("java.lang.String").getConstructors();

得到某一个构造方法:
例子: Constructor constructor =
Class.forName(“java.lang.String”).getConstructor(StringBuffer.class)
创建实例对象:

通常方式:String str = new String(new StringBuffer("abc"))
反射方式:String str = constructor.newlnstance(new StringBuffer("abc"));

反射获取对象实例的一般方式 class–>Constructor–>newlnstance
调用获得的方法时要用上面相同类型的实例对象
##Class.newlnstance()方法:(反射获取对象实例的便捷方式class–>newlnstance)
例子:String str = Class.forName(“java.lang.String”).newlnstance();

该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象

##Field类代表某个类中的一个成员变量
案例 person类
public int x;
private int y;
getter and setter

##通过反射获取属性值

Person p =new Person(1,2);
Field fieldx = p.getClass().getField("y");
//field x 的值是多少? 是1?,错!fieldx不是对象身上的变量,而是类上,要用它去取某个对象上对应的值
System.out.println(fieldx.get(p));
//获取y属性的时候由于是私有的用暴力反射获取
Field fieldy = p.getClass().getDeclaredField("x");
fieldy.setAccessiable(true);
System.out.println(field.get(p));

##Method代表类中的一个成员方法

例子:Method methodCharAt = Class.forName("java.lang.String").getMethod("charAt",int.class);
//getMethod后面一个参数是参数类型的可变长参数类型str.charAt(1)参数是int所以是int.class
通用方式:System.out.println(str.charAt(1));
反射方式:System.out.println(methodCharAt.invoke(str,1));
//method的invoke是mentod的方法,调用invoke的时候才代表charAt方法执行

如果传递给Method对象的invoke()方法的第一个参数为null,这有什么样的意义呢?说明该method传递对象对应的是一个静态方法

##反射的应用:

//修改对象String属性b换成a
public static void changeStringVlaue(Obeject obj){
Field field = obj.getClass().getFileds();
for(field.getType == String.class){
String oldVlaue = (String) field.get(obj);
String newValue = oldVlaue.replace('b','a');
field.set(obj,newValue);
}
}

##2019/2/18数组的反射(与Object的关系)

基本数据类型没有 父类。
基本数据类型的包装类的父类是 java.lang.Number
Number 的父类也是Object
那么我们也可以说,Object 也是基本数据类型的包装类的父类 。
数组的父类也是Object
String 的父类是Object。

int[] a1 = new int[]{1,2,3};
int[] a2 = new int[2];
int[][] a3 = new int[1][2];
String[] a4 = new String[]{"111","222","333"};
Object obj1 = a1;
Object obj4 = a4;
//Object[] obj11 = a1;//错误,因为a1一维数组可以看做是一个对象,但是不能看成是一个对象数组
Object[] obj3 = a3;//二维数组可以看做一维数组里面包含一个数组,而一维数组可以看做一个对象,所有二维可以看做是一个对象数组
Object[] obj44= a4;//字符串可以看做对象,所有字符串数组就可以看做是对象数组

操作数组的工具类Arrays.asList(数组)可以把数组转换成List
System.out.println(Arrays.asList(a1));//打印[[数组的哈希码地址]

System.out.println(Arrays.asList(a4));//打印集合元素

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

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

把框架看着是房子的话,我把房子卖给客户住(这个房子只是一个空的没有配带家具和门窗),有用户自己安装家具和门窗,房子看做框架,用户需要使用我提供的框架,把门窗和家具插入我的框架中。框架和工具类存在区别,工具类被用户的类调用,而框架则是调用用户提供的类。
框架要解决的核心问题:
问:我在写(房子)框架的时候,客户这个人可能在干其他的,还不会写程序,我写的框架程序怎么能掉用你以后写的(门窗)类呢?
**答:因为在写程序时无法知道要被调用的类名,所有无法直接new某个类的实例对象,所有需要用到反射来实现。 **

通过反射写一个小框架的应用:

在ide工具中创建一个配置文件 config.properties(内容为:className =java.util.HashSet )
然后通过字节流输入流(读进来)读取配置文件 InputStream input = new FileInputStream(配置文件路径);

创建配置文件 Properties pro = new Properties();
通配置文件加载输入流读取的内容 pro.load(input);
最后关流 input.close();
获取配置文件的key String className = pro.getProperty("calssName");
通过反射获取配置文件value的实例对象 Collections collection = (Collections) class.forName("className").newInstance();
创建几个对象(该对象重新生成了hashcode()和equals()方法)
Person p1 = new Person(10,"小明");
Person p2 = new Person(12,"小白");
Person p3 = new Person(12,"小白");
collection.add(p1);collection.add(p2);collection.add(p3);

System.out.println(collection.size());//长度打印出来了为2,反射成功!!!

##使用类加载器加载这些文件

适用场景:资源文件和类文件在不在同一目录都可以
注意:getResourceAsStream里的参数要写资源文件的全限定路径,
包名+文件名且开头千万不要写"/"

使用方法:

InputStream ips = 类名.class.getClassLoader().getResourceAsStream("配置文件的路径“);

例如:InputStream ips = TestReflect2.class.getClassLoader().getResourceAsStream("itcast/cn/Reflect/config.properties");
注意:ClassLoader加载配置文件时,它是在classpath 的根路径下搜索,所以在填写配置文件的路径时要特别注意。bin是classpath 根路径,在配置文件钱要加上完整的包名。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: