类的加载,反射,注解,动态代理

反射、注解、动态代理

今日内容

  • 类的加载
    • 加载器
    • 类加载机制
  • 反射
    • class对象获取
    • 常用方法
    • 通过反射获取构造方法
    • 通过返回获取成员方法
  • 注解
    • Target
    • Retention
  • 动态代理

第一章 类的加载

1.类的加载时机

当我们第一次使用某个类时,这个类会加载到方法区

1.  创建类的实例。
   2.  类的静态变量,或者为静态变量赋值。
   3.  类的静态方法。
   4.  使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。
   5.   初始化某个类的子类。
   6.  直接使用java.exe命令来运行某个主类。

以上六种情况的任何一种,都可以导致JVM将一个类加载到方法区。

类是由类加载器加载的。

2.类加载机制

  1. 启动类加载器(Bootstrap ClassLoader):用于加载系统类库 \bin目录下的
    class,例如:rt.jar。内容包括:String object…
  2. 扩展类加载器(Extension ClassLoader):用于加载扩展类库 \lib\ext目录下的
    class。
  3. 应用程序类加载器(Application ClassLoader):用于加载我们自定义类的加载器。
/*
如果想要获取加载读类的类加载器,可以使用下面代码
Person.class.getClassLoader()

启动类加载器是扩展类加载器的 父加载器
扩展类加载器是应用类加载器的 父加载器
*/

public class Demo01ClassLoaderTest {
public static void main(String[] args) {
System.out.println(Person.class.getClassLoader().getParent());
}
}

3双亲委派模型

alt

第二章 反射

1概念

反射:Java中的反射是一种操作机制,我们利用反射可以在程序运行时期获取到一个类的构造方法,成员方法,成员变量,并进行使用。

原理: 反射操作的就是Class类型的对象,当我们使用一个类时,这个类会由类加载器加载到方法区,同时会给这个类创建Class类型的对象。这个Class类型的对象是用来描述该类的,会保存该类的信息,所以我们可以通过这个Class对象获取到类的成员变量,成员方法,构造方法等进行使用。

alt

2.Class对象的获取方式

  1. 通过对象调用getClass方法获取

    Class getClass():获取Class对象
    1. 通过类调用class属性获取(最简单)
      类名.class
    2. 通过Class的静态方法forName获取(最常用,最灵活)
      static Class forName(String className):可以根据全类名(包含包的类名)获取类的Class对象

    一个类的Class对象只有一个,不管获取几次,获取的都是同一个Class对象。

public class Demo01GetClass {
public static void main(String[] args) throws ClassNotFoundException {
// 1. 通过对象调用getClass方法获取
Person p = new Person();
Class<? extends Person> clazz = p.getClass();
System.out.println(clazz);
// class cn.com.mryhl.demo02_refiect.Person
// 2. 通过类调用class属性获取(最简单)
Class<Person> personClass = Person.class;
System.out.println(personClass);
// 3. 通过Class的静态方法forName获取(最常用,最灵活)
Class<?> person = Class.forName("cn.com.mryhl.demo02_refiect.Person");
System.out.println(person);
// 验证3次获取的对象是否是同一个
System.out.println(clazz == personClass);
}
}

3.Class类常用方法

String getName():获取类的全限定类名(包含包的的类名)
String getSimpleName(): 获取类的简单类名(不包含包的类名)

public class Demo02ClassMethod {
public static void main(String[] args) {
// 获取Person类的Class
Class clazz = Person.class;
// String getName():获取类的全限定类名(包含包的的类名)
System.out.println(clazz.getName());
// String getSimpleName(): 获取类的简单类名(不包含包的类名)
System.out.println(clazz.getSimpleName());
}
}

4.使用反射获取的构造方法

在Class中有一些方法可以获取到类的构造方法:
Constructor[] getConstructors():获取到类中所有的构造方法。
Constructor getConstructor(Class… parameterTypes):获取到类中指定的构造方法。 参数表示要获取的构造方法的参数列表。
上面的方法只能获取到类中public权限的构造方法,其他权限无法获取。

