黑马程序员——Java中类的加载与反射笔记

来源:互联网 发布:嵌入式linux入门教材 编辑:程序博客网 时间:2024/06/09 18:36

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


一、类加载器


1、类的加载


当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。


1.1、 加载 
就是指将class文件读入内存,并为之创建一个Class对象。任何类被使用时系统都会建立一个Class对象。


1.2 、连接:接阶段又可以分为三个子步骤:验证、准备和解析。


验证: 是否有正确的内部结构,确保java类型数据格式的正确性,并适于JVM使。


准备: 负责为类的静态成员分配内存,并设置默认初始化值


解析: 解析过程就是在类型的常量池中寻找类、接口、字段和方法的符号引用,把这些符号引用替换成直接引用。这个阶段可以被推迟到初始化之后,当程序运行的过程中真正使用某个符号引用的时候 再去解析它。


1.3、类初始化时机


1)创建类的实例
2)访问类的静态变量,或者为静态变量赋值
3)调用类的静态方法
4)使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
5)初始化某个类的子类
6)直接使用java.exe命令来运行某个主类


2、类加载器


2.1、含义:负责将.class文件加载到内在中,并为之生成对应的Class对象。
2.2、类加载器的组成及作用


Bootstrap ClassLoader 根类加载器:也被称为引导类加载器,负责Java核心类的加载。比如System,String等。在JDK中JRE的lib目录下rt.jar文件中

Extension ClassLoader 扩展类加载器:负责JRE的扩展目录中jar包的加载。在JDK中JRE的lib目录下ext目录

Sysetm ClassLoader 系统类加载器:负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。


二、反射


1、概述


  1.1、反射机制:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。


1.2、反射技术:其实就是动态加载一个指定的类,并获取该类中的所有的内容。而且将字节码文件封装成对象,并将字节码文件中的内容都封装成对象,这样便于操作这些成员。简单说:反射技术可以对一个类进行解剖。


反射的好处:大大的增强了程序的扩展性。


2、反射的基本步骤:


2.1获得Class对象,就是获取到指定的名称的字节码文件对象。


2.2实例化对象,获得类的属性、方法或构造函数。


2.3访问属性、调用方法、调用构造函数创建对象。


3、获取Class对象的三种方式:


3.1每一个类对象都有一个静态的属性class。弊端:必须要先明确该类。如果是明确地获得某个类的Class对象可以用此方式,主要用于传参。 
Class clazz = Person.class; 


3.2通过每个对象都具备的方法getClass来获取。弊端:必须要创建该类对象,才可以调用getClass方法。如果拿到了对象,不知道是什么类型可以用此方式,用于获得对象的类型。 

Object obj = new Person(); 
Class clazz1 = obj.getClass(); 


3.3使用的Class类中的方法,静态的forName方法。指定什么类名(完整的类名),就获取什么类字节码文件对象,这种方式的扩展性最强,只要将类名的字符串传入即可,用于类加载。 

String classname = “cn.itcast.reflect.Person”; 
Class clazz = Class.forName(classname);//当类的字节码已经加载进了内存,就将该字节码返回;如果jvm还没有该字节码,就用类加载器加载,再返回加载的字节码。 
前两种方式不利于程序的扩展,因为都需要在程序使用具体的类来完成。 
三种方式得到的字节码都是同一个字节码: 

String str1 = “abc”; Class cls1 = str1.getClass(); Class cls2 = String.class; Class cls3 = Class.forName(“java.lang.String”); System.out.println(cls1 == cls2);//true System.out.println(cls1 == cls3);//true

4、九个预定义的Class:


1)包括八种基本类型(byte、short、int、long、float、double、char、boolean)的字节码对象和一种返回值为void类型的void.class。


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


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


5、反射的使用:


5.1、Class类


1)类中的方法,不包括获取成员属性及方法对象


static Class forName(String className):返回与给定字符串名的类或接口的相关联的Class对象。


Class getClass():返回的是Object运行时的类,即返回Class对象即字节码对象       


String getName():以String形式返回此Class对象所表示的实体名称。       


String getSuperclass():返回此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class      


boolean isArray():判定此Class对象是否表示一个数组       


boolean isPrimitive():判断指定的Class对象是否是一个基本类型。       


2)通过Class对象获取类实例


如:


String className="包名.Person";


Class clazz=Class.forName(className);


  Object obj=clazz.newInstance();


   5.2、反射获取成员方法并使用:


获取所有方法:


Method[] getMehtods():返回一个包含某些Method对象的数组,是所代表的的类中的公共成员方法。


Method[] getDeclareMethods():返回Method 对象的一个数组,这些对象反映此Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。


获取单个方法:
Method getMethod(String name,Class<?>… parameterTypes):返回一个Method对象,它表示的是此Class对象所代表的类的指定公共成员方法。


