概论
-
反射机制有什么用?
通过java语言中的反射机制可以操作(读和修改)字节码文件
通过反射机制可以操作代码片段
-
反射机制的相关类在哪个包下?
java.lang.reflect.*
-
反射机制相关的类有哪些?
java.lang.class:代表字节码文件,代表一个类型
java.lang.reflecr.method:代表字节码中的方法字节码
java.lang.reflect.Constructor:代表字节码中的构造方法字节码
java.lang.reflect.Field:代表字节码中的属性字节码
获取Class的三种方式
/*
要操作一个类的字节码,要首先获取这个类的字节码
怎么获取java.lang.Class实例?
三种方式:
第一种:Class c =new Class("完整路径带包名");
第二种:Class c =对象.getClass();
第三种:Class c int.class;
*/
public class ReflectTest01 {
public static void main(String[] args) throws ClassNotFoundException {
/*
Class.forName()
1.静态方法
2.方法的参数是一个字符串
3.字符串需要的是一个完整的类名
4.完整类名必须带有包名,
*/
Class c1 =Class.forName("java.lang.String");
Class c2 =Class.forName("java.util.Date");
//java中任何一个对象都有一个方法,getClass()
String s="sada";
Class x =s.getClass(); //x代表String类型
//第三种方法:java语言中任何一种类型,哪怕是基本数据类型,他都有.calss属性
Class z=String.class; //z代表String类型
}
}
获取到Class能做什么?
newInstance方法内部实际上调用了无参数构造方法,必须保证无参数构造存在才行
/*
获取到Class,能干什么?
通过Class的newInstance()方法来实例化对象
注意:newInstance方法内部实际上调用了无参数构造方法,必须保证无参数构造存在才行
*/
public class ReflectTest02 {
public static void main(String[] args) {
//通过反射机制获取User的Class
try {
Class c= Class.forName("com.baidu.bean.User.java");
//newInstance() 创建的是一个User的实例,调用了User的无参构造方法
//必须保证User的无参构造函数是存在的
Object obj= c.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
研究一下:Class.forName()发生了什么?
如果你只是希望一个类的静态代码块执行,其他代码一律不执行,你可以使用:
Class.forName(“完整类名”);
这个方法的执行会导致类加载,类加载时,静态代码块执行
提示:
后面JDBC技术会使用到
获取文件的绝对路径
/*
研究一下文件路径的问题
怎样获取一个文件的绝对路径.以下这种讲解方式是通用的.但前提是:文件需要在类路径下,才能使用这种方式
*/
public class AboutPathTest01 {
public static void main(String[] args) throws FileNotFoundException {
//这种方式的路径的缺点是:移植性差,IDEA中默认的当前路径就是1.txt的根
//这个代码假设离开了IDEA,换到了其他位置,可能当前路径就不行了
// FileReader fr=new FileReader("User.java" );
//接下来说一种比较通用的方式,即使代码位置更换了,这样编写仍然是通用的
//注意:前提是这个文件必须在类路径下
//什么事类路径下? 凡是在src下的都是类路径下
//src是类的根路径
/*
解释一下:
Thread.CurrentThread() 当前线程方法
getContextClassLoader() 是线程对象方法,可以获取到当前线程的类加载器对象
getResource() [获取资源]是类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源.
*/
// String path =Thread.currentThread().getContextClassLoader()
// .getResource("从类的根路径下作为起点开始").getPath();
// //以上代码可以拿到文件的绝对路径
// System.out.println(path);
//获取db.properties文件的绝对路径
String path2 =Thread.currentThread().getContextClassLoader()
.getResource("com/baidu/reflect/db.properties").getPath();
System.out.println(path2);
}
}
String path =Thread.currentThread().getContextClassLoader()
.getResource(“从类的根路径下作为起点开始”).getPath();
这种方式是为了获取一个文件的绝对路径,但是该文件要求放在类路径下,换句话说:也就是放到src下面.
src是类的根路径
这是通用的方式的,不受环境移植的影响
IO中的用途
直接以流的形式返回
InputStream reader =Thread.currentThread().getContextClassLoader().getResourceStream( “name”);
资源绑定器
/*
java.util包下提供了一个资源绑定器,便于获取配置文件的内容
使用以下方式的时候,属性配置文件xxx.properties必须放到类路径下
*/
public class ResoureBundle {
public static void main(String[] args) {
//资源绑定器,只能绑定xxx.properties文件,并且这个文件必须在类路径下.文件拓展名也必须是properties
//并且在写文件的时候,路径后面的拓展名不用写
ResourceBundle bundle =ResourceBundle.getBundle("com/baidu/reflect/db");
String className =bundle.getString("className");
System.out.println(className);
}
}
双亲委派机制(了解)
java中为了保证类的加载的安全,使用了双亲委派机制,优先从启动类加载器中加载,这个称为"父"
“父"无法加载到,再从拓展类加载器中加载,这个称为"母”
双亲委派,如果都加载不到,才会考虑从应用类加载器中加载,直到加载到为止
获取Field(了解)
public static void main(String[] args) throws ClassNotFoundException {
//获取整个类
Class studentClass = Class.forName("com.baidu.bean.Student");
//获取所有的类Filed
Field[] fields= studentClass.getFields();
System.out.println(fields.length); //测试数组中只有一个元素
//取出这个Filed的名字
String fieldName =fields[0].getName();
System.out.println(fieldName);
System.out.println("=========================");
//获取所有的Field
Field[] fs = studentClass.getDeclaredFields();
System.out.println(fs.length); //4
for (Field f : fs) {
//获取属性的修饰符列表
int i =f.getModifiers(); //返回的修饰符是一个数字,每个数字是修饰符的代号
System.out.println(i);
//可以将这个"代号"数字转换成字符串吗?
String moddifierString = Modifier.toString(i);
System.out.println(moddifierString);
//获取属性的类型
Class fieldType = f.getType();
String fName =fieldType.getName();
System.out.println(fName);
//获取属性的名字
System.out.println(f.getName());
}
}
## 反射机制访问对象属性(重点)
核心代码:
/*
怎样通过反射机制访问一个java对象的属性?
给属性赋值set
获取属性的值get
*/
public class ReflectTest04 {
public static void main(String[] args) throws Exception {
Class studentClass =Class.forName("com.baidu.bean.Student");
//获取它的属性
Object obj =studentClass.newInstance();//obj就是Student的对象(底层调用无参数构造方法)
//获取id属性(依靠名字区分)
Field noField =studentClass.getDeclaredField("id");
//给obj对象(Student对象)的no属性赋值
/*
虽然使用了反射机制,但是三要素依旧缺一不可
要素1:obj对象
要素2:no属性
要素3:1234值
*/
noField.set(obj,1234); //给obj对象的no属性赋值1234
//读取属性的值
System.out.println(noField.get(obj));
//可以访问私有属性吗?
Field nameField = studentClass.getDeclaredField("name");
//打破封装(反射机制的缺点,打破封装,可能会给不法分子留下机会)
//这样设置完后,在外部也是可以访问到private的
nameField.setAccessible(true);
//给name属性赋值
nameField.set(obj,"jike");
System.out.println(nameField.get(obj));
}
}
通过反射机制调用方法(重点)
核心代码:
Object retValue= loginMethod. invoke(obj,“admin”,“123”);
//翻译一下:调用obj对象中的loginMethod的方法,返回值是retValue,invoke负责调用
/*
通过反射机制怎样调用一个对象的方法
*/
public class ReflectTest07 {
public static void main(String[] args) throws Exception{
Class userService =Class.forName("com.baidu.reflect.UserService");
//创建对象
Object obj= userService.newInstance();
//获取Method
Method loginMethod =userService.getDeclaredMethod("login", String.class, String.class);
//调用方法
/*
要素分析:
要素1:对象userService obj是对象
要素2:login方法名 "admin""123"是实参
要素3:实参列表 retValue是返回值
要素4:返回值
*/
Object retValue= loginMethod. invoke(obj,"admin","123");
//翻译一下:调用obj对象中的loginMethod的方法,返回值是retValue,invoke负责调用
System.out.println(retValue);
}
}
通过反射机制读取注解
public static void main(String[] args) throws ClassNotFoundException {
Class c =Class.forName("com.baidu.annotation.AnnotationTest01");
//判断类上面是否有这个注解
if(c.isAnnotationPresent(MyAnnotationTest01.class)){
//获取该注解对象
MyAnnotationTest01 myAnnotation=(MyAnnotationTest01) c.getAnnotation(AnnotationTest02.class);
//获取注解对象属性怎么办?和调接口没区别
String value= myAnnotation.value();
System.out.println(value);
}
}