Constructor表示构造方法,里面的功能:
T newInstance(Object… initargs):通过该构造方法创建对象。
参数initargs:表示通过构造方法创建对象时传递的实际参数。
返回值:创建出来的对象

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Demo03Constructor {
public static void main(String[] args) throws ClassNotFoundException {
method();
}
/*
Constructor getConstructor(Class... parameterTypes):获取到类中指定的构造方法。 参数表示要获取的构造方法的参数列表。
上面的方法只能获取到类中public权限的构造方法,其他权限无法获取。
要求:获取Person类中的空参数的构造方法,并使用该构造方法创建对象
*/

public static void method2() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, InvocationTargetException {
//获取Person类的Class
Class clazz = Class.forName("cn.com.mryhl.demo02_refiect.Person");
//获取Person类空参数的构造方法
//public Person():空参构造
//因为getConstructor方法参数没有传递任何内容,所以表示我们获取的是空参数的构造方法
Constructor c = clazz.getConstructor();
//通过获取到的构造方法调用newInstance方法,使用获取到的构造方法创建对象
//newInstance方法的参数表示使用构造方法创建对象时传递的实际参数,因为使用空参数的构造方法创建对象,所以不需要传递任何实际参数。所以newInstance方法参数留空不写
//newInstance方法参数留空不写表示没有传递任何实际参数
Object obj = c.newInstance();
System.out.println(obj);
}




/*
Constructor[] getConstructors():获取到类中所有的构造方法。
*/

public static void method() throws ClassNotFoundException {
// 获取Person类的Class
Class<?> clazz = Class.forName("cn.com.mryhl.demo02_refiect.Person");
// 获取该类的构造方法
Constructor[] cs = clazz.getConstructors();
//遍历数组,拿到每一个构造方法并输出
for (Constructor c : cs) {
System.out.println(c);
}
}
}

5.带参

public class Demo04Constructor {
/**
*Constructor getConstructor(Class... parameterTypes):获取到类中指定的构造方法。 参数表示要获取的构造方法的参数列表。
*
* 要求:通过反射获须person类的带参数的构造方法,并根据读构造方法创建对象
*
* */
public static void main(String[] args) throws Exception {
//获取Person类的Class
Class clazz = Class.forName("cn.com.mryhl.demo02_refiect.Person");
// 获取带参数的构造方法
// Person(String name,int age)
// 因为调用getConstructor方法第一个参数传递的是String.class,第二个是int.class,表示要获取的构造方法有两个参数,第一个是String,第二个是int
Constructor c = clazz.getConstructor(String.class,int.class);
// 根据构造方法创建对象
// new Person("张三",13)
// 因为在调用newInstance方法的时候传递的两个参数分别是张三和13,所以表示调用构造方法时传递的实际参数为张三13
Object o = c.newInstance("张三", 13);// 相当于之前的new Person("张三",13)
System.out.println(o);

}
}

6.暴力反射

暴力反射(不推荐,会破坏封装性)
暴力反射可以获取到类中任何权限(包括私有)的成员并进行使用。

在Class中,有一些方法可以获取到类中的所有权限的构造方法:
Constructor[] getDeclaredConstructors():获取类中所有的构造方法
Constructor getDeclaredConstructor(Class… parameterTypes):获取类中指定的构造方法,参数是构造方法的参数列表
上面的方法可以获取到任何权限的构造方法。

通过上面的方法虽然可以获取到私有的成员,但是不能直接使用。
因为Java中有一个权限检查机制,私有的东西是不能在外面使用的。
我们可以取消这个权限检查机制。

反射有关的类
Constructor:构造方法
Method:成员方法
Field:成员变量

