javaweb/day09
类加载器
使用类加载
- 创建对象
- 使用静态成员、方法
- 使用子类会加载父类
三种类加载器
引导类加载器: java.lang.String -> rt.jar JDK基础类
Math、Date、System
扩展类加载器: JDK自带扩展类(现在不存在了)
应用类加载器: ClassPath 自定义类、第三方jar包
ClassPath——类路径——就是src下面的
com.zzxx.utils.DruidUtils -> 自定义类
org.apache.commons.beanutils.BeanUtils -> 自定义类
应用类加载器是可以获得到的,另外两种都是null
public class Demo01ClassLoader {
public static void main(String[] args) {
// 应用类加载器, 只有一个
ClassLoader loader = Demo01ClassLoader.class.getClassLoader();
ClassLoader loader2 = User.class.getClassLoader();
System.out.println(loader == loader2);//true
// 引导类加载器, 和 扩展类加载器 代码获取不到
ClassLoader loader1 = String.class.getClassLoader();
System.out.println(loader1);//null
}
双亲委派模型
- 加载一个类的时候, 会先由引导类加载器和扩展类加载器 来搜索有没有对应的类
- 如果有, 那么就加载完成,
- 如果没有, 会继续使用应用类加载器来完成搜索和加载工作
- 如果三个类加载器都找不到对应的类, 那么就会抛出 ClassNotFoundException(类未找到异常)
面试题:自己定义一个java.lang.String类, 能不能使用?
String类在使用的时候都要先加载,引导类加载器会找到一个java.lang.String类,加载完之后就不管后面了,自己定义的类就没有机会被加载,也就咩有机会被使用
反射
操作类的字节码对象 Class对象
获得class字节码对象
// 1.手动类加载
Class cls = null;
try {
cls = Class.forName("java.lang1.String");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 2.获得类的字节码类型
Class cls2 = int.class;//基本数据类型只能通过这种方式获取
// 3.通过对象的getClass方法
Class cls3 = "".getClass();
// 引用类型有:类, 接口, 注解, 枚举,通过1、3方法获得字节码对象
// 每一个类的字节码对象只有一个
System.out.println(cls == cls3); // true
获得所有的父接口
Class cls = String.class;
// 1.获得所有的父接口
Class[] interfaces = cls.getInterfaces();
System.out.println(Arrays.toString(interfaces));
操作构造器 Constructor
.getConstructor()
newInstance()
创建提供的对象
// 2.获得类的构造方法
Class cls = String.class;
try {
// 获得一个参数是String类型的构造器 new String("str");
Constructor constructor = cls.getConstructor(String.class);
// 通过构造器对象来创建string对象,"hello" 就是调用这个构造方法传递的实际参数
String ob = (String) constructor.newInstance("hello");
System.out.println(ob);//hello
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
创建自己写的对象
// 通过反射创建user对象
// 1.获得user的字节码对象
Class cl = User.class;
// 2.获得user的构造方法
Constructor con1 = cl.getConstructor();
// 3.创建对象
User u1 = (User) con1.newInstance();
System.out.println(u1);
// 成员私有时, 通过反射获得构造方法需要使用 getDeclaredxx
Constructor con2 = cl.getDeclaredConstructor(String.class, int.class);
// 私有成员可以手动设置可见
con2.setAccessible(true);
// 创建对象
User u2 = (User) con2.newInstance("lucy", 18);
System.out.println(u2);
// cl.newInstance(); // -- 直接通过无参构造器创建对象
操作方法 Method
.getMethod()
调用方法:method.invoke()
public boolean study(String course) {
System.out.println(this.name + "正在学习" + course);
return true;
}
public static void main(String[] args) throws Exception {
User user = new User("张三");
// 1.获得user的字节码对象
Class cls = User.class;
// 2.获得study这个方法对象,若为私有,则cls.getDeclaredMethod,并设置方法可见
Method method = cls.getMethod("study", String.class);
// 2.2设置方法可见
//method.setAccessible(true);
// 3.调用方法
boolean b = (Boolean) method.invoke(user, "Java");
System.out.println(b);
}
操作成员变量 Field
通过反射,可以直接调用私有的成员变量
get()
set()
User user = new User("张三");
System.out.println(user);
// 1.获得user的字节码对象
Class cls = User.class;
// 2.获得age属性
Field age = cls.getDeclaredField("age");
age.setAccessible(true);
// 3.给user对象的age属性赋值
age.set(user, 18);
// 4.获得user对象的age属性
age.get(user);
System.out.println(user);
注解
@Override: 重写
只能放在方法上, 没有属性
@Deprecated: 过时的
放在属性/方法/类/构造方法/局部变量/方法参数
@SuppressWarnings: 压制警告
放在属性/方法/类/构造方法/局部变量/方法参数
必须要有属性
元注解
元注解: 标记注解的注解,规定注解的使用位置
@Target(ElementType.METHOD): 规定注解的使用位置
@Retention(RetentionPolicy.RUNTIME)
SOURCE CLASS RUNTIME
注解解析——通过反射(了解)
注意: 注解的可见范围必须是 运行阶段 RUNTIME
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface MyAnnotation2 {
// 属性语法 类型 属性名()
int age() ; // 使用注解时属性必须赋值
}
@MyAnnotation2(age = 1)
public class Demo02 {
// @MyAnnotation2(age = 1)
public Demo02() {
}
@MyAnnotation2(age = 2)
public void test01() {
}
}
// 解析Demo02上的MyAnnotation2注解
public class Demo03 {
public static void main(String[] args) {
// 1.获得Demo02对应的字节码对象
Class cls = Demo02.class;
// 2.获得Demo02类上的MyAnnotation2注解
MyAnnotation2 annotation = (MyAnnotation2) cls.getAnnotation(MyAnnotation2.class);
// 3.获得注解中的属性值
int age = annotation.age();
System.out.println(age);
}
动态代理
装饰者模式
1.和被装饰者拥有相同的父接口
2.用被装饰对象来构建装饰对象
3.重写要增强的方法
动态代理
和装饰者模式一样是为了做方法增强, 代理对象和被代理对象拥有相同的父接口,用被代理对象来构建代理对象
但他的代理对象并不是一个真正存在的类,而且和被代理对象没有直接联系
代理对象: 增强后的对象
被代理对象: 原来的对象
实际上作用:提取公共代码
如果一个实现类没有父接口,那么他就不能使用动态代理,就不能使用方法增强