您的位置:首页 > 职场人生

黑马程序员_反射

2015-10-14 22:20 525 查看
------- android培训、java培训、期待与您交流!----------

反射

一.类加载器

1.类的加载

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

 加载 

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

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

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

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

解析:解析过程就是在类型的常量池中寻找类,接口,字段和方法的符号引用,把这些符号引用替换成直接引

用.这个阶段可以被推迟到初始化之后,当程序运行的过程中真正使用某个符号引用的时候再去解析它

类初始化时机

1)创建类的实例

2)访问类的静态变量,或者为静态变量赋值

3)调用类的静态方法

4)使用反射方式来强制创建某个类或接口对应的java.lang.Class对象

5)初始化某个类的子类

6)直接使用java.exe命令来运行某个主类

2.类加载器

含义:负责将.class文件加载到内在中,并为之生成对应的Class对象

类加载器的组成及作用

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.概述

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

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

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

2.反射的基本步骤:

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

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

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

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

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

Class clazz = Person.class; 

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

Object obj = new Person(); 

Class clazz1 = obj.getClass(); 

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

String classname = “cn.itcast.reflect.Person”; 

Class clazz = Class.forName(classname);//当类的字节码已经加载进了内存,就将该字节码返回;如果jvm还没有该字节码,就用类加载器加载,再返回加载的字节码

前两种方式不利于程序的扩展,因为都需要在程序使用具体的类来完成 

三种方式得到的字节码都是同一个字节码:

代码演示: 

<strong><span style="font-family:SimHei;font-size:14px;">[java] view plaincopy

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 </span></strong>

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.反射的使用:

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();

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

获取所有方法:

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 plaincopy

[java] view plaincopy 

package itcast; 

import java.lang.reflect.Constructor; 

import java.lang.reflect.Method; 

/** 

* @author oyfc 

*/ 

public class MethodDemo { 

/** 

* 通过反射获取成员方法并使用。 

* @param args 

* @throws ClassNotFoundException 

*/ 

<span style="white-space:pre"> </span>public static void main(String[] args) throws Exception { 

<span style="white-space:pre"> </span>//获取字节码文件对象 

<span style="white-space:pre"> </span>String className = "itcast.Person"; 

<span style="white-space:pre"> </span>Class class1 = Class.forName(className); 

<span style="white-space:pre"> </span>//创建对象 

<span style="white-space:pre"> </span>Constructor constructor = class1.getConstructor(String.class,int.class); 

<span style="white-space:pre"> </span>Object object = constructor.newInstance("zhangfei",21); 

<span style="white-space:pre"> </span>//获取所有成员方法 

<span style="white-space:pre"> </span>Method[] methods = class1.getDeclaredMethods(); 

<span style="white-space:pre"> </span>for(Method meth: methods){ 

<span style="white-space:pre"> </span>sop(meth); 

<span style="white-space:pre"> </span>} 

<span style="white-space:pre"> </span>//获取成员方法 

<span style="white-space:pre"> </span>Method method = class1.getDeclaredMethod("show"); 

<span style="white-space:pre"> </span>//暴力访问 

<span style="white-spac
d04a
e:pre"> </span>method.setAccessible(true); 

<span style="white-space:pre"> </span>//调用方法 

<span style="white-space:pre"> </span>method.invoke(object, null); 

<span style="white-space:pre"> </span>} 

/** 

* @param method 

*/ 

<span style="white-space:pre"> </span>private static void sop(Object object) { 

<span style="white-space:pre"> </span>System.out.println(object); 

<span style="white-space:pre"> </span>} 



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

获取所有构造方法:

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

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

获取单个构造方法:

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

Constructor<T> gettDeclaredConstructor():

使用方法:Constructor类的方法

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

代码演示:

[java] view plaincopy

</pre>程序示例:</p><p><span style="font-size:18px;"></span><pre name="code" class="java">[java] view plaincopy 

package itcast; 

import java.lang.reflect.Constructor; 

/** 

* 通过反射获取构造方法并使用。 



* @author oyfc 

*/ 

<span style="white-space:pre"> </span>public class ConstructorDemo { 

/** 

* @param args 

* @throws Exception 

*/ 

<span style="white-space:pre"> </span>public static void main(String[] args) throws Exception { 

<span style="white-space:pre"> </span>

<span style="white-space:pre"> </span>// 获取字节码文件对象 

<span style="white-space:pre"> </span>String className = "itcast.Person"; 

<span style="white-space:pre"> </span>Class class1 = Class.forName(className); 

<span style="white-space:pre"> </span>// 获取所有所有公共构造方法 

<span style="white-space:pre"> </span>// Constructor[] constructors = class1.getConstructors(); 

<span style="white-space:pre"> </span>// 获取所有所有构造方法 

<span style="white-space:pre"> </span>Constructor[] constructors = class1.getDeclaredConstructors(); 

<span style="white-space:pre"> </span>for (Constructor con : constructors) { 

<span style="white-space:pre"> </span>sop(con); 

<span style="white-space:pre"> </span>} 

<span style="white-space:pre"> </span>//获取无参构造方法 

<span style="white-space:pre"> </span>Constructor constructor = class1.getDeclaredConstructor(); 

<span style="white-space:pre"> </span>//取消访问检查,暴力访问 

<span style="white-space:pre"> </span>constructor.setAccessible(true); 

<span style="white-space:pre"> </span>//创建对象 

<span style="white-space:pre"> </span>Object object = constructor.newInstance(); 

<span style="white-space:pre"> </span>sop(object);//null::0 

<span style="white-space:pre"> </span>//获取有参构造方法 

<span style="white-space:pre"> </span>Constructor constructor2 = class1.getDeclaredConstructor(String.class); 

<span style="white-space:pre"> </span>//创建对象 

<span style="white-space:pre"> </span>Object object2 = constructor2.newInstance("张飞"); 

<span style="white-space:pre"> </span>sop(object2);//张飞::0 

<span style="white-space:pre"> </span>} 

/** 

* @param con 

*/ 

<span style="white-space:pre"> </span>private static void sop(Object obj) { 

<span style="white-space:pre"> </span>System.out.println(obj); 

<span style="white-space:pre"> </span>} 



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

获取所有成员:

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

Field[] getDeclaredFields():

获取单个成员:

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

Field getDeclaredField(String name)

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

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

代码演示:

<strong><span style="font-family:SimHei;font-size:14px;">[java] view plaincopy

[java] view plaincopy 

Field field = clazz.getField(fieldName);//获取指定的字段值 

field.setAccessible(true);//取消访问检查,也称暴力访问 

field.set(obj,value);//将指定对象变量上此 Field 对象表示的字段设置为指定的新值. 

//如:

[java] view plaincopy

[java] view plaincopy 

package 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); 



} </span></strong>

