Java基础加强之反射

反射

最近将以前总结的资料整理后发在网上,供自己以后的查漏补缺,以及回顾。

★★★★★

反射的基石---->Class类(反射比较占用性能)
Class获得的三种途径: 


1.类名.class 例如:System.class
2.对象.getClass(). 例如:new Date().getClass();
3.Class.forName("类名").例如: 
Boolean.TYPE, Character.TYPE, Byte.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE, Void.TYPE :为各个基本类型的包装类中表示基本数据类型的字节码文件。
___________________________________________________________________________________________________________________________________________________________________


Class中的方法:
 boolean isArray() 
          判定此 Class 对象是否表示一个数组类 


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


只要是在源程序中出现的类型,都有各自的class实例对象。例如:int[],void.....




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


例如:变量,方法,构造方法,修饰符,包:Method,Constructor,Package...


___________________________________________________________________________________________________________________________________________________________________


★★★反射之-----》构造函数(Constructor类)
class-->constructor-->new Object


1.getConstructor(参数).通过参数,来匹配要获得哪个构造方法。(返回值为,单个构造方法,Construct)。
2.getConstructors(),返回反射类class中,所有的构造方法。(返回值为,构造方法的数组Construct[])。
3.java向开发者,提供了一个比较方便的获得反射类对象的方法,就是class.newInstance(),不过是通过无参的构造方法的,这个方法的实质还是在class类的newInstance()内使用了Constructor类,获得反射类空的构造方法。(例如:String obj = String.class.newInstance())


//new String(new StringBuffer("abc"));

//指明要得到哪个构造方法,通过其构造方法的参数列表可以确定
Constructor con = String.class.getConstructor(StringBuffer.class);

//通过构造类的对象中的newInstance方法获得,一个新的对象(对应的。class哪个类的对象)。(注意:这个两行代码的参数必须是相同的)
//因为这两行代码只有在运行时,才能确定是哪个类的构造方法,编译时是不管的。所以newInstance的返回值为Object,需要强转为相应的类
String s2 = (String)con.newInstance(new StringBuffer("abc"));

/*
* java程序的开发要分为:编译时期和运行时期。
* 编译时期:只是由编译器来检查语法错误,并不进行,逻辑,之类的判断,只是检查语法和将java源程序转化为字节码文件class。
* 运行时期:运行时,才将需要的包导入,以及进行逻辑的判断。
* 像对于上面的两行代码来说,编译器在编译时期,会检查到con.newInstance的方法返回值是Object类型,但是你却将其赋给了String,
*类型不匹配会出现错误。(这样错误的根本是,在编译时期编译器不知con是哪个类的构造函数)所以需要强转为String。
*
* */
____________________________________________________________________________________________________________________________________


★★★反射之-----》变量 (Field类)


class-->Field
1.getField("参数"):参数为class类中指定的变量的名字。注意:getField()方法只是得到类中可见的变量,像private就拿不到。
2.getDeclaredField("参数"):参数为class类中指定的变量的名字。注意:getDeclaredField()方法可以得到所有的变量,不管其访问权限。
3.【强行访问】setAccessible(boolean):将此对象的 accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查,值为 false 则指示反射的对象应该实施 Java 语言访问检查(强行获取).



//测试反射的Test类:
ReflectPoin
{   private int x; 
   public int y;
}
  ReflectPoint pt1 = new ReflectPoint(3,5);


   示例代码:
Field fieldY = pt1.getClass().getField("y"); //getField只是得到类中可见的变量,像private就拿不到
//fieldY 的值是5?错!!fieldY只代表一个变量,不代表其表示的值要想得到其值,
//要使用其的get(Object obj)方法 . Object get(Object obj):返回指定对象上此 Field 表示的字段的值。
System.out.println(fieldY.get(pt1));

Field fieldX = pt1.getClass().getDeclaredField("x"); //getDeclaredField可以得到所有的变量,不管其访问权限
//将此对象的 accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
//值为 false 则指示反射的对象应该实施 Java 语言访问检查(强行获取)
fieldX.setAccessible(true);
System.out.println(fieldX.get(pt1));

    ****************
