反射是什么?
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。其实就是对类的解剖。
哪里用到反射机制?
a、JDBC中,利用反射动态加载了数据库驱动程序。
b、Web服务器中利用反射调用了Sevlet的服务方法。
c、Eclispe等开发工具利用反射动态刨析对象的类型与结构,动态提示对象的属性和方法。
d、很多框架都用到反射机制,注入属性,调用方法,如Spring。
反射有什么作用?
如果给定一个类名,就可以通过反射机制来获取类的所有信息,也可以动态的创建对象和编译;
反射的实现主要借助以下四个类:
1、Class:类的对象;
2、Constructor:类的构造方法
3、Field:类中的属性对象
4、Method:类中的方法对象
获取字节码的三种方式
测试对象Person.java
public class Person {
public String name;
private int age;
public Person(){
System.out.println("无参数构造方法!");
}
public Person(String name,int age){
System.out.println("有参数构造方法!"+name+"--->"+age);
}
public Person(String name){
System.out.println("一个有参数构造方法!"+name);
}
public Person(int age){
System.out.println("一个有参数构造方法--->"+age);
}
public void setName(String name){
System.out.println("传入的是:"+name);
this.name = name;
}
public String getName() {
System.out.println("调用getName:"+name);
return name;
}
public void setAge(int age) {
System.out.println("传入的是:"+age);
this.age = age;
}
public int getAge() {
System.out.println("调用getAge:");
return age;
}
private void show(String name){
System.out.println("show我的姓名:"+name);
}
public void show(){
System.out.println("show我的姓名null:");
}
}
方式一
/**
* 方式一:
* 使用 .class 获取字节码对象
*
*/
public void getObject1() throws IllegalAccessException, InstantiationException {
//原来 new 只能拿到已经知道的 只能是自己
// .class 没有.java
//通过反射获取对象 变得高级啦 实现变得复杂啦
Person person = new Person();
//反射
//获取字节码对象
Class<Person> clazz = Person.class;
Class<Person> clazz1 = Person.class;
//判断两个字节码对象是不是同一个
System.out.println(clazz == clazz1);//true
//使用字节码的newInstance()方法实例化对象 无参数构造
//Person person1 = clazz.newInstance();
}
方式二
/**
* 方式二:
* 使用Object.getClass()方法 获取字节码对象;
* 1、通过反射获取shi有的private
* 2、要使用new 不方便
*/
public void getObject2(){
//创建对象
Person person = new Person();
//获取字节码对象
Class<? extends Person> aClass = person.getClass();
//等价上面两步 Class<? extends Person> aClass1 = new Person().getClass();
Class<? extends Person> bClass = person.getClass();
//判断两个字节码对象是否相等
System.out.println(aClass == bClass);//true
}
方式三
/**
* 方式三:
* 使用 Class 的forName("") 获取字节码文件
* @CallerSensitive
* public static Class<?> forName(String className)
* throws ClassNotFoundException {
* Class<?> caller = Reflection.getCallerClass();
* return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
* }
*
* 这个重点掌握的
* 只需要知道class的名称就ok了
*/
public void getObject3() throws ClassNotFoundException {
//早期
//com.tedu.reflect.Person p = new com.tedu.reflect.Person();
//定义class名称
String className = "com.test.reflect.Person";
//使用class名称获取字节码文件
Class<?> aClass = Class.forName(className);
Class<?> bClass = Class.forName(className);
//判断字节码对象
System.out.println(aClass==bClass);
//规范出发 需要有配置文件
}
获取构造方法
获取无参构造方法
/**
* 功能:获取无参构造方法
* @throws IllegalAccessException
* @throws InstantiationException
* @throws ClassNotFoundException
*/
public void getConstructor1() throws IllegalAccessException, InstantiationException, ClassNotFoundException {
//定义class名称
String className = "com.tedu.reflect.Person";
//使用className获取字节码对象
Class<Person> aClass = (Class<Person>) Class.forName(className);
//创建一个对象
Person person = aClass.newInstance();//打印
//运行show方法
person.show();//打印
}
获取有参数构造方法
/**
* 功能:获取有参数构造方法
*
* @throws NoSuchMethodException
* @throws ClassNotFoundException
* @throws IllegalAccessException
* @throws InvocationTargetException
* @throws InstantiationException
*/
public void getConstructor2() throws NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InvocationTargetException, InstantiationException {
//定义class名称
String className = "com.test.reflect.Person";
//使用forName(“”)获取字节码对象
Class<?> aClass = Class.forName(className);
//获取有参数的构造方法 传入类型的字节码对象
Constructor<?> constructor = aClass.getConstructor(String.class, int.class);
//获取实例化对象
Person instance = (Person) constructor.newInstance("小花", 18);
//设置name
instance.setName("小强");
}
/**
* 功能:自己实现单个的构造方法
*
* @throws NoSuchMethodException
* @throws IllegalAccessException
* @throws InvocationTargetException
* @throws InstantiationException
*/
public void getConstructor3() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//使用反射做等同于下面的构造方法
//Person xiaohu = new Person("xiaohu");
//获取字节码对象
Class<Person> personClass = Person.class;//换种方式,换种心情
//获取一个参数的构造方法 传入一个String的字节码对象 定义类型
Constructor<Person> constructor =
personClass.getConstructor(String.class);
//实例化对象 new Person("xiaohu");
Person person = constructor.newInstance("xiaohu");
}
获取属性
/**
* 功能:
* 获取属性 public的
*
* @throws NoSuchFieldException
* @throws IllegalAccessException
* @throws InstantiationException
*/
public void getField1() throws NoSuchFieldException, IllegalAccessException, InstantiationException {
//获取字节码对象
Class<Person> personClass = Person.class;//换种方式,换种心情
Person person = personClass.newInstance();
//通过名称获取属性
Field field = personClass.getField("name");//public修饰的
//不使用setName方法赋值 对象 设置的值
field.set(person,"huhua");
//获取name,打印
person.getName();
//等同于上面
Person person1 = new Person();
person1.name="huhua";
person1.getName();
}
/**
* 功能:不知道属性名称 不知道属性类型
*
* @throws ClassNotFoundException
* @throws IllegalAccessException
* @throws InstantiationException
*/
public void getField3() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
//1、获取字节码对象
String className = "com.tedu.reflect.Person";
Class<?> clazz = Class.forName(className);
Object obj = clazz.newInstance();
//2、获取字节码的属性 获取所有属性
Field[] fields = clazz.getDeclaredFields();
//简单
/* for (int i=0;i<fields.length;i++){
Field field = fields[i];
//3、设置属性的访问权限 暴力访问
field.setAccessible(true);
//获取属性的类型
Class<?> type = field.getType();
//4、赋值 type要使用包装类型
setF(obj,field,type,"xioaxiao");
//5、获取属性值
System.out.println(field.get(obj));
}*/
//增强for循环
for (Field field:fields) {
//3、设置属性的访问权限 暴力访问
field.setAccessible(true);
//获取属性的类型
Class<?> type = field.getType();
setF(obj,field,type,"xioaxiao");
//5、获取属性值
System.out.println(field.get(obj));
}
}
/**
* 功能:封装 设置value
*
* @param obj
* @param field
* @param type
* @param value
* @throws IllegalAccessException
*/
public void setF(Object obj,Field field,Class<?> type,Object value) throws IllegalAccessException {
System.out.println(type);
//判断类型
if (type==String.class){
field.set(obj,value);
}else if (type==Integer.class){
//Iinterger cast()强制装换
field.set(obj,type.cast(value));
}else if (type==Boolean.class){
field.set(obj,type.cast(value));
}else if (type==Short.class){
field.set(obj,type.cast(value));
}else if (type == Long.class){
field.set(obj,type.cast(value));
}else if (type == Byte.class){
field.set(obj,type.cast(value));
}else if (type == Double.class){
field.set(obj,type.cast(value));
}else if (type == Float.class){
field.set(obj,type.cast(value));
}
}
获取方法
/**
* 功能:获取一般方法
* @throws ClassNotFoundException
* @throws IllegalAccessException
* @throws InstantiationException
* @throws NoSuchMethodException
* @throws InvocationTargetException
*/
public void getMethod1() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//1、获取字节码对象
String className = "com.tedu.reflect.Person";
Class<?> clazz = Class.forName(className);
Object obj = clazz.newInstance();
//2、获取方法 下面的只能获取public的
// Method show = clazz.getMethod("show");
// Method show1 = clazz.getMethod("show", String.class);
//能够获取所有的方法
Method show = clazz.getDeclaredMethod("show");
Method show1 = clazz.getDeclaredMethod("show",String.class);
//设置访问权限 暴力访问设置 作为黑客专用
show1.setAccessible(true);
//3、对方法赋值 传入参数 4、使用方法
show.invoke(obj);
show1.invoke(obj,"xiaoxiao");
//---------------******------------------
String name = show.getName();
int parameterCount = show1.getParameterCount();
TypeVariable<Method>[] typeParameters = show.getTypeParameters();
}
/**
* 在日志系统使用
*
* 这个方法非常重要 login
* show(String name) 监控
* 传入的值
* 传入的类型
* 指定我监控的方法
*/
public void getMethod2() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
System.out.println("GetClassDemo.getMethod2");//soutm
//1、获取字节码对象
Class<Person> clazz = Person.class;
//实例化对象 调用无参构造方法
Person obj = clazz.newInstance();
//2、获取字节码对象的方法
Method show = clazz.getDeclaredMethod("show", String.class);
show.setAccessible(true);
//3、赋值和使用
Object o = show.invoke(obj, "小小");
//获取方法的名称
String name = show.getName();
System.out.println("方法名称:"+name);
//获取方法的参数数量
int parameterCount = show.getParameterCount();
System.out.println("参数列表数量:"+parameterCount);
//获取参数类型
Class<?>[] parameterTypes = show.getParameterTypes();
//show.getDeclaredAnnotations();
for (Object m:parameterTypes) {
System.out.println("参数类型:"+m);
}
//获取返回值类型
Class<?> returnType = show.getReturnType();
System.out.println("获取返回值类型:"+returnType);
}
应用练习1
NetCard.java
public class NetCard implements PCI {
@Override
public void open() {
System.out.println("net open");
}
@Override
public void close() {
System.out.println("net close");
}
}
SoundCard.java
public class SoundCard implements PCI {
public void open(){
System.out.println("sound open");
}
public void close(){
System.out.println("sound close");
}
}
Mainboard.java
public class Mainboard {
public void run() {
System.out.println("main board run....");
}
public void usePCI(PCI p) {//PCI p = new SouncCard();
if (p != null) {
p.open();
p.close();
}
}
}
PCI.java
public interface PCI {
public void open();
public void close();
}
ReflectTest.java
/*
* 电脑运行。
*/
public class ReflectTest {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
Mainboard mb = new Mainboard();
mb.run();
//每次添加一个设备都需要修改代码传递一个新创建的对象
// mb.usePCI(new SoundCard());
//能不能不修改代码就可以完成这个动作。
// 不用new来完成,而是只获取其class文件。在内部实现创建对象的动作。
File configFile = new File("pci.properties");
Properties prop = new Properties();
FileInputStream fis = new FileInputStream(configFile);
prop.load(fis);
for(int x=0; x<prop.size(); x++){
String pciName = prop.getProperty("pci"+(x+1));
Class clazz = Class.forName(pciName);//用Class去加载这个pci子类。
PCI p = (PCI)clazz.newInstance();
mb.usePCI(p);
}
fis.close();
}
}
应用练习2
--反射与工厂设计模式
这时可以发现,利用反射机制实现的工厂设计模式,最大的优势:对于接口子类的扩充将不再影响到工厂类的定义。
JavaApiDemo.java
public class JavaApiDemo {
public static void main(String[] args) throws Exception {
IMessage msg=Factory.getInstance("com.mldn.demo.NetMessage",IMessage.class);
msg.send();//【网络消息发送】www.baidu.com
msg=Factory.getInstance("com.mldn.demo.CloudMessage",IMessage.class);
msg.send();//【云消息发送】www.baidu.com
IService service=Factory.getInstance("com.mldn.demo.HouseService",IService.class);
service.service();
}}class Factory{
private Factory(){}
/**
* 获取接实例化对象
* @param className 接口的子类
* @param clazz 描述的是一个接口的类型
* @return 如果子类存在则返回指定接口的实例化对象
*/
@SuppressWarnings("unchecked")
public static <T> T getInstance(String className,Class<T> clazz){
T instance=null;
try {
instance=(T) Class.forName(className).getDeclaredConstructor().newInstance();
}catch (Exception e) {
e.printStackTrace();
}
return instance;
}}interface IService{
void service();}class HouseService implements IService{
@Override
public void service() {
System.out.println("【服务】为您的住宿提供服务");
}}interface IMessage{
void send();//消息发送}class NetMessage implements IMessage{
@Override
public void send() {
System.out.println("【网络消息发送】www.baidu.com");
}}class CloudMessage implements IMessage{
@Override
public void send() {
System.out.println("【云消息发送】www.baidu.com");
}}
课后作业
1 、写一个方法,此方法可以获取obj对象中名为propertyName(自定义)的属性的值
2.(1)写一个Properties格式的配置文件,配置类的完整名称。
(2) 写一个程序,读取这个Properties配置文件,获得类的完整名称并加载这个类。
3、 写一个方法,此方法可将obj对象中名为propertyName的属性的值设置为value.