java


前言

适合已经学习过java但是忘记的朋友快速复习
此篇文章为本人复习java的笔录,会实时更新。
若文章内容有错误,欢迎各位批评指正。
本人使用开发工具为Idea。


一、java概述

1.1 java简介

java是由Sun公司推出的Java程序设计语言java平台的总称。

  • 现成代码多:标准库庞大,拿来就用。
  • 自动管家:内存自己扫,程序员不管释放。
  • 跨平台:字节码给 JVM,安全校验、移植性一并搞定。
  • 特点:简单性、面向对象性、健壮性 、跨平台性、高性能、多线程、动态性。

1.2 跨平台性

在这里插入图片描述

  • Java可以在计算机的操作系统之上再提供一个Java运行环境,该运行环境由Java虚拟机(Java Virtual Machine)、类库以及一些核心文件组成。
  • Java 源程序 → 编译成字节码 → 交给 对应系统的 JVM 翻译成机器码 → 一处编译,到处运行。

注意:

  • JVM 本身是用 C/C++ 写的,针对每个操作系统单独编译,所以 JVM 不能跨平台。
  • Java 程序编译成字节码后,靠对应平台的 JVM 解释/编译执行,因此 Java 程序能跨平台。

1.3 垃圾自动回收

Java 的垃圾回收线程会在后台自动检测和释放不再使用的内存,程序员无需手动管理内存,但可以通过显式将对象引用设为 null 来协助 垃圾收集线程GC,从而减少内存泄漏。
GC 的工作原理:

  1. 对象创建:
Person p = new Person(); // JVM 在堆内存中为这个对象分配空间。
  1. 引用断开:
p = null; // 现在程序中没有任何变量引用这个对象了。
  1. GC 检查:
    • 垃圾回收器周期性地扫描堆内存;
    • 找出“不可达对象”(即没有引用指向它的对象)。
  2. 回收内存:这些不可达对象的内存会被自动释放,供以后重新分配使用。

1.4 内存泄漏

内存泄漏是指程序中不再使用的对象仍被引用,导致垃圾回收器无法释放它们占用的内存空间,长期累积可能引起内存溢出(OutOfMemoryError)。
举例:

import java.util.ArrayList;
import java.util.List;

public class LeakDemo {
    private static List<byte[]> list = new ArrayList<>();

    public static void main(String[] args) {
        while (true) {
            byte[] data = new byte[1024 * 1024]; // 每次创建1MB数据
            list.add(data); // 添加到静态集合里
            System.out.println("对象数量:" + list.size());
        }
    }
}

data 数组本来用完就可以被回收;但你把它 add() 到了一个静态 list;只要 list 一直存在,data 的引用就永远不会断;GC 就不会清理这些数组;最终内存越来越多,直到内存溢出(OutOfMemoryError) 。

1.5 JVM

JVM(Java Virtual Machine)是可运行Java字节码的虚拟计算机系统,本质上是一个软件。加载java源程序编译得到的.class文件,读取其中的虚拟机指令并执行。
此过程由三部分组成,分别是:

  • 代码的装载
  • 代码的校验
  • 代码的执行
    在这里插入图片描述

1.6 JRE、JDK和JVM

  1. JDK(开发工具包),包含:
    • 编译器 javac(把 .java 编译成 .class)
    • 调试器 jdb
    • 打包工具 jar
    • 运行环境(JRE)
  2. JRE(运行环境),包含:
    • JVM(虚拟机)
    • Java 核心类库(rt.jar)
    • 一些运行所需的配置文件
      普通用户只要有 JRE 就能运行 .jar 或 .class 程序,但不能编译。
  3. JVM(虚拟机),功能:
    • 加载字节码文件
    • 解释或即时编译执行(JIT)
    • 管理内存(GC)
    • 提供跨平台能力(Write Once, Run Anywhere)

1.7 编程风格

  • Allmans风格 :“独行”风格,即左、右大括号各自独占一行。
  • Kernighan风格:“行尾”风格,即左大括号在上一行的行尾,而右大括号独占一行 。

1.8 java注释

行注释、块注释和文档注释。

  • 单行注释(快捷键Ctrl+/):
