(19)基础加强-反射-类加载器的使用
来源:互联网 发布:计算机维护与网络管理 编辑:程序博客网 时间:2024/06/17 00:50
黑马程序员基础加强-反射-类加载器的使用
Java反射
反射基础分析
概述总结:
反射就是把java类中的各个成员分别映射成相应的java类;
例如,
一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示。就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等。
一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象后,得到这些实例对象后有什么用呢?怎么用呢?这正是学习和应用反射的要点。
反射的基石——Class类
Java类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,至于属性的值是什么,则是由这个类的实例对来确定的;不同的实例对象有不同的属性值,Java程序中的各个Java类是否属于同一事物,是不是可以用一个类来描述这类事物呢?这个类的名字就是Class;
Class类描述了类的名字,类的访问属性,类所属包名,字段名称列表,方法名称列表等等;学习反射,首先要明白Class类。
如:
很多的人用什么类标示?那么很多的Java类用什么标示?
人—Person
Java类—Class
Person代表人,他的实例对象就是张三,李四等这样一个个具体的人;Class类代表Java类,他的各个实例对象又分别对应什么呢?
对应各个类在内存中的字节码,例如,Person类的字节码,ArrayList类的字节码等等;
一个类被类加载器加载到内存中,占用一定的存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以他们在内存中的内容是不同的,这一个个的空间可分别用一个个对象来标示,这些对象具体相同的类型。
什么是字节码?
当源程序中用到某个类时,首先要从硬盘把这个类的那些二进制代码即:一个类编译成class放在硬盘上以后,就是一些二进制代码,要把这些二进制代码加载到内存中里面来,再用这些字节码去复制出一个一个对象来。
如何得到各个字节码对应的实例对象(Class类型),有三种方法:
1, 类名.class;例如System.class;
2, 对象.getClass();例如:new Data().getClass();
3, Class.forName(“包名.类名”);例如:Class.forName(“java.util.Data”);// 这种方式较为简单,只要知道类的完整名称即可。不需要使用该类,也不需要去调用具体的属性和行为。就可以获取到Class对象了;仅知道类名就可以获取到该类字节码对象的方式,更有利于扩展
九个预定义的Class:
1,八种基本类型(byte、short、int、long、float、double、char、boolean)的字节码对象和一种返回值为void类型的void.class。
2,Integer.TYPE是Integer类的一个常量,(但是int.class和Integer.class是不相等的false,它们是两个不同的字节码)它代表此包装类型包装的基本类型的字节码,所以和int.class是相等的。
基本数据类型的字节码都可以用与之对应的包装类中的TYPE常量表示
数组类型的Class实例对象用的方法是:Class.isArray();
注意:
只要是源程序中出现的类型,都有各自的Class实例对象,例如:int[],void…都是代表一个类型。
Class类中常用方法列举:
1, static Class forName(String className);//返回给定字符串名的类或接口相关类的Class对象;
2, Class getClass();//返回的是Object运行时的类,即返回Class对象即字节码对象;
3, Constructor getConstructor();//返回Constructor对象,反映此Class对象所代表的类指定公共构造方法;
4, Field getField(String name);//返回Filed对象,标示Class对象所代表的的类或接口指定公共成员字段;
5, Field[] getField();//返回Field对象数组,代表类中成员字段;
6, Method getMethod(String name,Class…parameterTypes);//返回一个Method对象,标示次Class对象锁代表的类的指定公共成员方法;
7,Method[] getMehtods();//返回一个包含某些Method对象的数组,是所代表的的类中的公共成员方法。
8,String getName()以String形式返回此Class对象所表示的实体名称。
9,String getSuperclass()返回此Class所表示的类的超类的名称
10,boolean isArray()判定此Class对象是否表示一个数组
11,boolean isPrimitive()判断指定的Class对象是否是一个基本类型。
12,T newInstance()创建此Class对象所表示的类的一个新实例。
通过Class对象来获取类实例
因为Class类没有构造方法,所以只能通过该类方法来获取类的实例对象
具体步骤:
1, 查找并加载指定名字的字节码文件存储进内存,并封装成Class对象;
String className = “包名.Person”;
Class clas = Class.forName(className);
2, 通过Class对象的newInstance()方法创建该Class对应类的实例对象;
Person p = (Person)clas.newInstance();
3,调用构造函数进行初始化
Person p = new Person();
示例:
package 加强Day01.黑马;class Person{private String name;private int age;Person(String name,int age){this.name = name;this.age = age;}public String toString(){return name+":"+age;}}public class ReflectDemo {public static void main(String[] args) throws Exception{Class cla = Class.forName("加强Day01.黑马.Person");//获取Person类的Class对象Person p = (Person)cla.newInstance();//通过newInstance方法获取对应类对象。System.out.println(p.toString());}}
构造方法的反射应用
Constructor类
Constructor类代表某个类的构造方法;
如果指定的类中没有空参数的构造函数,或者要创建的类对象需要通过指定构造函数进行初始化,这是怎么办呢?
首先不能再使用Class类中newInstance方法,因为要通过指定的构造函数进行对象的初始化,那么就要用到Constructor来获取构造函数
获取构造方法例子:
1, 得到某个类所有的构造方法:
Constructor[] constructors = Class.forName(“java.lang.String”).getConstructor();
2, 得到某一个构造方法:
Constructor constrctor =
Class.forName(“java.lang,String”).getConstructor(StringBuffer.class);
//获取方法时要用到的类型。
小知识点:
在getConstructor(可变参数)方法中接收的参数没有限制,为什么呢?
因为可以传入1.5新特性可变参数。
再问:那么没有可变参数再来接收多个参数怎么写呢?
我们可以通过数组类获取
得到Constructor对象后干什么呢?
查阅API文档java.lang.reflect.Constructor<T>可参见该类的方法
有了对象就可以创建实例对象(接上面示例):
1,通常方式:String str = new String(new StringBuffer(“abc”));
2,反射方式:String str = (String)constrctor.newInstance(new StringBuffer(“abc”));
Class.newInstance()方法:
例子:String obj = (String)Class.forName(“java.lang.String”).newInstance();
该方法内部是先得到默认的构造方法,然后用该构造方法创建实例对象。
该方法内部具体代码怎么写的呢?
实际上用到了缓存机制类保存默认构造方法的实例对象。
总结:
1, 在创建实例时候,newInstance()方法中的参数列表必须和getConstructor()方法中的参数列表一致。
2, newInstance()方法在构造一个实例对象时,每调用一次就构造一个对象;
3, 利用Constructor类来创建实例的好处是可以指定构造函数,而Class类只能利用无参构造函数创建实例对象。
Field类java.lang.reflect.Field
Field类代表某个类中的一个成员变量;
常用方法:
1, Field getField(String 变量);//只能获取公有和父类中公有的变量;
2, Field getDeclaredField(String 变量);//获取该类中的任意类型的变量,包括私有的;
3, setAccessible(true);//若是私有的字段,即之前变量是私有的,该方法可以将私有字段进行取消权限获取,也称为暴力访问;
4, set(Object obj,Object value);//将指定对象变量上的Field对象标示的字段设置为指定的新值;
5, Class<?> getType();//返回一个Class对象。
6, Object get(Object obj);//返回指定对象上Field表示的字段的值;
示例:
package 加强Day01.黑马;public class ReflectDemoxy {public int x;private int y;//使用快捷键快速生成构造函数public ReflectDemoxy(int x, int y) {super();//this.x = x;this.y = y;}}public class ReflectDemo {public static void main(String[] args) throws Exception{Reflect();}public static void Reflect()throws Exception{ReflectDemoxy ref = new ReflectDemoxy(3,5);Field fid1 = ref.getClass().getField("x");//通过获取Class对象,得到一个字段,公有的;//fid1它不代表一个值,不是某个变量上的某个值,它代表一个变量。//作用是类上要用它去取是某个对象对应的值。//若要取出这个变量值,我们应该明确是在某个类身上的值:fid1.get(ref)System.out.println(fid1.get(ref));//取出指定类身上的变量。Field fid2 = ref.getClass().getDeclaredField("y");//这时可以看到任何类型的变量,但是对于私有字段不能取出,所以需要Field里的方法setAccessible();暴力访问;fid2.setAccessible(true);System.out.println(fid2.get(ref));}}
练习:把一个类里所有的String类型的字段里的值b变a;package 加强Day01.黑马;public class ReflectDemoxy {public int x;private int y;public String str1 = "ball";public String str2 = "abstract";public String str3 = "haohaoxuexi";//使用快捷键快速生成构造函数public ReflectDemoxy(int x, int y) {super();//this.x = x;this.y = y;}public String toString(){return str1+":"+str2+":"+str3;}}package 加强Day01.黑马;import java.lang.reflect.Field;public class ReflectTest {public static void main(String[] args) throws Exception{// new一个对象ReflectDemoxy ref = new ReflectDemoxy(3,4);//获取所有字段Field[] fields = ref.getClass().getFields();//对所有字段遍历并判断是否与String类的字节码相等;for(Field field: fields){//if(field.getType().equals(String.class))if(field.getType() == String.class){//获取对应字段的值的字符串;String oldValue = (String)field.get(ref);//将字符串里的b换成aString newValue = oldValue.replace('b', 'a');//改对象的字段field.set(ref,newValue);}}//打印替换后的结果:System.out.println(ref);System.out.println(ref);System.out.println(ref);}}
Method类java.lang.reflect.Method
Method类代表某个类中的一个成员方法;
简单的说:
实际上我们是使用反射的方式拿到指定类的字节码里面的方法,再用这个方法去作用于某个对象。
常用方法:
1, Method[] getMethods();//只获取公共和父类中的方法;
2, Method[] getDeclaredMethods();//获取本类中所有方法包含私有
3, Method getMethod(“方法名”,参数.class);//若是空参数可以写null;因为重载,参数列表和类型可能不一致,参数也可接收可变参数。
4, Object invoke(Object obj,Object…obj(参数));//调用方法,
注意invoke是调用方法对象身上的方法:
砖家模式:谁拥有这个数据谁就是调用它的砖家,那么就应该把方法分配给它!
比如停车:
调用者:对象是车,只有车知道该怎么停下来,是车在调用刹车的动作;
指挥者:人在指挥车停车的动作,给车发出信号让车去执行,停车的动作只有车才能做到,车拥有这个停车的动作,它就是这个的砖家,那么就应该把停车方法分配给车,而不是给人;
思考:
当方法对象身上的方法即:invoke(null,1)第一个参数是null,不通过对象就调用了,说明这个方法是静态的,因为静态方法是不需要对象的。
示例:
package 加强Day01.黑马;import java.lang.reflect.Method;public class MethodTest {public static void main(String[] args)throws Exception {//获取字符串中某一个字符String str = "abc";//通常方式System.out.println(str.charAt(1));//反射方式Class cla = Class.forName("java.lang.String");Method charAtMethod = cla.getMethod("charAt",int.class);System.out.println(charAtMethod.invoke(str, 1));}}
JDK1.4和JDK1.5的invoke方法的区别:
Jdk1.5:public Object invoke(Object obj,Object…obj);
Jdk1.4:public Object invoke(Object obj,Object[] args);
安装jdk1,4的语法,需要见那个一个数组作为参数传递给invoke方法时,数组中的每个元素分别对应被调用方法中的一个参数,所以,调用charAt方法的代码页可以用jdk1.4改写为charAt.invoke(“str”,new Object[]{1})的形式。
接上面示例:
System.out.println(charAtMethod.invoke(str, new Object[]{2}));
用反射方式执行某个类中的main方法
首先明确为什么要使用反射?
因为在写源程序时,或程序写好后,不知道使用者将传入什么类,类名是什么等,但是我们知道这个类中的方法有main这个方法。这时我们可以通过反射的方式把使用者传入的类名(可定义字符串变量作为传入类名的入口,通过这个变量代表类名),在内部通过传入类名来获取main方法,然后执行相应内容。
需求:写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法;
分析:
在启动Java程序的main方法参数是一个字符串数组,即public static void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?
按jdk1.5的语法,整个数组是一个参数,而按照jdk1.4的语法,数组中的每个元素对象对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会执行哪种语法来处理呢?
Jdk1.5兼容jdk1.4语法,所以会按照1.4语法去处理,即把数组打散成若干个单独的参数,所以在给main方法传递参数时,不能使用代码mainMethod.invoke(null,new String[]{“…”})javac只有把它当做jdk1.4语法进行理解,而不会当做jdk1.5语法来解释,因此会出现参数类型不对的问题。
解决办法:
1, mainMethod.invoke(null,new Object[]{new String[]{“xxx”}});
2, mainMethod.invoke(null,(Object)new String[]{“xxx”});。编译器会做特殊处理,不会吧它当数组看,也就不会数组打散成若干个参数了。
示例:
测试类package 加强Day01.黑马;public class MainTest {public static void main(String[] args) {// 测试类for(String arg:args){System.out.println(arg);}}}package 加强Day01.黑马;import java.lang.reflect.Method;//写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法public class ReflectMain{public static void main(String[] args)throws Exception{//普通方式,因为静态所以直接类名.main()MainTest.main(new String[]{"111","222","333"});System.out.println(".............");//反射方式:String strName = args[0];//传进来的参数不确定类型不确定;Class classMain = Class.forName(strName);//获取字节码对象Method method = classMain.getMethod("main",String[].class);//调用方法对象的方法invoked,去执行//方法一:使用Object超类method.invoke(null,(Object)new String[]{"111","222","333"});//方法二:将数组打包,那个编译器拆包后还一个String[]数组。method.invoke(null, new Object[]{new String[]{"111","222","333"}});}}
数组的反射
1,具有相同位数和元素类型的数组属于同一个类型,即具有相同的Class示例对象。
示例:
<span style="white-space:pre"></span>int[] a1 = new int[3];int[] a2 = new int[4];int[][] a3 = new int[2][3];String[] a4 = new String[3];System.out.println(a1.getClass()==a2.getClass());System.out.println(a1.getClass()==a3.getClass());System.out.println(a1.getClass()==a4.getClass());System.out.println(a1.getClass().getName());System.out.println(a4.getClass().getName());打印结果是:TrueFalseFalse[I[Ljava.lang.String;
分析结果:
从这里可以看出,当同是一维数组或二维数组,并都是int类型相同,所以为true。由于字节码一定是唯一的并且字节码文件就代表这个类,所以getClass()获取的字节码文件对象是一个,地址值也是一样。
数组类型的父类Object
任何元素类型的数组他们的直接父类就一个java.lang.Object;
我们可以通过Class类里面的方法getSuperclass();返回超类
示例:
package 加强Day01.黑马;import java.lang.reflect.*;import java.util.Arrays;public class ReflectArraysTest {public static void main(String[] args) {int[] a1 = new int[]{1,2,3,};int[] a2 = new int[4];int[][] a3 = new int[2][3];String[] a4 = new String[]{"a","b","c"};System.out.println(a1.getClass()==a2.getClass());//System.out.println(a1.getClass()==a3.getClass());//System.out.println(a1.getClass()==a4.getClass());System.out.println(a1.getClass().getName());System.out.println(a4.getClass().getName());//获取超类类名System.out.println(a1.getClass().getSuperclass());System.out.println(a4.getClass().getSuperclass());//判断是否成立?是否属于对应类型Object obj1 = a1;Object obj2 = a4;//Object[] obj3 = a1;不能从int转Object,类型不匹配,基本数据类型不能转Object[] obj4 = a3;//等效于数组里面装是一个数组,而该数组是属于Object的。Object[] obj5 = a4;//String是属于Object的System.out.println(Arrays.asList(a1));System.out.println(Arrays.asList(a4)); /* Arrays.asList()方法处理int[]和String[]时的差异。 * 打印Arrays.asList(a1);还是跟直接打印a1是一样的 打印Arrays.asList(a4);就会把a4的元素打印出来。 这是因为此方法在JDK1.4版本中,接收的Object类型的数组, 而a4是String类型可以作为Object数组传入。但是a1是基本数据类型不可以作为Object数组传入,所以只能按照JDK1.5版本可变参数来处理。 在JDK1.5版本中,传入的是一个可变参数,所以a1就被当作是一个object,也就是一个参数, 而不是数组传入,所以打印的结果还是跟直接打印a1一样,就无法直接显示数组中的元素。 */ }}打印结果:true[I[Ljava.lang.String;class java.lang.Objectclass java.lang.Object[[I@1bab50a][a, b, c]
数组反射应用
若我们需要获取数组里面的值,或设置值,得到数组的长度,用反射怎么做?
在Class类中有Array这个类
演示数组反射的作用:
步骤:
1,先获取Class对象
2,用Class类的isArray()方法判断是否是数组
3,不是数组就直接打印,是数组就用Array类的getLength(obj)静态方法获取长度
4,for循环遍历数组并用Array类的静态方法get(obj,index)获取数组中的对应角标的元素
示例接上面:
ArrayTest(a4);ArrayTest("abc");}//数组的反射应用:public static void ArrayTest(Object obj){//可以接收一堆(数组)或一个类型Class clazz = obj.getClass();//通过元素获取Class对象//判断是否是数组类型的字节码if(clazz.isArray()){int len = Array.getLength(obj);//通过Array的里的方法获取长度。for(int x=0; x<len;x++){System.out.println(Array.get(obj, x));}}else{System.out.println(obj);}}
ArrayList与HashSet的比较及HashCode分析(面试重点)
ArrayList是一个有顺序的集合,相当于一个数组,当我们放一个对象进去的时候,放进去的是对象的引用,然后再次放对象。是先后顺序放进去,不管相同。
HashSet是放之前判断集合里面是否有相同的引用变量,若有就不放,若要放就必须将原来的那个删除掉;
HashCode方法的作用(面试常问)
比如:我们在查找一个集合中是否包含某个即将插入的对象,通常做法是逐一将每个每个元素与要查的对象进行比较,当发现某个元素和要查找的对象进行equals比较相等时,就停止查找并返回信息;但是这样做的效率很低,这样就有了哈希算法!
有人发明的了哈希算法来提高集合中去查找元素的效率,这种方式将集合分成若干个存储区域,每个对象可以计算出一个哈希码值,将哈希码分组,每组分别对应某个区域,再根据要查找的哈希码就可以确定该对象应该存储到哪个区域;如下图:
注意:
1, 要想HashCode方法有价值的话,前提是对象存入的是hash算法这种类型的集合当中才有价值。如果不存入是hashCode算法的集合中,则不用复写此方法。
2, 如果没有复写hashCode方法,对象的hashCode值是按照内存地址进行计算的。这样即使两个对象的内容是想等的,但是存入集合中的内存地址值不同,导致hashCode值也不同,被存入的区域也不同。所以两个内容相等的对象,就可以存入集合中。
3, 当一个对象存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则对象修改后的哈希值与最初存储进HashSet集合中的哈希值就不同了。在这种情况下,调用contains方法或者remove方法来寻找或者删除这个对象的引用,就会找不到这个对象。从而导致无法从HashSet集合中单独删除当前对象,从而造成内存泄露。(程序中某一些对象不再被使用,以为被删掉了,但是没有,还一直在占用内存中,当这样的对象慢慢增加时,就会造成内存泄露。)
示例:package 加强Day01.黑马; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; public class HashCodeDemo { public static void main(String[] args) { //Collection collection =new ArrayList(); Collection collection =new HashSet(); HashCodeTest hct1=new HashCodeTest(1,2); HashCodeTest hct2=new HashCodeTest(3,4); HashCodeTest hct3=new HashCodeTest(1,2); collection.add(hct1); collection.add(hct2); collection.add(hct3); collection.add(hct1); //hct1.setX(5); //collection.remove(hct1); System.out.println(collection.size()); } } //测试类 class HashCodeTest{ private int x; public int y; public HashCodeTest(int x,int y){ this.x=x; this.y=y; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public int hashCode() { final int prime = 31; int result = 1; result = prime * result + x; result = prime * result + y; return result; } public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; HashCodeTest other = (HashCodeTest) obj; if (x != other.x) return false; if (y != other.y) return false; return true; } public void setY(int y) { this.y = y; } public String toString() { return "HashCodeTest [x=" + x + ", y=" + y + "]"; } }
反射的作用--》实现框架功能
框架与框架要解决的核心问题
比如;开发商建设房子给用户,然后由用户自己安装门窗大家电等;
开发商做的房子就是框架,用户使用框架,把门窗插入到框架中。
框架与工具类的区别:
框架是调用用户提供的类,工具是被用户的类调用;
框架要解决的核心问题
比如我在写框架时候,你这个用户可能还在上小学,还不会写程序,那么我写的框架程 序怎么才能调用你以后写的类(门窗)呢?
因为在写程序时无法知道要被调用的类名,所以,在程序中无法直接new某个类的实例对象了,而要用反射的方式来做Class.forName();
简单的框架程序代码实现:
分析步骤:
1, 定义配置文件config.properties;,在配置文件中定义好配置信息如:
className=java.util.ArrayList,等号左边是键,右边是值;
2, 创建文件读取流读取配置文件的信息,写出配置文件的绝对路径;
3, 通过Properties类的load方法将配置信息存储到集合中;
4, 关闭读取流;
5, 通过Properties类中的getProperty()方法获取value值即某个类名;
6, 后面步骤就可以通过反射的方式用Class类来获取实例
示例:
在源程序中不出现这个类,而通过配置文件来配,在再通过反射来获取该类的实例对象
package 加强Day01.黑马;import java.io.FileInputStream;import java.io.InputStream;import java.util.Collection;import java.util.Properties;public class ReflectDemo3 public static void main(String[] args)throws Exception{// 创建文件读取流,读取配置文件信息InputStream ins = new FileInputStream("config.Properties");//将流中的数据利用集合Properties类中的load方法存储进集合Properties pro =new Properties();pro.load(ins);//关闭读取流对象;ins.close();//通过集合getProperty(键)方法获取value 即某类名String className = pro.getProperty("className");//获取Class对象,通过反射方式创建实例对象;Class clazz = Class.forName(className);Collection coll = (Collection)clazz.newInstance();}}
类加载器
概述:
类加载器就是将.class文件加载进内存,也可以将普通文件中的信息加载进内存;
具体加载方式:
Java源文件(.java)在经过java编译器编译后被转换成java字节码(.class文件)。类加载器就负责读取java字节代码,并转换成java.lang.Class类的一个实例。每个这样的实例用来表示一个java类,再通过该实例的newInstance()方法就可以创建出该类的一个对象。
基本上所有的类加载器都是 java.lang.ClassLoader类的一个实例。
Java.lang.ClassLoader类介绍
作用:
1, 就是根据一个指定的类的名称,找到或生成其对应的字节码,然后从这些字节码中定义一个java类,即Class类实例对象。
2, ClassLoader还负责加载java应用所需的资源,比如图像文件和配置文件等,将配置文件放到.class文件目录中一同打包,类加载器就会一同加载。
加载资源文件
方法一:资源文件是由类加载器ClassLoader来加载进内存,具体就是使用getClassLoader()方法获取加载器,然后用类加载器的getResourceAsStream(String name)方法,将配置文件即资源文件加载进内存。
注意:利用类加载器来加载配置文件,需要把配置文件放置的包名一起写上,不然会报异常,这种方法只有读取功能。
方法二:在java.lang.Class类中也提供了getResourceAsStream(String name)方法来加载资源文件,这个时候是配置文件是相对类文件的当前目录,所以不用写包名。其实该方法内部也还是调用了ClassLoader的方法的。
如:Person.class.getResourceAsStream(“配置文件名”);
常用方法:
getParent();// 返回该类加载器的父类加载器。
loadClass(String name);// 加载名称为name的类,返回Class实例对象
findClass(String name);// 查找名为name的类,返回的也是Class实例。
findLoadedClass(String name);// 查找名为name的已被加载过的类。
defineClass(String name,bytr[] b,int off,int len);//把字节数组b中的类容转换成java类。
关于配置文件存放路径的疑问
根据上面示例,和配置文件的加载,我们可以了解到:
1,配置文件如果和classPath目录没有联系,那么要写上绝对路径;
2,配置文件如果和classPath目录有联系,比如是在该目录中或其子目录中,那么可以省略前面路径写文件名即可。
示例:加载资源文件
package 加强Day01.黑马;import java.io.FileInputStream;import java.io.InputStream;import java.util.Collection;import java.util.Properties;public class ReflectClassLoader {public static void main(String[] args) throws Exception{// 首先创建一个Properties对象来存储配置文件Properties pro = new Properties();//简单的相对性路径读取流演示,即当配置文件和.java在同一目录//InputStream ins1 = new FileInputStream("config.properties");//类加载器能加载.class文件,那么在classPath环境变量下附带的配置文件必须也能加载,//注意它也只能在该环境变量下加载,且使用类加载器时候不能以“/”打头;//先将配置文件转为字节码文件对象,使用Class类中的getClassLoader返回该类的类加载器,//再在classPath目录下逐一的查找给定名称的资源配置文件,使用方法getResourceAsStream(Name),返回InputStream//InputStream ins2 = //ReflectClassLoader.class.getClassLoader().getResourceAsStream("加强Day01/黑马/config.Properties");//另外Class类还提供了一个方法,用加载类的加载器去加载相同包目录下的文件;InputStream ins3 = ReflectClassLoader.class.getResourceAsStream("/加强Day01/黑马/propertes/configProperties");//将流里的数据存储到定义好的缓冲区pro.load(ins3);//关闭读取流对象ins3.close();//通过键获取值即文件类型 String className = pro.getProperty("className"); Class clazz = Class.forName(className); Collection collection = (Collection)clazz.newInstance(); HashCodeTest hct1=new HashCodeTest(1,2); HashCodeTest hct2=new HashCodeTest(3,4); HashCodeTest hct3=new HashCodeTest(1,2); collection.add(hct1); collection.add(hct2); collection.add(hct3); collection.add(hct1); System.out.println(collection.size());}}---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------
- (19)基础加强-反射-类加载器的使用
- 基础加强-类加载器
- (21)基础加强-泛型加强-类加载器加强
- 基础加强_类加载器,类的加载机制
- java基础加强(反射、内省、BeanUtils工具类、类加载器、动态代理)
- 黑马程序员: 基础加强(反射、类加载器、动态代理技术、AOP、InvocationHandler)
- Java基础加强_Eclipse、枚举、反射、注解、泛型、类加载器、动态代理
- 基础加强---反射的学习
- 基础加强-Class类 反射
- java基础加强5--类加载器
- java基础加强--类加载器
- Java基础加强---ClassLoader(类加载器)
- (34)基础加强&类加载器
- JAVA--基础加强_类加载器
- 基础加强 类加载器 代理
- java基础加强04 类加载器
- Java 基础加强 - 类加载器
- 黑马程序员基础加强---类加载器
- 有限状态机的实现方式
- hadoop常见问题
- MATLAB基本操作(一):MATLAB中变量的文件存储
- commons-dbcp commons-pool 包缺失报错处理
- android 写 selector.xml 时的注意事项
- (19)基础加强-反射-类加载器的使用
- 角点匹配方法(总结篇)
- alter index rebuild 与 drop create index效率分析
- jboss启动
- cpio命令用法
- mybatis_oracle 入门案例
- Extjs4.2 Html 编辑器扩展
- Httpwatch抓包显示ERROR_INTERNET_CONNECTION_RESET问题
- Eclipse将引用了第三方jar包的Java项目打包成jar文件的两种方法