黑马程序员-高新技术-反射

------- android培训java培训、期待与您交流! ----------
17透彻分析反射的基础
/* 反射的基石-->Class
如何获取字节码,有几种方法?
字节码:类的创建首先要把字节码加载到内存当中,再用这个字节码去复制出一个一个对象。
每一个字节码就是一个类的实例对象。
Java程序中的各个java类属于同一个类事物,描述这类事物的java类名就是Class。
对比提问:众多的人用一个什么类表示?众多的java类用一个什么类表示?
人-->Person
java类-->Class
对比提问:Person类代表人,它的实例对象就是张三,李四这样一个个具体的人Class类代表java类,
它的各个实例对象又分别对应什么呢?
对应各个类在内存中的字节码,例如,Person类的字节码,ArrayList类的字节码,等等。
一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类字节码,不同的
类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间可分别用一个个的对象
来表示,这些对象显然具有相同的类型,这个类型是什么呢?

如何得到各个字节码对应的实例对象(Class类型)
类名.class,例如,System.class
对象.getClass(),例如,new Date().getClass()
Class.forName("类名"),例如,Class.forName("java.util.Date").//传入字符串包名。

九个预定义Class实例对象:
参看Class.isPrimitive方法的帮助
int.class==Integer.Type

数组类型的Class实例对象
Class.isArray()
总之,只要是源程序中出现的类型,都有各自的Class实例对象,例如int[],void...
*/
isPrimitive
public boolean isPrimitive()判定指定的 Class 对象是否表示一个基本类型。 
有九种预定义的 Class 对象,表示八个基本类型和 void。这些类对象由 Java 虚拟机创建,与其表示的基本类型同名,即 boolean、byte、char、short、int、long、float 和 double。 

这些对象仅能通过下列声明为 public static final 的变量访问,也是使此方法返回 true 的仅有的几个 Class 对象。
Boolean.TYPE, Character.TYPE, Byte.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE, Void.TYP
代码:
                        String str1 = "abc";
Class cls1 = str1.getClass();
Class cls2 = String.class;
Class cls3 = Class.forName("java.lang.String");
System.out.println("cls1==cls2:"+(cls1==cls2));
System.out.println("cls1==cls3:"+(cls1==cls3));
System.out.println("cls1.isPrimitive()::"+cls1.isPrimitive());
System.out.println("int.class.isPrimitive()::"+int.class.isPrimitive());
System.out.println("int.class == Integer.class::"+(int.class == Integer.class));
//type常量代表这个包装类型包装的基本类型的字节码。
System.out.println("int.class == Integer.TYPE::"+(int.class == Integer.TYPE));
//数组不是原始类型。
System.out.println("int[].class.isPrimitive()::"+(int[].class.isPrimitive()));
System.out.println("int[].class.isArray()::"+(int[].class.isArray()));
结果:
                    
cls1==cls2:true

cls1==cls3:true
cls1.isPrimitive()::false
int.class.isPrimitive()::true
int.class == Integer.class::false
int.class == Integer.TYPE::true
int[].class.isPrimitive()::false
int[].class.isArray()::true
--------------------------------------------------
18    理解反射的概念
简而言之:反射就是把java类中的各种成分映射成相应的java类
反射就是把java类中的各种成分映射成相应的java类。例如,一个java类中用一个Class类的对象来表示,
一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的java类表示,就像汽车是
一个类,汽车中的发动机,变速箱等等也是一个个的类。表示java类的class类显然要提供一系列的方法,
来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,他们
-------------------------------------------
19    构造方法的反射应用
Constructor类

Constructor类代表某个类中的一个构造方法
得到某个类所有的构造方法:
    例子:Constructor[] constructor = 
                        Class.forName("java.lang.String").getConstructors();

得到某一个构造方法:
    //通过构造方法的参数类型来确定获取的是哪一个构造方法
    例子1:Constructor  constructor =
                         Class.forName("java.lang.String").getConstructor(StringBuffer.class);
   例子2:Constructor constructor2 = ReflectPoint.class.getConstructor( Integer.TYPE,Integer.TYPE)//基本数据类型用法
                    ReflectPoint rp3 =(ReflectPoint)constructor2.newInstance(2,2)
                        获得方法时要用到类型

创建实例对象:
    通常方式:String str = new String(new StringBuffer("abc"));
    反射方式:String str = (String) constructor.newInstance(new StringBuffer("abc"));
            
