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

黑马程序员——反射

2015-06-29 21:16 633 查看
——- android培训java培训、期待与您交流! ———-

什么是反射

java反射机制就是在运行状态中,对于任意一个类(class文件),都能够知道这个类的所有属性和方法

对于任意一个对象,都能够调用它任意一个方法和属性。

这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制

简单的讲:反射就是动态获取类中信息。可以理解为对类的解剖

如何得到各个字节码对应的实例对象(class类型)

1,类名.class; 例如,System.class

2,对象.getClass(); 例如,new Date.getClass()

3,Class.forName(“类名”); 例如,Class.forName(“java.util.Date”);

基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象。

总之:只要在源程序中出现的类型,都有各自的Class实例对象,例如:int[], void ….

反射简单的讲,就是把java类中的各种成分映射成相应的java类

使用反射获取一个类中的构造方法

import java.io.*;
import java.lang.reflect.Constructor;
import java.net.*;

public class Demo {
public static void main(String[] args) throws Exception {

//使用反射来获取String str = new String(new StringBuffer("abc"));
Constructor constructor1 = String.class.getConstructor(StringBuffer.class);//此方法是获取String的构造方法,并将该构造方法封装成一个对象Constructor,

//StringBuffer.class代表要获取哪个构造方法

String str1 = (String)constructor1.newInstance(new StringBuffer("abc"));//此方法相当于用构造方法创建了一个String类型的对象,参数是StringBuffer("abc");
//由于在编译时,编译器只知道它是一个构造方法对象,并不知道它是什么类型的构造方法对象,所以需要加上强制转换。
//编译器编译时,只看左边,既:只看constructor,只知道它是一个构造方法对象而以,并不知道是什么类型

//String str2 = (String)constructor1.newInstance("abc");//此方法是错误的。因为构造coustructor1是能接收StringBuffer参数的构造方法,不是接收String类型的构造方法。

System.out.println(str1.charAt(2));//c

}
}


使用反射获取一个类中的变量

import java.io.*;
import java.lang.reflect.Field;
import java.net.*;

class Person{
private int x;
public int y;
public Person(int x, int y) {
super();
this.x = x;
this.y = y;
}
}
public class Demo {
public static void main(String[] args) throws Exception {
Person p = new Person(3,5);

Field fieldY = p.getClass().getField("y");//fieldY的值是多少?是5,错!fieldY只是封装了某个基本类型的对象而已,想要知道该基本类型的值,需要用对象中的方法get获取
System.out.println(fieldY.get(p));// 5 获取p这个对象中的y的值

//      Field fieldX = p.getClass().getField("x");//此处会报错,因为Person类中的x是私有的,不可获取
//      System.out.println(fieldX.get(p));

//      Field fieldX =p.getClass().getDeclaredField("x");//能够获取到类中私有变量的方法
//      System.out.println(fieldX.get(p));//此处会报错。虽然fieldX获取到了私有的变量,但是fieldX.get(p)不可以获取到

Field fieldX = p.getClass().getDeclaredField("x");
fieldX.setAccessible(true);//设置fieldX中的get方法能够获取得到Person类中的私有变量
System.out.println(fieldX.get(p));//3

}
}


使用反射获取一个类中的方法

import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.*;

public class Demo {
public static void main(String[] args) throws Exception {

String str1 = "abcdefgh";

Method methodCharAt = String.class.getMethod("charAt", int.class);
System.out.println(methodCharAt.invoke(str1, 1));//b    获取str1这个字符串中,角标为1的字符
System.out.println(methodCharAt.invoke(str1, new Object[]{2}));//c  这一种接收参数的形式也可以,这是在jdk1.4版本的时候用法,因为当时没有可变参数

Method methodIndexOf = String.class.getMethod("indexOf", int.class,int.class);
System.out.println(methodIndexOf.invoke(str1, 'c', 0));//2  获取str1这个字符串中,字符'c'出现的角标

Method methodValueOf = String.class.getMethod("valueOf",boolean.class);
System.out.println(methodValueOf.invoke(null, true));//true     将boolean类型的true,变成字符串"true"
//invock第一个参数为null,代表valueOf这个方法是静态的,因为静态方法不需要对象调用,所以参数为null
}
}


数组反射

/*
数组反射:Array,该类中的方法全是静态的
该类提供了动态创建和访问 Java 数组的方法。
*/
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.Arrays;
public class Demo {
public static void main(String[] args) throws Exception {
String[] strs = new String[]{"abc","aaa","kkk"};

Object obj = strs;
method(obj);
method("123");
}

public static void method(Object obj) {
if(obj.getClass().isArray()){//判读obj是否为一个数组

int len = Array.getLength(obj);//通过反射的Array方法,获取obj对象中数组的长度

for(int i=0;i<len;i++){
//返回指定数组对象中索引组件的值。
String str = (String) Array.get(obj, i);
System.out.println(str);
}

}
else{
System.out.println(obj);
}
}
}


