黑马程序员----java基础--反射机制

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

 

一、反射的基本描述

  Java反射机制是在运行状态中,对于任意一个类(class文件),都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象方法的功能称为java语言的反射机制。动态获取类中信息,就是java反射。可以理解为对类的解剖。如果想要对指定名称的字节码文件进行加载并获取其中的内容并调用,这时就使用到了反射技术。

简单一句话就是:反射就是把java类中的各种成分映射成相应的java类。

二、反射的基石---Class类

1、了解Class类

  Java程序中把各个对象共性的东西向上抽取得到了Object类,同理各个java类属于同一类事物,我们向上抽取就能得到描述这类事物的Java类名就是Class。比如:描述很多人的时候我们会定义一个Person类,那么描述很多java类的时候我们就用Class类来描述众多的class类。Class是Java程序中各个Java类的总称;它是反射的基石,通过Class类来使用反射。

  一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间可分别用一个个的对象来表示,这些对象显然具有相同的类型,这个类型就是Class类型。

  用于描述字节码的类就是Class类,创建对象,可以提取字节码文件中的内容,如字段、构造函数、一般函数。该类就可以获取字节码文件中的所有内容,那么反射就是依靠该类完成的。想要对一个类文件进行解剖,只要获取到该类的字节码文件对象即可。java类的Class类提供一系列的方法来获得其中的变量,方法,构造函数,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们就是Field(字段、成员变量)、Method(一般方法)、Constructor(构造函数)、Package(包)等等。

2、获取字节码对应的实例对象(Class类型)的三种方法

(1)、Class clazz=对象.getClass();      

 Class clazz=new Person().getClass();

  这种方法是调用了Object类中的getClass()方法。这种方法每次都需要知道具体的类并且创建该类对象,以及调用getClass()方法。非常的繁琐和不利于后期程序的扩展性。

(2)、Class clazz=类名.class;  

Class clazz=System.class;

  这种方法相对简单只需要用到类名就可以了,因为任何数据类型都具备一个静态的属性.class来获取其对应的Class对象,但是还是要明确用到类中的静态成员。

(3)、Class clazz=Class.forName("包名.类名"); 

String className="cn.itheima.Person";
Class clazz=Class.forName(className);

  这种方法是最为常用的方法,该方法不需要创建对象,也不用调用对象的方法,只需要通过给定的类的字符串名称就可以获取该类。更为方便,扩展性更强。这种方法在以后的开发中非常的常见应该重点掌握。

注:1.这三种方法获取的字节码对象都是相同的,只存在一份。

  2.int.class==Integer.TYPE ,boolean、byte、char、short、int、long、float 和 double都有对应的装饰类.TYPE。

  3.所有具有相同元素类型和维数的数组都是一个 Class 对象。

  4.基本的 Java 类型(boolean、byte、char、short、int、long、float和double)和关键字void也表示Class对象。  

3、Class类中的常用方法

  通过查看API帮助文档发现:Class类没有构造函数,所以不可以直接new创建对象,说明内部一定有可以返回本类对象的静态方法,这个方法就是forName()方法。方然上面的三种方法都可以获取到Class对象的。既然有了Class对象那么我们就要用其方法。

 1 static Class<?>    forName(String className);//返回本类Class对象
 2 T   newInstance();//创建此Class对象的无参 构造函数
 3 boolean  isPrimitive();//判断指定的Class对象是否是一个基本类型
 4 boolean  isArray();//判断此Class对象是否是一个数组类
 5 Class<? super T>   getSuperclass();//返回此Class类所表示的实体的父类Class
 6 String   getName();//返回Class对象所表示的实体String名称
 7 String   getSimpleName();//返回源代码中给出的底层类的简称
 8 Package  getPackage();//获取此类的包
 9 int  getModifiers();//返回此类或者接口的修饰符
