反射机制


-----------------------------------------------------------------------------------------------------
反射机制,是jdk 1.1中的新特性,也是SSH,JUnit各种框架中常用的技术.
反射,会导致程序性能,严重下降!

反射,就是把Java类中的各种成份映射成相应的Java类,参考java.lang.reflect包.
1.一个Class,代表一份字节码.
2.一个Method,代表这个字节码里面的一个方法.
3.一个Constructor,代表这个字节码里面的一个构造方法.
 
public final class Class<T>extends Object implements Serializable,GenericDeclaration,Type,AnnotatedElement类:
1.Class类所代表的,就是Java内存中的code segment中的代码,它是一个字节码格式的文件.
2.Class类所包括的内容有(基本类型+void+类+接口+数组类+枚举+注解)
---------------------------------------------------------------------------------------------------
反射机制中的常见异常:
SecurityException<----RuntimeException
IllegalArgumentException<----RuntimeException
ArrayIndexOutOfBoundsException<----RuntimeException
NegativeArraySizeException<----RuntimeException
IllegalAccessException<----Exception
InstantiationException<----Exception
InvocationTargetException<----Exception
---------------------------------------------------------------------------------------------------
Class类中的专有方法(一):
public boolean isAnonymousClass()
是否是匿名类
public boolean isLocalClass
是否是本地类
public boolean isMemberClass()
是否是成员类
public boolean isSynthetic()
是否是复合类
public boolean isInterface()
是否表示一个接口类型
public boolean isArray()
是否表示一个数组类.
public boolean isPrimitive()
是否表示一个基本类型.
public boolean isEnum()
是否表示一个枚举类型
public boolean isAnnotation()
是否表示一个注解类型
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
是否存在指定类型的注解
public boolean isInstance(Object obj)
是否是类的一个实例对象,作用与instanceof关键字一样.
public boolean isAssignableFrom(Class<?> cls)
public boolean desiredAssertionStatus()
----------------------------------------------------------------------------------------------
Class类中的专有方法(二):
public String toString()
重写父类中方法,比getName()打印更多内容
public String getName()
返回所表示的实体的名称,不是全名.
public String getSimpleName()
public String getCanonicalName()
public Package getPackage()
返回类所在的包
public ClassLoader getClassLoader()
返回该类的类加载器
public T[] getEnumConstants()
返回枚举类的值的数组
public static Class<?> forName(重载) throws ClassNotFoundException
返回Class对象,参数中,需要使用完整的类名
public InputStream getResourceAsStream(String name)
查找给定名称的资源,可用来加载配置文件
public URL getResource(String name)
查找给定名称的资源
public ProtectionDomain getProtectionDomain()
public T cast(Object obj)
强制转换并返回该对象
public <U> Class<? extends U> asSubclass(Class<U> clazz)
将父类转化为子类,此方法需要多态的支持,才能用.
public T newInstance() throws InstantiationException,IllegalAccessException
通过字节码方式,创建类的对象,只能通过空参构造.
public TypeVariable<Class<T>>[] getTypeParameters()
public Class<? super T> getSuperclass()
返回当前类的父类的字节码
public Type getGenericSuperclass()
public Class<?>[] getInterfaces()
返回类所实现的接口
public Type[] getGenericInterfaces()
public Class<?> getComponentType()
public int getModifiers()
返回表示各种元素的修饰符的整数
public Object[] getSigners()
返回此类的标记
public Method getEnclosingMethod()
public Constructor<?> getEnclosingConstructor()
public Class<?> getDeclaringClass()
public Class<?> getEnclosingClass()
-------------------------------------------------------------------------------------
Class类中的专有方法(三):
1.public Class<?>[] getClasses()
返回表示公共成员的Class对象数组
public Class<?>[] getDeclaredClasses() throws SecurityException
返回表示已声明成员的Class对象数组  
                       
