反射机制

  • ​​反射机制、反射含义​​
  • ​​1.利用反射获取类对象的三种方法​​
  • ​​2.利用反射获取类对象的类名、方法、属性​​
  • ​​3.利用反射获取类对象中的构造器、构造器参数类型、实例化构造器​​
  • ​​实例化重点​​
  • ​​类的加载方式不同​​
  • ​​所调用的构造方法不尽相同​​
  • ​​执行效率不同​​
  • ​​4.利用反射获取类对象中的方法的包装类​​
  • ​​5.通过反射获取运行配置文件内容(Properties)​​
  • ​​6.利用反射和properties实现获取配置文件中的内容(类路径、方法名,然后反射实现 实例化、回调方法)​​
  • ​​6.2.String 的 endsWith() 方法​​
  • ​​7.在不更改源代码的前提下通过更改配置文件实现程序功能的扩展​​
  • ​​8.使用反射的小结​​

反射机制、反射含义

  • JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象, 都能够调用它的任意方法和属性;
  • 这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制
  • 帮助理解:就是我们可以通过代码获取到类中的所有属性和所有方法
  • 简而言之:自己找到自己,俗称“照镜子”

1.利用反射获取类对象的三种方法

  • 方法一:对象.getClass()方法
  • 方法二:类.clss属性
  • 方法三:Class.forName()

代码块

public class Test {

public static void main(String[] args) throws Exception {
//Student类的实例化
Student student01= new Student();

//方法一:通过getclass()
Class<? extends Student> s1 = student01.getClass();
System.out.println(s1);

//方法二:通过class属性
Class<Student> s2 = Student.class;
System.out.println(s2);

//方法三:通过Class.forname()
System.out.println(Class.forName(".reflex01.Student"));
}
}

2.利用反射获取类对象的类名、方法、属性

stu.getClass();

stu.getClass().getFields().getName() & field.getType() //属性名 和 属性类型

stu.getClass().getMethods().getName() & method.getReturnType() //类方法 和 方法返回值

/**
* @Description: 测试类:测试获取类对象中的类名、方法和属性
* @auther:Li Ya Hui
* @Time:2021年4月19日下午1:33:00
*/
public class Test {
public static void main(String[] args) {
//1.student类的实例化
Student stu = new Student();

//2.获取student的类对象 class
Class<? extends Student> student = stu.getClass();
System.out.println("类的class:\t"+student);
System.out.println("类的全称:"+stu.getClass().getSimpleName());
System.out.println("类的简称:"+stu.getClass().getName());

//3使用增强for遍历学生类的属性 属性名,属性类型
Field[] stufiled = stu.getClass().getFields();
for (Field field : stufiled) {
System.out.println("属性名:"+field.getName()+"\t\t属性的数据类型:"+field.getType());
}
//4.获取类中的方法
Method[] stumethods = stu.getClass().getMethods();
for (Method method : stumethods) {
System.out.println("类的方法"+method.getName()+"\t类的方法的返回值类型"+method.getReturnType());
}

}
}

3.利用反射获取类对象中的构造器、构造器参数类型、实例化构造器

package .reflex03;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Test {

public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {

//获取类对象
Class<?> stu = Class.forName(".reflex03.Student");

//获取所有的公有构造方法(前提 类是用public修饰的)
Constructor<?>[] stuConstru = stu.getConstructors();
for (Constructor<?> constructor : stuConstru) {
System.out.println("构造器:\t"+constructor);

//构造器执行 (参数 类型 个数 不对会导致出错)
//System.out.println("构造器执行:\t:"+constructor.newInstance(""));//等价于Student student = new Student(); 相当于类的初始化

//获取构造器中的参数类型
Class<?>[] parameterTypes = constructor.getParameterTypes();

//遍历各个构造器参数类型
for (Class<?> parameter : parameterTypes) {
System.out.println("构造器中的参数类型:\t"+parameter.getName());
}
System.out.println();
}
}
}

实例化重点

类的加载方式不同

在执行Class.forName(“”)时,JVM会在classapth中去找对应的类并加载,这时JVM会执行该类的静态代码段。在使用newInstance()方法的时候,必须保证这个类已经加载并且已经连接了,而这可以通过Class的静态方法forName()来完成的。

使用关键字new创建一个类的时候,这个类可以没有被加载,一般也不需要该类在classpath中设定,但可能需要通过classlaoder来加载。

所调用的构造方法不尽相同

​ new关键字能调用任何构造方法。
​ newInstance()只能调用无参构造方法。

执行效率不同

​ new关键字是强类型的,效率相对较高。
​ newInstance()是弱类型的,效率相对较低。

4.利用反射获取类对象中的方法的包装类

/**
* @Description:测试类,测试利用反射获取类的方法的包装类Method
* @auther:Li Ya Hui
* @Time:2021年4月19日下午8:57:13
*/
public class Test {

public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {

//1.获取类对象
Class<Student> ownerClass = Student.class;

//2.Student类的实例化
Student student = ownerClass.newInstance();

//3.通过类对象获取method类 方法名 方法返回值类型 需是类
Method method = ownerClass.getMethod("sayHi", String.class);

//多个参数时
//Method method = ownerClass.getMethod("sayHi", String.class, String.class);

//4.invoke() 是method类中的方法,起作用是回调类中的方法
//invoke 参数讲解: 1.回调那个实例化对象中的方法 2.传入的参数的值
String result = (String) method.invoke(student, "LiYaHui");

//多个参数时
// String result = (String) method.invoke(student, "LiYaHui","李亚辉");
System.out.println(result);
}
}

