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

黑马程序员——Java之反射

2015-04-13 01:03 344 查看
-------------------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ---------------------

一、反射

1、概念

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

          总之反射就是把Java类中的各个成分映射成相应的类。例如一个Java类中用一个class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱也是一个个的类。表示Java类的class类显然也要提供一系统的方法,来获取其中的变量,方法,构造方法,修饰符,包等信息。这些信息时就是用相应类的实例对象来表示,包括Field、Method、Contructor、Package等。(反射从1.2版本就存在)

2、反射的基石Class类

(1)概述

Java程序中的各个Java类属于同一类事物,描述这类事物的Java类名就是Class,它是经过类的不同属性向上不断抽取出来类。

Class类描述的信息有:类名、类访问属性、包名、字段,方法等。每一个字节码就是class的实例对象。

class的实例对象是什么呢?

Class对应各个类在内存中字节码,一个类被类加载器加载到内存中,占用一片存储空间,这个空间里的内容就是字节码,不同类的字节码是不同的,这一个个的空间可分别用一个个的对象来表示,这些对象显然具有相同类型这个类型就是Class类型。

 

注意:字节码就是当源程序中用到类时,jvm虚拟机会把Java程序编译成class字节码文件,再把字节码文件加载到内存中,再根据字节码在内存中创建对象。

 

(2)获得class的三种方法:

方式一:

类名.class

例如System.class,(返回类的字节码,虚拟机已经加载该类)

 

方式二:

对象.getClass()

例如:newPerson().getClass() (根据对象返回该对象所对应类的字节码,虚拟机已经加载该类)

 

方式三:

Class.forName("类名")

例如:Class.forName("java.util.Date")(类没有被加载到虚拟机,最常用的方法里面可以是变量,从文件中获取变量的值 )

(3)Java预定义对象

Java有九种预定义的 Class 对象,表示八个基本类型和void。这些类对象由 Java 虚拟机创建,与其表示的基本类型同名,即 boolean、byte、char、short、int、long、float 和 double。

注意:

TYPE是Integer类的一个常量,它代表此包装类型包装的基本类型的字节码,所以和int.class是相等的。基本数据类型的字节码都可以用与之对应的包装类中的TYPE常量表示

 

(4)Class常用方法

判断:

public boolean isPrimitive() 判定指定的 Class 对象是否表示一个基本类型。

public boolean isArray() 判定此 Class 对象是否表示一个数组类。

public boolean isInterface()判定指定的 Class 对象是否表示一个接口类型。

 

获取:

public static Class<?> forName(StringclassName)

返回与带有给定字符串名的类或接口相关联的Class 对象

public final
Class<?> getClass()

返回Class对象即字节码对象

 

public
Constructor<?>[]getConstructors()

返回Class对象的所有构造方法。

public Constructor<T>getConstructor(Class<?>... parameterTypes)

返回指定的构造方法。

 


public Field[] getFields()

返回包含某些Field对象的数组,表示所代表类中的成员字段。

public Field getField(String name)

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

      

public Method getMethod(Stringname,Class<?>... parameterTypes)

返回一个Method对象,表示的是此Class对象所代表的类的指定公共成员方法。

public Method[] getMethods()

返回一个包含某些Method对象的数组,是所代表的的类中的公共成员方法。

 

public String
getName()以String形式返回此Class对象所表示的实体名称。

public Class<? super
T> getSuperclass()返回此Class所表示的类的超类的名称

 

创建:

public T newInstance()

创建此Class对象所表示的类的一个新实例。

 

3、Constructor类

(1)概述

Constructor类是Java所有类中的构造方法抽取而形成的类,这个类用来封装反射得到的构造器

(2)获得Constructor四种方法

public Constructor<?>[]getConstructors()

返回类中所有的public构造器集合,默认构造器的下标为0

public Constructor<T>getConstructor(Class<?>... parameterTypes)

返回指定public构造器,参数为构造器参数类型集合

public Constructor<?>[]getDeclaredConstructors() 

返回类中所有的构造器,包括私有

public Constructor<T>getDeclaredConstructor(Class<?>... parameterTypes)

返回任意指定的构造器

