Java基础笔记
Java笔记
工作流程
数据类型
- boolean 包含true或者false(默认值false)(注意:java中boolean的true和false与整数值1和0不等价,不可进行关系比较)
- 整数(默认值0)
- byte 占一个字节,存储有符号整数-128~127,默认值0
- short 两个字节,-32768~32767(-215 ~ 215 -1),默认值0
- int 四个字节,约 -2x109 ~ 2x109 (-2^31 ~ 231 -1),默认值0
- long 八个字节,约 -9x1018 ~ 9x1018 (-263 ~ 263 -1),默认值0L,整数表示在后面加L,如 0L
- 浮点型(不能表示很精确的数)
- float 4字节,默认值0.0f。数量级1038 。数值表示时后面必须加f,如1.12f。
- double 8字节,默认值0.0d。数量级10308 。数值表示时末尾d可省略
- char 字符型,占2字节,默认值'\u0000',用unicode编码表示,范围\u0000~\uffff。用单引号+字符/unicode码 表示,如 '我'、'\u4e00'
- 类成员变量有默认值,而函数成员变量是没有默认值的,需要初始化才可使用,否则报错。(所有变量定义时最好都初始化)
分支和循环语句(与C++基本相同)
- 分支语句
- if-else语句
- switch-case语句(注意:Java中支持case为字符串)
- 循环
- while
- do-while
- for循环
- for-each
函数定义
- 只能在类内定义
- 一般声明为public,方便外部访问
- 函数重载:函数名相同,函数参数表类型、参数数量不同
- main函数不是类的成员函数,而是程序入口,不能被其他函数调用(结构PSVM -- public static void main)
命名传统
- main函数所在的类名和文件名相同
- 类名每个单词首字母大写 → 大驼峰法
- 方法和变量名首字母小写,其余单词首字母大写 → 小驼峰法
- 常量 (public static final) 一般全字符大写,单词中间以下划线相连
面向对象
简介
- 对象 = 属性 + 方法, 对象规范(类) = 属性定义 + 方法定义
- 变量的发展:基本类型(一种变量,可继承)⇒ 结构体(多变量捆绑,可包含但无法控制粒度)⇒ 类(变量 + 方法,可继承非private成员)
- 由面向过程 OP ⇒ 面向对象OO:OP注重行为的执行,变量是被动地被使用;OO更加注重行为的主体和类的重用
- 基本类型赋值是拷贝赋值,对象赋值是reference赋值
- 类成员变量有默认值,而函数成员变量是没有默认值的,需要初始化才可使用,否则报错。(所有变量定义时最好都初始化)
构造函数
- 构造函数名和类名相同,且没有返回值
- 每个类必须有构造函数,如不定义,java自动生成一个无参构造函数;若定义构造函数,则java不会自动生成
- 每个变量都有生命周期,其生命周期包含在离它最近的{}内
- Java没有析构函数。这是因为Java提供内存自动回收机制,当变量退出其生命周期,JVM自动回收变量的内存。对象回收效率取决于JVM中的垃圾回收器GC(Garbage Collector)
- 每个子类的构造函数第一句话,都默认调用父类的无参构造函数super(),除非子类第一句话是super,且super语句必须放在第一条。不会连续出现两条super语句。
信息隐藏
- 信息隐藏原则:类的成员属性是private,类的方法是public,其他类无法直接访问private属性,需通过public方法访问
- get和set方法是public,称为getter和setter方法。成员属性只能通过get和set访问。可通过IDE自动生成
this指针
- 指代本类。 可访问类属性和方法,当不产生混淆时,可省略this(默认形参名称优先级高于成员变量)
- 可用于指代本类的构造函数
继承
- 子类/派生类(Child/Derived Class)、父类/基类/超类(Father/Base/Super Class)
- 子类继承(extends)父类(包括父类的父类,依次类推)所有的属性和方法(但不能直接访问父类的private成员)
- 当子类存在和父类同名的属性和方法时(称为重写/覆写/覆盖 override,非overload),子类的属性和方法优先级更高 → 优先调用子类属性/方法
- 每个子类的构造函数第一句话,都默认调用父类的无参构造函数super(),除非子类第一句话是super,且super语句必须放在第一条。不会连续出现两条super语句。
- Java只支持单根继承,不支持多继承(与C++继承最大的不同)
- Java所有的类都继承自java.lang.Object类,Object类里有toString、Equals、clone、hashCode、finalize、getClass等方法
抽象类
- 完整的类:所有的方法都被实现(存在 { 方法体 } )
- 当一个类的方法没有被全部实现时,该类需要被定义为抽象类,需要对类和未实现的方法添加abstract声明
- 子类继承抽象类时,必须实现抽象类的所有方法。否则子类也成为抽象类(需加abstract)
- 抽象类也是类,仍遵循单根继承
- 抽象类不能被实例化(被new出来)
- 抽象类组成:
- abstract声明
- (可选)成员变量,个数不限
- (可选)具体方法,方法有实现,个数不限
- (可选)抽象方法,加abstract,个数不限
public abstract class Shape{
private int area;
public abstract int calArea();
}
接口
- 所有的方法都没有实现的类称为接口,需要改成interface声明。接口不能被实例化(new出来)
- 接口不是类,或者说是一种特殊的类。子类可以同时实现(implements)多个接口。继承和实现可以同时。
- 继承(extends)必须写在实现(implements)之前
- 接口可以继承(extends)多个接口(父类逗号隔开),继承父类未实现的所有方法
- 类实现接口,就必须实现未实现的所有方法。否则,需声明为抽象类
- 接口内可以定义变量,但一般是常量
// Animal.java
public interface Animal {
public void move();
public void eat();
}
// LandAnimal.java
public abstract class LandAnimal implements Animal{
public abstract void eat();
public void move() {
System.out.println("I can walk by feet.");
}
}
// ClimbTree.java
public interface ClimbTree {
public void climb();
}
// Rabbit.java
public class Rabbit extends LandAnimal implements ClimbTree{
public void eat() {
System.out.println("I can eat.");
}
public void climb() {
System.out.println("I can climb tree.");
}
public static void main(String args[]) {
/* ... */
}
}
抽象类和接口的对比
抽象类 | 接口 |
---|---|
abstract | interface |
可以有部分方法实现 | 方法均没有实现 |
子类只能继承一个抽象类 | 子类可以实现多个接口,接口可以继承多个接口 |
有构造函数 | 无构造函数 |
有main,能运行 | 无main |
方法可为private和protected | 方法均为public |
转型、多态和契约设计
- 类转型:子类可以类型转换为父类(由大变小,向上转型。因为子类继承了父类所有成员),但父类不能转型为子类(由小变大,向下转型),除非父类对象本身就是子类转型得到
- 多态:子类转型为父类后,调用普通方法时,依旧是调用子类方法
- 多态的作用
- 以统一的接口来操纵某一类不同对象的动态行为
- 对象之间的解耦 👇
- 契约设计:类不会直接使用另一个类,而是采用接口的形式,外部可以“空投”这个接口下的任意子类对象(通过多态实现对象之间的解耦)
- Java编程设计遵循契约设计
- 契约:规范了对象应该包含的行为方法
- 接口定义了方法名称、参数和返回值,规范了派生类的行为
- 基于接口,利用转型和多态,不影响真正方法的调用,实现调用类和被调用类的解耦(decoupling)
/* Animal.java */
public interface Animal{
public void eat();
public void move();
}
/* Cat.java */
public class Cat implements Animal{
public void eat(){
System.out.println("Cat eat.")j;
}
public void move(){
System.out.println("Cat move.");
}
}
/* Dog.java */
public class Dog implements Animal{
public void eat(){
System.out.println("Dog eat.")j;
}
public void move(){
System.out.println("Dog move.");
}
}
/* AnimalTest.java */
public class AnimalTest{
public static void main(String args[]){
Animal[] A = new Animal[4];
A[0] = new Cat();
A[1] = new Dog();
A[2] = new Cat();
A[3] = new Dog();
for(int i=0; i<A.length; ++i) {
A[i].move();
}
haveLunch(new Cat());
haveLunch(new Dog());
haveLunch(new Animal{ // 匿名类
public void eat(){
System.out.println("Anonymous class eat.");
}
public void move(){
System.out.println("Anonymous class move.");
}
})
}
public static void haveLunch(Aniaml a){
a.eat();
}
}
/* 输出:
Cat move.
Dog move.
Cat move.
Dog move.
Cat eat.
Dog eat.
Anonymous class eat.
*/
static和final
-
static可修饰变量、方法、类、匿名代码块
- 修饰变量:变量依赖于类存在,而不依赖于对象示例。不必new出来即可通过类名访问变量。类的所有对象实例共用一个静态变量(内存中只有一份拷贝)
- 修饰方法:静态方法也可通过类名直接引用。静态方法中不能使用非静态成员(变量/方法),可以使用静态成员(变量/方法)。而非静态方法可以调用静态成员
- 修饰类(内部类):用的较少
- 修饰匿名方法块:
- 直接由花括号{}包围的代码块成员称为匿名代码块
- 调用顺序:static块 → 普通匿名代码块 → 构造函数
- 静态static匿名代码块只在第一次加载类时调用,而普通匿名代码块在每次实例化时都会调用
- 不推荐使用匿名代码块,可将代码块封装成如init函数等,方便管理和调用
-
final可以修饰类、变量、方法
- 修饰类:则该类不能被继承
- 修饰方法:则该方法不能被子类重写override
- 修饰变量:
- 基本变量:变量的值不能被改变(即常量)
- 对象类型:不能修改其指针(但能修改对象内部的值)
-
常量设计
- Java中没有constant关键字
- 常量不可被修改 → final,内存中只有一份 → static,方便被访问 → public(即public static final)
- 常量 (public static final) 一般全字符大写,单词中间以连字符相连
- 接口中的成员变量默认为常量(public static final可缺省)
-
常量池
-
Java为基本类型的包装类(不包括Float和Double)和String类型建立了常量池,相同的值只存储一份,以节省内存空间
-
范围:
- Boolean → true/false
- Character → 0~127(\u0000 ~ \u007f)
- Byte/Short/Integer/Long → -128 ~ 127
- Float和Double:没有缓存(常量池)
- Java常量字符串都建立常量池缓存机制
-
包装类和字符串创建方式
- 常量(字面量)赋值创建,放在栈内存(将被常量化)
- new对象创建,放在堆内存(不会常量化)
- 因此两种创建方式创建的对象地址不同
-
包装类比较
- 基本类型和包装类比较、包装类之间进行算术运算,将对包装类自动拆箱
- 对象比较,比较地址
-
字符串比较
-
编译器会优化确定的字符串常量加法并使用常量池缓存。但对于包含new对象、变量之类的不确定字符串,则不优化
String a = "abc"; String b = "a" + "b" + "c"; String c = "a" + new String("bc"); // a == b ? true // a == c ? false
-
-
-
不可变对象(Immutable Object)
- 典型不可变对象:八种基本类型的包装类、String、BigInteger、BigDecimal等
- 创建方式:
- Immutable对象不可改变,要改变,需clone/new一个对象进行修改
- 所有属性都是private和final
- 类是final或所有方法为final(避免继承过程中被修改)
- 类中包含mutable对象,那么返回拷贝需要深度clone
- 优点
- 只读,线程安全
- 并发读,提高性能
- 可以重复使用
- 缺点:制造垃圾,浪费空间
- Java字符串
- String类是典型的不可变对象,字符串内容比较用equals方法,是否指向同一对象用 ==
- String类的加法效率低,可利用可变对象StringBuffer/StringBuilder类及其拥有的append方法运算
- StringBuffer 同步,线程安全,修改快速
- StringBuilder 不同步,线程不安全,修改极快
- 修改速度:StringBuilder > StringBuffer > String
import java.util.Calendar;
public class Test {
public static void main(String[] args) {
final int n = 50000;
Calendar t1 = Calendar.getInstance();
String a = "";
for(int i=0;i<n;++i) {
a = a + i + ",";
}
System.out.println(Calendar.getInstance().getTimeInMillis()-t1.getTimeInMillis());
Calendar t2 = Calendar.getInstance();
StringBuffer b = new StringBuffer();
for(int i=0;i<n;++i) {
b.append(i);
b.append(",");
}
System.out.println(Calendar.getInstance().getTimeInMillis()-t2.getTimeInMillis());
Calendar t3 = Calendar.getInstance();
StringBuilder c = new StringBuilder();
for(int i=0;i<n;++i) {
c.append(i);
c.append(",");
}
System.out.println(Calendar.getInstance().getTimeInMillis()-t3.getTimeInMillis());
}
}
//2712
//6
//3
单例模式
- 限定某一个类在整个程序运行中只能有一个实例对象在内存空间中(即一个类有且只有一个对象)
- 采用static共享对象实例
- 采用private构造函数,防止外界new操作
public class Singleton {
private static Singleton obj = new Singleton();
private String content;
private Singleton() {
content = "abc";
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public static Singleton getInstance() {
return obj;
}
public static void main(String[] args) {
Singleton obj1 = Singleton.getInstance();
Singleton obj2 = Singleton.getInstance();
System.out.println(obj1.getContent() + " " + obj2.getContent());
//abc abc
obj1.setContent("def");
System.out.println(obj1.getContent() + " " + obj2.getContent()); //def def
System.out.println(obj1 == obj2); // true
}
}
java访问权限
访问权限 | 同一个类 | 同一个包 | 不同包的子类 | 不同包的非子类 |
---|---|---|---|---|
private | ✔ | ❌ | ❌ | ❌ |
(default) | ✔ | ✔ | ❌ | ❌ |
protected | ✔ | ✔ | ✔ | ❌ |
public | ✔ | ✔ | ✔ | ✔ |
建议:成员属性都用private,成员方法都用public
package、import、jar和classpath
package管理
- Java支持多目录放置java文件,通过package/import/jar包/classpath等实现跨目录放置和调用java
- package类似于C++ 中的namespace,需在文件第一句话给出
- java文件要严格放置在声明的package目录下
- 包名尽量唯一。常用域名逆序作为包名,如
package cn.edu.zju;
import
- 若所有java文件都放在同一目录下,则可以不进行显示调用声明
- import时,必须采用全称引用(包名+类名,无后缀),程序正文可以用短名称
- import必须放在package之后,类定义之前。多个import的顺序无关
- 可用
*
来import目录下的所有类,如import A.B.* (不建议,容易导致同名程序混乱),但是不包括下面的子目录文件(即不能递归包含)
jar
- 本质:一组class文件的压缩包(其实存放java文件也是可以的)
- 优点:
- 只有一个文件,便于网络传输
- 只包含class文件,有利于保护版权
- 便于版本控制
- 可利用Eclipse等IDE工具或jar.exe 打包和导入jar文件
classpath(命令行)
- 编译格式:
javac -classpath ".;<路径1>;<路径n>" A/B/C.java
→ 文件全路径 - 运行格式:
java -classpath ".;<路径1>;<路径n>" A.B.C
→ 类的全称 - 编译时,用javac,classpath包含这个类依赖的类所在路径(以及依赖的类再次依赖的其他类)
- 运行时,用java,classpath除了包含编译时的classpath外,还要包括本类所在路径
- windows中路径用分号 ; 分隔,linux中用冒号 : 分隔;若路径中不含空格,classpath两侧双引号可省略
Java常用类
- Java中的类已达几千个,所以记住每个类是不现实的,只需记住几个常用的类。遇到其他的类可查询Java的API文档
- java -- Java核心包, javax -- Java扩展包
数字相关类
- BigInteger → 存储任意精度的整数
- 不能直接用算术符号运算,相应的,用
add(n) subtract(n) multiply(n) divide(n)
运算,divideAndRemainder(n)
返回数值,包含商和余数 max(n) min(n)
返回最大值和最小值equals(n)
比较数值大小,compareTo(n)
返回比较结果±1或0
- 不能直接用算术符号运算,相应的,用
- BigDecimal → 存储任意精度的浮点数,最好用字符串构造(用浮点数构造存在误差)
- 不能直接用算术符号运算,相应的,用
add(n) subtract(n) multiply(n) divide(n, 保留位数)
运算,divideAndRemainder(n)
返回数值,包含商和余数 max(n) min(n)
返回最大值和最小值equals(n)
比较数值大小,compareTo(n)
返回比较结果±1或0
- 不能直接用算术符号运算,相应的,用
- Random类
nextInt()
返回随机int,nextInt(n)
返回[0, n) 之间的随机int,nextDouble()
返回[0, 1]之间的浮点数,nextLong等依此类推ints(n, left, right).toArray()
返回包含给定范围[left, right) 范围内(若省略则为int范围内)的整数流,加上toArray()转换为数组- Math类中的
Math.random()
返回[0.0, 1.0] 之间的浮点数
- java.lang.Math类
abs、sin、cos、pow、round、floor、ceil、PI
等
字符串类
- String类(牢记常用方法) -- 不可变类,调用方法不影响对象本身
String a = "123;456;789;123 ";
System.out.println(a.charAt(0)); // 返回第0个元素
System.out.println(a.indexOf(";")); // 返回第一个;的位置
System.out.println(a.concat(";000")); // 连接一个新字符串并返回,a不变
System.out.println(a.contains("000")); // 判断a是否包含000
System.out.println(a.endsWith("000")); // 判断a是否以000结尾,startsWith类推
System.out.println(a.matches("[a-z]*")); //判断是否符合给定正则表达式
System.out.println(a.equals("000")); // 判断是否等于000
System.out.println(a.equalsIgnoreCase("000")); // 判断在忽略大小写情况下是否等于000
System.out.println(a.compareTo("abc"); //比较字符串大小,返回0或正负值。而compareToIgnoreCase忽略大小写
System.out.println(a.hashCode()); // 返回字符串hash值
System.out.println(a.length()); // 返回a长度
System.out.println(a.trim()); // 返回a去除前后空格后的字符串,a不变
String[] b = a.split(";"); // 将a字符串按照;分割成数组
for (int i = 0; i < b.length; i++) {
System.out.println(b[i]);
}
System.out.println("===================");
System.out.println(a.substring(2, 5)); // 截取a的第2个到第5个字符(不包括第5), a不变
System.out.println(a.replace("1", "a")); // replace和replaceAll均替换所有符合的字符串
System.out.println(a.replaceAll("1", "a")); // replaceAll第一个参数是正则表达式
System.out.println("===================");
String s1 = "12345?6789";
String s2 = s1.replace("?", "a");
String s3 = s1.replaceAll("[?]", "a");
// 这里的[?] 才表示字符问号,这样才能正常替换。不然在正则中会有特殊的意义就会报异常
System.out.println(s1.replaceAll("[\\d]", "a")); //将s1内所有数字替换为a并输出,s1的值未改变。
- StringBuffer/StringBuilder类,可变对象,方法与String基本相同,区别在同步
- 额外的方法:append/delete/insert/replace/substring
- length() 字符串实际大小,capacity() 字符串占用空间大小(capacity >= length)
- trimToSize() 取出空隙,将字符串压缩到实际大小(使capacity等于length)
- 若有大量append,预估大小,再调用相应构造函数,可以提升性能
时间类
-
java.util.Date类
- 基本废弃,主要使用getTime() 获取自1970-01-01年开始的毫秒数
- 可直接System.out.println(d) 输出
-
Calendar类 -- 目前主要使用的类(线程不安全)
- Calendar为抽象类,不能直接new。需通过Calendar.getInstance()获取类(实际上创建的是其子类GregorianCalendar类)
get(field)
获取指定时间域,field包括Calendar类内的静态常量,通过Calendar.name调用。常用的有YEAR MONTH(0~11), DAY_OF_MONTH(可用DATE代替), HOUR(12小时制) , HOUR_OF_DAY(24小时制), MINUTE, SECOND, DAY_OF_WEEK(星期,1~7,从星期日开始)
。注意月份和星期的特殊性。set(field, value)
设置某个时间域的值。set(year, month, date)
设置年月日add(field, value)
遵照日期规则增减某个时间域的值roll(field, value)
不遵照日期规则,仅增减某个时间域的值(在该时间域范围内循环,如10月1日中日期减一天变成10月31日)getTime()
返回相应的Date对象getTimeInMillis()
返回1970-01-01以来的毫秒数
-
java.time包(java 8 新类,线程安全,遵循设计模式)
-
LocalDate类
now()
返回当前时间的LocalDate对象,now(ZoneId.of("Asia/Shanghai"))
可加时区of(year, month, date)
返回指定时间的LocalDate对象(month建议用Month类的枚举成员替代,如Month.January)ofEpochDay(day)
返回自纪元日1970-1-1开始后的day天的日期ofYearDay(day)
返回给定年份第day天的日期
-
LocalTime类
now()
用法同LocalDate,可加时区of(hour, minute, second, nanoSecond)
创建指定时间ofSecondOfDay(n)
返回一天中的第n秒的时间
-
LocalDateTime类
-
now()
用法同LocalDate,可加时区 -
of(year, month, day, hour, minute, second)
创建指定时间of(LocalDate.now(), LocalTime())
通过LocalDate和LocalTime创建 -
ofEpochSecond(second, nanoSecond, ZoneOffset)
创建自纪元日开始的第几秒的时间
-
-
格式化相关类
-
java.text包
-
NumberFormat 数字格式化,抽象类,通过getInstance()实例化出子类DecimalFormat。具体格式查java API文档
DecimalFormat df1 = new DecimalFormat("0.##"); // 0代表不可省略,#代表可省就省 System.out.println(df1.format(0.80)); // output: 0.8, 若输入0.006则输出为0.01 df1 = new DecimalFormat("#,##0.00"); //每3为用逗号,分隔
-
MessageFormat 字符串类格式化
- 支持模板(参数)和文本(值)对位输出
- 支持变量自定义格式,如数字、日期等
String message = "{0}{1}{2}{3}"; Object[] array = new Object[]{"A","B","C","D"}; String value = MessageFormat.format(message, array); System.out.println(value); message = "oh, {0,number,#.##} is a good number"; array = new Object[]{13.1415};
-
DateFormat 日期格式化,抽象类,利用getInstance()示例化出子类SimpleDateFormat
- parse方法:将字符串格式化为日期Date对象
- format方法:将日期格式化为字符串
String strDate = "2008-10-19 10:11:30.345" ; // 准备第一个模板,从字符串中提取出日期数字 String pat1 = "yyyy-MM-dd HH:mm:ss.SSS"; // 准备第二个模板,将提取后的日期数字变为指定的格式 String pat2 = "yyyy年MM月dd日 HH时mm分ss秒SSS毫秒" ; SimpleDateFormat sdf1 = new SimpleDateFormat(pat1); // 实例化模板对象 SimpleDateFormat sdf2 = new SimpleDateFormat(pat2); // 实例化模板对象 Date d = null; try{ d = sdf1.parse(strDate) ; // 将给定的字符串中的日期提取出来 }catch(Exception e){ // 如果提供的字符串格式有错误,则进行异常处理 e.printStackTrace() ; // 打印异常信息 } System.out.println(sdf2.format(d));// 将日期变为新的格式
-
-
java.time.format.DateFormatter 时间格式化(java8发布,线程安全。SimpleDateFormat线程不安全)
- ofPattern 设定时间格式
- parse 将字符串格式化为时间对象
- format 将时间对象格式化为字符串
//将字符串转化为时间 String dateStr= "2016年10月25日"; DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日"); LocalDate date= LocalDate.parse(dateStr, formatter); System.out.println(date.getYear() + "-" + date.getMonthValue() + "-" + date.getDayOfMonth()); System.out.println("=========================="); //将日期转换为字符串输出 LocalDateTime now = LocalDateTime.now(); DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy年MM月dd日 hh:mm:ss"); String nowStr = now.format(format); System.out.println(nowStr);
Java笔记2
异常
分类
-
分类1:
- Throwable 所有错误的祖先
- Error系统内部错误或资源耗尽,不管
- Exception:程序有关异常,所有异常的父类
- RuntimeException:程序自身错误(如除0, 空指针,数组越界)
- 非RuntimeException: 外界相关错误(打开一个不存在的文件、加载不存在的类)
-
Unchecked Exception:包括Error和RuntimeException的子类(编译器不会辅助检查,需要程序员自己管的)。Exception中不是RuntimeException的即为Unchecked Exception
Checked Exception:非RutimeException(编译器辅助检查,如果没有处理会报错)
异常处理
- try-catch-finally(书写顺序不可变)
- try中包含可能出现错误的代码
- catch可以有0到多个,从上到下依次匹配,所以小异常要写在前面,大异常写在后面
- 当有一个catch匹配时,进入相应catch代码块,且仅会进入一个catch代码块
- 发生异常后,代码块后续代码不再执行,catch处理异常后也不再返回抛出异常的地方
- finally可以有0到1个。无论是否有异常,异常是否被捕捉,finally都会执行
- catch和finally至少要有一个
- 代码块中能够再包含try-catch-finally结构
- 抛出异常
- 方法存在可能发生异常的语句,但不处理,可以添加throws声明异常
- 调用带有checked Exception的方法,要么处理这些异常,要么再次向外throws异常,直到main函数为止
- 如果一个方法被覆盖,则覆盖它的方法也需要抛出相同的异常或异常的子类(不能超过父类抛出的异常的范围,即如果父类抛出n个异常,子类重写的方法可以不抛出、抛出1~n个异常或对应异常的子类)
- 自定义异常
- 继承自Exception则变成unchecked Exception,继承自RuntimeException则变成Checked Exception
- 自定义重点在构造函数
- 调用父类Exception的message构造函数
- 可以自定义自己的成员变量
- 在程序中采用throw主动抛出异常
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!