java进阶学习笔记
学习java深度学习,提升编程思维,适合掌握基础知识的工作者学习
1.反射和代理
1.1 概念介绍
在Java编程中,反射和代理是两个强大的特性,能够在运行时动态地操作和扩展类的行为。通过反射,我们可以在不知道类的具体信息的情况下操作类的属性和方法;而代理则允许我们创建一个代理类来拦截并增强目标对象的行为
1.2应用场景
像咱们平时大部分时候都是在写业务代码,很少会接触到直接使用反射机制的场景。
但是,这并不代表反射没有用。相反,正是因为反射,你才能这么轻松地使用各种框架。像 Spring/Spring Boot、MyBatis 等等框架中都大量使用了反射机制,掌握反射和代理机制,有利于我们了解这些框架的内核原理,提升编程思维
- xml的bean配置的注入
- AOP拦截
- 数据库事务
- springMVC
- Java注解
- 开发工具如IDEA,提供一个类的属性方法展示给我们智能快捷选择
这些内核都是反射和代理机制在其作用
1.3 反射-reflect
由于JVM为每个加载的class创建了对应的Class实例,并在实例中保存了该class的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段等,因此,如果获取了某个Class实例,我们就可以通过这个Class实例获取到该实例对应的class的所有信息。这种通过Class实例获取class信息的方法称为反射(Reflection)。
我们正常得到一个对象实例是通过new方法创建对象;反射就是通过class来实现,
1.获得类class
2.获得类的字段属性和方法
3.获得类的实例对象
4.获得对方法的调用
准备工作
Food.java
public class Food {
private Integer id;
private String name;
// ....省略get set 等
}
1.3.1 获得类-Class
获得类有三种方式,看代码演示
类.class
对象.getClass();
Class.forName(“类名”)
/***
* @Description: 得到Class
*/
@Test
public void t1() throws ClassNotFoundException {
Class cls=null;
//1.通过类名直接获得
cls= Food.class;
//2.通过实例获得
cls=new Food(1, "火锅").getClass();
//3.通过路径+类名的字符串获得
cls=Class.forName("com.jsoft.reflection.Food");
printClassInfo(cls);
}
/***
* @Description: 读取类的信息
*/
static void printClassInfo(Class cls) {
System.out.println("Class name: " + cls.getName());
System.out.println("Simple name: " + cls.getSimpleName());
if (cls.getPackage() != null) {
System.out.println("Package name: " + cls.getPackage().getName());
}
System.out.println("is interface: " + cls.isInterface());
System.out.println("is enum: " + cls.isEnum());
System.out.println("is array: " + cls.isArray());
System.out.println("is primitive: " + cls.isPrimitive());
}
打印效果
Class name: com.jsoft.reflection.Food
Simple name: Food
Package name: com.jsoft.reflection
is interface: false
is enum: false
is array: false
is primitive: false
1.3.2 获得类的字段-Field
演示代码
public class Father extends Man{
private Integer age;
public String job;
}
public class Man{
private Integer id;
private String name;
public String address;
}
对任意的一个Object实例,只要我们获取了它的Class,就可以获取它的一切信息。
我们先看看如何通过Class实例获取字段信息。Class类提供了以下几个方法来获取字段:
方法 | 说明 |
---|---|
Field getField(name) | 根据字段名获取某个public的field(包括父类) |
Field[] getFields() | 获取所有public的field(包括父类) |
Field getDeclaredField(name) | 根据字段名获取当前类的某个field(不包括父类) |
Field[] getDeclaredFields() | :获取当前类的所有field(不包括父类) |
注意:
Declared修饰就只管当前类字段,和private、public无关
否则就包含父类的public
测试代码
@Test
public void t2() throws NoSuchFieldException {
Class cls= Father.class;
//1.得到当前类的所有字段,不包含父类
Field[] fs=cls.getDeclaredFields();
System.out.println("1.得到当前类的所有字段,不包含父类");
for (Field f : fs) {
printFeild(f);
}
//2.得到当前类某个字段
System.out.println("2.得到当前类某个字段,不包含父类");
Field f=cls.getDeclaredField("job");
printFeild(f);
//3.得到当前类及父类的所有public字段
System.out.println("3.得到当前类及父类的所有public字段");
fs=cls.getFields();
for (Field f1 : fs) {
printFeild(f);
}
//4.得到当前类及父类的public字段
System.out.println("4.得到当前类及父类的public字段,如果是private,则要报错");
f=cls.getField("address");
printFeild(f);
}
/***
* @Description: 打印字段信息
*/
public void printFeild( Field f){
System.out.println("//------------字段信息 start");
System.out.println("字段名称:"+f.getName());
System.out.println("字段类型:"+f.getType().getName());
int m = f.getModifiers();
System.out.println("field is final:"+Modifier.isFinal(m));; // true
System.out.println("field is Public:"+Modifier.isPublic(m));; // true
System.out.println("field is Protected:"+Modifier.isProtected(m));; // true
System.out.println("field is Private:"+Modifier.isPrivate(m));; // true
System.out.println("field is Static:"+Modifier.isStatic(m));; // true
System.out.println("//------------字段信息 end");
}
1.3.3 动态访问和修改对象实例的字段
我们可以通过一个对象对应的class去动态访问或修改对象的字段
Field.set(vo对象,修改值) //修改字段值
Feild.get(vo对象) //获得字段值
Feild.setAccessible(true); //私有字段,需要设置这个允许访问,否则报异常
@Test
public void t3() throws NoSuchFieldException, IllegalAccessException {
Food food=new Food(1, "火锅");
System.out.println("food.getName:"+food.getName());
Class cls=food.getClass();
Field f=cls.getDeclaredField("name");
f.setAccessible(true); //不设置这个private 字段要抛出异常
//通过field获得值
Object name= f.get(food);
System.out.println("name:"+name);
//通过field设置值
f.set(food, "西北风");
System.out.println("name:"+food.getName());
}
1.3.4 获得类方法-Method
方法 | 说明 |
---|---|
Method getMethod(name, Class…) | 获取某个public的Method(包括父类) |
Method[] getMethods() | 获取所有public的Method(包括父类) |
Method getDeclaredMethod(name, Class…) | 获取当前类的某个Method(不包括父类) |
Method[] getDeclaredMethods() | :获取当前类的所有Method(不包括父类) |
示例代码
@Data
public class Father extends Man{
private Integer age;
public String job;
public String play(int type){
System.out.println("param value:"+type);
return type+"";
}
public String sleep(String type, Date date){
System.out.println("param value type:"+type+";date:"+date);
return type+"";
}
private void self(){
System.out.println("this is private method");
}
public void dd(){
System.out.println("没有参数方法()");
}
}
@Data
public class Man{
private Integer id;
private String name;
public String address;
@Override
public Food eat() {
return new Food(1,"火锅");
}
}
测试代码
@Test
public void t4() throws IllegalAccessException, NoSuchMethodException {
Class cls= Father.class;
System.out.println("1. 获取所有public的Method(包括父类)");
Method[] methods=cls.getMethods();
for (Method method : methods) {
printMethod(method);
}
System.out.println("2. 获取某个public的Method(包括父类)");
//第一个参数为方法名,第二参数是可变参数,传递方法的参数类型,如果无参数则不传递
Method m= cls.getMethod("getName");
printMethod(m);
//方法有1个参数
//注意int foat double这些基本类型对用的类是int.class,不是Integer.class
m=cls.getMethod("play", int.class);
printMethod(m);
//方法有多个参数
m=cls.getMethod("sleep", String.class, Date.class);
printMethod(m);
System.out.println("3. 获取当前类的的Method(不包括父类)");
m= cls.getDeclaredMethod("getJob");
printMethod(m);
//私有方法也可以
m= cls.getDeclaredMethod("self");
//父类方法不可以,要报java.lang.NoSuchMethodException
m= cls.getDeclaredMethod("eat");
printMethod(m);
printMethod(m);
}
public void printMethod(Method addMethod){
System.out.println("---------方法名称: 【" + addMethod.getName()+"】---相关属性------------");
System.out.println("修饰符: " + Modifier.toString(addMethod.getModifiers())); //public private等
System.out.println("返回值: " + addMethod.getReturnType()); //返回值类型的class数组
Class[] paramsCls= addMethod.getParameterTypes(); //参数值类型对应的class数组
}
1.3.5 调用方法.invoke
我们可以用Method.invoke(vo,参数)来调用方法
代码
@Test
public void t5() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Father father=new Father();
Class cls= father.getClass();
Method m=null;
//无参无返回方法
m=cls.getMethod("dd");
m.invoke(father);
//有参数,有返回值
m=cls.getMethod("sleep", String.class,Date.class);
String ret=(String)m.invoke(father, "1",new Date());
System.out.println("返回值:"+ret);
//private方法调用
m=cls.getDeclaredMethod("self");
//私有方法必须设置为m.setAccessible(true);
m.setAccessible(true);
m.invoke(father);
}
Method m =Father.class.getMethod(“dd”);
m.invoke(new Father());
其实就相当于
Father f=new Father();
f.dd();
1.3.6 构造函数
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!