int a = 1;  // 这个就是单行注释,使用 // 
  • 块注释(快捷键Ctrl+Shift+/)
    /*
    * 我叫块注释*/
  • 文档注释(先打 /** 后回车)
    /**
     * 我叫文档注释
     */

注释文档将用来生成HTML格式的代码报告,所以注释文档必须书写在类、域、构造函数、方法,以及字段(field)定义之前。

1.9 注意事项

  • 文件名 ≠ public 类名 → 编译直接报错;
  • Java 严格区分大小写。
  • main 手写错一个字母 → 找不到入口,运行提示 “Main method not found”。
  • 一个 .java 最多一个 public 类,且必须和文件名一致;其余类 去掉 public 就行。
  • main 签名 必须是
public static void main(String[] args)

变体(如 String args[]、大小写、少 static)都不行。

  • 入口只在 main——没有 main 就不是可运行 Application。

二、java基本语法

2.1 java程序基本格式

  1. 结构定义语句(如 class、方法头)不用分号结尾;
  2. 功能执行语句(真正干活的代码)必须英文分号 ; 结尾,中文分号 ; 会报 illegal character。
    举例:
public class Demo {                    // ① 结构定义语句——声明类,无分号
    public static void main(String[] args) {  // ② 结构定义语句——声明方法,无分号
        int a = 3;                    // ③ 功能执行语句——真正干活,必须英文分号;
        System.out.println(a);        // ④ 功能执行语句——必须英文分号;
    }                                 // 方法结束的大括号属于结构,无分号
}                                     // 类结束的大括号属于结构,无分号
  1. Java严格区分大小写:大小写必须完全一致 —— class ≠ Class,computer ≠ Computer。
  2. 在 Java 中,字符串不能跨行书写。如果字符串太长,需要分行,可以将其拆成多个字符串,用 + 连接,例如:
System.out.println("这是第一个" + 
                   "Java程序!");

2.2 Java文件命名规则

  1. 文件名与 public 类
    • 如果 Java 文件中有 public 类,文件名必须与该类名一致,并以 .java 作为后缀。
    • 一个 .java 文件中最多只能有一个 public 类;若没有 public 类,文件名可与任意类名相同。
    • Java 区分大小写。
  2. 编译规则
    • 每个类编译后生成一个对应的 .class 文件。
  3. main 方法
    • Java 应用程序必须包含 main() 方法,作为程序入口。
    • main() 方法可以定义在任意类中,不必是 public 类。
    • 文件名应为包含 main() 方法的类名加 .java。

2.3 标识符

  1. 标识符必须以字母、下划线_或美元符开头,后面可以跟字母、数字、下划线或美元符。
  2. 标识符可以包含数字,但不能以数字开头除下划线“_”和“$”符号外,标识符中不包含任何特殊字符,如空格。
  3. 标识符区分大小写,比如,“abc”和“Abc”是两个不同的标识符。
  4. 对于标识符的长度没有限制。
  5. 不能使用Java关键字作为标识符。

关键词:

public class static void main new
extends implements import package final this
super return if else switch case
for while do break continue default
try catch finally throw throws instanceof
abstract interface enum synchronized volatile transient
const native strictfp assert true false

2.4 标识符命名规范

  • 见名知义:命名要清晰表达含义。
  • 驼峰命名法:多个单词组合时,除首单词外,其余单词首字母大写。
  • 避免使用中文或拼音:虽然 Java 支持,但不推荐。
类型 命名规则
类名 首字母大写(驼峰)
方法名 首字母小写(驼峰)
变量名 首字母小写(驼峰)
常量 全部大写,单词间用下划线 _ 分隔

注意:

  • 所有的关键字都是小写的。
  • 不能使用关键字命名标识符。
  • const和goto是保留字关键字,虽然在Java中还没有任何意义,但在程序中不能用来作为自定义的标识符。
  • true、false和null虽然不属于关键字,但它们具有特殊的意义,也不能作为标识符使用。

2.5 变量

在 Java 中,变量是程序运行过程中用于存储数据的“容器”。每个变量都有:

  • 类型(type):表示变量存储的数据种类,如整数、浮点数、字符等。
  • 名字(name):变量的标识符,用于访问。
  • 值(value):变量存储的数据。

Java是一门强类型语言。即所有的变量都必须显式声明其类型。
变量必须先声明再使用。

示例:

int age = 20;      // 整型变量
double height = 1.75; // 浮点型变量
char grade = 'A';  // 字符变量
boolean isStudent = true; // 布尔型变量

2.5.1 变量的类型

  1. 基本类型:Java 的基本数据类型共 8 种:
类型 大小 默认值 示例
byte 1 字节 0 byte b = 10;
short 2 字节 0 short s = 100;
int 4 字节 0 int i = 1000;
long 8 字节 0L long l = 100000L;
float 4 字节 0.0f float f = 3.14f;
double 8 字节 0.0 double d = 3.1415;
char 2 字节 ‘\u0000’ char c = 'A';
boolean 1 字节(逻辑值) false boolean flag = true;
  1. 引用类型:引用类型变量用于存储对象的地址(不是对象本身):
    • 类对象:String str = “Hello”;
    • 数组:int[] arr = {1,2,3};
    • 自定义对象:Person p = new Person();

2.5.2 变量的分类

  1. 局部变量
    • 定义在方法、构造器或代码块内部
    • 只在作用域内有效
    • 不存在默认值,使用前必须初始化
    • 在同一个 作用域 内,变量名必须唯一,不允许重复声明
    • 不同作用域可以使用相同变量名(作用域覆盖)
void demo() {
    int x = 5; // 局部变量
    System.out.println(x);
}
  1. 成员变量
    • 定义在类内部、方法外部
    • 每个对象都有独立的副本
    • 有默认值,不必显式初始化
class Person {
    String name;  // 成员变量
    int age;
}
  1. 类变量
    • 使用 static 修饰
    • 属于类共享,每个对象共享同一个变量
class Counter {
    static int count = 0;
}
  1. 常量:使用 final 修饰,值不可修改
final double PI = 3.14159;

2.6 类型转换

在Java中,数据的类型如果是相容的,那么一种数据类型可以转换成另外一种数据类型。数据类型转换的方式有“自动类型转换”和“强制类型转换”两种 。

2.6.1 自动类型转换

将一种类型的变量赋给另一种类型的变量时,就会发生自动类型转换 ,发生自动类型转换需要满足:两种类型兼容、目标类型大于源类型

byteshortcharintlongfloatdouble 

2.6.2 强制类型转换

表示范围大的数据类型要转换成表示范围小的数据类型,需要用到强制类型转换 。
语法:

目标类型 变量名 = (目标类型) 原变量;

2.6.3 类型转换3

由高转低,int → byte 强制类型转换时,只保留低 8 位,高位丢失,数据可能发生 溢出或截断,导致值改变,这是 二进制截断原理,而不是随机错误。
注意:
Java 的整型运算规则:byte、short、char 在计算时会先 自动提升(promote)为 int。
举例:

public class Example03 {
    public static void main(String[] args) {
        byte b1 = 3; // byte类型变量
        byte b2 = 4;
        byte b3 = b1 + b2; // 问题所在
        System.out.println("b3=" + b3);
    }
}

byte b3 = …
试图把 int 类型 赋值给 byte 类型,编译器不允许自动截断(可能丢失数据),所以报错。

2.7 运算符

算术运算符、比较运算符、逻辑运算符、位运算符、赋值运算符、条件运算符

2.7.1 算数运算符

运算符 含义 示例
+ 3 + 2 = 5
- 3 - 2 = 1
* 3 * 2 = 6
/ 3 / 2 = 1(整型除法)
% 取余 3 % 2 = 1
++ 自增 i++(先用后加)、++i(先加后用)
-- 自减 i----i

2.7.2 比较运算符

运算符 含义 返回值类型 示例
== 等于 boolean 5 == 5true
!= 不等于 boolean 5 != 3true
> 大于 boolean 5 > 3true
< 小于 boolean 3 < 5true
>= 大于等于 boolean 5 >= 5true
<= 小于等于 boolean 3 <= 5true

2.7.3 逻辑运算符

运算符 含义 示例
&& 与(短路) (a > 0 && b > 0)
|| (a > 0 || b > 0)
! !(a > 0)

2.7.4 位运算符

运算符 含义 示例
& 按位与 5 & 3 = 1(0101 & 0011 = 0001)
| 按位或 5 | 3 = 7 (0101 | 0011 = 0111)
^ 按位异或 5 ^ 3 = 6(0101 ^ 0011 = 0110)
~ 按位取反 ~5 = -6
<< 左移 5 << 1 = 10
>> 右移 5 >> 1 = 2
>>> 无符号右移 -1 >>> 1 = 2147483647

2.7.5 赋值运算符

运算符 含义 示例
= 赋值 a = 5
+= 加后赋值 a += 3a = a + 3
-= 减后赋值 a -= 2
*= 乘后赋值 a *= 2
/= 除后赋值 a /= 2
%= 取余后赋值 a %= 2
&= 按位与后赋值 a &= 3
|= 按位或后赋值 a |= 7
^= 按位异或后赋值 a ^= 3
<<= 左移后赋值 a <<= 2
>>= 右移后赋值 a >>= 1
>>>= 无符号右移后赋值 a >>>= 1

2.7.6 算术运算符

变量 = 条件 ? 表达式1 : 表达式2;

2.8 程序基本流程

2.8.1 分支结构

  • if-else语句
if (条件) {
    // 条件为 true 时执行的代码
} else if (其他条件) {
    // 上一个条件不满足,当前条件为 true 时执行
} else {
    // 上述条件都不满足时执行
}
  • switch语句
switch (表达式) {
    case1:
        // 执行代码
        break; // 可选,用于跳出 switch
    case2:
        // 执行代码
        break;
    ...
    default:
        // 所有 case 都不匹配时执行
}

2.8.2 迭代结构

  • while语句
while (条件) {
    // 循环体代码
}
  • do-while语句
do {
    // 循环体代码
} while (条件);
  • for语句
for (初始化; 条件; 更新) {
    // 循环体代码
}

2.8.3 转移语句

  • break语句:在循环中用于立即从当前循环终止控制
for (int i = 1; i <= 5; i++) {
    if (i == 3) {
        break; // i==3 时终止循环
    }
    System.out.println(i);
}
  • continue语句:从其调用处跳至循环的开始处

跳过 本次循环剩余语句,直接进行 下一次循环判断。

for (int i = 1; i <= 5; i++) {
    if (i == 3) {
        continue; // i==3 时跳过本次循环
    }
    System.out.println(i);
}
  • return语句 :其之后的语句将不再执行
    • 立即终止方法执行
    • 并将控制权返回到调用该方法的位置
    • 方法中 return 之后的语句不再执行
public static int test(int x) {
    if (x < 0) {
        return -1; // 立即返回
    }
    return x * 2;
}

public static void main(String[] args) {
    System.out.println(test(-5)); // 输出 -1
    System.out.println(test(3));  // 输出 6
}

Java中没有goto语句,在多层循环时,可以用break、continue实现goto语句的功能。
带标签的 break 可一次跳出多层嵌套,格式 break 标签;,标签须放在目标语句块前。

2.9 数组

  • 数组(Array) 是一种 容器对象,用于 存储固定大小的同类型元素。
  • 一旦数组创建,长度固定,不能动态扩容(除非使用 ArrayList 等集合类)。
  • 索引从 0 开始。

2.9.1 数组的声明

// 方法一:类型在前,方括号在后
int[] arr1;

// 方法二:方括号在变量名后
int arr2[];

推荐使用 int[] arr 这种方式,更清晰地表达“这是一个整型数组”。
Java语言中声明数组时不能指定其长度(数组中元素的个数)。

2.9.2 数组的初始化

  1. 静态初始化
    已知元素,直接赋值:
int[] arr = {1, 2, 3, 4, 5};
  1. 动态初始化

只指定长度,元素使用默认值初始化:

类型 默认值
int, short… 0
double 0.0
char ‘\u0000’
boolean false
引用类型 null
int[] arr = new int[5]; // {0,0,0,0,0}

2.9.3 访问数组元素

int[] arr = {1, 2, 3};
System.out.println(arr[0]); // 1
arr[1] = 10;                // 修改第二个元素

注意越界问题:

System.out.println(arr[3]); // 会抛出 ArrayIndexOutOfBoundsException

2.9.4 数组的长度

int[] arr = {1,2,3};
System.out.println(arr.length); // 3

length 是数组的属性,不是方法。

2.9.5 数组的方法

  1. 普通 for 循环
for(int i = 0; i < arr.length; i++){
    System.out.println(arr[i]);
}
  1. 增强 for 循环(foreach)
for(int num : arr){
    System.out.println(num);
}

2.9.6 多维数组

同一维数组一样,二维数组也是通过new关键字来创建。声明的时候至少指定第一维的长度。仅声明第二维是非法的。
二维数组常用于矩阵或表格:

int[][] matrix = new int[2][3]; // 2行3列
matrix[0][1] = 5;

int[][] matrix2 = {
    {1,2,3},
    {4,5,6}
};

访问二维数组:

System.out.println(matrix2[1][2]); // 6

2.9.7 常用操作

  1. 复制数组
    (1)clone()
int[] copy = arr.clone();

(2)System.arraycopy

System.arraycopy(from, fromIndex, to, toIndex, count)
  1. 数组排序
    (1)sort()
import java.util.Arrays;
Arrays.sort(arr);

(2)冒泡排序

     for (int i = 0; i < arr.length; i++) {
        for (int j = 0; j < arr.length - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
               int temp = arr[j];
               arr[j] = arr[j + 1];
               arr[j + 1] = temp;
           }        }
     }

  1. 数组查找
