Java日常-知识点补充3

199 阅读11分钟

一、 函数式接口

1. 定义

只有一个抽象方法的接口

2. 使用

@FunctionalInterface注解

3. 代码

//以函数式接口作为参数
public static void show(MyFunctionalInterface myInter){
    myInter.method();
    
}

public static void main(String[] args){
    //1. 调用show()方法,方法参数是一个接口,可以传递接口的实现类对象
    show(new MyFunctionalInterfaceImpl());
    
    //2. 调用show()方法,方法参数是一个接口,可以传递接口的匿名内部类
    show(new MyFunctionalInterface(){
        @Override
        public void method(){
            System.out.println("使用匿名内部类重写接口中的抽象方法");
        }
    });
    
    //3. 调用show()方法,方法参数是一个函数式接口,所以可以传递lambda表达式
    show(()->System.out.println("使用lambda表达式重写接口中的抽象方法"));
}

Tips:

  • lambda表达式可近似看作匿名内部类的语法糖(实现简单,原理相同),因为实现简单,但原理不同

  • 匿名内部类会生成 Demo$1.class 但lambda表达式不会,内存中少加载一个class文件,提高效率

  • lambda表达式作为方法参数时,具有延迟执行的特点,可以应对不满足条件而无需执行的代码处,提高性能

4. 常用函数式接口

  1. Supplier接口 java.util.function.Supplier<T>

仅包含无参方法T get()

生产型接口

用来获取一个泛型参数指定类型的对象数据 2. Consumer接口 java.util.function.Consumer<T>

包含抽象方法void accept(T t) 默认方法andThen()

消费型接口

消费一个指定泛型的数据,andThen(Consumer<T>)默认方法可以连续消费两次数据 3. Predicate接口 java.util.function.Predicate<T>

包含抽象方法boolean test(T t) 默认方法and,or,negate

用来对指定数据类型的数据进行自定义条件判断的方法,and(Predicate<T>)默认方法可以进行逻辑与判断,or(Predicate<T>)默认方法可以进行逻辑或判断,negate()默认方法可以进行逻辑取反操作

  1. Function接口 java.util.function.Function<T,R>

包含抽象方法R apply(T t) 默认方法andThen(Function<T,R>)

根据类型T的参数获取类型R的结果,如将String类型数据转为Integer类型,andThen(Function<T,R> f)进行先做某事,再做某事操作,类似于Consumer接口中的andThen(Consumer<T>)方法

二、Stream流

JDK1.8新特性

Stream流属于管道流,只能被使用一次

1.使用

获取流有两种方式

1). stream()方法

所有Collection集合都有stream()方法

2). Stream.of(Array array)方法

2.意义

类似工厂流水线,对集合对象进行一系列特定操作

3.常用方法

1).终结方法

返回值不再是Stream接口自身类型,不再支持链式调用

  • forEach
  • count

2).延迟方法

返回值类型仍然是Stream接口自身类型,支持链式调用

  • filter
  • map

将流中元素映射到另一个流中,用到Function接口

  • limit

对流进行截取,只取前n个

  • skip
  • concat

将两个流合并成一个流

三、方法引用

1.格式

已知对象::已知方法

2.代码

/*
lambda表达式
*/
public static void main(String[] args){
    printString((s)->System.out.println(s));
    
    //方法引用
    printString(System.out::println);
}

3.构造方法引用

buildByName("张三",Person::new);

4.创建数组引用

creatArray(10,int[]::new);

四、类加载

当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过

1. 类的加载

  • class文件读入内存,并为之创建一个java.lang.Class对象
  • 任何类使用时,系统都会为之创建一个java.lang.Class对象

2. 类的连接

  • 验证阶段:检验被加载的类是否具有正确的内部结构,并和其他类协调一致
  • 准备阶段:为类的类变量(静态变量)分配内存,并设置默认初始化值
  • 解析阶段:将类的二进制数据中的符号引用替换为直接引用

3. 类的初始化

  • 如类还未被加载和连接,则程序先加载并连接类

  • 如该类直接父类还未被初始化,则先初始化其直接父类

    Tips:

    JVM总是优先初始化java.lang.Object

  • 如类中有初始化语句,则系统依次执行这些初始化语句

1). 类初始化的时机

  • 创建类的实例
  • 调用类的类方法(静态方法)
  • 访问类或者接口的类变量(静态变量),或者为该类变量赋值
  • 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
  • 初始化某个类的子类
  • 直接使用java.exe命令来运行某个主类