2.构造方法:
public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException,SecurityException
返回与指定的parameterTypes匹配的公共构造方法的Constructor对象
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) throws NoSuchMethodException,SecurityException
返回已声明的指定参数列表的Constructor对象
public Constructor<?>[] getConstructors() throws SecurityException
返回表示公共构造的Constructor对象数组
public Constructor<?>[] getDeclaredConstructors() throws SecurityException
返回表示已声明的构造Constructor对象数组
3.成员变量:
public Field getField(String name) throws NoSuchFieldException,SecurityException
返回name指定的该类的Field对象
public Field getDeclaredField(String name) throws NoSuchFieldException,SecurityException
返回已声明的指定字段的Field对象
public Field[] getFields() throws SecurityException
返回表示公共字段的Field对象数组
public Field[] getDeclaredFields() throws SecurityException
返回表示已声明成员的Class对象数组
4.成员方法:
public Method getMethod(String name,Class<?>... parameterTypes) throws NoSuchMethodException,SecurityException
返回与name和parameterTypes匹配的Method对象
public Method getDeclaredMethod(String name,Class<?>... parameterTypes) throws NoSuchMethodException,SecurityException
返回已声明的匹配的Method对象
public Method[] getMethods() throws SecurityException
返回表示公共方法的Method对象数组
public Method[] getDeclaredMethods() throws SecurityException
返回表示已声明方法的Method对象数组
5.注解:
public <A extends Annotation> A getAnnotation(Class<A> annotationClass)
返回其中的注解
public Annotation[] getAnnotations()
返回此元素上所有注解
public Annotation[] getDeclaredAnnotations()
返回此元素声明的所有注解
------------------------------------------------------------------------------------------------
Class类没有构造方法,有4种方法,可以得到Class类的实例:
1.对象名.getClass()
 Object类中的方法,而且"类的实例"只能使用这种方式
2.类名.class
 通用方式,而且"基础类型"和void只能使用这种方式
3.类名.TYPE
 基本类型的封装类和Void类,有一个专有"常量"
4.Class.forName(String name)
 name必须是类的全名,框架常用方式,从配置文件中读取类名.
Declared方法的作用:
1.无Declared的方法 (必须public,可以是继承)
2.有Declared的方法 (可以是private,不能继承)
注意,Declared方法,可以看到但不能调用private类成员,若要调用,可使用暴力反射.
得到类的字节码,会有2种情况:
1.类已加载  (直接返回)
2.类没加载  (先加载,再返回)
案例:
System.out.println(void.class == Void.class);     //结果false
System.out.println(int.class == Integer.class);     //结果false
--------------------------------------------------------------------------------------------------
类范围内的"暴力反射":
1.只能在"类"的范围内有效,包对反射的支持能力有限.
2.如果没有了"类"的访问权限,"暴力反射"也无能为力,比如----->只有包内权限的类.
java.lang.reflect.AccessibleObject   (类,有构造)
  |--Constructor<T>   (类,无构造)
  |--Field   (类,无构造)
  |--Method   (类,无构造)
注意,除了"暴力反射"外,使用反射,也可以突破泛型的限制.
AccessibleObject类中的部分方法:
public void setAccessible(boolean flag) throws SecurityException
这就是传说中的暴力反射
public boolean isAccessible()
是否可以暴力反射
public static void setAccessible(AccessibleObject[] array,boolean flag) throws SecurityException
Constructor<T>类中的部分方法:
public Class<T> getDeclaringClass()
返回构造方法所属于哪个类
public int getModifiers()
返回构造方法前面的修饰符
public T newInstance(Object... initargs) throws InstantiationException,IllegalAccessException,IllegalArgumentException,InvocationTargetException
jdk1.4没有可变参数,使用数组参数.
注意,此方法的返回值为Object类型,必须转换数据类型(如果使用泛型,就不用转换了).
案例:
利用反射重写new String(new StringBuffer("abc")),打印第3个字符.
思路:
1.Constructor<T>类中有newInstance方法,可变参数
2.Class<T>类中有newInstance方法,空参构造
Field类中的部分方法:
public Object get(Object obj) throws IllegalArgumentException,IllegalAccessException
取出字段在哪个对象上面的值
public void set(Object obj,Object value) throws IllegalArgumentException,IllegalAccessException
设置字段在哪个对象上面的值
案例:
使用暴力反射,调用类中的私有成员.
案例:
把对象身上,所有String类型的成员变量的值,中的'b'改为'a'.
问题:
碰到一个小问题   if(true) int i = 0; //报错了,苦逼的Java语法.
原因:
1.编译器认为这是2句话,所以报错.
2.如果这样    if(true) int i;     //这样也报错,因为局部变量没有初始化.
Method类中的部分方法:
public Object invoke(Object obj,Object... args) throws IllegalAccessException,IllegalArgumentException,InvocationTargetException
方法调用,方法名+可变参数列表(jdk1.4为Object数组)
注意,如果invoke的第1个参数为null,说明该Method对象对应的是一个静态方法.
案例:
利用反射重写str.charAt(3)方法,使用1.4和1.5的参数类型.
说明:
关于rt.jar中的包的命名规则.
1.以com,org,sun打头的包,都是内部用的,不建议外部使用
2.以java,javax打头的包,都是可以用的.
设计模式:
专家模式,谁拥有数据,谁就是干这个数据的专家,那么这个方法,就应该分配给谁.
---------------------------------------------------------------------------------------------------------------------
数组的多态机制和可变参数的原理剖析,
案例:
利用反射重写Test.main(new String[]{"111","222","333"}),
问题:
在invoke中匹配main中的可变参数时,运行时报错?
答案:
1.无论是jdk1.4还是jdk1.5,方法invoke都会有一个拆包的动作.
2.出现这种现象,与jdk1.4和jdk1.5并没有太大的关系.
 