从名字来看,还是很好懂的,带上Declared的都是获得所有的构造方法,包括私有。

(3)创建实例对象

通常方式:String str=new String(newStringBuffer("abc"))

反射方式:Stringstr=(String)Constructor.newInstance(new StringBuffer("abc"));

 

Class为什么也有newInstance()方法?

例如:Stringstr=(String)Class.forName("java.lang.String").newInstance();

原理:该方法的内部是先得到默认的构造方法,用缓存机制来保存得到的构造方法的实例对象,所以可以用该方法创建无参的实例对象。

public class ClassDemo
{
publicstatic void main(String[] args){
Personperson=new Person("张三",30);
try{
Constructorcon=person.getClass().getConstructor(String.class,int.class);//获取Person中参数为String和int的构造方法.
Personp=(Person)con.newInstance("六六",88);//使用该构造方法,创建Person实例,
System.out.println(p);
}
catch(Exceptione){
e.printStackTrace();
}
}
}


4、Field类

(1)概念

Field类是Java所有类中的变量抽取而形成的类,这个类用来封装反射得到的成员变量。

(2)获得Field类的四种方法

public Field getDeclaredField(Stringname)  获取任意指定名字的成员

public Field[] getDeclaredFields()          获取所有的成员变量

public Field getField(String name)         获取任意public成员变量

public Field[] getFields()                 获取所有的public成员变量

 

(3)暴力反射

可以看出和Constructor的方法一样,带Declared可以获取到私有变量。可以获取到但是无法看到其中的内容是什么,要想看到需要暴力反射

public void setAccessible(boolean flag)

该方法表示如果是私有字段,要先将该私有字段进行取消权限检查的能力。也称暴力访问。 

暴力反射代码示例:

public class Person
{
private String name;
public int age;
public Person(String name,int age){
this.name=name;
this.age=age;
}
}


public class ClassDemo
{
public static void main(String[] args){
Person person=new Person("张三",30);
try{
Field[] field=person.getClass().getFields();//获取所有的成员变量

Field fieldN=person.getClass().getDeclaredField("name");//获取成员变量name,因为name是私有的所以需要
fieldN.setAccessible(true);//暴力反射

Field fieldA=person.getClass().getField("age");//获取成员编程age

System.out.println("name值:"+fieldN.get(person));//获取ps对象的name变量的值。
System.out.println("age值:"+fieldA.get(person));//获取ps对象的age变量的值。
}
catch(Exception e){
e.printStackTrace();
}
}
}


练习:将字符串中的字母b替换成a;

public class Person
{
public  String str1;
public String str2;
public String str3;
public Person(String str1,String str2,String str3){
this.str1=str1;
this.str2=str2;
this.str3=str3;
}
public String toString(){
return "str1值:"+str1+"str2值:"+str2+"str3值:"+str3;
}
}


public class ClassDemo
{
public static void changValue(Object obj){
try{
Field[]fields=obj.getClass().getFields();//获取obj对象类中的可见成员变量。
for(Field field:fields){
if(field.getType() ==String.class){//getType为获取本身类型,因为字节码只有一份,所以用==号比较,也可以用equals比较。
StringoldValue=(String)field.get(obj);//如果field为string类型则获取此类型的值。
StringnewValue=oldValue.replace('b','a');//将值的b字符替换成a字符,替换成新的值,
field.set(obj,newValue);//将obj对象所表示的值,设置为改好后的值。
}
}
}
catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] args){
Person person=newPerson("abc","abcd","abcde");
changValue(person);
System.out.println(person);
}
}


5、Method类

(1)概念

Method类是Java所有类中的方法抽取而形成的类,这个类用来封装反射得到的方法。

(2)获得Method的四种方法

public Method[] getMethods()    获取所有的共有方法的集合

public Method getMethod(Stringname,Class<?>... parameterTypes)

获取指定公有方法 参数1:方法名 参数2:参数类型集合 

public Method[] getDeclaredMethods()  获取所有的方法

public Method getDeclaredMethod(Stringname,Class<?>... parameterTypes) 获取任意指定方法

 

(3)调用获得的方法

