目录
在学习之前,先牢记几个常用的英语单词代表的意思:
(1)class:类
(2)Method:普通的方法
(3)Constructor:构造方法
(4)Field:属性
(5)Modifiers:修饰符列表
(6)Type:修饰的类型
(7)Name:名字(8)ReturnType:返回值值类型
(9)parameterType:参数的类型
首先调用Class.forName("完整类名"),获取整个类;对于类也可以调用getName()方法获取完整类名,或者调用getSimpleName()方法获取简单类名。
第一种:调用getFields()方法,获取类中所有的public(公开的)修饰的Field(属性),返回的是一个Field数组;然后再调用getName()方法,得到属性的名字!
第二种:调用getDeclaredFields()方法,获取所有的Field(属性),得到一个Field数组,遍历这个数组,拿到每一个属性,调用对应的方法就可以得到每一个属性的:属性的修饰符列表(Modifiers)、属性的类型(Type)、属性的名字(Name)
(1)调用getModifiers()方法,返回的是一个代号,是int类型;在调用Modifier.toString()静态方法,把int值传进去,就可以转换成字符串;得到属性的修饰符列表
(2)调用getType()方法返回的是一个Class类型,然后在调用getSimpleName()方法得到String类型,得到属性的类型
(3)直接调用getName()方法,返回一个String类型,就能得到属性的名字
- package com.bjpowernode.java.reflect;
-
- import java.lang.reflect.Field;
- import java.lang.reflect.Modifier;
-
- // 反射Student类中的所有Field
- public class ReflectTest06 {
- public static void main(String[] args) throws Exception{
- // 获取整个类
- Class studentClass = Class.forName("com.bjpowernode.java.reflect.Student");
-
- // 补充:studentClass也有getName()方法
- String studentName = studentClass.getName();
- System.out.println("完整类名:"+studentName); // com.bjpowernode.java.reflect.Student
- String simpleName = studentClass.getSimpleName();
- System.out.println("简单类名"+simpleName); // Student
-
- // 1、 获取类中所有的public(公开的)修饰的Field(属性)
- Field[] fields = studentClass.getFields();
- System.out.println(fields.length); //1 测试数组中只有1个元素
- // 取出这个Field
- Field field = fields[0];
- // 取出这个Field的名字
- String fieldName = field.getName();
- System.out.println(fieldName); // no(只有这个是public修饰的)
-
- // 2、获取所有的Field
- Field[] fields1 = studentClass.getDeclaredFields();
- System.out.println(fields1.length); // 4
- // 遍历
- for(Field field1 :fields1){
- // 2.1 获取属性的修饰符列表
- // 用Modifiers()而不是Modifier,因为修饰符可能有多个
- // 返回的是一个整型数字,每个数字是修饰符的代号
- // 然后调用静态方法Modifier.toString(int) 把数字准换成对应的修饰符字符串
- int i = field1.getModifiers();
- String modifier = Modifier.toString(i);
- System.out.println(modifier);
-
- //2.2 获取属性的类型,field1.getType()返回的是一个Class
- Class fieldType = field1.getType();
- String fName = fieldType.getSimpleName();
- System.out.println(fName); // int String int boolean
-
- //2.3 获取属性的名字, field1.getName()返回的是一个String
- System.out.println(field1.getName()); //no name age sex
-
- }
-
- }
- }
-
- // 反射属性
- class Student{
- // 4个Field,分别采用了不同的访问控制权限修饰符
- public int no; // Field对象
- private String name; // Field对象
- protected int age;
- boolean sex;
- public static final double MATH_PI = 3.1415926; // 修饰符有多个
- }
通过反射机制,反编译一个类的属性Field;知道类的完整类名,就可以反编译该类的信息;主要运用的是字符串拼接的知识点。
- package com.bjpowernode.java.reflect;
- import java.lang.reflect.Field;
- import java.lang.reflect.Modifier;
-
- // 反编译Field
- public class ReflectTest07 {
- public static void main(String[] args) throws Exception {
- // 创建字符串拼接
- StringBuilder s = new StringBuilder();
-
- // 获取整个类
- Class studentClass = Class.forName("com.bjpowernode.java.reflect.Student");
-
- // 字符串拼接
- //s.append("public class Student{");
- // 采用动态获取的方式
- s.append(Modifier.toString(studentClass.getModifiers()) +
- " class " + studentClass.getSimpleName() + "{\n");
-
- // 获取所有的属性
- Field[] fields = studentClass.getDeclaredFields();
- for (Field field :fields){
- // 追加修饰符
- s.append("\t");
- s.append(Modifier.toString(field.getModifiers()));
- s.append(" ");
- // 追加类型
- s.append(field.getType().getSimpleName());
- s.append(" ");
- // 追加名字
- s.append(field.getName());
- s.append(";\n");
-
- }
-
- s.append("}");
-
- System.out.println(s);
-
- }
- }
-
(1)怎么通过反射机制访问一个java对象的属性?
①给属性赋值使用“属性.set(对象,值)”②获取属性的值使用“属性.get(对象)”。
(2)调用c.getDeclaredFields()无参方法,返回的是一个Field[] 数组;调用c.getDeclaredField(String name)有参方法,根据属性的名称拿到一个Field,返回的是一个指定字符串的Field属性;有了指定的属性,就可以调用get和set方法进行修改和取出值了
(3)对于private私有的属性也是可以取到并修改的,但是需要先调用setAccessible(true)方法打破封装,然后就可以调用了。
(4)反射机制的缺点:打破封装,在外部也是可以访问私有private修饰的变量;造成数据不安全
详细步骤:
第一步:通过类加载获取整个类,Class.forName("完整类型");
第二步:给属性赋值需要三要素:对象.属性 = 值;例如:s.no = 19;所以需要创建对象
第三步:调用getDeclaredField(属性名)方法,返回的是一个特定的Field属性对象
第四步:调用“Field属性对象.set(对象,值)”方法,完全符合属性赋值三要素,完成属性的赋值;调用“Field属性对象.get(对象)”完成取
补充:对于private修饰的属性,要想访问或者修改,必须调用“Field属性对象.setAccessible(true)”先打破封装!
- package com.bjpowernode.java.reflect;
-
- import java.lang.reflect.Field;
-
- public class ReflectTest08 {
- public static void main(String[] args) throws Exception {
- // 1. 不使用反射机制,怎么去访问一个对象的属性呢?
- Student s = new Student();
- // 2. 给属性赋值
- s.no = 1111;
- //三要素:给s对象的no属性赋值1111
- // 要素1:对象s 要素2:no属性 要素3:值1111
- // 3. 读属性值
- // 两个要素:获取s对象的no属性的值。
- System.out.println(s.no); // 对象.属性
-
-
- // 1.使用反射机制(set get)
- // 获取类
- Class studentClass = Class.forName("com.bjpowernode.java.reflect.Student");
- // 创建对象
- Object obj = studentClass.newInstance(); // Student对象
-
- // 2.获取no属性(根据属性的名称来获取Field)
- Field noField = studentClass.getDeclaredField("no");
- // 3.给obj对象(Student对象)的no属性赋值
- /*
- 虽然使用了反射机制,但是三要素还是缺一不可:
- 要素1:obj对象
- 要素2:no属性
- 要素3:2222值
- 注意:反射机制让代码复杂了,但是为了一个“灵活”,这也是值得的。
- */
- noField.set(obj,222);// 给obj对象的no属性赋值2222
- // 4. 读取属性的值
- // 两个要素:获取obj对象的no属性的值。
- System.out.println(noField.get(obj)); // 属性.get(对象)
-
-
- // 2.可以反问私有的属性name吗?(需要打破封装才可以访问)
- Field nameField = studentClass.getDeclaredField("name");
-
- // 打破封装(反射机制的缺点:打破封装,造成数据不安全)
- // 这样设置完之后,在外部也是可以访问私有private的。
- nameField.setAccessible(true);
-
- // 给name属性赋值
- nameField.set(obj,"zhangsan");
- // 获取name属性的值
- System.out.println(nameField.get(obj)); // zhangsan
-
- }
- }
注:对于static修饰的静态的属性访问我们不需要对象,是通过类来调用的;例如:nameField.set(Student.class,"zhangsan")。如果传一个null也是可以的,因为我们原本需要的就是一个对象,而静态方法的调用又不需要对象,是通过类,所以传null也可以!
(1)什么是可变长度参数?
例如:int... args 这就是可变长度参数
(2)语法是:类型... (注意:一定是3个点)①可变长度参数要求的参数个数是:0~N个。
②可变长度参数在参数列表中必须在最后一个位置上,而且可变长度参数只能有1个。
③可变长度参数可以当做一个数组来看待,有length属性。
- package com.bjpowernode.java.reflect;
-
- public class ArgsTest {
- public static void main(String[] args) {
- // 1.不带参数 一个参数 两个参数...都可以正常执行(参数可变)
- m();
- m(100);
- m(10,20);
-
- m2(100);
- m2(10,"abc");
-
- m3("abc","def","dd");
- // 里面也可以传一个数组进去
- String[] str = {"aaa","bb","c"};
- m3(str);
-
- }
- // 带有可变长度的方法
- public static void m(int... args){
- System.out.println("m方法执行了");
- }
-
- // 2.可变长度参数在参数列表中必须在最后一个位置上,而且可变长度参数只能有1个
- //public static void m2(String... args1,int... args2){}
- public static void m2(int a,String... args){
- System.out.println("m2方法执行");
- }
-
- // 3.可变长度参数可以当做一个数组来看待
- public static void m3(String... args){
- // args有length属性,说明args是一个数组
- for (int i = 0; i < args.length; i++) {
- System.out.println(args[i]);
- }
- }
-
- }
UserService类
- package com.bjpowernode.java.reflect;
- // 用户登录类
- public class UserService {
- /**
- * 登录方法
- * @param name 用户名
- * @param password 用户密码
- * @return 表示登录成功,false表示登录失败
- */
- public boolean login(String name,String password){
- if("admin".equals(name) && "123".equals(password)){
- return true;
- }
- return false;
- }
-
- /**
- * 退出系统的方法
- */
- public void logout(){
- System.out.println("退出系统");
- }
- }
反射Method代码和反射Field的方式很相似,有个别的区别:
(1)调用的是getDeclaredMethods()方法,而不是getDeclaredFields()
(2)返回的是一个Method数组,而不是Field数组
(3)获取类型,这里是返回值类型,调用的是getReturnType方法,而不是getType方法
(4)最终多了一项参数类型:调用的是getParameterTypes() ,返回的是一个Class数组,然后在调用getSimpleName()方法,就可以拿到对应的参数类型。
- package com.bjpowernode.java.reflect;
-
- import java.lang.reflect.Method;
- import java.lang.reflect.Modifier;
-
- public class ReflectMethod {
- public static void main(String[] args) throws Exception{
- // 获取类
- Class userServiceClass = Class.forName("com.bjpowernode.java.reflect.UserService");
- // 获取所有的Method
- Method[] methods = userServiceClass.getDeclaredMethods();
- for(Method method :methods){
- // 1.获取修饰符列表
- int i = method.getModifiers();
- System.out.println(Modifier.toString(i));
-
- // 2.获取方法的返回值类型
- Class classMethod = method.getReturnType();
- System.out.println(classMethod.getSimpleName());
-
- // 3.获取方法名
- System.out.println(method.getName()); //获取方法名:login logout
-
- // 4.获取方法的参数类型(方法的参数可能有多个)
- Class[] parameterTypes = method.getParameterTypes();
- for(Class parameterType : parameterTypes){
- System.out.println(parameterType.getSimpleName());
- }
-
- }
- }
-
- }
通过这种方式,可以反编译一个类的方法
- package com.bjpowernode.java.reflect;
-
- import java.lang.reflect.Method;
- import java.lang.reflect.Modifier;
-
- public class ReflectMethodTest02 {
- public static void main(String[] args) throws Exception {
- StringBuilder s = new StringBuilder();
- // 获取类
- Class userServiceClass = Class.forName("java.lang.String");
-
- // 追加
- //s.append("public class UserService{");
- // 采用动态获取的方式
- s.append(Modifier.toString(userServiceClass.getModifiers())+" class "+userServiceClass.getSimpleName()+ "{\n");
-
- // 获取方法
- Method[] methods = userServiceClass.getDeclaredMethods();
- for(Method method : methods){
- s.append("\t");
- // 追加修饰符
- s.append(Modifier.toString(method.getModifiers()));
- s.append(" ");
- // 追加类型
- s.append(method.getReturnType().getSimpleName());
- s.append(" ");
- // 追加方法名
- s.append(method.getName());
- // 追加参数类型
- s.append("(");
- Class[] parameterTypes = method.getParameterTypes();
- for (Class parameterType :parameterTypes){
- s.append(parameterType.getSimpleName());
- s.append(",");
- }
-
- // 最后会多一个 , 怎么删除?
- // 方法1:删除指定下标位置上的字符
- s.deleteCharAt(s.length()-1);
-
- s.append("){}");
- s.append("\n");
- }
-
- s.append("}");
-
- System.out.println(s);
- }
- }
重点:必须掌握,通过反射机制怎么调用一个对象的方法?
(1)通过反射机制访问对象的属性很相似,那是通过set和get方法进行修改和读取数据的;而通过反射机制调用方法,是通过invoke()方法进行传参调用的,用返回值作为数据的读出
(2)反射机制,让代码很具有通用性,可变化的内容都是写到配置文件当中,将来修改配置文件之后,创建的对象不一样了,调用的方法也不同了,但是java代码不需要做任何改动。这就是反射机制的魅力!
详细步骤:
第一步:通过类加载获取整个类,Class.forName("完整类型");
第二步:调用方法四要素:对象、方法、参数、返回值,例如:int i = s.sum(10,20);所以也需要创建对象;
第三步:调用getDeclaredMethod(“方法名”,参数1类型.class,参数2类型.class)返回的是一个特定的Method方法对象;
第四步:通过调用"Method方法对象.invoke(obj,参数1值,参数2值)"会得到一个返回值;完全满足调用方法四要素,可以正常完成方法的调用。
- package com.bjpowernode.java.reflect;
-
- import java.lang.reflect.Method;
-
- public class ReflectMethodTest03 {
- public static void main(String[] args) throws Exception {
- // 1.不使用反射机制调用方法
- UserService userService = new UserService();
- boolean login = userService.login("admin","123");
- System.out.println(login?"登录成功":"登录失败");
-
- // 2.使用反射机制来调用一个对象的方法
- // 获取Class
- Class userServiceClass = Class.forName("com.bjpowernode.java.reflect.UserService");
- // 创建对象
- Object obj = userServiceClass.newInstance();
- // 获取方法(通过方法名和参数区分方法)
- Method loginMethod = userServiceClass.
- getDeclaredMethod("login",String.class,String.class);
- // 调用方法
- /*
- 四要素:
- loginMethod方法
- obj对象
- "admin","123" 实参
- retValue 返回值
- */
- // invoke调用的意思
- Object retValue = loginMethod.invoke(obj,"admin","123");
- // obj这个对象invoke调用loginMethod方法,传参数
- System.out.println(retValue);
-
- }
- }
思考:通过调用newInstance()方法的情况进行实例化,默认是调用无参的构造器:要求首先无参的构造器要有,其次无参的构造器的访问权限要够(例如public);明显不够灵活。假设现在只有有参构造器,或者无参构造器的是private修饰的怎么办呢?
答:调用getDeclaredConstructor方法,参数相对于普通的方法调用而言不用指定方法名,因为构造方法的方法名是和类名相同的!传参就是调用有参构造器、不传参就是调用无参构造器,拿到构造器以后在调用newInstance方法进行对象的创建!对于private修饰的,还可以调用setAccessible(true)来打破封装!
构造方法
- package com.bjpowernode.java.reflect;
-
- public class Vip {
-
- int no;
- String name;
- String birth;
- boolean sex;
-
- public Vip() {
- }
-
- public Vip(int no) {
- this.no = no;
- }
-
- public Vip(int no, String name) {
- this.no = no;
- this.name = name;
- }
-
- public Vip(int no, String name, String birth) {
- this.no = no;
- this.name = name;
- this.birth = birth;
- }
-
- public Vip(int no, String name, String birth, boolean sex) {
- this.no = no;
- this.name = name;
- this.birth = birth;
- this.sex = sex;
- }
-
- // 重写一下toString
- @Override
- public String toString() {
- return "Vip{" +
- "no=" + no +
- ", name='" + name + '\'' +
- ", birth='" + birth + '\'' +
- ", sex=" + sex +
- '}';
- }
- }
反编译一个类的Constructor构造方法
- package com.bjpowernode.java.reflect;
-
-
- import java.lang.reflect.Constructor;
- import java.lang.reflect.Modifier;
-
- public class ReflectConstructorTest01 {
- public static void main(String[] args) throws Exception{
- StringBuilder s = new StringBuilder();
-
- Class vipClass = Class.forName("com.bjpowernode.java.reflect.Vip");
- //s.append("public class Vip {");
- // 采用动态方式获取
- s.append(Modifier.toString(vipClass.getModifiers())+" class "+vipClass.getSimpleName()+" {\n");
-
- // 拼接构造方法
- Constructor[] constructors = vipClass.getConstructors();
- for (Constructor constructor :constructors){
- s.append("\t");
- // 追加修饰符
- s.append(Modifier.toString(constructor.getModifiers()));
- s.append(" ");
- // 追加类名
- s.append(vipClass.getSimpleName());
- // 追加参数
- s.append("(");
- Class[] parameterTypes = constructor.getParameterTypes();
- for (Class paramaterType: parameterTypes){
- s.append(paramaterType.getSimpleName());
- s.append(",");
- }
- // 删除最后元素下标位置上的字符
- if(parameterTypes.length>0){
- s.deleteCharAt(s.length()-1);
- }
- s.append("){}\n");
- }
-
- s.append("}");
-
- System.out.println(s);
- }
- }
-
- /*
- public class Vip {
- public Vip(int,String,String,boolean){}
- public Vip(int,String,String){}
- public Vip(int,String){}
- public Vip(int){}
- public Vip(){}
- }*/
通过反射机制调用构造方法实例化java对象。(这个不是重点)
- package com.bjpowernode.java.reflect;
-
- import java.lang.reflect.Constructor;
-
- // 比上一个例子重要
- public class ReflectConstructorTest02 {
- public static void main(String[] args) throws Exception {
- //1. 不使用反射机制怎么创建对象
- Vip v1 = new Vip();
- Vip v2 = new Vip(110,"zhangsan","2022-10-11",true);
-
- //2. 使用反射机制
- Class c = Class.forName("com.bjpowernode.java.reflect.Vip");
- // 调用无参构造方法
- Object obj = c.newInstance();
- System.out.println(obj);
-
- // 调用有参构造方法( 区分构造方法只需要形参)
- // 第一步:先获取到这个有参数的构造方法
- Constructor con = c.getDeclaredConstructor(int.class,String.class,String.class,boolean.class);
- // 第二步:调用构造方法new对象、
- Object newobj = con.newInstance(220,"lisi","2022-10-10",true);
- System.out.println(newobj);
-
- // 调用无参构造方法,也可以先拿到构造方法
- Constructor con1 = c.getDeclaredConstructor(); // 什么都不传
- System.out.println(con1.newInstance());
-
- }
- }
一个类,怎么获取这个类的父类和已经实现了哪些接口(或者说是父类所拥有的接口)?
(1)调用getSuperclass()方法,可以获取当前类的父类。
(2)调用getInterfaces()方法,返回的是一个Class数组;遍历这个数组的每个对象调用getName()方法,可以获取当前类实现的所有接口或者说是当前类的父类所拥有的的所有的接口。
- package com.bjpowernode.java.reflect;
- /*
- 重点:给你一个类,怎么获取这个类的父类,已经实现了哪些接口?
- */
- public class ReflectTest09 {
- public static void main(String[] args) throws Exception{
- // String类举例
- Class stringClass = Class.forName("java.lang.String");
-
- // 获取String的父类
- Class superClass = stringClass.getSuperclass();
- System.out.println(superClass.getName()); // java.lang.Object
-
- // 获取String类实现的所有接口(一个类可以实现多个接口)
- Class[] interfaces = stringClass.getInterfaces();
- for (Class inter : interfaces){
- System.out.println(inter.getName());
- }
- }
- }