《On Java基础卷》阅读笔记(五)

第11章 内部类

内部类就是把类定义在一个包围它的类里面。

内部类有静态内部类和普通内部类,这两者在外部使用时的方式也不一样。但两者的相同点都是可以使用包围类下的所有方法和属性;

public class InnerTest {

    private Integer ageOuter;
    private static Integer staticAgeOuter;

    public void changeAge(Integer newAge){
       this.ageOuter = newAge;
    }

    public static void changeAgeStatic(Integer newAge){
        staticAgeOuter = newAge;
    }

    private Integer getAgeOuter(){
        return ageOuter;
    }
    private Integer getStaticAgeOuterAgeOuter(){
        return staticAgeOuter;
    }

    public class  InnerA{

        private String name;

        public void setName(String newName){
            this.name = newName;
        }

        public Integer getNewAge(Integer age){
            changeAge(age);
            return InnerTest.this.ageOuter;
        }
    }

    public static class InnerB{

        private String name;

        public void setName(String newName){
            this.name = newName;
        }

        public Integer getNewAge(Integer age){
            InnerTest test = new InnerTest();
            test.changeAge(age);

            // changeAge(age); 不能直接访问非静态方法
            changeAgeStatic(age);

            // return staticAgeOuter;   可直接返回静态属性
            return test.getStaticAgeOuterAgeOuter();
        }
    }

    public static void main(String[] args) {
        InnerTest test = new InnerTest();

        InnerA innerA = test.new InnerA();
        //   innerA = new InnerA();  非静态内部类不支持直接new
        InnerB innerB = new InnerB();  // 静态内部类可直接new
        innerB = new  InnerTest.InnerB ();    // 外部使用需要加上外部类名
        System.out.println(innerB.getNewAge(25));

        InnerTest.InnerA innerA1 = new InnerTest().new InnerA();

    }
}

内部类的使用:

  • 向上转型:需要向上转型获取基类时,可用内部类实现接口或继承基类。同时在外部包围类下定义获取基类的方法,返回的对象引用实际上是内部类。
  • 在方法和作用域中的内部类:
    • 原因1:要实现某个接口,用于创建和返回一个引用;
    • 原因2:在解决方案中创建了一个不用公开的类来辅助;
public class Test{
    
    public Destination destination(String s){
        final class DestinationImpl implements Destination{
            // xxx 
            void f(){}                   
        }
        
        Destination des =  new DestinationImpl();
        des.xxxx;
       // xxx
       return des;       
    }
    public static void main(String[] args){
        Test t = new Test();
        t.destination("hello").f() ;   
    }
}

匿名内部类:

创建一个继承XX的匿名内部类,下面例子就是创建了一个继承Contents的匿名内部类。这个被继承的类可以是普通类,也可以是个抽象类。

public class Test{
    public Contents contents(){
        return new Contents(){
            // xxx        
        }    
    }
}

嵌套类:

  • 被static修饰的内部类可看作是个嵌套类,它只能访问包围类static属性和方法。
  • 接口中的任何类都会自动成为public和static的,但是并不实际需要static字段出现修饰。
  • 多层嵌套并不影响内部类访问外部类的信息。

为什么需要内部类:每个内部类都可以独立地继承自一个实现,因此外部类是否已经继承了某个实现,对内部类并没有限制。

内部类实际解决了多重实现继承:

  • 内部类可以有多个实例,每个实例都有自己的状态信息,独立于外围类对象的信息;
  • 一个外围类中可以有多个内部类,他们可以以不同方式实现同一接口,护着继承同一个类。
  • 内部类对象的创建时机不与外围类对象的创建捆绑到一起
  • 内部类不存在肯能引起混淆的is-a关系,它是独立的实体。

第12章 集合

一组对象聚集在一起,能被一个对象说明,那么这就是集合。

借助泛型,我们可以知道要创建什么类型的对象集合,那么不是这个类型的对象,就无法放入这个集合中,在编译期间就不会通过。

基本概念

  • Collection,一个由单独元素组成的序列,并且这些元素要符合一条或多条规则。
  • Map,一组键值对,使用键来查找值。

添加一组元素

Arrays.asList方法可以将一个数组转成一个序列集合,该集合的底层数组还是原数组。

        String[] students = new String[]{"Tom", "Jerry", "Forest", };
        List<String> studentList = Arrays.asList(students);

