1、反射的概念。
是Java语言提供的一种机制,并不是所有语言都具备;反射可以实现在.java文件编译时,可以不明确类的具体类型,而在.class文件运行再去检查和明确这个类。
2、反射的原理。
- 首先要了解一下java程序的运行步骤,源(.java)文件经过javac.exe编译后生成二进制(.class)文件,编译主要工作就是语法检查,然后通过java.exe去执行二进制文件,这时jvm会通过类加载器把二进制文件装入方法区,同时在堆中会创建一个描述这个类的对象,类型为java.lang.Class(注意是大写)。
- 这个类描述的内容无非是装载类的基本属性:
class Class{
名称(类名)
字段(成员变量)
构造函数
一般函数(成员函数)
}
通过这个类可以获取到字节码文件的所有信息,而反射的就是依赖的这个字节码文件对象。
- 也许你有疑问,获取这些信息能干什么呢?
1. 有了构造函数,你可以创建这个类的新的实例(对象)。
2. 可以返回字段值。
3. 执行一般函数等。
所有的工作都可以做,跟你拿到这个类的源代码是同样的效果。下面的工作就是怎么获取一个类的二进制文件的对象。
3、获取Class对象的三种方式
1、 通过Object类提供的getClass
()
方法。2、 通过所有类都具备的静态属性.class。
packageitcast.reflect.demo;
publicclass ReflectDemo {
public static void main(String[] args)throws Exception{
//getClassObject_1()
//getClassObject_2()
getClassObject_3();
}
/*
*1、Object类的getClass()方法。必须要明确具体的类,并创建对象
*/
public static void getClassObject_1(){
Person p = new Person();
Class clazz = p.getClass();
Person p1 = new Person();
Class clazz1 = p1.getClass();
System.out.println(clazz ==clazz1);
}
/*
*2、任何数据类型都具备一个静态属性.class来获取起对应的class对象
*/
public static void getClassObject_2(){
Class clazz = Person.class;
Class clazz1 = Person.class;
System.out.println(clazz ==clazz1);
}
/*
*3、通过java.lang.Class类的forName,根据类名的字符串形式,获取该类的二进制文件对象,这种方式只需名称即可,扩展性更强。
*/
public static void getClassObject_3()throws Exception{
String name ="itcast.reflect.demo.Person";
Class clazz =Class.forName(name);
System.out.println(clazz);
}
}
packageitcast.reflect.demo;
publicclass Person {
private int age;
private String name;
public Person(String name,int age){
super();
this.age = age;
this.name = name;
System.out.println("Personparam run..."+this.name+":"+this.age);
}
public Person(){
super();
System.out.println("personrun");
}
public void show(){
System.out.println(name+"...showrun..."+age);
}
private void privateMethod(){
System.out.println("privateMethodrun");
}
public void paraMethod(String str,intnum){
System.out.println("paraMethodrun...."+str+":"+num);
}
public static void staticMethod(){
System.out.println("staticmethod run....");
}
}
4、获取构造函数(创建实例)
1、 如果是构建一个无参的实例,通过Class调用newInstance
()
方法。2、 如果是构建一个有参数的实例,通过Class的getConstructor
(
Class<?>... parameterTypes)
方法,根据传入的参数列表的类型去返回对象的构造函数对象,然后再调用newInstance(Object... initargs)方法创建实例。
packageitcast.reflect.demo;
importjava.lang.reflect.*;
publicclass ReflectDemo2 {
public static void main(String[] args)throws Exception {
createNewObject_2();
}
public static void createNewObject()throws Exception{
//早期:new时候,先根据被new的名称找寻该类的字节码文件,并加载进内存,
//并创建该自己吗文件对象,并接着创建该字节文件的对应的Person对象
//itcast.bean.Person p = newcn.itcast.bean.Person();
//现在:
String name ="itcast.reflect.demo.Person";
//找寻该名称类文件,并加载进内存,并产生Class对象
Class clazz =Class.forName(name);
//如何产生该类的对象呢
Object obj =clazz.newInstance();
}
public static void createNewObject_2()throws Exception{
/*
* 当获取指定名称对应类中的所体现的对象是
* 而该对象初始化不适用空参数构造该怎么办呢
* 既然是通过指定的构造函数进行初始化,所以应该先获取该构造函数。
* 通过字节码文件对象就可以获得,指定的:getConstructor(Class<?>...parameterTypes) 全部:getConstructors() 全部(包含私有):getDeclareConstrunctors()
*
*/
String name ="itcast.reflect.demo.Person";
Class<?> clazz =Class.forName(name);
//获取到了指定的构造函数对象
Constructor constructor =clazz.getConstructor(String.class,int.class);
//通过该构造器对象的newInstance方法进行对象的初始化
Object obj =constructor.newInstance("小强",23);
}
}
5、返回Class中字段
- 公有的字段,通过Class的getField
(
Stringname)
,根据字段名去获取。
私有的,通过Class的
getDeclaredField(
Stringname)
获取,又叫暴力获取。
- 以上返回Field对象,其中该对象的set
(
Objectobj,
Objectvalue)
方法可以修改对象中值。
packageitcast.reflect.demo;
importjava.lang.reflect.*;
publicclass ReflectDemo3 {
public static void main(String[] args)throws Exception {
getFieldDemo();
}
public static void getFieldDemo()throws Exception {
Class<?> clazz =Class.forName("itcast.reflect.demo.Person");
//私有成员变量无法获取
//Field field=clazz.getField("age");
//暴力获取私有成员变量
Field field =clazz.getDeclaredField("age");
//获取指定参数的构造函数
Constructor constructor =clazz.getConstructor(String.class,int.class);
//调用构造的函数传入参数,创建实例
Object obj = constructor.newInstance("小强",23);
//get(Object obj)返回指定对象上Field表示字段的值
//私有成员不能获取,通过SetAccessible()设置
field.setAccessible(true);
field.set(obj,89);
Object value =field.get(obj);
System.out.println(value);
}
}
6、 获取Class中方法
- 通过Class的getMethods
()
获取所有的公共方法,包括父类的。 - 通过Class的getDeclaredMethods
()
获取本类的方法,包括私有。
- 通过Class的getDeclaredMethod
(
Stringname,
Class<?>... parameterTypes)
获取单个方法,name表示函数名,parameterTypes表示参数。 - 以后返回Method对象,通过invoke
(
Objectobj,
Object... args)
,传入对象和参数值,执行该方法。
package itcast.reflect.demo;
import java.lang.reflect.*;
public class ReflectDemo4 {
public static void main(String[] args) throws Exception {
getMethodDemo_2();
}
/*
* 获取指定Class中的公共函数
*/
public static void getMethodDemo() throws Exception {
Class clazz =Class.forName("itcast.reflect.demo.Person");
Method[] methods = clazz.getMethods();//获取的都是公有的方法
methods = clazz.getDeclaredMethods();//获取本类的所有方法
for(Method method : methods){
System.out.println(method);
}
}
public static void getMethodDemo_2() throws Exception {
Class<?> clazz = Class.forName("itcast.reflect.demo.Person");
Method method =clazz.getMethod("staticMethod",null);
//Constructor constructor =clazz.getConstructor(String.class,int.class);
//Object obj =constructor.newInstance("小强",23);
method.invoke(null,null);
}
}
7、 反射的应用
通过读取配置文件,获取加载的类文件对象,然后动态执行程序,编码程序的修改。
packageitcast.reflect.test;
importjava.lang.reflect.*;
importjava.io.*;
importjava.util.*;
publicclass ReflectTest {
public static void main(String[] args)throws Exception {
Mainboard mb = newMainboard();
mb.run();
//每次添加一个设备都需要修改代码传递一个新创建的对象
//mb.usePCI(new SoundCard());
//能不能不修改代码就可以完成这个动作
//不用new来完成,而是值获取起class文件。
File configFile = newFile("pci.properties");
Properties prop = newProperties();
FileInputStream fis = newFileInputStream(configFile);
prop.load(fis);
for(intx=0;x<prop.size();x++){
String pciName =prop.getProperty("pci"+(x+1));
Class clazz =Class.forName(pciName);
Object obj =clazz.newInstance();
PCI p =(PCI)clazz.newInstance();
mb.usePCI(p);
}
fis.close();
}
}
packageitcast.reflect.test;
//importitcast.reflect.test.PCI;
publicclass Mainboard {
public void run() {
System.out.println("mainborad run...");
}
/*public void useSound(SoundCard c){
c.open();
c.close();
}*/
public void usePCI(PCI p){
if(p!=null){
p.open();
p.close();
}
}
}
packageitcast.reflect.test;
publicinterface PCI {
public void open();
public void close();
}
packageitcast.reflect.test;
publicclass SoundCard implements PCI {
public void open() {
System.out.println("soundopen");
}
public void close() {
System.out.println("soundclose");
}
}
packageitcast.reflect.test;
publicclass NetCard implements PCI {
public void open() {
System.out.println("netopen");
}
public void close() {
System.out.println("netclose");
}
}
文件名:pci.properties:
pci1=itcast.reflect.test.SoundCard
pci2=itcast.reflect.test.NetCard