反射机制基础学习笔记,
反射机制基础学习笔记,
一、反射机制
反射就是把 Java 类中的各种成分映射成相应的 java 类。
Class 类是用类来描述类。
Java 不符合传统动态语言的定义,因为 Java 在运行时不能改变程序结构和变量类型。
但 Java 有动态相关运行机制:反射。
通过反射,Java 可以于运行时加载、探索和使用编译期间完全未知的类,生成其对象实体。
二、反射的概念
在Java中的反射机制是指在运行状态中,对任意一个类,都能知道其属性和方法;
对于任意一个对象,都能够调用它的任意一个方法;这种动态获取信息、动态调用方法
的功能就被成为 Java 语言的反射机制。
构建框架技术的基础。
通常在开发通用性比较广的框架、基础平台时可能会大量的使用反射。
因为在很多Java EE 框架中都需要根据配置文件信息来创建 Java 对象,从配置文件读取的只是
某个类的字符串类名,程序想根据字符串来创建对应的实例,就必须使用反射,如 Spring 框架。
Person.java-> 编译器 -> Person.class -> Java虚拟机 -> 运行程序
Java反射:? -> 编译器 <-> 运行程序
三、动态性质
1、运行时生成对象实例
2、运行期间调用方法
3、运行期间更改属性
四、功能
1、运行时判断任意一对象所属的类。
2、 构造一个任意类的对象。
3、 判断任意一类所具有的方法和属性。
4、 调用任意一对象的方法。
5、 生成动态代理。
五、Java 反射应用场合
Java 程序中许多对象在运行时都会出现两种类型:(编译时类型)和(运行时类型)
编译时类型由声明该对象时使用的类型决定,运行时类型由实际赋给该对象的值决定。
person p=new Student();
编译时类型 运行时类型
因为对象 P 是引用类型,在编译时其类型由 Person 决定, 只有运行时才发现起实际引用内容
是 Student ,所以称为运行时类型。
此外,程序在运行时可能接收到一个编译类型为 Object 的类对象,若此对象又无法在编译时
被判断具体类型,但程序又需要调用该对象运行时类型的方法。所以程序要在运行时了解对象和类的真实信息。
六、反射 API
1、Class类 反射的核心类,可以获取类的属性、方法等内容信息(获取类的字节码)
2、Field类 表示类的属性,可以获取和设置类中属性的值 ————
3、Method类 表示类的方法,它可以用来获取类中方法的信息,或者执行方法。 ————Java.lang.reflect
4、Constructor类 表示类的构造方法 ————
5、AccessibleObject类 是 Field、Method 和 Constructor 对象的基类。
它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。
gender2.setAccessible(true);
七、ClassLoader
类装载器是用来把类(class)装载进 JVM 的。
1、JVM 规范定义了两种类型的类装载器:
a、启动类装载器 (bootstrap)
b、用户自定义装载器 (user-defined class loader)。
2、JVM 在运行时会产生 3个类加载器组成的初始化加载器层次结构
a、Bootstap Classloader
(引导类加载器:用C++编写,是 JVM 自带的类装载器,负责 Java 平台核心库
用来装载核心类库。该加载器无法直接获取)
a
3、classLoader=classLoader.getParent();//null
b
1、Class.forName("java.lang.Object").getClassLoader();//(Object类位于Java平台核心库)
b、Extension Classloader
(扩展类加载器:负责 jdk home/lib/ext 目录下的jar包
或-D java.ext.dirs 指定目录下的 jar 包转入工作库)
a
2、classLoader=classLoader.getParent();
c、System Classloader
(系统类加载器:负责 java-classpath 或 -D java.class.path 所指的
目录下的类与 jar 包转入工作,即当前项目中bin目录下所有文件的加载)
a
1、ClassLoader classLoader=ClassLoader.getSystemClassLoader();
c->b->a 自底向上检查类是否已加载
a->b->c 自顶向下尝试加载类
读取配置文件
1、利用系统类加载器的.getResourceAsStream(String name)方法
InputStream in=Test.class.getClassLoader().getResourceAsStream("cn/guigu/demo/test.Properties");
Properties p = new Properties();
p.load(in);
System.out.println("类加载器的 getResourceAsStream() 方法读取:"+p.get("name"));
2、利用 FileInputStream 的有参构造(部署到 Tomcat 中时,没有 src 目录)
FileInputStream fi=new FileInputStream("src/cn/guigu/demo/test.Properties");
Properties pr=new Properties();
pr.load(fi);
System.out.println("FileInputStream读取:"+pr.get("name"));
八、获取 Class 对象的方式
每个类被加载后,系统就会为该类生成一个 Class 对象,通过该对象就可以访问到 Java虚拟机 中的这个类。(Class 对象只能由系统建立对象,一个类在 JVM 中只会有一个 Class 实例)
Java 程序中获得 Class 对象通常有如下方法:
1、调用某个对象的 getClass() 方法(如果只是定义了一个接口,不知其具体实现类,而接口无法实例化,则无法使用该方法)
Person p = new Person();
Class cla = p.getClass();
2、调用某个类的 class 属性来获取(性能更高(无需调用方法),更安全(在编译器便可检查是否有调用的类)(在 Hibernate 中很常见。 )
Class cla = Person.class;
3、使用 Class 类的 forName() 静态方法 (程序设计者最常用)
Class cla = Class.forName("com.pb.reflect.classifo.Person");
括号内必须是类的全名(类名前要有完整的包名)
九、访问 Class 对应的类所包含的注释
1、<A extends Annotation>A getAnnotation(Class<A> annotationClass)
试图获取该 Class 对象所表示类上指定类型的注释;如果该类项的注释不存在则返回 null。
其中 annotationClass 参数对应于注释类型的 Class 对象。
2、Annotation[] getAnnotations();
返回此元素上存在的所有注释。
3、Annotation[] getDeclaredAnnotations();
返回直接存在于此元素上的所有注释。
十、访问 Class 对应的类所包含的内部类
Class[] getDeclaredClasses();
返回该 Class 对象所对应类里包含的全部内部类
十一、访问 Class 对应的类所在的外部类
Class getDeclaringClass();
返回该 Class 对象所在的外部类
十二、访问该 Class 对象所对应类继承的父类、所实现的接口等
1、int getModifiers(); 返回此类或接口的所有修饰符 (返回的整数需使用 Modifer 工具类的方法来解码,获取真实的修饰符)
2、Class[] getInterfaces(); 返回该 Class 对象对应类所实现的全部接口
3、package getPackage(); 获取此类的包
4、String getName(); 以字符串形式返回此 Class 对象所表示的类的名称
5、String getSimpleName(); 以字符串形式返回此 Class 对象所表示的类的简称
6、Class getSuperclass(); 返回该 Class 所表示的类的超类对应的 Class 对象
十三、从 Class 类中获取构造方法,创建对象(只有需要动态创建某个对象时,才考虑反射)
1、Constructor getConstructor(Class[] params);
Constructor co = c.getConstructor(String.class, List.class);
2、Constructor[] getConstructor();
3、Constructor getDeclaredConstructor(Class[] params);
4、Constructr[] getDeclaredConstructor();
5、Class 类直接使用 newInstance() 创建对象
实际上是利用默认构造方法来创建该类的实例。
//源码中将第一次调用的无参构造缓存起来以方便以后直接调用,这说明反射比较占用资源
String str2 = (String) Class.forName("java.lang.String").newInstance();
System.out.println(str2.contains(""));
6、使用 Constructor 对象创建对象
通过用某个类的指定构造方法来创建实例。
//a、得到某个构造(编译时只知道是一个 Constructor ,运行时才知道具体是哪个构造方法)
Constructor<?> constructor
= Class.forName("java.lang.String").getConstructor(StringBuffer.class);
//b、创建一个对象 (new String(new StringBuffer("abc"))
//编译期只知道是一个 constructor 创建的对象, 所以要强制转换
String str = (String) constructor.newInstance(/*abc(运行时异常)*/new StringBuffer("abc"));
System.out.println(str.charAt(2));
十四、类的方法:Method
注:此类代码一般只出现在编写开发工具中,随笔功能就是通过这类代码实现。
对我们而言最常用的是通过 Method 调用类中的方法。(invoke(Object obj, Object... args))
1、获得本类中声明的所有成员方法,无视权限
cla.getDeclaredMethods();
2、获得本类极其继承父类与接口中所有公开的成员方法
cla.getMethods();
3、获取本类中指定的公开方法,参数传方法名和方法参数类型.class
method = cla.getDeclaredMethod("setAge",Integer.class);
4、执行方法
Object obj = cla.newInstance( );
method.invoke( obj, new Integer( 22 ) );
method 类中的 invike 方法
Object invoke( Object obj, Object...args );
该方法中的 obj 是执行该方法的对象,后面的 args 是执行该方法时传入该方法的参数。
a、调用私有方法:
Class cla = Person.class; //获取该类对应的Class对象
Person p = new Person( ); //创建该类的对象
Method me = cla.getDeclaredMethod( "getName", null ); //获得私有方法
me.setAccessible( true ); //取消访问权限
Object ob= me.invoke( p, null ); //调用私有方法
b、Constructor<?> constructor
= Class.forName( "java.lang.String" ).getConstructor( StringBuffer.class );
Method charAt = Class.forName( "java.lang.String" ).getMethod( "charAt", int.class );
String str = (String) constructor.newInstance( new StringBuffer("abc") );
//JDK1.5,有自动装箱的功能
System.out.println( charAt.invoke(str, 2) );
c、根据用户提供的类名,去执行该类中的 main 方法。
public static void main(String[] args) {
String className = "chuanzhi.TestArguments";
Method method = Class.forName( className ).getMethod( "main", String[].class );
//JDK1.5 为了兼容 JDK1.4,如果传入一个 String[ ], 会把数组元素拆出来 //静态方法.invoke(null, 2);不需要对象
// method.invoke( null, new Object[ ] { new String[ ]{ "111","222","333" } } );
method.invoke( null, ( Object ) new String[ ]{ "111","222","333" } );
}
class TestArguments{
public static void main(String[] args){
for ( String str : args ) {
System.out.println( "main:" + str );
}
d、自己获取方法的权限,返回值类型,方法名,字符串参数及异常并按顺序拼接出来。
例:
interface Student2{
void remove();
}
class StudentImpl2 implements Student2{
@SuppressWarnings("unused")
private void run(){}
public void add(){}
@Override
public void remove() {}
}
public class CopyOfConstructorTest3 {
@Test
public void methodTest() throws SecurityException, ClassNotFoundException{
Class<?> clazz = Class.forName("chuanzhi.StudentImpl2");
Method[] methods = clazz.getMethods();
for (Method method : methods) { //获取本类极其继承的父类对象与接口的 public 方法。
String modifier = Modifier.toString(method.getModifiers()); //获取方法权限
String returnType = method.getReturnType().getSimpleName(); //获取返回值类型
String name = method.getName(); //获取方法名
Class<?>[] types = method.getParameterTypes(); //获取所有参数类型
String args = "" ;
if(types.length>0){
for (int i = 0; i < types.length; i++) {
if(i==types.length-1){
args +=types[i].getSimpleName()+" age"+i;
break;
}
args +=types[i].getSimpleName()+" age"+i+",";
}
}
System.out.println(modifier+" "+returnType+" "+name+" ("+args+")");
Class<?>[] exps = method.getExceptionTypes(); //获取所有异常
String exp = "" ;
if(exps.length>0){
for (int i = 0; i < exps.length; i++) {
if(i==exps.length-1){
exp += exps[i].getSimpleName();
break;
}
exp += exps[i].getSimpleName()+",";
}
System.out.println("throws "+exp);
}
}
}
}
十五、访问属性
Field 类用于获取类中的属性
1、getFields( ) -- 获取本类及继承接口的 所有 public 属性
2、getField( )
3、getDeclaredFields( ) -- //获取本类的 所有属性
4、getDeclaredFiled( )
5、Class.forName( "" ).getSuperclass( ).getDeclaredFields( ); -- 获取该类的 父类的所有属性
得到属性值
getXxx(object obj);
获取 obj 对象该属性的属性值,其中参数 obj 为该属性所在的对象。
此处的 Xxx 对应 8 个基本类型,如果该属性的类型是 引用类型则取消
get 后面的 Xxx。
为属性赋值
setXxx(object obj,Xxx val);
将 obj 对象的该属性设置成 val 值。此处的 Xxx 对应 8 个基本类型,如果该属性
的类型是 引用类型,则取消 set 后面的 Xxx。
Customer c = new Customer( "叶秋", "男" );
//a、获取公有字段
Field name = c.getClass( ).getField( "name" );
name.get(c);
//b、获取私有字段
Field gender2 = c.getClass( ).getDeclaredField( "gender" );
gender2.setAccessible( true );
gender2.get( c );
//c、为字段赋值
gender2.set( c, "男人" );
//d、更改字段中的字符。
changeStringValue(c);
private void changeStringValue(Object obj ) {
Field[] fields = obj.getClass( ).getFields( ); //获取所有 public 属性
for ( Field f : fields ) {
//字节码用 == 比较,专业
if( f.getType() == String.class ){
String str = ( String )f.get( obj );
String s = str.replace( 'b' , 'a' );//字符替换,返回一个新的字符串
f.set( obj, s );
}
}
e、获取属性的综合信息
interface Message{
public static final String INFO = "Hello world";
}
@SuppressWarnings("unused")
class Person{
private String name;
private Integer age;
}
@SuppressWarnings("unused")
class Student3 extends Person implements Message{
private String school;
private Double prive;
}
public class fieldTest {
@Test
public void fTest() throws ClassNotFoundException, InstantiationException, IllegalAccessException, SecurityException, NoSuchFieldException{
Class<?> clazz = Class.forName("chuanzhi.Student3");
Object obj = clazz.newInstance();
//获取本类及继承接口的 public 属性
//1、获取对象信息
Field[] fields = clazz.getFields();
for (Field field : fields) {
String modifier = Modifier.toString( field.getModifiers() ); //获取属性权限
String type = field.getType( ).getSimpleName( ); //获取属性类型
String name = field.getName( ); //获取属性名称
Object value = field.get( obj );
StringBuilder sbr = new StringBuilder( );
sbr.append( modifier );
sbr.append (" " + type );
sbr.append( " "+ name );
sbr.append( " " + "== " + value );
System.out.println(sbr);
}
System.out.println( "\n\n" );
//获取本类的所有属性
Field[] dFields = clazz.getDeclaredFields( );
for ( Field field : dFields ) {
String modifier = Modifier.toString( field.getModifiers( ) );
Class<?> type = field.getType( );
String name = field.getName( );
StringBuffer sb = new StringBuffer( );
sb.append( modifier );
sb.append( " " + type );
sb.append( " " + name );
System.out.println( sb );
}
//获取父类属性
Field[] supfield = clazz.getSuperclass( ).getDeclaredFields( );
for(Field field :supfield){
System.out.println( field.getName( ) );
}
//修改属性
Field dfield = clazz.getDeclaredField( "school" );
dfield.setAccessible( true );
dfield.set( obj, "私有属性" );
System.out.println(dfield.get( obj ));
}
}
十六、动态创建和访问数组
Array 类
1、创建数组
static Object newInstance( Class componentType , int...length )
创建一个具有指定的元素类型、指定维度的新数组。
2、返回元素
static xxx getXxx( Object array, int index );
返回 array 数组中第 index 个元素。其中 xxx 是各种基本数据类型,如果数组元素是引用数据类型,
则去掉 xxx,方法变为
static get( Object array, int index );
3、为数组元素赋值
static void setXxx( Object array, int index, xxx val );
将 array 数组中 index 元素的值设为 val。其中 xxx 是各种基本数据类型,如果数组
元素是引用类型,则去掉xxx,方法变为
static void set(Object array,int index,xxx val);
例:
1、创建一个元素类型为 String ,长度类型为 10 的数组
Object arr = Array.newInstance(String.class, 10);
Array.set(arr, 0, "空");
System.out.println(Array.get(arr,0));
2、创建一个维度为 [5][10] 数组
Object arr1 =Array.newInstance( String.class, 5 , 10 );
Object firstIndex = Array.get( arr1 , 0);
Array.set( firstIndex, 6, "默" );
System.out.println(Array.get( firstIndex, 6) );
//将arr1 数组转换为 arr_str 二维数组
String[][] arr_str = ( String[][] ) arr1;
System.out.println( arr_str[0][6] );
int[] a1 = new int[]{1, 2, 3};
int[] a2 = new int[4];
int[][] a3 = new int[4][3];
String[] a4 = new String[]{"a","b","c"};
a1.getClass() == a2.getClass();//true
a1.getClass().getSuperclass();//class java.lang.Object
Object o2 = a2;//int[] 是Object 类型, int 不是。
Object[] o3 = a3;
Arrays.asList(a1);//[[I@48f0c0d3]
Arrays.asList(a4);//[a, b, c]
相关文章
- 暂无相关文章
用户点评