10 ClassLoader  getClassLoader();//获取此类的类加载器
11 Class<?>[]  getClassess();//返回类中成员所有公共类和接口
12 Class<?>[]  getDeclaredClasses();//获取类的成员的所有类和接口
13 Constructor<T>   getConstructor(Class<?>...参数类型);//获取类中指定参数的公共(Public)构造函数
14 Constructor<?>[]   getConstructors();//返回类中所有公有构造函数
15 Constructor<T>   getDeclaredConstructor(Class<?>...参数类型);//获取带有指定参数列表(公共、保护、默认及私有)的构造函数
16 Costructor<?>[]  getDeclaredConstructors()//获取类中所有的构造函数
17 Field  getField(String name);//获取类中指定公有成员变量。name表示的是指定的所需成员变量名
18 Field[]  getFields();//获取类中所有公有的成员变量
19 Field  getDeclaredField(String name);//获取类中指定的成员变量
20 Field[]  getDeclaredFields();//获取类中所有成员变量
21 Method  getMethod(String name,Class<?>...参数类型);//获取类中指定公有成员方法,name代表方法名
22 Method[]  getMethods();//获取类中所有公有的成员方法
23 Method  getDeclaredMethod(String name,Class<?>...参数类型);//获取类中指定的成员方法
24 Method[]  getDeclaredMethods();//获取类中所有的成员方法。包括公共、保护、默认和私有方法,但不包括继承的方法。
25 Class<?>[]  getInterfaces();//获取全部接口
26 InputStream  getResourceAsStream(String name);//查找指定名称的资源

需求:用Class方法创建一个无参构造函数

//Person p=new Person();
Person p=(Person) Class.forName("com.itheima.Person").newInstance();

 三、获取Class中的构造函数(Constructor类)  

  以前我们创建对象先根据被new的类的名称找寻该类的字节码文件,并加载进内存,并创建该字节码文件对象,并接着创建该字节文件的对应的对象。如:Person p=new Person()。现在我们可以通过反射Class.forName(“类名”).getInstance()来创建无参的构造函数,但是这种方法有一个弊端,就是:假如类中没有无参构造函数怎么办或者创建指定的构造函数又该怎么办?这时候Class类中的newInstance都无法完成。这时候就必须先获取这个构造函数——Constructor。然后通过Constructor中的newInstance(Object ...参数列表)的方法创建指定的构造函数。

  那么首先了解一下Constructor类的继承体系,以方便我们一下更好的学习。  

  java.lang.Object

     |---java.lang.reflect.Array

     |---java.lang.reflect.AccessibleObject

         |---- java.lang.reflect.Constructor<T>

      |---- java.lang.reflect.Field

      |---- java.lang.reflect.Method

   这里我们需要知道AccessibleObject类中有void setAccessible(boolean flag) 方法,当flag为true的时候可以对私有成员的访问取消权限检查,暴力访问。

1、Constructor类中的方法 

1 String  getName();//以字符串形式返回构造方法的名称
2 T  newInstance(Object...initargs);//创建实例对象
3 void  setAccessbie();//暴力访问私有构造方法
4 Class<?>[]  getParamterTypes();//获取对象所表示构造方法的形参类型
5 int  getModifiers();//获取构造方法的修饰符的整数形式

 2、用Constructor对象创建对象的步骤

  (1)、首先获取Class字节码对象

    Class clazz=Class.forName(String className);

  (2)、获取到字节码对象后调用其getDeclaredConstructor()方法获取Constructor对象

    Constructor con=clazz.getDeclaredConstructor(String.class,int.class);

  (3)、通过获取到的Constructor对象中的newInstance(Class<?>...参数类型)方法得到指定的构造函数

    Object obj=con.newInstance("黑马",88);  

  注:

        (1)、创建实例时newInstance方法中的参数列表必须与获取Constructor的方法getConstructor方法中的参数列表一致。

        (2)、newInstance()构造出一个实例对象,每调用一次就构造一个对象。

        (3)、利用Constructor类来创建类实例的好处是可以指定构造函数,而Class类只能利用无参构造函数创建类实例对象。