//通过字节码获取constructor构造方法类。
Constructor constructor1 =  String.class.getConstructor(StringBuffer.class);
//通过constructor创建一个string类,此处有类型提升,需要强转。
//String.class.newInstance()可以通过class类直接创建实例
String conStr = (String)constructor1.newInstance(new StringBuffer("abc"));
System.out.println(conStr);

获取构造方法并创建person类:
        Person p = (Person)Person.class.getConstructor( String.class,/*int.class*/Integer.TYPE).newInstance(new String("zhangsan"),13);
----------------------------------------------------------------
20    成员变量的反射
Field类
Field类代表某个类中的一个成员变量
演示eclipse自动生成java类的构造方法
问题:得到的Field对象是对应到的类上面的饿成员变量,还是对应到对象上的
        成员变量?类只有一个,二该类的实例对象又多个,如果是与对象关联,哪
        关联的是哪个对象呢?所以字段fieldX代表的是x的定义,而不是具体的X变量。
-----------------------------------------------------------------------------------
21    成员变量反射的综合案例
Field 类
//此类用于反射案例,无实际作用。
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}

/* (non-Javadoc)
 * @see java.lang.Object#toString()
 */
@Override
public String toString() {
return "ReflectPoint [str1=" + str1 + ", str2=" + str2 + ", str3="
+ str3 +",x="+x+",y="+y+"]";
}
package cn.itcast.day1;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class ReflectTest2 {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
ReflectPoint pt1 = new ReflectPoint(3,7);
                //fieldY代表的是Y变量而不是具体的y的值
Field fieldY = pt1.getClass().getField("y");
System.out.println(fieldY.get(pt1));
//getDeclaredField()获取所有该类对象的成员无论是否私有,该x的是为prviate私有
Field fieldX = pt1.getClass().getDeclaredField("x");
//如果要获取私有的值必须先设为setAccessible为ture才可以获取值。 叫暴力反射
fieldX.setAccessible(true);
System.out.println(fieldX.get(pt1));
//设定pt1对象fieldY,y的新值为9
fieldY.set(pt1, 9);
System.out.println(pt1);
changeStringValue(pt1);
System.out.println(pt1);
}
//用反射原理获取某个类的String成员,并更改成员的内容将'b'替换成a
private static void changeStringValue(Object obj) throws Exception {
//将某个类的所有成员用Field[] 数组存起来
Field[] fields= obj.getClass().getFields();
//通过for遍历每一个成员
for(Field field : fields){
//field.getType()判断该成员是否为String类型
if(field.getType()==String.class){
//获取旧值存入oldValue内
    String oldValue = (String)field.get(obj);
    //新字符串的a替换旧字符串内所有的b
String newValue = oldValue.replace('b', 'a');
//将对象内成员的值设定为新字符串
field.set(obj,newValue);
}
}
}
}
-----------------------------------------------------------------------------
22    成员方法的反射
Method类代表某个类中的一个成员方法
得到类中的某一个方法:
    例子:        Method charAt= Class.forName("java.lang.String").getMethod("charAt",int.class);
调用方法:
    通常方式:System.out.println(str.charAt(1));
    反射方式:System.out.println(charAt.invoke(str,1));
        如果传递给Method对象的invoke()方法的第一个参数为null,这有着什么样的意义呢?说明
        该Method对象对应的是一个静态方法!
jdk1.4和jdk1.5的invoke方法的区别:
    jdk1.5:public Object invoke(Object obj,Object...args);
    jdk1.4:public Object invoke(Object obj,Object[] args),即按jdk1.4的语法,需要将一个
    数组作为参数传递给invoke方法时,数组中的每个元素分别对应被调用方法中的一个参数
    所以,调用charAt方法的代码也可以用jdk1.4改写为char.invoke("str",new Object[]{1})形式。

String str1 = "abc";
Method methodCharAt = String.class.getMethod("charAt", int.class);
System.out.println(methodCharAt.invoke(str1, 1)); 
---------------------------------------
23    用反射方式执行某个类中的main方法
目标:
        写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。
        用普通方式调完后,大家要明白为什么要用反射方式去调啊?
问题:
        启动java程序的main方法的参数是一个字符串数组,即public static void main(String[]  args)
        通过反射方式来调用这个main方法时,如果为invoke方法传递参数呢?按jdk1.5的语法,整个数
        组是一个参数,而按jdk1.4的语法数组中的每个元素对应一个参数,当把一个字符串数组作为参
        数传递给invoke方法时javac会到底按照那种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,
        会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。
        所以在给main方法传递参数时,不能使用代码
        mainMthod.invoke(null.new String[]{"xxxx"}),
        javac只把他当做jdk1.4的语法进行理解,而不把它当做jdk1.5的语法解释,因此会出现参数类型不对的问题。