int index = Arrays.binarySearch(arr, 10);
  1. 数组转字符串
System.out.println(Arrays.toString(arr));

2.10 字符串

String 是 Java 中的 对象类型,用于存储 一串字符。

  • 在 Java 中,字符串是 不可变的(immutable):
  • 对字符串的任何修改都会产生一个新的字符串对象。
  • Java 中的字符串使用 双引号 " " 表示。

2.10.1 字符串的创建方式

  1. 直接赋值
String str1 = "Hello"; // 字符串常量池
  1. 使用构造方法
String str2 = new String("Hello"); // 堆内存

2.10.2 字符串常规操作

  1. 获取长度
String str = "Hello";
int len = str.length(); // 5
  1. 访问字符
char ch = str.charAt(0); // 'H'
  1. 遍历字符串
for(int i = 0; i < str.length(); i++){
    System.out.println(str.charAt(i));
}

// 增强 for
for(char c : str.toCharArray()){
    System.out.println(c);
}
  1. 比较字符串
String a = "abc";
String b = "abc";
String c = new String("abc");

System.out.println(a == b);      // true(同一字符串常量池)
System.out.println(a == c);      // false(不同对象)
System.out.println(a.equals(c));// true(内容相同)
  1. 查找与截取
String str = "Hello World";

System.out.println(str.indexOf("o"));      // 4
System.out.println(str.lastIndexOf("o"));  // 7
System.out.println(str.substring(0, 5));  // Hello
  1. 大小写转换