反射的练习

/*
需求:
用反射的方法,将Person对象中的字符串的'b'字符全部改为'a',
如:"aabbcc"修改后的值为"aaaacc"。
*/
import java.io.*;
import java.lang.reflect.Field;
import java.net.*;

class Person {
public int a = 2;
public int b = 5;
public String str1 = "aabbcc";
public String str2 = "abcabbcc";
public String str3 = "qwert";

@Override
// 此处的意思代表,如果toString写错了,编译时会出现提示,因为自己并不能保证覆盖父类的方法写对了
public String toString() {
return str1 + "..." + str2 + "..." + str3;
}
}

public class Demo {
public static void main(String[] args) throws Exception {
Person p = new Person();

System.out.println(p);//aabbcc...abcabbcc...qwert

Field[] fields = p.getClass().getFields();// 获取对象中所有的公共变量,并存放到Field类中
for (Field field : fields) {//遍历fields对象中的所有public类型

//if(field.getType().equals(String.class))
//字节码都是一个份的,干嘛用equals去比较
if (field.getType() == String.class) {//通过getType方法,获取Person中变量的类型,如果获取到的类型是String类型,那么就执行以下代码

String str1 = (String) field.get(p);//通过Field类中的get方法,获取Person类中的字符串
String str2 = str1.replace('b', 'a');//将Person类中的字符串中的'b'修改为'a'

field.set(p, str2);//将修改后的字符串再写入Person类中
}
}
System.out.println(p);//aaaacc...aacaaacc...qwert
}
}


/*
需求:用反射方式执行某个类中的main方法
*/
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.*;

public class Demo {
public static void main(String[] args) throws Exception {
// 普通的调用方式
// String[] strs = new String[]{"111","222","333"};
// Demo2.main(strs);

// 反射的调用方式
String[] strs = new String[] { "111", "222", "333" };
Method methodMain = Class.forName("Demo2").getMethod("main", String[].class);
//methodMain.invoke(null, strs);//此处报异常,因为传递strs数组进来时,invoke自动将数组拆成了3个参数,那么传递给Demo2类中main方法的
//参数就不是一个数组了,而已3个String类型的参数,所以会出现异常

methodMain.invoke(null, new Object[]{strs});//此方法是正确的,因为invoke将Object数组拆成了1个参数,而此参数却是一个数组,所以,
//将此数传递给Demo2类中的main方法是可行的

methodMain.invoke(null, (Object)strs);//此方法正确,通过反射的形式调用了Demo2类中的main方法,
//invoke中第一个参数为null的原因是,main方法为静态的,不需要对象去调用。而第二个参数是(Object)strs是因为,
//告诉invoke方法,此方法不是一个数组,是一个Object类型的对象,这样invoke方法就不会将strs数组拆成3个String类型

}
}

class Demo2 {
public static void main(String[] args) throws Exception {
for (String arg : args)
System.out.println(arg);
}
}


/*
练习:将集合的名称以键值的形式存入一个配置文件中,
通过键获取集合的名称,并在该集合中存入数据,并打印集合的长度
*/
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.util.*;

class Person {
private int x, y;

public Person(int x, int y) {
this.x = x;
this.y = y;
}

@Override
public int hashCode() {
return x + y * 32;
}

@Override
public boolean equals(Object obj) {
if (!(obj instanceof Person))
throw new RuntimeException();
Person p = (Person) obj;
return x == p.x && y == p.y;
}

}

public class Demo {
public static void main(String[] args) throws Exception {
InputStream is = new FileInputStream("c:\\properties.txt");
Properties prop = new Properties();
prop.load(is);
is.close();

Person p1 = new Person(3,3);
Person p2 = new Person(5,5);
Person p3 = new Person(3,3);

String className = prop.getProperty("className");//获取键为className的值

Constructor con = Class.forName(className).getConstructor();//获取className这个字符串的构造方法
Collection coll = (Collection) con.newInstance();//调用这个字符串的构造方法
coll.add(p1);
coll.add(p2);
coll.add(p3);
coll.add(p1);
System.out.println(coll.size());//4

/*
此程序的使用方法:

在c盘中创建一个properties.txt文件。
在文件中写   className=java.util.ArrayList
这样程序的运行结果为4

如果在文件中写 className=java.util.HashSet
那么程序的运行结果为2

*/
}
}


package cn.itcast.reflect;

import java.io.*;
import java.util.*;

/*
* 练习:电脑运行基于主板。
*
* 如:有一个声卡,声卡想要运行,就必须要通过主板调用。
*    后期又来了个网卡,网卡想要运行,也必须通过主板调用。
*/
interface PCI {
public abstract void open();

public abstract void close();
}

class ZhuBan {//主板
public void run() {
System.out.println("ZhuBan...run");
}

public void pciInvoke(PCI pci) {
pci.open();
pci.close();
}
}

