Java反射机制笔记

后期学习的高级框架底层实现原理都采用了反射机制,学会反射机制有利于理解剖析框架底层的源代码,所以反射机制还是重要的。(比较简单,因为只要会查帮助文档,就可以了。)

一、反射机制概述

  1. 什么是反射机制?
    Java的反射机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。

  2. 反射机制有什么用?

    通过java语言中的反射机制可以操作字节码文件(class文件)。优点类似于黑客,可以读和修改字节码文件,可以操作代码片段,使程序更加灵活。

  3. 反射机制的相关类在哪个包下?

    java.lang.reflect.*;

  4. 反射机制相关的重要的类有哪些?

    java.lang.Class:代表整个字节码。代表整个类。
    java.lang.reflect.Method:代表字节码中的方法字节码。代表类中的方法。
    java.lang.reflect.Constructor:代表字节码中的构造方法字节码。代表类中的构造方法
    java.lang.reflect.Field:代表字节码中的属性字节码。代表类中的成员变量(静态变量+实例变量)。

    例如:

//java.lang.Class:
public class User{
		// Field
		int no;
				
		// Constructor
		public User(){		
				
		}
		public User(int no){
			this.no = no;
		}

		// Method
		public void setNo(int no){
			this.no = no;
		}
		public int getNo(){
			return no;
		}
}

二、Class类

获取Class实例的三种方法

要操作一个类的字节码,需要首先获取到这个类的字节码,怎么获取java.lang.Class实例?

  1. 第一种:Class c = Class.forName(“完整类名带包名”)
    Class.forName()
    1、静态方法
    2、方法的参数是一个字符串。
    3、字符串需要的是一个完整类名。
    4、完整类名必须带有包名。java.lang包也不能省略。

     例如:												
      Class c1 = Class.forName("java.lang.String");
    

    如果只想让一个类的“静态代码块”执行的话,应该怎么做?(重点)

    Class.forName(“该类的类名”);
    这样类就加载,类加载的时候,静态代码块执行!在这里,对该方法的返回值不感兴趣,主要是为了使用“类加载”这个动作。

  2. 第二种:Class c = 对象.getClass()
    java中任何一个对象都有一个方法:getClass()

     例如:												
     String s = "abc";
     Class c = s.getClass(); // c代表String.class字节码文件,c代表String类型。
    
  3. 第三种:Class c = 任何类型.class
    java语言中任何一种类型,包括基本数据类型,它都有.class属性。

     例如:												
     Class c = String.class; // c代表String类型
    

通过反射实例化对象

获取到Class,能干什么?
通过Class的newInstance()方法来实例化对象。
注意:newInstance()方法内部实际上调用了无参数构造方法,必须保证无参构造存在才可以。

// 通过反射机制,获取Class,通过Class来实例化对象
	Class c = Class.forName("com.bjpowernode.java.bean.User"); // c代表User类型。
// newInstance() 这个方法会调用User这个类的无参数构造方法,完成对象的创建。
// 重点是:newInstance()调用的是无参构造,必须保证无参构造是存在的!
	Object obj = c.newInstance();

更灵活的写法(读配置文件实例化对象)

反射机制的灵活性: java代码写一遍,在不改变java源代码的基础之上,可以做到不同对象的实例化,非常灵活。(符合OCP开闭原则:对扩展开放,对修改关闭。)

后期学习以及工作过程中,都是使用高级框架。而这些高级框架底层实现原理,都采用了反射机制,所以反射机制还是重要的。学会了反射机制有利于理解剖析框架底层的源代码。

// 以下代码是灵活的,代码不需要改动,可以修改配置文件,配置文件修改之后,可以创建出不同的实例对象。
        // 通过IO流读取classinfo.properties文件
        FileReader reader = new FileReader("chapter25/classinfo.properties");
		//配置文件classinfo.properties内容:
		//className=java.util.Date

        // 创建属性类对象Map
        Properties pro = new Properties(); // key value都是String
        // 加载
        pro.load(reader);
        // 关闭流
        reader.close();

        // 通过key获取value
        String className = pro.getProperty("className");
        //System.out.println(className);

        // 通过反射机制实例化对象
        Class c = Class.forName(className);
        Object obj = c.newInstance();

三、Field类

Field翻译为字段,其实就是属性/成员

反射Field(了解)

获取Field实例

// 获取整个类
Class studentClass = Class.forName("com.bjpowernode.java.bean.Student");
// 获取类中所有的public修饰的Field
Field[] fs = studentClass.getFields();
// 获取类中所有的Field
Field[] fs = studentClass.getDeclaredFields();

Field类中常用方法
假设field为Field类的一个对象

// 获取属性的修饰符列表
// 返回的修饰符是一个数字,每个数字是修饰符的代号!!!
int i = field.getModifiers();
// 可以将这个“代号”数字转换成“字符串”吗?
String modifierString = Modifier.toString(i);
        
// 获取属性的类型
Class fieldType = field.getType();
String fName = fieldType.getName();
String fName = fieldType.getSimpleName();

// 取出属性的名字
String fieldName = field.getName();

总结:
1. 获取属性的修饰符列表:
String modifierName = Modifier.toString(Field对象.getModifiers());
2. 获取属性的类型:
String typeName = Field对象.getType().getSimpleName();
3. 获取属性的名字:
String fieldName = Field对象.getName();