6.数组的反射 

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

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

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

代码演示:

<span style="font-family:SimHei;">[java] view plaincopy

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)); 

}</span> 

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 形式返回指定数组对象的长度

代码演示:

<strong>[java] view plaincopy

<span style="white-space:pre"> </span>private static void printObject(Object obj) { 

<span style="white-space:pre"> </span>Class clazz = obj.getClass(); 

<span style="white-space:pre"> </span>if(clazz.isArray()){ 

<span style="white-space:pre"> </span>int len = Array.getLength(obj); 

<span style="white-space:pre"> </span>for(int i=0;i<len;i++){ 

<span style="white-space:pre"> </span>System.out.println(Array.get(obj, i)); 

<span style="white-space:pre"> </span>} 

<span style="white-space:pre"> </span>}else{ 

<span style="white-space:pre"> </span>System.out.println(obj); 

<span style="white-space:pre"> </span>} 

} </strong>

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

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

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

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

代码演示:

<strong><span style="font-family:SimHei;">[java] view plaincopy

<span style="white-space:pre"> </span>public class ReflectTest2 { 

<span style="white-space:pre"> </span>public static void main(String[] args) throws Exception{ 

<span style="white-space:pre"> </span>//InputStream ips = new FileInputStream("config.properties"); 

<span style="white-space:pre"> </span>//查找资源,类加载器,配置文件放在classpath的目录下 

<span style="white-space:pre"> </span>//InputStream ips = ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties"); 

<span style="white-space:pre"> </span>//相对该类包的路径,写相对路径 

<span style="white-space:pre"> </span>//InputStream ips = ReflectTest2.class.getResourceAsStream("resources/config.properties"); 

<span style="white-space:pre"> </span>//在classpath的根目录下找,必须从根开始写绝对路径 

<span style="white-space:pre"> </span>InputStream ips = ReflectTest2.class.getResourceAsStream("/cn/itcast/day1/resources/config.properties"); 

<span style="white-space:pre"> </span>Properties props = new Properties(); 

<span style="white-space:pre"> </span>props.load(ips); 

<span style="white-space:pre"> </span>ips.close(); 

<span style="white-space:pre"> </span>String className = props.getProperty("className"); 

<span style="white-space:pre"> </span>//Collection collections = new HashSet(); 

<span style="white-space:pre"> </span>Collection collections = (Collection)Class.forName(className).newInstance(); 

<span style="white-space:pre"> </span>} 

} </span></strong>

版权声明:本文为博主原创文章,未经博主允许不得转载。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: