反射
web 框架 底层 全都都是使用反射来做。
反射的概念: 把字节文件 映射到 内存中 成为 class文件对象, 根据class文件对象 创建 类的对象 使用类中的各种内容(成员变量 构造方法 成员方法)
class文件 用 Class 对象表示
class文件中 成员变量 Field 对象表示
class文件中 构造方法 Constructor 对象表示
class文件中 成员方法 Method 对象表示。
// idea
/*
自动提示 你对象里面具备什么方法。
底层怎么知道 这个对象中具备什么方法啊?
获取 对象 所属的 class文件对象。
根据class文件对象 拿到所有的method对象。
获取每个method对象的名字
展示出来。
*/
获取class文件对象:
反射研究生的是 通过class文件对象 创建 类的对象。 所以首先你得先获取 class文件对象
三种方式:
1:对象.getClass();
Student s = new Student();
Class c = s.getClass();
class Student {
@Override
public boolean equals(Object obj){
if (obj == this){
return true;
}
if (obj ==null){
return false;
}
if (obj.getClass() != this.getClass()){
return false;
}
Student s = (Student)obj;
return s.name.equals(this.name)&& s.age ==this.age;
}
}
2: 类名.class
Class c = Student.class;
class Student {
public static synchronized void show(){
}
public static void show1(){
synchronized(Student.class){
}
}
}
3: Class.forName("类的全路径名(带包的)"); // 框架中用的很多。
Class c = Class.forName("com.itheima.domain.Student");
利用class文件对象
创建 类 对象
使用 成员变量
Class c = Class.forName("com.itheima.domain.Student");
Field[] fs = c.getFields(); // fs 包含了Student里面所有的 public的属性
Field f = c.getField("age"); // f 是Student里面的age属性
//f = 18; // 错误
Student s = new Student();
f.set(s, 18); // age属性 是Student的 成员变量 ,成员变量的生命周期 随着对象的创建而存在,随着对象的消失而消失。
System.out.println(s); // Student[name=null,age = 18]
Field[] fs1 = c.getDeclaredFields();// fs1 包含了Student里面所有的属性
Field f1 = c.getDeclaredField("name"); // f 是Student里面的私有的name属性
Student s1 = new Student();
f1.setAccessible(true);
f.set(s1, "张三");
使用 构造方法
Class c = Class.forName("com.itheima.domain.Student");
//c.getConstructor(可变参数) 此处之所以设计为可变参数 是因为 你的构造方法 不知道有多少参数把。
Constructor con = c.getConstructor(); //可变参数
Object obj = con.newInstance(); //可变参数
Student s = (Student)obj;
使用 成员方法
class Student {
private void show(int a){
System.out.println(this.a); //this指的是 谁来调用 包含this的这个方法 this就指的是谁。
}
}
Class c = Class.forName("com.itheima.domain.Student");
Method[] ms = c.getMethods(); //ms 包含了Student里面所有的 public的方法
Method[] ms1 = c.getDeclaredMethods(); //ms 包含了Student里面所有的方法
Method ms2 = c.getDeclaredMethod("show", int.class)
ms2.setAccessible(true);
Constructor con = c.getConstructor();
Object obj = con.newInstance();
ms2.invoke(obj,20); // 20 必须要传一个 对象进去, 非静态方法 是需要对象调用的,否则里面的this不知道是谁,
案例:已知 配置文件a.properties 和 Student类, 请读取配置文件中的类名className 和方法名methodName ,通过反射 运行该方法。
class Student {
public void show(){
System.out.println("abc");
}
}
配置文件a.properties
className=advance.day15.Student
methodName=show
代码:
Properties pro = new Properties();
//1.2加载配置文件,转换为一个集合
//1.2.1获取class目录下的配置文件
ClassLoader classLoader = ReflectTest.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("pro.properties");
pro.load(is);
//2.获取配置文件中定义的数据
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//3.加载该类进内存
Class cls = Class.forName(className);
//4.创建对象
Object obj = cls.newInstance();
//5.获取方法对象
Method method = cls.getMethod(methodName);
//6.执行方法
method.invoke(obj);
作业:模拟 idea的提示功能// 键盘录入一个 类的名字。 你立刻给我打印出 他有哪些方法 哪些成员变量
Scanner sc = new Scanner(System.in);
String s = sc.nextLine();
Class<?> c = Class.forName(s);
Field[] declaredFields = c.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
Method[] declaredMethods = c.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
注解
框架 web 注解开发。
java的数据类型:
基本类型:byte short int long float double boolean char
引用类型:
数组 []
类 class
lambda jdk1.8的时候出现的
枚举 enum jdk1.5的时候出现的
接口 interface
注解 @interface jdk1.5的时候出现的
注解的概念:
1:也叫做 代码的元数据。
元数据:用来修饰其他数据的 数据 叫做元数据。
2:和类 接口 数组 并列为 引用类型。
3:注解往往写在 类 接口 方法 成员变量的前面。
注解功能来分:
文档注释的注解:对生成 程序的说明文档 的时候 起作用。
@author Joseph D. Darcy 作者
@since 1.0 版本
@param 参数说明
@return 返回值说明
@see 方法中涉及到的其他类的内容
编译期的注解: 编译期起作用
@Override 重写
@Deprecated 过时
@FunctionalInterface 函数式接口
@SuppressWarnings("all") 压制警告
运行时注解: 运行时期起作用
框架中给我们提供:
jdk给我们也提供了:
自定义注解。
自定义注解:
格式:
元注解
public @interface 名字{
属性列表;
}
元注解:
元数据:就是修饰数据的数据
元注解:就是修饰注解的注解。
本质:
public interface 名字 extends java.lang.annotation.Annotation {
public abstract void show();
}
属性:
属性类型:
基本类型
String类型
枚举类型
注解类型
以上类型数组
enum En{
e1,e2,e3;
}
public @interface MyAnno{
int a();
String str();
En en();
}
public @interface MyAnno1{
int a();
String str();
En en();
MyAnno ma();
int[] as();
//Student sts(); //编译报错
// 为什么 属性类型里面 是不能有 类 和接口啊。
// 我们的定义的注解 以后加在哪上面? 类 或者 接口上面。 那么你在 注解里面有定义 类属性和接口 就会 递归下去了。
}
@MyAnno1(a=10,str="abc",en=En.e2,ma=@MyAnno(a=20,str="bcd",en=En.e1),as={10,20})
class Student{
}
注意事项:
案例1:
public @interface MyAnno{
int[] as();
}
@MyAnno(as=10)
class Student{
}
案例2:
public @interface MyAnno{
int[] as();
}
@MyAnno() //编译报错 因为 有as属性 你没赋值就比编译报错
class Student{
}
案例3:
public @interface MyAnno{
int[] as() default {10,20};
}
@MyAnno() //编译正确
class Student{
}
案例4:
public @interface MyAnno{
int[] as();
}
@MyAnno({10,20}) //编译报错 因为 没有 as =
class Student{
}
案例5:
public @interface MyAnno{
int[] value();
}
@MyAnno({10,20}) //编译正确
class Student{
}
案例6:
public @interface MyAnno{
int[] value();
String str();
}
@MyAnno({10,20},str="abc") //编译报错 因为 没有 value = value不是单独的
class Student{
}
元注解:用于描述注解的注解
@Target: 描述注解能够作用的位置
ElementType取值:
TYPE:可以作用于类上
METHOD:可以作用于方法上
FIELD:可以作用于成员变量上
@Retention: 描述注解被保留的阶段
@Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到class字节码文件中,并被JVM读取到
@Documented: 描述注解是否被抽取到api文档中
@Inherited: 描述注解是否被子类继承
自定义注解的使用:
(框架底层)反射+配置文件 案例:已知 配置文件a.properties 和 Student类, 请读取配置文件中的类名className 和方法名methodName ,通过反射 运行该方法。
已知:
class Student {
public void show(){
System.out.println("abc");
}
}
配置文件a.properties
className=advance.day15.Student
methodName=show
程序:
Properties p = new Properties();
p.load("a.properties");
p.getProperty("className");
p.getProperty("methodName");
// 反射创建Student对象 反射执行show方法
(框架底层)反射+注解
已知:
public @interface MyAnno{
String className();
String methodName();
}
class Student {
public void show(){
System.out.println("abc");
}
}
@Pro(className = "cn.itcast.annotation.Demo1",methodName = "show")
程序:
/*
反射获取注解
获取 className methodName
反射 创建Student对象 反射执行 show方法。
*/
@Pro(className = "cn.itcast.annotation.Demo1",methodName = "show")
public class ReflectTest {
public static void main(String[] args) throws Exception {
//1.解析注解
//1.1获取该类的字节码文件对象
Class<ReflectTest> reflectTestClass = ReflectTest.class;
//2.获取上边的注解对象
//其实就是在内存中生成了一个该注解接口的子类实现对象
/*
public class ProImpl implements Pro{
public String className(){
return "cn.itcast.annotation.Demo1";
}
public String methodName(){
return "show";
}
}
*/
Pro an = reflectTestClass.getAnnotation(Pro.class);
//3.调用注解对象中定义的抽象方法,获取返回值
String className = an.className();
String methodName = an.methodName();
System.out.println(className);
System.out.println(methodName);
//3.加载该类进内存
Class cls = Class.forName(className);
//4.创建对象
Object obj = cls.newInstance();
//5.获取方法对象
Method method = cls.getMethod(methodName);
//6.执行方法
method.invoke(obj);
}
}