Collection.addAll接收另一个Collection集合;

        String[] students = new String[]{"Tom", "Jerry", "Forest", };
        List<String> studentList = Arrays.asList(students);

        List<String> emails = new ArrayList<>();
        emails.add("1");
        emails.add("2");
        emails.add("3");
        emails.add("33");

        emails.addAll(studentList);

Collections.addAll,同样也是接收另一个集合。

List

列表,常用有ArrayList、LinkedList。

ArrayList,擅长随机访问,在集合中间插入或删除元素费时;

LinkedList,在集合中间插入或删除元素很快,不适合查找返回指定位置的元素。

两者的区别是因为底层实现方式不一样,ArrayList是数组对象,LinkedList是链表对象。

Iterator

迭代器,所有实现Collection接口的类,都同样实现了Iterator接口。

public interface Collection<E> extends Iterable<E> {
    
}

结果就是这些这些集合在循环的时候,本质都是通过迭代器里的方法在进行数据迭代获取展示;

Stack

栈,先进后出,Java1时实现很糟糕,在Java6的时候推出了ArrayDeque完善了栈的功能的实现

Set

不允许集合里有重复值,常用的有HashSet,无序的;TreeSet,有序的。

Map

根据一个键找到一个值,常用HashMap

Java16推出了record关键字,会使得编译器自动生成部分代码:构造器、equals、hashcode、

Queue

队列,先进先出。常用的LinkedBlockingQueue

在并发编程中可以安全转移对象。

PriorityQueue,优先级队列,优先级高的先出。

队列和栈的行为都是通过LinkedList提供的。

for-in 和迭代器

有序集合都能使用for-in语法,和迭代器方法有关。

第13章 函数式编程

函数式编程,通过整合现有代码来产生新的功能,而不是从零开始编写所有内容。

面向对象编程抽象数据,而函数式编程抽象行为。

interface Strategy {
    String approach(String msg);
}


static class Unrelated {
    static String twice(String msg) {
        return msg + " " + msg;
    }
}


public  class Test{
    public static void main(String[] args){
        Strategy[] strategies = {new Strategy() {
            @Override
            public String approach(String msg) {
                return msg.toUpperCase() +"!";
            }
        },
        msg1 -> msg1.substring(0, 5),
        Unrelated::twice
        };
                
    }
}

上述案例代码中, 我们定义了数组,数组里的元素用了三种方式,这三种方式有一个共同的特点就是返回值类型一样。

Java8中的方法引用用两个冒号表示,::,::左边是类名或对象名,右边是方法名,没有参数列表。

Lambda表达式

lambda表达式产生的是函数,而不是类。

基本语法如下:

  1. 参数;
  2. 后面根 ->,可以理解为产生;
  3. 再后面根的都是方法体
  • h -> h+ "";  一个参数
  • () -> "", 无参数时要单写括号;
  • 多个参数要在括号里逗号分隔。

lambda在递归的处理

下面的代码表达的含义是数字n的阶乘。

在for循环之前的第9行代码,定义函数公式。

interface IntCall{
    int call(int arg);
}

public class Test{
    IntCall fact;
    
    public static void main(String[] args){
        fact = n -> n==0 ?  1 : n * fact.call(n-1);
        for(int i=0;i<=10;i++){
            fact.call(i);        
        }     
    }
}

方法引用:

类名::方法名,是个方法引用,指向类中的方法。

方法签名指的的方法的参数顺序和类型以及返回值,签名一致的方法是可以 用 等号练起来,表示将方法A映射到了方法B上。

interface A{
    void aa(String s);
}
class B{
    void bb(String s){
    //            
    }
}

public class Test{
    static void cc(String s){
        //    
    }
    public static void main(String[] args){
        B b = new B();
        A aa = b::bb;  
        aa = Test::cc;  
    }
}

Java中的Runnable类,有个run方法,返回类型是void,那么就可以自定义一个无参且返回类型是void的方法,在代码中来替代Runnable类。

public class List2Str {
    public static void main(String[] args) {

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(777);
            }
        }).start();
        new Thread(()->{System.out.println(888);}).start();
        new Thread(List2Str::HHHH).start();

        List2Str list2Str = new List2Str();
        new Thread(list2Str::GGG).start();
    }


    public static void HHHH(){

        System.out.println(666);
    }

    public void GGG(){

        System.out.println(666);
    }

}

