------- android培训、java培训、期待与您交流! ----------
一:什么是反射。
反射的概念:主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。
java中的反射:是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,并能直接操作程序的内部属性和方法
总结:简而言之Java的反射能在程序运行的时候,获取正在运行类中的属性和方法。并且还能访问、检测和修改他们的能力。
二:反射的基础Class类。
Java程序中的各个java类属于同一类事物,描述这类事物的java类名就是Class.
如何得到Class的对象呢?有三种方法可以的获取:
1、调用Object类的getClass()方法来得到Class对象,这也是最常见的产生Class对象的方法。例如:
MyObject x;
Class c1 = x.getClass();
2、使用Class类的中静态forName()方法获得与字符串对应的Class对象。例如:
Class c2=Class.forName("MyObject"),Employee必须是接口或者类的名字。
3、获取Class类型对象的第三个方法非常简单。如果T是一个Java类型,那么T.class就代表了匹配的类对象。例如
Class cl1 = Manager.class;
Class cl2 = int.class;
Class cl3 = Double[].class;
注意:Class对象实际上描述的只是类型,而这类型未必是类或者接口。例如上面的int.class是一个Class类型的对象。由于历史原因,数组类型的getName方法会返回奇怪的名字。
Class类的常用方法
1、getName()
一个Class对象描述了一个特定类的属性,Class类中最常用的方法getName以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void) 名称。
2、newInstance()
Class还有一个有用的方法可以为类创建一个实例,这个方法叫做newInstance()。 例如:
x.getClass.newInstance(),创建了一个同x一样类型的新实例。newInstance()方法调 用默认构造器(无参数构造器)初始化新建对象。
3、getClassLoader()
返回该类的类加载器。
Class的一些使用技巧
1、forName和newInstance结合起来使用,可以根据存储在字符串中的类名创建对象。例 如
Object obj = Class.forName(s).newInstance();
2、虚拟机为每种类型管理一个独一无二的Class对象。因此可以使用==操作符来比较类 对象。例如:
if(e.getClass() == Employee.class)...
三:什么是字节码
字节码是一种中间码,它比机械码更抽象。它经常被看作是包含一个执行程序的二进制文件,更像一个对象模型。在java中,字节码是CPU构架(JVM)的具有可移植性的机械语言。
四:写java反射类一般的两步骤:
第一步:获取类的字节码;获取方法有四种:
第一种:运用getClass() 注:每个class 都有此函数
第二种:运用Class.getSuperclass() 通过子类求出父类的字节码
第三种:运用Class.forName()(最常被使用)。通过用Class类中的方法
第四种:运用primitive wrapper classes的TYPE 语法,这个有局限性只能用于 九个预定义的对象:八个基本数据对象 + void
举例:
String str1 = "abc";
第一种:Class cls1 = str1.getClass(); 或者Class cls2 = String.class;
第二种:Class c2 = cls1.getSuperclass();
第三种:Class cls3 = Class.forName("java.lang.String");
第四种:Class c1 = Boolean.TYPE;
第二步:使用java的api,常用的操作类有。
Field类:简单的理解可以把它看成一个封装反射类的属性的类。
//构造一个含构造方法的变量
public class ReflectPoint {
private int x;
public int y;
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
}
得到某个类中的某个public成员变量:
例:Field filedY = pt1. getClass.getField(“y”);
打印某个类中的某个public成员变量:
System.out.println(fieldY.get(pt1));
得到某个类中的某个private成员变量:
Field filedX = pt1. getClass.getDeclaredfield(“x”);
打印某个类中的某个private成员变量:
fieldX.setAccessible(true);
System.out.println(fieldx.get(pt1));
Constructor类:Constructor类则封装了反射类的构造方法。
得到某个类所有的构造方法:
例:Constructor[] constructor = Class.forName(“java lang String”).getCoustructor();
得到某一个的构造方法:
例:Constructor constructor =
Class.forName(“java lang String”).getCoustructor(StringBuffer.class);
创建实例对象。
通常方式:
String str = new String(new StringBuffer(“abc”));
反射方式:
String str = (String)constructor.newInstance(new StringBuffer(“abc”));
Method类:它是用来封装反射类方法的一个类。
//方法与对象时没有关系的。参数(方法的名字,参数类型)
String Str1 = "abc";
获取某个对象的方法。
Method methodCharAt = String.class.getMethod("charAt", int.class);
调用某个对象方法中的方法。
//invoke意思是调用,方法对象中的方法。(对象名,传入的参数)
System.out.println(methodCharAt.invoke(Str1, 1));
//如果invoke(null, 1)第一个参数为null,则这个方法是静态方法。
五:数组与Object之间注意事项。
1.数组是基础类型(int,byte,short,long, char, double,float ,boolean )的数组就不能存入Object数组内;
例:int[] a1 = new int[]{1,2,3};
Object[] obj = a1;//是错误的。
原因:Object[]内部要装的是Object类型的元素,而int[] a1内装的是int类型的数组(基本类型),所以错误。
2.数组不是基本类型时,数组存入Object数组时要拆包。
例:
int[] a1 = new int[]{1,2,3};
String[] a4 = new String[]{"a","b","c"};
sop(Arrays.asList(a1));//打印的是[[I@3d4b7453]
sop(Arrays.asList(a4));//打印的是[a, b, c]
原因:jdk1.4中的asList接收的是Object的数组,因为是int类型,所以和Object[]数组对不上, 所以跳到jdk1.5版本中T... a可变参数的Object,但这Object当做是一个参数,
五:数组的反射
1.创建数组打印的类,所以要获取字节码,数组长度。
public static void printObject(Object obj){
Class<? extends Object> 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);
}
}
2.就是对数组就会拆包,对不是数组就打印。
printObject(a4);
printObject("xcv");
printObject(a1);
打印结果:
a
b
c
xcv
1
2
3
六:反射实现框架功能:
框架(Framework):
是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法;另一种定义认为,框架是可被应用开发者定制的应用骨架。前者是从应用
方面而后者是从目的方面给出的定义。
框架与工具的区别:
工具被用户的类调用,而框架则是调用用户提供的类。
框架解决的核心问题:
1.框架要顾及以后写的类。
2.框架不能new实例对象,而是要用反射类获取。
框架举例:
package fanshe2;
//用反射做框架
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Collection;
import java.util.Properties;
public class ReflectTest3 {
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
InputStream ips = ReflectTest3.class.getClassLoader().getResourceAsStream("config2.properties");
Properties props = new Properties();
props.load(ips);
ips.close();//要马上关闭资源,因为这样系统资源会泄露。
String className = props.getProperty("className");
Collection collections = (Collection)Class.forName(className).newInstance();
ReflectPoint pt1= new ReflectPoint(3,3);
ReflectPoint pt2= new ReflectPoint(5,5);
ReflectPoint pt3= new ReflectPoint(3,3);
collections.add(pt1);
collections.add(pt2);
collections.add(pt3);
collections.add(pt1);
System.out.println(collections.size());
}
}
在反射中配置文件的设置方法://配置文件名叫config.properties
//配置文件方式一:-------------------------------------------------------------------------------------------
//config2.properties为相对路径,相对于当前工作目录
//InputStream ips = new FileInputStream("config2.properties");
//绝对路径例:("d:\\config2.properties");
//真正实际用途是配置出来的。就是用完整的路径,但完整的不是硬编码,而是运算出来的。getReal.path();
//配置文件方式二:用类加载器----------------------------------------------------------------------------
//InputStream ips = ReflectTest3.class.getClassLoader().getResourceAsStream("config2.properties");
//如果("config2.properties")这样写,则去classPath的根目录下逐个找"config2.properties"该文件。
//如果("fanshe2/config2.properties")
//类加载器方法的缺点只能读取文件,不能写文件。方式一还能写文件用OutputStream.
//配置文件方式三:用类方法:-------------------------------------------------------------------------------
InputStream ips = ReflectTest3.class.getResourceAsStream("config2.properties");
//如果配置文件不再自己的包内,则直接写成:
//InputStream ips = ReflectTest3.class.getResourceAsStream("fanshe22/config2.properties");
//如果所在的包名加入了/,则要完整的包名因为他是绝对路径了。
//InputStream ips = ReflectTest3.class.getResourceAsStream("完整的包路径/所在的包名/config2.properties");