以上三个步骤完成类的初始化操作,不出意外情况,JVM连续完成这三个步骤,所以有事把这三步骤统称为类加载类初始化

4. 类的加载器

1).作用

  • 负责将.class文件加载到内存中,并为之生成对应的java.lang.Class对象

2). 类加载机制

  • 全盘负责

    当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也由该类加载器负责载入,除非显式使用另外一个类加载器载入

  • 父类委托

    当一个类加载器负责加载某个Class时,先让其父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类

  • 缓存机制

    保证所有加载过的Class都会被缓存,当程序需要使用某个Class对象时,优先从缓存中搜索该Class,如果不存在,则读取该类对应二进制数据,并转换成Class对象,存储到缓存区

3). 加载器类

java.lang.ClassLoader

JRE具有如下内置类加载器

  • Bootstrap class loader

    JVM内置的类加载器,通常表示为null,且没有父null,是平台类加载器的父类,是所有类加载器的祖先

  • Platform class loader

    平台类加载器可看到所有平台类,包括由平台类加载器或其祖先定义的JavaSE平台的API,其实现类和JDK特定的运行时类,是系统类加载器的父类

  • System class loader

    系统类加载器,也称为应用程序类加载器,与平台类加载器不同,其通常用于定义应用程序类路径,模块路径和JDK特定工具上的类

4). 常用方法

  • static ClassLoader getSystemClassLoader()

    返回用于委托的系统类加载器

  • ClassLoader getParent()

    返回父类加载器进行委托

五、 反射

1. 意义

将包含成员变量、成员方法、构造方法的所有class类向上抽象一层,定义了 Class ,可以理解为程序中所有任何class文件都是Class类的一个实例,通过Class类来访问类的成员变量等称为反射

2. 反射机制概述

运行时获取一个类的变量和方法信息,然后通过获取到的信息来创建对象、调用方法的一种机制,使得程序不需要在编译期完成确定,在运行期仍可扩展,增强了程序的灵活性

3. 使用

1). 获取Class类的对象

a. 使用类的class属性

Student.class将会获得Student类Class对象

Class<Student> class = Student.class;

b. getClass()方法

调用对象的getClass()方法,将会获得所属类对应的Class对象

Student student = new Student();
Class<? extends Student> class = student.getClass(); 

c. Class类中的静态方法forName(String className)方法

参数是某个类的全路径

Class<?> class = Class.forName("com.reflection.Student");

2).常用方法

A. 反射获取构造方法及创建实例

a. getConstructors()

返回一个包含Constructors对象的数组,反映了由该Class对象对应的类的所有公共构造方法

b. getConstructors(Class<?>... parameterTypes)

返回一个Constructor对象,反映该Class对象对应的类的指定参数类型字节码文件的构造方法

Constructor<?> constructor = class.getConstructor(String.class);
c. getDeclaredConstructors()

返回该Class对象对应的类声明的所有构造方法

d. getDeclaredConstructor(Class<?>... parameterTypes)

返回一个Constructor对象,反映该Class对象对应的类的指定参数类型字节码文件的构造方法

e. T newInstance(Object...initargs)

Constructor对象的创建实例的构造方法,返回Object实例,根据指定的构造方法创建对象,若构造方法参数为空,则该方法参数为空

B. 反射获取成员变量并使用

a. Field[] getFields()

返回所有公共成员变量对象的数组

b. Field getField(String name)

根据指定成员变量的名称,返回单个公共成员变量对象

c. Field[] getDeclaredFields()

返回所有成员变量对象数组

d. Field getDeclaredField(String name)

根据指定成员变量的名称,返回单个成员标量对象

e. void set(Object object,Object value)

给object对象的成员变量赋值为value

C. 反射获取成员方法并使用

a. Method[] getMethods()

返回公共成员方法对象数组,包括继承的

b. Method getMethod(String name,Class<?> ...parameterTypes)

根据方法名称,参数类型的字节码文件,返回单个公共成员方法对象

c. Method[] getDeclaredMethods()

返回所有成员方法对象的数组,不包括继承的

d. Method getDeclaredMethod(String name,Class<?>...parameterTypes)

根据方法名称,参数类型的字节码文件,返回单个成员方法对象

e. Object invoke(Object object,Object...args)

调用object对象的成员方法,参数是类的成员方法的参数args,返回值是类的成员方法的返回值,可用Object类型