String str = "Java";
System.out.println(str.toUpperCase()); // JAVA
System.out.println(str.toLowerCase()); // java

7.去空格

String str = "  hello  ";
System.out.println(str.trim()); // "hello"
  1. 分割字符串
String str = "apple,banana,orange";
String[] arr = str.split(","); // ["apple","banana","orange"]
  1. 判断字符串
String str = "Hello";
System.out.println(str.startsWith("He")); // true
System.out.println(str.endsWith("lo"));   // true
System.out.println(str.contains("ll"));   // true

2.11 StringBuffer

StringBuffer 是 Java 提供的 可变字符串类。
与 String 不同,它 可以直接修改内容,不会生成新的对象。
线程安全(方法上使用了 synchronized),适合多线程场景。

2.11.1 创建方式

  1. 使用无参构造
StringBuffer sb = new StringBuffer(); // 默认容量16
  1. 使用字符串初始化
StringBuffer sb = new StringBuffer("Hello"); // 容量 = 字符长度 + 16
  1. 指定容量
StringBuffer sb = new StringBuffer(50); // 初始容量50

2.11.2 常用操作

  1. 追加内容
StringBuffer sb = new StringBuffer("Hello");
sb.append(" World");   // Hello World
System.out.println(sb);
  1. 插入内容
sb.insert(6, "Java "); // Hello Java World
  1. 替换内容
sb.replace(6, 10, "C++"); // Hello C++ World
  1. 删除内容
sb.delete(6, 9); // 删除索引6到8的字符
System.out.println(sb); // Hello + World
  1. 反转字符串
sb.reverse();
System.out.println(sb); // dlroW + olleH
  1. 获取字符
char ch = sb.charAt(0);
sb.setCharAt(0, 'h'); // 修改第一个字符
  1. 长度与容量
System.out.println(sb.length());   // 当前字符数
System.out.println(sb.capacity()); // 当前容量

容量 ≥ 长度,容量可以动态增长,但长度表示的是实际内容长度。
8.转变为String使用toString()方法
与String的区别

特性 String StringBuffer
可变性 不可变 可变
线程安全 不保证 线程安全(同步方法)
拼接效率 低(大量操作会产生新对象) 高(原地修改)
用途 少量字符串操作 大量字符串修改操作

2.12 包装类

  1. Java中使用基本类型(如int,float)来保存数值,为了使用方便,有时需要将基本类型的数据包装成对象类型,为了处理这些情况,Java提供了类型包装器类。
  2. Java的类型包装器有Double、Float、Long、Integer、Short、Byte、Character和Boolean,这些类提供了一系列方法,允许基本类型和对象类型之间进行转换。
  3. 主要用于集合类(如 ArrayList)只能存储对象,而不能直接存储基本类型。
基本类型 对应包装类
int Integer
double Double
char Character
boolean Boolean
byte Byte
short Short
long Long
float Float

2.12.1 自动装箱

自动装箱:Java 自动把基本类型转换成对应的包装类对象。
从 Java 5 开始引入,无需手动 new。

int a = 5;
Integer b = a;  // 自动装箱,相当于 Integer.valueOf(a)

应用场景:

ArrayList<Integer> list = new ArrayList<>();
list.add(10);  // 自动装箱,将 int 转成 Integer

2.12.2 自动拆箱

自动拆箱:Java 自动把包装类对象转换回基本类型。

Integer a = 20;
int b = a; // 自动拆箱,相当于 a.intValue()

使用场景:

Integer a = 30;
int sum = a + 10; // 自动拆箱,a -> int

2.12.3 包装类常用方法

方法 说明
intValue() 转为 int
doubleValue() 转为 double
valueOf(String s) 将字符串转为对应包装类
parseInt(String s) 将字符串转为 int(静态方法)
toString() 转为字符串

