黑马程序员——Java 反射 (第十二篇)

-----------android培训java培训、java学习型技术博客、期待与您交流!------------ 


学习前先想一下下面的问题:

 (1)我们为何需要反射?

 (2)如何使用反射?

 (3)Class对象的三种获取方式?


  java反射概念


        Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类中的所有属性和方法;

        对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态

        调用对象的方法的功能称为java语言的反射机制。 (反射技术大大提高了程序的扩展性)


   反射的一般操作方式:

        1)加载这个类。

        2)创建该类的对象。

        3)调用该类中的内容。


       应用程序使用的类不确定时,可以通过提供配置文件,让使用者将具体的子类存储到配置文件中。然后该程序通过反射技术,对指定的类进行内容的获取。

  

      反射的经单总结:  反射就是把Java类中的各种成分映射成相应的java类。 ----张孝祥老师的某个学生


二、反射的基石——Class


  类的加载

        当源程序中用到类时,首先要从硬盘把这个类的那些二进制代码,一个类编译成class放在硬盘上以后,就是一些二进制代码,要把这些二进制代码加载到内存中里面来,再用这些字节码去复制出一个一个对象来。

2Classclass的区别

        1classJava中的类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则由此类的实例对象确定,不同的实例对象有不同的属性值。

        2Class:指的是Java程序中的各个Java类是属于同一类事物,都是Java程序的类,这些类称为Class。例如人对应的是Person类,Java类对应的就是ClassClassJava程序中各个Java类的总称;它是反射的基石,通过Class类来使用反射。


Class对象的三种获取方式:

    (1)  运用getClass()  (注:每个class 都有此函数)

    (2)  运用Class.forName()(最常被使用)

    (3)  类名.class 语法


        1、九个预定义的Class

                1)包括八种基本类型(byteshortintlongfloatdoublecharboolean)+  void.class

                2Integer.TYPEInteger类的一个常量,它代表此包装类型包装的基本类型的字节码,所以和int.class是相等的。基本数据类型的字节码都可以用与之对应的包装类中的TYPE常量表示

        2、只要是在源程序中出现的类型都有各自的Class实例对象,如int[].class。数组类型的Class实例对象,可以用Class.isArray()方法判断是否为数组类型的。



  Class对象来获取类实例对象的做法之一:

        1)查找并加载指定名字的字节码文件进内存,并被封装成Class对象。

        2)调用newInstance()方法会去使用该类的空参数构造函数进行初始化。

             如:

                     String className="包名.Person";

                     Class clazz=Class.forName(className);

                     Object obj=clazz.newInstance();


    这和之前我们学过的类的创建方式有很大区别! 即使拿到实例对象后,也不能直接去调用内部字段或方法··。 

    注意:如果指定的类中没有空参构造函数,那么就必须用到Constructor这个类来获取指定类中存在的构造方法,然后再调用次构造函数去创建新对象


    反射中最常用到的三个类:Constructor类    Field类   Method


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

 import java.lang.reflect.*;
 import java.lang.*;
/*
 定义一个Person的类,为后面反射用!
*/
public class Person 
{  
    private String name;    //注意: name定义的为私有
    public int age;  
    public Person()
	{  
        System.out.println("Person is run");  
    }  
    public Person(String name,int age)
	{  
        this.age=age;  
        this.name=name;  
    }  
      
    public String toString()
	{  
        return name+":"+age;  
    }  
}  


 /*=======================main==========================*/
  
public class reflectClass {  
    public static void main(String[] args) throws Exception
	{  
          createPersonClass();   //空参实例对象
		  createPersonClass_2(); //带参实例对象
		  getPersonField();      //获得成员字段,并为其设值
		  getPersonMethod();     //通过反射获取类中的方法
    } 
	

 /*=======================空参实例对象==========================*/

