黑马程序员——反射

时间:2023-02-19 16:50:06
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

一、反射技术概述

反射技术:动态的获取指定的类以及动态的调用类中的内容。
若一个应用程序已经写好了,后期出现的借口子类无法直接在该应用程序中用new创建对象。那该怎么办呢?既然子类不确定,可以通过对外提供配置文件信息的形式,将不确定的信息存储到配置文件即可。
该程序只要之前写好如何读取配置文件信息即可。把具体实现的子类名称定义到配置文件中。如果存储了指定的子类名,就根据具体的名称找到该类进行加载和对象的创建,这些动作都是在前期定义软件时做好的。在没有类之前就已经将创建对象的动作完成了。这就是动态的获取指定的类,并使用类中的功能,即反射技术。
反射技术的出现大大提高了程序的扩展性。

二、描述字节码文件的类Class(注意区分class)

class Class {
Field field; //将字段封装成了对象,类型是Field
Constructor constructor ; //将构造函数封装成了对象,类型是Constructor
Method method;
//将类中的成员都封装成了对象,并提供了对这些对象的操作
public Field getField(){
return field;
}
public Method getMethod() {
return method;
}
public Constructor getConstructor() {
return constructor;
}
}

三、字节码文件对象

1.获取字节码文件对象的三种方式 2.通过newInstance就可以创建字节码文件对象所表示的类的实例 3.反射中会出现的异常
public class GetClassDemo {

public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
/*
* 要先获取字节码文件中的成员,必须要先获取字节码文件对象
* 获取字节码文件对象的方式
* 1.通过Object类中的getClass()方法
* 这种方法虽然通用,但前提必须有指定类。并对该类进行对象创建,菜可以调用该类的getClass()方法
*
* 2.使用任意数据类型的一个静态成员class,所有数据类型都具备的一个属性
* 好处:不需要new对象,但是,还需要使用具体的类
*
* 3.使用Class类中的forName方法。通过给定类名类获取对应的字节码文件对象
* 只要知道类的名字就可以了,获取对应的字节码文件直接由forName完成
* 这就是反射技术获取字节码文件对象的方式
*/
//getClass_1();
//getClass_2();
getClass_3();
}

public static void getClass_1(){
Person p1 = new Person();
Person p2 = new Person();
Class cls1 = p1.getClass();
Class cls2 = p2.getClass();
//cls1等于cls2,字节码文件在内存中只有一份
System.out.println(cls1==cls2);
//获取类的名字
System.out.println(cls1.getName());
}

public static void getClass_2(){
Class cls = Person.class;
}

public static void getClass_3() throws ClassNotFoundException, InstantiationException, IllegalAccessException{
String classNamw = "Test.Person"; //写全名(包名 + 类名),否则会出现异常
Class cls = Class.forName(classNamw);

//通过newInstance就可以创建字节码文件对象所表示的类的实例
Object object = cls.newInstance();
System.out.println(object);

/*
* Class cls = Class.forName(classNamw);
* 1.通过给定的类名称加载指定的的字节码文件,并封装成字节码文件对象Class
* Object object = cls.newInstance();
* 2.通过newInstance创建给定的类的实例
* 3.调用构造函数进行初始化
* 通常被反射的类通常会提供空参数的构造函数,没有对应的构造函数,会有InstantiationException异常
* 如果有提供,但权限不够,会有IllegalAccessException异常
*/

/*
* Person p = new Person();
* 1.加载Person类,并将Person类封装成字节码文件对象
* 2.通过new创建Person对象
* 3.调用构造函数进行初始化
*/
}

}

四、获取构造器并初始化对象
package Test;

import java.lang.reflect.Constructor;

public class GetContractorDemo {
public static void main(String[] args) throws Exception {
/*
* 如果要通过指定的构造函数初始化对象,该怎么办呢?
* 1.获取字节码文件对象
* 2.在获取指定的构造函数
* 3.通过构造函数初始化对象
*/
getcons();
}

public static void getcons() throws Exception{
String classNamw = "Test.Person";
Class cls = Class.forName(classNamw);

//获取指定构造器,获取Person类中两个参数string,int的构造函数
Constructor constructor = cls.getConstructor(String.class,int.class);
//有了构造器对象,就通过构造器对象来初始化该类对象
Object object = constructor.newInstance("wangwu",23);
System.out.println(object);
}
}

