反射
Reflection(反射)是被视为动态语言的关键
- 反射机制允许程序在执行期获得任何类的内部信息
- 并能直接操作任意对象的内部属性及方法(操作字节码)。
加载完类之后,在堆内存的方法区中就产生了一个class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。
关于java.lang.Class类的理解
类的加载过程:
- 程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。
- 接着我们使用java.exe命令对Class文件进行解释运行。相当于将某个字节码文件加载到内存中,此过程就称为类的加载。
- 加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例。
获取Class
创建Class的四种方式
//方式1 通过运行时类的属性
Class<User> userClass=User.class;
Class s = String.class;
//方式2 通过运行时类的对象,调用 getClass方法
User u=new User();
Class userClass2=u.getClass();
//方式3 通过forName静态方法
Class c1= Class.forName("java.lang.String");
Class userClass3 = Class.forName("com.jie.pojo.User");
//方式4 使用类的加载器 (了解即可)
ClassLoader classLoader=reflectionTest.class.getClassLoader();
Class userClass4=classLoader.loadClass("com.jie.pojo.User");
System.out.println(userClass4);
- 无论是通过类还是对象创建的Class,都是指向的同一个字节码文件
public static void main(String[] args) throws ClassNotFoundException {
Class s1= Class.forName("java.lang.String");
Class s2 = "asd".getClass();
System.out.println(s1==s2);//true
}
实例化对象
使用反射创建对象
Class<Date> dateClass = Date.class;
//反射调用 无参构造函数 创建对象
Date now = dateClass.newInstance();
System.out.println(now);
通过IO流 读取配置文件,创建对象
- 例如项目中resources(也可以在src目录下)目录下有mybatis.properties文件
-
首先需要使用到
String path = Thread.currentThread().getContextClassLoader() .getResource("mybatis.properties") .getPath();
path为编译后tartget文件夹的路径
-
读取文件位置后即可通过反射,实例化对象
-
优点:适用于寻找各种场景,脱离了idea也能使用
代码:
String path = Thread.currentThread().getContextClassLoader()
.getResource("mybatis.properties")
.getPath();
System.out.println(path);
FileReader fileReader = new FileReader(path);
Properties properties=new Properties();
//读取
properties.load(fileReader);
//通过反射 实例化对象
Class date = forName(properties.getProperty("class"));
System.out.println(date.newInstance());
第二种简单方法
- 文件必须在项目的src 或者 resources目录下
- 必须是**.properties**结尾的文件
//读取文件
ResourceBundle mybatis = ResourceBundle.getBundle("mybatis");
//读取key
String _class = mybatis.getString("class");
Class date = forName(_class);
//实例化对象
System.out.println(date.newInstance());
属性 Field
获取属性
- java.lang.reflect.Field:代表字节码中的属性字节码
- 可以通过Class获取属性
- Filed类即代表一个属性,里面包含这个属性的 权限、类型、名字;
Pojo如下:
public class User {
private int id=1;
boolean isBoy;
protected int salary;
public String name="sad";
}
通过Class获取public属性
//获取public属性数组
Field[] fields = userClass.getFields();
System.out.println(fields.length); //1
//获取public属性 某个属性
Field salary = userClass.getField("salary"); //因为 salary不为public 所以error
System.out.println(salary.getName());
获取任何权限的属性
//获取属性数组(不受权限限制)
Field[] fields = userClass.getDeclaredFields();
System.out.println(fields.length); //4
//获取指定的某个属性
Field salary = userClass.getDeclaredField("salary");
System.out.println(salary.getName());
可以通过Field类获取 类名、类型、修饰符
//获取属性的名字
System.out.println(salary.getName());
//获取属性的返回值
Class<?> type = salary.getType();
System.out.println(type.getName());
//获取属性的权限
int modifiers = salary.getModifiers(); //2:private default:0 4:protected 1:public
//转化成String
System.out.println(Modifier.toString(modifiers));
反编译
通过Class反编译出类的属性结构 (了解即可)
public static void main(String[] args) throws Exception {
Class<?> c = forName("java.lang.String");
StringBuilder s=new StringBuilder();
s.append(Modifier.toString(c.getModifiers()) + " class " + c.getSimpleName() + " {\n");
//获取所有的属性
Field[] fields = c.getDeclaredFields();
for(Field f : fields){
s.append("\t");
s.append(Modifier.toString(f.getModifiers()));
s.append(" ");
s.append(f.getType().getSimpleName());
s.append(" ");
s.append(f.getName());
s.append(";\n");
}
s.append("}");
System.out.println(s);
}
访问对象的属性
Field salary = userClass.getDeclaredField("name");
//可以给public属性 关键字赋值
salary.set(user,"zcj");
//访问
System.out.println(salary.get(user));
- 但如果是 除public外的其他的属性,会报错
Field salary = userClass.getDeclaredField("salary");
//打破属性的封装 即可访问类的私有属性
salary.setAccessible(true);
salary.set(user,2000);
System.out.println(salary.get(user));
方法 Method
- java.lang.reflect.Method:代表字节码中的方法字节码
- Method类中包含了这个类的修饰符、返回值、名字、参数等
userService类
public class UserService {
public boolean login(String name,String password){
if (name.equals("admin")&&password.equals("123456")){
System.out.println("登陆成功");
return true;
}else{
System.out.println("登陆失败");
return false;
}
}
protected void logout(){
System.out.println("安全退出");
}
}
获取方法
Class userServiceClass=Class.forName("com.jie.service.UserService");
//获取该类的public方法 (包括类本身和继承的方法)
Method[] methods = userServiceClass.getMethods();
//获取该类的任何属性的 本类方法(不包括继承)
Method[] methods = userServiceClass.getDeclaredMethods();
获取指定放方法
- 因为方法可以重载,只传入方法名无法获得指定方法
- 所以还需要传入方法的参数
getMethod //获取public的方法 包括继承的方法
getDeclaredMethod //获取全部属性的方法 但不包括继承的方法
//获取login方法
Method login = userServiceClass.getMethod("login",String.class,String.class);
//获取logout方法
Method logout = userServiceClass.getDeclaredMethod("logout");
访问属性
//获取方法的访问修饰符
String modifier = Modifier.toString(logout.getModifiers());
//获取返回类型
System.out.println(logout.getReturnType());
//获取方法的参数列表
Class<?>[] parameterTypes = logout.getParameterTypes();
for (Class c:parameterTypes){
//输出返回值
System.out.println(c.getSimpleName());
}
调用方法
//获取login方法
Method login = userServiceClass.getMethod("login",String.class,String.class);
//创建对象
Object o = userServiceClass.newInstance();
//利用反射 调用方法
login.invoke(o,"admin","123456");
- 调用有参数的方法,需要传参
构造方法 Constructor
- java.lang.reflect.Constructor:代表字节码中的构造方法字节码
- 需要根据传入的参数,确定获取哪个构造函数
Class userClass = User.class;
//直接调用无参构造函数
Object user = userClass.newInstance();
//获取无参构造方法
Constructor constructor = userClass.getDeclaredConstructor();
Object o = constructor.newInstance();
//获取有参构造方法
Constructor constructor2 = userClass.getDeclaredConstructor(int.class, String.class);
Object o2 = constructor2.newInstance(1,"asd");
获取接口
//获取接口
Class[] interfaces = userClass.getInterfaces();
for (Class c:interfaces){
System.out.println(c);
}
方法
//创建运行时类
Class user_class= User.class;
//调用指定构造函数
Constructor constructor = user_class.getConstructor(int.class, String.class);
User u= (User)constructor.newInstance(1,"zcj");
//获取无参构造函数
User u= userClass.newInstance();
//通过反射私有属性
Field id = user_class.getDeclaredField("id");
id.setAccessible(true);//通过暴力访问 取消的私有权限的限制
id.set(u,2);
System.out.println(u);
//获取指定方法
Method hello=user_class.getDeclaredMethod("hello");
hello.invoke(u); //运行
//调用私有方法
Method show=user_class.getDeclaredMethod("show", String.class);
show.setAccessible(true);
String a = (String) show.invoke(u, "我的id:"); //相当于u.show("我的id")
//获取全部方法
Method[] methods = user_class.getMethods()
//获取共有方法
Field[] declaredFields = user_class.getDeclaredFields();
pojo测试代码
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
public static void hello(){
System.out.println("hello");
}
private String show(String s){
return s+id;
}
}
测试
@Test
public void test1() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
Class user_class= User.class;
//通过反射创建对象
Constructor constructor = user_class.getConstructor(int.class, String.class);
User u= (User)constructor.newInstance(1,"zcj");
System.out.println(u);
//通过反射私有属性
Field id = user_class.getDeclaredField("id");
//通过暴力访问 取消的私有权限的限制
id.setAccessible(true);
id.set(u,2);
System.out.println(u);
//通过反射调用方法
Method hello=user_class.getDeclaredMethod("hello");
hello.invoke(u);
//调用私有方法
Method show=user_class.getDeclaredMethod("show", String.class);
show.setAccessible(true);
String a = (String) show.invoke(u, "我的id:"); //相当于u.show("我的id")
System.out.println(a);
}
注解
元注解
- 负责修饰注解
注解包含一下内容
- @Target :表示注解的使用范围
- @Retention :描述注解的生命周期(Source<Class<Runtime)
- Source:表示该注解只被保存与java文件中
- Class:表示该注解能被保存与 class文件中
- Runtime:表示该注解能被保存class文件中,并且被java反射读取
- @Documented : 说明该注解被包含在javadoc中
- @Inherited : 说明子类可以继承父类的该注解
元注解使用
//表示可以用在哪些地方
@Target(value = {ElementType.METHOD,ElementType.TYPE}) //表明该注解可以用在方法以及类上
//描述注解的生命周期(Source<class<Runtime)
@Retention(value = RetentionPolicy.RUNTIME)
//@Documented 生成在javadoc中
@Documented()
//@Inherited : 说明子类可以继承父类的该注解
@Inherited()
public @interface test {
}
注解内属性
- 可以在注解中定义属性
- 包括数组等
- 当属性有且仅有一个value时, 赋值时可以忽略value的书写
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "Hello";
String name();
int []count();
}
JDK注解
- @Override
- 用于重写父类的方法 或者是写接口实现类时用到该注解。
- @Deprecated
- 用于表示该方法是一个过期的方法。
- @suppressWarnings
- 表示该方法在编译时自动忽略警告。
获取注解信息
@Target({TYPE,METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "Hello";
String name();
int []count();
}
如果注解在类上
Class<User> userClass = User.class;
//判断这个class上有没有这个注解
boolean annotationPresent = userClass.isAnnotationPresent(MyAnnotation.class);
if (userClass.isAnnotationPresent(MyAnnotation.class)){
//获取类上的注解
MyAnnotation myAnnotation = userClass.getAnnotation(MyAnnotation.class);
//输出信息
String value = myAnnotation.value();
System.out.println(value);
System.out.println(myAnnotation.name());
}
注解在方法上
@MyAnnotation(name = "zcj2",count = 1)
private void show(){
}
Class<User> userClass = User.class;
//首先需要获得方法
Method show = userClass.getDeclaredMethod("show");
//判断
if (show.isAnnotationPresent(MyAnnotation.class)){
//得到注解
MyAnnotation myAnnotation = show.getAnnotation(MyAnnotation.class);
//输出信息
String value = myAnnotation.value();
System.out.println(value);
System.out.println(myAnnotation.name());
}