	/*
	如果类中 带有 空参构造函数,那么可以直接用newInstance()来创建对象
	*/
    //通过Class对象创建类实例方法  
    public static void createPersonClass() throws Exception{  
        //获取Person类的Class对象  
       // String className=Person.class;  
        Class<? extends Person> clazz=Person.class;  
        //通过newInstance方法获取类的无参构造函数实例  
        Person p=(Person)clazz.newInstance();  
    }  

  /*=======================带参实例对象==========================*/

	/*
     如果类中 没有 空参构造函数,那么必须先通过Class类获得带参数的Constructor类的对象,
	 然后再用Constructor对象的带参方法创建出新的person对象
	*/
	public static void createPersonClass_2() throws Exception{  
    //获取Person类的Class对象  
    //String className="Person";  
    Class<? extends Person> clazz=Person.class;  
    //Class clazz=Person.class;  
          
    //获取指定构造函数的类实例  
    Constructor<? extends Person> con=clazz.getConstructor(String.class,int.class);  
    Person p=(Person) con.newInstance("liuliu",25);  
    System.out.println(p.toString());  
}  


  /*=======================获取类中的字段==========================*/

    //获取Person对象的成员变量  
     public static void getPersonField() throws Exception{     
    //如果想要给该变量赋值,必须先要有对象。  
    //Class clazz=Class.forName("Person")
    Class<? extends Person> clazz=Person.class;  
    Person p=(Person)clazz.newInstance();  
          
    //获取所以的成员变量  
    Field[] fs=clazz.getDeclaredFields();  
    for(Field f:fs){  
        System.out.println("field--"+f);  
    }  
          
    //获取指定的成员变量  
    Field fage=clazz.getField("age");  
    Field fname=clazz.getDeclaredField("name");  
          
    //显示改变后的值  
    fage.set(p, 20);  
    System.out.println(fage.get(p));  
          
    //暴力访问私有变量  
    fname.setAccessible(true);  
    fname.set(p, "zhangsan");  
    System.out.println(fname.get(p));  
}  


  /*=======================获取类中的方法==========================*/

     //获取Person类中的方法  
     public static void getPersonMethod() throws Exception{  
    //如果想要获取方法,必须先要有对象。  
    Class<? extends Person> clazz=Person.class;  
    //Person p=(Person)clazz.newInstance();  
    Constructor<? extends Person> con=clazz.getConstructor(String.class,int.class);  
    Person p=(Person) con.newInstance("liuliu",25);   
		  
    //获取所以方法  
    Method[] mes=clazz.getMethods();//只获取公共的和父类中的。  
    //mes=clazz.getDeclaredMethods();//获取本类中包含私有。  
    for(Method me:mes){  
        System.out.println("method:::"+me);  
    }  
          
    //获取单个方法  
	 Object objs=null;
     Method me=clazz.getMethod("toString",new Class[0]);   //注意这里的写法
     Object returnVaule=me.invoke(p,new Object[]{});  

     System.out.println(returnVaule);      
}  
} 



练习题:        写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。


import java.lang.reflect.Method;  
//定义一个测试类  
class Test
{  
    public static void main(String[] args)
	{  
        for(String arg : args) 
		{  
            System.out.println(arg);  
        }  
    }  
}  
//用反射方式根据用户提供的类名,去执行该类中的main方法。   
public class PerformedMain{  
    public static void main(String[] args) throws Exception
	{  
        //普通方式  
        Test.main(new String[]{"lai","hei","ma"}); 
		//分割符
        System.out.println("******************************");  
        //拿到对应的Class对象
        String className="Test";  
        Class clazz=Class.forName(className);  
        //拿到主函数方法          
        Method methodMain=clazz.getMethod("main",String[].class);  
        //强制转换为超类Object,不用拆包  
        methodMain.invoke(null, (Object)new String[]{"ai","hei","ma"});  
        //将数组打包,编译器拆包后就是一个String[]类型的整体   
        methodMain.invoke(null, new Object[]{new String[]{"jiushiai","hei","ma"}});  
    }  