通过反射机制访问一个java对象的属性(重点)

必须掌握:通过反射机制访问一个java对象的属性
给属性赋值set
获取属性的值get

public class Student {
    private String name; // Field对象
    protected int age; // Field对象
    boolean sex;
    public int no;
    public static final double MATH_PI = 3.1415926;
}

Class studentClass = Class.forName("com.bjpowernode.java.bean.Student");
Object obj = studentClass.newInstance(); // obj就是Student对象。(底层调用无参数构造方法)

// 获取no属性(根据属性的名称来获取Field)
Field noFiled = studentClass.getDeclaredField("no");
// 给obj对象的no属性赋值2222
noFiled.set(obj, 22222); 
// 给obj对象(Student对象)的no属性赋值
/*
虽然使用了反射机制,但是三要素还是缺一不可:
obj对象     no属性      具体赋值
注意:反射机制让代码复杂了,但是为了“灵活”,这也是值得的。
 */

// 读取属性的值
// 两个要素:获取obj对象的no属性的值。
System.out.println(noFiled.get(obj));

// 如何访问私有的属性
Field nameField = studentClass.getDeclaredField("name");

// 打破封装(反射机制的缺点:打破封装,可能会给不法分子留下机会!!!)
// 这样设置完之后,在外部也是可以访问private的。
nameField.setAccessible(true);

// 给name属性赋值
nameField.set(obj, "jackson");
// 获取name属性的值
System.out.println(nameField.get(obj));

四、Method类

可变长度参数

语法是:类型… (注意:一定是3个点。)
例如: int… args 这就是可变长度参数

  1. 可变长度参数要求的参数个数是:0~N个。
  2. 可变长度参数可以当做一个数组来看待。
  3. 可变长度参数在参数列表中必须在最后一个位置上,而且可变长度参数只能有1个。
public static void fun(int a, String... args){
        //args有length属性,说明args是一个数组!
        // 可以将可变长度参数当做一个数组来看。
        for(int i = 0; i < args.length; i++){
            System.out.println(args[i]);
        }
}

fun(100);
fun(200, "abc");
fun(200, "abc", "def");
fun(200, "abc", "def", "xyz");

String[] strs = {"a","b","c"};
fun(100, strs)

反射Method(了解)

获取Method实例

Class userServiceClass = Class.forName("com.bjpowernode.java.service.UserService");
// 获取所有的Method(包括私有的!)
Method[] methods = userServiceClass.getDeclaredMethods();

Method类中常用方法
假设method为Method类的一个对象

// 获取修饰符列表
Modifier.toString(method.getModifiers());
// 获取方法的返回值类型
method.getReturnType().getSimpleName();
// 获取方法名
method.getName();
// 方法实参的修饰符列表(一个方法的参数可能会有多个。)
Class[] parameterTypes = method.getParameterTypes();
for(Class parameterType : parameterTypes){
      System.out.println(parameterType.getSimpleName());
}

通过反射机制调用方法(重点)

通过反射机制调用一个对象的方法。必须掌握

反射机制,让代码很具有通用性,可变化的内容都是写到配置文件当中,将来修改配置文件之后,创建的对象不一样了,调用的方法也不同了,但是java代码不需要做任何改动。这就是反射机制的魅力。

// 使用反射机制来调用一个对象的方法该怎么做?
Class userServiceClass = Class.forName("com.bjpowernode.java.service.UserService");
// 创建对象
Object obj = userServiceClass.newInstance();
// 获取Method
Method loginMethod = userServiceClass.getDeclaredMethod("login", String.class, String.class);
// 调用方法
// 反射机制中最最最最最重要的一个方法,必须记住。
/*
调用方法的四要素:
	对象          方法           实参          返回值
	obj      loginMethod   "admin","123"    retValue
*/
Object retValue = loginMethod.invoke(obj, "admin","123123");

五、Constructor类

反射Constructor(了解)

获取Constructor实例

Class c = Class.forName("java.lang.String");
Constructor[] constructors = c.getDeclaredConstructors();

Constructor类中常用方法

//可参考Method类
Modifier.toString(constructor.getModifiers())
Class[] parameterTypes = constructor.getParameterTypes();

通过反射机制调用构造方法实例化java对象(重点)

// 使用反射机制调用构造方法创建对象
Class c = Class.forName("com.bjpowernode.java.bean.Vip");

// 调用无参数构造方法
Object obj = c.newInstance();

// 调用有参数的构造方法
// 第一步:先获取到这个有参数的构造方法
Constructor con = c.getDeclaredConstructor(int.class, String.class, String.class,boolean.class);
// 第二步:调用构造方法new对象
Object newObj = con.newInstance(110, "jackson", "1990-10-11", true);

// 获取无参数构造方法
Constructor con2 = c.getDeclaredConstructor();
Object newObj2 = con2.newInstance();

获取父类和父接口(重点)

// String举例
Class stringClass = Class.forName("java.lang.String");

// 获取String的父类
Class superClass = stringClass.getSuperclass();

// 获取String类实现的所有接口(一个类可以实现多个接口。)
Class[] interfaces = stringClass.getInterfaces();
for(Class in : interfaces){
    System.out.println(in.getName());
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值