---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity开发</a>、<a href="http://www.itheima.com"target="blank">.Net培训</a>、期待与您交流! ----------------------
产生反射的背景
在实际开发中,一个软件做完以后。后期需要功能扩展,开发之初就会留有接口。新建一个类实现之前的接口覆盖相应的方法。需求是之前的程序软件如何用到后来的那个子类呢?如果在原来的程序上new相当于修改源代码,这是不提倡的。怎么办呢?
当初给软件的同时提供配置文件,后来更新的部分产生的子类放在配置文件中,原来的程序自动读取配置文件把子类加载进来进行对象的建立。这样做的好处:扩展性增强。后来的人只需要实现接口,配置配置文件
关键是原来的软件怎样拿到后来在配置文件中加入的子类并建立这个类的对象呢?这就需要一项技术——反射
图解:
子类的名字如Demo往配置文件一放,通过名字找Demo.class即通过过反射技术拿这类就可以了(拿到类就拿到了类中东西)反射技术中最重要的一点就是先获取那个类
有反射之前怎么做
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
//描述笔记本
public class NoteBook {
/**
* 运行功能。
*/
publicvoid run(){
System.out.println("notebookrun");
}
/**
* 使用usb设备。
*/
publicvoid useUSB(USB usb){
if(usb!=null){
usb.open();
usb.close();
}
}
}
//主方法
public class NoteBookMain {
/**
* @param args
* @throws Exception
*/
publicstatic void main(String[] args) throws Exception {
NoteBookbook = new NoteBook();
book.run();
book.useUSB(null);//刚开始没用usb设备
//因为有了鼠标。所在需要在源程序中,创建鼠标对象并传到给笔记本。
//希望后期出现了设备以后,可不可以不用修改NoteBookMain的代码。还可以不断的加入新设备。
//book.useUSB(newMouseByUSB());通过反射来解决问题。
//反射之后的做法
//对外提供配置文件。。
File configFile =new File("usb.properties");
if(!configFile.exists()){
configFile.createNewFile();
}
//读取流和配置文件关联。
FileInputStream fis= new FileInputStream(configFile);
Properties prop =new Properties();
//将流中的数据加载到prop。
prop.load(fis);
for(int x=1;x<=prop.size(); x++){
StringclassName = prop.getProperty("usb"+x);
/对指定的类进行加载。
Class clazz =Class.forName(className);
USB usb =(USB)clazz.newInstance();
book.useUSB(usb);
}
fis.close();
}
}
在usb.properties配置文件中编写
usb1=cn.itcast.reflect.test.MouseByUSB
usb2=cn.itcast.reflect.test.KeyByUSB
//留接口,以便后期功能扩展
package cn.itcast.reflect.test;
public interface USB {
/**
* 定义关闭。
*/
voidclose();
/**
* 定义开启。
*/
voidopen();
}
//接了个usb设备鼠标需要实现usb接口
public class MouseByUSB implements USB {
@Override
publicvoid close() {
System.out.println("mouseclose");
}
@Override
publicvoid open() {
System.out.println("mouseopen");
}
}
//接键盘
KeyByUSB.Java
packagecn.itcast.reflect.test;
public class KeyByUSB implements USB {
@Override
public void close() {
System.out.println("key close");
}
@Override
public void open() {
System.out.println("key open");
}
}
反射技术
ReflectDemo.java
packagecn.itcast.reflect.constructor;
importcn.itcast.domain.Person;
public class ReflectDemo {
/**
* @param args
* @throws ClassNotFoundException
*/
public static void main(String[] args) throws ClassNotFoundException {
/*
*反射技术:动态的获取类以及类中的成员,并可以调用该类成员。
*以前是有什么类,就new什么对象。现在是没有类,给什么类就new什么对象。
*
*无论new什么对象,都需要先获取字节码文件。
* 如何获取呢?发现 java 已对字节码文件进行了描述用的 Class 类完成的。
* 如何获取一个字节码文件的对象呢?
*Person类一加载进内存,就会生成一个Person.class对象再根据这个对象创建一个一个的Person对象
所以可根据Person对象获取自己所属的字节码文件对象,简单说可以通过孩子获取到孩子他妈
*方式一:Object getClass();方法。发现在反射技术里,该方法不合适。反射技术不明确具体类。而这方式根据new的对象拿class,意味着连它的构造函数都知道
*
*方式二:所有的数据类型都有自己对应的Class对象。表示方式很简单。
*每一个数据类型都有一个默认的静态的属性.class,用该属性就可以获取到字节码文件对象。
*虽然不用对象调用了,还是要用具体的类调用静态属性。仍然不合适
*
*
*方式三:在Class类中的找到了forName方法。通过名称就可以获取对应的字节码文件对象。重点。
*Class类本身是字节码文件的类,那么这个类有没有获取.class文件(Class的对象)的方法呢?
*/
methodDemo_3();
}
获取Class对象的方式三
public static void methodDemo_3() throws ClassNotFoundException {
String className = "cn.itcast.domain.Person";//非常要注意的地方,类名包含包名
Class clazz = Class.forName(className);//根据类名去classpath下找对应的类文件,找到之后自动加载person类进内存,并封装成了Class对象。如何再根据类创建对象呢?见ReflectDemo2.java
System.out.println(clazz);
}
//获取Class对象的方式二
public static void methodDemo_2() {
Class clazz = Person.class;
Class clazz2 = Person.class;
System.out.println(clazz == clazz2);
//结果为true
}
//获取Class对象的方式一。
public static void methodDemo_1(){
//调用getclass先有对象。
Person p1 = new Person();
Class clazz1 = p1.getClass();//完成了person所属字节码文件的获取
Person p2 = new Person();
Class clazz2 = p2.getClass();
System.out.println(clazz1 == clazz2);
//结果为true
}
}
//以上示例中需要的类
Person.java
public class Person {
private String name;
private int age;
public Person() {
super();
System.out.println("Person run");
}
public Person(Stringname, int age) {
super();
this.name = name;
this.age = age;
}
public void show(){
System.out.println("person show run");
}
public static void staticShow(){
System.out.println("person static show run");
}
public void paramShow(String name,int age){
System.out.println("show:"+name+"---"+age);
}
}
如何根据给定的名称获取到指定的Class对象后建立该类的对象呢?
ReflectDemo2.java
packagecn.itcast.reflect.constructor;
importjava.lang.reflect.Constructor;
import cn.itcast.domain.Person;
public class ReflectDemo2 {
/**
* @param args
* @throws Exception
* @throws InstantiationException
* @throws ClassNotFoundException
*/
public static void main(String[] args) throws ClassNotFoundException,InstantiationException, Exception {
/*
*演示如何根据给定名称获取到指定的Class对象后建立该类的对象呢?
*/
// getObject();
getObject2();
}
public static void getObject2() throws Exception {
String className = "cn.itcast.domain.Person";
Class clazz = Class.forName(className);
/*
*万一给定类中没有空参数的呢?
*可以先获取指定的构造函数。再通过该构造函数进行实例化。
*/
//1,通过Class获取指定构造函数。比如带两个参数。
Constructor cons = clazz.getConstructor(String.class,int.class);
//获得字节码中与之匹配类型构造器(String和int类型组成构造器),传的是类型对象,每个类型都有对应的Class描//述。这个地方定义的时候需要把String类加载进来
// System.out.println(cons);
//2,通过指定的构造器对象进行对象的初始化。
Object obj = cons.newInstance("lisisi",23);
// Person p = new Person("lisi",20);
}
public static void getObject() throws ClassNotFoundException,InstantiationException, IllegalAccessException {
//1,根据给定的类名获取Class对象。
String className = "cn.itcast.domain.Person";
Classclazz = Class.forName(className);//给我什么new什么
Objectobj = clazz.newInstance();//创建一个Person对象。默认调用该类的空参数构造函数。
// java.lang.InstantiationException:没有调用到与之对应的构造函数。
// 记住了,一般被反射的类通常都有空参数的构造函数。
// Person p = new Person();与该句等同。内存创建过程一样的 1加载Person类2开辟空间3调用构造函数,因为对象要初始化
}
}
动态获取字段&暴力访问
拿到.class对象之后,就可以拿到类的各个组成部分。反射技术也可称为对类的解剖
ReflcetFileDemo.java
packagecn.itcast.reflect.field;
importjava.lang.reflect.Field;
public class ReflectFieldDemo {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
/*
*获取类中的成员。
*反射字段。
*/
getFieldDemo();
}
public static void getFieldDemo() throws Exception{
String className = "cn.itcast.domain.Person";
Class clazz = Class.forName(className);
//获取指定age字段(配置文件中指定的)。
// Field field =clazz.getField("age");//该方法只获取公有的。
Field field = clazz.getDeclaredField("age");//获取所有类型的都行
//字段有修饰符 类型 也封装成一个对象
//要对非静态的字段操作必须有对象。
Object obj = clazz.newInstance();
//使用父类的方法将访问权限检查能力取消。
field.setAccessible(true);//暴力访问。
field.set(obj, 40);
System.out.println(field.get(obj));//从obj这个对象获取
// System.out.println(field);
// Person p = new Person();
// p.age = 30;
}
}
动态获取方法
packagecn.itcast.reflect.method;
importjava.lang.reflect.Method;
public class ReflectMethodDemo {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
getMethodDemo3();
}
//反射方法。非静态,有参数的show方法。
public static void getMethodDemo3() throws Exception{
StringclassName = "cn.itcast.domain.Person";
Class clazz = Class.forName(className);
Method method= clazz.getMethod("paramShow",String.class,int.class);//我要加载的(使用的、反射的)是paramShow名称带String和int两个参数
Object obj = clazz.newInstance();
method.invoke(obj, "xiaoqiang",40);//传对象 传参数
}
//反射方法。静态,无参数的show方法。
public static void getMethodDemo2() throws Exception{
String className = "cn.itcast.domain.Person";
Class clazz = Class.forName(className);
Method method = clazz.getMethod("staticShow", null);
method.invoke(null, null);
}
//反射方法。非静态,无参数的show方法。
public static void getMethodDemo() throws Exception{
String className = "cn.itcast.domain.Person";
Class clazz = Class.forName(className);
Method method = clazz.getMethod("show", null);//null表示这个函数没有参数
Object obj = clazz.newInstance();
method.invoke(obj, null);
// Person p = new Person();
// p.show();
}
}
---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity开发</a>、<a href="http://www.itheima.com"target="blank">.Net培训</a>、期待与您交流! ----------------------
详细请查看:<a href="http://www.itheima.com" target="blank"> www.itheima.com </a>