2.13 输入和输出

2.13.1 控制台输出

使用 System.out.println 和 System.out.print

System.out.println("Hello, Java!"); // 输出并换行
System.out.print("Hello ");         // 输出不换行
System.out.printf("Hello %s!", "Java"); // 格式化输出

格式化占位符常用:

占位符 说明
%d 十进制整数
%f 浮点数
%s 字符串
%c 字符
%n 换行符

2.13.2 控制台输入

  1. 使用 Scanner 类
import java.util.Scanner;

Scanner sc = new Scanner(System.in);

System.out.print("请输入整数: ");
int num = sc.nextInt();

System.out.print("请输入字符串: ");
String str = sc.next();

System.out.println("输入的整数: " + num);
System.out.println("输入的字符串: " + str);

sc.close(); // 关闭扫描器

常用方法:

  • nextInt() → 读取整数
  • nextDouble() → 读取浮点数
  • next() → 读取一个单词(遇空格停止)
  • nextLine() → 读取整行文本
  1. 使用 BufferedReader
import java.io.*;

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.print("请输入内容: ");
String line = br.readLine(); // 读取一行
System.out.println("输入内容: " + line);

BufferedReader 速度快,但需要处理异常(IOException)。

2.13.3 文件输出

  1. 使用 FileWriter
import java.io.*;

try {
    FileWriter fw = new FileWriter("output.txt");
    fw.write("Hello, Java!");
    fw.close();
} catch(IOException e) {
    e.printStackTrace();
}
  1. 使用 BufferedWriter
BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"));
bw.write("Hello, BufferedWriter!");
bw.newLine(); // 换行
bw.close();

2.13.4 文件输入

  1. 使用 FileReader
try {
    FileReader fr = new FileReader("input.txt");
    int ch;
    while((ch = fr.read()) != -1) {
        System.out.print((char)ch);
    }
    fr.close();
} catch(IOException e) {
    e.printStackTrace();
}
  1. 使用 BufferedReader
BufferedReader br = new BufferedReader(new FileReader("input.txt"));
String line;
while((line = br.readLine()) != null) {
    System.out.println(line);
}
br.close();

小结:

I/O 类型 特点
控制台输出 System.out.println 简单直接,适合测试
控制台输入 Scanner 易用,推荐
高效输入输出 BufferedReader/Writer 适合大量数据,需处理异常
文件输入输出 FileReader/FileWriter 简单文件读写
高效文件操作 BufferedReader/BufferedWriter 高速文件读写

2.14 java错误和异常

2.14.1 错误

Java 中的错误分为三类:

类型 说明 例子
编译错误(Compile-time Error) 编译阶段就能发现的错误,代码无法通过编译。 语法错误、类型不匹配、缺少分号等
运行时错误(Runtime Error) 程序运行时发生的错误,会抛出异常,但编译能通过。 除零、数组越界、空指针等
逻辑错误(Logical Error) 语法和运行都正确,但结果不符合预期。 算法错误、计算公式错误

2.14.2 异常

异常是运行时可捕获的错误,是 Throwable 的子类。
Java 将异常分为两类:

类型 说明 特点
受检异常(Checked Exception) 编译器要求必须处理或抛出的异常。 IOExceptionSQLException
非受检异常(Unchecked Exception) 编译器不要求必须处理。 继承 RuntimeException,如 NullPointerExceptionArithmeticException

常见异常示例:

异常类 说明 示例代码
ArithmeticException 算术错误,如除零 int a = 10 / 0;
NullPointerException 空指针引用 String s = null; s.length();
ArrayIndexOutOfBoundsException 数组越界 int[] arr = new int[3]; arr[5];
ClassCastException 类型转换错误 Object obj = "abc"; Integer i = (Integer)obj;
IOException I/O 操作异常 FileReader fr = new FileReader("no_file.txt");

2.14.3 异常处理机制

  1. try-catch 捕获异常
try {
    int a = 10 / 0;
} catch (ArithmeticException e) {
    System.out.println("捕获异常: " + e);
}
  1. try-catch-finally
try {
    int[] arr = new int[3];
    System.out.println(arr[5]);
} catch (ArrayIndexOutOfBoundsException e) {
    System.out.println("捕获异常: " + e);
} finally {
    System.out.println("无论是否异常都会执行");
}
  1. throws 声明异常
public void readFile() throws IOException {
    FileReader fr = new FileReader("file.txt");
}

三、面向对象

  • 面向对象编程的组织方式是围绕“对象”,而不是围绕“行为”;围绕数据,而非逻辑
  • 面向对象程序采用的观点是“一切都是对象”
  • 面向对象编程语言都提供面向对象模型的机制,这些机制就是:封装,继承和多态性
  • 面向对象程序设计的重点是类的设计,而不是对象的设计。

3.1 封装

封装是将 数据(属性)和操作数据的方法(行为) 包装在一个类中。
提供 访问控制(public、private、protected)隐藏内部实现,只暴露必要接口。
优点:
隐藏内部实现,降低耦合。
提高安全性,防止数据被随意修改。
可以通过 getter/setter 控制访问逻辑。

class Person {
    private String name; // 私有属性
    private int age;

    // getter
    public String getName() {
        return name;
    }

    // setter
    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if(age >= 0) this.age = age; // 控制合法性
    }
}

public class Test {
    public static void main(String[] args) {
        Person p = new Person();
        p.setName("Alice");
        p.setAge(20);
        System.out.println(p.getName() + " " + p.getAge());
    }
}

3.2 继承

继承是 子类继承父类的属性和方法,实现代码复用和层次化设计。
使用关键字 extends。
优点:

  • 代码复用。
  • 体现“is-a”关系(子类是一种父类)。
  • 支持方法重写(Override)。
class Animal {
    public void eat() {
        System.out.println("动物吃东西");
    }
}

