JAVA编程

Java语言不使用指针,而是引用。

Java程序

编写

代码结构

一个*.java文件可以包含多个类的定义:

  • 只能有一个public类/接口,源文件名与public类名相同;
打包声明

包用于描述代码的逻辑组织结构(类似于C#的命名空间):

  • 把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用

  • 并防止命名冲突。

如果一个public类要包含在某个包内,需要在代码的第一行使用package关键字进行声明。

package org.develop.app;

在指定的源代码根目录下,层级的打包声明也对应源代码(编译输出字节码)文件树目录结构

不同类型项目的源文件根目录设置可能不同,例如Maven项目的源代码根目录src/main/java,则上述源代码对应的路径为src\main\java\org\develop\App.java

导入包声明

为了使用外部代码(例如Java库),源文件文件中可以导入(import)外部依赖包的内容。

导入语句位于打包语句之后。

import java.io.*;		// import all
import java.io.File;	// import single class

包具有层级嵌套结构,导入声明仅能引用当前层级包中的类,而不能引用下一层级的包中的类(需要单独声明)。

模块系统(Java 9)

同一个jar文件中的.class文件并没有依赖关系限制;模块则附加依赖关系,还可包含二进制代码。

oop-module
├── bin                           # 存放编译后的class文件
├── build.sh
└── src                           # 源码:按包名的目录结构存放
    ├── com
    │   └── hello
    │       └── world
    │           ├── Greeting.java
    │           └── Main.java
    └── module-info.java          # 模块的描述文件,包含依赖声明

在Java代码中仅能引用模块描述文件中声明的依赖库。

module hello.world {
   requires java.base;       // 可不写,任何模块都会自动引入java.base
   requires java.xml;
   exports com.hello.world;  // 只有声明导出的包,外部代码才能问。(进一步隔离了代码的访问权限)
}

java --list-modules显示JDK自带模块。 java -d,describe-module module_name显示模块的描述信息。

执行Java程序

编译

Java程序被编译为字节码格式(*.class文件)。在运行时,Java平台中的Java解释器对这些字节码进行解释执行,执行过程中需要的类在联接阶段被载入到运行环境中。

运行

Java应用程序是由若干类定义组成的独立的解释型程序,其中必须有一个包含Main方法的主类;执行Java应用程序时,需要使用Java解释器(java.exe)来执行这个主类的字节码文件(.class)。

java App.java                                  # 直接编译执行源文件
java [options] -jar package-name.jar [args...] # 执行jar包中包含Main方法的class文件
java [options] -cp <classpath> package.ClassName [args...] # 指定搜索路径下的class

package.ClassName:程序执行时应该执行入口函数所在的类,解释器在搜索路径下寻找指定类。Java应用程序的入口函数为类中的名为Main静态方法。使用-jar选项执行时,jar包中需要在manifest中声明主类。

--dry-run:创建VM并加载主类,但不执行主方法。用于验证命令行参数是否有效。

Java小程序(Java Applet)的源代码编辑与字节码编译生成过程与Java应用程序相同,但它不是一类可独立运行的程序。Applet程序的字节码文件必须嵌入到HTML文件中并由负责解释HTML文件的浏览器充当其解释器。将Java Applet引入HTML中,使得网页能够提供动态信息。Java小程序与Java应用程序最大不同在于Applet不需要Main方法,而要求程序中必须有且只有一个类是Applet类的子类。系统类Applet类中已经定义了许多成员,它们规定了Applet如何与执行它的解释器——浏览器配合工作。

Java环境变量

-D<name>=<value>  # 系统属性(system.Properties),传递应用程序
-X                # 输出所有`-X`(extra)虚拟机选项的名称;
Java虚拟机选项

-Xms:Java虚拟机的初始堆内存分配量,例如-Xms256m;单位包括:k,m,g,...

-Xmx:JVM最大堆内存分配量,例如-Xmx2048m,通常默认值为256m;如果超过最大内存分配限制,程序将触发java.lang.OutOfMemoryError异常。

Java系统属性

路径

当前路径

执行Java程序时所在的工作目录。

类搜索路径

Java解释器需要在搜索路径CLASSPATH中寻找.class文件进行执行。==类文件(*.class)可能位于文件夹、jar文件或zip文件==,使用以下选项添加搜索路径。

-{cp,class-path,classpath} libpath1:libpath2/*:lib3.jar:...

特别地,==如果文件夹以/*结尾,那么该文件夹下的所有jar/zip文件都会被加入搜索路径==。

JVM自带的标准库rt.jar不要写到CLASSPATH中,写了反而会干扰JVM的正常运行。*不能视为通配符,因此*.jar的写法将无效且造成错误。

连接动态库

如果程序依赖共享库(JNI),而不能定位,则会引发java.lang.UnsatisfiedLinkError

使用-Djava.library.path=/your/lib/path选项指定。

另一种方法是在执行程序之前(或加入/etc/profit)设置环境变量 LD_LIBRARY_PATH

不建议修改共享库搜索路径。如果使用的共享库在系统中存在其他版本,可能影响程序使用的共享库版本,使程序出错(ImportError: tensorflow/python/_pywrap_tensorflow_internal.so: undefined symbol)。

export LD_LIBRARY_PATH=:~/Workspace/lib/java/tensorflow/
模块搜索路径
-p,--module-path <module path> path1:path2:...

语法

标识符大小写敏感。命名规则:

  • 类名:单词首字母大写;
  • 方法名:首字母小写,其余单词首字母大写;

注释和文档

注释:

  • 使用///*...*/添加单行注释;
  • 使用/*...*/添加多行注释。

文档化注释

/**
* <h1>Find average of three numbers!</h1> 
* <p>The FindAvg program implements an application that 
* simply calculates average of three integers and Prints 
* the output on the screen. </p>
*
* @param parameter_name description 	[Parameters” section]
* @return description					[“Returns” section]
* @exception ExceptionClass				[Throws subheading]
* @author Author Name
* @version version						[“Version” subheading]
* @since release-date					[“Since” subheading]
* @see reference 						[See Also” heading]
* @deprecated Indicating Deprecated components
* {@link package.class#member label}
* {@code text}
*/

使用javadoc生成格式化文档。

javadoc Program.java

https://www.geeksforgeeks.org/comments-in-java/

https://www.oracle.com/technetwork/java/javase/documentation/index-137868.html

标注(Annotation)

标注以@开始,用于辅助程序元素与元信息的关联,并控制编译器的行为,但对编译后的程序不起作用。

@MarkerAnnotation
@SingleValueAnnotation("info")
@FullAnnotation(key1="value1",key2="value2")

预定义标注:

import java.lang.annotations.*;
@Deprecated		// indicate an oboslated declaration
@Override		// must override a method from superclass
@SuppressWarnings({"checked", "deprecation"}) // 参数可为空
@FunctionalInterface
@SafeVarargs   // 标注的方法不会对可变参数执行不安全操作

标注可用于类、方法、语句等的开头(上一行),for语句等的条件语句的开头。

自定义标注,类似于定义接口(使用@interface关键字),自定义标注本身还可添加标注信息(meta annotations):

@Documented                         // 添加所定义标注的元素信息文档化(javadoc etc.)
@Target(ElementType.METHOD)         // 标注的目标TYPE, METHOD, FIELD...
@Inherited                          // 子类是否继承父类元素的该标注
@Retention(RetentionPolicy.RUNTIME) // === SOURCE,CLASS, RUNTIME
@Repeatable                         // 该标注是否可重复声明
public @interface MethodInfo{
   String author() default "gary";  // 方法不能包含参数
   int revision() default 1;        // 方法返回值仅限基本类型
}

Java Annotations - JournalDev

https://www.geeksforgeeks.org/annotations-in-java/

变量声明

变量在使用前必须声明:

type var_name[ = value, var_name2[ = value]...];

变量根据作用域可分为:类变量(静态字段)、实列变量(字段)和局部变量。

局部变量是在栈上分配的,在所在作用域(方法、代码块)被执行时创建,执行完即被销毁。

局部变量没有默认值,所以局部变量被声明后,必须经过初始化,才可以使用。

流程控制

条件

if(condition){
  
}else if (condition){
  
}else{
  
}

循环

while
while (condition){
    do_something;
}

赋值语句的返回值可作为条件语句,例如:(line = reader.readLine()) != nullnull本身不能直接作为条件语句)。