需求:
将任意对象的所有String类型的成员变量所对应的字符串的内容中的'b'改为a


private static void changeStringValue(Object obj) throws Exception {

Field[] fields = obj.getClass().getFields();
for (Field field : fields) {
//if(field.getType().equals(String.class));
//因为字节码文件就只有一份,所以,可以用==来判断,用equals方法,其实是使用Object类中的equals方法,也是==比较
if(field.getType() == String.class)
{
String oldValue = (String) field.get(obj);
String newValue = oldValue.replace('b', 'a');
field.set(obj, newValue);
}
}
}
    ****************
/*
* 代码解读:
*
* 先拿到所有的变量字段,通过getFields()
* Field[] fields = pt1.getClass().getFields();

*  遍历所有字段,进行匹配检查,看该字段是否为String类型。
* for(field : fields)
* {
* if(field.getType() == String.class)
* {
* //如果确认为String,那么将其值进行改变。注意:通过get()方法获得到的值,返回值为Object,需要进行强制转换
* String oldValue = (String) field.get(pt1);
* //按要求替换字符
* String newValue = oldValue.replace('b','a');
* //将对象中的值改变.set(Object obj, Object value) 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
* field.set(pt1,newValue);
* }
* }

* */
____________________________________________________________________________________________________________________________________


★★反射-->方法(Method)
Class--->Method 

  1. 通过getMethod("方法名",参数类型.class .....),获得某一类中一个具体的方法。
  2. 通过getMethods(),获得一个类中,所有的方法。返回是一个数组。


    获得了方法对象,可以通过invoke()方法来调用,invoke(Object obj, Object... args)  对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。第一个参数为指定对象,第二个为参数列表的各个类型,通过可变参数来实现。(注意:如果获得方法时静态的,那么第一个参数一定要为null)。 
    
//str1.charAt(1); 先得到String类上的CharAt方法。getMethod.
Method methodCharAt = String.class.getMethod("charAt", int.class);
//Object invoke(Object obj, Object... args)  对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
//通过得到的方法,调用指定对象身上的方法invoke()
System.out.println(methodCharAt.invoke(str1, 0));//存在可变参数
System.out.println(methodCharAt.invoke(str1, new Object[]{1}));//1.4的调用方法,没有可变参数



例子:


/*
* 需求:用反射方式执行某个类中的main方法(本例采用TestArguements类)
* 目标:写一个程序,这个程序能根据用户提供的类名,去执行该类中的main方法。

* */
//获得要指明用户提供的类的名字
String startingClassName = args[0];
//通过class的第二中方式,Class.forName("name");的方式先获得class类,然后获得该类的名字为main,参数为String[]的方法
Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class);
//这一块,如果直接传String[],则根据jdk1.5版本要兼容1.4,系统会自动认为这个是一包参数,会打开,包。这样等于参数为3个,而要求的是一个,
//所以,会报参数个数不匹配异常。 所以解决的方法是,将这个参数再打一层包,这样就可以了。
mainMethod.invoke(null, new Object[]{new String[]{"11","22","33"}});


class TestArguements{
public static void main(String[] args){
for (String string : args) {

System.out.println(string);
}
}
}


____________________________________________________________________________________________________________________________________


★★★反射-->数组

每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象!!!!!!!!!
含义为如果数组的元素类型相同,维数相同,则视为同一个Class对象. 注意:这里所指的是数组的维数,而不是元素个数




int[] arr1 = new int[3]//[]{1,4,5,6};
int[] arr2 = new int[4];
int[][] arr3 = new int[2][3];
String[] strs = new String[3]//[]{"aaa","ddd","ccc"};
System.out.println(arr1.getClass() == arr2.getClass());//true
System.out.println(arr1.getClass() == strs.getClass());//false
System.out.println(arr1.getClass() == arr3.getClass());//false
System.out.println(arr1.getClass().getName());
//数组的基类(父类)为:Object
System.out.println(arr1.getClass().getSuperclass().getName());
System.out.println(strs.getClass().getSuperclass().getName());