3、Constructor综合练习

(1)、需求:获取Person类中的构造函数

 Person.java文件

 1 package com.itheima;
 2 public class Person
 3 {
 4     private String name;
 5     private int age;
 6     public static String country="CN";
 7     public Person(){
 8         System.out.println("Person run");
 9     }
10     public Person(String name,int age){
11         this.name=name;
12         this.age=age;
13         System.out.println("name="+name+",age="+age);
14     }
15     private Person(String name){
16         this.name=name;
17         System.out.println("Person run"+name);
18     }
19     private void method(String name,int age){
20         System.out.println("name="+name+"..method run.."+"age="+age);
21     }
22     public void show(){
23         System.out.println("Person show");
24     }
25     public static void staticMethod(){
26         System.out.println("staticMethod run");
27     }
28 }

ConstructorDemo.java文件

 1 mport java.lang.reflect.*;
 2 import com.itheima.Person;
 3 public class ConstructorDemo
 4 {
 5     public static void main(String[] args) throws Exception
 6     {    
 7         String className="com.itheima.Person";    
 8         constructor(className);
 9     }
10     public static void constructor(String className)throws Exception{
11         //获取Class字节码文件对象
12         Class clazz=Class.forName(className);
13 
14         //获取所有公有的构造函数:getConstructors()
15         printLine("所有公共构造函数如下:");
16         Constructor[] con1=clazz.getConstructors();
17         printArray(con1);
18 
19         //获取所有的构造函数:getDeclaredConstructors()
20         printLine("所有构造函数如下:");
21         Constructor[] con2=clazz.getDeclaredConstructors();
22         printArray(con2);
23         
24         //获取指定的构造函数
25         printLine("指定公有构造函数如下:");
26         Constructor con3=clazz.getConstructor(String.class,int.class);
27         System.out.println(con3);
28     }
29     public static void printArray(Object[] arr){
30         for (Object o:arr )
31         {
32             System.out.println(o);
33         }
34     }
35     public static void printLine(String str){
36         System.out.println();
37         System.out.println("--"+str+"------------------");        
38     }
39 }

运行结果:

(2)、需求:创建Person实例对象

ConstructorDemo.java文件

 1 import java.lang.reflect.*;
 2 import com.itheima.Person;
 3 public class ConstructorDemo
 4 {
 5     public static void main(String[] args) throws Exception
 6     {        
 7         String className="com.itheima.Person";    
 8         createObj(className);
 9      }
10      public static void createObj(String className)throws Exception{    
11         Class clazz=Class.forName(className);
12 
13         //以前我们创建对象的方式:new Person(),现在我们用反射创建对象。
14         
15         //反射第一种方法,直接用字节码对象的newInstance()创建空参实例对象                
16         Person p=(Person)Class.forName(className).newInstance();
17 
18         //第二种方法:获取Constructor对象,通过Constructor对象的newInstance()创建实例对象
19         //newInstance()方法中的参数列表必须与获取Constructor的方法getConstructor()方法中的参数列表一致
20         Constructor con= Class.forName(className).getConstructor();
21         Person p1=(Person)con.newInstance();
22         
23         //上面的两种方法貌似都可以创建出来Person的实例对象,那么思考一下:如果创建指定参数的对象呢?
24         //那么用字节码对象创建空参数的对象就无法完成了,这时候我们必须使用获取Constructor对象来创建对象了
25         //Person p2=new Person("黑马",88);
26         Constructor c=clazz.getConstructor(String.class,int.class);
27         Person p2=(Person)c.newInstance("黑马",88);
28     }
29 }

运行结果:

四、获取Class的字段(Field类)

  Field类代表某个类中一个成员变量.

1、Field类中的方法