通常方式:System.out.println(str.charAt(2 ));

反射方式:System.out.println(charAt.invoke(str,2 ));

 

注意:如果invoke方法参数1为null; 则代表Method对象对应的是静态方法。jdk1.4和jdk1.5区别;

jdk1.4: public Object invoke(Object obj,Object[ ]args)参数2列表需要存入到数组中。

jdk1.5: public Object invoke(Object obj,Object... args)参数2列表引入了可变参数。

实现代码:

public class ClassDemo
{

public static void main(String[] args){
try{
String str="abcd";
Methodm=Class.forName("java.lang.String").getMethod("charAt",int.class);//获取String类中的charAt方法且方法中的形参为int类型。
System.out.println(m.invoke(str,1));//调用该方法,把1传值进去。
System.out.println(m.invoke(str,new Object[]{3}));//调用该方法
}
catch(Exception e){
e.printStackTrace();
}
}
}
 

(4)反射其他类main方法

        我们平常在写源程序时,并不知道使用者传入的类名是什么,但是虽然传入的类名不知道,而知道的是这个类中的方法有main这个方法。所以可以通过反射的方式,通过使用者传入的类名(可定义字符串型变量作为传入类名的入口,通过这个变量代表类名),内部通过传入的类名获取其main方法,然后执行相应的内容。

代码示例:

class TestArguments
{
public static void main(String[] args){
for(String s:args){
System.out.println(s);
}
}
}


public class ClassDemo
{
//练习:执行其他类中的main方法。
public static void main(String[] args){
try{
StringstrMainName=args[0];//args传值进来的就是其他类的名字。
Methodm=Class.forName(strMainName).getMethod("main",String[].class);//获取类的main方法,mian方法的参数是String数组。
m.invoke(null,new Object[]{newString[]{"111","222","333"}});
//第二种方法:m.invoke(null,(Object)newString[]{"111","222","333"});

/**参数1为null:因为main方法为静态方法。
参数2:传值进去一个数组,因为mian方法中只有一个数组参数,所以外面再包装成一个Object数组,
mian方法就会把)newString[]{"111","222","333"}当作一个参数,否则的话会把它当作三个参数。
*/
}
catch(Exception e){
e.printStackTrace();
}
}
}


6、数组反射

(1)规则

具有相同维数和元素的类型的数组属于同一类型数组。即具有相同Class对象。

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

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

(2)获得数组的元素

如何得到某个数组中的某个元素的类型,

  例: int a = new int[3];Object[] obj=new Object[]{”ABC”,1};

注意:无法得到某个数组的具体类型,只能得到其中某个元素的类型,

如:Obj[0].getClass().getName()得到的是java.lang.String。

 

Array工具类用于完成对数组的反射操作。

        Array.getLength(Objectobj);//获取数组的长度

        Array.get(Objectobj,int x);//获取数组中的元素

 

练习:数组反射

i

mport java.lang.reflect.*;
public class ClassDemo
{
public static void printArray(Object obj){
try{
Class cl=obj.getClass();//获取该类型的字节码。
if(cl.isArray()){//判断是否为数组类型字节码。
int len=Array.getLength(obj);//是数组则获取数组的长度
for(int x=0;x<len;x++){//循环遍历数组
System.out.println(Array.get(obj,x));//通过Array的get方法获取某个对象的数组坐标元素。
}
}
else{
System.out.println(obj);//不是数组则直接输出。
}
}
catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] args){
String[] str={"a","b","c"};
printArray(str);
printArray("ssss");
}
}


7、用反射实现框架的功能

(1)什么是框架

   房地产商造房子用户住,门窗和空调等等内部都是由用户自己安装,房子就是框架,用户需使用此框架,安好门窗等放入到房地产商提供的框架中。

注意:框架和工具类的区别:工具类被用户类调用,而框架是调用用户提供的类。

 

(2)框架要解决的核心问题:

   我们在写框架(造房子的过程)的时候,调用的类(安装的门窗等)还未出现,那么,框架无法知道要被调用的类名,所以在程序中无法直接new其某个类的实例对象,而要用反射来做。

 

 

 

-------------------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ---------------------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 编程语言