class Dog extends Animal {
    public void bark() {
        System.out.println("狗叫");
    }
}

public class Test {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.eat();  // 继承自Animal
        dog.bark(); // 自己的方法
    }
}

3.3 多态

多态是 同一个方法调用表现出不同行为的能力。
前提条件:

  • 有继承或实现关系。
  • 方法被重写(Override)。
  • 父类引用指向子类对象。
class Animal {
    public void sound() {
        System.out.println("动物叫");
    }
}

class Dog extends Animal {
    @Override
    public void sound() {
        System.out.println("汪汪");
    }
}

class Cat extends Animal {
    @Override
    public void sound() {
        System.out.println("喵喵");
    }
}

public class Test {
    public static void main(String[] args) {
        Animal a1 = new Dog(); // 父类引用指向子类对象
        Animal a2 = new Cat();

        a1.sound(); // 汪汪
        a2.sound(); // 喵喵
    }
}

3.4 类

类(Class) 是 对象的模板或蓝图,定义了对象的属性和行为。
对象(Object) 是类的实例,是程序运行时具体存在的实体。
类是 面向对象编程(OOP) 的核心概念。

class 类名 {
    // 属性(成员变量)
    数据类型 属性名;
    
    // 方法(成员方法)
    返回类型 方法名(参数列表) {
        // 方法体
    }
}

3.4.1 创建对象

public class Test {
    public static void main(String[] args) {
        // 创建对象
        Person p = new Person();
        p.name = "Alice";
        p.age = 20;
        p.sayHello(); // Hello, my name is Alice
    }
}

3.4.2 类的成员

  1. 属性(成员变量)
    • 存储对象状态
    • 可加访问修饰符(private、public、protected)
    • 可以有默认值,也可通过构造方法初始化
  2. 方法(成员方法)
    • 表示对象行为
    • 可操作对象属性

实例方法:方法声明中不用static修饰,必须通 过对象来调用
类方法(静态方法):方法声明中用static修饰,可以通过类名调用

  1. 构造方法(Constructor)
    • 用于初始化对象
    • 名称与类名相同,没有返回值(无void)
    • 可重载
    • 一旦在程序中定义了有参数的构造方法,都需要手动再定义一个无参数的构造方法
class Person {
    String name;
    int age;

    // 无参构造
    Person() {
        name = "Unknown";
        age = 0;
    }

    // 有参构造
    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

构造方法的特点
(1)构造方法和类具有相同的名字
(2)一个类可以有多个构造方法
(3)构造方法可以有0个、1个或多个参数
(4)构造方法没有返回值和返回类型
(5)构造方法不能被显式调用。构造方法总是和new运算符一起被调用,在创建一个类的新对象的同时,系统会自动调用该类的构造方法为新对象初始化。

  1. 静态成员
class MathUtil {
    static double PI = 3.14159;

    static double square(double x) {
        return x * x;
    }
}

System.out.println(MathUtil.PI);
System.out.println(MathUtil.square(5));
成员类型 属于谁 调用方式 访问限制
静态成员(static) 属于类本身 可通过类名调用 可访问静态成员
不能访问非静态成员
非静态成员 属于对象 必须通过对象调用 可访问所有成员(包括静态)
  1. this 关键字
    • 表示当前对象的引用
    • 用于区分成员变量和局部变量
class Person {
    String name;

    Person(String name) {
        this.name = name; // this.name 是成员变量,name 是参数
    }
}

this关键字:当代码块中出现全局变量、局部变量重名时,
this.变量名 调用全局变量
this关键字作为构造方法调用注意事项

  • this关键字作为构造方法调用时,必须写在构造方法的第一行。
  • this关键字表示当前被引用的实例对象,可以访问对象成员,包括成员变量,成员方法

static 方法中不能用 this:
this 是一个 隐式对象引用,它表示“当前对象本身”。
调用静态方法时,可能根本没有对象存在。因此,没有“当前对象”可以让 this 指向。

