目录
1.反射概述
2.获取Class并实例化对象
(1)获取Class
(2)实例化对象
3.反射机制访问对象的属性
4.反射机制调用方法
5.通过反射调用构造方法
6.通过反射找父类以及实现的接口
7.注意事项
(1)路径问题
(2)绑定属性资源文件
(3)可变长度参数
(1)反射机制有什么用?
通过java语言中的反射机制可以操作字节码文件。
优点类似于黑客。(可以读和修改字节码文件。)
通过反射机制可以操作代码片段。(class文件。)
(2)反射机制的相关类在哪个包下?
java.lang.reflect.*;
(3)反射机制相关的重要的类有哪些?
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){
this.no = no;
}
// Method
public void setNo(int no){
this.no = no;
}
public int getNo(){
return no;
}
}
第一种方法:Class c=Class.forname("完整包名+类名");
静态方法 方法的参数是字符串 字符串需要一个完整类名 完整类名必须带包名
Class c1=null;
try {
c1=Class.forName("java.lang.String");//c1代表String.class文件,c2代表String类型
Class c2=Class.forName("java.util.Date");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
第二种方法:Class c=对象.getClass();
java中任何一个对象都有一个方法:getClass()
String s="abc";
Class x=s.getClass();//x代表String.class字节码文件,x代表String类型
第三种方法:Class c=任何类型.class属性;
java语言中任何一种类型都有.class属性
Class k= Date.class;
Class f=int.class;
通过反射机制,获取Class后,可以通过Class来实例化对象
- 通过Class的newInstance()方法实例化对象
- newInstance()实际上调用了无参构造,所以必须保证无参构造存在!
Class c=Class.forName("com.bean.User");
Object obj=c.newInstance();
如果只想让一个类的“静态代码块”执行的话,就用Class.forName("该类的类名");
这样类就加载,类加载的时候,静态代码块执行!
在这里,对该方法的返回值不感兴趣,主要是为了使用“类加载”这个动作。
java.lang.reflect.Field类
访问一个java对象的属性
import java.lang.reflect.Field;
public class ReflectTest06 {
public static void main(String[] args) throws Exception{
//整个类
Class studentClass=Class.forName("com.qiqi.bean.Student");
Object obj=studentClass.newInstance();
//根据属性名来获取属性
Field noField=studentClass.getDeclaredField("no");
//给obj对象的no属性赋值
/*
三要素:obj对象,no属性,2022值
反射机制使得代码复杂了,但是更加灵活
*/
noField.set(obj,2022);
//获取属性的值
//两个要素:obj对象和属性的值
System.out.println(noField.get(obj));
Field nameField= studentClass.getDeclaredField("name");
//访问私有属性需要打破封装
//反射机制的缺点:打破封装可能会给不法分子留下机会
nameField.setAccessible(true);
nameField.set(obj,"Tom");
System.out.println(nameField.get(obj));
}
}
java.lang.reflect.Method类
区分方法用方法名+参数
import java.lang.reflect.Method;
/*
反射机制使代码具有通用性,将可变化的内容写到配置文件中
修改配置文件后,创建的对象不同,调用的方法不同,但是代码不变
*/
public class ReflectTest10 {
public static void main(String[] args) throws Exception {
Class userServiceClass=Class.forName("com.qiqi.bean.UserService");
Object obj=userServiceClass.newInstance();
//获取Method
Method loginMethod=userServiceClass.getDeclaredMethod("login",String.class,String.class);
/*
调用方法
invoke()
要素1:对象userService-----loginMethod
要素2:login方法名-----obj对象
要素3:实参列表------"admin","123"
要素4:返回值------retValue
*/
Object retValue=loginMethod.invoke(obj,"admin","123");
System.out.println(retValue);
}
}
java.lang.reflect.Constructor类
区分构造方法只需要形参
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class ReflectTest12 {
public static void main(String[] args) throws Exception {
Class vipClass=Class.forName("com.qiqi.bean.Vip");
//调用无参构造方法,方法一
Object obj=vipClass.newInstance();
System.out.println(obj);
//调用有参构造方法
//1.获取这个有参构造方法
Constructor con= vipClass.getDeclaredConstructor(int.class,String.class,String.class,boolean.class);
//2.调用构造方法new对象
Object newObj=con.newInstance(110,"Tom","1990-11-10",true);
System.out.println(newObj);
//调用无参构造方法,方法二
Constructor con2=vipClass.getDeclaredConstructor();
Object newObj2=con2.newInstance();
System.out.println(newObj2);
}
}
package com.qiqi.reflect;
public class RefelctTest13 {
public static void main(String[] args) throws Exception {
Class stringClass=Class.forName("java.lang.String");
//获取String类的父类
Class superClass=stringClass.getSuperclass();
System.out.println(superClass);
//获取String类实现的多个接口
Class[] interfaces=stringClass.getInterfaces();
for(Class in:interfaces){
System.out.println(in.getName());
}
}
}
这种方式是为了获取一个文件的绝对路径。(通用方式,不会受到环境移植的影响。)
但是该文件要求放在类路径下,换句话说:也就是放到src下面,src看做是类的根路径。
String path = Thread.currentThread().getContextClassLoader()
.getResource("写相对路径,但是这个相对路径从src出发开始找").getPath();
currentThread()当前线程对象 getContextClassLoader()是线程对象的方法,可以获取到当前现成的类加载器对象 getResource()是类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源 getPath()获取路径
直接以流的形式返回:
public static void main(String[] args) throws Exception {
//获取一个文件的绝对路径!
/*String path=Thread.currentThread().getContextClassLoader()
.getResource("classinfo.properties").getPath();
FileReader fr=new FileReader(path);*/
//直接以流的形式返回
InputStream fr=Thread.currentThread().getContextClassLoader().
getResourceAsStream("classinfo.properties");
Properties pro=new Properties();
pro.load(fr);
fr.close();
//通过key获取value
String className=pro.getProperty("className");
System.out.println(className);
java.util包下提供了一个资源绑定器,便于获取属性配置中的内容
public static void main(String[] args) {
ResourceBundle bundle=ResourceBundle.getBundle("classinfo");
String className=bundle.getString("className");
System.out.println(className);
ResourceBundle bundle1=ResourceBundle.getBundle("com/qiqi/bean/classinfo2");
System.out.println(bundle1.getString("className"));
}
语法:类型...(一定是三个点) 要求的参数个数是0~n个 可变长度参数在参数列表中的最后一个,且参数列表中只能有一个可变长参数
public class ArgsTest {
public static void main(String[] args) {
m();
m(10);
m(1,2,3);
String[] str={"a","b","c"};
n(str);
}
public static void m(int...args){
System.out.println("m方法执行了!");
//args有length属性,说明args是一个数组
for(int i=0;i< args.length;i++)
System.out.println(args[i]);
}
public static void n(String...args){
for(int i=0;i< args.length;i++)
System.out.println(args[i]);
}
}