1 Object  get(Object obj);//获取指定对象上的字段值
2 int  getModifiers();//获取字段上的修饰符
3 String  getName();//获取字段的名称
4 void  set(Object obj,Object value);//将指定对象上的字段设置为指定的新值
5 Class<?>  getType();//获取字段自身的类型
6 void  setAccessible(true);//私有字段进行取消权限检查的能力,暴力访问

 2、获取并修改指定成员变量的值的步骤

  (1)、首先需要创建对象,因为获取和修改成员变量的值都需要明确修改的是哪个对象身上的

  (2)、获取字节码对象,用其getDeclaredField(String name)方法来获取Field对象

  (3)、通过Field对象的set(Object obj,Object value)来设置指定对象上的指定成员变量。

    在设置之前一般都需要暴力访问,因为成员变量一般都是私有的。

  (4)、通过Field对象中的get(Object obj)获取其身上的字段值。

3、Field综合练习

(1)、需求:获取Person类中的成员变量并修改

  注:Person类因为和Constructor练习中的Person类一样,所以这里就不单独贴了。

FieldDemo.java文件

 1 import java.lang.reflect.*;
 2 import com.itheima.Person;
 3 public class FieldDemo
 4 {
 5     public static void main(String[] args) throws Exception
 6     {    
 7         String className="com.itheima.Person";
 8         field(className);
 9     }
10     public static void field(String className)throws Exception{
11         //获取Class字节码文件对象
12         Class clazz=Class.forName(className);
13 
14         //获取所有的成员变量
15         printLine("所有成员变量如下:");
16         Field[] f1=clazz.getDeclaredFields();
17         printArray(f1);
18 
19         //获取指定成员变量
20         printLine("指定成员变量如下:");
21         Field f2=clazz.getDeclaredField("name");
22         System.out.println(f2);
23 
24         //为对象的属性赋值,首先要有对象
25         Person p=(Person)clazz.getConstructor(String.class,int.class).newInstance("黑马",88);
26         //修改成员变量name的值
27         Field f3=clazz.getDeclaredField("name");
28         //取消访问检查,暴力访问私有成员变量
29         f3.setAccessible(true);
30         f3.set(p,"小明");
31         //获取某对象的属性值        
32         System.out.println("name="+f3.get(p));
33 
34     }
35     public static void printArray(Object[] arr){
36         for (Object o:arr )
37         {
38             System.out.println(o);
39         }
40     }
41     public static void printLine(String str){
42         System.out.println();
43         System.out.println("--"+str+"------------------");        
44     }
45 }

 运行结果:

 