class WangKa implements PCI {//网卡实现PCI接口
public void open() {
System.out.println("WangKa....open");
}

public void close() {
System.out.println("WangKa....close");
}
}

class ShengKa implements PCI {//声卡实现PCI接口
public void open() {
System.out.println("ShengKa....open");
}

public void close() {
System.out.println("ShengKa....close");
}
}

public class Demo {

public static void main(String[] args) throws Exception {
File file = new File("D:\\peizhi.txt");
if (!file.exists()) {
throw new RuntimeException("文件不存在");
}

ZhuBan zb = new ZhuBan();
zb.run();

Properties prop = new Properties();//创建一个Properties对象
FileReader fr = new FileReader(file);//创建一个流和peizhi.txt文件相关联
prop.load(fr);//将peizhi.txt文件中的键值存入prop集合中

//循环遍历peizhi.txt文件中的键值
for (int i = 0; i < prop.size(); i++) {
//获取peizhi.txt文件中的值,该值是一个对象的名称
String className = prop.getProperty("peizhi" + (i + 1));

//获取peizhi.txt文件中名称的字节码文件对象
Class clazz = Class.forName(className);
//创建获取到的名称  的对象。例如:获取到的名称是WangKa。那么相当于PCI pci = new WangKa();
PCI pci = (PCI) clazz.newInstance();
//调用主板中的方法
zb.pciInvoke(pci);
}
}
}
/*
* 此程序用法:在D盘中创建一个peizhi.txt文件。并在文件中写入一下数据
*      peizhi1=cn.itcast.reflect.WangKa
*      peizhi2=cn.itcast.reflect.ShengKa
*
* 此程序这么设计的扩展性极强。以后如果想加入一个内存卡,那么就写一个内存卡的类,并实现PCI接口,
* 当类写完了以后,再将类名存入peizhi.txt文件中。 当程序运行时,就会自动获取peizhi.txt文件中的信息。
*/


总结

/*
反射方法的总结。
看完该例子就会找到反射的使用规律

例如:
1,获取某类的构造函数,想都不用想,直接就上,Constructor constructor = 字节码文件对象.getConstructor();
这样就拿到了构造方法的Constructor对象。相对此构造方法怎么宰割,查API找方法去

2,获取某类的方法,想都不用想,直接就上,Method method = 字节码文件对象.getMethod("方法名",参数类型.class);
这样也拿到了方法的Method。想对此方法怎么宰割,继续查API去

3,如果方法是私有时,Method method = 字节码文件对象.getDeclaredMethod("方法名",参数类型.class);
然后再加上
method.setAccessible(true);
那么此私有的方法也获取到了,爱怎么使用再查API去。
*/
import java.io.*;
import java.lang.reflect.*;
import java.net.*;
import java.util.*;

class Person {
public int x = 3;

private int y = 5;

public Person() {
System.out.println("person()....run");
}

public Person(int x, int y) {
System.out.println("Person(int x,int y).....run");
}

public void method() {
System.out.println("method()......run");
}

public void method(String str, int num) {
System.out.println("method(String str,int num)....run");
}

public static void staticMethod() {
System.out.println("staticMethod().....run");
}

private void privateMethod() {
System.out.println("privateMethod.....run");
}
}

public class Demo {
public static void main(String[] args) throws Exception {
// 获取无参构造函数
Constructor constructor1 = Class.forName("Person").getConstructor();
Person p = (Person) constructor1.newInstance();// person()....run

// 获取有参构造函数
Constructor constructor2 = Class.forName("Person").getConstructor(
int.class, int.class);
constructor2.newInstance(5, 6);// Person(int x,int y).....run

// 获取 公有 的字段
Field field1 = Class.forName("Person").getField("x");
int x = (Integer) field1.get(p);
System.out.println(x);// 3

// 获取 私有 的字段 ,getDeclaredField代表能够获取Person类中私有的字段
Field field2 = Class.forName("Person").getDeclaredField("y");
field2.setAccessible(true);// 想要获取就必须加上这一句
int y = (Integer) field2.get(p);
System.out.println(y);// 5

// 获取无参方法
Method method1 = p.getClass().getMethod("method", null);
method1.invoke(p, null);// method()......run

// 获取有参方法
Method method2 = p.getClass().getMethod("method", String.class,
int.class);
method2.invoke(p, "abc", 2);// method(String str,int num)....run

// 获取无参的静态方法
Method method3 = p.getClass().getMethod("staticMethod", null);
method3.invoke(p, null);// staticMethod().....run

// 获取 私有 的方法 ,getDeclaredMethod代表能够获取Person类中的私有方法
Method method4 = p.getClass().getDeclaredMethod("privateMethod", null);
method4.setAccessible(true);// 如果要获取私有的,就必须加上这一句
method4.invoke(p, null);// privateMethod.....run

}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: