一、枚举
枚举是Java中的一种特殊类型,一般用来做信息的标记和分类 使用标记来区分春、夏、秋、冬四个季节
1、传统的枚举方法

2、枚举的枚举方法


3、枚举的特点
1、每一个枚举项其实就是该枚举的一个对象
2、通过枚举类名 去访问指定的枚举项
3、所有枚举类都是Enum的子类
4、枚举也是类,可以定义成员变量
5、枚举类的第一行上必须是枚举项 ,最后一个 枚举项后的分号是可以省略的
但是如果枚举类有其他的东西 ,这个分号就不能省略(建议不要省略)
6、枚举类可以有构造器 ,但必须是private的 ,它默认也是private
7、枚举类也可以有抽象方法 ,但是枚举项 必须重写该方法
4、代码示例
java
package com.itheima.my_enum;
import java.lang.annotation.ElementType;
public class EnumTest {
/*
枚举介绍: Java中的一种特殊的类型, 常用于信息的标记和分类
1. 常量
2. 枚举 (入参严谨, 提示性更好代码优雅)
*/
public static void main(String[] args) {
useSeason(Season.WINTER);
System.out.println(Season.SPRING.ordinal());//枚举项是该类的对象,可以调用成员方法
System.out.println(Season.SUMMER.ordinal());//获取枚举项的序号
System.out.println(Season.AUTUMN.ordinal());
System.out.println(Season.WINTER.ordinal());
}
//常量标记不严谨
public static void useSeason(Season season) {
switch (season) {
case SPRING:
System.out.println("春季");
break;
case SUMMER:
System.out.println("夏季");
break;
case AUTUMN:
System.out.println("秋季");
break;
case WINTER:
System.out.println("冬季");
break;
}
}
}
//每一个枚举项都是该类的对象
enum Season {
SPRING("春季"), SUMMER("夏季"), AUTUMN("秋季"), WINTER("冬季");//对象调用构造方法
//枚举一般没有乱七八糟的东西,只有几个枚举项
String name;//可以定义成员变量
private Season(String name){
this.name = name;
}
}
5、枚举的使用场景
用来做信息的标记和分类

--->

代码可读性更高,入参约束严谨,代码优雅
二、类加载器
将类的字节码载入方法区中
1、加载时机
加载时机:用到就加载
包括:创建对象、调用类的静态成员、初始化继承体系、使用反射加载类的字节码
2、加载过程
(1)加载
-
通过包名+类名,获取这个类,准备用流进行传输
-
将类加在到内存中
-
加载完毕创建一个class对象

(2)链接
-
验证:验证类是否符合JVM规范,安全性检查
-
准备:为static变量分配空间,设置默认值
-
解析:将常量池中的符号引用 解析为直接引用
(3)初始化
根据程序员程序编码制定的主观计划 去初始化类变量 和其他资源
三、类加载器的分类
1、Bootstrap class loader:启动类加载器,虚拟机的内置类加载器,通常表示为null
C++实现,获取到的只能是null
2、Platform class loader:平台类加载器,负责加载JDK中一些特殊的模块
负责加载lib\modules内部的类
JDK9之前是:(Extension Class Loader)扩展类加载器 负责加载jre\lib\ext目录下的类
3、Application class loader:应用程序类加载器
负责加载自己写的类
自定义类加载器:上级为Application 目前不做讲解
java
public Classloader getclassLoader()
获取该类的,类加载器对象
逻辑上父类:应用程序类-->扩展类-->启动类
代码示例:
java
package com.itheima.classloader;
import sun.net.spi.nameservice.dns.DNSNameService;
public class ClassLoaderDemo {
public static void main(String[] args) {
// 1. 获取BootStrapClassLoader (启动类\引导类加载器)
ClassLoader classLoader1 = String.class.getClassLoader();
System.out.println(classLoader1);
// 2. 获取PlatformClassLoader (平台类加载器 jdk\lib\modules)
ClassLoader classLoader2 = DNSNameService.class.getClassLoader();
System.out.println(classLoader2);
// 3. 获取ApplicationClassLoader (应用程序类加载器)
ClassLoader classLoader3 = ClassLoaderDemo.class.getClassLoader();
System.out.println(classLoader3);
// 测试类加载器的上下级关系
System.out.println(classLoader2.getParent());//获取上级加载器
System.out.println(classLoader2.getParent().getParent());
}
}
四、双亲委派模式
如果一个类加载器收到了类加载请求,它并不会自己先去加载
而是把这个请求委托给父类的加载器去执行
如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归
请求最终将到达顶层的启动类加载器
如果父类加载器可以完成类加载任务 ,就成功返回
倘若父类加载器无法完成此加载任务 ,子加载器才会尝试自己去加载,这就是双亲委派模式
双亲委派模式图解