揭示---可变参数的编译本质,
1.方法声明,中的可变参数,在编译的过程中,直接被转换为数组.
2.方法调用,中的可变参数,在编译的过程中,编译器会检查"现有参数"类型是否可赋值给<?>[ ],检查的过程中遵从"多态机制"!
3.如果可赋值,编译器不修改"现有参数",如果不可赋值,编译器试图把"现有参数"打包为<?>[ ]类型的数组.
注意,
1.编译器只会检查实参的可赋值性,并不会检查实参的真实类型与形参是否匹配!
2."可变参数"在编译过程中就会被干掉,所以编译后的代码里没有"可变参数"的概念.
---------------------------------------------------------------------------------------------------------------------------------------------------- 
可赋值性与真实类型,之间的区别:
System.out.println((Object)"4661" instanceof String);    //结果true,可见真实类型是一样的,
String s = (Object)"4661";                               //报错,尽管真实类型一样,也具有不可赋值性!!!
System.out.println((Object)(new String[5]) instanceof Object[]);  //结果true,可见真实类型是一样的,
Object[] objs = (Object)(new String[]{"1546","4545"});            //报错,尽管真实类型一样,也具有不可赋值性!!!
System.out.println((Object)(new int[5]) instanceof Object[]);           //结果false,这个真实类型不一样,
--------------------------------------------------------------------------------------------------------------------------------------------------- 
案例:
不用for循环,打印数组中的值.
思路:
使用java.util.Arrays工具类中的方法,
public static <T> List<T> asList(T... a)
返回指定数组的列表视图(可变参数兼容jdk1.4数组)
System.out.println(Arrays.asList(new String[ ]{"111","222","333"}));   //打印结果[111, 222, 333]
System.out.println(Arrays.asList(new int[ ]{5,9,2,95,5}));         //打印结果[[I@28b56559]
原理:
参见上面的---可变参数的编译本质.
案例:
数组的测试.
Object obj1 = new int[ 3 ];
Object[  ] obj4 = new int[ 5 ][ 3 ];
Object[  ] obj3 = new int[ 3 ];  //报错,数组多态.
Object obj2 = new String[ 3 ];
Object[  ] obj5 = new String[ 3 ];
思路:
无论怎么写,数组都始终遵循多态机制,利用多态判断即可.
---------------------------------------------------------------------------------------------------------------------
数组的反射,
java.lang.Object
  |--java.lang.reflect.Array(工具类)
Array工具类中的部分方法:
public static int getLength(Object array) throws IllegalArgumentException
返回数组对象的长度
public static Object get(Object array,int index) throws IllegalArgumentException,ArrayIndexOutOfBoundsException
返回数组对象中的指定元素
public static Object newInstance(Class<?> componentType,int length) throws NegativeArraySizeException
返回指定类型和长度的一维数组.
public static Object newInstance(Class<?> componentType,int... dimensions) throws IllegalArgumentException,NegativeArraySizeException
返回指定类型和长度的多维数组.

案例:
利用反射打印数组中的内容.
思路:
使用clazz变量.
-----------------------------------------------------------------------------------------------------------
ArrayList和HashSet比较:
1.有序可以重复,甚至可以重复放同一个对象.
2.无序不可重复,也能重复放同一个对象,就是放不进去而已.
HashSet会先行调用对象的equals方法进行比较.
Eclipse自动生成的代码:
1.hashCode,
 值类型,直接 +
 引用类型,调用hashCode
2.equals,
 值类型,直接 =
 引用类型,调用equals