五、获取类中字段

package Test;

import java.lang.reflect.Field;

public class getFieldDemo {

public static void main(String[] args) throws Exception {
/*
* 获取字段
*/
getfield();
}

public static void getfield() throws Exception{
String classNamw = "Test.Person";
Class cls = Class.forName(classNamw);
//获取字段
String fieldName = "age";
//Field field = cls.getField(fieldName); //获取的是公共的字段
Field field = cls.getDeclaredField(fieldName); //获取的是已声明的字段
System.out.println(field);

//对字段的值进行值的设置,必须先有对象
Object object = cls.newInstance();
field.setAccessible(true);//取消权限检查,暴力访问,是其父类方法
field.set(object, 30); //IllegalAccessException,字段是私有的
System.out.println(field.get(object));
}
}

六、获取类中的方法

package Test;

import java.lang.reflect.Method;

public class getMethod {

public static void main(String[] args) throws Exception {
getMethodDemo1();
getMethodDemo2();
}

public static void getMethodDemo1() throws Exception{
String classNamw = "Test.Person";
Class cls = Class.forName(classNamw);

String methodName = "show";
Method method = cls.getMethod(methodName, String.class , int.class);
Object object = cls.newInstance();
method.invoke(object, "wangwu",20);
}

//调用静态方法,无参数
public static void getMethodDemo2() throws Exception{
String classNamw = "Test.Person";
Class cls = Class.forName(classNamw);

String methodName = "staticshow";
Method method = cls.getMethod(methodName, null);
method.invoke(null, null);
}
}

七、反射应用案例

在Java工程下建立一个Test包, 1.测试类:NoteBookTest
package Test;

import java.io.File;
import java.io.FileReader;
import java.util.Properties;


public class NoteBookTest {

public static void main(String[] args) throws Exception{
/*
* 案例一:
* 阶段一:笔记本电脑运行
* 阶段二:想用一些外围设备,如比如鼠标,键盘......
* 为了提高笔记本的扩展性,应该降低这些设备和笔记本的耦合性
* 需要接口,只需要在设计之初先定义一个接口,而且笔记本在使用这个接口
*
* 后期有了USB设备后,需要不断地new对象才可以用,每一次都要修改代码
* 能不能不修改代码,就可以使用后期的设备呢?
* 设备不明确,而前期还要对其进行对象的建立,需要反射技术
* 对外提供一个配置文件
*/
NoteBook noteBook = new NoteBook();
noteBook.run();
//noteBook.useUSB(null);
//noteBook.useUSB(new MouseByUSB());

//通过反射方法重新设计应用程序,以提高更好的扩展性


File configFile = new File("usb.properties");
if(configFile.exists()){
configFile.createNewFile();
}
//读取配置文件
FileReader fileReader = new FileReader(configFile);
//为了获取键值信息方便,建立Properties
Properties properties = new Properties();
properties.load(fileReader);

for(int i=1 ; i<=properties.size() ; i++){
String className = properties.getProperty("usb" + i);
//反射
Class cls = Class.forName(className);
USB usb = (USB)cls.newInstance();
noteBook.useUSB(usb);
}
}

}

2. NoteBook类
package Test;

public class NoteBook {
/**
* 运行
*/
public void run(){
System.out.println("NoteBook run!!!");
}
/**
* 使用USB的设备
*/
public void useUSB(USB usb){
if(usb!=null){
usb.open();
usb.close();
}
}
}

3. USB接口
package Test;

public interface USB {
/**
* 开启
*/
public void open();
/**
* 关闭
*/
public void close();
}

4. MouseByUSB 类
package Test;

public class MouseByUSB implements USB {

@Override
public void open() {
System.out.println("mouse open!!!");
}

@Override
public void close() {
System.out.println("mouse close!!!");
}
}

5. KeyBoardByUSB 类
package Test;

public class KeyBoardByUSB implements USB {

@Override
public void open() {
System.out.println("KeyBoard open!!!");
}

@Override
public void close() {
System.out.println("KeyBoard close!!!");
}
}

6.工程根目录下的一个配置文件usb.properties
文件中的内容如下: usb1=Test.MouseByUSB
usb2=Test.KeyBoardByUSB