Method getDeclaredMethod(String name,Class<?>… parameterTypes):返回一个Method 对象,该对象反映此Class 对象所表示的类或接口的指定已声明方法。name 参数是一个String,它指定所需方法的简称,parameterTypes 参数是Class 对象的一个数组,它按声明顺序标识该方法的形参类型。


使用方法:Method类方法。


void setAccessible(boolean)方法:值为true时取消 Java 语言访问检查


Object invoke(Object obj ,参数);//调用对象的method方法


程序示例:

[java] view plaincopypackage itcast;    import java.lang.reflect.Constructor;  import java.lang.reflect.Method;    /**  * @author oyfc  */  public class MethodDemo {        /**      * 通过反射获取成员方法并使用。      * @param args      * @throws ClassNotFoundException       */      public static void main(String[] args) throws Exception {                    //获取字节码文件对象          String className = "itcast.Person";          Class class1 = Class.forName(className);                    //创建对象          Constructor constructor = class1.getConstructor(String.class,int.class);          Object object = constructor.newInstance("zhangfei",21);                    //获取所有成员方法          Method[] methods = class1.getDeclaredMethods();          for(Method meth: methods){              sop(meth);          }          //获取成员方法          Method method = class1.getDeclaredMethod("show");          //暴力访问          method.setAccessible(true);          //调用方法          method.invoke(object, null);                      }        /**      * @param method      */      private static void sop(Object object) {          System.out.println(object);      }    }  

5.3反射类的构造函数并使用:

获取所有构造方法:

 Constructor<?>[] getConstructors()返回一个包含某些Constructor 对象的数组,这些对象反映此Class 对象所表示的类的所有公共构造方法。



 Constructor<?>[] getDeclaredConstructors()返回Constructor 对象的一个数组,这些对象反映此Class 对象表示的类声明的所有构造方法。它们是公共、保护、默认(包)访问和私有构造方法。



获取单个构造方法:



Constructor<T> getConstructor(Class<?>...parameterTypes):返回一个Constructor 对象,它反映此Class 对象所表示的类的指定公共构造方法。



Constructor<T> gettDeclaredConstructor():



使用方法: Constructor类的方法



T newInstance(Object ... initargs)):创建此Class对象所表示的类的一个新实例。


</pre>程序示例:</p><p><span style="font-size:18px;"></span><pre name="code" class="java">[java] view plaincopypackage itcast;    import java.lang.reflect.Constructor;    /**  * 通过反射获取构造方法并使用。  *   * @author oyfc  */  public class ConstructorDemo {        /**      * @param args      * @throws Exception       */      public static void main(String[] args) throws Exception {            // 获取字节码文件对象          String className = "itcast.Person";          Class class1 = Class.forName(className);            // 获取所有所有公共构造方法  //      Constructor[] constructors = class1.getConstructors();          // 获取所有所有构造方法          Constructor[] constructors = class1.getDeclaredConstructors();          for (Constructor con : constructors) {              sop(con);          }                    //获取无参构造方法          Constructor constructor = class1.getDeclaredConstructor();          //取消访问检查,暴力访问          constructor.setAccessible(true);          //创建对象          Object object = constructor.newInstance();          sop(object);//null::0                    //获取有参构造方法          Constructor constructor2 = class1.getDeclaredConstructor(String.class);          //创建对象          Object object2 = constructor2.newInstance("张飞");          sop(object2);//张飞::0        }        /**      * @param con      */      private static void sop(Object obj) {          System.out.println(obj);      }    }  

5.4反射类的成员变量并使用:


获取所有成员:


Field[] getFields():返回包含某些Field对象的数组,表示所代表类中的成员字段。

Field[] getDeclaredFields():

获取单个成员:


Field getField(String name):返回一个Field对象,它表示此Class对象所代表的类或接口的指定公共成员字段。   


Field getDeclaredField(String name):


修改成员的值:Field类的方法


set(Object obj,Object value) :将指定对象变量上此 Field 对象表示的字段设置为指定的新值。


如:

[java] view plaincopyField field = clazz.getField(fieldName);//获取指定的字段值  field.setAccessible(true);//取消访问检查,也称暴力访问  field.set(obj,value);//将指定对象变量上此 Field 对象表示的字段设置为指定的新值. 

示例:

[java] view plaincopypackage itcast;    import java.lang.reflect.Constructor;  import java.lang.reflect.Field;    /**  * @author oyfc  */  public class FieldDemo {        /**      * @param args      * @throws Exception       */      public static void main(String[] args) throws Exception {          //获取字节码文件对象          String className = "itcast.Person";          Class class1 = Class.forName(className);                    //创建实例对象          Constructor constructor = class1.getConstructor(String.class,int.class);          Object object = constructor.newInstance("张非",21);                    //获取所有成员变量  //      Field[] fields = class1.getDeclaredFields();  //      for(Field field:fields) {  //          sop(field);  //      }                    //获取单个成员变量          Field field = class1.getField("name");          //为指定变量赋值          field.set(object, "xiaozhang");          sop(object);//xiaozhang::21                }          /**      * @param field      */      private static void sop(Object object) {          System.out.println(object);      }    } 

6、数组的反射 


(1)每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组属于同一个类型,即都共享该 Class 对象。 

(2)代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。 

(3)基本数据类型的一维数组可以被当做Object类型使用,不能当做Object[]类型使用;非基本数据类型的一维数组既可以当做Object类型使用,有可以当做object[]类型使用。

示例:

public class ReflectTest {    public static void main(String[] args) throws Exception {        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());//true        System.out.println(a1.getClass() == a4.getClass());//false        System.out.println(a1.getClass() == a3.getClass());//false        System.out.println(a1.getClass().getName());//[I        System.out.println(a1.getClass().getSuperclass().getName());//java.lang.Object        System.out.println(a4.getClass().getSuperclass().getName());//java.lang.Object        Object aObj1 = a1;        Object aObj2 = a4;        //Object[] aObj3 = a1;//错误        Object[] aObj4 = a3;        Object[] aObj5 = a4;                         System.out.println(a1);//打印的是变量a1的值,不是数组的值[I@1cfb549        System.out.println(a4);        System.out.println(Arrays.asList(a1));        System.out.println(Arrays.asList(a4));  }

(4)Arrays.asList()方法处理int[]和String[]时的差异 

static List asList(T… a)// 返回一个受指定数组支持的固定大小的列表。(对返回列表的更改会“直接写”到数组。) 

System.out.println(Arrays.asList(a1));//[[I@1cfb549] 

System.out.println(Arrays.asList(a4));//[a,b,c] 

(5)Array工具类用于完成对数组的反射操作 

Array 类提供了动态创建和访问 Java 数组的方法。 

static Object get(Object array,int index)//返回指定数组对象中索引组件的值。如果该值是一个基本类型值,则自动将其包装在一个对象中。array - 数组,index - 索引 

static int getLength(Object array) //以 int 形式返回指定数组对象的长度。

private static void printObject(Object obj) {        Class clazz = obj.getClass();        if(clazz.isArray()){            int len = Array.getLength(obj);            for(int i=0;i<len;i++){                System.out.println(Array.get(obj, i));            }        }else{            System.out.println(obj);        }    }

HashSet中的hashCode()方法示例:当从HashSet集合中查找某个对象时,java系统首先调用对象的hashCode()方法获取对象的哈希码,然后根据哈希码找到相应的存储区域,取出该存储区域内的每个元素与该对象进行equals方法比较。 

注意: 

(1)通常,一个类的两个实例对象用equals()方法比较的结果相等时,它们的哈希码也必须相等,反之则不成立。

(2)当一个对象被存储进HashSet集合中以后,就不能修改这个集合对象中那些参与计算哈希值的字段了,否则,对象被修改后的哈希值会改变,这样用该对象的当前引用去集合中查找对象时,会找不到,导致无法从集合中单独删除当前对象造成内存泄漏。 

Collection collections = new HashSet(); 

ReflectPoint pt1 = new ReflectPoint(3,3);//ReflectPoint类是自定义的,包含字段x,y 

ReflectPoint pt2 = new ReflectPoint(5,5); 

ReflectPoint pt3 = new ReflectPoint(3,3); 

collections.add(pt1); 

collections.add(pt2); 

collections.add(pt3); 

collections.add(pt1); 

//pt1.y = 7;//字段y参与了哈希值的运算 

collections.remove(pt1);//操作之后没有删除pt1.



7、反射的作用:实现框架的功能 


框架与工具类有区别,工具类被用户的类调用,而框架式调用用户提供的类。 

在写框架时,用户可能还不存在,那么我写的框架程序怎么调用用户以后写的类?因为在写程序时无法知道要被调用的类名,所有在程序中无法直接new某个类的实例对象,要用到反射方式。 

采用配置文件加反射的方式创建ArrayList和HashSet的实例对象。

示例:

public class ReflectTest2 {    public static void main(String[] args) throws Exception{        //InputStream ips = new FileInputStream("config.properties");        //查找资源,类加载器,配置文件放在classpath的目录下        //InputStream ips = ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties");        //相对该类包的路径,写相对路径        //InputStream ips = ReflectTest2.class.getResourceAsStream("resources/config.properties");        //在classpath的根目录下找,必须从根开始写绝对路径        InputStream ips = ReflectTest2.class.getResourceAsStream("/cn/itcast/day1/resources/config.properties");        Properties props = new Properties();        props.load(ips);        ips.close();        String className = props.getProperty("className");                    //Collection collections = new HashSet();        Collection collections = (Collection)Class.forName(className).newInstance();    }}


0 0
原创粉丝点击