五、反射介绍-获取字节码对象
框架技术的灵魂
是在运行状态中 ,对于任意一个类,都能够知道这个类的所有属性和方法
对于任意一个对象 ,都能够调用它的任意属性和方法
这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制

学习路径:
获取类的字节码对象
反射类中的构造方法 ,随后创建对象
反射类中的成员变量 ,完成赋值和获取
反射类中的成员方法 ,调用成员方法
获取字节码对象的三种方式
java
1. Class.forName("全类名");//Class不是一个class类,而是一个对象。第一种方式比较灵活
2. 类名.class//第二种方式比较简单
3. 对象.getClass();//第三种方式可以获取对象的。来自Object类,所以所有对象都可以调用该方法
代码示例:
java
package com.itheima.reflect;
import com.itheima.pojo.Student;
public class ReflectDemo1 {
/*
反射获取类的字节码对象
1. Class.forName("全类名");
2. 类名.class
3. 对象.getClass();
*/
public static void main(String[] args) throws ClassNotFoundException {
// 1. Class.forName("全类名");
Class<?> class1 = Class.forName("com.itheima.pojo.Student");
// 2. 类名.class
Class<Student> class2 = Student.class;
// 3. 对象.getClass();
Student stu = new Student();
Class<? extends Student> class3 = stu.getClass();
//证实字节码对象的唯一性
System.out.println(class1 == class2);
System.out.println(class2 == class3);
System.out.println(class3 == class1);
}
}
六、反射获取构造方法创建对象
反射类中的构造方法
java
1. Constructor<?>[] getConstructors() 返回所有公共构造方法对象的数组(获取多个,返回公共权限的构造方法)
2. Constructor<?>[] getDeclaredConstructors() 返回所有构造方法对象的数组(暴力反射获取)
3. Constructor<T> getConstructor(Class<?>... parameterTypes) 返回单个公共构造方法对象(返回指定的Constructor对象)
4. Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 返回单个构造方法对象(暴力反射获取)
1. T newInstance(Object...initargs) 根据指定的构造方法创建对象
2. setAccessible(boolean flag) 设置为true,表示取消访问检查
java
package com.itheima.reflect;
import com.itheima.pojo.Student;
import java.lang.reflect.Constructor;
public class ReflectDemo2 {
/*
反射类中的构造方法
1. Constructor<?>[] getConstructors() 返回所有公共构造方法对象的数组
2. Constructor<?>[] getDeclaredConstructors() 返回所有构造方法对象的数组
3. Constructor<T> getConstructor(Class<?>... parameterTypes) 返回单个公共构造方法对象
4. Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 返回单个构造方法对象
---------------------------------------------------------------------------------------------------
创建对象的方法
1. T newInstance(Object...initargs) 根据指定的构造方法创建对象
2. setAccessible(boolean flag) 设置为true,表示取消访问检查
---------------------------------------------------------------------------------------------------
*/
public static void main(String[] args) throws Exception {
// 1. 获取类的字节码对象
Class<Student> studentClass = Student.class;
// 2. 反射构造方法对象
Constructor<Student> constructor = studentClass.getDeclaredConstructor(String.class, int.class);//反射String和int类型的构造方法
//取消访问检查
constructor.setAccessible(true);
// 3. 通过构造方法对象, 完成实例化
Student stu = constructor.newInstance("张三", 23);
System.out.println(stu);
}
private static void method(Class<Student> studentClass) {//暴力反射获取
Constructor<?>[] constructors = studentClass.getDeclaredConstructors();
for (Constructor<?> c : constructors) {
System.out.println(c);
}
}
}
七、反射操作类中成员变量
反射类中的成员变量
java
1. Field[] getFields() 返回所有公共成员变量对象的数组
2. Field[] getDeclaredFields() 返回所有成员变量对象的数组
3. Field getField(String name) 返回单个公共成员变量对象
4. Field getDeclaredField(String name) 返回单个成员变量对象
Field类的设置和获取方法
1. void set(Object obj, Object value) 赋值
2. Object get(Object obj) 获取值
代码示例:
java
package com.itheima.reflect;
import com.itheima.pojo.Student;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
public class ReflectDemo3 {
/*
反射类中的成员变量
1. Field[] getFields() 返回所有公共成员变量对象的数组
2. Field[] getDeclaredFields() 返回所有成员变量对象的数组
3. Field getField(String name) 返回单个公共成员变量对象
4. Field getDeclaredField(String name) 返回单个成员变量对象
---------------------------------------------------------------------------------------------------
Field类的设置和获取方法
1. void set(Object obj, Object value) 赋值
2. Object get(Object obj) 获取值
---------------------------------------------------------------------------------------------------
*/
public static void main(String[] args) throws Exception {
//拿到Students的字节码对象
Class<Student> studentClass = Student.class;
// 反射类的空参构造方法并创建对象
Student stu1 = studentClass.getConstructor().newInstance();
Student stu2 = studentClass.getConstructor().newInstance();
// 获取成员变量的对象
Field nameField = studentClass.getDeclaredField("name");
nameField.setAccessible(true);//取消访问检查
Field ageField = studentClass.getDeclaredField("age");
ageField.setAccessible(true);//取消访问检查
// 通过成员变量对象, 完成赋值和获取操作
nameField.set(stu1, "张三");
nameField.set(stu2, "李四");
ageField.set(stu1, 23);
ageField.set(stu2, 24);
System.out.println(nameField.get(stu1));
System.out.println(nameField.get(stu2));
System.out.println(ageField.get(stu1));
System.out.println(ageField.get(stu2));
}
}
八、反射操作类中的成员方法
开发中禁止暴力反射操作
反射类中的成员方法
java
1. Method[] getMethods() 返回所有公共成员方法对象的数组,包括继承的
2. Method[] getDeclaredMethods() 返回所有成员方法对象的数组, 不包括继承的
3. Method getMethod(String name, Class<?>... parameterTypes) 返回单个公共成员方法对象
4. Method getDeclaredMethod(String name, Class<?>... parameterTypes) 返回单个成员方法对象
Method类的执行方法
Object invoke(Object obj, Object... args) 运行方法
代码示例:
java
package com.itheima.reflect;
import com.itheima.pojo.Student;
import java.lang.reflect.Method;
public class ReflectDemo4 {
/*
反射类中的成员方法
1. Method[] getMethods() 返回所有公共成员方法对象的数组,包括继承的
2. Method[] getDeclaredMethods() 返回所有成员方法对象的数组, 不包括继承的
3. Method getMethod(String name, Class<?>... parameterTypes) 返回单个公共成员方法对象
4. Method getDeclaredMethod(String name, Class<?>... parameterTypes) 返回单个成员方法对象
---------------------------------------------------------------------------------------------------
Method类的执行方法
Object invoke(Object obj, Object... args) 运行方法
---------------------------------------------------------------------------------------------------
*/
public static void main(String[] args) throws Exception {
Class<Student> studentClass = Student.class;
//反射构造方法并创造实例
Student stu = studentClass.getConstructor().newInstance();
// 反射指定的成员方法对象
Method eat1 = studentClass.getMethod("eat");
Method eat2 = studentClass.getMethod("eat", int.class);
// 让成员方法执行
eat1.invoke(stu);
eat2.invoke(stu, 3);
}
}
九、反射练习
1、泛型Integer添加字符串
java
package com.itheima.test;
import java.lang.reflect.Method;
import java.util.ArrayList;
public class ReflectTest1 {
/*
需求: 请向一个泛型为 Integer 的集合,添加一个 String 字符串
思路: Java 中的泛型是假的,只在编译的时候有效
*/
public static void main(String[] args) throws Exception {
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
Class<? extends ArrayList> listClass = list.getClass();
Method addMethod = listClass.getMethod("add", Object.class);
addMethod.invoke(list, "张三");
System.out.println(list);
}
}
2、反射的灵活性
改动配置文件的全类名和方法名就可以让程序有不一样的效果
java
package com.itheima.test;
import java.io.FileInputStream;
import java.lang.reflect.Method;
import java.util.Properties;
public class ReflectTest2 {
/*
编写三个任意的Javabean类,Student类,Teacher类,Worker类。
属性均为:name,age
方法要求:
Student里面写学习study和吃饭eat的方法(方法无参无返回)
Teacher里面写上课teach和吃饭eat的方法(方法无参无返回)
Worker里面写工作work和睡觉sleep的方法(方法无参无返回)
本地新建配置文件properties
属性: className=Student类的全类名 methodName=study
在测试类中读取properties文件中的两个属性值
利用反射创建对象学生类的对象,并调用方法
修改配置文件,不修改代码,运行老师类中的上课方法
修改配置文件,不修改代码,运行工人类中的工作方法
*/
public static void main(String[] args) throws Exception {
// 1. 加载配置文件
FileInputStream fis = new FileInputStream("day06\\config.properties");
// 2. 创建Properties集合, 加载数据
Properties prop = new Properties();
prop.load(fis);
fis.close();
// 3. 取出数据
String className = prop.getProperty("className");
String methodName = prop.getProperty("methodName");
// 4. 获取类的字节码对象
Class<?> aClass = Class.forName(className);
// 5. 反射内部的构造方法对象, 并实例化
Object obj = aClass.getConstructor().newInstance();
// 6. 反射内部的成员方法对象
Method method = aClass.getMethod(methodName);
// 7. 让方法执行
method.invoke(obj);
}
}
java
//配置文件
className=com.itheima.test.Worker
methodName=work
十、注解
Annotation表示注解,是JDK1.5的新特性。
注解的主要作用:对程序进行标注。
理解:注释是给人看的,注解是给虚拟机看的
通过注解可以给类增加额外的信息。
编译器或JVM可以根据注解来完成对应的功能
1、JDK中常见注解
1.@override:表示方法的重写
2.@Deprecated:表示修饰的方法已过时
3.@SuppressWarnings("al1"):压制警告
java
package com.itheima.anno;
@SuppressWarnings("all")//压制警告
public class JDKAnnoDemo {
public static void main(String[] args) {
method();
int a = 10;
int b = 20;
int max = a > b ? a : b;
}
@Deprecated//已过时
public static void method() {
}
}
2、自定义注解
自定义注解单独存在意义不大,一般会跟反射结合起来使用
格式
java
//自定义注解格式
public @interface 注解名称{
public 属性类型 属性名() default 默认值;
}
//举例
public @interface Anno {
String show() default "show...";
}
自定义注解并定义属性
java
package com.itheima.anno;
public @interface MyAnno1 {
String show1();
String[] show2();
String show3() default "C";
}
属性类型:
基本数据类型
String
Class
注解
枚举
以上类型的一维数组
java
public @interface MyAnno {
public static final int numl =100;
public static final String num2 ="abc";
public static final MyAnno num3 = null;
public static final Class num4= String.class;
public static final int[l num5 ={};
public abstract String showl() default "showl";
public abstract int show2() default 132;
public abstract MyAnno2 show3() default @MyAnno2;
public abstract Class show4()default String.class;
public abstract int[] show5()default {1,2,3};
}
自定义注解的使用
常用位置
1.类
2.方法
注意
在使用注解时,如果注解的属性没有给出默认值 ,需要手动给出
@Anno(name="张三")
如果数组中只有一个属性值,在使用时{}是可以省略的
特殊属性:属性名如果是value()就不用写value = ""了
java
package com.itheima.anno;
public @interface MyAnno2 {
String value();
}
java
package com.itheima.anno;
@MyAnno1(show1 = "A", show2 = {"B", "C", "D"})
@MyAnno2("A")
public class JDKAnnoDemo2 {
@MyAnno1(show1 = "B", show2 = "Q")
public void method() {
}
}
3、自定义注解实战
需求:自定义一个注解Test,用于指定类的方法上
如果类中的某个的方法上使用了该注解,就执行该方法
元注解:就是用在注解上注解
@Target 指定了注解能在哪里使用
@Target:用来标识注解使用的位置,如果没有使用该注解标识,则自定义的注解可以使用在任意位置

@Retention 可以理解为保留时间(生命周期)
@Retention:用来标识注解的生命周期(有效范围)
可使用的值定义在RetentionPolicy枚举类中,常用值如下
SOURCE:注解只作用在源码阶段,生成的字节码文件中不存在
CLASS:注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值
RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段
代码范例
java
//自定义注解
package com.itheima.anno;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//如果不指定生命周期,默认生命周期为在字节码文件中
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
}
java
//配合反射技术,使含有该注解的方法自动运行一个方法
package com.itheima.anno;
import java.lang.reflect.Method;
public class ScanTest {
public static void main(String[] args) throws Exception {
Class<MethodDemo> methodDemoClass = MethodDemo.class;
//字节码反射构造方法创建对象
MethodDemo instance = methodDemoClass.getConstructor().newInstance();
// 获取该类中所有成员方法的对象
Method[] methods = methodDemoClass.getMethods();
// 遍历数组获取每一个成员方法对象
for (Method method : methods) {
// 判断该方法的头顶上, 是否存在@Test注解
if (method.isAnnotationPresent(Test.class)) {//方法用于判断是否有这个注解
// 方法执行
method.invoke(instance);
}
}
}
}
java
//带有该注解的方法自动运行该注解所代表的方法
package com.itheima.anno;
public class MethodDemo {
@Test
public void print() {
System.out.println("print");
}
public void show() {
System.out.println("show");
}
@Test
public void method() {
System.out.println("method");
}
}
十一、动态代理
已有的创建对象的方式
java
Dog d = new Dog();//new
Animal a = new Dog();//多态
Dog.class.getconstructor().newInstance();//反射
在运行时动态生成代理类,无需手动为每个目标类编写代理类
java.lang.reflect.Proxy类
提供了使用动态代理方式 创建对象的API
java
static Object newProxyInstance(ClassLoader loader,Class<?>[]interfaces,InvocationHandler h)
参数介绍
参数一:
用于指定用哪个类加载器(应用程序类加载器),去加载生成的实现类
参数二:
指定接口,这些接口用于指定生成的实现类中有什么也就是有哪些方法
参数三:
用来指定生成的实现类中不同方法的实现方案
代码案例
java
package com.itheima.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
@SuppressWarnings("all")
public class ProxyDemo {
/*
Proxy类:
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
参数一: 用于指定用哪个类加载器,去加载生成的实现类
参数二: 指定接口,这些接口用于指定生成的实现类中有什么,也就是有哪些方法
参数三: 用来指定生成的实现类中不同方法的实现方案
动态代理: 对象的另外一种创建方式, 这种方式可以在不修改源码的情况下, 对方法进行增强
*/
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
//创建代理对象
List<String> newList = (List<String>) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{List.class}, new InvocationHandler() {
// proxy : 生成的代理对象, 一般放在这里不用
// method : 代理的方法
// args : 调用方法的时候, 传入的实际参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
if ("add".equals(name)) {
// 计算add方法的耗时
long begin = System.currentTimeMillis();
Object result = method.invoke(list, args);
long end = System.currentTimeMillis();
System.out.println("耗时: " + (end - begin));
return result;
}
return null;
}
});
newList.add("张三");
newList.add("李四");
newList.add("王五");
System.out.println(list);
}
}
总结
动态代理是对象的另外一种创建方式,这种方式可以在不修改源码的情况下,增强方法
代理对象可以在原有功能的基础上进行增强
动态代理在运行时动态的生成代理类,无需手动为每个目标类编写代理类
使用Proxy中的newProxyInstance()方法创建代理对象