反射
通过某种机制,动态得到这个类的所有信息(属性,方法,构造方法),也可以动态的调用这个类所有的属性,方法,构造方法... 这种机制就是所谓的反射
反射的核心:类的镜子,类的字节码文件 对应的类:Class类
每一个类,它的Class类的对象,唯一一个,类的Class对象, 在这个类的字节码文件加载到内存时候,JVM就会为该类的字节码文件创建一个Class对象
与new对象不同
不确定的类、不确定的对象创建 在框架底层 使用反射
反射的使用
创建对象
-
得到某个类的Class对象
1) 类名.class()
Class<Student> clazz = Student.class;
2) 类的对象.getClass()
Student stu = new Student(); Class<? extends Student> clazz2 = stu.getClass();//Student本类 子类 上界 ?泛型通配符
3)通过Class类的static方法:forName(类的全限定名:包名.类名)
Class<?> clazz3 = Class.forName("com.fs.Student");
-
从这个Class对象获取某个类所有的属性,方法,构造方法
-
通过Class对象,调用属性,方法
创建这个对象,类名是可变的String 如"Student","Dog"
new Student() 不可变 固定
获取构造方法
方法,构造方法,属性,参数都可以看作是一个个对象
getConstructor() getConstructors()
<init>(int) 表示的构造方法
// NoSuchMethodException异常: 没有匹配的方法的异常
Constructor[] constructors = clazz.getConstructors(); //只能得到公开的构造方法
Constructor c1 = clazz.getDeclaredConstructor(int.class,String.class);//得到所有的 不管是公开还是私有
指定参数类型的构造方法
.getModifiers() 得到访问修饰符 返回值int 0. 缺省的 1. public 2. private 4. protected
用构造方法创建对象 new 构造方法() 创建对象
反射方式创建对象:
1)通过Class类的newInstance(),底层调用本类的无参构造方法
Student stu = clazz.newInstance(); // new Student()
2)通过构造方法对象的Constructor类newInstance(Object obj)
Constructor<Student> c = clazz.getDeclaredConstructor(int.class);
c.Accessble(true);//如果调用的类的private修饰的属性无法访问,设置允许访问
Student stu = c.newInstance();
获取属性
Field
//获得所有的public修饰的属性(继承自父类的属性也能获得),private修饰的属性不能获得
Field [] fs = cls.getFields();
Field [] fs = cls.getDeclaredFields(); // 获得本类所有的属性
for(int i=0;i<fs.length;i++){
System.out.println(fs[i]);
}
//获得指定的属性
Field f = cls.getDeclaredField("mastername");
//mastername属性为私有的,所以要先解除封装
f.setAccessible(true);
//修改指定的属性
Field f = cls.getDeclaredField("mastername");
f.setAccessible(true);
f.set(obj, "lisi");
System.out.println(f.get(obj));
获取方法
//获得所有的方法,包括继承自父类的方法,但只能是public 修饰的方法
Method[] ms = cls.getMethods();
//获得本类所有的方法,不包括继承自父类的方法,但private 修饰的方法也能获得
Method[] ms = cls.getDeclaredMethods();
noSuchMethodException 找不到方法异常
//方法的执行
//通过invoke调用方法,obj表示在哪个对象里调用方法,后续的参数都是方法的传入的参数
//invoke返回值:如果方法为void 返回值为null
String str = (String) ms.invoke(obj, "测试有参方法");
System.out.println("方法执行的结果:"+str);
I
getName() //获取全限定名 包名 +类名
getSimpleName() // 只得到类名
注解
//
/** */
/**/
注释:便于阅读代码,对代码的描述
注解(Annotation):对代码的描述,作为代码的形式保留
Annontation像一种修饰符一样,应用于包、类型、构造方法、方法、成员变量、参数及本地变量的声明语句中。
注释类似于商品的标签,描述商品;注解类似于商品的条形码,方便后期结算。
注解的作用
-
生成文档
-
实现代替配置文件功能
-
在编译时进行格式检查 如 @Override 重写 方法名和参数必须相同
注解的本质:特殊的接口
声明注解:创建了一个特殊接口
使用注解: @注解名 (创建注解的一个对象)
注解的分类
内置注解
jdk提供的这个注解的声明,开发者直接使用
@Override
检查重写
@Deprecated
标记这个方法已过时
@SupperssWarnings
抑制生成警告信息
1900~1999 计算机元年 1970年
元注解
使用元注解,对自定义的注解进行声明,限制使用区域
1.@Documented
-注解是否将包含在JavaDoc中
2.@Retention
–什么时候使用该注解 Retention 保留期 存活时间
自定义注解一定设置为runtime
RetentionPolicy.RUNTIME
注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。
RetentionPolicy.SOURCE
注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
RetentionPolicy.CLASS
注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
3. @Target
–注解用于什么地方 如果没有写,表示这个注解可以在任意地方使用
注解可以使用在 类、方法、属性 上
给注解的参数赋值
如果给注解定义的参数是数组类型,给数组类型赋值
赋一个值@Target(value = {ElementType.TYPE})
描述类、接口或enum声明
赋多个值 @Target(value = {ElementType.TYPE,ElementType.METHOD})
描述类、接口或enum声明和方法
4.@Inherited
– 定义该注释和子类的关系 Inherited 继承 子类可以继承父类的注解
自定义注解
声明注解的语法:@interface 默认继承Annotation
参数成员访问修饰符只能为默认default或public 方法返回值
@Target(ElementType.FIELD)//可以用来修饰对象,属性
@Retention(RetentionPolicy.RUNTIME)//什么时候都不丢弃
@Documented//用于生成javadoc文档
public @interface 注解名{
//成员:常量
//String value() defualt ="zhangsan"; 给参数赋值就是给方法的返回值
String value();
//如果没有设置default默认值,使用这个注解时一定要给参数赋值
//如果设置了default,可以在使用时改变默认值
//参数数据类型:八大数据类型,String,枚举,Class,注解类型,数组类型(12种)
}
通过接口自动生成Annotation的实现类
自定义注解,可以有参数,也可以没有。没有参数这个注解没有意义
必须代码解析注解它的功能,获取该注解的对象,只能使用反射来获取
创建一个类,在类中应用自定义的注解:
package AnnotationExample;
//商品类
public class Good {
@Role("管理员")
public void shangjia(){
System.out.println("商品正在上架");
}
@Role("管理员")
public void xiajia(){
System.out.println("商品正在下架");
}
public void show(){
System.out.println("商品正在展示");
}
@Role("顾客")
public void buy(){
System.out.println("商品正在购买");
}
}
创建一个注解:
package AnnotationExample;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//角色的注解
//如果参数名为value 在注解上 单独给这个value参数赋值 省略 value=
//如果给多个参数赋值,不能省略
@Target(ElementType.METHOD) //只能在方法上面使用
@Retention(RetentionPolicy.RUNTIME)//运行时
public @interface Role {
// String role();//权限 管理员 顾客
String value();
//String value() default "";
}
创建一个工具类用来处理注解:
package AnnotationExample;
import java.lang.reflect.Method;
import java.util.Scanner;
public class TestGood {
public static void main(String[] args) throws Exception {
Scanner input = new Scanner(System.in);
System.out.println("请输入您的角色");
String role = input.next();
System.out.println("请输入您要调用的方法");
String methodName = input.next();
Good good = new Good();
//调用方法前 判断角色与方法上定义的角色是否匹配
//如果不匹配,禁止访问
//通过反射
// 1、获取Good的Class
Class<Good> clazz = Good.class;
//2.获取指定方法名的对象
Method method = clazz.getDeclaredMethod(methodName);
//获取这个方法上需要的角色
if (method.isAnnotationPresent(Role.class)){ //判断是否有@Role这个注解
//获取方法上的注解对象
Role annotation = method.getAnnotation(Role.class);
//获取方法上的value参数
String value = annotation.value();
if (role.equals(value)){//判断是否匹配
System.out.println("您有权限");
method.invoke(good); //调用方法
}else{
System.out.println("您没有权限");
}
}else {//没有 直接执行
System.out.println("没有这个注解");
}
}
}
补 记忆版
内置注解:Java已经提供的注解,叫做内置注解。
注解名称 | 注解作用 |
@Override | 用于重写方法上面,可以避免子类重写方法时出错。 |
@Deprecated | 标识某个方法已经过时了,后续不推荐使用该方法(但是可以使用,只是不建议)。 |
@SuppressWarnings | 告诉编译器忽略指定的警告信息(这样代码里面就看不见那些黄色波浪线了)。 |
@FunctionalInterface | 标记某个接口是函数式接口(Jdk1.8新增)。 |
元注解:用于对其他注解进行解释说明的,一般用于创建自定义注解时候,声明自定义注解的一些信息,例如:注解作用范围、注解能够保存到哪个阶段,注解是否需要包含到用户文档里面等等。
注解名称 | 注解作用 |
@Documented | 标着这个注解是否包含在用户文档中(即:javadoc文档里面)。 |
@Target | 标记这个注解能够作用在哪些成员上面,例如:类上面、属性上面、方法上面等等。 |
@Retention | 标记这个注解能够保存到哪个阶段,有三个可选值:源码阶段、字节码阶段、运行阶段。 |
@Inherited | 标记某个注解是继承自哪个注解类(注解默认是不继承任何类的)。 |