Java程序在运行时操作类中的属性和方法的机制,称为反射机制。 一个关键点:运行时 一般我们在开发程序时,都知道自己具体用了什么类,直接创建使用即可。但当你写一些通用的功能时没办法在编写时知道具体的类型,并且程序跑起来还会有多种类型的可能,则需要在运行时动态的去调用某个类的属性和方法,这就必须使用反射来实现。 例子说明: Father f = new Children(); 编译时变量f 为Father类型,运行时为Children类型; 编译时demo方法参数类型为Object,一般有两种做法 第一种做法是知道参数类型有哪几种情况,可以使用instanceof运算符进行判断,再利用强制类型转换将其转换成其运行时类型的变量即可。 第二种做法是编译时根本无法预知该对象和类可能属于哪些类,程序只依靠运行时信息动态的来发现该对象和类的真实信息,这就必须使用反射。 那反射是怎么做到在运行时获取类的属性和方法的呢? 理解类的加载机制的应该知道,当java文件编译成.class文件,再被加载进入内存之后,JVM自动生成一个唯一对应的Class对象,这个Class是一个具体的类,这个Class类就是反射学习的重点。反射的操作对象就是这个Class类,通过Class类来获取具体类的属性和方法。 Class 类是用于保存类或接口属性和方法信息的类,就是保存类信息的类,它类名称就叫 Class。 Class类和构造方法源码 简单分析下Class类 Class对象的创建是在加载类时由 Java 虚拟机以及通过调用类加载器中的 方式一:常用方式,Class.forName("包名.类名") 执行结果:全限定类名=com.yty.fs.Children 方式二:每个类下的静态属性 class,类名.class 执行结果:类名称=Children 方式三:每个类最终都继承了Object,Object类下的getClass() 执行结果:类所在包=package com.yty.fs 三种方式简单对比: 怎么选: Class类中有非常多的方法,通过案例掌握常用的方法即可。 1.构造方法操作 1.1.所有构造方法 1.2.所有public构造方法 1.3.无参构造方法 1.4.单个私有构造方法 2.字段操作(成员变量) 2.1.获取所有成员变量 2.2.获取所有公共成员变量 2.3.获取单个公共成员变量 2.4.获取单个私有成员变量 3.方法操作(成员方法) 3.1.获取所有方法--不会获取父类的方法 3.2.获取所有公共方法--会获取父类的方法 3.3.获取单个公共方法 3.3.1.获取单个公共方法--无参方法 3.3.2.获取单个公共方法--有参方法 3.4.获取单个私有方法 具体看代码 测试类:Children类 Class类的具体操作 执行结果:拷贝过去执行就知道了…… 自定义一个测试用的注解 注解使用类和测试用的main方法 执行结果: PersonAnnotationDemo是否是注解类:false 输出注解中的值:name=张三 PersonAnnotationDemo{name=张三, age=0} 常用于框架底层开发 Spring 框架通过将成员变量值以及依赖对象等信息都放在配置文件中进行管理的,类发生改变时只需要更新配置文件,对于反射模块则无需更改,从而实现了较好的解耦。 测试类 配置文件信息 反射测试类 执行结果:Do you like yellow BigBanana? 通过代理的方式(增加中间层)对想要访问的类做一些控制,使代码职责清晰、通用化、智能化、易扩展。 代理模式三个要点:一个公共接口、一个具体类(被代理类)、一个代理类 代理模式分为:静态代理和动态代理 JDK动态代理本质是通过反射来实现,涉及InvocationHandler接口和Proxy类。 Proxy类:创建动态代理实例; InvocationHandler对象:当执行被代理对象里的方法时,实际上会替换成调用InvocationHandler对象的invoke方法,动态的代理到接口下的实现类。 本次案例的类图关系: 华为 小米 爱疯 测试结果: 代理方法--start--代理前可以考虑做些跟有趣的事 卖Huawei Huawei 16 pro 的手机 代理方法--end--代理后可能你有更想要做的事 代理方法--start--代理前可以考虑做些跟有趣的事 卖Xiaomi MI 13 pro 的手机 代理方法--end--代理后可能你有更想要做的事 代理方法--start--代理前可以考虑做些跟有趣的事 卖IPhone IPhone 13 pro 的手机 代理方法--end--代理后可能你有更想要做的事 通过实战进一步理解泛型和反射。 PrintResult类:注意成员方法私有 测试类 执行结果: 执行的方法=printSuccessInfo printSuccessInfo--执行成功 执行的方法=printAdd 求和结果=4443 在此可以看到泛型T 的对象t,是可以像普通的对象一样使用反射。 泛型在编译期通过类型抹除机制来完成; 反射在运行期完成执行,可以理解为反射是在运行期将编译好的list集合再新增元素进去。 执行结果:集合中的内容:[qwert, 1234Z, 100] 原创不易,三联支持:点赞、分享1.反射概述
public void demo(Object obj){
// 不知道调用者传什么具体对象
……
}
2.Class类
2.1.理解Class类
public final class Class
defineClass
方法自动构造的,关于类的加载可以通过继承ClassLoader来实现自定义的类加载器,本文着重讲反射,在此不展开讲类加载相关知识。2.2.获取Class对象的三种方式
public static void main(String[] args) {
// 方式一:全限定类名字符串
Class> childrenClass = null;
try {
childrenClass = Class.forName("com.yty.fs.Children"); // 包名.类名
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 获取类名称
System.out.println("全限定类名="+childrenClass.getName());
}
public static void main(String[] args) {
// 方式二:每个类下的静态属性 class
Class
public static void main(String[] args) {
// 方式三:Object类下的getClass()
Children children = new Children();
Class> childrenClass3 = children.getClass();
System.out.println("类所在包="+childrenClass3.getPackage());
}
2.3.案例一:构造方法、成员变量和成员方法的获取和使用
public class Children {
public String testString; //测试用
private int id;
private String name;
// 无参构造方法
public Children() {
System.out.println("====无参构成方法被调用");
}
// 多个参数构造方法
public Children(int id, String name) {
this.id = id;
this.name = name;
}
// default构造方法--测试
Children(String name, int id){
this.id = id;
this.name = name;
}
// 受保护构造方法--测试
protected Children(int id) {
this.id = id;
}
// 私有构造方法--测试
private Children(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Children{ id=" + id + ", name=" + name + "}";
}
public void printName(){
System.out.println("====printName--"+this.name);
}
public void printName(String name){
this.name = name;
System.out.println("====printName--"+this.name);
}
private void demoTest(){
System.out.println("====demoTest--执行了");
}
}
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* 案例一:构造方法、成员变量和成员方法的获取和使用
*/public class Demo1 {
public static void main(String[] args) throws Exception {
Class> chilrenClass = Class.forName("com.yty.fs.Children");
// 1.构造方法操作
// 1.1.获取所有构造方法
System.out.println("1.构造方法操作n1.1.所有构造方法");
Constructor>[] declaredConstructors = chilrenClass.getDeclaredConstructors();
for (Constructor constructor : declaredConstructors){
System.out.println(constructor.toString()); // Constructor类的toString已重写
}
// 1.2.获取所有public构造方法
System.out.println("1.2.所有public构造方法");
Constructor>[] constructors = chilrenClass.getConstructors();
for(Constructor constructor : constructors){
System.out.println(constructor.toString());
}
// 1.3.获取无参构造方法
Constructor> onParamConstructor = chilrenClass.getConstructor();//参数类型为null,表示无参
System.out.println("1.3.无参构造方法:n"+onParamConstructor.toString());
// 实例化对象
Object o = onParamConstructor.newInstance();
if(o instanceof Children){
Children children = (Children)o;
children.setId(111);
children.setName("myName");
System.out.println(o.toString());// Children类重写了toString
}
// 1.4.获取单个私有构造方法
// 指定了私有构造方法的参数类型,所以只会获取到一个构造方法
Constructor> privateConstructor = chilrenClass.getDeclaredConstructor(String.class);
System.out.println("1.4.单个私有构造方法:n"+privateConstructor.toString());
//私有构造方法需要取消访问权限检查,否则报异常:IllegalAccessExceptionw
privateConstructor.setAccessible(true);
Object obj = privateConstructor.newInstance("myName");
System.out.println(o.toString());
// 2.字段操作(成员变量)
// 2.1.获取所有成员变量
System.out.println("2.字段操作(成员变量)n2.1.获取所有成员变量");
Field[] declaredFields = chilrenClass.getDeclaredFields();
for (Field declaredField : declaredFields){
// 获取fieldName
System.out.println(declaredField.getName());
}
// 2.2.获取所有公共成员变量
System.out.println("2.2.获取所有公共成员变量");
Field[] fields = chilrenClass.getFields();
for (Field field : fields){
// 获取fieldName
System.out.println(field.getName());
}
// 2.3.获取单个公共成员变量
System.out.println("2.3.获取单个公共成员变量");
Field field = chilrenClass.getField("testString");
Object o1 = chilrenClass.getConstructor().newInstance();
field.set(o1,"yty");
Object o1_1 = field.get(o1);
// 获取fieldName
System.out.println("成员变量名-值:"+field.getName()+"="+o1_1.toString());
// 2.4.获取单个私有成员变量
System.out.println("2.4.获取单个私有成员变量");
Field field2 = chilrenClass.getDeclaredField("name");
//私有成员变量需要取消访问权限检查,否则报异常:IllegalAccessExceptionw
field2.setAccessible(true);
Object o2 = chilrenClass.getConstructor().newInstance();
field2.set(o2,"myName");
Object o2_2 = field2.get(o2);
// 获取fieldName
System.out.println("成员变量名-值:"+field2.getName()+"="+o2_2.toString());
// 3.方法操作(成员方法)
// 3.1.获取所有方法(成员方法)
System.out.println("3.方法操作(成员方法)n3.1.获取所有方法--不会获取父类的方法");
Method[] declaredMethods = chilrenClass.getDeclaredMethods();
for (Method method : declaredMethods){
// 获取方法名
System.out.println(method.getName());
}
// 3.2.获取所有公共方法
System.out.println("3.2.获取所有公共方法--会获取父类的方法");
Method[] methods = chilrenClass.getMethods();
for (Method method : methods){
// 获取方法名
System.out.println(method.getName());
}
// 3.3.获取单个公共方法
System.out.println("3.3.获取单个公共方法n3.3.1.获取单个公共方法--无参方法");
Method printName = chilrenClass.getMethod("printName"); //方法名称
System.out.println(printName);
System.out.println("3.3.2.获取单个公共方法--有参方法");
Method printName2 = chilrenClass.getMethod("printName",String.class); //方法名称,参数类型
System.out.println("参数个数:"+printName2.getParameterCount());
// 遍历所有参数信息
Parameter[] parameters = printName2.getParameters();
for (int i=0;i
2.4.案例二:注解的相关操作
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(value = RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)
public @interface PersonAnnotation {
String name() default "myName";
}
import java.lang.reflect.Field;
public class PersonAnnotationDemo {
@PersonAnnotation(name = "张三")
private String name;
private int age;
@Override
public String toString() {
return "PersonAnnotationDemo{" + "name=" + name + ", age=" + age + "}";
}
public static void main(String[] args) throws Exception {
Class> annotateClass = Class.forName("com.yty.fs.PersonAnnotationDemo");
Object o = annotateClass.newInstance();
System.out.println("PersonAnnotationDemo是否是注解类:"+annotateClass.isAnnotation());
Field[] declaredFields = annotateClass.getDeclaredFields();
for(Field field : declaredFields){
// PersonAnnotationDemo类中的成员变量是否有 PersonAnnotation注解
if (field.isAnnotationPresent(PersonAnnotation.class)){
// 获取成员变量中 单个PersonAnnotation注解
PersonAnnotation annotation = field.getAnnotation(PersonAnnotation.class);
// 获取 多个PersonAnnotation注解
// PersonAnnotation[] annotationsByType = field.getAnnotationsByType(PersonAnnotation.class);
/**
* 相类似的获取注解方法:getDeclaredAnnotation、getDeclaredAnnotationsByType、getAnnotations、getDeclaredAnnotations
*/
// 输出注解中的值
System.out.println("输出注解中的值:"+field.getName()+"="+annotation.name());
// 将注解的值 赋值 到PersonAnnotationDemo对象对应字段
field.setAccessible(true);//私有字段需要忽略修饰符
field.set(o,annotation.name());
}
}
// 输出:注解的值 赋值给 对象
System.out.println(o.toString());
}
}
3.反射的应用
3.1.实战一:通过配置文件解耦类和反射
public class BigBanana {
public void printBigBanana(String color){
System.out.println("Do you like "+color+" BigBanana?");
}
}
bigbanana.class.name=com.yty.fs.BigBanana
bigbanana.class.method=printBigBanana
bigbanana.class.method.param=java.lang.String
public class TestBigBanana {
private static Properties properties;
// 通过Key 获取配置文件value 值
public static String getProperty(String key) throws IOException {
if (properties == null){
properties = new Properties();
FileReader fileReader = new FileReader(new File("properties.properties"));
properties.load(fileReader);
}
return properties.getProperty(key);
}
// 测试
public static void main(String[] args) throws Exception {
Class> aClass = Class.forName(getProperty("bigbanana.class.name"));
Object o = aClass.getConstructor().newInstance();
Class> paramClass = Class.forName(getProperty("bigbanana.class.method.param"));
Method method = aClass.getMethod(getProperty("bigbanana.class.method"),paramClass);
method.invoke(o,"yellow");
}
}
3.2.实战二:代理卖手机--JDK动态代理
3.2.1.简单理解代理模式
3.2.2.手机接口
public interface Phone {
void sellPhone();
}
3.2.3.三款手机类
public class Huawei implements Phone {
// 型号
private String phoneModelName;
public Huawei(String phoneModelName){
this.phoneModelName=phoneModelName;
}
@Override
public void sellPhone() {
System.out.println("卖Huawei "+this.phoneModelName+" 的手机");
}
}
public class Xiaomi implements Phone{
// 型号
private String phoneModelName;
public Xiaomi(String phoneModelName){
this.phoneModelName=phoneModelName;
}
public Xiaomi(){
}
@Override
public void sellPhone() {
System.out.println("卖Xiaomi "+this.phoneModelName+" 的手机");
}
}
public class IPhone implements Phone {
// 型号
private String phoneModelName;
public IPhone(String phoneModelName){
this.phoneModelName=phoneModelName;
}
@Override
public void sellPhone() {
System.out.println("卖IPhone "+this.phoneModelName+" 的手机");
}
}
3.2.4.Invocation 实现类
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/**
* 代理前可以考虑做些跟有趣的事
*/
System.out.println("代理方法--start--代理前可以考虑做些跟有趣的事");
Object invoke = method.invoke(target, args);
/**
* 代理后可能你有更想要做的事
*/
System.out.println("代理方法--end--代理后可能你有更想要做的事n");
return invoke;
}
}
3.2.5.代理商类
public class MyProxy {
public static Object getProxy(Object target){
MyInvocationHandler myInvocationHandler = new MyInvocationHandler(target);
Object proxyInstance = Proxy.newProxyInstance(Phone.class.getClassLoader(), new Class[]{Phone.class}, myInvocationHandler);
return proxyInstance;
}
}
3.2.6.测试类
public class Test {
public static void main(String[] args) {
// 不管你想要买什么手机,只要通过同一个代理商就可以买到
Phone huawei = (Phone) MyProxy.getProxy(new Huawei("Huawei 16 pro"));
Phone xiaomi = (Phone) MyProxy.getProxy(new Xiaomi("MI 13 pro"));
Phone iphone = (Phone) MyProxy.getProxy(new IPhone("IPhone 13 pro"));
huawei.sellPhone();
xiaomi.sellPhone();
iphone.sellPhone();
}
}
4.反射与泛型的简单实战
4.1.实战一:泛型方法和反射的结合
public class PrintResult {
private void printSuccessInfo(){
System.out.println("printSuccessInfo--执行成功");
}
private void printAdd(int[] ints){
int n=0;
for (int i :ints){
n=n+i;
}
System.out.println("求和结果="+n);
}
}
public class Demo {
// 执行指定类型的方法
public
4.2.实战二:通过反射越过泛型检查
public class Demo2 {
public static void main(String[] args) throws Exception{
List
参与评论
手机查看
返回顶部