第7章 Java高级技术

一、枚举

枚举是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()方法创建代理对象

相关推荐
LONGZHIQIN1 小时前
C#基础复习笔记
开发语言·笔记·c#
程序员佳佳1 小时前
模型网关灰度不是调百分比:把放量、观测和回滚做成账本
java·数据库·人工智能·redis·gpt·aigc·embedding
可靠的仙人掌1 小时前
SAC(Soft Actor-Critic)算法底座
开发语言·算法·php
学逆向的2 小时前
汇编——数据存储模式
开发语言·汇编·网络安全
geovindu2 小时前
CSharp: Prototype Pattern
开发语言·后端·设计模式·.net·原型模式·创建型模式
Listen·Rain2 小时前
Springboot整合Ollama实现调用本地多模型
java·spring boot·后端
霸道流氓气质2 小时前
Java 工程师 AI 智能体(Agent)完整学习路线
java·人工智能·学习
天天进步20153 小时前
Python全栈项目--校园食堂点餐与推荐系统
开发语言·python
aaPIXa6223 小时前
C++模板元编程:编译期计算Fibonacci数列
java·开发语言·c++