案例:
说说hashCode的作用.
补充:
1.哈希算法,
为了提高从集合中查找元素的效率,将集合分成若干存储区域,
集合中的每个对象,可以计算出一个哈希码,将这些哈希码进行分组,
每一组哈希码对应一个集合中的存储区域,
最后,根据对象的哈希码,应可以确定该对象存储在哪个区域.
2.HashSet集合,
Object类中定义了hashCode,可以返回任何对象的哈希码,是根据对象在内存中的地址推算出来的.
HashSet集合根据对象的这个哈希码,找到相应存储区域,并在区域内equals比较,
所以,HashSet集合,具有很好的对象检索性能.
例如:  区域值=哈希码%32;  对32取模,就可以得到32个存储区域.
只有在这种哈希算法实现的集合中,hashCode才有用,才有价值.
equals和hashCode的关系:
1.如果2个对象equals的话,hashCode也应该相等,反之则不成立,不然哈希类型集合就会出错.
例如:  System.out.println("BB".hashCode() == "Aa".hashCode());  //结果true
2.由此可见,hashCode只是一个后备的方法,真正起决定性作用的还是equals方法.
内存泄露:
当对象存储进HashSet后,就不能修改参与计算hashCode的字段,
否则,对象的哈希值就与所在存储区域不相符了,
此时contains方法返回false,造成remove方法删除失败,集合中还有,就是删不了了.
实验:
Collection<Person> collection = new HashSet<Person>();
Person p = new Person();
System.out.println(collection.add(p));  //结果true
p.weight = 85;
System.out.println(collection.remove(p));  //结果false
以上,将永远迷路找不到,contains和remove方法也失效了!
--------------------------------------------------------------------------------------------------
用反射写个小框架
使用别人的类的方式:
1.自己的类去调用别人的类--->工具
2.别人的类去调用自己的类--->框架
案例:
通过框架的方式,重写上面的实验
思路:
config.properties放在了项目根目录下
如何选择IO类,(先设备,后缓冲)
如何处理IO类的异常,(使用2个null即可)
java.lang.Object
 |--java.util.Dictionary<K,V>(抽象类)
   |--java.util.Hashtable<Object,Object>(类)
     |--java.util.Properties(类)
Properties类,是   Map+IO   的产物.
Properties类中的部分方法:
1.读
public void load(InputStream inStream) throws IOException
public void load(Reader reader) throws IOException
从输入流中读取属性列表  (键和值)
public String getProperty(String key)
查找并返回键的值
public String getProperty(String key,String defaultValue)
如果没找到,返回defaultValue
2.写
public void store(OutputStream out,String comments) throws IOException
适合load(InputStream inStream)的格式,加点描述.
public void store(Writer writer,String comments) throws IOException
适合load(Reader reader)的格式,加点描述.
public void list(PrintStream out)
public void list(PrintWriter out)
输出到打印流
答案:
视频,基础加强,第27集
-------------------------------------------------------------------------------------------------------------------
配置文件的路径的问题,
不同的方法,支持的文件路径,又有所不同,
1.使用 IO 系统,能读写.
2.使用类加载器,只能读,不能写,因为它没有OutputStream.
(一)FileReader和FileInputStream方法:
这2个方法,都是依赖于操作系统而言的,
1.相对路径---可以用---指的是当前工作目录,
   在Eclipse中运行程序,当前工作目录,就是项目根目录,
2.绝对路径---可以用---指的是从操作系统的盘符开始的路径,
3.根路径 / ---可以用---还是指的是操作系统的根路径,
   比如有文件D:/a/b/c.txt,使用 /D:/a/b/ 和 /a/b/ 这2种方式都可以.
(二)类加载器的getResourceAsStream方法:
1.相对路径---可以用---指的是classpath的根目录,
2.绝对路径---不能用---因为classpath目录有多个,操作系统路径只有一个,
3.根路径 / ---不能用---还是因为classpath代表的是多个目录,
(三)使用Class类的getResourceAsStream方法:
这种方式,内部调用的还是,类加载器的加载方法,(只能读不能写).
1.相对路径---可以用---指的是当前类所在的目录,
2.绝对路径---不能用---因为是基于classpath的,而classpath目录有多个,操作系统路径只有一个,
3.根路径 / ---可以用---指的是classpath的根目录,
注意,建议使用,应用程序类装载器,也就是系统类装载器,ClassLoader.getSystemClassLoader().
Eclipse中的src目录和bin目录的关系:
1.(点保存时)目录src中的.java文件,将被编译后,放到bin对应目录下.
2.(点保存时)目录src中的非.java文件,将被直接复制到bin对应目录下.
Java中的类装载器:
java.lang.Object
    |--java.lang.ClassLoader(抽象类)
          |--java.security.SecureClassLoader(类)
                |--java.net.URLClassLoader(类)
                     |--sun.misc.Launcher$ExtClassLoader(静态内部类)
                     |--sun.misc.Launcher$AppClassLoader(静态内部类)
ClassLoader中的部分方法:
public URL getResource(String name)
首先搜索资源的父类加载器,然后虚拟机的内置类加载器的路径,还没有就调用findResource(String) 来查找资源
public Enumeration<URL> getResources(String name) throws IOException
同上.
protected URL findResource(String name)
指定从何处查找资源
protected Enumeration<URL> findResources(String name) throws IOException
返回表示所有具有给定名称的资源的 URL 对象的枚举
public static InputStream getSystemResourceAsStream(String name)
从加载类的搜索路径加载资源
public InputStream getResourceAsStream(String name)
返回读取指定资源的输入流
public static ClassLoader getSystemClassLoader()
拿到系统类加载器,系统类加载器,就是应用程序类加载器.
--------------------------------------------------------------------------------------------------------------
 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值