1. 反射和动态代理

参考博文:https://blog.csdn.net/sinat_38259539/article/details/71799078

1.0 什么是Class:

  我们都知道,对象可以用类来描述,但是类应该用什么来描述呢。类描述对象是将对象的公共部分抽离出来。同理,描述类的话也是讲类中公共的部分抽离出来这个用来描述类的事物叫做Class(实质也是一个类),如下图

1.1  反射(reflect)概述

  java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取信息以及动态调用对象的方法的功能称为java语言的反射机制。

   要想解剖一个类,必须先要获取到该类的字节码文件的对象。而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象。

如下图是类的正常加载过程:反射的原理在于Class对象,Class对象的由来是将class文件读入内存,并为之创建一个Class对象

反射的通俗理解:反射是先得到某个类的Class对象(包括了某个类中的所有信息),进而通过Class对象获知到该类中包含的所有信息,反过来,我们就能将这个类的对象创建出来,从而得到这个对象的信息

由上可知,反射的必要条件是获取一个Class对象,那么怎么获取Class对象呢?

1.2 反射的使用

Person类(以下例子中Persn结为此)

public class Person extends SuperPerson {
public String name;
public int age;
private char gender;
public Person() { }
private Person(int age) {
this.age = age;
}
protected Person(String name) {
this.name = name;
}
Person(String name,int age){
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", gender=" + gender + "]";
} public void show() {
System.out.println("show");
} private int getSum(int a,int b) {
return a+b;
}
protected void test() {
System.out.println("test");
} }
class SuperPerson{
public String a;
public int b;
public void haha() {
System.out.println("哈哈");
}
} interface A{} interface B{}

1.2.1. 获取Class对象(字节码文件,即.class文件)的三种方式  

(1)第一种:Object------>getClass()

(2)任何数据类型(包括基本数据类型)都有一个“静态”的class属性======》 类名.class

(3)通过Class类的静态方法:forName(String className)    此方法最常用

案例:

