1,反射的基石
描述类的类Class
(1)如何得到各字节码对应的实例对象
类名.class
对象.getClass()
Class.forName("类名")
(2)九个预定义对象
八个基本类型+void
void.class 也可以
总之,只要是在源程序中出现的类型,都有各自的Class实例对象。
2,反射
反射就是把java类中的成员映射成java类。
描述方法的类 Method
描述属性的类 Field
描述构造器的类 Constructor
其实完成反射要记住的是反射的基本思路:
拿到类的字节码-->要反射的内容(方法、构造器、字段等等)-->查看反射类API
3,构造器的反射
*
构造器的反射
*/
import java.lang.reflect.*;
public class ConstructorReflect
{
public static void main(String args[]) throws Exception{
//通过类名.class的方式得到类的字节码
Class cs = Cat.class;
//拿到字节码后,通过反射得到描述构造器的类:Constructor。
Constructor catConstr = cs.getConstructor(String.class);
//通过反射得到Constructor类后,这个用以描述构造器的类,有构造器的共用特点。
System.out.println(catConstr.getName()); //返回构造名称
//通过反射得到的构造器来new 一个Cat对象
Cat refCat = (Cat)catConstr.newInstance("niki");
System.out.println(refCat);
//这是对String类的构造方法的反射。
Constructor strConstr = String.class.getConstructor(String.class);
String str = (String)strConstr.newInstance("abc");
System.out.println(str);
}
}
class Cat
{
private String name;
public Cat(String name){
this.name = name;
}
public String toString(){
return name;
}
}
4,成员变量的反射
还是沿用上述的思路:
拿到类的字节码-->要反射的内容(Field)-->查看反射类的API
/*
成员变量的反射
*/
import java.lang.reflect.Field;
public class FieldReflect
{
public static void main(String args[])throws Exception{
ReflectPoint pt = new ReflectPoint(2,5);
/*
----------------------以下内容是对公有成员变量的反射----------------------
*/
//通过反射得到描述成员变量的类:Field
Field refField = pt.getClass().getField("y");
//由于我们设计的类是依赖于具体的对象,在使用get方法时要传入具 的对象
System.out.println(refField.get(pt));
//get方法接受一个新对象,并设置 y 的值为4。此方法是依赖对象 System.out.println(refField.get(new ReflectPoint(2,4)));
/*对于静态常量,由于与对象无关,当类加载时,其值就初始化了。
如果你对反射的步骤比较熟悉了,可以尝试用一句话来完成。但是
阅读性就比较差了。
*/
System.out.println(ReflectPoint.class.getField("z").get(null));
/*
----------------------以下内容是对私有成员变量的反射----------------------
这种反射方式也称为暴力反射
*/
Field privateField = ReflectPoint.class.getDeclaredField("x");
privateField.setAccessible(true);
System.out.println(privateField.get(new ReflectPoint(12,7)));
}
}
class ReflectPoint //这个类是为了反射字段而创建的。
{
private int x; //注意它的访问权限为private
public int y;
public static int z = 6;
public ReflectPoint(int x ,int y){
this.x = x;
this.y = y;
}
}
5,方法的反射
其实反射操作的方式是类似的。我们先先来看看对于非静态方法(实例方法,依赖于对象的)的反射。
/* 方法的反射 以String类 和 自定义普通类来说明 */ import java.lang.reflect.Method; public class MethodReflect { public static void main(String args[]) throws Exception{ /* -----------------以下内容是对实例方法的反射----------------- 以String的charAt方法为例 */ //拿到字节码-->拿到Method对象。 Method methodReflect = String.class.getMethod("charAt",int.class); System.out.println(methodReflect.invoke(new String("abc"),2)); System.out.println(methodReflect.invoke(newString("defgh"),newObject[]{2})); /* public Method getMethod(String name,Class<?>... parameterTypes) 参数说明:name————方法名(要反射的那个方法的名称) parameterTypes————参数列表(要反射那个方法接收的参数) public Object invoke(Object obj,Object... args) 参数说明: obj————从中调用底层方法的对像 args————用于方法调用的参数,这里要注意,是一个可变参数列表,可以接收数组。 */ /* -----------------以下内容是对类方法的反射----------------- 以自定义普通类为例 */ //在这里我还是用一句话来完成整个方法的反射 Demo.class.getMethod("staticMethod").invoke(null); /* 对于statciMethod 方法,它参数列表为空,在使用getMethod和 invoke 方法时,可以不指定参数。 */ } } class Demo { public static void staticMethod(){ System.out.println("this is my mehtod"); } }
---------- 运行java程序 ----------
c
f
this is my mehtod
6,对接收数组参数的方法的反射
/*
重点:对接收数组参数的方法的反射
难点:反射过程中参数的传递
需求:编写程序,调用一个接收数组参数的方法
再用反射的方式实现
*/
import java.lang.reflect.Method;
public class ReflectTest
{
public static void main(String[] args)throws Exception{
//先用普通调用的方式完成要求,注意参数列表的书写
Test.test(new String[]{"com",".","hei","ma"});
//用反射的方式再来实现方法调用
Method testMethod = Test.class.getMethod("test",String[].class);
//testMethod.invoke(null,new String[]{"reflect","method","test"});
/*
在编译阶段就会有提示:
ReflectTest.java:13: 警告:最后一个参数使用了不准确的变量类型的varargs 方法的非 varargs 调用;
对于 varargs 调用,应使用 java.lang.Object[]
对于非 varargs 调用,应使用 java.lang.Object[],这样也可以抑制此警告
尝试的改成
testMethod.invoke(null,new Object[]{"reflect","method","test"});
警告消失,但是在运行时还是出现了错误:参数个数不匹配(wrong number of argumets)
造成这个问题的原因是:如果参数列表还是为:new String[]{"reflect","method","test"}
反射时,调用方法,参数在传递过程中,会拆一次包,导致参数为三个String对象: "reflect","method","test"。
但是test方法接收的是一个String[],所以出现运行错误。
解决方案:1,把这个数组再打包一次,自动拆箱后是一个String[]
2,(Object)new String[]{"reflect","method","test"}
*/
testMethod.invoke(null,newObject[]{newString[]{"reflect","method","test"}});
}
}
class Test
{
public static void test(String[] args){
for(String arg : args){
System.out.println(arg);
}
}
}
7,数组的反射
数组中的元素相同且数组的维度相同,那么反射得到的字节码就是同一份。
基本类型的一维数组可以被当做Object类型使用,不能当做Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[] 类型使用。
程序如下:
import java.lang.reflect.Array;
public class ArrayReflect
{
public static void main(String args[]){
int[] a1 = new int[]{1,2,3};
int[] a2 = new int[2];
int[][] a3 = new int[3][4];
String[] a4 = new String[]{"array","reflect","test"};
String[][] a5 = new String[2][3];
System.out.println(a1.getClass().equals(a2.getClass()));
System.out.println(a1.getClass().equals(a3.getClass()));
System.out.println(a2.getClass().equals(a4.getClass()));
System.out.println(a4.getClass().equals(a5.getClass()));
/*
System.out.println(a1.getClass() == a3.getClass());
这里会出错:不兼容的操作数类型。
用equals方法时,equals方法接收的参数类型是Object会对传入
的参数完成向上转型。然后在再比较它们是否指向同一份字节码。
所以这里用equals方法而不是 == 。
*/
/*--------------用反射来操作数组--------------
*/
Print("array reflect test");
Print(a4);
}
/*
这个方法的功能是打印传入的对象的内容。如果是一个数组,将数组
的值全部打印,如果是单个对象,则直接打印。
*/
public static void Print(Object obj){
if(obj.getClass().isArray()){ //通过反射判断传入的值是否为数组类型。
int len = Array.getLength(obj); //通过Array类得到数组的长度
for(int i=0;i<len;i++){ //循环变量打印数组中的元素
System.out.println(Array.get(obj,i));
}
}
else{
System.out.println(obj);
}
}
}
---------- 运行java程序 ----------
true
false
false
false
array reflect test
array
reflect
test
------------------------------------------
当然程序还有很多不足,但是已经表达对已有问题的阐述。在测试的时候也是选用的String 和 String一维数组。为了方便看打印内容。
8,集合框架的反射
在进行反射之前,我们要了解下集合框架的内容。
集合时用于存储对象的引用。
ArrayList集合 与 HashSet 集合的区别。
ArrayList集合的底层数据结构是链表结构,HashSet集合的底层数据结构是哈希表结构。
注意哈希值与地址值的联系和区别。
/*
反射的作用-->实现框架功能
*/
import java.io.*;
import java.util.*;
public class ReflectCollection
{
public static void main(String args[])throws Exception {
InputStream ips = new FileInputStream("E:\\JavaCode\\day15\\config.properties");//新建读取流对象并与要读取设备相关联。这里的路径是个问题。
/*
这一步这是将文件读取到流对象中了。
Properties 是一个容器,它的底层结构是Map。它是集合连接流的一座桥梁。
*/
Properties pro = new Properties();//新建一个容器
pro.load(ips); //将流中的数据存入Properties 这个集合中
ips.close(); //关闭流对象,实际是关闭流所关联的系统资源。
//通过反射的方式来加载配置文件
String className = pro.getProperty("className");
Collection con = (Collection)Class.forName(className).newInstance();//为了简化代码,Class类也可以新建一个空参实例
//新建三个实例对象,用于添加到集合中
for(int i=0;i<2;i++){
con.add(new ReflectPoint(2,3));
}
}
}
class ReflectPoint
{
private int x;
private int y;
public ReflectPoint(int x,int y){
super();
this.x = x;
this.y = y;
}
public void setX(int x){
this.x = x;
}
public int getX(){
return x;
}
public void setY(int y){
this.y = y;
}
public int getY(){
return y;
}
}