do-while
do{
	do_something;
}while(true);
for
for(int i=0; i<len; ++i){
    do_something;
}

增强for循环,结合迭代器。

for(String name : names){
   do_something;
}
Stream API

定义对元素的计算函数,由Stream框架执行内部迭代,从而实现并行计算、过滤、映射等功能特性。

Java 8 Stream - Java Stream - JournalDev

return list.stream().filter(x -> x > 10).mapToInt(x -> x).sum();

switch-case

switch(expr){
    case const_val1:
        statements;
        break;
    case const_val2:
    case const_val3:
        statements;
        break;
    default:
        statements;
}

说明:

  1. 变量类型可以是:byteshortintchar以及string(Java 7);
  2. case语句的值为常量字面值常量

try-catch-finally

用于处理程序异常的控制结构。

try {
	do_something;
} catch (ExceptionTypeA | ExceptionTypeB e) {
	handle_exception;
} finally {
	clean_work;
}

try-with-resource

简化资源关闭与异常处理流程(Java 7)。

try (open_resources){
	do_something;
}
catch(exception e){
	handle_exception;
}

为了能够配合try-with-resource,资源必须实现AutoClosable接口。

运算符

instanceof

检查该对象是否是一个特定类型(类类型或接口类型)

Object instanceof ClassName