上面这三个类都有一个父类叫做AccessibleObject,在AccessibleObject中有一个方法可以取消检查机制。
void setAccessible(boolean flag):如果参数是true表示取消权限检查

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Demo05Constructor {
public static void main(String[] args) throws Exception {
//method();
method2();
}

/*
Constructor getDeclaredConstructor(Class... parameterTypes):获取类中指定的构造方法,参数是构造方法的参数列表
要求:使用反射获取私有的构造方法,并创建对象

*/

public static void method2() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, InvocationTargetException {
//获取Person类的Class
Class clazz = Class.forName("cn.com.mryhl.demo02_refiect.Person");
//获取Person类空参数的构造方法
//public Person():空参构造
//因为getConstructor方法参数没有传递任何内容,所以表示我们获取的是空参数的构造方法
Constructor c = clazz.getDeclaredConstructor(String.class);
//通过获取到的构造方法调用newInstance方法,使用获取到的构造方法创建对象
// 取消权限检查机制
c.setAccessible(true);
Object obj = c.newInstance("张三丰");
System.out.println(obj);
}




/*
Constructor[] getDeclaredConstructors():获取到类中所有的构造方法。
*/

public static void method() throws Exception {
// 获取Person类的Class
Class clazz = Class.forName("cn.com.mryhl.demo02_refiect.Person");
// 获取该类的构造方法
Constructor[] cs = clazz.getDeclaredConstructors();
//遍历数组,拿到每一个构造方法并输出
for (Constructor c : cs) {
System.out.println(c);
}
}
}

7.简单的创建对象方法

/*
在CLass中有一个方法叫做newInstance,可以十分方便的创建对象
T newInstance():使用空参数的构造方法创建对象。
如果要通过空参数的构造方法创建对象,那么不需要像之前那样先获取构造方法,然后再通过构造方法创建对象了。
我们可以通过class直接调用newInstance方法,利用空参数的构造方法对象。
*/

public class Demo06NewInstance {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
// 获取Person类的Class
Class clazz = Class.forName("cn.com.mryhl.demo02_refiect.Person");
//直接调用newInstance创建对象
Object obj = clazz.newInstance( ) ;
//输出结果
System.out.println(obj);
}
}

8.使用反射获取成员方法并使用。

在Class中有一些功能,可以获取到类中的成员方法
Method[] getMethods():获取到类中所有的成员方法。
Method getMethod(String name, Class… parameterTypes):获取到类中指定的成员方法。参数name表示要获取的方法名,参数parameterTypes表示要获取的成员方法的参数列表。
上面的两个功能只能获取到类中public权限的成员方法,其他权限无法获取。

Method表示成员方法,里面的功能:
Object invoke(Object obj, Object… args):让该方法执行。
参数obj:表示通过哪个对象调用了该方法。如果是调用的静态方法,该参数可以是null。
参数args:表示调用该方法时传递的实际参数
返回值:方法调用的返回值

import java.lang.reflect.Method;

public class Demo07Method {
public static void main(String[] args) throws Exception {
method2();
}

/**
* Method getMethod(String name, Class... parameterTypes):获取到类中指定的成员方法。参数name表示要获取的方法名,参数parameterTypes表示要获取的成员方法的参数列表。
*/
public static void method2() throws Exception {
// 获取Person类的Class
Class clazz = Class.forName("cn.com.mryhl.demo02_refiect.Person");
// 获取成员方法
//getMethod方法第一个参数传递的是sleep,表示获取的方法他的名字是sleep
//getMethod方法第二个参数是可变参数,该参数位置我们没有传递任何东西,表示获取的方法是空参数的方法
Method method1 = clazz.getMethod("sleep");
//System.out.println(method1);
// 创建Person对象
Object o = clazz.newInstance();
// 调用invoke,让方法执行
// 因为通过method1调用incoke方法,method1表示sleep方法,所以表示sleep执行了
// 因为invoke方法第一个参数传递的是obj,表示通过obj调用了
// 因为invoke方法第二个参数没有传递任何数据,所以表示调用sLeep方法时,没有传递任何内容
method1.invoke(o);
}


/**
* Method[] getMethods():获取到类中所有的成员方法。
*/
public static void method() throws Exception {
// 获取Person类的Class
Class clazz = Class.forName("cn.com.mryhl.demo02_refiect.Person");
// 获取成员方法
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method);
}
}
}
import java.lang.reflect.Method;

