1.反射概述
概念: 反射是一种机制,利用该机制可以在程序运行过程中对类进行解剖并操作类中的所有成员(成员变量,成员方法,构造方法)。
class对象:反射需要获取该类的字节码文件对象,就是class对象。
Class 类的实例表示正在运行的 Java 应用程序中的类和接口。Java中任何一种类型不管是引用数据类型还是基本数据类型都具有其对应的类型对象,即Class对象
Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的
获取Class对象的三种方式:
方式1: 通过类名.class获得
方式2:通过对象名.getClass()方法获得
方式3:通过Class类的静态方法获得: static Class forName(“类全名”)
(注意:每个类都只会生成一个字节码对象,因此三种方式获取的都是同一个对象)
举例:
public static void main(String[] args) throws ClassNotFoundException {
// 方式1: 通过类名.class获得
Class<String> stringClass = String.class;
// 方式2:通过对象名.getClass()方法获得
String s = new String();
Class<? extends String> aClass = s.getClass();
// 方式3:通过Class类的静态方法获得: static Class forName("类全名")
Class<?> aClass1 = Class.forName("java.lang.String");
}
}
Class类常用方法:
String getSimpleName(); 获得类名字符串:类名
String getName(); 获得类全名:包名+类名
T newInstance() ; 创建Class对象关联类的对象【依赖无参构造而存在】
举例:
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
//先获取字节码对象
Class<String> stringClass = String.class;
// String getSimpleName(); 获得类名字符串:类名
String simpleName = stringClass.getSimpleName();
System.out.println(simpleName);
// String getName(); 获得类全名:包名+类名
String name = stringClass.getName();
System.out.println(name);
// T newInstance() ; 创建Class对象关联类的对象【依赖无参构造而存在】
String s = stringClass.newInstance();
System.out.println("String ="+s.isEmpty());//因为是无参构造方法,所以没有给String赋值,为null
}
}
java一切皆是对象
java中的成员变量,成员方法,构造方法都有对应的类对象
Constructor(构造方法)类:提供关于类的单个构造方法的信息以及对它的访问权限。
获取构造方法对象:
获取单个指定参数的构造方法
//只能获得public修饰的构造方法
Constructor getConstructor(Class… parameterTypes)
// 可以是public、protected、package-private、private修饰符的构造方法。
Constructor getDeclaredConstructor(Class… parameterTypes)
获取所有符合条件的构造方法
// 获得类中的所有构造方法对象,只能获得public的
Constructor[] getConstructors()
//可以是public、protected、package-private、private修饰符的构造方法。
Constructor[] getDeclaredConstructors()
Method类: 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。所反映的方法可能是类方法【静态方法】或实例方法【非静态方法】(包括抽象方法)。
获取成员方法对象:
Method getMethod(String name,Class…args);
* 根据方法名和参数类型获得对应的方法对象,只能获得public的
Method getDeclaredMethod(String name,Class…args);
* 根据方法名和参数类型获得对应的方法对象,包括非public的【只能获取本类中的一个】
Method[] getMethods();
* 获得类中的所有成员方法对象,返回数组,只能获得public修饰的且包含父类的
Method[] getDeclaredMethods();
* 获得类中的所有成员方法对象,返回数组,只获得本类的,包括非public的方法【只能获取本类中所有】
使用方法对象:
Object invoke(Object obj, Object… args)
obj:调用方法所使用的对象,如果是静态方法可以传入null
args:调用方法时传递的参数【实参】
返回值就是该方法对象调用后的返回值,如果是无返回值方法固定返回null
void setAccessible(true) 如果是非public方法,就得调用该方法设置为可访问。
设置"暴力访问"——是否取消权限检查,true取消权限检查,false表示不取消
代码演示:
class ceshi31{
private int a = 1;
private String show(String s){
System.out.println("私有方法");
System.out.println(s);
return new String("返回值");
}
}
class ceshi3{
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//创建对象,来获取字节码对象
ceshi31 ceshi31 = new ceshi31();
Class<? extends com.Frank.HomeWork4.ceshi31> aClass = ceshi31.getClass();
//通过字节码对象,获取方法对象。如果没有参数,则参数类型为null
Method declaredMethod = aClass.getDeclaredMethod("show",String.class);
//因为是私有方法,需要取消权限检查,暴力访问
declaredMethod.setAccessible(true);
//执行方法对象,如果是静态方法调用,不依赖对象,第一个参数为null
Object invoke = declaredMethod.invoke(ceshi31,"方法参数");
System.out.println(invoke.toString());
}
}
Field类: 通过Field对象给对应的成员变量赋值和取值
获取Field对象的方法:
Field getField(String name);
* 根据成员变量名获得对应Field对象,只能获得public修饰
Field getDeclaredField(String name);
* 根据成员变量名获得对应Field对象,包括public、protected、(默认)、private的
Field[] getFields();
* 获得所有的成员变量对应的Field对象,只能获得public的
Field[] getDeclaredFields();
* 获得所有的成员变量对应的Field对象,包括public、protected、(默认)、private的
。
从上可知:
Java的 反射机制 是在运行状态中,对于任意一个类,都能够 知道这个类的所有属性和方法 ;对于任意一个对象,都能够 调用它的任意一个方法和属性
简而言之,只要你给我一个 .class ——类的名字,我就能通过反射获取到类的属性和方法。
比如 Spring IOC 容器帮我们实例化众多的bean:
Spring 配置文件:
<bean id="pony" class="com.xblzer.dp.proxy.springaop.Pony"></bean>
ApplicationContext ctx = new ClassPathXmlApplicationContext("app_aop.xml")
Pony pony = (Pony) ctx.getBean("pony");
java的反射机制,使得程序十分灵活
举例
参考工厂模式:
interface Fruit{//水果接口
public void eat();//吃水果
}
class Apple implements Fruit{//苹果类
@Override
public void eat() {
System.out.println("我要吃苹果");
}
}
class Orange implements Fruit{//橘子类
@Override
public void eat() {
System.out.println("我要吃橘子");
}
}
class Factory{//水果工厂类用来创建水果对象
public static Fruit getInstance(String fruitName){
Fruit f = null;
if (fruitName.equals("苹果")){
f = new Apple();
}else if (fruitName.equals("橘子")){
f = new Orange();
}
return f;
}
}
class FactoryTest{
public static void main(String[] args) {
Fruit orange = Factory.getInstance("橘子");
orange.eat();
}
}
这种情况下,如果我们需要再添加一种水果的时候,不仅要新增一个水果类,还要去修改水果工厂代码。如果使用反射,通过Class对象来根据水果类的全名来创建水果类字节码对象,再创建水果类对象。这样不需要修改水果工厂类了。
修改后的代码:
class Factory{
public static Fruit getInstance(String fruitName) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class<?> aClass = Class.forName(fruitName);
Object o = aClass.newInstance();
Fruit fruit = (Fruit)o;
return fruit;
}
}
class FactoryTest{
public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
Fruit orange = Factory.getInstance("com.Frank.HomeWork4.Orange");
orange.eat();
}
}