函数

Lambda表达式(匿名函数)

使用Lambda表达式:可减少代码量,传递函数作为参数,在使用时定义函数。

(args)->{statements} // 参数和方法内容均可省略,单语句可省略"{}"

无返回值的方法体以最后语句的返回值作为方法的返回值。

参数类型可==同时==省略(自动推断)。

方法引用(Java 8)

面向对象的编程

Java中所有内容均包含在类的定义中。

类与对象

[public] [modifier] class ClassName [extends BaseClass][implements Interfaces,...]{
   [protected] [modifier] int PropertyName [= value];
   [public] ClassName(int param,...) throw exceptions{
      super(param,...)
         do_initialization;
   }
   [private] [modifer] void methodName(int param,...)throw exceptions{
      statements;
      this.another_method(param,...);
      super.base_method(param,...);
   }
   public int static main(String[] args){
      ClassName c = new ClassName(...);
   }
   protected class InternalClass{
      class_definitions;
   }
}

使用new关键字创建对象。创建对象时将调用类的构造方法

注意:类的实例都是通过引用来使用的,使用new创建的对象都会在内存中开辟独立的空间,两个对象即使内容相同,但是在内存中的地址不同,所以如果使用==进行比较,会得到false;但是使用类的方法equals比较两个实例的内容。使用对象相互赋值的方法,则只是复制了对象的引用,并没有开辟新空间,所以两者指向同样的区域。==对于字符串来说,如果直接将带引号的字符串赋值给两个字符串对象,则由于Java处理字符串的特性,两个对象将指向内存中的同一位置(内存中的特殊区域String Pool),使用======也会返回true==。

类的成员包括方法和字段。在类内部可直接通过名称访问成员。当成员名称与参数名或局部变量重名时,使用this关键字访问成员。this还可用于在构造方法内部引用该类的其他构造方法(只能出现在构造方法内部的第一行,即只能调用一次)。

修饰符

访问控制
  • public:这个类可以被其他所有类访问和引用,只能被定义在同名文件中。

  • default:即没有访问控制符修饰,这样的类只能被同一个包内的类引用,而对其他包内的类不可见。

  • protected

  • private:只用于内部类。

其他
  • abstract:抽象类,不能直接被实例化的类,但是可以声明对象的引用,只是声明的对象引用默认值都是null,必须使用可实例化的子类的构造函数对其赋值。抽象类内部可以包含任意个抽象方法,也可以包含构造方法、字段和实体方法。
  • final:不能被继承的类。

字段

字段(Fields,属性Attributes)是类内部定义的变量

访问控制修饰符
  • public: 允许任何位置对字段的访问。由于具有默认访问权限的类本身只能被同一包内的成员所访问,所以这种类中的成员即使具有public权限也只能被同一包内的其他类访问。
  • default:同一个包内的位置可对字段进行访问。
  • protected:允许该类及其子类中对该字段进行访问。
  • private:允许在该类内部对该字段进行访问。访问控制是针对类而言的,而并非针对具体实例而言,即同一个类的不同实例可以互相访问它们的私有成员。而通过继承关系生成的子类,其新定义的方法则不能访问父类的私有成员,但继承自父类的成员函数因为本来可以访问父类成员,所以在子类中仍然可以访问父类成员。