public class Demo08Method {
/*
要求:获取有参数的成员方法并调用
public String eat(string food)
*/

public static void main(String[] args) throws Exception {
// 获取Person类的Class
Class clazz = Class.forName("cn.com.mryhl.demo02_refiect.Person");
// 通过class对象,获取指定的成员方法
// Method getMethod(String name, Class... parameterTypes):获取到类中指定的成员方法。参数name表示要获取的方法名,参数parameterTypes表示要获取的成员方法的参数列表。
// 第一个参数传递的是eat,表示方法名是eat
// 第二个参数是可变参数,表示参数类别,传的是String.class,表示字符串
Method eat = clazz.getMethod("eat", String.class);
Object o = clazz.newInstance();
//调用invoke,让eat方法执行
//Object invoke(Object obj, Object...args):让该方法执行
//因为eat表示的是eat方法,所以通过eat调用invoke方法时让eat方法执行
//因为invoke方法第一个参数是o,表示通过o调用了eat方法
//invoke方法第二个参数是可变参数,我们可以传递任意个数据
// 返回值就是eat的结果
Object o1 = eat.invoke(o, "法式鹅肝就米饭");
System.out.println(o1);
}
}

9.使用反射操作成员变量(了解)

在Class中有一些方法可以获取到类中的成员变量
Field[] getFields():获取类中所有的成员变量
Field getField(String name):获取类中指定的成员变量,参数是成员变量的名字。
上面的两个方法只能获取public权限的成员变量,其他权限无法获取。

Field表示成员变量,里面功能:
void set(Object obj, Object value):给成员变量赋值。
参数obj:表示给哪个对象的成员变量赋值。
参数value:将该成员变量赋成什么值

Object get(Object obj):获取成员变量的值。
参数obj:表示获取哪个对象的成员变量的值。
返回值:获取到的结果

import java.lang.reflect.Field;
public class Demo09ReflectField {
//获取Person类的hobby属性,然后进行赋值取值操作

public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException {
//获取Person类的Class
Class clazz = Class.forName("com.itheima.demo01_reflect.Person");
//获取Person类中的hobby属性
Field field = clazz.getField("hobby");
//创建Person对象
Object obj = clazz.newInstance();
//给对象hobby属性赋值
field.set(obj, "写代码");
//相当于 obj.hobby = "写代码";
System.out.println(obj);
//获取hobby属性的值
//获取obj的hobby属性的值
Object result = field.get(obj);
System.out.println(result);
}
}

10.练习

将类名与方法名放到文件中,通过读取文件的方法决定创建哪个类调用哪个方法
步骤:
1.创建配置文件,保存类名和方法名
2.创建Properties集合
3.创建输入流
4.调用Load方法,将文件中的键值对加载到Properties集合中
5.通过Properties集合根据键获取值。
6.获取该类的CLass对象
7.通过CLass调用getMethod,获取对应的方法
8.让方法执行。

import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

public class Demo01Test {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
// 步骤:
// 1.创建配置文件,保存类名和方法名
// 2.创建Properties集合
Properties p = new Properties();
// 3.创建输入流
FileInputStream is = new FileInputStream("day17\\config.properties");

// 4.调用Load方法,将文件中的键值对加载到Properties集合中
p.load(is);
is.close();
// 5.通过Properties集合根据键获取值。
String className = p.getProperty("className");
String methodName = p.getProperty("methodName");
// 6.获取该类的CLass对象
Class clazz = Class.forName(className);
// 7.通过CLass调用getMethod,获取对应的方法
Method m = clazz.getMethod(methodName);
// 8.让方法执行。
Object obj = clazz.newInstance();
m.invoke(obj);


}
}

