18 反射机制
18.1 概述
- 概述
有什么用?通过反射机制可以操作字节码文件,可以读和修改字节码文件
在哪个包下?在java.lang.reflect包下
相关的类有哪些?
①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){
}
//Method方法
public void setNo(int no){
this.no = no;
}
public int getNo(){
return no;
}
}
18.2 获取class的三种方式
- 代码实现
java
package com.bjpowernode._14Reflect;
import java.util.Date;
/**
要操作一个类的字节码,首先要获取这个类的字节码,怎么获取java.lang.Class实例
第一种:Class c = Class.forName("完整类名带包名")
第二种:Class c = 对象.getClass();
*/
public class Reflect01 {
public static void main(String[] args) {
//第一种方法:Class类中的静态方法
//static Class forName(String className)返回带有给定字符串的名的类(参数传的String的包名,且必须带有包名)
// 或接口相关联的Class对象
Class c1= null;
Class c2= null;
try {
c1 = Class.forName("java.lang.String");//c1代表String.class文件,或者说c1代表String类型
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()
String s = "abc";
Class x = s.getClass();//x代表String.class字节码文件,x代表String类型
System.out.println(c1==x);//看下内存地址一样吗,结果是true
Date time = new Date();
Class y = time.getClass();
System.out.println(c2==y);//看下内存地址一样吗,结果是true
//c2和y两个变量种保存到内存地址都是一样的,都指向方法区中的字节码文件,只有一份文件
//第三种方式,java语言中任何一种类型,他都有.class属性
Class z = String.class;//z代表String类型
Class k = Date.class;
Class f = int.class;
Class e = double.class;
System.out.println(z==x);//true
}
}
- forName()类加载用法
java
package com.bjpowernode._14Reflect;
/**
研究下:Class.forName()发生了什么?
如果想让一个静态代码块执行可以用forName()的方法
后续JDBC技术的时候我们需要使用
*/
public class Reflect04 {
public static void main(String[] args) {
try {
//forName()发生了类加载,静态代码块必然执行
Class.forName("com.bjpowernode._14Reflect.MyClass");//MyClass类的静态代码块执行了
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
class MyClass{
//静态代码块在类加载时执行,并且只执行一次
static {
System.out.println("MyClass类的静态代码块执行了");
}
}
18.3 通过反射实例化对象
18.3.1 instance()实例化对象
User类
java
package com.bjpowernode._14Reflect.Reflect02.bean;
public class User {
public User(){
System.out.println("无参构造方法!");
}
// //有参构造方法,无参构造器就等于没了
// public User(String s){
//
// }
}
程序入口类
java
package com.bjpowernode._14Reflect.Reflect02.reflect;
import com.bjpowernode._14Reflect.Reflect02.bean.User;
/**
获取到class,能干什么?
通过Class的newInstance()方法来实例化对象
注意:instance()底层会调用User类的无参构造器方法创建对象,必须保证无参构造是存在的!
*/
public class ReflectTest02 {
public static void main(String[] args) {
//这个是不使用反射机制,创建对象
User user = new User();//无参构造方法!
System.out.println(user);//com.bjpowernode._14Reflect.Reflect02.bean.User@1b6d3586
//通过反射机制,获取Class,通过class实例化对象,
try {
Class c1 = Class.forName("com.bjpowernode._14Reflect.Reflect02.bean.User");
//public T instance()方法:调用的是无参构造,必须保证无参构造是存在的!
Object obj = c1.newInstance();//无参构造方法!instance()底层会调用User类的无参构造器方法创建对象
//即使没有设置无参构造方法,也会自动构造
//如果设置了有参构造器方法,无参相当于没有,instance()会报异常,
//java.lang.InstantiationException:就会报异常,实例化异常
// Caused by: java.lang.NoSuchMethodException: com.bjpowernode._14Reflect.Reflect02.bean.User.<init>()
//或者加泛型
Class<User> c2 = (Class<User>)Class.forName("com.bjpowernode._14Reflect.Reflect02.bean.User");
User user1 = c2.newInstance();
System.out.println(obj);//com.bjpowernode._14Reflect.Reflect02.bean.User@1b6d3586
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
18.3.2 通过读属性文件实例化对象
属性文件
properties
className =com.bjpowernode._14Reflect.Reflect02.bean.User
程序入口类
java
package com.bjpowernode._14Reflect;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;
/**
验证反射机制的灵活性
java代码写一遍,在不改变java源代码的基础之上,通过配置文件,可以做到不同对象的实例化,非常之灵活,
符合OCT开闭原则:对扩展开放,对修改关闭
原来的new类只能创建一种类型的对象
未来学的Spring,SpringMVC,MyBatis,Structs,Hibernate都是使用了反射机制
*/
public class Reflect03 {
public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
//通过IO流读取classinfo/properties文件
FileReader reader = new FileReader("D:\\学习\\专业学习\\4-编程语言类\\java\\java代码\\13_Java_advanced\\Java_basic\\src\\main\\java\\com\\bjpowernode\\_14Reflect\\classinfo.properties");
//创建属性类对象Map
Properties pro = new Properties();
//加载
pro.load(reader);
//关闭流
reader.close();
//通过key获取value
String className = pro.getProperty("className");
System.out.println(className);//com.bjpowernode._14Reflect.Reflect02.bean.User
//通过反射机制实例化对象
Class c = Class.forName(className);
Object obj = c.newInstance();//无参构造方法!
System.out.println(obj);//com.bjpowernode._14Reflect.Reflect02.bean.User@1b6d3586
}
}
18.3.3 优化文件路径
- 获取文件路径getContextClassLoader()
java
package com.bjpowernode._14Reflect;
import java.io.FileNotFoundException;
import java.io.FileReader;
/**
研究一下文件路径的问题
*/
public class AboutPath05 {
public static void main(String[] args) throws FileNotFoundException {
//这种方式的路径缺点是:移植性差,在IDEA中默认的当前路径是project的根
//离开IDEA,当前路径就不对了,路径无效
//FileReader reader = new FileReader("main/java/com/bjpowernode/_14Reflect/classinfo.properties");
//1.通用路径写法,但是前提是:文件必须在类路径下(在scr下的都是类路径下,scr的是类的根路径)
/**
解释:
当前线程对象,getContextClassLoader()是线程对象的方法,目的是可以获取到当前线程的类加载器对象
getResource()是类加载器对象的方法,默认当前线程的类加载器从类的根目录下加载资源
可以拿到当前文件的绝对路径!!!
*/
String path = Thread.currentThread().getContextClassLoader().getResource("classinfo2.properties").getPath();
System.out.println(path);
//2.获取classinfo.properties的绝对路径(从根路径下作为起点)
String path2 = Thread.currentThread().getContextClassLoader()
.getResource("main/java/com/bjpowernode/_14Reflect/classinfo.properties").getPath();
System.out.println(path2);
}
}
其实真正的类路径是out里面开始的路径,out表示输出的class类
- 具体运用在以往IO流案例上getResourceAsStream()
java
package com.bjpowernode._14Reflect;
import sun.plugin2.ipc.InProcEvent;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class IoPropertiesTest06 {
public static void main(String[] args) throws IOException {
//1.可以获取绝对路径传进去,classinfo2.properties在src根目录下
String path = Thread.currentThread().getContextClassLoader().
getResource("classinfo2.properties").getPath();
FileReader reader = new FileReader(path);
Properties pro = new Properties();
pro.load(reader);
reader.close();
//通过key获取value
String className = pro.getProperty("className");
System.out.println(className);
//2.或者直接以流的形式返回
InputStream reader2 = Thread.currentThread().getContextClassLoader()
.getResourceAsStream("classinfo2.properties");
System.out.println(reader2);
}
}
- 资源绑定器ResourceBundle
不用使用File和Properties了,全部用资源绑定器,但是只能是properties文件
java
package com.bjpowernode._14Reflect;
import java.util.ResourceBundle;
/**
java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容(只能是properties结尾的文件)
前提是属性配置文件xxx.properties必须放在类路径下(src下面)
*/
public class ResourceBundleTest07 {
public static void main(String[] args) {
//资源绑定器,记得去掉properties结尾
ResourceBundle bundle = ResourceBundle.getBundle("classinfo2");
//也不用pro对象去获取value了
String className = bundle.getString("className");
System.out.println(className);
}
}
18.4 类加载器(额外)
1. 概述
什么是类加载器?专门负责加载器的命令/工具
有什么加载器?
①启动类加载器
②扩展类加载器
③应用类加载器
- 场景:
假设有这样一段代码:
代码在开始执行之前,会将所需要类全部加载道JVM当中,通过类加载器加载,看到以上代码类加载器后类加载器会去找Spring.class文件去加载
- 具体:
通过"启动类加载器"加载。注意:专门加载rt.gar,是最重要的类库
如果"启动类加载器"加载不到,会通过"拓展类加载器"加载*.jar
如果"扩展类加载器"没有找到,会通过"应用类加载器"加载classpath中的类,就像以前配置的环境变量
- 双亲委派机制
java中为了保证类加载的安全,使用了双亲委派机制
"父"无法加载到,再从扩展类加载器加载,成为"母",叫做双亲委派,最后才会从应用类加载类加载,知道加载位置
是为了避免例如自己写一个String一摸一样的类,先加载启动类和扩展类中的String
18.4 获取和反射Field【属性】(了解)
- 获取Field属性getDeclaredFields()
java
package com.bjpowernode._14Reflect.Relect08.reflect;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class Reflect08 {
public static void main(String[] args) throws ClassNotFoundException {
//获取整个类
Class studentClass = Class.forName("com.bjpowernode._14Reflect.Relect08.bean.Student");
//获取类名
System.out.println(studentClass.getName());//com.bjpowernode._14Reflect.Relect08.bean.Student
System.out.println(studentClass.getSimpleName());//Student
//获取类中所有的public修饰的Field
Field[] fields = studentClass.getFields();
System.out.println(fields.length);//1
//取出元素
Field f = fields[0];
//取数Field的名字
System.out.println(f.getName());//no
System.out.println(f);//public int com.bjpowernode._14Reflect.Relect08.bean.Student.no
//获取所有的Field
Field[] fs = studentClass.getDeclaredFields();
System.out.println(fs.length);//4
//取出数组
for (Field field : fs) {
//
//获取属性的名字
System.out.println(field.getName());/**no name age sex*/
//获取属性的类型
System.out.println(field.getType());//int,java.lang.String,int,boolean
//获取属性的类型简单名
System.out.println(field.getType().getSimpleName());//int sex boolean boolean
//获取属性的修饰符列表:getModifiers修饰符可能有多个
//返回的修饰符是一个数字,每个数字是修饰符的代号,可以将代号转换成字符串吗?可以的
System.out.println(field.getModifiers());//25
int i = field.getModifiers();
String modifierString = Modifier.toString(i);
}
}
}
- 反编译Field
java
package com.bjpowernode._14Reflect.Relect08.reflect;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
//通过反射机制拿到了源码
public class ReReflect {
public static void main(String[] args) throws ClassNotFoundException {
//创建这个是为了拼接字符串
StringBuilder s = new StringBuilder();
Class studentClass = Class.forName("java.lang.String");
// Class studentClass = Class.forName("com.bjpowernode._14Reflect.Relect08.bean.Student");
//public class Student{}
s.append(Modifier.toString(studentClass.getModifiers())+" class "+studentClass.getSimpleName()+"{\n");
Field[] fields = studentClass.getDeclaredFields();
for (Field field : fields) {
s.append("\t"+Modifier.toString(field.getModifiers())+" "+field.getType().getSimpleName()+" "+field.getName()+";\n");
}
s.append("}");
System.out.println(s);
/**
* public final class String{
* private final char[] value;
* private int hash;
* private static final long serialVersionUID;
* private static final ObjectStreamField[] serialPersistentFields;
* public static final Comparator CASE_INSENSITIVE_ORDER;
* }
*
* Process finished with exit code 0
*/
}
}
18.5 通过反射机制访问对象属性
set(obj,"") get(obj)
java
package com.bjpowernode._14Reflect.Relect08.reflect;
import com.bjpowernode._14Reflect.Relect08.bean.Student;
import java.lang.reflect.Field;
/**
如何通过反射机制访问一个java对象的属性?
即给属性赋值set/获取属性的get
*/
public class Reflect09 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
//1.
//我们不适用反射机制,怎么去访问一个对象的属性?
Student s = new Student();
//给属性赋值:三要素:对象,no属性,值
s.no = 1111;
//读属性值:两要素:1对象 2 值
System.out.println(s.no);//1111
//使用反射机制如何访问一个对象的属性
Class studentClass = Class.forName("com.bjpowernode._14Reflect.Relect08.bean.Student");
Object obj = studentClass.newInstance();//obj就是Student对象(底层调用无参构造器方法)
//获取no属性(根据属性名称来获取Field),使用反射机制我可以把属性名称写到配置文件中
Field noField = studentClass.getDeclaredField("no");
//给object对象(Student)的no属性赋值
//虽然使用了反射对象,但是三要素缺一不可:1对象2no属性32222
//虽然反射机制让代码复杂了,但是为了一个"灵活",也是值得的
noField.set(obj,2222);//给obj的对象赋值2222
//获取no属性的值:两要素:1对象 2 值
System.out.println(noField.get(obj));//2222
//2.可以访问私有的属性吗?
Field namefield = studentClass.getDeclaredField("name");
//namefield.set(obj,"jackson");//报错:私有的范围不了
//打破封装(反射机制缺点:打破封装,会给不法分子留下机会!!!)
namefield.setAccessible(true);
namefield.set(obj,"jackson");//报错:私有的范围不了
System.out.println(namefield.get(obj));//jackson
}
}
18.6 反射Method【方法】(了解)
1.可变长度参数
m(int ...args)
java
package com.bjpowernode._14Reflect.Relect08.reflect;
/**
1.可变长度参数要求的参数个数是:0~N个
2.可变长度参数在参数列表中必须在最后一个位置上,而且可变长度参数只能有1个
3.可变参数可以当作一个数组来看待
*/
public class ArgsTest {
public static void main(String[] args) {
m();
m(10);
m(10,20);
//m("abc");编译报错
m2(100);
m2(200,"abc");
m2(200,"abc","def");
m3("ab","de","gh");
String[] strs = {"a","b","c"};
m3(strs);
m3(new String[]{"我","是","中","国","人"});
}
public static void m(int ...args){
System.out.println("m方法执行了!");
}
// public static void m2(String...args1,int...args2){//编译报错
//
// }
//必须只能有一个,并且在最后
public static void m2(int a,String...args2){
}
public static void m3(String...args2){
//args有length属性,说明是一个数组!
//将可变长度参数可以当作数组
for (int i = 0; i < args2.length; i++) {
System.out.println(args2[i]);
}
}
}
- 反射Method方法
java
package com.bjpowernode._14Reflect.Relect08.reflect;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
/**
作为了解内容"反射Method
*/
public class Reflect11 {
public static void main(String[] args) throws ClassNotFoundException {
//获取类
Class userServiceClass = Class.forName("com.bjpowernode._14Reflect.Relect08.service.UserService");
//获取所有的Method
Method[] methods = userServiceClass.getDeclaredMethods();
System.out.println(methods.length);
//遍历方法
for (Method method : methods) {
System.out.println(method.getName());//login logout
//获取方法返回类型
System.out.println(method.getReturnType().getSimpleName());//boolean void
//获取修饰符
System.out.println(Modifier.toString(method.getModifiers()));//public public
//获取参数
Class[] parameterTypes= method.getParameterTypes();
for (Class parameterType : parameterTypes) {
System.out.println(parameterType.getSimpleName());//String String
}
}
}
}
- 反编译Method
java
package com.bjpowernode._14Reflect.Relect08.reflect;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**
* 了解内容:反编译Method()
*/
public class Reflect12 {
public static void main(String[] args) throws ClassNotFoundException {
StringBuilder s = new StringBuilder();
//获取类,可以灵活获取类的方法
// Class userServiceClass = Class.forName("com.bjpowernode._14Reflect.Relect08.service.UserService");
Class userServiceClass = Class.forName("java.lang.String");
//
s.append(Modifier.toString(userServiceClass.getModifiers())+" class "+userServiceClass.getSimpleName()+"{\n");
// public boolean login(String name,String password){
for (Method methods : userServiceClass.getDeclaredMethods()) {
s.append("\t");
s.append(Modifier.toString(methods.getModifiers())+" ");
s.append(methods.getReturnType().getSimpleName()+" ");
s.append(methods.getName());
s.append("(");
Class[] parameterTypes = methods.getParameterTypes();
for (Class parameterType : parameterTypes) {
s.append(parameterType.getSimpleName());
s.append(",");
}
//删除指定下边位置上面的字符
s.deleteCharAt(s.length()-1);
s.append("){}\n");
}
s.append("}");
System.out.println(s);
}
}
18.7 反射机制调用方法(最重要)
invoke()
java
package com.bjpowernode._14Reflect.Relect08.reflect;
import com.bjpowernode._14Reflect.Relect08.service.UserService;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 重点:必须掌握
反射机制,让代码更灵活且具有通用性,可变化的内容都是写到配置文件中
将来创建对象不一样,调用方法也不同了,修改配置文件即可
*/
public class ReflectTest13 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//不使用反射机制,如何调用方法
UserService userService = new UserService();
//调用方法:要素分析:1对象2方法3实际参数列表4返回值
boolean logicSuccess = userService.login("admin", "12312312");
// System.out.println(logicSuccess);
System.out.println(logicSuccess?"登录成功":"登陆失败");
//使用反射机制调用一个对象的方法
Class userClass = Class.forName("com.bjpowernode._14Reflect.Relect08.service.UserService");
Object obj = userClass.newInstance();
//获取方法单纯靠方法名是不行的,有的方法名是重载的,可能同名
//java中如何去定一个方法,通过方法名和参数列表
// public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
//传String.class是因为:java语言中任何一种类型,他都有.class属性 Class z = String.class;
Method loginMethod = userClass.getDeclaredMethod("login", String.class, String.class);
//调用方法 要素分析:1对象2方法3实际参数列表4返回值
// 反射机制中最重要东西!!
Object retValue = loginMethod.invoke(obj, "admin", "12312312");
System.out.println(retValue);//false
}
}
18.8 反射Constructor【构造器】
- 反编译Constructor
java
package com.bjpowernode._14Reflect.Relect08.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.lang.reflect.TypeVariable;
public class Reflect14 {
public static void main(String[] args) throws ClassNotFoundException {
StringBuilder s = new StringBuilder();
//类名public class Vip{}
// Class vipClass = Class.forName("com.bjpowernode._14Reflect.Relect08.bean.Vip");
Class vipClass = Class.forName("java.lang.String");
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[] parametersTypes = constructor.getParameterTypes();
for (Class parametersType : parametersTypes) {
s.append(parametersType.getSimpleName());
s.append(",");
}
//如果数组长度大于0,再删除最后下标位置上的字符
if(parametersTypes.length>0){
s.deleteCharAt(s.length()-1);}
s.append(")}\n");
}
s.append("}");
System.out.println(s);
}
}
- 通过反射机制如何创造对象
java
package com.bjpowernode._14Reflect.Relect08.reflect;
import com.bjpowernode._14Reflect.Relect08.bean.Vip;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
通过反射机制调用构造方法实例化java对象
*/
public class Reflect15 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//不使用反射机制如何创建对象
Vip v1 = new Vip();
Vip v2 = new Vip(110,"zhangsan","2001-10-11",true);
//使用机制如何创建对象
Class c = Class.forName("com.bjpowernode._14Reflect.Relect08.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", "2000-11-08",true);
System.out.println(newObj);//Vip{no=110, name='jackson', birth='2000-11-08', sex=true}
//获取无参数构造方法
Constructor con2 = c.getDeclaredConstructor();
Object newObj2 = con2.newInstance();
System.out.println(newObj2);//Vip{no=0, name='null', birth='null', sex=false}
}
}
18.9 反射获取父类和父接口
- getSuperclass(),getInterfaces()
java
package com.bjpowernode._14Reflect.Relect08.reflect;
/**
重点:给你一个类,怎么获取这个类的父类,已经实现了什么接口
*/
public class Reflect16 {
public static void main(String[] args) throws ClassNotFoundException {
//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
}
}