public class ReflectDemo {
public static void main(String[] args) {
// 第一种
Person p = new Person();
Class<? extends Person> class1 = p.getClass();
System.out.println(class1);//class com._51doit.javase.day20.Person // 第二种
Class<?> class2 = Person.class;
System.out.println(class2); // 第三种
try {
Class<?> class3 = Class.forName("com._51doit.javase.day20.Person");
System.out.println(class3);// class com._51doit.javase.day20.Person
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

由结果可知,Person类只有一个Class对象

1.2.2  获取方法

System.out.println(class1.getName());//获取全类名
System.out.println(class1.getSimpleName()); // 获取类名
System.out.println(class1.getPackage()); // 获取包名
System.out.println(class1.getSuperclass());// 获取父类的Class对象
System.out.println(class1.getInterfaces());//获取父接口的Class,得到的是数组(Class[]),一个类可以实现多个接口

1.2.3 反射获取构造方法

(1)public Constructor[] getConstructors(); 获取所有用public 修饰的构造方法
(2)public Constructor getConstructor(Class…args); 获取单个的用public 修饰构造方法
(3)public Constructor[] getDeclaredConstructors (); 获取所有的构造方法
(4)public Constructor getDeclaredConstructor (Class…args); 获取单个构造方法

案例(此处只写了第一种方法,其他方法是类似的)

public class ReflectDemo1 {
public static void main(String[] args) {
try {
//1. 获取Class对象
Class clazz = Class.forName("com._51doit.javase.day20.reflect.Person");
//2. 利用Class对象获取构造方法
Constructor[] cs = clazz.getConstructors(); //只能获取类中用public修饰的构造方法
for (Constructor constructor : cs) {
System.out.println(constructor);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}

运行结果:

得到两个用public修饰的构造方法

1.2.4 使用构造方法创建对象

格式:构造方法.newInstance();  根据构造方法传参

这里需要注意下私有构造方法创建对象

public class ReflectDemo2 {
public static void main(String[] args) {
try {
//1 使用构造方法创建对象
Class<?> clazz = Class.forName("com._51doit.javase.day20.reflect.Person");
Constructor<?> c = clazz.getDeclaredConstructor(String.class);
Object o = c.newInstance("张三");
System.out.println(o);//Person [name=张三, age=0, gender= //2 使用私有构造方法创建对象
Constructor c1 = clazz.getDeclaredConstructor(int.class);// 默认是无法访问私有的构造方法的
c1.setAccessible(true);
Object o1 = c1.newInstance(18);
System.out.println(o1); // Person [name=null, age=18, gender= } catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

1.2.5 反射获取成员变量

Field[] fs       getFields()                // 得到所有用public修饰的变量(包括父类中变量)
Field[] fs getDeclaredFields()       // 得到类中所有的变量(成员,静态),不包括父类中的变量
Field getField(Class....args)    // 只能获得单个用public修饰的变量
Field getDeclaredField(Class...args) // 获得单个变量(成员,静态),不包括父类中的变量

案例(只列出了一种,其他类似)

public class ReflectDemo3 {
public static void main(String[] args) {
try {
Class<?> clazz = Class.forName("com._51doit.javase.day20.reflect.Person");
Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.println(field);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

运行结果:

1.2.6 反射给变量赋值

public class ReflectDemo3 {
public static void main(String[] args) {
try {
Class<?> clazz = Class.forName("com._51doit.javase.day20.reflect.Person");
// 获取具体的某一个变量
Field f = clazz.getField("name");
System.out.println(f);
Field f1 = clazz.getDeclaredField("age");
System.out.println(f1);
// 给变量赋值,先要创建一个对象
Object o = clazz.getDeclaredConstructor(String.class,int.class).newInstance("张三",20);
f.set(o, "老王");
f1.set(o, 22);
System.out.println(o);
// 给私有变量赋值
Field f2 = clazz.getDeclaredField("gender");
f2.setAccessible(true);
f2.set(o,'男');
System.out.println(o);
} catch (Exception e) {
e.printStackTrace();
}
}
}

运行结果:

1.2.7 反射获取成员方法

(1)Method[]      getMethods()              //得到本类中和父类中所有用public修饰的方法
(2)Method [] getDeclaredMethods() //得到本类中所有的方法
(3)Method getMethod() //得到本类或父类中某一个用public修饰的方法
(4)Method getDeclaredMethod() //得到类(父类也行)中某一个方法(可以是私有方法)

案例

1.

public class ReflectDemo4 {
public static void main(String[] args) {
try {
Class<?> clazz = Class.forName("com._51doit.javase.day20.reflect.Person");
Method[] m = clazz.getMethods();
for (Method method : m) {
System.out.println(method);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}

运行结果(可知,得到本类中和父类中所有用public修饰的方法):

2.

Method[] m1 = clazz.getDeclaredMethods();
for (Method method : m1) {
System.out.println(method);

运行结果:

3

// 得到本类中的show方法
Method m2 = clazz.getMethod("show");
// 得到父类中的haha方法
Method m3 = clazz.getMethod("haha");
System.out.println(m2);
System.out.println(m3);

运行结果

4

// 获取私有方法getSum
Method m4 = clazz.getDeclaredMethod("getSum",int.class,int.class);
// 获取父类方法haha
Method m5 = clazz.getDeclaredMethod("haha");
System.out.println(m4);
System.out.println(m5);

1.2.8 方法的调用

格式:

  方法.invoke(对象,参数)

// 调用方法:成员需有对象
Object o = clazz.getConstructor().newInstance();
// 用o对象调用m2这个方法,没有参数
m2.invoke(o); // show
// 调用有参数,也有返回值的私有方法
m4.setAccessible(true);
Object re = m4.invoke(o, 11,20);
System.out.println(re); // 31

练习:给你一个ArrayList<String>的一个对象,我想在这个集合中添加一个整数型数据,如何实现呢?

public class ExerDemo1 {
public static void main(String[] args) {
ArrayList<String> array = new ArrayList<>();
array.add("你好,明天");
try {
//1 获取Class对象(还可以通过array.getClass()获取Class对象)
Class<?> clazz = Class.forName("java.util.ArrayList");
//2 得到add方法,参数为泛型的类型,在运行中参数泛型被擦除掉,变为默认的Object类型
Method m = clazz.getMethod("add",Object.class);
//3. 调用add方法,并传参
m.invoke(array , 100);
System.out.println(array);
} catch (Exception e) {
e.printStackTrace();
}
}
}

1.3 反射中配置文件的使用

需求,原来有个Teacher类,想在想升级一下,将Teacher改为superTeacher,直接点的方法就是改程序的代码,将Teacher类改成super类,但这样不利于程序的维护,这个时候就可以通过修改配置文件并且不修改原程序代码的方式达到这种需求,利于程序的维护。,如下图

Teachable接口

public interface Teachable {
public void teach();
}

Teacher类

public class Teacher implements Teachable {
public void teach() {
System.out.println("教学的很水。。。。。");
}
}

SuperTeacher类

public class SuperTeacher implements Teachable {
public void teach() {
System.out.println("教的非常好。。。。");
}
}

测试类

public class TeacherTest {
public static void main(String[] args) {
try {
//1 创建一个Properties对象
Properties p = new Properties();
//2 使用p对象,加载配置文件
p.load(TeacherTest.class.getClassLoader().getResourceAsStream("config.properties"));
//3 使用p对象获取配置文件中的内容
String value = p.getProperty("className"); //4 获取需要修改成类的Class对象
Class<?> clazz = Class.forName(value);
//此处的o就是Teacher类(Teacher,SuperTeacher,此处不是指数据类型,所以需要用Object类型来接收),所以下面才能将之向上转型为Teachable
Object o = clazz.getConstructor().newInstance();
// 此处o转成Teachable接口比较好,这样就实现了多态的调用
Teachable t = (Teachable) o;
t.teach();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

配置文件中就可以通过配置全类名,用来获取其Class对象,若配置成Teacher类的全类名,执行的就是Teacher类中的功能,置成SuperTeacher类的全类名,执行的就是SuperTeacher类中的功能,这样程序维护就很方便

上面的例子中的全类名配置的是SuperTeacher类,最终运行结果为:教的非常好

零基础学习java------20---------反射的更多相关文章

  1. 音乐出身的妹纸,零基础学习JAVA靠谱么

    问:表示音乐出身的妹纸一枚  某一天突然觉得身边认识的是一群程序员   突然想 要不要也去试试... 众好友都觉得我该去做个老师,可是我怕我会误人子弟,祸害祖国下一代..... 要不要 要不要 学Ja ...

  2. 总结了零基础学习Java编程语言的几个基础知识要点

    很多Java编程初学者在刚接触Java语言程序的时候,不知道该学习掌握哪些必要的基础知识.本文总结了零基础学习Java编程语言的几个基础知识要点. 1先了解什么是Java的四个方面   初学者先弄清这 ...

  3. Android零基础入门第20节:CheckBox和RadioButton使用大全

    原文:Android零基础入门第20节:CheckBox和RadioButton使用大全 本期先来学习Button的两个子控件,无论是单选还是复选,在实际开发中都是使用的较多的控件,相信通过本期的学习 ...

  4. 零基础学Java第四节(字符串相关类)

    本篇文章是<零基础学Java>专栏的第四篇文章,文章采用通俗易懂的文字.图示及代码实战,从零基础开始带大家走上高薪之路! String 本文章首发于公众号[编程攻略] 在Java中,我们经 ...

  5. 零基础学Java第一节(语法格式、数据类型)

    本篇文章是<零基础学Java>专栏的第一篇文章,从本篇文章开始,将会连更本专栏,带领大家将Java基础知识彻底学懂,文章采用通俗易懂的文字.图示及代码实战,从零基础开始带大家走上高薪之路! ...

  6. 【零基础学习iOS开发】【转载】

    原文地址:http://www.cnblogs.com/mjios/archive/2013/04/24/3039357.html 本文目录 一.什么是iOS 二.主流手机操作系统 三.什么是iOS开 ...

  7. 李洪强iOS开发之【零基础学习iOS开发】【01-前言】01-开篇

    从今天开始,我就开始更新[零基础学习iOS开发]这个专题.不管你是否涉足过IT领域,也不管你是理科生还是文科生,只要你对iOS开发感兴趣,都可以来阅读此专题.我尽量以通俗易懂的语言,让每个人都能够看懂 ...

  8. 零基础学习hadoop到上手工作线路指导

    零基础学习hadoop,没有想象的那么困难,也没有想象的那么容易.在刚接触云计算,曾经想过培训,但是培训机构的选择就让我很纠结.所以索性就自己学习了.整个过程整理一下,给大家参考,欢迎讨论,共同学习. ...

  9. MongoDB实战开发 【零基础学习,附完整Asp.net示例】

    MongoDB实战开发 [零基础学习,附完整Asp.net示例] 阅读目录 开始 下载MongoDB,并启动它 在C#使用MongoDB 重构(简化)代码 使用MongoDB的客户端查看数据 使用Mo ...

  10. 【零基础学习iOS开发】【01-前言】01-开篇

    本文目录 一.什么是iOS 二.主流手机操作系统 三.什么是iOS开发 四.学习iOS开发的目的 五.学习iOS开发的前提 从今天开始,我就开始更新[零基础学习iOS开发]这个专题.不管你是否涉足过I ...

随机推荐

  1. 最近公共祖先 牛客网 程序员面试金典 C++ Python

    最近公共祖先 牛客网 程序员面试金典 C++ Python 题目描述 有一棵无穷大的满二叉树,其结点按根结点一层一层地从左往右依次编号,根结点编号为1.现在有两个结点a,b.请设计一个算法,求出a和b ...

  2. Luogu P4390 [BOI2007]Mokia 摩基亚 | CDQ分治

    题目链接 $CDQ$分治. 考虑此时在区间$[l,r]$中,要计算$[l,mid]$中的操作对$[mid+1,r]$中的询问的影响. 计算时,排序加上树状数组即可. 然后再递归处理$[l,mid]$和 ...

  3. Swift-技巧(一)缩放并填充图片

    摘要 直接操作图片来实现它的缩放或者填充多余空间,首选 UIGraphicsBeginImageContext 函数来实现,它就相当于一个画布,你甚至可以用它来涂鸦. 最近有一个需求,就是将图片先等比 ...

  4. 一步一步学ROP之linux_x64篇(蒸米spark)

    目录 一步一步学ROP之linux_x64篇(蒸米spark) 0x00 序 0x01 Memory Leak & DynELF - 在不获取目标libc.so的情况下进行ROP攻击 0x02 ...

  5. Spark面试题(四)

    1.Spark中的HashShufle的有哪些不足? 1)shuffle产生海量的小文件在磁盘上,此时会产生大量耗时的.低效的IO操作: 2)容易导致内存不够用,由于内存需要保存海量的文件操作句柄和临 ...

  6. js 实现边缘撞击检测动画

    js 实现边缘撞击检测动画 <!DOCTYPE html> <html lang="en"> <head> <meta charset=& ...

  7. 编译静态库的方式使用spdlog和fmt

    前言 spdlog++库,而且支持header only方式,但header only的使用方式会造成编译时长增加,所以这里简单描述一下,其编译静态库的方式. 又因为spdlog还依赖另一个开源库fm ...

  8. redis sentinel搭建

    /usr/local/bin /usr/local/etc https://www.centos.bz/2017/08/redis-3-x-sentinel-ha-service/ https://w ...

  9. C#中base 和this

    [意义] this:指当前类,this调用当前类的属性,方法,包括构造函数的方法,继承本类的构造函数 base:指当前类的父类,可调用父类的非私有属性,方法,继承父类的构造函数括号里的参数 [用处] ...

  10. Nginx面试题(总结最全面的面试题!!!)

    https://blog.csdn.net/weixin_43122090/article/details/105461971