第三章 注解

1.简介

@Xxx 表示注解
介绍:
1. 注解是jdk5开始有的
2. 注解可以加在类上,方法上,变量上…
3. 程序可以根据注解完成一些功能

注解和注释:
注释:是给程序员看的,注释不会被编译和执行
注解:是给程序看的,程序可以根据注解完成一些功能
常见的注解:
@Override:该注解用来验证一个方法是否重写父类的方法
@FunctionalInterface:该注解用来用来验证一个接口是否是函数式接口
@Deprecated: 过时标签

public class Demo01Anno {
public static void main(String[] args) {
method();
}
/**
* 定义一个非常厉害的方法
*/
@Deprecated // 过时标签
public static void method(){
System.out.println("牛逼");
}
}

2.定义格式

自定义注解

格式:
public @interface 注解名 {

}

3.在注解中可以提供一些属性

属性格式:
数据类型 属性名();
数据类型 属性名() default 默认值;

注解中的属性的数据类型只能是下面几种:
1. 八种基本类型 byte short int long float double boolean char
2. String, Class, 枚举, 注解
3. 上面所有类型的数组

4.注解的使用格式

​ @注解名
​ @注解名(属性名=属性值, 属性名=属性值)

注解使用的注意事项:
1. 如果注解中的属性没有默认值,那么在使用注解时必须要给该属性赋值
2. 如果注解中的属性有默认值,那么在使用注解时,可以赋值,也可以不赋值。
3. 如果注解中的属性是数组类型,那么在给该属性赋值时,可以使用大括号包裹多个元素
4. 如果注解中的属性是数组类型,并且只需要给该属性赋值一个元素,那么也可以省略大括号。
public @interface Student {
// 姓名属性
String name();
// 年龄属性
int age() default 0;
// 爱好
String[] hobbies();

}

@Student(name = "上官嘏",hobbies = {"抽烟","喝酒","烫头"})
public void method(){

}

5.注解的特殊属性

/*
注解中的特殊属性value
如果注解中只有一个没有默认值的属性,并且这个属性叫做value,那么在给该属性赋值时,可以省略属性名
*/
public @interface Book {
// 属性value,表示书名
String value();
}
@Book("jin")
public class Demo03Anno {
}

4.Target注解

@Target是一个元注解,作用是用来限制一个注解的使用位置。如果某个注解不使用@Target进行修饰,那么该注解可以用在类,方法,变量,构造方法等任何位置。

在@Target这个元注解中,只有一个属性叫做value,那么在给该属性威值时,可以省略属性名。value属性表示被修饰的
注解可以在哪个位置使用。

这个value属性是ElementType数组类型的,ElementType是枚举类型,枚举就是特殊的类,枚举就是特殊的类,枚举中的每一个属性都是自身的一个对象.
在给value属性赋值时,我们需要使用ElementType中的属性,每一个属性都有自己的作用.

ElementType中的每一个属性都有自己的作用。
ElementType.TYPE,类,接口
ElementType.FIELD, 成员变量
ElementType.METHOD, 成员方法
ElementType.PARAMETER, 方法参数
ElementType.CONSTRUCTOR, 构造方法
ElementType.LOCAL_VARIABLE, 局部变量

@Target({ElementType.TYPE,ElementType.METHOD})
public @interface MyAnno {
}

7.Retention注解

如果某个注解不使用@Retention进行修饰,那么注解默认只在源代码阶段以及编译后的.class中有效,在运行时期内存中是没有的。