其他修饰符
  • static:同一个类所有的实例共享静态字段。

    静态成员不属于对象,所以推荐使用类名来访问静态成员(在类内部可直接使用字段名)。

    ClassName.StaticProperty;
    

    由于静态字段从属于类,而不属于实例,所以不能用构造函数进行初始化。同时由于除了通过在声明字段时对字段赋初值,还可以通过静态代码块对静态字段进行初始化。

    static{ initialize-statements; }
    
  • final:常量字段,通常用大写字母作为常量标识符

  • transient序列化对象时,跳过该类变量。

  • volatile:在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值。而且,当成员变量发生变化时,会强制线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。

方法

方法的访问控制与字段一致。

修饰符
  • abstract:只有函数声明(declaration/signature),而没有函数体。具有抽象方法的类即抽象类。抽象类的类必须重载这些抽象方法才能实例化,否则子类也必须声明为抽象类。

  • final:不能被类的子类重写。

  • static:静态方法,只能访问和修改类的静态成员,通过类名调用。

    ClassName.StaticMethod(args);
    

    名为main的静态方法可作为Java程序的入口函数。

    public static void main(String[] args)   // args not contain program name
    

    主函数无须返回值(return)。由于程序由java解释器执行,因此从主函数返回值并不能被shell接收。java解释器将获取由System.exit(value)返回的值,并返回给shell。

  • synchronized:方法同一时间只能被一个线程访问。

方法重载(Overload)

重载的方法必须拥有不同的参数列表。不能仅依据修饰符或者返回类型的不同来重载方法。

可变参数

一个方法中只能指定一个可变参数,它必须是方法的最后一个参数,在参数类型后加一个省略号(...)。

public static void printMax( double... numbers)

命令行参数解析

picocli

可以通过外部依赖或源代码(CommandLine.java)的方式使用。以下为通过Maven配置依赖项。

<dependency>
  <groupId>info.picocli</groupId>
  <artifactId>picocli</artifactId>
  <version>4.2.0</version>
</dependency>

启用Annotation Processor

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <version>3.5.0</version> <!-- 3.5 or higher -->
  <configuration>
    <annotationProcessorPaths>
      <path>
        <groupId>info.picocli</groupId>
        <artifactId>picocli-codegen</artifactId>
        <version>4.2.0</version>
      </path>
    </annotationProcessorPaths>
    <compilerArgs>
      <arg>-Aproject=${project.groupId}/${project.artifactId}</arg>
    </compilerArgs>
  </configuration>
</plugin>

示例代码:

@Command(name="App", version="v1.0.0", header="Example App.")
class Tar {
    @Option(names="-c", description="create a new archive")
    boolean create;
    @Option(names={"-f","--file"}, paramLabel="ARCHIVE", ...)
    File archive;
    @Parameters(paramLabel="FILES", description = "one ore more files...")
    File[] files;  // all positional arguments
    @Option(names = { "-h", "--help" }, usageHelp = true, ...)
    private boolean helpRequested = false;
    @Parameters(index = "0")   // index="2-4", index="3-*"
    String command = "save";   // first  positional arguments
    public static void main(String args){
        Tar tar = new Tar();
        new CommandLine(tar).parseArgs(args);
        if (tar.helpRequested){
            CommandLine.usage(app, System.out);
            System.exit(0);
        }
    }
}
CommonCLI

Commons CLI https://commons.apache.org/proper/commons-cli/usage.html

picocli - a mighty tiny command line interface

jcommander

How do I parse command line arguments in Java?

构造方法

用于对类的成员变量进行初始化。

构造方法的特点:方法名与类名相同;没有返回值。

构造方法同一般方法一样,可以重载;如果一个类没有声明构造方法,则系统会为类添加一个默认构造方法,其方法体为空,访问权限与类相同;如果自己声明了构造方法,则系统就不再添加默认构造方法。

finalize

finalize()方法在对象被垃圾收集器析构(回收)之前调用,用来清除回收对象,确保一个对象打开的文件被关闭了。

内部类

在某个类内部定义的类称为内部类。一个类如同使用其它类一样使用自己的内部类,包括创建内部类的对象并调用其方法。而内部类拥有对外层类所有字段和方法成员的访问权

内部类的访问权限除了默认权限和public外,还可以是protectedprivate。如果为private,则该内部只能在本类中使用;如果为protected,则外层类、处于同一包中的类及外层类的子类可以访问它。