  1. 方法重载(Overload)
    • 同一个类中 方法名相同,参数列表(个数、类型、顺序)不同
    • 提高代码可读性
class Calculator {
    int add(int a, int b) { return a + b; }
    double add(double a, double b) { return a + b; }
}
  1. 实例方法和类方法的区别

1.对象调用实例方法
当对象调用实例方法时,该方法中出现的实例变量就是分配给该对象的实例变量;该方法中出现的类变量也是分配给该对象的变量,只不过这个变量和所有的其他对象共享而已。

2.类名调用类方法
类方法不仅可以被类创建的任何对象调用执行,也可以直接通过类名调用。和实例方法不同的是,类方法不可以操作实例变量,这是因为在类创建对象之前,实例成员变量还没有分配内存。

1、非静态变量只限于实例,并只能通过实例来访问。
2、静态方法里只能直接调用同类中的其他静态成员,不能直接访问非静态成员

// static方法中要直接访问非静态变量的尝试会引起编译错误。
         public class Wrong
        {
                int x;
                public static void main(String args[]) {
                       x = 9; // 编译错误!
                }
          } 
  1. 小结
成员类型 说明
属性 对象的状态(字段、变量)
方法 对象的行为
构造方法 初始化对象
静态成员 类共有,不属于单个对象
this 当前对象的引用
方法重载 同名方法,参数不同,实现多种调用形式

3.5 访问修饰符

修饰符 作用范围
public 对所有类可见(最开放)
protected 对同包类和子类可见
default 对同一个包中的类可见
private 仅对当前类可见(最严格)

访问范围比较:

访问修饰符 同类 同包 不同包的子类 其他包
public
protected ×
default × ×
private × × ×

3.6 理解main方法的语法

main方法的标准定义:

public static void main(String[] args)
关键字 含义
public 公共的。JVM(Java 虚拟机)需要从外部调用这个方法,因此它必须是公共的,否则 JVM 无法访问。
static 静态的。JVM 调用 main() 时不会先创建类的对象,所以它必须是静态的,这样 JVM 才能直接通过类名调用。
void 无返回值。main() 只是程序的入口,不需要向 JVM 返回任何结果。
main 方法名,固定写法。JVM 通过这个名字来定位程序入口。
String[] args 参数列表,用于接收命令行传入的字符串参数。
class Test {
    int x = 10;
    public static void main(String[] args) {
        // System.out.println(x); ❌ 错误,无法访问实例成员

        Test t = new Test();      // ✅ 必须创建对象
        System.out.println(t.x);  // ✅ 正确
    }
}

3.7 按值传递和对象引用传递

类型 传递内容 是否影响原变量 示例
基本类型(int、double) 值的副本 不影响 改副本不影响原值
引用类型(对象、数组) 引用(地址)的副本 可改内部属性
不可改引用本身
改对象内容可反映到外部

3.8 内部类

3.8.1 静态内部类

public class Outer {
    static class Inner {
        int a = 0;    // 实例变量a
        static int b = 0;    // 静态变量 b
    }
}
class OtherClass {
    Outer.Inner oi = new Outer.Inner();
    int a2 = oi.a;    // 访问实例成员
    int b2 = Outer.Inner.b;    // 访问静态成员
}

静态内部类中可以定义静态成员和实例成员。外部类以外的其他类需要通过完整的类名访问静态内部类中的静态成员,如果要访问静态内部类中的实例成员,则需要通过静态内部类的实例。

public class Outer {
    int a = 0;    // 实例变量
    static int b = 0;    // 静态变量
    static class Inner {
        Outer o = new Outer;
        int a2 = o.a;    // 访问实例变量
        int b2 = b;    // 访问静态变量
    }
}

静态内部类可以直接访问外部类的静态成员,如果要访问外部类的实例成员,则需要通过外部类的实例去访问。

3.8.2 匿名内部类

class Person {
    public void sayHello() {
        System.out.println("Hello, I'm a person.");
    }
}

public class Demo {
    public static void main(String[] args) {
        Person student = new Person() {
            public void sayHello() {
                System.out.println("Hello, I'm a student.");
            }
        };

        student.sayHello(); // 输出:Hello, I'm a student.
    }
}

匿名内部类不能有构造方法;
匿名内部类不能定义任何静态成员;
只能创建匿名内部类的一个实例;
一个匿名内部类一定跟在new的后面,创建其实现的接口或父类的对象。

四、继承

  1. Java中只支持单一继承,即一个类只能继承一个父类,而不能继承多个类。
  2. 定义一个类没指明任何父类,则默认自动继承java.lang.Object类。
  3. 继承中,子类拥有父类的所有属性和方法,但父类可通过封装保留自己的隐藏数据,并通过暴露设计提供子类可访问的属性和方法。
  4. 子类不能直接继承父类的构造方法
  5. 调用父类构造方法:
public Teacher(String name, String gender, int age, float salary,String department) {

super(name, age, gender);
this.salary = salary;

}

public Teacher( ){
super();
}

构造方法的调用顺序:

  • 先递归向上:
    • 父类 → 父类的父类 → … → 最顶层 Object
    • 依次执行各父类构造方法。
  • 再向下:
    • 回到直接父类构造方法执行完后,
    • 按出现顺序初始化本类成员变量(含实例代码块)。
  • 最后:
    • 执行子类自己的构造方法剩余代码。

子类重写父类方法时,不能使用比父类中被重写的方法更严格的访问权限。例如,父类中的方法是public权限,子类的方法就不能是private权限。如果子类在重写父类方法时定义的权限缩小,则在编译时将出现错误提示。

4.1 Super关键字

“super”关键字代表父类对象。通过使用super关键字,可以访问父类的属性或方法,也可以在子类构造方法中调用父类的构造方法,以便初始化从父类继承的属性。

public Person(String name, int age, String gender) {
      this.name = name;
      this.gender = gender;	
      this.age = age;	
}

class Teacher extends Person {
	private float salary;// 薪酬
	private String department;// 部门
	public Teacher(String name, String gender, int age, float salary,	String department) {
		super(name, age, gender);
		this.salary = salary;
	} 
}

子类覆盖父类属性和方法:
子类的属性和父类的属性同名时,即子类的属性覆盖父类属性的同时,用super.属性来引用父类的属性。
子类的方法和父类的方法同名时,即子类的方法覆盖父类方法的同时,用super.方法(参数列表)来引用父类的方法。

  1. 子 类 无 条 件 继 承 父 类 的 无 参 的 构 造 方 法 , 并 在 创 建 新 子 类对象时自动执行
  2. 若子类的构造方法中没有 s u p e r ( ) 语 句 , 创 建 对 象 时 系 统 将 自 动 调 用 父 类 的 无 参 构 造 方 法 , 再 执 行 自 己 的 构 造 方 法
  3. 子类不能继承父类带参数的构造方法,而只能通过 s u p e r 关
    键字调用父类的某个构造方法
  4. 子类的构造方法定义中,若要 调用父类的含参数的构造方法 ,需用 s u p e r 关键字,且该调 用语句必须是子类构造方法的 第一条可执行语句

引用数据类型转换:

