----------- android培训、java培训、期待与您交流! ------------
反射的基石-Class类
Class类:用于描述每个类在内存中的“字节码”这一事物。字节码包含的信息有:类名、类的访问权限、类所属的包名、字段名称列表、方法名称列表等。
一个类被加载到内存中后,占用一片内存空间,这个空间里的内容即为字节码。
如何得到各个字节码的实例对象?
1、类名.class 如System.class 应用前提:知道这个类的类名
2、对象.getClass() 如new Date().getClass 应用前提:有这个对象或其引用
3、Class.forName("类名") 如Class.forName("java.util.System") 应用前提:知道类的全路径名
框架主要用到反射技术。
主要用到第三种Class.forName("classFullName"), 因为在写源程序时还不知道类的名字,是别人传过来的。
class ClassDemo {
public static void main(String[] args) throws Exception {
String str = "Hello";
Class c1 = str.getClass();
Class c2 = String.class;
Class c3 = Class.forName("java.lang.String");//抛出类没找到异常
System.out.println(c1 == c2);//true
System.out.println(c3 == c2);//true
System.out.println(c1.isPrimitive());//false:字符串不是基本数据类型
System.out.println(int.class.isPrimitive());//true:int是基本数据类型
}
}
int.class == Integer.TYPE;//true
数组类型Class实例对象
System.out.println(int[].class.isArray());//true
只要在源程序中出现的类型,都有各自的Class实例对象,如int[]、void...
反射成份类
反射:将java类的各个成份(构造方法、普通方法、方法参数、字段、访问权限)分别映射成java类。
创建实例对象的三种方式:
1、普通方式: String str1 = new String("Hello");
2、反射构造方法: String str2 = str1.getClass.getConstructor((StringBuilder.class).newInstance(new StringBuilder("Hello"));
3、Class.newInstance()方法:
String str3 = String.class.newInstance();//该方法内部先得到默认的构造方法,再用构造方法创建实例对象
Field类
需求:定义一个描述点的类,实例化一个对象,然后用反射将这个对象中的String类型的属性值里的字符'o',全部更改为字符'e'
思路:1、创建一个类对象 2、设计一个能更改对象属性值的方法。
步骤:①将对象的所有字段用反射取出,存放于一个字段数组里 ②、判断某个数组里每个字段所属的类型 ③、取得数组里的字段值赋值给一个字符串
④、将此字符串中的'o'替换为'e'。⑤、将替换后的字符串写入对象中
代码如下:
import java.lang.reflect.*;
class ReflectFieldDemo {
public static void main(String[] args) throws Exception {
Point p = new Point(3, 6);
//获取所有字段并存放于数组中
Field[] fields = p.getClass().getDeclaredFields();
for(Field field : fields) {//遍历字段数组
field.setAccessible(true);//去除字段的访问限制
if(field.getType() != String.class) {
continue;//不是String类型的字段就判断下一个
}
//获取每个字段的值
String oldValue = (String)field.get(p);
//修改字段值
String newValue = oldValue.replace('o', 'e');
//重新设置
field.set(p, newValue);
}
//打印出每个String字段的值
System.out.println(p);
}
}
class Point {
private int x, y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
public String str1 = "Hello";
public String str2 = "Cool";
private String str3 = "Melon";//私有的String字段
public String toString() {
return str1 + ", " + str2 + ", " + str3;
}
}
反射的一个用途:修改对象的属性值。
Method类
反射方法:Method charAt = String.class.getMethod("charAt", int.class);
方法调用:1、通常方法:str.charAt(1);
2、反射方法:charAt.invoke(str, 1);
如果传递给Method对象的invoke()方法一个参数(null,args),表明该Method对象对应的方法是一个静态方法。
需求:根据用户提供的类名,去执行该类中的main方法
import java.lang.reflect.*;
public class ReflectMainMethod {
public static void main(String[] args) throws Exception {
//普通方式调用
//MainDemo.main(new String[]{"it", "cast", "heiMa"});
//接收用户输入的类名
String startClassMain = args[0];
//获取主函数字节码
Method mainMethod = Class.forName(startClassMain).getMethod("main", String[].class);
//调用主函数
mainMethod.invoke(null, (Object)new String[]{"it", "cast", "heiMa"});//此处的(Object强制转换是为了防止兼容jdk,1.4拆包)
}
}
class MainStart {
public static void main(String[] args) {
for(String arg : args) {
System.out.println(arg);
}
}
}
//运行时,应加参数,java ReflectMainMethod MainStart 用于声明被调用的类名
数组与Object类的关系
class ArrayVSObject {
public static void main(String[] args) {
int[] i1 = new int[3];
int[] i2 = new int[4];
int[][] i3 = new int[3][];
String[] str = new String[3];
//具有相同维数和相同元素类型的数组的Class字节码文件相同。
sop(i1.getClass() == i2.getClass());//true
System.out.println(i1.getClass() == i3.getClass());//false
System.out.println(i1.getClass() == str.getClass());//false
//打印数组的父类名,结果是Object
sop(i1.getClass().getSuperclass().getName());//java.lang.Object
sop(i3.getClass().getSuperclass().getName());//java.lang.Object
/*基本类型的一维数组可以当作Object使用,不能当作Object[]使用;
非基本类型的一维数组既可以当Object使用,又能当Object[]使用。*/
Object obj1 = i1;//OK
Object[] obj2 = i1;//不OK
Object obj3 = str;//OK
Object[] obj4 = str;//OK
}
public static void sop(Object obj) {//打印语句
System.out.println(obj);
}
}
数组的反射
需求:改写打印语句,是数组就将数组里的元素分别打印,是字符串就直接打印。
class ReflectArray {
public static void main(String[] args) {
String[] str1 = new String[] {"it", "cast", "heiMa"};
String str2 = "ich";
sop(str1);
sop(str2);
}
public static void sop(Object obj) {//打印语句
Class clazz = obj.getClass();
if(clazz.isArray()) {//判断如果传入的是数组类型则符合
//用反射得出数组的长度
int len = Array.getLength(obj);
for(int x=0; x<len; x++) {
//用反射取出每个元素并打印
System.out.println(Array.get(obj, x));
}
} else {
System.out.println(obj);
}
}
}
sop(str1[0].getClass().getName());//打印数组中元素的类型。
反射的作用
反射的作用:实现框架功能
框架(我用它,它调用我的类)、工具(我用它,我调用它的类)
框架开发原理代码:
config.Properties文件中的内容: className = java.util.HashSet
一般在开发中,让用户自己配置应用程序的存放路径,即为根目录,再加上config.Properties的内部路径,即可。
据说javaWeb有个getRealPath()方法可以获根目录,再加上内部路径。
框架的配置文件都放在classpath下。
public class ConfigProperties {
public static void main(String[] args) throws Exception {
//准备读配置文件的内容
InputStream is = new FileInputStream("config.Properties");
Properties props = new Properties();
props.load(is);//使用Properties从配置文件中加载配置信息
is.close();//关闭资源
//获取类名
String className = props.getProperty("className");
//根据加载的类名创建实例对象
Collection c = (Collection) Class.forName(className).newInstance();
//Collection c = new ArrayList();
//往集合中添加元素
ReflectPoint1 p1 = new ReflectPoint1(3,3);
ReflectPoint1 p2 = new ReflectPoint1(3,5);
ReflectPoint1 p3 = new ReflectPoint1(3,3);
c.add(p1);
c.add(p2);
c.add(p3);
c.add(p1);
//打印集合的无数个数
System.out.println(className + ":" + c.size());
}
}
class ReflectPoint1 {//定义点
public int x;
public int y;
public ReflectPoint1(int x, int y) {
this.x = x;
this.y = y;
}
//重写点的hashCode()、equals()方法,其为HashSet判断元素是否相同的依据
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + x;
result = prime * result + y;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ReflectPoint1 other = (ReflectPoint1) obj;
if (x != other.x)
return false;
if (y != other.y)
return false;
return true;
}
public String toString() {
return "(" + x + "," + y + ")";
}
}
//下面这种方式也能用于加载文件:是类加载器加载文件信息。此方式只能读,没有写功能。
InputStream is = ConfigProperties.class.getClassLoader().getResourceAsStream("cn/itcast/javaEnhance/config.Properties");
//第三种方式:
InputStream is = ConfigProperties.class.getResourceAsStream("config.Properties");//比上面方式简单一点,用的是相对路径,相对ConfigProperties.class文件所在包。
内省
内省:IntroSpector
内省是操作JavaBean的方式,JDK提供了对JavaBean操作的一套API,这套API就称为内省。
JavaBean:是一种特殊的java类,主要用于传递数据信息,这种java类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。
JavaBean类的属性是通过方法名来判断的。
java中属性和字段的区别:
属性:通过set和get方法得到
字段:类中的成员变量
class Person {
private String str; //str是字段,是类成员变量
//通过setName()和getName(),得出有个name属性
public void setName(String name) {
str = name;
}
public String getName() {
return str;
}
}
需求:简单内省方式获取、设置javaBean类的属性值
import java.beans.PropertyDescriptor;
import java.lang.reflect.*;
/*用内省API操作javaBean类*/
public class IntroSpector {
public static void main(String[] args) throws Exception {
//定义点对象
ReflectPoint p1 = new ReflectPoint(3, 7);
//已知属性名
String propertyName = "x";
//获取属性描述对象
PropertyDescriptor pd = new PropertyDescriptor(propertyName, p1.getClass());
//获取属性的读方法
Method methodGetX = pd.getReadMethod();
Object obj = methodGetX.invoke(p1);//读方法调用
System.out.println(obj);
//获取属性的写方法
Method methodSetX = pd.getWriteMethod();
methodSetX.invoke(p1, 5);//写方法调用
System.out.println(p1.getX());//为了显示这里调用对象本身的方法
}
}
用复杂内省方式 修改上面的 获取属性的读方法 代码:用IntroSpector.getBeanInfo(arg);
//得到类的javaBean信息
BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
//获取所有属性描述对象
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
Object retVal = null;
//遍历查找指定的属性描述对象,并操作此属性
for(PropertyDescriptor pd : pds) {
if(pd.getName().equals(propertyName)) {
retVal = pd.getReadMethod().invoke(obj);
}
}
return retVal;
还可以用BeanUtils工具(要下载)进行操作JavaBean。
BeanUtils以字符串的形式对JavaBean进行操作
PropertyUtils以属性本身的类型操作。
----------------------- android培训、java培训、期待与您交流! ----------------------
详情请查看: