枚举 枚举是Java中一种特殊类型,一般用来做信息的标记和分类
在不是枚举的情况下,也可以实现信息的标记和分类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package _enum;public class EnumDemo01 { public static void main (String[] args) { useSeason(Season.AUTUMN); } public static void useSeason (String string) { switch (string) { case Season.SPRING -> System.out.println("春天" ); case Season.SUMMER -> System.out.println("夏天" ); case Season.AUTUMN -> System.out.println("秋天" ); case Season.WINTER -> System.out.println("冬天" ); } } } class Season { public static final String SPRING = "SPRING" ; public static final String SUMMER = "SUMMER" ; public static final String AUTUMN = "AUTUMN" ; public static final String WINTER = "WINTER" ; private Season () {} public static String[] getAllSeasons() { return new String []{SPRING, SUMMER, AUTUMN, WINTER}; } }
使用枚举技术优化代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package _enum;public class EnumDemo01 { public static void main (String[] args) { useSeason(Season.AUTUMN); } public static void useSeason (Season season) { switch (season) { case SPRING -> System.out.println("春天" ); case SUMMER -> System.out.println("夏天" ); case AUTUMN -> System.out.println("秋天" ); case WINTER -> System.out.println("冬天" ); } } } enum Season { SPRING, SUMMER, AUTUMN, WINTER; }
枚举入参严谨,提示性更好,代码优雅
枚举定义格式:
1 2 3 修饰符 enum 枚举类名 { 枚举项1 , 枚举项2 , 枚举项3. .. }
枚举的特点
每一个枚举项其实就是该枚举类的一个对象
通过枚举类名去访问指定的枚举项
所有枚举类都是Enum的子类
枚举也是类,可以定义成员变量
枚举类的第一行上必须是枚举项 ,最后一个枚举项的分号是可以省略的。但如果枚举类有其他东西,就不能省略(推荐不省略)
枚举类可以有构造器 ,但必须是private的,它默认也是private
枚举类也可以有抽象方法 ,但枚举项必须重写该方法
类加载器 主要作用就是将类的字节码载入方法区中
加载时机:用到就加载。比如创建对象、调用类的静态成员、初始化继承体系、使用反射加载类的字节码
加载过程:加载、链接、初始化
第一步加载:
通过包名 + 类名,获取这个类,准备用流进行传输
将类加载到内存中
加载完毕创建一个class对象
第二步链接:
验证:验证类是否符合JVM规范,安全性检查
准备:为static变量分配空间,设置默认值
解析:将常量池中的符号引用 解析为直接引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package classloader;public class ClassLoaderDemo01 { public static void main (String[] args) { ClassLoader classLoader = B.class.getClassLoader(); B b = new B (); } } class B { C c = new C (); } class C {}
第三步初始化:
初始化:根据程序员程序编码制定的主观计划去初始化类变量和其他资源
可以理解为将类中的静态成员变量赋上真实的值
类加载器的分类
Bootstrap class loader:启动类加载器,虚拟机内置类加载器 ,通常表示为null。因为这个加载器是利用C++实现,所以获取到的只能是null
Platfrom class loader:平台类加载器,负责加载 JDK中一些特殊的模块 。负责加载lib/modules内部的类
平台类加载器是从JDK9版本之后出现的
JDK9版本之前是Extension Class Loader,扩展类加载器。负责加载jre/lib/ext目录下的类
Application class loader:应用程序类加载器。负责加载自己写的类
自定义类加载器:上级为Application,目前不做讲解
方法
功能
public Classloader getClassLoader()
获取该类的 类加载器对象
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package classloader;public class ClassLoaderDemo { public static void main (String[] args) { ClassLoader classLoader1 = String.class.getClassLoader(); System.out.println(classLoader1); ClassLoader classLoader2 = ClassLoaderDemo.class.getClassLoader(); System.out.println(classLoader2); System.out.println(classLoader2.getParent()); System.out.println(classLoader2.getParent().getParent()); } }
Application类加载器的上级:Platform类加载器
Platform类加载器的上级:Bootstrap类加载器
双亲委派模式
如果一个类加载器收到了类加载请求,它并不会自己先去加载
而是把这个请求委托给父类的加载器去执行
如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归
请求最终将到达顶层的启动类加载器
如果父类加载器可以完成类加载任务,就成功返回
但父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式
这种加载模式的好处在于避免类的重复加载
反射
框架技术的灵魂
在运行状态 中,对于任意一个类,都能够知道这个类的所有属性和方法。对于任意一个对象,都能够调用它的任意属性和方法
这种动态获取信息以及动态调用对象方法的功能成为Java语言的反射机制
获取类的字节码对象
Class.forName(“全类名”);
类名.class
对象.getClass();
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package reflect;import pojo.Student;public class ReflectDemo01 { public static void main (String[] args) throws ClassNotFoundException { Class<?> class1 = Class.forName("pojo.Student" ); Class<Student> class2 = Student.class; Student s = new Student (); Class<? extends Student > class3 = s.getClass(); System.out.println(class1); System.out.println(class2); System.out.println(class3); System.out.println((class1 == class2) && (class2 == class3)); } }
反射类中的构造方法
方法
功能
Constructor<?>[] getConstructors()
返回所有公共构造方法 对象的数组
Constructor<?>[] getDeclaredConstructors()
返回所有构造方法 对象的数组
Constructor<T> getConstructor(Class<?>… parameterTypes)
返回单个公共构造方法对象
Constructor<T> getDeclaredConstructor(Class<?>… parameterTypes)
返回单个构造方法对象
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package reflect;import java.lang.reflect.Constructor;import java.util.Arrays;public class ReflectDemo02 { public static void main (String[] args) throws ClassNotFoundException, NoSuchMethodException { Class<?> class1 = Class.forName("pojo.Student" ); Constructor<?>[] constructors1 = class1.getConstructors(); Constructor<?>[] constructors2 = class1.getDeclaredConstructors(); System.out.println(Arrays.toString(constructors1)); System.out.println(Arrays.toString(constructors2)); Constructor<?> constructor1 = class1.getConstructor(String.class, int .class); System.out.println(constructor1); Constructor<?> constructor2 = class1.getDeclaredConstructor(String.class); System.out.println(constructor2); } }
通过构造方法对象进行实例化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package reflect;import pojo.Student;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;public class ReflectDemo03 { public static void main (String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { Class<Student> class1 = Student.class; Constructor<Student> constructor1 = class1.getConstructor(String.class, int .class); Student student1 = constructor1.newInstance("张三" , 18 ); System.out.println(student1); Constructor<Student> constructor2 = class1.getDeclaredConstructor(String.class); constructor2.setAccessible(true ); Student student2 = constructor2.newInstance("张三" ); System.out.println(student2); } }
newInstance:实例化对象的方法
setAccessible:设置为true表示取消权限检查
反射类中的成员变量
方法
功能
Field[] getFields()
返回所有公共成员变量 对象的数据
Field[] getDeclaredFields()
返回所有成员变量 对象的数组
Field getField(String name)
返回单个公共成员变量对象
Field getDeclaredField(String name)
返回单个成员变量对象
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package reflect;import pojo.Student;import java.lang.reflect.Field;import java.util.Arrays;public class ReflectDemo04 { public static void main (String[] args) throws NoSuchFieldException { Class<Student> class1 = Student.class; Field[] fields1 = class1.getFields(); System.out.println(Arrays.toString(fields1)); Field[] fields2 = class1.getDeclaredFields(); System.out.println(Arrays.toString(fields2)); Field name = class1.getField("id" ); Field age = class1.getDeclaredField("age" ); System.out.println(name); System.out.println(age); } }
Field类的设置和获取方法:
方法
说明
void set(Object obj,Object value)
赋值
Object get(Object obj)
获取值
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package reflect;import pojo.Student;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;public class ReflectDemo05 { public static void main (String[] args) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException { Class<Student> studentClass = Student.class; Student student1 = studentClass.getConstructor().newInstance(); Student student2 = studentClass.getConstructor().newInstance(); Field name = studentClass.getDeclaredField("name" ); Field age = studentClass.getDeclaredField("age" ); name.setAccessible(true ); age.setAccessible(true ); name.set(student1,"张三" ); age.set(student1,18 ); name.set(student2,"李四" ); age.set(student2,20 ); System.out.println(student1); System.out.println(student2); String s1 = (String) name.get(student1); String s2 = (String) name.get(student2); System.out.println(s1); System.out.println(s2); } }
反射建议:禁止暴力反射操作
反射类中的成员方法
方法
功能
Method[] getMethods()
返回所有公共成员方法 对象的数组,包括继承 的
Method[] getDeclaredMethods()
返回所有成员方法对象的数组,不包括继承 的
Method getMethod(String name, Class<?>… parameterTypes)
返回单个公共成员方法对象
Method getDeclaredMethod(String name, Class<?>… parameterTypes)
返回单个成员方法对象
方法
功能
Object invoke(Object obj, Object… args)
运行方法
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package reflect;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.Arrays;public class ReflectDemo06 { public static void main (String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { Class<Zi> class1 = Zi.class; Method[] methods1 = class1.getMethods(); System.out.println(Arrays.toString(methods1)); Method[] methods2 = class1.getDeclaredMethods(); System.out.println(Arrays.toString(methods2)); Method count = class1.getMethod("count" , int [].class); Zi zi = class1.getConstructor().newInstance(); count.invoke(zi,new int []{1 ,2 ,3 ,4 ,5 }); } } class Fu { public void show () { System.out.println("Fu Show ..." ); } } class Zi extends Fu { public Zi () {}; public void count (int ... nums) { int cnt = 0 ; for (int i = 0 ; i < nums.length; i++) { cnt += nums[i]; } System.out.println(cnt); } private void printHello (int i) { System.out.println("Hello" ); } private void printHello (double i) { System.out.println("Hello" ); } }
反射练习 伪泛型 需求:请向一个Integer的集合,添加一个String字符串
思路:Java中的泛型是假的,只在编译的时候有效
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package reflect;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.ArrayList;import java.util.Collections;public class ReflectTest01 { public static void main (String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { ArrayList<Integer> list = new ArrayList <>(); Collections.addAll(list,1 ,2 ,3 ,4 ,5 ); Class<? extends ArrayList > class1 = list.getClass(); Method add = class1.getMethod("add" , Object.class); add.invoke(list,"你好" ); System.out.println(list); } }
综合练习 需求:
编写三个任意的Javabean类:Student、Teacher、Worker
属性均为name、age
方法要求:
Student里面写学习study和吃饭eat的方法(方法无参返回)
Teacher里面写上课teach和吃饭eat的方法(方法无参返回)
Worker里面写工作work和睡觉sleep的方法
本地新建配置文件properties
属性:className=Student类的全类名,methodName=study
在测试类中读取properties文件中的两个属性值
利用反射创建学生类的对象,并调用方法
修改配置文件,不修改代码,运行老师类中的上课方法
修改配置文件,不修改代码,运行工人类中的工作方法
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package reflect;import java.io.FileInputStream;import java.io.IOException;import java.lang.reflect.Method;import java.util.Properties;public class Test { public static void main (String[] args) throws Exception { Properties prop = new Properties (); try (FileInputStream fis = new FileInputStream ("Day06\\config.properties" )) { prop.load(fis); } catch (IOException e) { e.printStackTrace(); } Class<?> class1 = Class.forName(prop.getProperty("className" )); Object o = class1. getConstructor(String.class,int .class). newInstance(prop.getProperty("name" ),Integer.parseInt(prop.getProperty("age" ))); Method method = class1.getMethod(prop.getProperty("methodName" )); method.invoke(o); } }
config.properties:
1 2 3 4 className=reflect.Worker methodName=work name=Alice age=28
注解
Annotation表示注解,是JDK1.5的新特性。主要作用:对程序进行标注
理解:注释是给人看的,注解是给JVM虚拟机看的。通过注解可以给类增加额外的信息 。编译器或JVM可以根据注解来完成对应的功能。
JDK中常见的注解:
@Override:表示方法的重写
@Deprecated:表示修饰的方法已经过时
@SuppressWarning(“all”):压制警告
注解常用位置:
类
方法
自定义注解
自定义注解单独存在意义不大,一般会跟反射结合起来使用
1 2 3 4 5 6 7 8 9 public @interface 注解名称 { public 属性类型 属性名() default 默认值; } public @interface Anno { String show () default "show..." ; }
属性类型:
基本数据类型
String
Class
注解
枚举
以上类型的一维数组
当属性不存在默认值时,使用注解需要指定属性值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package anno;@MyAnno02(show = "Hello") public class MyAnnoDemo01 { @MyAnno02(show = "World",test = "CBA") public static void main (String[] args) { method(); } @MyAnno03("Test") public static void method () { System.out.println("method" ); } } @interface MyAnno02 { String show () ; String test () default "ABC" ; } @interface MyAnno03 { String value () ; }
在使用注解时,如果注解的属性没有给出默认值,需要手动给出
@Anno(name=”张三”)
如果数组中只有一个属性值,在使用时{}是可以省略的
如果只有一个属性名字为value没有赋值,使用时直接给出值,不需要写属性名
自定义测试注解 需求:自定义一个@Test,用于指定类的方法上。如果类中的某个的方法上使用了该注解,就执行该方法
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package anno;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;public class MyTestDemo01 { public static void main (String[] args) throws Exception { Class<MethodDemo> clazz = MethodDemo.class; MethodDemo methodDemo = clazz.getConstructor().newInstance(); Method[] methods = clazz.getMethods(); for (Method method : methods) { if (method.isAnnotationPresent(Test.class)) { method.invoke(methodDemo); } } } } @interface Test {}class MethodDemo { public MethodDemo () {}; @Test public void show () { System.out.println("show" ); } @Test public void print () { System.out.println("print" ); } public void method () { System.out.println("method" ); } }
测试类代码没有问题,但是执行后没有调用show和print方法
主要是因为注解存在生命周期:源代码阶段、字节码阶段、运行阶段,默认是处于字节码阶段
但是反射是在运行阶段 的进行操作,所以无法正确识别到注解
isAnnotationPresent() :查看是否存在对应注解,参数为注解的字节码对象
元注解
元注解名
说明
@Target
指定了注解能在哪里使用
@Retention
可以理解为保留时间(生命周期)
@Target:用来标识注解使用的位置,如果没有使用该注解表示,则自定义的注解可以使用在任意位置
@Retention:用来标识注解的生命周期(有效范围)
将自定义测试注解案例进行修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package anno;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;public class MyTestDemo01 { public static void main (String[] args) throws Exception { Class<MethodDemo> clazz = MethodDemo.class; MethodDemo methodDemo = clazz.getConstructor().newInstance(); Method[] methods = clazz.getMethods(); for (Method method : methods) { if (method.isAnnotationPresent(Test.class)) { method.invoke(methodDemo); } } } } @Retention(RetentionPolicy.RUNTIME) @interface Test {}class MethodDemo { public MethodDemo () {}; @Test public void show () { System.out.println("show" ); } @Test public void print () { System.out.println("print" ); } public void method () { System.out.println("method" ); } }
现在就可以正确调用show和print方法了
动态代理
动态代理是对象的另外一种创建方式,这种方式可以在不修改源码的情况下,增强方法
动态代理就是在运行时生成代理类,无需手动为每个目标类编写代理类
java.lang.reflect.Proxy类:提供了使用动态代理方式创建对象的API
方法
说明
static Object newProxyInstance( ClassLoader loader , Class<?>[] interfaces , InvocationHandler h )
参数一: 用于指定用哪个类加载器,去加载生成的实现类 参数二: 指定接口,这些接口用于指定生成的实现类中有什么 也就是有哪些方法 参数三: 用来指定生成的实现类中不同方法的实现方案
如果目标类的方法不是通过接口实现的,JDK动态代理(Proxy.newProxyInstance)无法直接使用
代理 类似于通过一个中间类对源方法进行增强
SaleImpl这个类就是中间类(代理类)
不过每当有用户需要进行新的方法增强时,就得重新编写另一份专门的代理类
这种代理方式被称为静态代理
示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package 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 ProxyDemo01 { public static void main (String[] args) { ArrayList<Integer> list = new ArrayList <>(); List<Integer> o = (List<Integer>) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class []{List.class}, new InvocationHandler () { @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { String name = method.getName(); long begin = 0 , end = 0 ; if ("add" .equals(name)) { begin = System.currentTimeMillis(); Object invoke = method.invoke(list, args); end = System.currentTimeMillis(); System.out.println("耗时:" + (end - begin)); return invoke; } return null ; } }); o.add(111 ); o.add(222 ); o.add(333 ); System.out.println(list); } public static void showClassLoader () { ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); System.out.println(systemClassLoader); } }
实现动态代理的步骤较为复杂,不需要会熟练使用,但一定要认真理解
但在Spring框架中,有面向切面编程(AOP),其中已经对动态代理进行了封装
小结
动态代理是对象的另外一种创建方式,这种方式可以在不修改源码的情况下,增强方法
代理对象 可以在原有功能的基础上进行增强
动态代理在运行时动态的生成代理类 ,无需手动为每个目标类编写代理类
使用Proxy中的newProxyInstance()方法创建代理对象