5.通过反射获取运行配置文件内容(Properties)

properties位于java.util.properties,是Java语言的配置文件所使用的类

/**
* @Description: 利用properties获取配置文件中的内容
* @auther:Li Ya Hui
* @Time:2021年4月20日上午9:14:52
*/
public class Test {

public static void main(String[] args) throws IOException {
//实例properties类
Properties properties = new Properties();
//创建读取对象
FileReader fileReader = new FileReader("dbconfig.txt");
//加载读取对象
properties.load(fileReader);
//获取username属性
String username = properties.getProperty("username");
//打印username的值
System.out.println(username);//打印 root
}
}
自己写的配置文件
//dbconfig.txt
username=root
password=root
drivername=com.mysql.jdbc.driver
url=127.0.01:3306

6.利用反射和properties实现获取配置文件中的内容(类路径、方法名,然后反射实现 实例化、回调方法)

package .reflex05;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.ObjectInputStream.GetField;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Properties;

/**
* @Description: 利用反射和properties实现获取配置文件中的内容
* @auther:Li Ya Hui
* @Time:2021年4月20日上午9:39:51
*/
public class Test {
public static void main(String[] args) throws Exception {
//1.propertie类的初始化
Properties properties = new Properties();

//2.加载配置文件哎
FileReader fileReader = new FileReader("playerList.txt");

//3.properties加载配置文件
properties.load(fileReader);

//4.利用properties获取配置文件中的内容

//当前的配置文件中的className的属性值, 得到应用类的路径
String className = properties.getProperty("className");
System.out.println("获取到的类的路径名:\t"+className);

//当前的配置文件中的methodName的属性值, 得到类的方法名
String methodName = properties.getProperty("methodName");
System.out.println("获取到的类中方法的名字:\t"+methodName);

//得到类的class
Class<?> heroclass = Class.forName(className);

//得到构造器
Constructor constructor = heroclass.getConstructor();

//初始化构造器,其作用相当于是new ,目前是实例化类
Object hero = constructor.newInstance();

//调用方法 getMethod使用类对象来调用的,所以不能用hero对象
Method method = heroclass.getMethod(methodName, String.class);//通过类对象获取到方法的包装类Method,且同时指出本次执行的方法是run,且该方法的传入参数为String

//回调hero类中的run方法
method.invoke(hero,"ss枪手");
}
}

//hero类
/**
* @Description: 英雄类
* @auther:Li Ya Hui
* @Time:2021年4月20日上午9:36:45
*/
public class Hero {
public void run(String attribute)
{
if (attribute.endsWith("肉盾"))
{
System.out.println("肉盾!可以慢跑");
}else if(attribute.endsWith("枪手")){
System.out.println("枪手可以边打边跑");
}else {
System.out.println("可以正常跑!");
}
}
}

//配置文件
//playerList.txt
className=.reflex05.Hero
methodName=run

6.2.String 的 endsWith() 方法

(上面例子的hero英雄类中有应用场景)

endwith和equals方法有些相似,但是不同的是endwith判断是否以指定字符结尾,有点正则表达式的意思

7.在不更改源代码的前提下通过更改配置文件实现程序功能的扩展

package .reflex07;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.ObjectInputStream.GetField;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Properties;

/**
* @Description: 在不更改源代码的前提下通过更改配置文件实现程序功能的扩展
* @auther:Li Ya Hui
* @Time:2021年4月20日上午9:39:51
*/
public class Test {

public static void main(String[] args) throws Exception {
//1.propertie类的初始化
Properties properties = new Properties();

//2.加载配置文件哎
FileReader fileReader = new FileReader("playerList2.txt");

//3.properties加载配置文件
properties.load(fileReader);

//4.利用properties获取配置文件中的内容

//当前的配置文件中的className的属性值, 得到应用类的路径
String className = properties.getProperty("className");
System.out.println("获取到的类的路径名:\t"+className);
//当前的配置文件中的methodName的属性值, 得到类的方法名
String methodName = properties.getProperty("methodName");
System.out.println("获取到的类中方法的名字:\t"+methodName);

//得到类的class
Class<?> heroclass = Class.forName(className);

//得到构造器
Constructor constructor = heroclass.getConstructor();

//初始化构造器,其作用相当于是new ,目前是实例化类
Object hero = constructor.newInstance();

//调用方法 getMethod使用类对象来调用的,所以不能用hero对象
Method method = heroclass.getMethod(methodName, String.class);//通过类对象获取到方法的包装类Method,且同时指出本次执行的方法是run,且该方法的传入参数为String

//以上代码没有更改,封装后可实现多次扩展实例代码
//回调hero类中的run方法
method.invoke(hero,"boss"); //只需在调用时换参数即可
}
}
//妖怪类(Devil)
package .reflex07;
/**
* @Description: 妖怪类
* @auther:Li Ya Hui
* @Time:2021年4月20日上午9:36:45
*/
public class Devil {
public void run(String attribute)
{
if (attribute.endsWith("boss"))
{
System.out.println("boss!可以慢跑");
}else if(attribute.endsWith("littleboss")){
System.out.println("littleboss可以稍微快点跑");
}else {
System.out.println("可以正常跑!");
}
}
}
//配置文件//playerList2.txt
className=.reflex07.Devil
methodName=run

8.使用反射的小结

1.可以在任何地方通过反射获取到类中的属性和方法和构造器还有实例化

2.因为可以获取到构造器,当然也可以实例化==(通过newInstance()方法即可实现)==

3.经常被应用在框架中,例如Struts,Soring框架

要点:newInstance这个方法勉强等同于news这个关键字