------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
1 反射的基石 Class
java是一门面向对象的语言,java如何对java类class进行描述呢?java为描述类对象(类的字节码对象.class 文件)提供了一个特殊的类Class类
用于描述一个类的属性(field),类名(name),方法(method),构造函数(constructor)等相关信息。一个类的字节码对象时唯一的。
如何获得一个类型为类 所属的类呢?即是如何去获得一个内的字节码对象呢?
有三种方式:
Person p1 = new Person();
Person p2 = new Person();
方式一:
person.getClass();//对象.getClass();
方式二:
Class.forName("java.lang.String");// 1 字节码存在已经加载进虚拟机了就直接获得
// 2 字节码未加载进虚拟机,则使用类加载器去加载该类的字节码
方式三:
类名.class 如:Person.class;
方式2最灵活; 很多框架就是基于第二种方式,通过动态的获取一个String参数来获取一个类的字节码对象。
多获得的字节码对象我们可以进行一些简单的操作。如判断是否是Array类型的类。是否是基本类型的类
如:
String str = "abc";
Class cls1 = String.class;
Class cls2 = str.getClass();
Class cls3 = Class.forName("java.lang.String");
System.out.println(cls1 == cls2);
System.out.println(cls1 == cls3);//都对应同一份字节码
System.out.println(cls1.isPrimitive());//判断是否是基本类型
System.out.println(int.class.isPrimitive());//判断是否是基本类型
System.out.println(int.class == Integer.class);//判断基本类型和器包装类对象的Class实例是否相同
System.out.println(int.class == Integer.TYPE);//包装类获取被包装类的Class对象实例
System.out.println(int[].class.isPrimitive());//只要是类型都有Class对象实例
2什么叫反射?
反射就是将一个类中的各个成员映射成对应的java类。
1 对类构造函数的反射
类的构造函数对应:Constructor类
如何通过类的字节码反射出该类的构造函数对象呢?
例如:
Constructor String.class.getConstructor();//返回具体某一个具体的(Constructor)构造函数对象
Constructor[] String.class.getConstructors();//返回所有的构造函数对象
一个类可能包含多个构造函数,如何指定 获取具体哪一个构造函数呢?
: 可通过构造函数的参数类型字节码进行区分不同构造函数。
Constructor construct = String.class.getConstructor(StringBuffer.class);
得到了一个类的构造方法对象可以做什么呢?
可创建该类对象的实例:
construct.newInstance(new StringBffer(abc));//等价于:如new String(new StringBuffer("abc"));
2 对成员变量的反射
类的成员变量对应:Field类
1 通过类字节码获取类变量名。
2 通过变量名区别不同的变量
3 getFiled(name)方法只能获得该类对外暴露的成员变量(public)
4 对private修饰的成员变量getDeclaerdFiled(name)获取。
5 对私有设定可达才能进行该变量的访问操作setAccessible(true);
6 因为获取的是类变量 必须为该变量指定具体的类对象才能获得具体的值
如:
public class Pointer {
public int x;
private int y;
public Pointer(int x, int y) {
this.x = x;
this.y = y;
}
}
Pointer p1 = new Pointer(4, 6);
Field filed = p1.getClass().getField("x");//1 通过变量名获取具体的类变量 2获得是类的变量不是某个具体对象上的变量
System.out.println(filed.get(p1)); //3 通过为该类变量指明具体的对象 获得具体对象上该变量的值
Field filed2 = p1.getClass().getDeclaredField("y");//对私有类成员变量的反射
filed2.setAccessible(true);// 暴力反射 让类中被Private修饰的成员可达,破坏封装性
System.out.println(filed2.get(p1))
-------------------------------------------------
5 成员方法的反射
1 通过函数名,和参数类型来获取某个具体的方法
如:
String str = "abc";
Method method = String.class.getMethod("charAt",int.class);//获取 String类的charAt(int index);方法 注意:参数类型是一个可变参数
method.invoke(str,1);// b 2通过获得方法对象的invoke(对象,实参)方法,来调用获得方法 注意当方法为静态方法时:可将第一个参数设为NULL invoke(null,实参列表);
需注意:对参数类型为数组的函数进行反射,得到Method对象,调用invoke();方法时;
可能会出现参数个数不匹配的异常。这是因为JDK1.5对JDK1.4的兼容引起的
对参数类型为数组类型的函数的反射
Class A
{
public void arraySort(String[] arr)
{
String sum =null;
for(String a:arr)
{
sum += a;
}
System.out.print(sum);
}
}
Class B
{
public static void main(String[]arg)
{
A a = new A();
Method m = a.getClass().getMethod("arraySort",String[].class);
m.invoke(a,new String[]{"abc","das","dasda"});//java虚拟机会拆包认为传递的是三个参数,解决办法:1 new Object[]{new String[]{"abc","das","dasda"}}
2 (Object)new String[]{"abc","das","dasda"}
原因:invoke(Object,Object...)// 当传来new String[]{"abc","das","dasda"}为兼容jdk1.4(没有可变参数,要传递多个参数是通过数组的形式)会认为传来的是三个参数
}
}
3 反射机制到底有啥用呢?
当我们在开发一些应用时:为提高程序的灵活性,可在配置文件中设置一些配置信息,然后程序通过反射机制 Class.forName();来调用具体的方法。如Spring配置文件
底层实现就是基于反射完成的。可以完成类的动态装配。执行对配置文件进行修改而不必修改源文件就能完成不一样的功能。
也可以通过反射来获取一个类的字节码---->类加载器----->加载资源和配置文件
如:
reflactTest.class.getClassLoader().getResourceAsStrea("javalearn15\classProperties.properties");
----------------------------------------------------------------------------------------
4 javaBean
1内省:IntroSpector ---->JavaBean------>特殊的Java类(方法的取名有一定的规则:get或set开头)
如:int getAge();
void setAge(int age);
j avaBean的属性是根据方法来的
Age--->如果第二字母是小写,则把第一个字母变成小写---age;
javaBean的内省操作
正是由于javaBean定义属性方法特殊的命名规则为javaBean 的反射操作提供了一些统一的操作流程,步奏。
针对这些统一的步奏,可抽象提取出一些工具(如BeanUtils)类来简化javaBean的反射操作
如:
Person p = new Person(121, "张三");
//使用属性名通过反射来设置张三的年纪
String property = "age";//javaBean的属性特点--->设置属性的方法名为 setAge(int age);
利用基本的反射原理做
//age--->Age--->setAge(int age)得有个转换过程
String methodname = "set"+property.replace('a', 'A');
Method method = p.getClass().getMethod(methodname, int.class);
method.invoke(p, 23);
利用一些工具类做
PropertyDescriptor pds = new PropertyDescriptor(property, p.getClass());
pds.getWriteMethod().invoke(p,21);
System.out.println(p.getAge());
注释:建议采用UtilBeans工具类操作
--------------------------------------------------------------------------------------------------------------------------------------
ArrayLisr 和hashSet的比较以及hashcode分析
Arraylist 底层结构是数组,当插入新的元素时直接在数组后面进行存储操作,因此ArrayList中可以存相同的元素。在基本数组类型结构的容器中对象的hashcode没用
1 一个对象hashcode 只有在将对象存储进哈希表结构的容器(如hasbSet,hashmap)时才有用
hashcode 图解