4. 特点

  • 反射可以越过泛型检查

    ArrayList 的add()方法可以添加整数,因为getMethod的add方法参数可以是Object类型

  • 反射可以访问类中的私有成员变量
  • 反射可以通过配置文件实施指定内容

六、模块化

1. 标识

JDK1.9之后,IDEA中的module-info.java文件中可以进行模块化定义

2. 意义

项目下分为模块,模块下分为包,包下分java文件,为应对项目过于庞大,引入模块化

3.使用

  • 模块导出用exports 包名
  • 模块依赖用requires 模块名

七、Junit单元测试

1. 概述

属于白盒测试的一种

2. 使用

  1. 定义一个测试类(测试用例)
  • 测试类名:被测试的类名+Test CalculatorTest
  • 测试包名:xxx.xxx.test cn.itcast.test
  1. 定义测试方法:可独立运行
  • 使用@Test注解 import org.junit.Test

建议:

  • 方法名:test测试的方法名 testAdd()
  • 返回值:void
  • 参数列表:空参
  1. 判定结果: 普通判定只能判断常识上的错误,无法判断逻辑上的错误,因此需要断言assert

3.代码

@Test
public void testAdd(){
    Calculator cal = new Calculator();
    
    int result = cal.add(1,2);
    
    //assertEquals(expected,actual);
    Assert.assertEquals(3,result);
}

4. 常用注解

1). 初始化注解

@Before

用于资源申请,所有测试方法在执行前都会执行该方法

2). 释放资源注解

@After

在所有测试方法执行完后,都会自动执行该方法

八、注解

1.主要功能

1).编译检查

@Override

2).javadoc文档生成

@author

3).代码分析

通过代码里的注解标识对代码使用反射进行分析

2. 常用注解

1). 内置注解

  1. @Override

  2. @Deprecated

    已过时

  3. @SuppressWarnings("all")

    压制所有警告

2). 自定义注解

a. 格式

public @interface 注解名称(){
    属性列表;
}

b. 本质

本质上就是一个接口,该接口默认继承Annotation接口

public interface MyAnnotation extends java.lang.annotation.Annotation{}

Tips:

javap xxx.class命令可进行反编译操作

c. 注意

  • 注解里可以定义基本数据类型、String、enum、注解类型返回值的方法

  • 注解里不可以定义void返回值的抽象方法

  • 注解里的方法也称为属性,因为使用时需要给注解赋值,也可在注解里String name() default "张三"形式赋默认值

  • 如果只有一个属性需要赋值,且属性的名称是value,则赋值时value可以省略

  • Method.isAnnotationPresent(注解类.class)方法可以判断当前方法上是否有注解

3). 元注解

注解的注解

  • @Target:描述注解能够作用的位置
    • ElementType取值:
      • TYPE:可以作用于类上
      • METHOD:可以作用于方法上
      • FIELD:可以作用于成员变量上
  • @Retention:描述注解被保留的阶段
    • RetentionPolicy取值:
      • SOURCE:当前被描述的注解,只保留到源码阶段
      • CLASS:当前被描述的注解,会保留到class字节码文件中
      • RUNTIME:最常用,当前被描述的注解,会保留到class字节码文件中,并被JVM读取到
  • @Documented:描述注解是否被抽取到API文档中
  • @Inherited:描述注解是否被子类继承

3. 解析注解

1). 步骤

  1. 获取注解位置的字节码文件对象
  2. 获取字节码文件对象的注解对象
  3. 调用注解对象中的定义的抽象方法(属性),来获取返回值
  4. 加载该类进入内存,创建对象
  5. 获取方法对象
  6. 执行方法

2). 代码



/*
还没接触框架,看下来的意义就是通过反射可以让类在使用时才加载进内存,注解的作用可以更方便地进行配置操作
*/

@Pro(className = "cn.itcast.annotation.Demo1",methodName = "show")
public class ReflectTest{
    public static void main(String[] args) throws Exception{
        Class<ReflectTest> reflectTestClass = ReflectTest.class;
        
        //就是在内存中生成了一个该注解接口的子类实现对象,属性(也即复写了抽象方法)返回需要的数据
        Pro pro = reflectTestClass.getAnnotation(Pro.class);
    
        String className = pro.className();
        String methodName = pro.methodName();
        
        Class class = Class.forName(className);
        
        Object object = class.newInstance();
        
        Method method = class.getMethod(methodName);
        
        method.invoke(object);
        
        
}