  1. 自动转换:子类转换成父类时(或者实现类转换成接口)可以自动完成。例如,Teacher是Person的子类,将一个Teacher对象赋给一个Person类型的变量时,转换自动完成。
  2. 强制转换:父类转换成子类时(或者接口转换成实现类),必须使用强制转换。例如,Teacher类是Person的子类,如果将一个Person对象赋给一个Teacher类型变量的时候,必须使用强制转换。

4.2 This 和 super

区别点 this super
属性访问 先找本类成员 → 找不到再往上(父类)找 直接跳过本类,找父类成员
方法调用 先绑定本类方法 → 找不到再往上(父类)找 直接绑定父类方法
构造调用 调用本类其它构造,必须放在首行 调用直接父类构造,必须放在子类构造首行

4.3 null关键字

“null”关键字用于标识未初始化的对象。可以将null赋值给引用类型变量,但不可以赋给基本类型变量。

4.4 final关键字

用来修饰类、方法和变量,其含义是“不可改变的、最终的” 。
(1)修饰类:声明为final的类不能被继承,一个final类中的所有方法都隐式地指定为final。
(2)修饰变量:声明为final的变量是一个常量,在定义时必须给予初始值,变量一旦初始化,将不能改变。
(3)修饰方法参数:不允许在方法体内重新赋值。

 final class Person{
     public final void getInfo(final String s){
                s=“java”;//错误
       }
   }

4.5 抽象类

  1. 抽象方法:简单说就是只需要给出方法头部的定义,而不需要实现方法体,因为,没有具体的方法实现,所以称这样的方法为抽象方法。

  2. 如果一个类中含有抽象方法,那么这个类一定是抽象类,反之则不成立。在定义类时,使用abstract修饰。当然,即使类中没有抽象方法这个类也可以用abstract修饰,从而变成一个抽象类。这么做仅仅是为了避免对类进行实例化。

  3. 抽象类不能实例化。

  4. final不能修饰abstract

  5. 抽象类表示的是一种继承关系,一个类只能使用一次继承关系,这样限制了类的多重体现 。

4.6 接口

Java是单继承的语言,利用接口可以模拟多继承。接口是特殊的抽象类,是抽象方法声明和常量的定义集合,而没有变量和方法的实现。
当一个类中所有变量都是final修饰的常量,所有方法都是抽象方法的时候,该类就被 称为接口,使用关键字interface来定义,其中:

  • 定义接口中的常量的修饰符必须是public static final
  • 定义接口中的方法的修饰符必须是public

接口的实现通过“implements”关键字来实现,示例代码如下 :

public class MyClass implements MyInterface {
	public void add(int x, int y) {
		// do something
	}
	public void volume(int x, int y, int z) {
		// do something
	}
}

作为接口来说,一个类可以从接口继承(或者叫实现接口),这也是多继承,接口里面的成员变量不专属于某个对象,都是静态的成员变量,是属于整个类的,因此一个类去实现多个接口也是无所谓的,不会存在对象之间互相冲突的问题。实现多个接口,也就实现了多重继承,而且又避免了多重继承容易出现问题的地方,这就是用接口实现多重继承的好处。

接口具有继承性,通过关键字extends声明该接口是某父类的派生接口;一个接口可以有多个父接口,它们之间 用逗号分隔。

抽象类与接口区别:

概念 定义 作用
抽象类(abstract class) 含有 abstract 方法(或仅作为父类使用)的类 一组子类提供统一的模板部分实现
接口(interface) 一组抽象方法的集合(Java 8 起可含默认方法) 定义行为规范,规定类必须做什么

4.7 Object类

Object类是所有类的顶级父类,在Java体系中,所有类都是直接或间接的继承了Object类。声明类时未使用extends指明父类的,其父类都是Object。
Object类包含了所有Java类的公共属性和方法,这些属性和方法在任何类中均可以直接使用,其中较为重要的方法如下表所示:

方法名 作用 常见用途
equals(Object obj) 判断两个对象是否“内容相等” 比较对象内容(需重写)
hashCode() 返回对象的哈希值 equals 一起重写,用于集合(如 HashMap)
toString() 返回对象的字符串描述 打印对象信息(建议重写)
getClass() 获取对象的运行时类型 反射、调试
clone() 对象拷贝(浅拷贝) 实现对象复制(类需实现 Cloneable
finalize() 垃圾回收前调用(已废弃) 清理资源(现代开发中不推荐使用)
wait() 线程等待 用于线程同步(与 synchronized 配合)
notify() 唤醒一个等待线程 多线程通信
notifyAll() 唤醒所有等待线程 多线程通信

五、异常

从理论的角度Java中异常分为两类,分别为 :
Error(错误):JVM系统内部错误、资源耗尽等严重情况,建议程序终止。
Exception(异常):因编程错误或偶然的外在因素导致的一般性问题,例如:对负数开平方根、空指针访问、试图读取不存在的文件、网络连接中断等。
在这里插入图片描述

5.1 try-catch-finally

① 如果try里有某条语句发生异常,则try中该语句后的代码不再执行
② Catch如果能捕获异常,则方法中try—catch后的普通代码能执行
③ Catch如果不能捕获异常,则方法中try catch后的普通代码不能执行,但能执行方法中的finally中的语句,并抛出异常

5.2 抛出异常

关键字 位置 作用 后跟内容 数量限制
throw 方法体内 实际抛出异常 异常对象 只能一个
throws 方法声明后 声明可能抛出的异常类型 异常类型名 可多个(用逗号分隔)

六、集合

集合(Collection) 是一种 存储多个数据对象 的容器,用于替代数组,提供更强的动态管理能力。
区别于数组:

特点 数组 集合
容量 固定 可动态扩容
元素类型 基本类型或对象 只能是对象(不能存基本类型)
功能 仅存取 可增删改查、排序、去重、遍历等

主要接口与实现类:

接口 特点 常用实现类 说明
List 有序、可重复 ArrayList, LinkedList, Vector 像动态数组
Set 无序、不重复 HashSet, LinkedHashSet, TreeSet 自动去重
Queue 先进先出(FIFO) LinkedList, PriorityQueue 常用于排队结构
Map 键值对(key-value) HashMap, LinkedHashMap, TreeMap, Hashtable key 唯一

总结

后面部分较为粗糙,主要是一次性写也有些疲劳,后续会继续更新。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