未绑定方法引用(非静态),需要先提供对象,才能使用。

构造器方法引用

class Dog{
    Dog(){}
}

interface MakeNoArgs{
    Dog make();
}

public static void main(String[] args){
    MakeNoArgs no1 = Dog::new;
    
    Dog dog = no1.make();
}

函数式接口

lambda表达式里包含了类型推断,所以对于传统的通过 + 生成字符串时,要确保参数类型包含字符串,否则数字类型相加会出错。

java.util.Function

@FunctionalInterface,要求接口上有这个注解的接口,里面只能有一个方法。

这个方法,有参数/无参数、有返回值/无返回值。例如Runnable接口和Callable接口

高阶函数

高阶函数只是一个能接受函数作为参数或能把函数当返回值的函数。

闭包

指声明在一个函数中的函数,所以lambda表达式就是闭包。

内部类经常用做闭包。

IntSupplier makeFun(int x){
    int i=0;
    return new IntSupplier(){
        public int getAsInt(){
            return  x+i;       
        }    
    }
}

// 对内部类可移除其实现的接口和方法名,简写为
IntSupplier makeFun(int x){
    int i=0;
    return () -> {
            return  x+i;       
        }    
    }
}

第14章 流

按照操作类型,可将流分为创建、修改、消费流元素 三种,也代表前置、中间、终结三种操作。

流的创建

1.使用Stream.of()方式将一组条目变成一个流。

2.每一个Collection都可以使用stream方法来生成一个流。

随机数流:Random类已经被增强,有了一组可以生成流的方法。

new Random().ints(10,20).boxed(); // 有上下界的随机流

int类型的区间范围:IntStream.range(10,20)

Stream.generate(),创建一个由完全相同的对象组成的流

Stream.iterate(),从第一个参数开始,然后将其穿给第二个参数所有引用的方法。

Stream提供了流生成器builder()

Arrays类中包含了名为stream()的静态方法,可以将数组转换为流。

正则表达式加入了splitAsStream方法

流的中间操作

跟踪与调试:peek()操作就是用来辅助调试的,它允许我们查看流对象而不修改它们;

对流元素进行排序:sorted()方法参数里加入Comparator函数方法进行排序。

移除元素:distinct()移除了流中的重复元素;filter(Predicate)过滤操作只保留符合特定条件的元素,也就是传了过滤参数后,结果返回true的元素。

将函数应用于每个流元素:map(Function):将Function应用于输入流中的每个对象,结果作为输出流继续传递,还有mapToInt、mapToLong、mapToDouble;、

在应用map()期间组合流:flatMap()做两件事:接受生成流的函数,并将其应用于传入元素;然后再将每个流扁平化处理,将其展开为元素。

Optional类型

为了避免流返回结果时有异常,使用Optional类型对返回结果进行了包装。

先检查后处理:ifPresent(Consumer),如果值存在,则用这个值来调用Consumer,否则什么都不做。

orElse(otherObject),如果对象存在,则返回这个对象,否则返回otherObject

orElseGet(Supplier),如果对象存在,则返回这个对象,否则返回使用Supplier函数创建的替代对象。

orElseThrow(Supplier),如果对象存在,则返回这个对象,否则抛出一个使用Supplier函数创建的异常。

创建Optional:empty,返回一个空的Optional

of(value):如果已经知道这个value不是null,可以使用该方法将其抱在一个Optional中

ofNullable(value):如果不知道这个value是不是null,使用这个方法。

Optional对象上操作:filter(Predicate)、map(Function)、 flatMap(Function)

流的终结操作

将流转换为一个数组:toArray(),将流元素转换到适当类型的数组中。

在每个流元素上应用某个终结操作:forEach(Consumer)

收集操作:collect(Collector),使用这个Collector将流元素累加到一个结果集合中。

collect(Surplier, BiConsumer, BiConsumer)和上面类型,但是Supplier会创建一个新的结果集合,第一个BiConsumer是用来将下一个元素包含到结果中的函数,第二个BIConsumer用于将两个值组合起来。

组合所有的元素:reduce(BinaryOperator),使用BinaryOperator来组合所有的流元素。reduce(identity, BinaryOperator)

匹配:allMatch(Predicate)、anyMatch(Predicate)、 noneMatch(Predicate)

选择一个元素: findFirst()、 findAny()

获得流相关信息:count() 、max(Comparator) 、min(Comparator)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值