零基础学习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 ...
随机推荐
- binary-tree-preorder-traversal leetcode C++
Given a binary tree, return the preorder traversal of its nodes' values. For example: Given binary t ...
- Spring Boot 快速整合Swagger
一.前言 Spring Boot作为当前最为流行的Java web开发脚手架,越来越多的开发者选择用其来构建企业级的RESTFul API接口.这些接口不但会服务于传统的web端(b/s),也会服务于 ...
- C++ substr 的两个用法
substr是C++语言函数,主要功能是复制子字符串,要求从指定位置开始,并具有指定的长度. basic_string substr(size_type _Off = 0,size_type _C ...
- Swift-Framework Error(一)桥接文件
摘要 Xcode 编译工程代码时,出现编译错误时除了红色图标外,还会附送几句英文文本. 常规操作拷贝英文文本,放到搜索框中找答案,但是读懂这几句话能事半功倍. 项目中如果有 OC 和 Swift 两种 ...
- ofd文件电子签章实现方法
前言 文档处理一般经过三个环节:流.版.签:流式软件负责编辑,如:office.wps等.版式软件负责文档定型,保证显示样式不跑偏:版式文件格式有两种:pdf.ofd.签章软件负责对版式文档签章.签章 ...
- 大一C语言学习笔记(1)---编译顺序问题;不同数据类型赋值,运算问题;算数运算符易错点(以解一元二次方程为例)
废话少说,上代码: #include<stdio.h> #include<math.h> int main()//解一元二次方程 { int a,b,c; double too ...
- webpack 项目接入Vite的通用方案介绍(上)
愿景 希望通过本文,能给读者提供一个存/增量项目接入Vite的点子,起抛砖引玉的作用,减少这方面能力的建设成本 在阐述过程中同时也会逐渐完善webpack-vite-serve这个工具 读者可直接fo ...
- More Effective C++笔记(一)(精心整理)
一.基础议题 条款1:仔细区别pointers和references 指针使用*和->,引用使用"." 引用必须指向一个已初始化的对象,不能为null,而指针可以指向某个对象 ...
- .net工程师学习vue的心路历程(二)
本章主要搞懂在通过vue init webpack projectname 命令创建 vue 项目过程中有个选择.即关于如何选择:runtime+compiler和runtime+only. 现在我通 ...
- MySQL用limit代替SQL Server :top
mysql 中不支持top,而是用limit代替 若要查询前10条记录,mysql用limit 10 LIMIT可以实现top N查询,也可以实现M至N(某一段)的记录查询,具体语法如下: SELEC ...