反射机制
1.反射机制概述
-
反射机制相关的类:
代表字节码文件,代表一个类型:java.lang.Class
代表字节码中方法字节码,代表类中方法:java.lang.reflect.Method
代表字节码中构造方法字节码,代表类中构造方法:java.lang.reflect.Constructor
代表字节码中属性字节码,代表类中成员变量:java.lang.reflect.Field
//java.lang.Class
public class User{
//java.lang.reflect.Field
int no;
//java.lang.reflect.Constructor
public User(){ }
//java.lang.reflect.Method
public void setNo(int no){ this.no = no; }
}
2.反射类Class
-
要操作一个类字节码,首先需要获取这个类的字节码。获取Class三种方式:
第一种:Class.forName()
这是一种静态方法、方法参数是一个字符串、字符串是一个完整类名、完整类名必须带有包名。
public class ReflectTest { public static void main(String[] args) { //Class.forName()方法 try { Class c1 = Class.forName("java.lang.String"); Class c2 = Class.forName("java.util.Date"); Class c3 = Class.forName("java.lang.Integer"); Class c4 = Class.forName("java.lang.System"); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } }
第二种:java中的任何对象中的getClass()方法
public class ReflectTest { public static void main(String[] args) { //java中所有对象都有一个方法:getClass() String s = "abc"; Class x = s.getClass(); //x代表String.class字节码文件,x代表String类型。 } }
第三种:java语音中任何一种类型,包括基本数据类型,都有.Class属性
public class ReflectTest { public static void main(String[] args) { Class z = String.class; Class k = Date.class; Class f = int.class; Class e = double.class; } }
-
通过反射机制获取Class,通过Class.newInstance()实例化对象。
public class ReflectTest {
public static void main(String[] args) {
try {
//通过反射机制获取Class,通过Class实例化对象
Class c = Class.forName("reflect.User");
//newInstance()会调用类的无参构造方法完成对象创建。即实例化对象。
//必须保证无参构造必须存在。
Object obj = c.newInstance();
System.out.println(obj);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
public class User {
public User() {
System.out.println("无参构造方法");
}
//如果这里不写无参构造方法,直接写有参构造方法,无参构造方法就没了,会使newInstance()报错
}
- java代码写一遍,在不改变java源代码的基础上,可以做到不同对象的实例化。
//验证反射机制的灵活性
public class ReflectTest {
public static void main(String[] args) throws Exception {
//这种方式是写死的,只能创建User类型对象
User user = new User();
//以下方式是灵活的,代码不需要改动,只需要修改配置文件就能创建不同实例对象。
//通过IO流读取classinfo.properties文件
FileReader reader = new FileReader("D:\\study\\study5\\study5\\classinfo.properties");
//创建属性类对象Map
Properties pro =new Properties(); //key和value都是String
//加载
pro.load(reader);
//关闭流
reader.close();
//通过key获取value
String className = pro.getProperty("className");
//通过反射机制实例化对象
Class c = Class.forName(className);
Object obj = c.newInstance();
System.out.println(obj);
}
}
创建的classinfo.properties文件
className=reflect.User
#测试其他类:
#className=java.util.Date
#className=java.lang.String
- 以后学习的高级框架如Spring、SpringMVC、MyBatis……底层实现原理都采用了反射机制。
- 在调用Class.forName会直接执行会导致类加载,类加载时会执行静态代码块。
- 当只希望一个类静态代码块执行,其他代码不执行。可以直接调用Class.forName(“完整类名”);
public class ReflectTest {
public static void main(String[] args) {
try {
//这个直接执行会导致类加载,类加载时会执行静态代码块。
Class.forName("reflect.MyClass");
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
class MyClass{
//静态代码块在加载时执行,并且执行一次
static {
System.out.println("MyClass静态代码块执行了");
}
}
- 获取类的根路径(src)下的某个文件的绝对路径:Thread.currentThread().getContextClassLoader().getResource(“从根目录开始,到目标文件的路径”).getPath()
public class AboutPath {
public static void main(String[] args) throws Exception {
//通过IO流读取classinfo.properties文件
//以下这种方法如果代码移植了找不到这个文件
//FileReader reader = new FileReader("D:\\study\\study5\\study5\\classinfo.properties");
//通用的路径,即使代码换位置了还能读到这个文件
//注:这个文件必须在类路径下,即src下。src是类的根路径。
//当前线程对象Thread.currentThread()
//线程对象的方法,获取到当前线程的类加载器对象getContextClassLoader()
//类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源getResource()
//使用这里获取文件的绝对路径
//获取src/classinfo.properties文件
String path = Thread.currentThread().getContextClassLoader()
.getResource("classinfo.properties").getPath();
//getResource("")这里填根路径开始到获取的资源的路径
System.out.println(path);
//获取src/bean/db.properties文件
String path2 = Thread.currentThread().getContextClassLoader()
.getResource("bean/db.properties").getPath();
//getResource("")这里填根路径开始到获取的资源的路径
System.out.println(path2);
}
}
- 以流的形式直接返回
public class IoPropertiesTest {
public static void main(String[] args) throws Exception{
//先拿到流的路径,以流形式返回
//String path = Thread.currentThread().getContextClassLoader().getResource("classinfo.properties").getPath();
//FileReader reader = new FileReader(path);
//直接以流的形式返回
InputStream reader = Thread.currentThread().getContextClassLoader()
.getResourceAsStream("classinfo.properties");
Properties pro = new Properties();
pro.load(reader);
reader.close();
//通过key获取value
String className = pro.getProperty("className");
System.out.println(className);
}
}
- java.util包下提供了一个资源绑定器,便于获取属性配置文件中内容。属性配置文件xxx.properties必须放到类路径下。
public class ResourceBundleTest {
public static void main(String[] args) {
//资源绑定器,只能绑定xxx.properties文件,且必须在类路径下。
//在写路径时,路径后面的扩展名不能写
ResourceBundle bundle = ResourceBundle.getBundle("classinfo");
String className = bundle.getString("className");
System.out.println(className);
}
}
3.反射属性Field
-
获取Field属性
Field[] getFields():获取所有带public修饰符的Field
Field[] getDeclaredFields():获取所有的Field
int getModifiers():获取修饰符,用int返回
Modifier.toString(int):将int类型转换为修饰符名称
Class<> getType():获取Field属性的类型
String getName():获取Field属性的名字
package bean;
public class Student {
//Field翻译为字段,即属性/成员
//4个Field分别采用不同访问控制权限修饰符
public int no;
private String name;
protected int age;
boolean sex;
}
package reflect;
import java.io.FileReader;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
//反射Student类中所有Field
public class ReflectTest {
public static void main(String[] args) throws Exception{
//获取整个类
Class studentClass = Class.forName("bean.Student");
//完整类名
System.out.println("完整类名:" + studentClass.getName());//完整类名:bean.Student
//简类名
System.out.println("简类:" + studentClass.getSimpleName());//简类:Student
//获取类中所有的public修饰的Field
Field[] fields = studentClass.getFields();
System.out.println(fields.length);//1
System.out.println(fields[0]); // public int bean.Student.no
//获取所有的Field
Field[] fs = studentClass.getDeclaredFields();
System.out.println(fs.length); //4
for (Field field:
fs) {
//获取修饰符
//field.getModifiers()返回的是int类型
//通过Modifier.toString()转换为修饰符
System.out.printf(Modifier.toString(field.getModifiers()) + " ");
//获取属性类型
System.out.printf(field.getType().getSimpleName() + " ");
//获取属性名字
System.out.printf(field.getName());
System.out.println();
//public int no
//private String name
//protected int age
//boolean sex
}
}
}
- 反编译Field
//通过反编译机制,反编译一个类的属性Field
public class ReflectTest {
public static void main(String[] args) throws Exception{
//创建用来拼接的字符串
StringBuilder s = new StringBuilder();
//获取整个类
//还可以拿到java.lang.String等
Class studentClass = Class.forName("bean.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);
}
}
- 通过反射机制访问对象属性
//通过反射机制访问java对象的属性
public class ReflectTest {
public static void main(String[] args) throws Exception{
//不使用反射机制,访问一个对象的属性
Student s = new Student();
//属性赋值
s.no=1111;//要给一个属性赋值需要三要素:对象、属性、值
//读取属性值
System.out.println(s.no);
//获取整个类
Class studentClass = Class.forName("bean.Student");
Object obj = studentClass.newInstance(); //底层调用无参构造方法创建student对象
//获取no属性(根据属性的名称来获取Field)
Field noField = studentClass.getDeclaredField("no");
//要给一个属性赋值需要三要素:对象、属性、值
//给obj对象(Student)对象的no属性赋值
noField.set(obj,22222); //给obj对象的no属性赋值22222
//读取属性值
System.out.println(noField.get(obj));
//访问私有属性
Field nameField = studentClass.getDeclaredField("name");
//打破封装
nameField.setAccessible(true);
nameField.set(obj,"coffee");
System.out.println(nameField.get(obj));
}
}
4.可变长参数
-
可变长参数:int… args(类型… )
可变长参数的参数个数是:0~N个都可以
可变长参数在参数列表中必须在最后一个位置上,并且可变长度参数只能有1个。
可变长参数可以当做一个数组来看待。
public class ArgsTest {
public static void main(String[] args) {
m();
m(10);
m(10,20);
m2(10,"abc");
m3("ice","coffee");
String[] sts = {"a","b","c"};
m3(sts);
m3(new String[]{"ice","coffee"});
}
public static void m(int... args){
System.out.println("m方法执行了");
}
//可变长参数在参数列表只能放在最后一个位置上,且只有一个
public static void m2(int a ,String... args){
System.out.println("m2方法执行了");
}
//可以将可变长参数作为数组来看
public static void m3(String... args){
//args有length属性
for (int i = 0; i < args.length; i++) {
System.out.println(args[i]);
}
}
}
5.反射方法Method
- 反编译获取方法类型、名称等
public class ReflectTest {
public static void main(String[] args) throws Exception{
//获取类
Class userServiceClass = Class.forName("service.UserService");
//获取所有的Method
Method[] methods = userServiceClass.getDeclaredMethods();
System.out.println(methods.length); //2
//遍历Method
for (Method method:
methods) {
//获取修饰符列表
System.out.printf(Modifier.toString(method.getModifiers()) + " ");
//获取方法类型
System.out.printf(method.getReturnType().getSimpleName() + " ");
//获取方法名称
System.out.printf(method.getName());
//获取方法参数列表
Class[] parameterTypes = method.getParameterTypes();
for (Class parameterType:
parameterTypes) {
System.out.printf(" "+parameterType.getSimpleName());
}
System.out.println();
}
}
}
- 通过反射机制调用方法
package service;
public class UserService {
/**
* 登录方法
* @param name 用户名
* @param password 密码
* @return true表示登录成功,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("系统已经安全退出");
}
}
package reflect;
import service.UserService;
import java.lang.reflect.Method;
public class ReflectTest {
public static void main(String[] args) throws Exception {
//不使用反射机制,调用方法
//调用方法要素:对象、方法名、实参列表、返回值
UserService userService = new UserService();
boolean loginSuccess = userService.login("admin","123");
System.out.println(loginSuccess? "登录成功" : "登录失败");
//使用反射机制调用对象的方法
Class userserviceClass = Class.forName("service.UserService");
//创建对象
Object obj = userserviceClass.newInstance();
//获取Method
Method loginMethjod = userserviceClass.getDeclaredMethod("login",String.class,String.class);
//调用方法要素:对象、方法名、实参列表、返回值
Object retValue = loginMethjod.invoke(obj,"admin","123");
System.out.println(retValue);
}
}
6.反射构造方法Constructor
- 反编译构造方法
//定义一个多个构造方法的类
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;
}
@Override
public String toString() {
return "Vip{" +
"no=" + no +
", name='" + name + '\'' +
", birth='" + birth + '\'' +
", sex=" + sex +
'}';
}
}
//反编译构造方法
public class ReflectTest {
public static void main(String[] args) throws Exception{
StringBuilder s = new StringBuilder();
Class vipClass = Class.forName("bean.Vip");
s.append(Modifier.toString(vipClass.getModifiers()));
s.append(" class ");
s.append(vipClass.getSimpleName());
s.append("{\n");
//拼接构造方法
Constructor[] constructors = vipClass.getDeclaredConstructors();
for (Constructor constructor:
constructors) {
s.append("\t");
s.append(Modifier.toString(constructor.getModifiers()));
s.append(" ");
s.append(vipClass.getSimpleName());
s.append("(");
//拼接参数
Class[] paramterTypeds = constructor.getParameterTypes();
for (Class parameterType:
paramterTypeds) {
s.append(parameterType.getSimpleName());
s.append(",");
}
if (paramterTypeds.length>0){
s.deleteCharAt(s.length()-1);
}
s.append("){}\n");
}
s.append("}");
System.out.println(s);
}
}
- 通过反射机制创建对象
public class ReflectTest11 {
public static void main(String[] args) throws Exception{
//不使用反射机制创建对象
Vip v1 = new Vip();
Vip v2 = new Vip(110,"coffee","2001-06-21",true);
//使用反射机制创建对象
Class c = Class.forName("bean.Vip");
//调用无参数构造方法
Object obj = c.newInstance();
//调用有参构造方法
Constructor con = c.getDeclaredConstructor(int.class,String.class,String.class,boolean.class);
//调用构造方法new对象
Object newObj = con.newInstance(110,"ice","2001-06-21",true);
System.out.println(newObj); //Vip{no=110, name='ice', birth='2001-06-21', sex=true}
}
}
7.获取父类
public class ReflectTest12 {
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 in : interfaces
) {
System.out.println(in.getName());
/**
* java.io.Serializable
* java.lang.Comparable
* java.lang.CharSequence
*/
}
}
}
——本章节为个人学习笔记。学习视频为动力节点Java零基础教程视频:动力节点—JAVA零基础教程视频