1.1反射机制的作用
通过java 语言中的发射机制可以操作字节码文件。
通过反射机制可以操作代码片段。(.class文件)。
1.2 反射机制的原理:
1、首先明确的概念: 一切皆对象----类也是对象.
2、然后知道类中的内容 :modifier(权限修饰符) constructor(构造方法) field(属性) method(方法).
3、其次明白加载: 当Animal.class在硬盘中时,是一个文件,当载入到内存中,可以认为是一个对象,是java.lang.class的对象.
1.3反射机制相关类所在包
java.lang.reflect.*;
1.4反射机制相关的重要类
java.lang.Class 代表整个字节码,代表一个类型(整个类)
java.lang.reflect.Method 代表字节码中的方法字节码(类中的方法)
java.lang.reflect.Constructor 代表字节码中的构造方法字节码(类中的构造方法)
java.lang.reflect.Field 代表字节码中的属性字节码(类中的成员变量:静态变量+实例变量)
1.5获取整个类的字节码的三种方式:
第一种:Class c = Class.forName("完整包名+类名");
第二种:Class c = 对象.getClass();
第三种:Class c=任何类型.class;
public static void main(String[] args) {
Class c1=null;
Class c2=null;
try {
/*
方法一:Class.forName():Class类下的forName()方法
1.静态方法
2.方法的参数是一个字符串,字符串需要是一个完整的类名
3.完整的类名必须带有包名。java.lang包也不可以省略
4.返回Class对象
*/
//获取整个类的字节码文件
c1=Class.forName("java.lang.String");//c1代表String.class,或者说明c1代表String类型
c2=Class.forName("java.util.Date");//c2代表Date类型
Class c3=Class.forName("java.lang.Integer");//c3代表Integer类型
System.out.println(c1);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 方法二:java中任何一个对象(Object)都有一个方法:getClass()
String str="www";
Class x=str.getClass();//x代表String.class字节码文件,x代表String类型
System.out.println(c1==x);//true(判断的是对象的内存地址)
Date d=new Date();
Class dCl=d.getClass();
System.out.println(c2==dCl);//true(c2和dCl两个变量中保存的内存地址都是一样的,都指向这个方法区中的字节码文件)
// 方法三:java语言中任何一种类型,包括基本数据类型,它都有.class属性
Class a = String.class;//a代表String类型
Class b = int.class;//b代表int类型
Class c = double.class;//c代表double类型
Class f = Date.class;//f代表Date类型
System.out.println(x==a);//true
}
注意:字节码文件装载到JVM中时,只装载一份。
1.6 反射机制的应用
1.通过反射机制获得的Class,可以调用无参数构造方法来实例化对象
通过Class的newInstance()方法来实例化对象。
// c代表日期Date类型
Class c = Class.forName("java.util.Date");
// 实例化一个Date日期类型的对象
Object obj = c.newInstance();
注意:newInstance()方法内部实际调用了无参构造,必须确保无参构造存在,否则会出现"实例化"异常。
public class ReflectTest02 {
public static void main(String[] args) {
// 使用构造方法创建对象
User user=new User();
System.out.println(user);
// 使用反射机制的方式创建对象
try {
// 通过反射机制,获得Class,通过Class来实例化对象
Class c = Class.forName("com.wanho.po.User");//c代表User类型
// newInstance()方法调用User类的无参构造,完成对象的创建。
// 注意:newInstance()方法调用的是无参构造,要确保无参构造的存在。
Object obj = c.newInstance();
System.out.println(obj);//com.wanho.po.User@6d06d69c
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
2. 验证反射机制的灵活性
java代码写一遍,再不改变java源代码的基础之上,可以做到不同对象实例化。符合OCP开闭原则:对扩展开放,对修改关闭。
public class ReflectTest03 {
public static void main(String[] args) throws Exception {
// 只能创建一个User类对象
//User user = new User();
// 通过修改配置文件,创建不同的实例化对象
// 通过IO流读取classinfo.properties配置文件
// 工程下的相对路径/review/classinfo.properties
FileReader reader = new FileReader("classinfo.properties");
// 创建属性类对象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();
System.out.println("通过反射实例化的对象:"+obj);
}
}
3. 如果只想让一个类的“静态代码块”执行的话,可以使用Class.forName("该类的完整类名称")
重点:如果你只是希望一个类的静态代码块执行,其他代码一律不执行,可以使用:Class.forName("完整类名"); 这个方法的执行会导致类加载,类加载时,静态代码块执行。
public class ReflectTest04 {
public static void main(String[] args) {
try {
//Class.forName()这个方法的执行会导致:类加载
Class.forName("com.wanho.reflect.MyClass");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class MyClass {
//静态代码块在类加载时执行,并且只执行一次
static {
System.out.println("MyClass类的静态代码块执行了!!!!");
}
}
4.文件路径问题的研究
以下是获取一个文件的绝对路径的通用方式。前提是:文件需要在类路径下(src下),这种方式才可以使用。
public class AboutPath {
public static void main(String[] args) throws Exception{
//这种方式的路径缺点是:移植性差,默认当前路径是project的根
//代码离开eclipse,到了其他位置,路径不是project的根了,这时这个路径就无效了
//FileReader reader = new FileReader("classinfo.properties");
//下面是一种比较通用的路径,即使代码换了位置,这样的编写仍然是通用的
//注意:使用以下通用方式的前提是:这个文件必须在类路径下。
//类路径下:凡是在src下的都是类路径下。
//src是类的根路径
/*
类加载器的种类:启动类加载器、应用类加载器、扩展类加载器
Thread.currentThread() 当前线程对象
getContextClassLoader() 是线程对象的方法,可以获取到当前线程的类加载器对象
getResource() 【获取资源】这是类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源
*/
//以下获取文件的绝对路径是通用的
String path = Thread.currentThread().getContextClassLoader().getResource("classinfo2.properties").getPath();
//采用以上代码可以获得一个文件的绝对路径。
// /D:/software/eclipse-workspace/review/bin/classinfo2.properties
System.out.println(path);
// 获取db.properties文件的路径,需要从类的根路径下作为起点开始
String path2 = Thread.currentThread().getContextClassLoader().getResource("com/wanho/po/db.properties").getPath();
System.out.println(path2);
}
}
5.IoProperties.java
注意:classinfo2.properties配置文件在类路径(src)下
public class IoPropertiesTest {
public static void main(String[] args) throws Exception{
//获取一个文件的绝对路径-----根据相对路径获取绝对路径
// String path = Thread.currentThread().getContextClassLoader().getResource("classinfo2.properties").getPath();
//System.out.println(path);
// 读取配置文件
// FileReader reader = new FileReader(path);
InputStream reader = Thread.currentThread().getContextClassLoader().getResourceAsStream("classinfo2.properties");
Properties pro = new Properties();
// 加载配置文件
pro.load(reader);
reader.close();
// 根据key获取value
String className = pro.getProperty("className");
System.out.println(className);
}
}
6. 资源绑定器
java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容。
注意:使用以下这种方式的时候,属性配置文件xxx.properties必须放到类路径下。
public class ResourceBundleTest {
public static void main(String[] args) {
// 资源绑定器,只能绑定xxx.properties文件。并且这个文件必须在类路径下。文件的扩展名也必须是properties
// 注意:在写路径的时候,路径后面的扩展名不能写。
// ResourceBundle bundle = ResourceBundle.getBundle("classinfo2");
ResourceBundle bundle = ResourceBundle.getBundle("com/wanho/po/db");
// 根据key获得value
String className = bundle.getString("className");
System.out.println(className);
}
}
1.7 反射属性Field(Field类)
1.7.1 获取Field对象
方法 | 含义 |
---|---|
getField(String name) | 获取指定的public修饰的属性对象 |
getFields() | 获取类中所有的使用public修饰的属性对象,返回属性数组Field[] |
getDeclaredField(String name) | 获取指定的属性对象 |
getDeclaredFields() | 获取所有属性对象,返回属性数组Field[] |
getModifiers() | 获取属性的修饰符列表,返回的修饰符是一个int型数字,每个数字是修饰符的代号 |
Modifier.toString(int i) | 将修饰符代号转换成字符串,返回String类型 |
getType() | 获取属性的类型,返回Class对象 |
public class ReflectTest05 {
public static void main(String[] args) throws Exception{
// 获取整个类
Class studentClass = Class.forName("com.wanho.po.Student");
System.out.println("studentClass:"+studentClass);
// 获取类名
String className = studentClass.getName();
System.out.println("完整类名:"+className);
String simpleName = studentClass.getSimpleName();
System.out.println("简单类名:"+simpleName);
System.out.println("================");
// 获取类中所有的使用public修饰的属性
Field[] fields = studentClass.getFields();
System.out.println(fields.length);
// 取出属性
Field f = fields[0];
// 取出属性的名称
String fieldName = f.getName();
System.out.println(fieldName);
// 获取所有属性
Field[] fields2 = studentClass.getDeclaredFields();
System.out.println(fields2.length);
System.out.println("================");
// 遍历
for(Field field:fields2) {
// private java.lang.String com.wanho.po.Student.name
System.out.println(field);
// 获取属性的修饰符列表
int i = field.getModifiers(); //返回的修饰符是一个数字,每个数字是修饰符的代号!
System.out.println(i);
// 将修饰符代号转换成字符串
String modifierString = Modifier.toString(i);
System.out.println("属性的修饰符:"+modifierString);
// 获取属性的类型
Class fieldType = field.getType();
//String fName = fieldType.getName(); //java.lang.String
String fName = fieldType.getSimpleName(); //String
System.out.println("属性的类型:"+fName);
// 获取属性的名字
System.out.println("属性的名字:"+field.getName());
}
}
}
1.7.2 反编译Field
public class ReflectTest06 {
public static void main(String[] args) throws Exception{
// 创建这个为了拼接字符串
StringBuilder sBuilder = new StringBuilder();
// 获取整个类
Class studentClass = Class.forName("com.wanho.po.Student");
sBuilder.append(Modifier.toString(studentClass.getModifiers())+" class "+studentClass.getSimpleName()+"{\n");
// 获取所有属性
Field[] fields = studentClass.getDeclaredFields();
for(Field field:fields) {
sBuilder.append("\t");
sBuilder.append(Modifier.toString(field.getModifiers()));
sBuilder.append(" ");
sBuilder.append(field.getType().getSimpleName());
sBuilder.append(" ");
sBuilder.append(field.getName());
sBuilder.append(";\n");
}
sBuilder.append("}");
System.out.println(sBuilder);
}
}
1.7.3 通过反射机制访问对象属性(重点)
/**
* (掌握):通过反射机制访问对象属性
* 怎么通过反射机制访问一个java对象的属性?
* 给属性赋值set
* 获取属性的值get
*/
public class ReflectTest07 {
public static void main(String[] args) throws Exception{
//不使用反射机制,访问一个对象的属性
Student student = new Student();
student.no=1001;//给student对象的no属性赋值1001
//要素1:Student对象;要素2:no属性;要素3:属性值1001
// 读属性值
System.out.println(student.no);
//使用反射机制,怎么去访问一个对象的属性。(set get)
// 获取整个类
Class studentClass = Class.forName("com.wanho.po.Student");
Object obj = studentClass.newInstance();// Student对象。(底层调用无参构造方法)
// 获取no属性(根据属性名获得属性)
Field noField=studentClass.getDeclaredField("no");
// 给属性赋值
/*
虽然使用了反射机制,但是三要素还是缺一不可:
要素1:obj对象
要素2:no属性
要素3:no属性值1002
*/
noField.set(obj, 1002);//给obj对象(Student对象)的no属性赋值1002
// 读取属性的值
// 两个要素:获取obj对象的no属性的值。
System.out.println(noField.get(obj));
// 可以访问私有的属性吗?---- 不可以
Field nameField=studentClass.getDeclaredField("name");
//System.out.println(nameField);
// 打破封装(反射机制的缺点:打破封装,可能会给不法分子留下机会!!!)
// 这样设置完之后,在外部也是可以访问private的。
nameField.setAccessible(true);
// 给name属性赋值
nameField.set(obj, "marry");
// 获取name属性的值
System.out.println(nameField.get(obj));
}
}
1.8 反射Method类(重点)
1.8.1 反射Method
方法 | 含义 |
---|---|
getMethods() | 获取所有公共public的方法对象 |
getMethod(String name, Class... parameterTypes) | 获取指定的公共public的方法对象 |
getDeclaredMethods() | 获取所有Method方法对象,返回Method[] |
getDeclaredMethod(String name, Class... parameterTypes) | 获取指定的方法对象 |
getParameterTypes() | 获取参数列表,返回Class[] |
public class ReflectTest08 {
public static void main(String[] args) throws Exception{
// 获取类
Class uSClass = Class.forName("com.wanho.service.UserService");
// 获取所有方法Method
Method[] methods = uSClass.getDeclaredMethods();
// 遍历
for(Method method:methods) {
// 获取修饰符列表
System.out.println(Modifier.toString(method.getModifiers()));
// 获取方法返回值类型
System.out.println(method.getReturnType().getSimpleName());
// 获取方法名
System.out.println(method.getName());
// 获取参数列表
Class[] parameterTypes = method.getParameterTypes();
// 遍历
for(Class parameterType:parameterTypes) {
System.out.println(parameterType.getSimpleName());
}
}
}
}
1.8.2 反编译Method
public class ReflectTest09 {
public static void main(String[] args) throws Exception{
StringBuilder s = new StringBuilder();
// 获取类
Class uSClass = Class.forName("java.lang.String");
s.append(Modifier.toString(uSClass.getModifiers())+" class "+uSClass.getSimpleName()+"{\n");
Method[] methods = uSClass.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(",");
}
s.deleteCharAt(s.length()-1);
s.append(")");
s.append("{}\n");
}
s.append("}");
System.out.println(s);
}
}
1.8.3 通过反射机制调用方法(重点)
Method类中的invoke()方法
方法 | 含义 |
---|---|
invoke(Object obj, Object ...value) | 调用该方法 |
/**
* (重点)通过反射机制怎么调用一个对象的方法
* 五个星※※※※※
*
* 反射机制,让代码很具有通用性,可变化的内容都是写到配置文件当中,
* 通过修改配置文件,可以创建不同的对象,调用不同的方法,
* 但是java代码不需要做任何改动。这就是反射机制的魅力。
* @author Lucky
*
*/
public class ReflectTest10 {
public static void main(String[] args) throws Exception{
// 不使用反射机制,怎么调用方法
//创建对象
UserService userService = new UserService();
// 调用方法
/*
要素分析:
要素1:userService对象
要素2:login方法名
要素3:实参列表
要素4:返回值
*/
boolean f= userService.login("admin", "123");
System.out.println(f);
//userService.logout();
// 使用反射机制
Class c = Class.forName("com.wanho.service.UserService");
// 创建对象
Object obj = c.newInstance();
// 获取方法对象
Method loginMethod = c.getDeclaredMethod("login", String.class,String.class);
// 调用方法
// 调用方法需要4个要素。
// 反射机制中最最最重要的一个方法,必须记住。
// 四要素
/*
loginMethod方法
obj对象
"admin"、"123"实际参数
retValue返回值
*/
Object retValue = loginMethod.invoke(obj, "admin", "123");//使用invoke调用方法
System.out.println(retValue);
}
}
1.9 反射Constructor
1.9.1 反编译Constructor
public class ReflectTest11 {
public static void main(String[] args) throws Exception{
StringBuilder s = new StringBuilder();
Class vipClass = Class.forName("com.wanho.po.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[] parameterTypes = constructor.getParameterTypes();
for(Class parameterType : parameterTypes) {
s.append(parameterType.getSimpleName());
s.append(",");
}
if(parameterTypes.length > 0) {
// 删除最后下标位置上的字符串
s.deleteCharAt(s.length() - 1);
}
s.append("){}\n");
}
s.append("}");
System.out.println(s);
}
}
1.9.2 通过反射机制调用构造方法
public class ReflectTest12 {
public static void main(String[] args) throws Exception{
// 不使用反射机制创建对象
Vip vip = new Vip();
Vip vip2 = new Vip(1001, "marry", "1999", true);
// 使用反射机制创建对象
Class vipClass = Class.forName("com.wanho.po.Vip");
// 调用无参构造
Object obj = vipClass.newInstance();
System.out.println(obj);
// 调用有参构造方法
// 1.获取构造方法
Constructor con = vipClass.getDeclaredConstructor(int.class);
// 2.调用构造方法的new对象
Object obj2 = con.newInstance(1002);
System.out.println(obj2);
// 获取无参构造的方法
Constructor con2 = vipClass.getDeclaredConstructor();
Object obj3 = con2.newInstance();
System.out.println(obj3);
}
}
1.10 通过反射获取父类和父接口
public class ReflectTest13 {
public static void main(String[] args) throws Exception{
// 获取String类
Class stringClass = Class.forName("java.lang.String");
// 获取String类的父类
Class superClass = stringClass.getSuperclass();
System.out.println("String的父类:"+superClass.getName());
// 获取String类实现的所有接口(一个类可以实现多个接口)
Class[] interfaces = stringClass.getInterfaces();
for(Class inter : interfaces) {
System.out.println("String类实现的接口:"+inter.getName());
}
}
}