@Retention中有一个属性叫做value,这个属性表示被修饰的注解的声明周期。这个value属性是RetentionPolicy类型的,
RetentionPolicy是枚举类型。我们在使用@Retention这个注解时,可以给value属性赋值RetentionPolicy枚举的各个属性,
在RetentionPolicy中,每一个属性都有自己的作用
RetentionPolicy.SOURCE:表示被修饰的注解只在源代码阶段有效,在编译后的.class文件中以及运行时内存中是没有的。
RetentionPolicy.CLASS:表示被修饰的注解在源代码阶段以及编译后的class文件中有效,在运行时期内存中是没有的。(默认值)
RetentionPolicy.RUNTIME:表示被修饰的注解在源代码阶段,以及编译后的class文件中,以及运行时内存中都有效

@Retention(RetentionPolicy.SOURCE)
public @interface MyAnno {
}
@Retention(RetentionPolicy.CLASS)
public @interface MyAnno2 {
}
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnno3 {
}

8.注解解析

注解解析指的是获取注解中的内容(属性)然后进行使用。

解析相关API:
Annotation:是所有注解的根接口,所有注解都会默认实现这个接口。
AnnotatedElement:这个接口中定义了有关操作注解的方法
Annotation[] getAnnotations():获取到所有的注解。
T getAnnotation(Class annotationClass):获取到指定的注解。 参数是注解的Class对象。
boolean isAnnotationPresent(Class annotationClass):判断是否有该注解。参数是注解的Class对象。

反射有关的类【Class,Method,Constructor,Field】都实现了AnnotatedElement这个接口。
注解解析要结合反射技术实现。
如果要操作类上的注解,通过CLass对象调用上面的方法进行操作。
如果要操作构造方法上的注解,通过Constructor对象调用上面的方法进行操作
如果要操作成员方法上的注解,通过Method对象调用上面的方法进行操作。
要求:获取BookStore这个类上的注解,并输出注解的属性
分析:如果要获取BookStore上的注解,我们可以先获取BookStore的Class对象,通过Class对象对注解进行操作.

import java.lang.annotation.Annotation;

public class Demo01ParseAnno {
public static void main(String[] args) throws ClassNotFoundException {
// 获取Class对象
Class clazz = Class.forName("cn.com.mryhl.demo07_parse_anno.BookStore");
//判断有没有注解
boolean flag = clazz.isAnnotationPresent(Book.class);
// System.out.println(flag);
if (flag){
// 如果条件成立,表示bookstore上有注解
Annotation a = clazz.getAnnotation(Book.class);
// 向下转型
Book book = (Book) a;
System.out.println("书名:"+book.name());
System.out.println("价格:"+book.price());
System.out.println("作者:"+book.author());
}


}

}
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface Book {
String name();
int price();
String author() default "佚名";
}
@Book(name = "撼天经",price = 25,author = "悍天大帝")
public class BookStore {
}

9.练习

模拟JUnit单元测试

要求:自定义@MyTest注解,然后在一个类中的多个方法上使用@MyTest注解,然后运行带有@MyTest注解的方法。

步骤:
1. 获取TestDemo的Class对象

  1. 创建TestDemo对象

  2. 通过Class对象调用getMethods获取所有的成员方法

  3. 遍历这些成员方法,拿到里面的每一个方法(Method)

  4. 判断该方法上有没有@MyTest注解。

  5. 如果有该注解,就让这个方法执行。

import java.lang.reflect.Method;

public class Demo01Test {
public static void main(String[] args) throws Exception {
// 1. 获取TestDemo的Class对象
Class clazz = Class.forName("cn.com.mryhl.demo08_anno_test.TestDemo");
// 2. 创建TestDemo对象
Object obj = clazz.newInstance();
// 3. 通过Class对象调用getMethods获取所有的成员方法
Method[] methods = clazz.getMethods();
// 4. 遍历这些成员方法,拿到里面的每一个方法(Method)
for (Method method : methods) {
// 5. 判断该方法上有没有@MyTest注解。
if (method.isAnnotationPresent(MyTest.class)){
// 6. 如果有该注解,就让这个方法执行。
method.invoke(obj);
}
}



}
}
public class TestDemo {
public void methodOne(){
System.out.println("methodOne");
}
@MyTest
public void methodTwo(){
System.out.println("methodTwo");
}
public void methodThree(){
System.out.println("methodThree");
}
@MyTest
public void methodFour(){
System.out.println("methodFour");
}
public void methodFive(){
System.out.println("methodFive");
}
}

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)//运行有效
@Target(ElementType.METHOD)// 用在方法上
public @interface MyTest {
}

