类加载器
- 引导类加载器:java.lang.String -> rt.jar JDK基础类
- 扩展类加载器:JDK自带扩展类
- 应用类加载器:ClassPath 自定义类
// 应用类加载器, 只有一个
ClassLoader loader = DemoClassLoader.class.getClassLoader();
ClassLoader loader2 = User.class.getClassLoader();
// 引导类加载器, 和 扩展类加载器 代码获取不到
ClassLoader loader1 = String.class.getClassLoader();
双亲委派模型
– 加载一个类的时候, 会先由引导类加载器和扩展类加载器 来搜索有没有对应的类
– 如果有, 那么就加载完成,
– 如果没有, 会继续使用应用类加载器来完成搜索和加载工作
– 如果三个类加载器都找不到对应的类, 那么就会抛出ClassNotFoundException
- 问:自己定义一个java.lang.String类, 能不能使用?
不能
反射
操作类的字节码对象 — Class对象
- 获得Class对象
// 1.手动类加载
Class cls1 = null;
try {
cls1 = Class.forName("java.lang.String");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 2.获得类的字节码类型
Class cls2 = int.class;
// 3.通过对象的getClass方法
Class cls3 = "".getClass();
// 类, 接口, 注解, 枚举
// 每一个类的字节码对象只有一个
System.out.println(cls1 == cls3); // true
- 操作构造器 getConstructor – newInstance()
Class cls = String.class;
// 1.获得所有的父接口
Class[] interfaces = cls.getInterfaces();
// 2.获得类的构造方法
// 获得一个参数是String类型的构造器 new String("str");
Constructor constructor = cls.getConstructor(String.class);
// "hello" 就是调用这个构造方法传递的实际参数
String ob = (String) constructor.newInstance("hello");
// 通过反射创建user对象
// 1.获得user的字节码对象
Class cl = User.class;
// 2.获得user的构造方法
Constructor con1 = cl.getConstructor();
// 3.创建对象
User u1 = (User) con1.newInstance();
// cl.newInstance(); // -- 直接通过无参构造器创建对象
// new User("张三", 23);
// 获得user的构造方法
// Constructor con2 = cl.getConstructor(String.class, int.class);
// 成员私有时, 获得方法需要使用 getDeclaredxx
Constructor con2 = cl.getDeclaredConstructor(String.class, int.class);
// 私有成员可以手动设置可见
con2.setAccessible(true);
// 创建对象
User u2 = (User) con2.newInstance("lucy", 18);
- 操作方法 getMethod — invoke()
// 通过反射调用study方法
/*
User user = new User("张三", 23);
user.study("Java");
*/
public class Demo {
public static void main(String[] args) throws Exception {
User user = new User("张三");
// 1.获得user的字节码对象
Class cls = User.class;
// 2.获得study这个方法对象(私有方法)
Method method = cls.getDeclaredMethod("study", String.class);
// 2.2设置方法可见
method.setAccessible(true);
// 3.调用方法
method.invoke(user, "Java");
}
}
- 操作成员变量 getField — get()、set()
// 通过反射调用成员变量
/*
User user = new User("张三", 23);
user.age
*/
public class Demo {
public static void main(String[] args) throws Exception {
User user = new 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);
}
}
注解
@Interface 可以包含属性
- @Override: 重写
只能放在方法上, 没有属性 - @Deprecated: 过时的
放在属性/方法/类/构造方法/局部变量/方法参数 - @SuppressWarnings: 压制警告
放在属性/方法/类/构造方法/局部变量/方法参数
必须要有属性 - 自定义注解(包含元注解)
// 元注解
@Retention(RetentionPolicy.RUNTIME) // 注解在整个运行阶段都可见
@Target({ElementType.METHOD, ElementType.TYPE}) // 规定注解的使用位置
public @interface MyAnnotation {
// 属性语法 类型 属性名()
int age(); // 使用注解时属性必须赋值
String name() default ""; // 使用注解时可以不赋值, 就使用默认值
int[] score(); // 使用注解时必须赋值, 可以是多个
}
- 解析注解 — 反射
注意:注解的可见范围必须是 运行阶段 — RUNTIME
// 解析Demo上的MyAnnotation注解
public class Demo01 {
public static void main(String[] args) {
// 1.获得Demo对应的字节码对象
Class cls = Demo.class;
// 2.获得Demo类上的MyAnnotation注解
MyAnnotation annotation = (MyAnnotation) cls.getAnnotation(MyAnnotation.class);
// 3.获得注解中的属性值
int age = annotation.age();
}
}
动态代理(方法增强)
-
代理对象: 增强后的对象
被代理对象: 原来的对象 -
代理对象和被代理对象拥有相同的父接口
用被代理对象来构建代理对象,但没有直接联系
/**
* 生成代理对象
* 1.准备一个接口
* 2.准备接口的实现类
*/
public class Demo01 {
UserService us ;
public void main(String[] args) {
/*
1.ClassLoader loader
被代理对象的类加载器
2.Class<?>[] interfaces
被代理对象的父接口
3.InvocationHandler
方法执行器
*/
UserService proxyService = (UserService) Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(),
UserServiceImpl.class.getInterfaces(),
new InvocationHandler() {
/* proxyService.modify();
proxyService.register("张三");
1. proxy: 代理对象 proxyService
2. method: 方法对象 register
3. args: 方法参数实际值列表 "张三"
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开启事务");
// 执行原来的方法
Object o = method.invoke(us, args);
System.out.println("关闭事务");
return o;
}
});
proxyService.modify(); // --> 实际去调用了invoke方法
int i = proxyService.register("张三");
}
}
public interface UserService {
int register(String user);
void modify();
}
public class UserServiceImpl implements UserService {
@Override
public int register(String user) {
System.out.println(user + " register");
return user.length();
}
@Override
public void modify() {
System.out.println("modify");
}
}
补充:
- 面向接口开发的优势
1、解耦合
2、方便动态代理的实现