解决办法:
        mainMthod.invoke(null,new Object[]{new String[]{"xxx"}});
        mainMthod.invoke(null,(Object)new String[]{"xxx"});编译器会做特殊处理,编译时不把参数当做数组看待,
        也就不会讲数组打散成若干个参数了。

package cn.itcast.day1;
import java.lang.reflect.Method;
public class ReflectTest3 {
/**
 * @param args
 */
public static void main(String[] args) throws Exception{
//传递一个字符串给当前类运行,参数为,cn.itcast.day1.TestArguments
String startingClassName = args[0];
//用反射获取TestArguments类的main方法。
Method mainMethod =Class.forName(startingClassName).getMethod("main", String[].class);
//用反射原理调用这个main方法;此处的有两种方式.
//方式一:数组的父类就是Object[]。此处将String数组变成Object一个数组元素。
mainMethod.invoke(null,new Object[]{new String[]{"111","222","333"}});
//方式二:此处通过强转的方式,告诉编译器当前数组就是一个Object数组,让编译器把数组当做是一个整体处理。
//mainMethod.invoke(null,(Object)new String[]{"111","222","333"});
}
}
//创建一个有main函数的类。
class TestArguments{
public static void main(String[] args){
for(String arg : args){
System.out.println("arg"+arg);
}
}

}
--------------------------------------------------------
24         数组与Object的关系及反射类型

  数组与Object的关系及反射类型

|-具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
|-代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
|-基本类型的一维数组可以被当做Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以
    当作Object类型使用,又可以当作Object[]类型使用。
Arrays.asList()方法处理int[]和String[]时的差异:
  1. int[] a1 = new int[]{1,2,3};
  2. String[] a4 = new String[]{"a","b","c"};
  3. System.out.println(Arrays.asList(a1));//输出 [[I@ca0b6]
  4. System.out.println(Arrays.asList(a4));//输出[a,b,c]

复制代码
1.4版本 asList(Object[] obj) ,1.5版本 asList(T...a),因为int[]不可以转换成Object[],所以是1.5版本的方法,接收的是对象,打印哈希码,
而String[]可以被当做Object[]类型,所以是1.4版本方法

package cn.itcast.day1;

import java.lang.reflect.Array;
import java.util.Arrays;

public class ReflectTest24 {

/**数组
 * @param args
 */
public static void main(String[] args) {
// 数组与Object的关系及其反射类型
int[] a1 = new int[]{'1','2'};
int[] a2 = new int[4];
int[][] a3 = new int[2][3];
String[] a4 = new String[]{"1","3","b"};
System.out.println(a1.getClass()== a2.getClass());
//System.out.println(a1.getClass()== a3.getClass());
//System.out.println(a1.getClass()== a2.getClass());
System.out.println(a1.getClass().getName());
System.out.println(a1.getClass().getSuperclass().getName());
System.out.println(a4.getClass().getSuperclass().getName());
System.out.println(Arrays.asList(a1));
System.out.println(Arrays.asList(a4));
Object aObj1 = a1;//父类都是 Object。
Object aObj2 = a4;
//Object[] aObj3 =a1;//编译无法通过,因为a1是int的基本数据类型他不是一个object。
Object[] aObj4 = a3;
Object[] aObj5 = a4;
/* for(int x=0;x<Array.getLength(a4)-1;x++){
System.out.println((Array.get(a1,x)));
}*/
printObject(a4);
printObject(a1);
}
//打印数组元素。
public static void printObject(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);
}
}

}

------------------------------------------------------------
25         数组的反射的应用
具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。

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

基本类型的一维数组可以被当做Object类型使用,不能当做Object[]类型使用;非基本类型的
一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。

Arrays.asList()方法处理int[]和String[]时的差异。int显示的哈希值,string可以打印里面的元素。
Array工具类用于完成对数组的反射操作。
思考题:怎么得到数组中的元素类型?
//用反射的方式,打印数组元素。
public static void printObject(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);
}
}
26    ArrayList_Hashset的比较及Hashcode分析
提示:
1,通常来说,一个类的两个实例对象用equals()方法比较的结果相等时,它们的哈希码必须相等,
    但反之则不成立,即equals方法比较结果不相等的对象可以有相同的哈希码,或者哈希码相同的
    两个对象的equals方法比较的结果可以不等,例如字符串“bb”和“Aa“的equals方法比较结果
    肯定不相等,但它们的hashCode方法返回值却相等。