内部类还可以定义在方法内部,其作用域仅限于该方法内部。内部类可以在使用的地方,声明的同时使用。

继承

所有的类都是继承于java.lang.Object。当一个类没有声明继承,则默认继承Object

Java只能进行单继承。

子类可以从父类那里继承非private成员:即子类可以直接访问父类的非private成员,private成员在子类中不可见,仅能通过父类提供的非private成员作为接口间接访问。

子类不继承父类的构造方法。子类在构造方法中使用super关键字调用父类的构造方法对父类的成员进行初始化:

super(args);  //出现在构造方法内部的第一行

如果子类的构造方法中不调用父类的构造方法,则系统会自动调用父类的默认构造方法(如果父类已经声明了构造方法,则需要显式添加默认构造方法,否则不能实现自动调用)。

类成员的初始化过程:

  1. 分配内存空间,并将字段单元初始化;

  2. 使用字段在声明时赋的值,对字段初始化;

  3. 调用构造函数,在构造函数中,首先对父类进行初始化,再初始化子类字段。

重写/覆盖/隐藏(Override)

在子类中声明的成员与父类的成员完全一致,则父类的成员称为被子类成员隐藏。

一般不推荐对父类的字段进行隐藏,这样会导致类的结构混乱。

进行方法隐藏时,子类声明的方法必须和父类的方法原型一致,而且子类方法的访问限制不能比父类严格(否则就直接访问父类方法了)。

子类不能直接访问到父类的同名方法,通过super关键字访问父类方法。

规则:

  • 如果不能继承一个方法,则不能重写这个方法。
  • 访问权限不能比父类中被重写的方法的访问权限更低。
  • 回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类。
  • 声明为finalstatic的方法不能被重写

抽象类

使用abstract关键字声明的类(可能并不包含抽象方法)。

如果一个类包含未实现方法,则该类必须被声明为抽象类:

  • 继承的接口未实现的类;

  • 包含使用abstract关键字声明的抽象方法的类;

不能被继承的方法,如构造方法,静态方法,不能声明为抽象方法。

接口

接口是包含若干抽象方法和常量的一个集合,提供对某一种功能的抽象,使实现接口的类具有统一的外部访问方式。

public interface 接口名 [extends 父接口名列表]{
    public static final int CONST_VAR = const_value;
    public abstract [native] int methodName(params)[throw exceptions];
}

实现接口:首先在类的声明中使用implements添加要实现的接口名,如类的声明格式所示;如果声明继承某接口的类,则接口的所有方法都必须实现,否则该类为抽象类;类在实现接口的方法时,必须保证方法原型与接口中声明的原型一致,否则就成了方法的重载。

接口的方法的访问限制符都限定为public,所以类在实现方法时,也必须使用public修饰符

与抽象类相似,不能声明不能被继承的方法,如静态方法;

接口不能包含成员变量,除了static final变量。

标记接口

标记接口是没有任何方法和字段的接口。

  • 向一个类添加数据类型:通过引用多态使该类对象可以作为接口类型引用,供其他代码来测试类型。
  • 建立公共的父接口。
函数接口

An interface with exactly one abstract method is called Functional Interface(可使用@FunctionInterface对接口进行标注,以避免在该接口中声明多个方法)。

java.util.function

对象引用多态

对象引用的多态是指:声明对象的类型不由对象的引用类型(类似于指针)决定,而是由创建对象时调用的构造方法决定。虽然对象本身是确定的,但是因为子类对象可以作为父类对象来引用,所以可以实现对象引用的多态。

引用多态的三个必要条件:继承、重写、父类引用子类对象。

示例:

SuperClass sc = new SuperClass();		//父类的引用实际表示父类对象
SuperClass sc1 = new SubClass();			//父类的引用实际表示子类对象

动态绑定

==通过父类/接口引用子类对象的方法时,总是调用子类方法,而非父类同名方法==。区别于C++,C++中只有虚函数才具有此性质,否则使用父类指针将访问父类同名函数。也就是说Java中的成员方法具有C++虚函数的性质。如果 Java 中不希望某个方法具有虚函数特性,可以加上final关键字变成非虚函数。

判断一个引用究竟指向那种类的对象可以使用instanceof运算符来进行判断。