利用数组的父类为Object,则有以下结论:


Object aObj1 = arr1;
Object aObj2 = arr2;
//Object[] aObj3 = arr1; //基本类型的数组时不能转换为 Object[] 这种形式,只能当做Object使用,因为基本类型并不是Object类型。非基本类型的数组既可以当做 Object又可以当做Object[]使用
Object[] aObj4 = arr3;
Object[] aObj5 = strs;

//利用上面结论有下面的例子:
System.out.println(arr1);
System.out.println(strs);
System.out.println(Arrays.asList(arr1));
System.out.println(Arrays.asList(strs));
/*
*输出结果: 
* [I@5fd1358f
* [Ljava.lang.String;@2013706e
* [[I@5fd1358f]
* [aaa, ddd, ccc]


*   分析:在jdk1.4中因为没用可变参数,使用的方法时Object[] 传参方式。1.5则兼容1.4的基础上,增加一个可变参数(类型为Object)
*的方法。当我们使用asList方法时jvm会自己判断,你给的参数能否转换为Object[]这种形式,如果能,就调用1.4的方法,此时在函数内部
*会将数组打开,取出一个一个元素,转化为List集合。(所以会打印出原数组元素)。
*   如果不能转换为Object[]的形式,例如:int[],这类基本类型数组。那么jvm则会判断为1.5的可变参数的形式,这种传参,方法在内部并不会
*将其拆开,而是将其认为一个整体作为Object存起来。
*
* 这样打印的结果,就不同了,第三个打印出来的是一个集合中存的一个一维数组,
* 而第四个打印则是,一个集合中存入的元数组的元素。
**/




练习:
//需求:写一个打印函数,要求,接受的是数组就打印数组元素,不是数组就打印其本身


private static void printObject(Object obj) {

Class clas = obj.getClass();
if(clas.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);
}

}


这里有一个小知识点,就是数组中到底能不能存不同类型的值了??答案是可以的。因为java中不管是那种类型,其必然是Object的子类,那么对于一个Object[]数组来说
就可以存的值的真实类型就可以不同了,因为可以统称它们为Object类型。
例如:Object[] obj = new Object[]{"asdc",1,'e',12.3f,12.5};
____________________________________________________________________________________________________________________________________

★★★★★★
    反射的作用---->实现框架功能
框架是提前写好的类。


  框架和工具类的区别:
    工具类是被用户的类调用,而框架则是调用用户提供的类。
  框架要解决的核心问题
    因为写程序时无法知道要被调用的类名,所以,在程序中无法直接new某个类的实例对象,而是要用反射方式来做。
 
例子:写了一个配置文件config.properties,在配置文件中,指明了所用的类,
   现在要使用反射的方法动态生成这个对象(这样就可以在编程的时候不指定,或确认类名照样可以继续编程)


//首先加载properties文件
InputStream  ips = new FileInputStream("config.properties");
//定义一个properties对象,来存放读取的config文件
Properties props = new Properties();
//读取配置文件config
props.load(ips);
//关闭资源 
ips.close();
//拿到所需要的信息
String className = props.getProperty("className");
//动态生成对象
Collection collection = (Collection)Class.forName(className).newInstance();


config.properties文件中存放的数据:
className=java.util.HashSet
____________________________________________________________________________________________________________________________________


★★★
    类加载器管理资源和配置文件
InputStream ips = ReflectTest.class.getClassLoader().getResourceAsStream("day01/config.properties");


//类加载器方式配置和管理资源和配置文件,getClassLoader()方法为获得指定类的类加载器。
这种方法在框架开发的时候用的特别多
这样做其实是有点麻烦的,每次加载还需要显示调用类加载器,其实可以直接使用class中的方法
InputStream ips = ReflectTest2.class.getResourceAsStream("config.properties");
//没有使用类加载器,而是使用了class中提供的方法,但这时在指定路径时使用相对路径,如果文件没有和类文件放在一起,则在前面要加相对的路径











































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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值