第13章:Java反射机制
13.1 理解Class类并实例化Class类对象
13.1.1 反射概述
1)Java Reflection
Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内 部信息,并能直接操作任意对象的内部属性及方法
正常方式:引入需要的包类名称->通过new实例化->取得实例化对象
反射方式:实例化对象->getClass()方法->得到完整的包类名称
2)Java反射机制提供的功能
a)在运行时判断任意一个对象所属的类
b)在运行时构造任意一个类的对象
c)在运行时判断任意一个类所具有的成员变量和方法
d)在运行时调用任意一个对象的成员变量和方法
e)生成动态代理
3)有了反射,可以通过反射创建一个类的对象,并调用其中的结构
@Test
public void test2() throws Exception{
Class clazz = Person.class;
//1.创建clazz对应的运行时类Person类的对象
Person p = (Person)clazz.newInstance();
//2.通过反射调用运行时类的指定的属性
Field f1 = clazz.getField("name");//public
f1.set(p,"LiuDeHua");
Field f2 = clazz.getDeclaredField("age");//private
f2.setAccessible(true);
f2.set(p, 20);
//3.通过反射调用运行时类的指定的方法
Method m1 = clazz.getMethod("show");//不带参数
m1.invoke(p);
Method m2 = clazz.getMethod("display",String.class);//带参数
m2.invoke(p,"CHN");
}
4)反射的源头:java.lang.Class
我们创建了一个类,通过编译(javac.exe),生成对应的.class文件。之后我们使用java.exe加载(JVM的类加载器 完成的)。此.class文件,此.class文件加载到内存以后,就是一个运行时类,存在在缓存区。那么这个运行时类本 身就是一个Class的实例!
a)每一个运行时类只加载一次!
b)有了Class的实例以后,我们才可以进行如下的操作:
>创建对应的运行时类的对象
>获取对应的运行时类的完整结构(属性、方法、构造器、内部类、父类、所在的包、异常、注解、...)
>调用对应的运行时类的指定的结构(属性、方法、构造器)
>反射的应用:动态代理
5)获取Class的实例(4种)
a)调用运行时类本身的.class属性
Class clazz = Person.class;
b)通过运行时类的对象获取
Person p = new Person();
Class clazz = p.getClass();
c)通过Class的静态方法获取.通过此方式,体会一下,反射的动态性。
String className = "com.lidong.java.Person";
Class clazz = Class.forName(className);
d)(了解)通过类的加载器
String className = "com.lidong.java.Person";
ClassLoader classLoader = this.getClass().getClassLoader();
Class clazz = classLoader.loadClass(className);
6)ClassLoader
类加载器是用来把类(class)装载进内存的。JVM规范定义了两种类型的类加载器:启动类加载器(bootstrap) 和用户自定义加载器(user-defined class loader)。JVM在运行时会产生3个类加载器组成的初始化加载器层次结 构
ClassLoader loader = this.getClass().getClassLoader();
InputStream is = loader.getResourceAsStream("com\\atguigu\\java\\jdbc.properties");
Properties pros = new Properties();
pros.load(is);
String name = pros.getProperty("user");
System.out.println(name);
String password = pros.getProperty("password");
System.out.println(password);
13.2 运行时创建类对象并获取类的完整结构
13.2.1 有了Class对象,能做什么?
1)创建类的对象:调用Class对象的newInstance()方法
a)类必须有一个无参数的构造器
b)类的构造器的访问权限需要足够
String className = "com.atguigu.java.Person";
Class clazz = Class.forName(className);。
Object obj = clazz.newInstance();
Person p = (Person)obj;
2)没有无参的构造器就不能创建对象了吗?
不是!只要在操作的时候明确的调用类中的构造方法,并将参数传递进去之后,才可以实例化操作。步骤如下:
-
通过Class类的getDeclaredConstructor(Class ... parameterTypes)取得本类的指定形参类型的构造器
-
向构造器中的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。
String className = "com.atguigu.java.Person";
Class clazz = Class.forName(className);
Constructor cons = clazz.getDeclaredConstructor(String.class,int.class);
cons.setAccessible(true);
Person p = (Person)cons.newInstance("罗伟",20);
-
在Constructor类中存在的一个方法
public T newInstance(Object ... initargs)
这些是反射机制应用最多的地方
13.2.2 通过反射调用类的完整结构
1)获取对应的运行时类的属性
@Test
public void test(){
Class clazz = Person.class;
//getFields():只能获取到运行时类中及其父类中声明为public的属性
Field[] fields1 = clazz.getFields();
for(int i = 0;i < fields1.length;i++){
System.out.println(fields1[i]);
}
System.out.println();
//getDeclaredFields():获取运行时类本身声明的所有的属性
Field[] fields2 = clazz.getDeclaredFields();
for(Field f : fields2){
System.out.println(f.getName());
}
}
2)获取属性的各个部分的内容:权限修饰符 变量类型 变量名
@Test
public void test(){
Class clazz = Person.class;
Field[] fields = clazz.getDeclaredFields();
for(Field f : fields){
//1.获取每个属性的权限修饰符
int i = f.getModifiers();
String str = Modifier.toString(i);
System.out.print(str + " ");
//2.获取属性的类型
Class type = f.getType();
System.out.print(type.getName() + " ");
//3.获取属性名
System.out.print(f.getName());
System.out.println();
}
}
3)调用运行时类指定的属性
@Test
public void test() throws Exception{
Class clazz = Person.class;
//1.获取指定的属性
//getField(String fieldName):获取运行时类中声明为public的指定属性名为fieldName的属性
Field name = clazz.getField("name");
//2.创建运行时类的对象
Person p = (Person)clazz.newInstance();
System.out.println(p);
//3.将运行时类的指定的属性赋值
name.set(p,"Jerry");
System.out.println(p);
System.out.println("%"+name.get(p));
System.out.println();
//getDeclaredField(String fieldName):获取运行时类中指定的名为fieldName的属性
Field age = clazz.getDeclaredField("age");
//由于属性权限修饰符的限制,为了保证可以给属性赋值,需要在操作前使得此属性可被操作。
age.setAccessible(true);
age.set(p,10);
System.out.println(p);
}
4)获取运行时类的方法
@Test
public void test(){
Class clazz = Person.class;
//getMethods():获取运行时类及其父类中所有的声明为public的方法
Method[] m1 = clazz.getMethods();
for(Method m : m1){
System.out.println(m);
}
System.out.println();
//getDeclaredMethods():获取运行时类本身声明的所有的方法
Method[] m2 = clazz.getDeclaredMethods();
for(Method m : m2){
System.out.println(m);
}
}
5)获取方法的各个部分的内容:注解 权限修饰符 返回值类型 方法名 形参列表 异常
@Test
public void test(){
Class clazz = Person.class;
Method[] me = clazz.getDeclaredMethods();
for(Method m : me){
//1.注解
Annotation[] an = m.getAnnotations();
for(Annotation a : an){
System.out.println(a);
}
//2.权限修饰符
String str = Modifier.toString(m.getModifiers());
System.out.print(str + " ");
//3.返回值类型
Class returnType = m.getReturnType();
System.out.print(returnType.getName() + " ");
//4.方法名
System.out.print(m.getName() + " ");
//5.形参列表
System.out.print("(");
Class[] params = m.getParameterTypes();
for(int i = 0;i < params.length;i++){
System.out.print(params[i].getName() + " args-" + i + " ");
}
System.out.print(")");
//6.异常类型
Class[] exps = m.getExceptionTypes();
if(exps.length != 0){
System.out.print("throws ");
}
for(int i = 0;i < exps.length;i++){
System.out.print(exps[i].getName() + " ");
}
System.out.println();
}
}
6)调用运行时类中指定的方法
@Test
public void test() throws Exception{
Class clazz = Person.class;
//getMethod(String methodName,Class ... params):获取运行时类中声明为public的指定的方
法
Method m1 = clazz.getMethod("show");
Person p = (Person)clazz.newInstance();
//调用指定的方法:Object invoke(Object obj,Object ... obj)
Object returnVal1 = m1.invoke(p);
System.out.println(returnVal1);
Method m2 = clazz.getMethod("toString");
Object returnVal2 = m2.invoke(p);
System.out.println(returnVal2);
//对于运行时类中静态方法的调用
Method m3 = clazz.getMethod("info");
m3.invoke(Person.class);
//getDeclaredMethod(String methodName,Class ... params):获取运行时类中声明了的指定的
方法
Method m4 = clazz.getDeclaredMethod("display",String.class,Integer.class);
m4.setAccessible(true);
Object value = m4.invoke(p,"CHN",10);
System.out.println(value);
}
7)获取运行时类的父类
@Test
public void test(){
Class clazz = Person.class;
Class superClass = clazz.getSuperclass();
System.out.println(superClass);
}
8)获取带泛型的父类
@Test
public void test(){
Class clazz = Person.class;
Type type = clazz.getGenericSuperclass();
System.out.println(type);
}
9)获取父类的泛型
@Test
public void test(){
Class clazz = Person.class;
Type type = clazz.getGenericSuperclass();
ParameterizedType param = (ParameterizedType)type;
Type[] ars = param.getActualTypeArguments();
System.out.println(((Class)ars[0]).getName());
}
10)获取实现的接口
@Test
public void test(){
Class clazz = Person.class;
Class[] interfaces = clazz.getInterfaces();
for(Class i : interfaces){
System.out.println(i);
}
}
11)获取所在的包
@Test
public void test(){
Class clazz = Person.class;
Package pack = clazz.getPackage();
System.out.println(pack);
}
12)获取注解
@Test
public void test(){
Class clazz = Person.class;
Annotation[] anns = clazz.getAnnotations();
for(Annotation a : anns){
System.out.println(a);
}
}
13.3 通过反射调用类的指定方法、指定属性
13.3.1 调用指定方法
1)通过反射,调用类中的方法,通过Method类完成。步骤:
a)通过Class类的getMethod(String name,Class ... parameterTypes)方法取得一个Method对象,并设置此方 法操作时所需要的参数类型
b)之后使用Object invoke(Object obj,Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信 息。
@Test
public void test() throws Exception{
Class clazz = Person.class;
//getMethod(String methodName,Class ... params):获取运行时类中声明为public的指定
的方法
Method m1 = clazz.getMethod("show");
Person p = (Person)clazz.newInstance();
//调用指定的方法:Object invoke(Object obj,Object ... obj)
Object returnVal1 = m1.invoke(p);
System.out.println(returnVal1);
Method m2 = clazz.getMethod("toString");
Object returnVal2 = m2.invoke(p);
System.out.println(returnVal2);
//对于运行时类中静态方法的调用
Method m3 = clazz.getMethod("info");
m3.invoke(Person.class);
//getDeclaredMethod(String methodName,Class ... params):获取运行时类中声明了的指
定的方法
Method m4 = clazz.getDeclaredMethod("display",String.class,Integer.class);
m4.setAccessible(true);
Object value = m4.invoke(p,"CHN",10);
System.out.println(value);
}
@Test
public void test() throws Exception{
String className = "com.atguigu.java.Person";
Class clazz = Class.forName(className);
Constructor cons = clazz.getDeclaredConstructor(String.class,int.class);
cons.setAccessible(true);
Person p = (Person)cons.newInstance("罗伟",20);
System.out.println(p);
}
13.3.2 调用指定的属性
@Test
public void test() throws Exception{
Class clazz = Person.class;
//1.获取指定的属性
//getField(String fieldName):获取运行时类中声明为public的指定属性名为fieldName的属性
Field name = clazz.getField("name");
//2.创建运行时类的对象
Person p = (Person)clazz.newInstance();
System.out.println(p);
//3.将运行时类的指定的属性赋值
name.set(p,"Jerry");
System.out.println(p);
System.out.println("%"+name.get(p));
System.out.println();
//getDeclaredField(String fieldName):获取运行时类中指定的名为fieldName的属性
Field age = clazz.getDeclaredField("age");
//由于属性权限修饰符的限制,为了保证可以给属性赋值,需要在操作前使得此属性可被操作。
age.setAccessible(true);
age.set(p,10);
System.out.println(p);
}
13.4 动态代理
13.4.1 Java动态代理
1)静态代理,特征:代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只 能为一个接口服务,这样一来程序开发中必然产生过多的代理
interface ClothFactory{
void productCloth();
}
//被代理类
class NikeClothFactory implements ClothFactory{
@Override
public void productCloth() {
System.out.println("Nike工厂生产一批衣服");
}
}
//代理类
class ProxyFactory implements ClothFactory{
ClothFactory cf;
public ProxyFactory(ClothFactory cf){
this.cf = cf;
}
@Override
public void productCloth() {
System.out.println("代理类开始执行,收代理费$1000");
cf.productCloth();
}
}
public class TestClothProduct {
public static void main(String[] args) {
NikeClothFactory nike = new NikeClothFactory();
//创建被代理类的对象
ProxyFactory proxy = new ProxyFactory(nike);
//创建代理类的对象
proxy.productCloth();
}
}
2)动态代理(体会反射):客户通过代理类来调用其他对象的方法,并且是在程序运行时根据需要动态创建目标类 的代理对象
a)调试
b)远程方法调用
interface Subject {
void action();
}
// 被代理类
class RealSubject implements Subject {
public void action() {
System.out.println("我是被代理类,记得要执行我哦!么么~~");
}
}
class MyInvocationHandler implements InvocationHandler {
Object obj;// 实现了接口的被代理类的对象的声明
// ①给被代理的对象实例化②返回一个代理类的对象
public Object blind(Object obj) {
this.obj = obj;
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
.getClass().getInterfaces(), this);
}
//当通过代理类的对象发起对被重写的方法的调用时,都会转换为对如下的invoke方法的调用
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//method方法的返回值时returnVal
Object returnVal = method.invoke(obj, args);
return returnVal;
}
}
public class TestProxy {
public static void main(String[] args) {
//1.被代理类的对象
RealSubject real = new RealSubject();
//2.创建一个实现了InvacationHandler接口的类的对象
MyInvocationHandler handler = new MyInvocationHandler();
//3.调用blind()方法,动态的返回一个同样实现了real所在类实现的接口Subject的代理类的对
象。
Object obj = handler.blind(real);
Subject sub = (Subject)obj;//此时sub就是代理类的对象
sub.action();//转到对InvacationHandler接口的实现类的invoke()方法的调用
}
}
3)代理设计模式原理:使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上
13.4.2 动态代理与AOP(Aspect Orient Programming)
1)前面介绍的Rroxy和InvocationHandler,很难看出这种动态代理的优势,下面介绍一种更实用的动态代理机制
改进后说明:代码段1、代码段2、代码段3和深色代码段分离开了,但代码段1,2,3又和一个特定的方法A耦合了! 最理想的效果是:代码块1、2、3既可以执行方法A,又无序在程序中以硬编码的方式直接调用深色代码的方法
interface Human {
void info();
void fly();
}
// 被代理类
class SuperMan implements Human {
public void info() {
System.out.println("我是超人!我怕谁!");
}
public void fly() {
System.out.println("I believe I can fly!");
}
}
class HumanUtil {
public void method1() {
System.out.println("=======方法一=======");
}
public void method2() {
System.out.println("=======方法二=======");
}
}
class MyInvocationHandler implements InvocationHandler {
Object obj;// 被代理类对象的声明
public void setObject(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
HumanUtil h = new HumanUtil();
h.method1();
Object returnVal = method.invoke(obj, args);
h.method2();
return returnVal;
}
}
class MyProxy {
// 动态的创建一个代理类的对象
public static Object getProxyInstance(Object obj) {
MyInvocationHandler handler = new MyInvocationHandler();
handler.setObject(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
.getClass().getInterfaces(), handler);
}
}
public class TestAOP {
public static void main(String[] args) {
SuperMan man = new SuperMan();//创建一个被代理类的对象
Object obj = MyProxy.getProxyInstance(man);//返回一个代理类的对象
Human hu = (Human)obj;
hu.info();//通过代理类的对象调用重写的抽象方法
System.out.println();
hu.fly();
}
}