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. Ubuntu Python2 和 Python3 共存 切换

    例如 你写了代码 创建一个文件 在终端 vim test.py 然后写入代码 print "hello world" 接着运行代码 python test.py 会输出 hello ...

  2. POJ 1442 Air Raid(DAG图的最小路径覆盖)

    题意: 有一个城镇,它的所有街道都是单行(即有向)的,并且每条街道都是和两个路口相连.同时已知街道不会形成回路. 可以在任意一个路口放置一个伞兵,这个伞兵会顺着街道走,依次经过若干个路口. 问最少需要 ...

  3. 转移指令原理和Inline Hook

    目录 转移指令原理和Inline Hook 转移指令 操作符offset jmp指令 根据位移进行转移的jmp指令 插播HOOK知识 Inline Hook Inline Hook 原理 Hook代码 ...

  4. 五分钟,让你明白MySQL是怎么选择索引《死磕MySQL系列 六》

    系列文章 二.一生挚友redo log.binlog<死磕MySQL系列 二> 三.MySQL强人"锁"难<死磕MySQL系列 三> 四.S 锁与 X 锁的 ...

  5. ELK集群之logstash(5)

    Logstash工作原理   Logstash事件处理有三个阶段:inputs → filters → outputs.是一个接收,处理,转发日志的工具.支持系统日志,webserver日志,错误日志 ...

  6. Linux usb 5. usbip (USB Over IP) 使用实例

    文章目录 0. 简介 1. Server 配置 2. Client 配置 参考资料 0. 简介 USB Over IP 是一种应用很多的场景,目前已经有现成的解决方案 usbip.linux 和 wi ...

  7. Linux usb 3. Host 详解

    文章目录 1. 简介 2. Usb Core 驱动设备模型 2.1 Usb Device Layer 2.1.1 device (struct usb_device) 2.1.2 driver (st ...

  8. docker file 笔记

    FROM    # FROM scratch,  FROM centos, FROM ubuntu:latest LABEL RUN  # 每运行一次RUN,image都会生成新的一层,为了美观,避免 ...

  9. C#中OnLoad事件和Form1_Load事件的区别

    在学习<GDI+高级编程>第二章的过程中遇到一个疑问,就是为何有的代码用的是覆写一个OnLoad事件,而平日里我用的一般是Form1_Load事件,这两个函数很相近,但是具体有什么关系呢? ...

  10. FZU ICPC 2020 寒假训练 6 —— 字符串处理

    P1603 斯诺登的密码 题目描述 2013年X月X日,俄罗斯办理了斯诺登的护照,于是他混迹于一架开往委内瑞拉的飞机.但是,这件事情太不周密了,因为FBI的间谍早已获悉他的具体位置--但这不是最重要的 ...