(2)、需求:将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"a"改成"b"。

 1 import java.lang.reflect.*;
 2 class Person
 3 {
 4     private String s1="com.itheima";
 5     private String s2="basketball";
 6     private String s3="我爱黑马";    
 7 }
 8 class FieldTest
 9 {
10     public static void main(String[] args) throws Exception
11     {
12         Class clazz=Class.forName("Person");
      //创建对象
13 Person p=(Person)clazz.newInstance();
      //获取全部成员变量
14 Field[] field=clazz.getDeclaredFields();
      //遍历Field数组
15 for(Field f:field){ 16 if(f.getType()==String.class){//如果字段的类型和String类型的字节码相同,则视为同一类型 17 f.setAccessible(true);//暴力访问 18 String oldValue=(String)f.get(p); 19 f.set(d,oldValue.replace('a','b'));//String类型的成员变量中含有a的字符换成b字符 20 System.out.println(f.get(p)); 21 } 22 } 23 } 24 }

 运行结果:

五、获取Class中的成员方法(Method类)  

 Method类代表某个类中的一个成员方法。调用某个对象身上的方法,要先得到方法,再针对某个对象调用。
1、Method类中的方法
1 Object  invoke(Object obj,Object...args);//调用方法

 注:

  (1)、如果传递给Method对象的invoke()方法的第一个参数为null,说明Method对象对应的是一个静态方法。

  (2)、jdk1.5以上如果调用的这个方法没有参数列表Object...args填写null编译的时候会提示警告。

     所以这里的Object...args可以用new Object[0],来抑制jvm编译时期的警告提示。

2、Method综合练习

(1)、获取Person类中的成员方法并调用

 //Person类接Constructor里的Person类
1
import java.lang.reflect.*; 2 import com.itheima.Person; 3 public class MethodDemo 4 { 5 public static void main(String[] args) throws Exception 6 { 7 String className="com.itheima.Person"; 8 method(className); 9 } 10 public static void method(String className)throws Exception{ 11 //获取Class字节码文件对象 12 Class clazz=Class.forName(className); 13 14 //获取所有的成员方法 15 printLine("所有成员方法如下:"); 16 Method[] m1=clazz.getDeclaredMethods(); 17 printArray(m1); 18 19 //获取指定成员方法 20 printLine("指定成员方法如下:"); 21 Method m2=clazz.getDeclaredMethod("method",String.class,int.class); 22 System.out.println(m2); 23 24 //创建对象调用方法,Person p=new Person().method(String name,int age); 25 System.out.println("hahhaha"); 26 Person p=(Person)clazz.newInstance(); 27 Method m3=clazz.getDeclaredMethod("method",String.class,int.class); 28 m3.setAccessible(true); 29 m3.invoke(p,"黑马",66); 30 31 //调用静态方法的时候不需要对象,因为静态优先于对象存在 32 //Person.staticMethod(); 33 Method m4=clazz.getMethod("staticMethod",new Class[0]); 34 m4.invoke(null,new Object[0]); 35 /* 36 警告: 最后一个参数使用了不准确的变量类型的 varargs 方法的非 varargs 调用; 37 [javac] 对于 varargs 调用,应使用 java.lang.Object 38 [javac] 对于非 varargs 调用,应使用 java.lang.Object[],这样也可以抑制此警告 39 在jdk1.4下可以编译通过,但在1.5就不行。 40 在调用方法的时候,如果方法的参数列表为null的时候就会有此提示。这是java的友好提示。 41 解决方法:就是null的位置上创建空的数组对象即可。如:invoke(null,new Object[0]); 42 43 */ 44 45 } 46 public static void printArray(Object[] arr){ 47 for (Object o:arr ) 48 { 49 System.out.println(o); 50 } 51 } 52 public static void printLine(String str){ 53 System.out.println(); 54 System.out.println("--"+str+"------------------"); 55 } 56 }

 运行结果:

(2)、需求:用反射方式执行某个类中的main方法。
  在写源程序时,并不知道使用者传入的类名是什么,但是虽然传入的类名不知道,而知道的是这个类中的方法有main这个方法。所以可以通过反射的方式,通过使用者传入的类名(可定义字符串型变量作为传入类名的入口,通过这个变量代表类名),内部通过传入的类名获取其main方法,然后执行相应的内容。

此时会出现下面的问题:

  启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。所以,在给main方法传递参数时,不能使用代码mainMethod.invoke(null,new String[]{“xxx”}),javac只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5的语法解释,因此会出现参数类型不对的问题。

解决办法:

            mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});

            mainMethod.invoke(null,(Object)new String[]{"xxx"});

这两种方式编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了。

 1 import java.lang.reflect.*;
 2 class Demo
 3 {
 4     public static void main(String[] args){
 5         for(String s:args){
 6             System.out.println(s);
 7         }
 8     }
 9 }
10 class MainDemo 
11 {
12     public static void main(String[] args) throws Exception
13     {
14         //一般调用Demo类中的main()方法
15         Demo.main(new String[]{"abc","123","我爱黑马"});
16         System.out.println("------------------------");
17 
18         //通过反射方式根据用户提供的类名,去执行该类中的main方法。
19         String className=args[0];
20         Class clazz=Class.forName(className);
21         Method m=clazz.getMethod("main",String[].class);
22         //第一种方式:将数组打包,编译器拆包后就是一个String[]类型的整体
23         m.invoke(null,new Object[]{new String[]{"abc","123","我爱黑马"}});
24         //第二种方式:强制转换为父类Object,不用拆包 
25         m.invoke(null,(Object)new String[]{"abc","123","我爱黑马"});
26     }
27 }

 运行结果:

 

六、数组的反射

(1)、具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
(2)、代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
(3)、基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
(4)、Object[] 与String[]没有父子关系,Object与String有父子关系,所以new Object[]{“aaa”,”bb”}不能强制转换成new String[]{“aaa”,”bb”};,Object x = “abc”能强制转换成String x = “abc”。
(5)、无法得到某个数组的具体类型,只能得到其中某个元素的类型。如:Obj[0].getClass().getName()得到的是java.lang.String。
(6)、Array工具类用于完成对数组的反射操作。
1 Array.getLength(Object obj);//获取数组的长度
2 Array.get(Object obj,int x);//获取数组中的元素

代码演示:    

 1 import java.lang.reflect.*;
 2 public class ArrayReflectDemo {
 3     public static void main(String[] args)throws Exception{
 4         int[] arr1=new int[]{4,12,3,5,485,78};
 5         int[] arr2=new int[5];
 6         int[][] arr3=new int[3][4];
 7         String[] arr4=new String[]{"abc","haha","nihao"};
 8         System.out.println(arr1.getClass()==arr2.getClass());//true
 9 //      System.out.println(arr1.getClass()==arr3.getClass());//编译直接报错,arr1是数组类型,arr3可以看作Object[]类型
10 //      System.out.println(arr1.getClass()==arr4.getClass());//arr1是Object类型,arr4是Object[]类型
11         System.out.println(arr1.getClass().getSuperclass());
12         System.out.println(arr2.getClass().getSuperclass());
13         System.out.println(arr3.getClass().getSuperclass());
14         System.out.println(arr4.getClass().getSuperclass());
15         Object obj1=arr1;
16         Object obj2=arr2;
17         Object[] obj3=arr3;
18         Object[] obj4=arr4;//String数组中的元素属于Object
19 //      Object[] obj5=arr2;//int不是对象,int[]和String[]是对象.
20         printArray(arr1);
21         printArray(234343);
22     }
    //打印对象,如果是数组的遍历打印元素,如果不是直接打印
23 public static void printArray(Object obj){ 24 if(obj.getClass().isArray()){//判断是否是数组 25 int len=Array.getLength(obj);//获取数组的长度,得到循环的条件 26 for(int x=0;x<len;x++){ 27 System.out.println(Array.get(obj, x));//获取数组元素 28 } 29 }else 30 System.out.println(obj); 31 } 32 }

 七、反射的作用----实现框架功能

  什么是框架,例如,我们要写程序扫描.java文件中的注解,要解决哪些问题:读取每一样,在每一个中查找@,找到的@再去查询一个列表,如果@后的内容出现在了列表中,就说明这是一个我能处理和想处理的注解,否则,就说明它不是一个注解或者说至少不是一个我感兴趣和能处理的注解。接着就编写处理这个注解的相关代码。现在sun提供了一个apt框架,它会完成所有前期工作,只需要我们提供能够处理的注解列表,以及处理这些注解的代码。Apt框架找到我们感兴趣的注解后通知或调用我们的处理代码去处理。框架与框架要解决的核心问题

比如:我做房子卖给用户住,由用户自己安装门窗和空调,我做的房子就是框架,用户需要使用我的框架,把门窗插入进我提供的框架中。
    框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类。
框架机器要解决的核心问题:
  我在写框架(房子)时,你这个用户可能还在上小学,还不会写程序呢?我写的框架程序怎样能调用到你以后写的类(门窗)呢?因为在写才程序时无法知道要被调用的类名,所以,在程序中无法直接new 某个类的实例对象了,而要用反射方式来做。

1、通过反射读取配置文件信息。

  思路:

  (1)、右击项目File命名一个配置文件:config.properties,然后写入配置信息。键值对:className=java.util.ArrayList。

      (2)、 将文件读取到读取流中,要写出配置文件的绝对路径.                    

      (3)、用Properties类的load()方法将流中的数据存入集合。

      (4)、关闭流:关闭的是读取流,因为流中的数据已经加载进内存。

      (5)、通过getProperty()方法获取className,即配置的值,也就是某个类名。

      (6)、用反射的方式,创建对象newInstance()。  

代码实现:

Person.java文件

 1 package ReflectDemo;
 2 
 3 public class Person {
 4     private String name;
 5     private int age;    
 6     public Person(String name, int age) {        
 7         this.name = name;
 8         this.age = age;
 9     }
10     public String getName() {
11         return name;
12     }
13     public void setName(String name) {
14         this.name = name;
15     }
16     public int getAge() {
17         return age;
18     }
19     public void setAge(int age) {
20         this.age = age;
21     }
22     //复写中hashCode方法
23     public int hashCode(){
24         
25         return name.hashCode()+age*37;
26     }
27     //复写了Object中equalss方法
28     public boolean equals(Object obj){
29         if(!(obj instanceof Person))
30             throw new RuntimeException("类型转换异常");
31         Person p=(Person)obj;
32         return this.name.equals(p.name)&& this.age==p.age;
33     }
34     @Override
35     public String toString() {
36         return "Person =[age=" + age + ", name=" + name + "]";
37     }
38 }

 RflectDemo.java文件

 1 package com.itheima;
 2 import java.util.*;
 3 import java.io.*;
 4 import java.lang.reflect.*;
 5 public class ReflectDemo {
 6     public static void main(String[] args)throws Exception {
 7         //文件关联流对象,用字节流读取配置文件信息
 8         FileInputStream in=new FileInputStream("config.properties");
 9         //创建Properties对象,用来操作配置文件信息
10         Properties pro=new Properties();
11         //把流中的数据加载到集合中
12         pro.load(in);
13         in.close();
14         //获取集合中的键当做className
15         String className=pro.getProperty("className");
16         //用反射创建ArrayList对象用来存储Person对象
17         Collection<Person> con=(ArrayList)Class.forName(className).newInstance();
18         con.add(new Person("黑马",120));
19         con.add(new Person("小明",50));
20         con.add(new Person("王五",12));
21         con.add(new Person("逗逗",40));
22         con.add(new Person("赵四",80));
23         con.add(new Person("王五",12));//ArrayList集合的特点是元素可重复,所以这个视为不相同的Person对象
24         //遍历集合中的元素
25         for(Person p:con){
26             System.out.println(p);
27         }    
28         
29         
30         //原运行程序
31         Collection<Person> collection=new HashSet<Person>();
32         collection.add(new Person("黑马",120));
33         collection.add(new Person("小明",50));
34         collection.add(new Person("王五",12));
35         collection.add(new Person("逗逗",40));
36         collection.add(new Person("赵四",80));
37         collection.add(new Person("王五",12));//因为Person复写了hashCode和equals方法,所以视为重复元素
38         for(Person pe:collection){
39             System.out.println(pe);
40         }
41     }
42 
43 }

 config.properties文件

1 className=java.util.ArrayList

 2、类加载器

类加载器是将.class的文件加载进内存,也可将普通文件中的信息加载进内存。

资源文件的加载:是使用类加载器。

(1)、由类加载器ClassLoader来加载进内存,即用getClassLoader()方法获取类加载器,然后用类加载器的getResourceAsStream(String name)方法,将配置文件(资源文件)加载进内存。利用类加载器来加载配置文件,需把配置文件放置的包名一起写上。这种方式只有读取功能。

(2)、Class类也提供getResourceAsStream方法来加载资源文件,其实它内部就是调用了ClassLoader的方法。这时,配置文件是相对类文件的当前目录的,也就是说用这种方法,配置文件前面可以省略包名。

 如:类名.class.getResourceAsStream(“资源文件名”)

 注:菜鸟日记,如有错误欢迎大神指正,欢迎探讨!!

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
原文地址:https://www.cnblogs.com/521Android/p/4719717.html