第四章 动态代理

动态代理可以对一个类(对象)的功能进行【增强】
动态:会在程序运行时期创建一个代理类以及代理对象
代理:会创建一个代理对象对原有功能进行增强

在Proxy这个类中有一个方法叫做newProxyInstance,这个方法可以创建一个代理对象,并对另一个对象(被代理对象)进行增强。
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):该方法用来创建动态代理对象并将该对象返回。
参数loader:表示类加载器。 固定写法 当前类.class.getClassLoader();
参数interfaces:代理对象实现的接口,一般这些接口要和被代理对象一致。 固定写法:被代理对象.getClass().getInterfaces()
参数h:该参数是InvocationHandler类型,InvocationHandler表示执行处理类(接口),这个接口中有一个方法叫做invoke
Object invoke(Object proxy, Method method, Object[] args):这个invoke方法会在每次动态代理对象调用方法时执行
参数proxy:表示代理对象
参数method:表示代理对象调用的方法。
参数args:表示代理对象调用方法时的实际参数。
返回值:代理对象调用方法的返回值

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class Demo01ProxyTest {
public static void main(String[] args) {
// 创建对象
Carable car = new Car();
// 创建动态代理对象
Carable superCar =(Carable) Proxy.newProxyInstance(Demo01ProxyTest.class.getClassLoader(), car.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 判断一下调用的方法是否是run,如果是run,再增强
if (method.getName().equals("run")){
System.out.println("起飞");
}
// 让method执行,通过被代理对象调用method方法,让方法执行【放行】
Object result = method.invoke(car, args);
return result;
}
});
// 调用方法
//System.out.println(superCar.getClass());
superCar.run();
String say = superCar.say();
System.out.println(say);
}
}
/*
小汽车的接口,里面定义了小汽车的功能
*/

public interface Carable {
// 跑
void run();
// 说话
String say();
}
// 实现类
public class Car implements Carable{
@Override
public void run() {
System.out.println("小汽车跑");
}

@Override
public String say() {
System.out.println("按喇叭");
return "滴滴";
}
}

总结

一. 类加载器
1. 类的加载时间
a. 创建类的实例。
b. 类的静态变量,或者为静态变量赋值。
c. 类的静态方法。
d. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。
e. 初始化某个类的子类
f. 直接使用java.exe命令来运行某个主类。

2. 类加载器
启动类加载器:加载核心类库中的内容
扩展类加载器:加载ext包下的内容
应用类加载器:加载我们自己写的东西

二. 反射
反射就是在程序运行时期获取一个类的成员变量,成员方法构造方法等进行使用。
反射核心操作的是Class对象,当我们使用一个类时,这个类会由类加载器加载到方法区,同时由这个类加载器给这个类创建Class对象。
这个Class对象是用来描述这个类的,保存这个类的信息

1. 获取Class对象的三种方式
a. 通过对象调用getClass方法获取
Class getClass():获取类的Class对象

b. 通过类名调用class属性获取【最简单】
类.class

c. 通过Class的静态方法forName获取【最常用,最灵活】
static Class forName(String className):根据全类名【包含包的类名】获取对应的Class对象

2. 反射获取构造方法并使用
Class中有一些方法可以拿到类中的构造方法:
Constructor[] getConstructors():获取到类中所有的构造方法。
Constructor getConstructor(Class... parameterTypes):获取类中指定的构造方法。参数表示要获取的构造方法的参数列表。
上面的方法只能获取到public权限的方法,其他权限获取不到