2,当一个对象呗存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,
    否则,对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了。这种情况下,即使在
    contains方法使用该对象的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的结果,
    这也会导致无法从HashSet集合中单独删除当前对象,从而造成内存泄露。
-----------------------------------------------------------------------------
27  框架的概念及用反射技术开发框架的原理
框架与框架要解决的核心问题
    我做房子卖给用户住,由用户自己安装门窗和空调,我做的房子就是框架,用户
    需要使用我的框架,把门窗插入进我提供的框架中。框架与工具类有区别,工具
    类被用户的类调用,而框架则是调用用户提供的类。
框架要解决的核心问题
    我在写框架(房子)时,你这个用户可能还在上小学,还不会写程序呢?我写的框架程序怎么能调用你以后写
    类(门窗)呢?
    因为在写程序时无法知道要被调用的类名,所以在程序中无法直接new 某个类的实例对象了,
    而要用反射方式来做。

综合案例
1    先直接用new语句创建ArrayList和HashSet的实例对象,演示用eclipse自动生成
        ReflectPoint类的equals和hashcode方法,比较两个集合的运行结果差异。  
2    然后改为采用配置文件加反射的方式创建ArrayList和HashCode的实例对象,
     比较观察运行结果差异
3    引入了eclipse对资源文件的管理方式的讲解。

//只需通过更改配置文件就可以更改,创建的集合比如:ArrayList和HashSet
public static void main(String[] args) throws Exception {
//创建流读取配置文件信息
InputStream ips = new FileInputStream("config.properties");
//创建properties类。
Properties props = new Properties();
//将文件读取到props里
props.load(ips);
//获取className对应的java.util.ArrayList
String className = props.getProperty("className");
//利用反射原理,建立集合类
Collection collections = (Collection)Class.forName(className).newInstance();
//Collection collections = new HashSet();
ReflectPoint rp1 =new ReflectPoint(3,3);
ReflectPoint rp2 =new ReflectPoint(5,5);
ReflectPoint rp3 =new ReflectPoint(3,3);
collections.add(rp1);
collections.add(rp2);
collections.add(rp3);
collections.add(rp1);
System.out.println(collections.size());
}
28    用类加载器的方式管理资源和配置文件
文件夹位置D:\MyEclipse 8.5\javaenhance\src\cn\itcast\day1
配置文件如果放在.java目录下面,myeclipse会自动将他复制到class path下面就是编译过的.class文件夹下面。
开发当中配置文件都放在class path下面。
//通过类加载器ArrayListHashsetDemo.class. getClassLoader()方法获取配置文件的信息。cn前面如果添加/那就是代表根目录javaenhance。
方式1:
     InputStream ips = ArrayListHashsetDemo.class.getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties");       
方式2:
    //a如果类和配置文件在同一个文件夹,相对路径是默认在一起的。
    //b如果用的是绝对路径就要写清楚当前的路径和配置文件所在文件夹。"/cn/itcast/day1/resources/config.properties"
    InputStream ips = ArrayListHashsetDemo.class.getResourceAsStream("config.properties");
不管是相对还是绝对他们内部都是调用getClassLoader()

总结:
Java程序在运行时,Java运行时系统一直对所有的对象进行所谓的运行时类型标识。这项信息纪录了每个对象所属的类。虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类。Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建。
      Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。 
      虚拟机为每种类型管理一个独一无二的Class对象。也就是说,每个类(型)都有一个Class对象。运行程序时,Java虚拟机(JVM)首先检查是否所要加载的类对应的Class对象是否已经加载。如果没有加载,JVM就会根据类名查找.class文件,并将其Class对象载入。
      基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也都对应一个 Class 对象。 
      每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。
      一般某个类的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() 
    返回该类的类加载器。 
    4、getComponentType() 
    返回表示数组组件类型的 Class。 
    5、getSuperclass() 
    返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class。 
    6、isArray() 
    判定此 Class 对象是否表示一个数组类。 
三、Class的一些使用技巧
    1、forName和newInstance结合起来使用,可以根据存储在字符串中的类名创建对象。例如
    Object obj = Class.forName(s).newInstance(); 
    2、虚拟机为每种类型管理一个独一无二的Class对象。因此可以使用==操作符来比较类对象。例如:
    if(e.getClass() == Employee.class)

 ------- Windows Phone 7手机开发.Net培训、期待与您交流! -------
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值