零基础学习java------20---------反射
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---------反射的更多相关文章
- 音乐出身的妹纸,零基础学习JAVA靠谱么
问:表示音乐出身的妹纸一枚 某一天突然觉得身边认识的是一群程序员 突然想 要不要也去试试... 众好友都觉得我该去做个老师,可是我怕我会误人子弟,祸害祖国下一代..... 要不要 要不要 学Ja ...
- 总结了零基础学习Java编程语言的几个基础知识要点
很多Java编程初学者在刚接触Java语言程序的时候,不知道该学习掌握哪些必要的基础知识.本文总结了零基础学习Java编程语言的几个基础知识要点. 1先了解什么是Java的四个方面 初学者先弄清这 ...
- Android零基础入门第20节:CheckBox和RadioButton使用大全
原文:Android零基础入门第20节:CheckBox和RadioButton使用大全 本期先来学习Button的两个子控件,无论是单选还是复选,在实际开发中都是使用的较多的控件,相信通过本期的学习 ...
- 零基础学Java第四节(字符串相关类)
本篇文章是<零基础学Java>专栏的第四篇文章,文章采用通俗易懂的文字.图示及代码实战,从零基础开始带大家走上高薪之路! String 本文章首发于公众号[编程攻略] 在Java中,我们经 ...
- 零基础学Java第一节(语法格式、数据类型)
本篇文章是<零基础学Java>专栏的第一篇文章,从本篇文章开始,将会连更本专栏,带领大家将Java基础知识彻底学懂,文章采用通俗易懂的文字.图示及代码实战,从零基础开始带大家走上高薪之路! ...
- 【零基础学习iOS开发】【转载】
原文地址:http://www.cnblogs.com/mjios/archive/2013/04/24/3039357.html 本文目录 一.什么是iOS 二.主流手机操作系统 三.什么是iOS开 ...
- 李洪强iOS开发之【零基础学习iOS开发】【01-前言】01-开篇
从今天开始,我就开始更新[零基础学习iOS开发]这个专题.不管你是否涉足过IT领域,也不管你是理科生还是文科生,只要你对iOS开发感兴趣,都可以来阅读此专题.我尽量以通俗易懂的语言,让每个人都能够看懂 ...
- 零基础学习hadoop到上手工作线路指导
零基础学习hadoop,没有想象的那么困难,也没有想象的那么容易.在刚接触云计算,曾经想过培训,但是培训机构的选择就让我很纠结.所以索性就自己学习了.整个过程整理一下,给大家参考,欢迎讨论,共同学习. ...
- MongoDB实战开发 【零基础学习,附完整Asp.net示例】
MongoDB实战开发 [零基础学习,附完整Asp.net示例] 阅读目录 开始 下载MongoDB,并启动它 在C#使用MongoDB 重构(简化)代码 使用MongoDB的客户端查看数据 使用Mo ...
- 【零基础学习iOS开发】【01-前言】01-开篇
本文目录 一.什么是iOS 二.主流手机操作系统 三.什么是iOS开发 四.学习iOS开发的目的 五.学习iOS开发的前提 从今天开始,我就开始更新[零基础学习iOS开发]这个专题.不管你是否涉足过I ...
随机推荐
- songwenxin
# -*- coding: utf-8 -*- import wx from modelmngr_frame import MyFrame1 ############################# ...
- 理解ASP.NET Core - 文件服务器(File Server)
注:本文隶属于<理解ASP.NET Core>系列文章,请查看置顶博客或点击此处查看全文目录 提供静态文件 静态文件默认存放在 Web根目录(Web Root) 中,路径为 项目根目录(C ...
- 【Docker】Maven打包SpringBoot项目成Docker镜像并上传到Harbor仓库(Eclipse、STS、IDEA、Maven通用)
写在前面 最近,在研究如何使用Maven将SpringBoot项目打包成Docker镜像并发布到Harbor仓库,网上翻阅了很多博客和资料,发现大部分都是在复制粘贴别人的东西,没有经过实践的检验,根本 ...
- virt-v2v命令将ESXI 虚机迁移到OpenStack中
一简介: virt-v2v是将外部的虚拟化平台上的虚拟机转化到可以运行的KVM平台上.它可以读取在VMware.Xen运行Hyper-V和其他虚拟机管理程序上的Windows和Linux的虚拟机,并将 ...
- SQL*Loader-704: Internal error: ulconnect: OCIServerAttach [0] ORA-12541: TNS:no listener
使用/app/oracle/product/11.2.0/bin/sqlldr导入数据报错: 监听没有开启?检查发现监正常 猜测是监听端口不是默认的1521有关系,直接在sid里面加上数据库服务器的i ...
- vue禁用浏览器回退
解决方案 mounted() { history.pushState(null, null, document.URL) window.addEventListener('popstate', () ...
- idea Mark Directory as 的几种文件类型
1. Source roots (or source folders) 源文件夹 通过为该类别分配文件夹,可以告诉IntelliJ IDEA该文件夹及其子文件夹包含应在构建过程中进行编译的源代码. 2 ...
- PTA甲级1094 The Largest Generation (25分)
PTA甲级1094 The Largest Generation (25分) A family hierarchy is usually presented by a pedigree tree wh ...
- python连接集群mongodb,封装增删改查
1.下载pymongo pip install pymongo 2.直接上代码 [ini配置文件] 封装读ini省略~~ [db.py] class Database(): def __init__( ...
- 大爽Python入门教程 0-4 安装Pycharm
大爽Python入门公开课教案 点击查看教程总目录 安装重量级IDE--Pycharm 一 下载 下面步骤1,2中网络卡顿的朋友, 请直接前往步骤3来下载. 使用搜索引擎搜索Pycharm, 打开搜索 ...