if (object instanceof ClassName){
    do_something
}

对象引用多态的使用情形:

  1. 参数传递。当一个函数需要接收的参数可能是某个类的多个子类时,就可将参数声明为父类的引用,实际传参时,则传递实际的子类对象引用。

  2. 存储。在存储一系列不同子类的对象时,可以声明一个父类对象的数组进行存储,每个数组元素可以是具体的子类对象。

  3. 强制类型转换。虽然将各种子类统一使用父类引用进行管理非常方便,但使用父类的引用就只能访问到子类中父类的成员。如果要访问子类的成员,就需要将父类引用强制转换为子类引用,强制类型转换的前提是引用对象本身必须是转换目标所属的类,这时可以先使用instanceof先判断,再转换。

    Object [] list = {"abc", "def", "ghi"};
    for (String str : list){
      // String str = (String)list[i];
      do_some_things;
    }
    

反射

https://www.journaldev.com/1789/java-reflection-example-tutorial#invoke-public-method

Poor Performance:动态解析类型

java.lang.Class:类的元数据,用于查找类的属性以及创建新的实例。

泛型(Generic)

泛型提供了编译时类型安全检测机制。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。Java编译器使用类型擦除将泛型类型替换为通用类型(例如Object, Comparable)并适当添加类型转换。

不同于C++模板,编译器不会将泛型类型替换为具体类型,从而不会产生新的类型。

泛型类型不支持类型转换,不能创建泛型数组

泛型方法

public [static] <T1,T2> void methodName(T1[] data, GenericsType<T2> info)
  • 类型参数声明:在在方法返回类型之前;
  • 类型参数声明可以包含多个类型参数,
  • 类型参数只能代表引用型类型,不能是基本类型。
有界类型参数

限制被允许传递到一个类型参数的类型种类。

public <T extends Type[,...]> void methodName(T[] data)

泛型类

在声明的类名后面添加了类型参数声明部分:

[modifier] class ClassName <T [extends Type][,...]>{
   public void methodName(T data)
}

在类的定义中可以使用声明的类型参数来定义字段,或作为方法的参数类型。

类型通配符

类型通配符一般是使用?代替泛型类的具体类型参数,?不需要放在类型参数声明列表中。

public static void getData(List<?> data)
public static void getUperNumber(List<? extends Number> data)
public static void getUperNumber(List<? super Number> data)

extends定义类型参数的上限(该类及其子类);super定义类型的下限(该类及其父类)。

在使用类型通配符具体化类型作为参数的方法中,仅使用泛型类提供的公共方法,而不使用具体类型相关的方法。

https://docs.oracle.com/javase/tutorial/extra/generics/index.html.

Java Generics Example Tutorial - Generic Method, Class, Interface - JournalDev

Java包简介

类库简介
java.io提供系统的输入输出,包括各类输入输出流。
java.langJava语言的基础,包括对基本数据类型的封装,以及MathProcessThread等类。java.lang包默认加载到所有的Java程序的。
java.net提供Java访问网络的功能,包括对TCP/UDP套接字的封装以及对应用层协议的封装。
java.util包括了容器框架的接口和类,日期和时间的处理,事件模型以及其他功能(随机数发生、字符串格式化等)。
java.applet提供创建Java Applet程序所必要的类。
java.awt提供图形界面编程的类和接口。

异常处理

异常分类

所有的异常类是从java.lang.Exception类继承的子类。Exception类是Throwable类的子类。除了Exception类外,Throwable还有一个子类Error

java exception hierarchy

Exception Handling in Java - JournalDev

处理异常

使用try-catch语句捕获异常。

使用捕获的异常对象,获取异常信息:

String getMessage()   // 关于发生的异常的详细信息
String toString()     // 返回异常类的名字
Throwable getCause()  // 异常原因
void printStackTrace() // 

抛出异常:

如果一个方法存在未捕获的异常,则方法的声明需要使用throws关键字来给出未处理异常列表。

public void method(···) throws XxxException, YyyException

在方法内部可以使用throw关键字抛出一个捕获到的异常或新实例化的异常。

自定义异常

所有异常都必须是 Throwable 的子类。

如果希望写一个检查性异常类,则需要继承 Exception 类。

如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。

设计模式

==Java Design Patterns== - Example Tutorial - JournalDev

参考文献

  1. The Java Language Specification, Java SE 14 Edition.