Constructor表示构造方法,里面的功能;
T newInstance(Object... initargs):通过该构造方法创建对象。
参数initargs:通过构造方法创建对象时传递的实际参数。
返回值:创建出来的对象

3 反射获取成员方法并使用
在Class中有一些功能,可以获取类中的成员方法
Method[] getMethods():获取类中所有的成员方法
Method getMethod(String name, Class... parameterTypes):获取指定的成员方法。 参数name表示要获取的方法的名字。 参数 parameterTypes表示要获取的成员方法的参数列表
上面的两个方法只能获取public权限的方法,不能获取其他权限

Method表示成员方法,里面功能:
Object invoke(Object obj, Object... args):让该方法执行。
参数obj:表示通过哪个对象调用了该方法。 如果是静态方法,该参数可以传递null
参数args:调用该方法时 传递的实际参数。
返回值:方法调用后的返回值。

三. 注解
@Xxx表示注解
自定义注解格式:
public @interface 注解名 {
}

属性格式:
数据类型 属性名();
数据类型 属性名() default 默认值;

注解的属性只能是下面这些类型:
1. 八种基本类型 byte short int long float double char boolean
2. String, Class, 枚举, 注解
3. 以上所有类型的数组

注解的使用格式:
@注解名
@注解名(属性名=属性值,属性名=属性值)

注解使用的注意事项:
1. 如果注解中有属性没有默认值,那么在使用注解时,必须要给这些属性赋值。
2. 如果注解中的属性有默认值,那么再使用注解时,可以不赋值。
3. 如果注解的属性是数组类型,那么在赋值时可以用大括号包裹多个元素。
4. 如果注解的属性是数组类型,并且只赋值一个元素的情况下,可以省略大括号。
特殊属性value
如果注解中只有一个没有默认值的属性叫做value,在给属性赋值时可以省略属性名

元注解
元注解就是修饰注解的注解
@Target:限制注解的使用位置
@Retention:限制注解的生命周期

解析注解:
解析相关API
Annotation:是所有注解的根接口,所有的注解都会默认实现这个接口
AnnotatedElement:这个接口中定义了操作注解的方法
Annotation[] getAnnotations():获取所有的注解
T getAnnotation(Class annotationClass):获取指定的注解。 参数表示注解的Class
boolean isAnnotationPresent(Class annotationClass):判断是否存在指定的注解,参数是注解的Class

反射有关的类【Class,Constructor,Method,Field】都实现了AnnotatedElement这个接口。

注解解析要结合反射技术实现
如果要操作类上的注解,通过Class对象调用上面的方法进行操作。
如果要操作构造方法上的注解,通过Constructor对象调用上面的方法进行操作
如果要操作成员方法上的注解,通过Method对象调用上面的方法进行操作。

三. 动态代理
作用:对某个对象进行【增强】
在Proxy中有一个类,叫做newProxyInstance,这个方法可以创建一个代理对象,并对另一个对象(被代理的)进行增强
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):该方法可以创建动态代理对象并返回。
参数loader:表示类加载器。 固定写法 当前类.class.getClassLoader()
参数interfaces:代理对象实现的接口,这些接口一般与被代理对象实现的接口一致。固定写法:被代理对象.getClass().getInterfaces();
参数h:参数是InvocationHandler类型,表示执行处理类【真正是接口】,里面有一个方法叫做invoke
Object invoke(Object proxy, Method method, Object[] args):这个invoke方法会在每次动态代理对象调用任何方法的时候执行。
参数proxy:表示代理对象
参数method:代理对象调用的方法
参数args:代理对象调用方法时的实际参数
返回值:代理对象调用方法后产出结果

今天重点:反射必须能写出来。
注解能够会使用别人提供的注解
能够看懂动态代理
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr.YHL

谢谢您的肯定

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值