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; // 方法返回值仅限基本类型
}
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()) != null
(null
本身不能直接作为条件语句)。
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;
}
说明:
- 变量类型可以是:
byte
、short
、int
、char
以及string
(Java 7); 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
How do I parse command line arguments in Java?
构造方法
用于对类的成员变量进行初始化。
构造方法的特点:方法名与类名相同;没有返回值。
构造方法同一般方法一样,可以重载;如果一个类没有声明构造方法,则系统会为类添加一个默认构造方法,其方法体为空,访问权限与类相同;如果自己声明了构造方法,则系统就不再添加默认构造方法。
finalize
finalize()
方法在对象被垃圾收集器析构(回收)之前调用,用来清除回收对象,确保一个对象打开的文件被关闭了。
内部类
在某个类内部定义的类称为内部类。一个类如同使用其它类一样使用自己的内部类,包括创建内部类的对象并调用其方法。而内部类拥有对外层类所有字段和方法成员的访问权。
内部类的访问权限除了默认权限和public
外,还可以是protected
和private
。如果为private
,则该内部只能在本类中使用;如果为protected,则外层类、处于同一包中的类及外层类的子类可以访问它。
内部类还可以定义在方法内部,其作用域仅限于该方法内部。内部类可以在使用的地方,声明的同时使用。
继承
所有的类都是继承于java.lang.Object
。当一个类没有声明继承,则默认继承Object
。
Java只能进行单继承。
子类可以从父类那里继承非private
成员:即子类可以直接访问父类的非private成员,private成员在子类中不可见,仅能通过父类提供的非private
成员作为接口间接访问。
子类不继承父类的构造方法。子类在构造方法中使用super
关键字调用父类的构造方法对父类的成员进行初始化:
super(args); //出现在构造方法内部的第一行
如果子类的构造方法中不调用父类的构造方法,则系统会自动调用父类的默认构造方法(如果父类已经声明了构造方法,则需要显式添加默认构造方法,否则不能实现自动调用)。
类成员的初始化过程:
-
分配内存空间,并将字段单元初始化;
-
使用字段在声明时赋的值,对字段初始化;
-
调用构造函数,在构造函数中,首先对父类进行初始化,再初始化子类字段。
重写/覆盖/隐藏(Override)
在子类中声明的成员与父类的成员完全一致,则父类的成员称为被子类成员隐藏。
一般不推荐对父类的字段进行隐藏,这样会导致类的结构混乱。
进行方法隐藏时,子类声明的方法必须和父类的方法原型一致,而且子类方法的访问限制不能比父类严格(否则就直接访问父类方法了)。
子类不能直接访问到父类的同名方法,通过super
关键字访问父类方法。
规则:
- 如果不能继承一个方法,则不能重写这个方法。
- 访问权限不能比父类中被重写的方法的访问权限更低。
- 回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类。
- 声明为
final
、static
的方法不能被重写
抽象类
使用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
}
对象引用多态的使用情形:
-
参数传递。当一个函数需要接收的参数可能是某个类的多个子类时,就可将参数声明为父类的引用,实际传参时,则传递实际的子类对象引用。
-
存储。在存储一系列不同子类的对象时,可以声明一个父类对象的数组进行存储,每个数组元素可以是具体的子类对象。
-
强制类型转换。虽然将各种子类统一使用父类引用进行管理非常方便,但使用父类的引用就只能访问到子类中父类的成员。如果要访问子类的成员,就需要将父类引用强制转换为子类引用,强制类型转换的前提是引用对象本身必须是转换目标所属的类,这时可以先使用
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.lang | Java语言的基础,包括对基本数据类型的封装,以及Math 、Process 、Thread 等类。java.lang 包默认加载到所有的Java程序的。 |
java.net | 提供Java访问网络的功能,包括对TCP/UDP套接字的封装以及对应用层协议的封装。 |
java.util | 包括了容器框架的接口和类,日期和时间的处理,事件模型以及其他功能(随机数发生、字符串格式化等)。 |
java.applet | 提供创建Java Applet程序所必要的类。 |
java.awt | 提供图形界面编程的类和接口。 |
异常处理
异常分类
所有的异常类是从java.lang.Exception
类继承的子类。Exception
类是Throwable
类的子类。除了Exception
类外,Throwable
还有一个子类Error
。
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