                usb.properties

                usb1=cn.itheima.test.MouseUSB

                usb2=cn.itheima.test.KeyUSB

注:此练习中,配置文件要存在工程中:工程名——>新建File

 

   数组的反射

1、具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。数组字节码的名字:有[和数组对应类型的缩写,如int[]数组的名称为:[I

2Object[]String[]没有父子关系ObjectString有父子关系,所以new Object[]{aaa,bb}不能强制转换成new String[]{aaa,bb}; Object x =abc”能强制转换成String x =abc”。

3、如何得到某个数组中的某个元素的类型,

        例:

              int a = new int[3]Object[] obj=new Object[]{ABC,1};

        无法得到某个数组的具体类型,只能得到其中某个元素的类型,

        如:

               Obj[0].getClass().getName()得到的是java.lang.String。

4Array工具类用于完成对数组的反射操作。

        Array.getLength(Object obj);//获取数组的长度

        Array.get(Object obj,int x);//获取数组中的元素

5基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。


基本判断:

import java.util.Arrays;  
   
public class SuZuDemo {  
  public static void main(String[] args) {  
    int[] a = new int[3];  
    int[] b = new int[4];  
    int[][] c = new int[3][4];  
    String[] s1 = new String[3];  
    System.out.println(a.getClass() == b.getClass());// true  
    //System.out.println(a.getClass()==c.getClass());//编译失败  
    //System.out.println(a.getClass()==s1.getClass());//编译失败  
    /** 
     * 获得父类的字节码,从结果知道,他们的父类的是Object 
     */  
    System.out.println(a.getClass().getSuperclass().getName());  
    System.out.println(s1.getClass().getSuperclass().getName());  
    int[] a1 = new int[] { 1, 2, 3 };  
    String[] s2 = new String[] { "a", "b", "c" };  
    System.out.println(Arrays.asList(a1));// 在1.5后,此此方法会把整形数组当作一个参数处理  
    System.out.println(Arrays.asList(s2));// 会把String数组拆分,分别作为参数来处理  
  }  
   
}  
结果:  
true  
java.lang.Object  
java.lang.Object  
[[I@1f66cff]  
[a, b, c]  

反射数组:

import java.lang.reflect.Array;  
import java.util.Arrays;  
   
public class SuZuDemo {  
  public static void main(String[] args) throws Exception {  
    String[] s = { "abc", "def", "hig" };  
    String ss = "hello";  
    print(s);  
    print(ss);  
  }  
   
  public static void print(Object ob) {  
    Class classzz = ob.getClass();// 反射  
    if (classzz.isArray()) {// 判断的是否是数组  
      int len = Array.getLength(ob);// 获取长度  
      for (int i = 0; i < len; i++) {  
         System.out.println(Array.get(ob, i));// 获得数组中的元素  
      }  
    } else {  
      System.out.println(ob);  
    }  
  }  
   
}  
结果:  
abc  
def  
hig  
hello  


        所以就有这样的说法:如果两个对象equals相等的话,你应该让他们的hashCode也相等。如果对象存入的不是根据hash算法的集合中,就不需要复写hashCode方法。

4、当一个对象存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,

     否则对象修改后的哈希值与最初存储进HashSet集合中的哈希值就不同了。在这种情况下,调用 

     contains方法或者remove方法来寻找或者删除这个对象的引用,就会找不到这个对象。从而导

     致无法从HashSet集合中单独删除当前对象,从而造成内存泄露。(程序中某一些对象不再被使用,

      以为被删掉了,但是没有,还一直在占用内存中,当这样的对象慢慢增加时,就会造成内存泄露。) 

补充:

        内存泄露:某些对象不再使用了,占用着内存空间,并未被释放,就会导致内存泄露;也就是说当程序不断增加对象,修改对象,删除对象,日积月累,内存就会用光了,就导致内存溢出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值