java笔记
规范
类名首字母大写
一个.java文件只能包含一个public类,但可以包含多个非public类。如果有public类,文件名必须和public类的名字相同
数据类型
对于整型类型,Java只定义了带符号的整型,因此,最高位的bit表示符号位(0表示正数,1表示负数)。各种整型能表示的最大范围如下:
| 类型 | 位数 | 最小值 | 最大值 | 2 的幂形式 |
|---|---|---|---|---|
byte |
8 bit | -128 | 127 | $−2^7∼2^7−1$ |
short |
16 bit | -32768 | 32767 | $-2^{15} \sim 2^{15}-1$ |
int |
32 bit | -2147483648 | 2147483647 | $-2^{31} \sim 2^{31}-1$ |
long |
64 bit | -9223372036854775808 | 9223372036854775807 | $-2^{63} \sim 2^{63}-1$ |
对于float类型,需要加上f后缀。
Java的char类型除了可表示标准的ASCII外,还可以表示一个Unicode字符:
引用类型类似指针
加final修饰变成常量
使用var自动推断变量类型,类似auto,让编译器根据右边的值自动推断变量类型。
与或非运算,|或 ,&与,~非,^异或
引用类型和字符串
null = 没有对象
** = 有一个字符串对象,但里面没有字符
字符串比较技巧,比较常量字符串和变量时可以这样,预防可能为null的情况
1 | "-version".equals(arg) |
可变参数
相当于数组类型
1 | class Group { |
传入null默认为数组类型String[]
1 | g.setNames(null); ==== g.setNames((String[]) null); |
构造方法
没有在构造方法中初始化字段时,引用类型的字段默认是null,数值类型的字段用默认值,int类型默认值是0,布尔类型默认值是false
一个构造方法可以调用其他构造方法,调用其他构造方法的语法是this(…)
1 | class Person { |
继承 is
在Java中,任何class的构造方法,第一行语句必须是调用父类的构造方法。如果没有明确地调用父类的构造方法,编译器会帮我们自动加一句super()
子类不会继承任何父类的构造方法。子类默认的构造方法是编译器自动生成的,不是继承的
sealed 用于限制一个类或接口允许哪些类型继承或实现。
1 | public sealed class Shape |
表示 Shape 只允许 Circle 和 Rectangle 继承。
直接继承 Shape 的子类必须声明为以下三种之一:
final:不能再被继承sealed:继续限制它的子类non-sealed:解除限制,允许任意类继承
向下转型,向上转型
1 | Student s = new Student(); |
向上转型必须是子类继承父类
1 | Person p1 = new Student(); // upcasting, ok |
强制向下转型必须保证本来就是子类类型,加instanceof可以避免转换出错
判断instanceof后,可以直接避免再次强制转型。例如,对于以下代码:
1 | Object obj = "hello"; |
1 | Object obj = "hello"; |
组合 has
1 | class Student extends Person { |
对于多态
编译时看左边,运行时看右边
1 | Animal a = new Dog(); |
1 | class Animal { |
1 | Animal a = new Dog(); |
1 | 【编译阶段】 |
抽象类,接口类
在Java中,一个类只能继承自另一个类,不能从多个类继承。但是,一个类可以实现多个interface
default
假设原来有一个接口:
1 | interface Person { |
并且已经有很多实现类:
1 | class Student implements Person { } |
如果直接新增一个抽象方法:
1 | interface Person { |
所有实现类都必须立即实现 run(),否则无法编译。
如果新增为默认方法:
1 | interface Person { |
原来的实现类不需要修改就能继续使用;只有需要特殊行为的类才重写 run()。
default方法和抽象类的普通方法是有所不同的。因为interface没有字段,default方法无法访问字段,而抽象类的普通方法可以访问实例字段。
关于包
包没有父子关系。java.util和java.util.zip是不同的包,两者没有任何继承关系。
匿名类
- 没有类名的类,定义类的同时立刻创建对象。
- 语法:
1 | new 父类或接口() { |
- 可以:
- 实现接口
- 继承一个类
- 重写方法
- 定义字段、方法、实例初始化块
- 匿名类没有名字,所以不能像普通类一样单独重复
new。 - 匿名类可以访问外部类成员,包括
private成员。 - 访问外部类同名成员时可用:
1 | Outer.this.xxx |
- 例如:
1 | Runnable r = new Runnable() { |
模块
| 概念 | 全称 / 文件形式 | 是什么 | 主要作用 | 和其他概念的关系 |
|---|---|---|---|---|
| module-info.java | 模块描述文件 | 用来描述一个 Java 模块 | 使用 requires 声明依赖,使用 exports 开放包 |
它只是描述模块,不是模块本身 |
| JAR | .jar |
Java 最常见的打包格式 | 打包 .class、资源文件、第三方库、应用程序等 |
JAR 也可以包含 module-info.class,成为模块化 JAR |
| JRE | Java Runtime Environment | Java 程序的运行环境 | 提供 JVM 和运行 Java 程序需要的标准库 | Java 程序最终运行在 JRE/JVM 提供的环境中 |
1 | java.base.jmod ← 一个 JMOD 文件 |
1 | Java 程序 / JAR |
字符串类
结论:两个字符串比较,必须总是使用equals()方法。
trim()并没有改变字符串的内容,而是返回了一个新字符串。
另一个strip()方法也可以移除字符串首尾空白字符,类似中文的空格字符\u3000/也会被移除:
Java核心类
创建新对象时,优先选用静态工厂方法而不是new操作符。
1 | BigDecimal 金额: |
instanceof
instanceof 是 Java 的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。
1 | Number n = new Integer(...); |
1 | Number n = Integer.valueOf(100); |
这里有三件事要分开:
1 | 变量名: n |
1 | n instanceof Integer // true |
java bean
有很多class的定义都符合这样的规范:
- 若干
private实例字段; - 通过
public方法来读写实例字段。
例如:
1 | public class Person { |
如果读写方法符合以下这种命名规范:
1 | // 读方法: |
那么这种class被称为JavaBean:
异常
- Checked Exception 必须处理:要么
catch,要么throws。 catch:我在这里处理异常。throws:我不处理,交给调用我的方法处理。throw:真正抛出一个异常对象。- 异常会沿着调用栈向上传递:
1 | getBytes() |
| 关键字 | 作用 | 写在哪 |
|---|---|---|
throw |
真正抛出一个异常对象 | 方法体里面 |
throws |
声明这个方法可能抛异常 | 方法声明后面 |
1 | void test(int age) throws Exception { |
多个 catch:子类异常放前面,父类异常放后面;否则编译不过。
捕获异常后再次抛出时,尽量保留原始异常作为 cause。
1 | catch (NullPointerException e) { |
| 知识点 | 作用 |
|---|---|
throw new XxxException() |
主动抛异常 |
| 异常传播 | 当前方法不捕获 → 向调用者传播 |
printStackTrace() |
打印完整异常调用栈 |
new XxxException(e) |
抛新异常并保存原异常 |
getCause() |
获得原始异常 |
finally |
即使 catch 再抛异常,一般仍执行 |
finally 抛异常 |
可能屏蔽原本异常,应避免 |
addSuppressed() |
保存被屏蔽的异常 |
getSuppressed() |
获取被屏蔽的异常 |

1 | catch 能不能捕获异常 |
JDK Logging
用日志代替 System.out.println()
使用 java.util.logging.Logger
创建Logger:
Logger.getGlobal()
Logger.getLogger(ClassName.class.getName())常用日志:
logger.severe()
logger.warning()
logger.info()
logger.fine()日志级别:
SEVERE
WARNING
INFO
CONFIG
FINE
FINER
FINEST默认一般是INFO:
INFO及以上显示
FINE等较低级别默认不显示日志可以:
- 自动记录时间、类、方法
- 设置级别
- 输出到文件
- 通过配置控制输出
异常处理中可以使用logger记录错误
Java第三方日志
Commons Logging
- 日志接口/门面
- LogFactory创建Log
- Log负责写日志
1
2
3
4
5
6
7static final Log log =
LogFactory.getLog(Main.class);
log.info()
log.warn()
log.error()
log.debug()异常:
1
log.error("发生异常", e);
Log4j
具体日志实现
可作为Commons Logging背后的实现
主要组件:
Appender:输出位置
Filter:过滤日志
Layout:日志格式log4j2.xml配置
可以输出:
Console
File
Socket
JDBC支持日志文件滚动
SLF4J
- 日志接口/门面
- 类似Commons Logging
1
2
3
4
5
6
7static final Logger logger =
LoggerFactory.getLogger(Main.class);
logger.info()
logger.warn()
logger.error()
logger.debug()支持{}占位符:
1
logger.info("user={}, age={}", user, age);
Logback
- 具体日志实现
- 通常配合SLF4J
- logback.xml进行配置
- 可输出控制台、文件等
关系:
1 | Commons Logging → Log4j |
最重要思想:代码面向日志接口,具体日志实现通过依赖和配置决定。
1 | 日志接口 / 门面 |
反射
通过Class实例获取class信息的方法称为反射(Reflection);
动态代理
动态代理主要用于在不修改原有业务类代码的情况下,在运行时为对象的方法统一增加额外功能,例如日志、事务、权限校验、性能统计等。它避免了为每个目标类手动编写代理类,同时实现了业务逻辑和通用功能的解耦。