甜筒是我的罪过啊

今天吃完饭,去“苦沙火乡”买了一支甜筒。边骑车边吃,哪曾想一下路肩甜筒就被颠断了…… 哎,太可悲了太可恨了。 怨我,这就是我的罪孽啊。

July 21, 2026

异常

异常简介 异常是为了在出错的时候,提供一个机制把控制权交给能够处理这个错误的代码手上。如果没有异常,异常产生处和处理处的代码可能很远。 异常的层次结构: 基类:Throwable 直接继承Throwable:Error和Exception Error被设计成虚拟机内部错误,程序员不应该抛出 Exception是我们平常关注的范围。 IOException和RuntimeException是Exception下的两个常见的直接子类。Exception还有更多直接子类。 所有派生于Error类或RuntimeException类的异常都是非检查型unchecked异常。 所有其他异常都是检查型checked异常。 语义上,RuntimeException指的是编程错误导致的异常,如 ArrayIndexOutOfBoundsException NullPointerException ClassCastException 创建异常类 我们需要继承一个Exception的子类。同时惯例我们应该实现两个构造器:一个无参和一个带详细信息字符串的构造器。 异常的捕获和传播 要捕获异常,需要建立一个try-catch块。 如果在try块中遇到catch指定的异常,那么直接跳到对应catch语句块。 预估遇到catch未指定的异常,那么方法直接退出。 如果try块正常运行,那么不运行catch块,继续运行方法的后续代码。 如何判断处理还是传递异常? 在工程实践中,推荐遵循**尽早抛出,延迟捕获(Throw early, catch late)**的原则: 在代码最底层的逻辑验证阶段,一旦检测到异常状态立即抛出;在整个调用链路中,除非能够真正处理问题或必须转译异常,否则应一路放行,交由拥有全局业务视野的顶层框架或统一异常处理器(如 Spring 的 @ExceptionHandler)进行集中处理。 finnally和try-with-resources finally子句:无论是否排除是否被捕获的异常,都会在最后执行。 可以只有finally没有catch 不要在finally中使用throw, return, break, continue等改变控制流的语句。 一个良好的实践是使用两个独立的try,try-finally用于关闭资源,try-catch用于处理异常。 java7以上支持try-with-resources 对于实现了AutoClosable接口: interface AutoClosable { void close() throws Exception } 的类(资源),try-with-resources语法会在try块结束后自动关闭这些资源。 try (Resource res = ...) { do with res..... } // 不管有没有异常,是否被捕获,都会在这里调用res.close() catch (Exception e) { //... } java9之后,可以提供effectively final的变量了: // out 是方法的参数,满足effectively final try (out) { // do sth } // out.close 没什么人在意的细节:在try-with-resources中,如果try块和close都抛出了异常,close抛出的异常会被抑制。它们被用addSuppressed方法加到try抛的异常上,然后重新抛出try抛出的异常。如果你感兴趣,可以使用getSuppressed方法获取这些异常。 ...

July 18, 2026

Lambda表达式和函数式接口

Lambda表达式和函数式接口 Lambda表达式语法的本质上就是一种表达式,而非对象。 Lambda表达式的返回值会被自动推导。如果参数被省略,那么也会尝试自动推导。 函数式接口(Functional Interface)指的是只有一个抽象方法的接口。(可以有一些非抽象方法,如重写或默认) Lambda表达式是被设计为快速地转换为函数式接口的实例。程序员不用手动初始化函数式接口的实例了,这些对象和类的管理由编译器处理。 方法引用和构造器引用 方法引用(Method Reference)是一种简便方式,可以在只想调用一个方法而不做其他操作时,直接使用某个方法的逻辑。 原理类似于Lambda表达式,编译器自动为你创建函数式接口的实例。 例如: var timer = new Timer(1000, System.out::println) 要注意方法重载的选择受函数式接口的签名影响。 方法引用支持三种语法: object::instanceMethod:lambda表达式的参数列表原样传送到这个对象的这个方法的参数中。 Class::instanceMethod:lambda表达式的第一个参数成为方法的隐式参数,也就是说:String::compareToIgnoreCase, 等价于(a, b) -> a.compareToIgnoreCase(b) Class::staticMethod: 所有参数传递到静态方法。 构造器引用 格式:Class::new 解决的问题:将类型被擦除的数组重新构建为特定对象数组。 例如:Person[] people = stream.toArray(Person::new) 变量作用域和闭包 Lambda表达式由三部分组成: 一个代码块 参数 自由变量的值,这里是指非参数切不在代码块内被定义的变量 当lambda体使用了外部的变量时,称为这个变量被捕获(captured) 捕获capture自由变量的代码块称为闭包closure。lambda表达式就是闭包。 捕获变量的限制(这是为了并发安全考虑): 在lambda体中,不可以改变捕获的变量。 捕获的变量不可能在外部改变 捕获的变量必须是事实最终变量(efectively final)。aka,变量初始化后不会再被赋新值。 lambda表达式的体与它的上一层嵌套块有相同的作用域。也就是说,lambda表达式不新建作用域,参数和内部变量不能和上一层重名。 这也是为什么你可以直接使用外层方法的this Lambda表达式的应用 《Core Java 中文版》6.2.7 为什么要使用Lambda表达式?一个重要原因是支持代码的延迟执行(deferred execution) 换句话说,如果不需要延迟执行,那么很可能不需要lambda表达式。 在一个单独的线程运行代码 在算法的适当位置运行代码(比如排序的比较操作) 发生某种事件时才触发代码 只在必要时才执行代码 常用的函数式接口: Runnable Supplier<T> Comsumer<T> BiConsumer<T, U> Function<T,R> BiFunction<T, U, R> UnaryOperator<T> BynaryOperator<T> Predicate<T> BiPredicator<T, U> 除了Runnable,它们都在java.util.function里面 ...

July 17, 2026

Hello World

这是我的第一篇博客!

July 16, 2026